summaryrefslogtreecommitdiff
path: root/absl
diff options
context:
space:
mode:
Diffstat (limited to 'absl')
-rw-r--r--absl/BUILD.bazel7
-rw-r--r--absl/CMakeLists.txt1
-rw-r--r--absl/algorithm/CMakeLists.txt74
-rw-r--r--absl/algorithm/algorithm.h4
-rw-r--r--absl/algorithm/container.h88
-rw-r--r--absl/algorithm/container_test.cc15
-rw-r--r--absl/base/BUILD.bazel70
-rw-r--r--absl/base/CMakeLists.txt634
-rw-r--r--absl/base/attributes.h57
-rw-r--r--absl/base/bit_cast_test.cc4
-rw-r--r--absl/base/call_once.h12
-rw-r--r--absl/base/call_once_test.cc4
-rw-r--r--absl/base/casts.h57
-rw-r--r--absl/base/config.h57
-rw-r--r--absl/base/exception_safety_testing_test.cc80
-rw-r--r--absl/base/inline_variable_test.cc4
-rw-r--r--absl/base/inline_variable_test_a.cc4
-rw-r--r--absl/base/inline_variable_test_b.cc4
-rw-r--r--absl/base/internal/atomic_hook.h4
-rw-r--r--absl/base/internal/bits.h195
-rw-r--r--absl/base/internal/bits_test.cc97
-rw-r--r--absl/base/internal/cycleclock.cc4
-rw-r--r--absl/base/internal/cycleclock.h4
-rw-r--r--absl/base/internal/direct_mmap.h24
-rw-r--r--absl/base/internal/endian.h29
-rw-r--r--absl/base/internal/endian_test.cc24
-rw-r--r--absl/base/internal/exception_safety_testing.cc4
-rw-r--r--absl/base/internal/exception_safety_testing.h351
-rw-r--r--absl/base/internal/exception_testing.h2
-rw-r--r--absl/base/internal/hide_ptr.h4
-rw-r--r--absl/base/internal/identity.h4
-rw-r--r--absl/base/internal/inline_variable_testing.h4
-rw-r--r--absl/base/internal/invoke.h4
-rw-r--r--absl/base/internal/low_level_alloc.cc24
-rw-r--r--absl/base/internal/low_level_alloc.h11
-rw-r--r--absl/base/internal/low_level_alloc_test.cc4
-rw-r--r--absl/base/internal/low_level_scheduling.h4
-rw-r--r--absl/base/internal/raw_logging.cc22
-rw-r--r--absl/base/internal/raw_logging.h49
-rw-r--r--absl/base/internal/scheduling_mode.h4
-rw-r--r--absl/base/internal/spinlock.cc53
-rw-r--r--absl/base/internal/spinlock.h10
-rw-r--r--absl/base/internal/spinlock_benchmark.cc52
-rw-r--r--absl/base/internal/spinlock_linux.inc72
-rw-r--r--absl/base/internal/spinlock_wait.cc15
-rw-r--r--absl/base/internal/spinlock_wait.h4
-rw-r--r--absl/base/internal/sysinfo.cc4
-rw-r--r--absl/base/internal/sysinfo.h4
-rw-r--r--absl/base/internal/sysinfo_test.cc4
-rw-r--r--absl/base/internal/thread_identity.cc14
-rw-r--r--absl/base/internal/thread_identity.h4
-rw-r--r--absl/base/internal/thread_identity_test.cc4
-rw-r--r--absl/base/internal/throw_delegate.cc8
-rw-r--r--absl/base/internal/throw_delegate.h4
-rw-r--r--absl/base/internal/unaligned_access.h131
-rw-r--r--absl/base/internal/unscaledcycleclock.cc4
-rw-r--r--absl/base/internal/unscaledcycleclock.h4
-rw-r--r--absl/base/invoke_test.cc4
-rw-r--r--absl/base/log_severity.h6
-rw-r--r--absl/base/macros.h18
-rw-r--r--absl/base/raw_logging_test.cc31
-rw-r--r--absl/base/spinlock_test_common.cc7
-rw-r--r--absl/base/thread_annotations.h13
-rw-r--r--absl/compiler_config_setting.bzl39
-rw-r--r--absl/container/BUILD.bazel498
-rw-r--r--absl/container/CMakeLists.txt707
-rw-r--r--absl/container/fixed_array.h382
-rw-r--r--absl/container/fixed_array_exception_safety_test.cc119
-rw-r--r--absl/container/fixed_array_test.cc213
-rw-r--r--absl/container/flat_hash_map.h582
-rw-r--r--absl/container/flat_hash_map_test.cc243
-rw-r--r--absl/container/flat_hash_set.h491
-rw-r--r--absl/container/flat_hash_set_test.cc128
-rw-r--r--absl/container/inlined_vector.h1255
-rw-r--r--absl/container/inlined_vector_benchmark.cc2
-rw-r--r--absl/container/inlined_vector_test.cc78
-rw-r--r--absl/container/internal/compressed_tuple.h177
-rw-r--r--absl/container/internal/compressed_tuple_test.cc168
-rw-r--r--absl/container/internal/container_memory.h407
-rw-r--r--absl/container/internal/container_memory_test.cc190
-rw-r--r--absl/container/internal/hash_function_defaults.h145
-rw-r--r--absl/container/internal/hash_function_defaults_test.cc303
-rw-r--r--absl/container/internal/hash_generator_testing.cc74
-rw-r--r--absl/container/internal/hash_generator_testing.h152
-rw-r--r--absl/container/internal/hash_policy_testing.h184
-rw-r--r--absl/container/internal/hash_policy_testing_test.cc45
-rw-r--r--absl/container/internal/hash_policy_traits.h191
-rw-r--r--absl/container/internal/hash_policy_traits_test.cc144
-rw-r--r--absl/container/internal/hashtable_debug.h110
-rw-r--r--absl/container/internal/hashtable_debug_hooks.h83
-rw-r--r--absl/container/internal/layout.h740
-rw-r--r--absl/container/internal/layout_test.cc1557
-rw-r--r--absl/container/internal/node_hash_policy.h90
-rw-r--r--absl/container/internal/node_hash_policy_test.cc69
-rw-r--r--absl/container/internal/raw_hash_map.h187
-rw-r--r--absl/container/internal/raw_hash_set.cc48
-rw-r--r--absl/container/internal/raw_hash_set.h1950
-rw-r--r--absl/container/internal/raw_hash_set_allocator_test.cc430
-rw-r--r--absl/container/internal/raw_hash_set_test.cc1830
-rw-r--r--absl/container/internal/test_instance_tracker.cc5
-rw-r--r--absl/container/internal/test_instance_tracker.h56
-rw-r--r--absl/container/internal/test_instance_tracker_test.cc22
-rw-r--r--absl/container/internal/tracked.h80
-rw-r--r--absl/container/internal/unordered_map_constructor_test.h407
-rw-r--r--absl/container/internal/unordered_map_lookup_test.h117
-rw-r--r--absl/container/internal/unordered_map_modifiers_test.h275
-rw-r--r--absl/container/internal/unordered_map_test.cc40
-rw-r--r--absl/container/internal/unordered_set_constructor_test.h411
-rw-r--r--absl/container/internal/unordered_set_lookup_test.h91
-rw-r--r--absl/container/internal/unordered_set_modifiers_test.h190
-rw-r--r--absl/container/internal/unordered_set_test.cc39
-rw-r--r--absl/container/node_hash_map.h584
-rw-r--r--absl/container/node_hash_map_test.cc220
-rw-r--r--absl/container/node_hash_set.h490
-rw-r--r--absl/container/node_hash_set_test.cc105
-rw-r--r--absl/copts.bzl23
-rw-r--r--absl/debugging/BUILD.bazel1
-rw-r--r--absl/debugging/CMakeLists.txt413
-rw-r--r--absl/debugging/failure_signal_handler.cc25
-rw-r--r--absl/debugging/failure_signal_handler.h4
-rw-r--r--absl/debugging/internal/address_is_readable.cc8
-rw-r--r--absl/debugging/internal/address_is_readable.h4
-rw-r--r--absl/debugging/internal/demangle.cc17
-rw-r--r--absl/debugging/internal/demangle.h4
-rw-r--r--absl/debugging/internal/demangle_test.cc4
-rw-r--r--absl/debugging/internal/elf_mem_image.cc4
-rw-r--r--absl/debugging/internal/elf_mem_image.h4
-rw-r--r--absl/debugging/internal/examine_stack.cc8
-rw-r--r--absl/debugging/internal/examine_stack.h4
-rw-r--r--absl/debugging/internal/stack_consumption.cc4
-rw-r--r--absl/debugging/internal/stack_consumption.h4
-rw-r--r--absl/debugging/internal/stack_consumption_test.cc4
-rw-r--r--absl/debugging/internal/stacktrace_aarch64-inl.inc4
-rw-r--r--absl/debugging/internal/stacktrace_arm-inl.inc4
-rw-r--r--absl/debugging/internal/stacktrace_config.h40
-rw-r--r--absl/debugging/internal/stacktrace_generic-inl.inc5
-rw-r--r--absl/debugging/internal/stacktrace_powerpc-inl.inc13
-rw-r--r--absl/debugging/internal/stacktrace_unimplemented-inl.inc4
-rw-r--r--absl/debugging/internal/stacktrace_win32-inl.inc4
-rw-r--r--absl/debugging/internal/stacktrace_x86-inl.inc4
-rw-r--r--absl/debugging/internal/symbolize.h8
-rw-r--r--absl/debugging/internal/vdso_support.cc4
-rw-r--r--absl/debugging/internal/vdso_support.h4
-rw-r--r--absl/debugging/leak_check.cc8
-rw-r--r--absl/debugging/leak_check.h4
-rw-r--r--absl/debugging/stacktrace.cc22
-rw-r--r--absl/debugging/stacktrace.h4
-rw-r--r--absl/debugging/symbolize.h4
-rw-r--r--absl/debugging/symbolize_elf.inc8
-rw-r--r--absl/debugging/symbolize_test.cc2
-rw-r--r--absl/debugging/symbolize_unimplemented.inc4
-rw-r--r--absl/debugging/symbolize_win32.inc4
-rw-r--r--absl/hash/BUILD.bazel113
-rw-r--r--absl/hash/CMakeLists.txt111
-rw-r--r--absl/hash/hash.h314
-rw-r--r--absl/hash/hash_test.cc427
-rw-r--r--absl/hash/hash_testing.h376
-rw-r--r--absl/hash/internal/city.cc346
-rw-r--r--absl/hash/internal/city.h94
-rw-r--r--absl/hash/internal/city_test.cc595
-rw-r--r--absl/hash/internal/hash.cc25
-rw-r--r--absl/hash/internal/hash.h898
-rw-r--r--absl/hash/internal/print_hash_of.cc23
-rw-r--r--absl/hash/internal/spy_hash_state.h220
-rw-r--r--absl/memory/BUILD.bazel2
-rw-r--r--absl/memory/CMakeLists.txt79
-rw-r--r--absl/memory/memory.h74
-rw-r--r--absl/memory/memory_exception_safety_test.cc13
-rw-r--r--absl/memory/memory_test.cc42
-rw-r--r--absl/meta/type_traits.h74
-rw-r--r--absl/meta/type_traits_test.cc161
-rw-r--r--absl/numeric/BUILD.bazel1
-rw-r--r--absl/numeric/int128.cc32
-rw-r--r--absl/numeric/int128.h94
-rw-r--r--absl/numeric/int128_test.cc12
-rw-r--r--absl/strings/BUILD.bazel196
-rw-r--r--absl/strings/CMakeLists.txt146
-rw-r--r--absl/strings/ascii.cc4
-rw-r--r--absl/strings/ascii.h18
-rw-r--r--absl/strings/ascii_benchmark.cc120
-rw-r--r--absl/strings/charconv.cc12
-rw-r--r--absl/strings/charconv.h10
-rw-r--r--absl/strings/charconv_test.cc20
-rw-r--r--absl/strings/escaping.cc24
-rw-r--r--absl/strings/escaping.h37
-rw-r--r--absl/strings/escaping_test.cc17
-rw-r--r--absl/strings/internal/bits.h55
-rw-r--r--absl/strings/internal/char_map.h4
-rw-r--r--absl/strings/internal/charconv_bigint.cc4
-rw-r--r--absl/strings/internal/charconv_bigint.h19
-rw-r--r--absl/strings/internal/charconv_bigint_test.cc4
-rw-r--r--absl/strings/internal/charconv_parse.cc6
-rw-r--r--absl/strings/internal/charconv_parse.h4
-rw-r--r--absl/strings/internal/charconv_parse_test.cc16
-rw-r--r--absl/strings/internal/escaping_test_common.h4
-rw-r--r--absl/strings/internal/memutil.cc4
-rw-r--r--absl/strings/internal/memutil.h12
-rw-r--r--absl/strings/internal/numbers_test_common.h27
-rw-r--r--absl/strings/internal/ostringstream.cc4
-rw-r--r--absl/strings/internal/ostringstream.h16
-rw-r--r--absl/strings/internal/pow10_helper.cc122
-rw-r--r--absl/strings/internal/pow10_helper.h38
-rw-r--r--absl/strings/internal/pow10_helper_test.cc122
-rw-r--r--absl/strings/internal/resize_uninitialized.h10
-rw-r--r--absl/strings/internal/stl_type_traits.h4
-rw-r--r--absl/strings/internal/str_format/arg.cc379
-rw-r--r--absl/strings/internal/str_format/arg.h422
-rw-r--r--absl/strings/internal/str_format/arg_test.cc113
-rw-r--r--absl/strings/internal/str_format/bind.cc231
-rw-r--r--absl/strings/internal/str_format/bind.h199
-rw-r--r--absl/strings/internal/str_format/bind_test.cc133
-rw-r--r--absl/strings/internal/str_format/checker.h327
-rw-r--r--absl/strings/internal/str_format/checker_test.cc152
-rw-r--r--absl/strings/internal/str_format/convert_test.cc577
-rw-r--r--absl/strings/internal/str_format/extension.cc86
-rw-r--r--absl/strings/internal/str_format/extension.h414
-rw-r--r--absl/strings/internal/str_format/extension_test.cc65
-rw-r--r--absl/strings/internal/str_format/float_conversion.cc485
-rw-r--r--absl/strings/internal/str_format/float_conversion.h23
-rw-r--r--absl/strings/internal/str_format/output.cc72
-rw-r--r--absl/strings/internal/str_format/output.h103
-rw-r--r--absl/strings/internal/str_format/output_test.cc80
-rw-r--r--absl/strings/internal/str_format/parser.cc311
-rw-r--r--absl/strings/internal/str_format/parser.h294
-rw-r--r--absl/strings/internal/str_format/parser_test.cc391
-rw-r--r--absl/strings/internal/str_join_internal.h16
-rw-r--r--absl/strings/internal/str_split_internal.h36
-rw-r--r--absl/strings/internal/utf8.cc4
-rw-r--r--absl/strings/internal/utf8.h4
-rw-r--r--absl/strings/match.cc11
-rw-r--r--absl/strings/match.h26
-rw-r--r--absl/strings/match_test.cc11
-rw-r--r--absl/strings/numbers.cc16
-rw-r--r--absl/strings/numbers.h28
-rw-r--r--absl/strings/numbers_benchmark.cc263
-rw-r--r--absl/strings/numbers_test.cc17
-rw-r--r--absl/strings/str_cat.cc12
-rw-r--r--absl/strings/str_cat.h38
-rw-r--r--absl/strings/str_cat_test.cc11
-rw-r--r--absl/strings/str_format.h514
-rw-r--r--absl/strings/str_format_test.cc628
-rw-r--r--absl/strings/str_join.h61
-rw-r--r--absl/strings/str_replace.cc4
-rw-r--r--absl/strings/str_replace.h34
-rw-r--r--absl/strings/str_replace_benchmark.cc8
-rw-r--r--absl/strings/str_split.cc17
-rw-r--r--absl/strings/str_split.h77
-rw-r--r--absl/strings/str_split_benchmark.cc8
-rw-r--r--absl/strings/str_split_test.cc34
-rw-r--r--absl/strings/string_view.cc4
-rw-r--r--absl/strings/string_view.h65
-rw-r--r--absl/strings/string_view_benchmark.cc10
-rw-r--r--absl/strings/string_view_test.cc20
-rw-r--r--absl/strings/strip.h22
-rw-r--r--absl/strings/strip_test.cc4
-rw-r--r--absl/strings/substitute.cc4
-rw-r--r--absl/strings/substitute.h40
-rw-r--r--absl/synchronization/BUILD.bazel36
-rw-r--r--absl/synchronization/CMakeLists.txt2
-rw-r--r--absl/synchronization/barrier.cc4
-rw-r--r--absl/synchronization/barrier.h4
-rw-r--r--absl/synchronization/blocking_counter.cc4
-rw-r--r--absl/synchronization/blocking_counter.h4
-rw-r--r--absl/synchronization/blocking_counter_test.cc4
-rw-r--r--absl/synchronization/internal/create_thread_identity.cc4
-rw-r--r--absl/synchronization/internal/create_thread_identity.h4
-rw-r--r--absl/synchronization/internal/graphcycles.cc10
-rw-r--r--absl/synchronization/internal/graphcycles.h4
-rw-r--r--absl/synchronization/internal/graphcycles_test.cc4
-rw-r--r--absl/synchronization/internal/kernel_timeout.h30
-rw-r--r--absl/synchronization/internal/mutex_nonprod.cc4
-rw-r--r--absl/synchronization/internal/mutex_nonprod.inc4
-rw-r--r--absl/synchronization/internal/per_thread_sem.cc4
-rw-r--r--absl/synchronization/internal/per_thread_sem.h4
-rw-r--r--absl/synchronization/internal/per_thread_sem_test.cc11
-rw-r--r--absl/synchronization/internal/thread_pool.h4
-rw-r--r--absl/synchronization/internal/waiter.cc4
-rw-r--r--absl/synchronization/internal/waiter.h4
-rw-r--r--absl/synchronization/lifetime_test.cc15
-rw-r--r--absl/synchronization/mutex.cc12
-rw-r--r--absl/synchronization/mutex.h14
-rw-r--r--absl/synchronization/mutex_benchmark.cc159
-rw-r--r--absl/synchronization/mutex_test.cc575
-rw-r--r--absl/synchronization/notification.cc5
-rw-r--r--absl/synchronization/notification.h5
-rw-r--r--absl/synchronization/notification_test.cc13
-rw-r--r--absl/time/BUILD.bazel9
-rw-r--r--absl/time/CMakeLists.txt4
-rw-r--r--absl/time/civil_time.cc90
-rw-r--r--absl/time/civil_time.h488
-rw-r--r--absl/time/civil_time_benchmark.cc107
-rw-r--r--absl/time/civil_time_test.cc1073
-rw-r--r--absl/time/clock.cc22
-rw-r--r--absl/time/clock.h4
-rw-r--r--absl/time/clock_test.cc94
-rw-r--r--absl/time/duration.cc31
-rw-r--r--absl/time/duration_benchmark.cc78
-rw-r--r--absl/time/duration_test.cc169
-rw-r--r--absl/time/format.cc20
-rw-r--r--absl/time/format_benchmark.cc7
-rw-r--r--absl/time/format_test.cc46
-rw-r--r--absl/time/internal/cctz/BUILD.bazel3
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time.h6
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time_detail.h78
-rw-r--r--absl/time/internal/cctz/include/cctz/time_zone.h133
-rw-r--r--absl/time/internal/cctz/include/cctz/zone_info_source.h13
-rw-r--r--absl/time/internal/cctz/src/cctz_benchmark.cc64
-rw-r--r--absl/time/internal/cctz/src/civil_time_detail.cc4
-rw-r--r--absl/time/internal/cctz/src/civil_time_test.cc6
-rw-r--r--absl/time/internal/cctz/src/time_zone_fixed.cc41
-rw-r--r--absl/time/internal/cctz/src/time_zone_fixed.h10
-rw-r--r--absl/time/internal/cctz/src/time_zone_format.cc23
-rw-r--r--absl/time/internal/cctz/src/time_zone_format_test.cc314
-rw-r--r--absl/time/internal/cctz/src/time_zone_if.cc4
-rw-r--r--absl/time/internal/cctz/src/time_zone_if.h32
-rw-r--r--absl/time/internal/cctz/src/time_zone_impl.cc17
-rw-r--r--absl/time/internal/cctz/src/time_zone_impl.h45
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.cc114
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.h16
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.cc222
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.h13
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup.cc41
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc456
-rw-r--r--absl/time/internal/cctz/src/time_zone_posix.cc4
-rw-r--r--absl/time/internal/cctz/src/time_zone_posix.h46
-rw-r--r--absl/time/internal/cctz/src/tzfile.h2
-rw-r--r--absl/time/internal/cctz/src/zone_info_source.cc23
-rw-r--r--absl/time/internal/cctz/testdata/version2
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjanbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Accrabin842 -> 828 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Abababin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiersbin760 -> 751 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmarabin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmerabin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamakobin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Banguibin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjulbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissaubin208 -> 194 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyrebin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzavillebin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumburabin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairobin1972 -> 1963 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablancabin1643 -> 1533 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceutabin2059 -> 2050 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakrybin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakarbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaambin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Djiboutibin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Doualabin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiunbin1473 -> 1403 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetownbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaboronebin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Hararebin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburgbin271 -> 262 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Jubabin683 -> 669 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampalabin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoumbin713 -> 699 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigalibin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasabin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagosbin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Librevillebin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Lomebin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Luandabin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashibin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusakabin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabobin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputobin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Maserubin271 -> 262 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabanebin271 -> 262 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishubin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Monroviabin233 -> 224 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobibin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamenabin225 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Niameybin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchottbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougoubin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novobin171 -> 157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tomebin234 -> 225 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktubin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripolibin655 -> 641 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunisbin710 -> 701 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoekbin988 -> 979 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Adakbin2365 -> 2356 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Anchoragebin2380 -> 2371 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Anguillabin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Antiguabin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Araguainabin910 -> 896 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Airesbin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarcabin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadaviabin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordobabin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuybin1081 -> 1072 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Riojabin1123 -> 1114 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendozabin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegosbin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Saltabin1081 -> 1072 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juanbin1123 -> 1114 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luisbin1139 -> 1130 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucumanbin1137 -> 1128 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaiabin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Arubabin212 -> 198 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Asuncionbin2077 -> 2068 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Atikokanbin345 -> 336 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Atkabin2365 -> 2356 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Bahiabin1050 -> 1036 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderasbin1588 -> 1574 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Barbadosbin344 -> 330 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Belembin602 -> 588 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Belizebin978 -> 964 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablonbin307 -> 298 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vistabin658 -> 644 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Bogotabin271 -> 262 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Boisebin2403 -> 2394 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Airesbin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Baybin2098 -> 2084 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grandebin2016 -> 2002 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Cancunbin816 -> 802 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Caracasbin289 -> 280 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Catamarcabin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Cayennebin224 -> 210 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Caymanbin203 -> 194 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Chicagobin3585 -> 3576 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahuabin1522 -> 1508 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbourbin345 -> 336 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Cordobabin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Ricabin341 -> 332 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Crestonbin233 -> 224 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Cuiababin1988 -> 1974 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Curacaobin212 -> 198 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavnbin712 -> 698 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Dawsonbin2093 -> 2084 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creekbin1059 -> 1050 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Denverbin2453 -> 2444 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Detroitbin2188 -> 2174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Dominicabin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Edmontonbin2402 -> 2388 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepebin690 -> 676 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvadorbin250 -> 236 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Ensenadabin2356 -> 2342 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelsonbin2249 -> 2240 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Waynebin1675 -> 1666 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Fortalezabin742 -> 728 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Baybin2206 -> 2192 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Godthabbin1892 -> 1878 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Baybin3219 -> 3210 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turkbin1881 -> 1872 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Grenadabin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupebin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Guatemalabin306 -> 292 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquilbin271 -> 262 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Guyanabin266 -> 252 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Halifaxbin3438 -> 3424 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Havanabin2437 -> 2428 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillobin454 -> 440 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolisbin1675 -> 1666 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knoxbin2437 -> 2428 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengobin1731 -> 1722 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburgbin1913 -> 1904 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_Citybin1735 -> 1726 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevaybin1423 -> 1414 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennesbin1703 -> 1694 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamacbin1787 -> 1778 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolisbin1675 -> 1666 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Inuvikbin1928 -> 1914 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluitbin2046 -> 2032 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Jamaicabin507 -> 498 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Jujuybin1081 -> 1072 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Juneaubin2362 -> 2353 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisvillebin2781 -> 2772 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticellobin2361 -> 2352 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Knox_INbin2437 -> 2428 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijkbin212 -> 198 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/La_Pazbin257 -> 248 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Limabin431 -> 422 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angelesbin2845 -> 2836 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Louisvillebin2781 -> 2772 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princesbin212 -> 198 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Maceiobin770 -> 756 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Managuabin463 -> 454 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Manausbin630 -> 616 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Marigotbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Martiniquebin257 -> 248 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Matamorosbin1416 -> 1402 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlanbin1564 -> 1550 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Mendozabin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Menomineebin2283 -> 2274 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Meridabin1456 -> 1442 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatlabin1418 -> 1409 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_Citybin1618 -> 1604 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Miquelonbin1696 -> 1682 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Monctonbin3163 -> 3154 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Monterreybin1416 -> 1402 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Montevideobin1564 -> 1550 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Montrealbin3503 -> 3494 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Montserratbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Nassaubin2284 -> 2270 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/New_Yorkbin3545 -> 3536 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Nipigonbin2131 -> 2122 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Nomebin2376 -> 2367 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Noronhabin742 -> 728 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulahbin2389 -> 2380 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Centerbin2389 -> 2380 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salembin2389 -> 2380 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Ojinagabin1522 -> 1508 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Panamabin203 -> 194 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtungbin2108 -> 2094 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribobin296 -> 282 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Phoenixbin353 -> 344 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Princebin1455 -> 1446 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spainbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acrebin662 -> 648 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velhobin602 -> 588 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Ricobin255 -> 246 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenasbin1911 -> 1902 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_Riverbin2131 -> 2122 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inletbin1930 -> 1916 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Recifebin742 -> 728 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Reginabin994 -> 980 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Resolutebin1930 -> 1916 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Brancobin662 -> 648 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Rosariobin1109 -> 1100 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabelbin2356 -> 2342 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Santarembin632 -> 618 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Santiagobin2538 -> 2529 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingobin491 -> 482 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulobin2016 -> 2002 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysundbin1930 -> 1916 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Shiprockbin2453 -> 2444 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Sitkabin2350 -> 2329 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemybin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/St_Johnsbin3664 -> 3655 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/St_Kittsbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/St_Luciabin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomasbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincentbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Currentbin574 -> 560 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpabin278 -> 264 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Thulebin1528 -> 1514 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Baybin2211 -> 2202 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Tijuanabin2356 -> 2342 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Torontobin3503 -> 3494 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Tortolabin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Vancouverbin2901 -> 2892 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Virginbin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorsebin2093 -> 2084 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Winnipegbin2891 -> 2882 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Yakutatbin2314 -> 2305 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknifebin1980 -> 1966 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Caseybin311 -> 297 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davisbin311 -> 297 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrvillebin216 -> 202 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquariebin1543 -> 1534 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawsonbin225 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdobin2460 -> 2451 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmerbin1432 -> 1418 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rotherabin186 -> 172 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Polebin2460 -> 2451 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowabin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Trollbin1176 -> 1162 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostokbin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyenbin2251 -> 2242 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Adenbin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Almatybin1031 -> 1017 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Ammanbin1877 -> 1863 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyrbin1222 -> 1208 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtaubin1017 -> 1003 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobebin1047 -> 1033 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabatbin651 -> 637 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabadbin651 -> 637 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyraubin1025 -> 1011 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdadbin1004 -> 995 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrainbin225 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Bakubin1269 -> 1255 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkokbin220 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaulbin1255 -> 1241 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirutbin2175 -> 2166 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkekbin1045 -> 999 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Bruneibin229 -> 215 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcuttabin312 -> 303 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Chitabin1257 -> 1243 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsanbin991 -> 977 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqingbin414 -> 545 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungkingbin414 -> 545 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombobin413 -> 404 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Daccabin370 -> 361 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascusbin2320 -> 2306 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhakabin370 -> 361 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Dilibin253 -> 239 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubaibin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbebin621 -> 607 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagustabin2042 -> 2028 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Gazabin2295 -> 2286 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbinbin414 -> 545 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebronbin2323 -> 2314 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minhbin389 -> 375 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kongbin1189 -> 1175 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovdbin921 -> 907 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutskbin1276 -> 1267 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbulbin2166 -> 2157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakartabin392 -> 383 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapurabin251 -> 237 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalembin2265 -> 2256 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabulbin229 -> 220 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatkabin1198 -> 1184 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachibin417 -> 403 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgarbin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandubin238 -> 224 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandubin238 -> 224 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandygabin1311 -> 1297 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkatabin312 -> 303 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarskbin1243 -> 1229 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpurbin424 -> 415 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuchingbin521 -> 507 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwaitbin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Macaobin771 -> 1241 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Macaubin771 -> 1241 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadanbin1258 -> 1244 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassarbin288 -> 274 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Manilabin367 -> 350 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscatbin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosiabin2016 -> 2002 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetskbin1197 -> 1183 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirskbin1255 -> 1241 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Omskbin1243 -> 1229 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Oralbin1039 -> 1025 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penhbin220 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianakbin395 -> 381 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyangbin267 -> 253 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatarbin225 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylordabin1047 -> 1017 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoonbin297 -> 288 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadhbin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigonbin389 -> 375 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalinbin1234 -> 1220 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkandbin619 -> 605 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoulbin531 -> 517 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghaibin414 -> 545 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Singaporebin424 -> 415 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymskbin1244 -> 1230 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipeibin790 -> 781 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkentbin635 -> 621 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisibin1080 -> 1071 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehranbin1718 -> 1704 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Avivbin2265 -> 2256 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbubin229 -> 215 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphubin229 -> 215 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyobin318 -> 309 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomskbin1255 -> 1241 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandangbin288 -> 274 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatarbin921 -> 907 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Batorbin921 -> 907 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqibin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nerabin1290 -> 1276 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientianebin220 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostokbin1244 -> 1230 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutskbin1243 -> 1229 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangonbin297 -> 288 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburgbin1281 -> 1267 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevanbin1213 -> 1199 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azoresbin3493 -> 3484 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermudabin2004 -> 1990 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canarybin1911 -> 1897 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verdebin284 -> 270 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroebin1829 -> 1815 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroebin1829 -> 1815 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayenbin2251 -> 2242 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeirabin3484 -> 3475 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavikbin1188 -> 1174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgiabin181 -> 172 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helenabin170 -> 156 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanleybin1251 -> 1242 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/ACTbin2223 -> 2214 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaidebin2238 -> 2233 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbanebin452 -> 443 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hillbin2274 -> 2269 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberrabin2223 -> 2214 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Curriebin2223 -> 2214 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwinbin323 -> 318 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Euclabin503 -> 494 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobartbin2335 -> 2326 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/LHIbin1889 -> 1880 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindemanbin522 -> 513 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howebin1889 -> 1880 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbournebin2223 -> 2214 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/NSWbin2223 -> 2214 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Northbin323 -> 318 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Perthbin479 -> 470 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Queenslandbin452 -> 443 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Southbin2238 -> 2233 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydneybin2223 -> 2214 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmaniabin2335 -> 2326 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoriabin2223 -> 2214 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Westbin479 -> 470 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinnabin2274 -> 2269 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acrebin662 -> 648 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronhabin742 -> 728 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Brazil/Eastbin2016 -> 2002 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Brazil/Westbin630 -> 616 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/CETbin2102 -> 2102 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/CST6CDTbin2294 -> 2294 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlanticbin3438 -> 3424 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Centralbin2891 -> 2882 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Easternbin3503 -> 3494 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountainbin2402 -> 2388 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundlandbin3664 -> 3655 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacificbin2901 -> 2892 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewanbin994 -> 980 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukonbin2093 -> 2084 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Chile/Continentalbin2538 -> 2529 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIslandbin2242 -> 2233 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Cubabin2437 -> 2428 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/EETbin1876 -> 1876 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/ESTbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/EST5EDTbin2294 -> 2294 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Egyptbin1972 -> 1963 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Eirebin3531 -> 3522 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMTbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0bin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9bin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0bin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10bin150 -> 122 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11bin150 -> 122 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12bin150 -> 122 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13bin150 -> 122 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14bin150 -> 122 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9bin149 -> 121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0bin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwichbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/UCTbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/UTCbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/Universalbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulubin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdambin2949 -> 2940 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorrabin1751 -> 1742 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhanbin1197 -> 1183 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Athensbin2271 -> 2262 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfastbin3687 -> 3678 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgradebin1957 -> 1948 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlinbin2335 -> 2326 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislavabin2338 -> 2329 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Brusselsbin2970 -> 2961 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharestbin2221 -> 2212 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapestbin2405 -> 2396 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingenbin1918 -> 1909 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinaubin2445 -> 2436 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagenbin2160 -> 2151 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublinbin3531 -> 3522 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltarbin3061 -> 3052 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernseybin3687 -> 3678 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinkibin1909 -> 1900 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Manbin3687 -> 3678 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbulbin2166 -> 2157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Jerseybin3687 -> 3678 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningradbin1518 -> 1509 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Kievbin2097 -> 2088 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirovbin1167 -> 1153 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbonbin3469 -> 3469 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljanabin1957 -> 1948 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Londonbin3687 -> 3678 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourgbin2974 -> 2960 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Madridbin2637 -> 2628 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Maltabin2629 -> 2620 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamnbin1909 -> 1900 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Minskbin1370 -> 1361 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Monacobin2953 -> 2944 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscowbin1544 -> 1535 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosiabin2016 -> 2002 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslobin2251 -> 2242 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Parisbin2971 -> 2962 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgoricabin1957 -> 1948 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Praguebin2338 -> 2329 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Rigabin2235 -> 2226 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Romebin2692 -> 2683 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Samarabin1253 -> 1215 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marinobin2692 -> 2683 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevobin1957 -> 1948 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratovbin1197 -> 1183 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopolbin1490 -> 1481 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopjebin1957 -> 1948 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofiabin2130 -> 2121 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholmbin1918 -> 1909 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinnbin2187 -> 2178 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiranebin2098 -> 2084 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspolbin2445 -> 2436 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovskbin1281 -> 1267 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorodbin2103 -> 2094 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduzbin1918 -> 1909 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaticanbin2692 -> 2683 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Viennabin2237 -> 2228 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilniusbin2199 -> 2190 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgogradbin1167 -> 1183 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsawbin2705 -> 2696 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagrebbin1957 -> 1948 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhyebin2115 -> 2106 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurichbin1918 -> 1909 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Factorybin148 -> 120 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/GBbin3687 -> 3678 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/GB-Eirebin3687 -> 3678 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/GMTbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/GMT+0bin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/GMT-0bin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/GMT0bin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Greenwichbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/HSTbin128 -> 119 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Hongkongbin1189 -> 1175 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Icelandbin1188 -> 1174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivobin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagosbin225 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmasbin182 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocosbin191 -> 182 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Comorobin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelenbin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahebin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldivesbin220 -> 211 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritiusbin267 -> 253 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayottebin285 -> 271 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunionbin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Iranbin1718 -> 1704 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Israelbin2265 -> 2256 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Jamaicabin507 -> 498 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Japanbin318 -> 309 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Kwajaleinbin259 -> 250 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Libyabin655 -> 641 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/METbin2102 -> 2102 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/MSTbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/MST7MDTbin2294 -> 2294 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNortebin2356 -> 2342 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSurbin1564 -> 1550 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Mexico/Generalbin1618 -> 1604 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/NZbin2460 -> 2451 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/NZ-CHATbin2087 -> 2078 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Navajobin2453 -> 2444 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/PRCbin414 -> 545 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/PST8PDTbin2294 -> 2294 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apiabin1134 -> 1125 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Aucklandbin2460 -> 2451 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainvillebin296 -> 286 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chathambin2087 -> 2078 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuukbin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easterbin2242 -> 2233 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efatebin492 -> 478 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderburybin259 -> 250 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofobin221 -> 212 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fijibin1104 -> 1090 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafutibin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagosbin268 -> 254 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambierbin186 -> 172 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanalbin188 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guambin225 -> 216 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulubin276 -> 329 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnstonbin276 -> 329 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimatibin263 -> 254 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosraebin251 -> 242 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajaleinbin259 -> 250 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majurobin221 -> 212 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesasbin195 -> 181 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midwaybin196 -> 187 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Naurubin282 -> 268 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niuebin266 -> 257 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolkbin323 -> 314 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumeabin328 -> 314 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pagobin196 -> 187 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palaubin182 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairnbin223 -> 214 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpeibin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponapebin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresbybin206 -> 196 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotongabin602 -> 593 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipanbin225 -> 216 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoabin196 -> 187 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahitibin187 -> 173 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawabin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapubin393 -> 384 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Trukbin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wakebin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallisbin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yapbin183 -> 174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Polandbin2705 -> 2696 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Portugalbin3469 -> 3469 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/ROCbin790 -> 781 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/ROKbin531 -> 517 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Singaporebin424 -> 415 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Turkeybin2166 -> 2157 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/UCTbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Alaskabin2380 -> 2371 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Aleutianbin2365 -> 2356 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Arizonabin353 -> 344 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Centralbin3585 -> 3576 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/East-Indianabin1675 -> 1666 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Easternbin3545 -> 3536 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Hawaiibin276 -> 329 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starkebin2437 -> 2428 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Michiganbin2188 -> 2174 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Mountainbin2453 -> 2444 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Pacificbin2845 -> 2836 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Samoabin196 -> 187 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/UTCbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Universalbin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/W-SUbin1544 -> 1535 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/WETbin1873 -> 1873 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Zulubin127 -> 118 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab34
-rw-r--r--absl/time/internal/get_current_time_chrono.inc31
-rw-r--r--absl/time/internal/get_current_time_ios.inc82
-rw-r--r--absl/time/internal/get_current_time_posix.inc4
-rw-r--r--absl/time/internal/get_current_time_windows.inc19
-rw-r--r--absl/time/internal/test_util.cc14
-rw-r--r--absl/time/internal/test_util.h28
-rw-r--r--absl/time/time.cc338
-rw-r--r--absl/time/time.h843
-rw-r--r--absl/time/time_benchmark.cc38
-rw-r--r--absl/time/time_norm_test.cc306
-rw-r--r--absl/time/time_test.cc393
-rw-r--r--absl/time/time_zone_test.cc2
-rw-r--r--absl/types/BUILD.bazel21
-rw-r--r--absl/types/CMakeLists.txt10
-rw-r--r--absl/types/any.h8
-rw-r--r--absl/types/any_exception_safety_test.cc8
-rw-r--r--absl/types/bad_any_cast.cc8
-rw-r--r--absl/types/bad_any_cast.h20
-rw-r--r--absl/types/bad_optional_access.cc8
-rw-r--r--absl/types/bad_optional_access.h20
-rw-r--r--absl/types/bad_variant_access.cc8
-rw-r--r--absl/types/bad_variant_access.h20
-rw-r--r--absl/types/internal/variant.h88
-rw-r--r--absl/types/optional.cc4
-rw-r--r--absl/types/optional.h25
-rw-r--r--absl/types/optional_exception_safety_test.cc30
-rw-r--r--absl/types/optional_test.cc33
-rw-r--r--absl/types/span.h55
-rw-r--r--absl/types/span_test.cc3
-rw-r--r--absl/types/variant.h76
-rw-r--r--absl/types/variant_benchmark.cc4
-rw-r--r--absl/types/variant_exception_safety_test.cc108
-rw-r--r--absl/types/variant_test.cc25
-rw-r--r--absl/utility/utility.h10
955 files changed, 35216 insertions, 4892 deletions
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index 439addbf..edd0274c 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -18,11 +18,10 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
-config_setting(
+load(":compiler_config_setting.bzl", "create_llvm_config")
+
+create_llvm_config(
name = "llvm_compiler",
- values = {
- "compiler": "llvm",
- },
visibility = [":__subpackages__"],
)
diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt
index 689f64e2..1d09b193 100644
--- a/absl/CMakeLists.txt
+++ b/absl/CMakeLists.txt
@@ -20,6 +20,7 @@ add_subdirectory(base)
add_subdirectory(algorithm)
add_subdirectory(container)
add_subdirectory(debugging)
+add_subdirectory(hash)
add_subdirectory(memory)
add_subdirectory(meta)
add_subdirectory(numeric)
diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt
index fdf45c55..87a165c0 100644
--- a/absl/algorithm/CMakeLists.txt
+++ b/absl/algorithm/CMakeLists.txt
@@ -14,50 +14,50 @@
# limitations under the License.
#
-list(APPEND ALGORITHM_PUBLIC_HEADERS
- "algorithm.h"
- "container.h"
-)
-
-
-#
-## TESTS
-#
-
-# test algorithm_test
-list(APPEND ALGORITHM_TEST_SRC
- "algorithm_test.cc"
- ${ALGORITHM_PUBLIC_HEADERS}
- ${ALGORITHM_INTERNAL_HEADERS}
-)
-
-absl_header_library(
- TARGET
- absl_algorithm
- EXPORT_NAME
+absl_cc_library(
+ NAME
algorithm
+ HDRS
+ "algorithm.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
)
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
algorithm_test
- SOURCES
- ${ALGORITHM_TEST_SRC}
- PUBLIC_LIBRARIES
+ SRCS
+ "algorithm_test.cc"
+ DEPS
absl::algorithm
+ gmock_main
)
+absl_cc_library(
+ NAME
+ algorithm_container
+ HDRS
+ "container.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::algorithm
+ absl::core_headers
+ absl::meta
+ PUBLIC
+)
-
-
-# test container_test
-set(CONTAINER_TEST_SRC "container_test.cc")
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
container_test
- SOURCES
- ${CONTAINER_TEST_SRC}
- PUBLIC_LIBRARIES
- absl::algorithm
+ SRCS
+ "container_test.cc"
+ DEPS
+ absl::algorithm_container
+ absl::base
+ absl::core_headers
+ absl::memory
+ absl::span
+ gmock_main
)
diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h
index 3a8f2724..1eef16cb 100644
--- a/absl/algorithm/algorithm.h
+++ b/absl/algorithm/algorithm.h
@@ -27,7 +27,7 @@
#include <type_traits>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace algorithm_internal {
@@ -146,7 +146,7 @@ ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
ForwardIterator>());
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_ALGORITHM_ALGORITHM_H_
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 8eb10d7a..b7718206 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -46,6 +46,8 @@
#include <iterator>
#include <numeric>
#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -54,8 +56,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2018_06_20 {
-
+inline namespace lts_2018_12_18 {
namespace container_algorithm_internal {
// NOTE: it is important to defer to ADL lookup for building with C++ modules,
@@ -102,6 +103,17 @@ ContainerIter<C> c_begin(C& c) { return begin(c); }
template <typename C>
ContainerIter<C> c_end(C& c) { return end(c); }
+template <typename T>
+struct IsUnorderedContainer : std::false_type {};
+
+template <class Key, class T, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
+
+template <class Key, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>>
+ : std::true_type {};
+
} // namespace container_algorithm_internal
// PUBLIC API
@@ -315,7 +327,7 @@ container_algorithm_internal::ContainerDifferenceType<const C> c_count_if(
// c_mismatch()
//
-// Container-based version of the <algorithm> `std::mismatchf()` function to
+// Container-based version of the <algorithm> `std::mismatch()` function to
// return the first element where two ordered containers differ.
template <typename C1, typename C2>
container_algorithm_internal::ContainerIterPairType<C1, C2>
@@ -495,7 +507,7 @@ BidirectionalIterator c_copy_backward(const C& src,
// Container-based version of the <algorithm> `std::move()` function to move
// a container's elements into an iterator.
template <typename C, typename OutputIterator>
-OutputIterator c_move(C& src, OutputIterator dest) {
+OutputIterator c_move(C&& src, OutputIterator dest) {
return std::move(container_algorithm_internal::c_begin(src),
container_algorithm_internal::c_end(src), dest);
}
@@ -635,7 +647,7 @@ container_algorithm_internal::ContainerIter<C> c_generate_n(C& c, Size n,
// Note: `c_xx()` <algorithm> container versions for `remove()`, `remove_if()`,
// and `unique()` are omitted, because it's not clear whether or not such
-// functions should call erase their supplied sequences afterwards. Either
+// functions should call erase on their supplied sequences afterwards. Either
// behavior would be surprising for a different set of users.
//
@@ -1155,7 +1167,13 @@ bool c_includes(const C1& c1, const C2& c2, Compare&& comp) {
// Container-based version of the <algorithm> `std::set_union()` function
// to return an iterator containing the union of two containers; duplicate
// values are not copied into the output.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
return std::set_union(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
@@ -1165,7 +1183,13 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
// Overload of c_set_union() for performing a merge using a `comp` other than
// `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
Compare&& comp) {
return std::set_union(container_algorithm_internal::c_begin(c1),
@@ -1179,7 +1203,13 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
//
// Container-based version of the <algorithm> `std::set_intersection()` function
// to return an iterator containing the intersection of two containers.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_intersection(container_algorithm_internal::c_begin(c1),
@@ -1190,7 +1220,13 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2,
// Overload of c_set_intersection() for performing a merge using a `comp` other
// than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) {
return std::set_intersection(container_algorithm_internal::c_begin(c1),
@@ -1205,7 +1241,13 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2,
// Container-based version of the <algorithm> `std::set_difference()` function
// to return an iterator containing elements present in the first container but
// not in the second.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_difference(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_difference(container_algorithm_internal::c_begin(c1),
@@ -1216,7 +1258,13 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2,
// Overload of c_set_difference() for performing a merge using a `comp` other
// than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_difference(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) {
return std::set_difference(container_algorithm_internal::c_begin(c1),
@@ -1231,7 +1279,13 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2,
// Container-based version of the <algorithm> `std::set_symmetric_difference()`
// function to return an iterator containing elements present in either one
// container or the other, but not both.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_symmetric_difference(
@@ -1243,7 +1297,13 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
// Overload of c_set_symmetric_difference() for performing a merge using a
// `comp` other than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output,
Compare&& comp) {
@@ -1638,7 +1698,7 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first,
output_first, std::forward<BinaryOp>(op));
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_ALGORITHM_CONTAINER_H_
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc
index de66f146..1502b17f 100644
--- a/absl/algorithm/container_test.cc
+++ b/absl/algorithm/container_test.cc
@@ -636,6 +636,21 @@ TEST(MutatingTest, Move) {
Pointee(5)));
}
+TEST(MutatingTest, MoveWithRvalue) {
+ auto MakeRValueSrc = [] {
+ std::vector<std::unique_ptr<int>> src;
+ src.emplace_back(absl::make_unique<int>(1));
+ src.emplace_back(absl::make_unique<int>(2));
+ src.emplace_back(absl::make_unique<int>(3));
+ return src;
+ };
+
+ std::vector<std::unique_ptr<int>> dest = MakeRValueSrc();
+ absl::c_move(MakeRValueSrc(), std::back_inserter(dest));
+ EXPECT_THAT(dest, ElementsAre(Pointee(1), Pointee(2), Pointee(3), Pointee(1),
+ Pointee(2), Pointee(3)));
+}
+
TEST(MutatingTest, SwapRanges) {
std::vector<int> odds = {2, 4, 6};
std::vector<int> evens = {1, 3, 5};
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index d117a4fe..4566c697 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -19,6 +19,7 @@ load(
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
"ABSL_EXCEPTIONS_FLAG",
+ "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
)
package(default_visibility = ["//visibility:public"])
@@ -29,6 +30,7 @@ cc_library(
name = "spinlock_wait",
srcs = [
"internal/spinlock_akaros.inc",
+ "internal/spinlock_linux.inc",
"internal/spinlock_posix.inc",
"internal/spinlock_wait.cc",
"internal/spinlock_win32.inc",
@@ -73,7 +75,6 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
deps = [
":config",
- ":dynamic_annotations",
],
)
@@ -106,6 +107,7 @@ cc_library(
"internal/identity.h",
"internal/inline_variable.h",
"internal/invoke.h",
+ "internal/scheduling_mode.h",
],
copts = ABSL_DEFAULT_COPTS,
visibility = [
@@ -179,13 +181,13 @@ cc_library(
srcs = ["internal/throw_delegate.cc"],
hdrs = ["internal/throw_delegate.h"],
copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
visibility = [
"//absl:__subpackages__",
],
deps = [
":base",
":config",
- ":core_headers",
],
)
@@ -193,6 +195,7 @@ cc_test(
name = "throw_delegate_test",
srcs = ["throw_delegate_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":throw_delegate",
"@com_google_googletest//:gtest_main",
@@ -225,6 +228,7 @@ cc_library(
srcs = ["internal/exception_safety_testing.cc"],
hdrs = ["internal/exception_safety_testing.h"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":base",
":config",
@@ -232,7 +236,7 @@ cc_library(
"//absl/memory",
"//absl/meta:type_traits",
"//absl/strings",
- "//absl/types:optional",
+ "//absl/utility",
"@com_google_googletest//:gtest",
],
)
@@ -241,6 +245,7 @@ cc_test(
name = "exception_safety_testing_test",
srcs = ["exception_safety_testing_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":exception_safety_testing",
"//absl/memory",
@@ -299,6 +304,7 @@ cc_test(
size = "medium",
srcs = ["spinlock_test_common.cc"],
copts = ABSL_TEST_COPTS,
+ tags = ["no_test_wasm"],
deps = [
":base",
":core_headers",
@@ -309,6 +315,33 @@ cc_test(
)
cc_library(
+ name = "spinlock_benchmark_common",
+ testonly = 1,
+ srcs = ["internal/spinlock_benchmark.cc"],
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = [
+ "//absl/base:__pkg__",
+ ],
+ deps = [
+ ":base",
+ ":base_internal",
+ "//absl/synchronization",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+ alwayslink = 1,
+)
+
+cc_binary(
+ name = "spinlock_benchmark",
+ testonly = 1,
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":spinlock_benchmark_common",
+ ],
+)
+
+cc_library(
name = "endian",
hdrs = [
"internal/endian.h",
@@ -337,6 +370,9 @@ cc_test(
name = "config_test",
srcs = ["config_test.cc"],
copts = ABSL_TEST_COPTS,
+ tags = [
+ "no_test_wasm",
+ ],
deps = [
":config",
"//absl/synchronization:thread_pool",
@@ -348,6 +384,9 @@ cc_test(
name = "call_once_test",
srcs = ["call_once_test.cc"],
copts = ABSL_TEST_COPTS,
+ tags = [
+ "no_test_wasm",
+ ],
deps = [
":base",
":core_headers",
@@ -362,6 +401,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
deps = [
":base",
+ "//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
@@ -387,6 +427,7 @@ cc_test(
"//absl:windows": [],
"//conditions:default": ["-pthread"],
}),
+ tags = ["no_test_ios_x86_64"],
deps = [":malloc_internal"],
)
@@ -399,6 +440,9 @@ cc_test(
"//absl:windows": [],
"//conditions:default": ["-pthread"],
}),
+ tags = [
+ "no_test_wasm",
+ ],
deps = [
":base",
":core_headers",
@@ -419,3 +463,23 @@ cc_test(
"@com_github_google_benchmark//:benchmark_main",
],
)
+
+cc_library(
+ name = "bits",
+ hdrs = ["internal/bits.h"],
+ visibility = [
+ "//absl:__subpackages__",
+ ],
+ deps = [":core_headers"],
+)
+
+cc_test(
+ name = "bits_test",
+ size = "small",
+ srcs = ["internal/bits_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":bits",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 303533e2..212dd083 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -14,373 +14,401 @@
# limitations under the License.
#
-list(APPEND BASE_PUBLIC_HEADERS
- "attributes.h"
- "call_once.h"
- "casts.h"
- "config.h"
- "dynamic_annotations.h"
- "log_severity.h"
- "macros.h"
- "optimization.h"
- "policy_checks.h"
- "port.h"
- "thread_annotations.h"
+absl_cc_library(
+ NAME
+ spinlock_wait
+ HDRS
+ "internal/scheduling_mode.h"
+ "internal/spinlock_wait.h"
+ SRCS
+ "internal/spinlock_akaros.inc"
+ "internal/spinlock_linux.inc"
+ "internal/spinlock_posix.inc"
+ "internal/spinlock_wait.cc"
+ "internal/spinlock_win32.inc"
+ DEPS
+ absl::core_headers
)
+absl_cc_library(
+ NAME
+ config
+ HDRS
+ "config.h"
+ "policy_checks.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ dynamic_annotations
+ HDRS
+ "dynamic_annotations.h"
+ SRCS
+ "dynamic_annotations.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEFINES
+ "__CLANG_SUPPORT_DYN_ANNOTATION__"
+ PUBLIC
+)
-list(APPEND BASE_INTERNAL_HEADERS
- "internal/atomic_hook.h"
- "internal/cycleclock.h"
- "internal/direct_mmap.h"
- "internal/endian.h"
- "internal/exception_testing.h"
- "internal/exception_safety_testing.h"
- "internal/hide_ptr.h"
- "internal/identity.h"
- "internal/invoke.h"
- "internal/inline_variable.h"
- "internal/low_level_alloc.h"
- "internal/low_level_scheduling.h"
- "internal/per_thread_tls.h"
- "internal/pretty_function.h"
- "internal/raw_logging.h"
- "internal/scheduling_mode.h"
- "internal/spinlock.h"
- "internal/spinlock_wait.h"
- "internal/sysinfo.h"
- "internal/thread_identity.h"
- "internal/throw_delegate.h"
- "internal/tsan_mutex_interface.h"
- "internal/unaligned_access.h"
- "internal/unscaledcycleclock.h"
+absl_cc_library(
+ NAME
+ core_headers
+ HDRS
+ "attributes.h"
+ "macros.h"
+ "optimization.h"
+ "port.h"
+ "thread_annotations.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ PUBLIC
)
+absl_cc_library(
+ NAME
+ malloc_internal
+ HDRS
+ "internal/direct_mmap.h"
+ "internal/low_level_alloc.h"
+ SRCS
+ "internal/low_level_alloc.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base
+ absl::config
+ absl::core_headers
+ absl::dynamic_annotations
+ absl::spinlock_wait
+)
-# absl_base main library
-list(APPEND BASE_SRC
- "internal/cycleclock.cc"
- "internal/raw_logging.cc"
- "internal/spinlock.cc"
- "internal/sysinfo.cc"
- "internal/thread_identity.cc"
- "internal/unscaledcycleclock.cc"
- "internal/low_level_alloc.cc"
- ${BASE_PUBLIC_HEADERS}
- ${BASE_INTERNAL_HEADERS}
+absl_cc_library(
+ NAME
+ base_internal
+ HDRS
+ "internal/hide_ptr.h"
+ "internal/identity.h"
+ "internal/inline_variable.h"
+ "internal/invoke.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
)
-absl_library(
- TARGET
- absl_base
- SOURCES
- ${BASE_SRC}
- PUBLIC_LIBRARIES
- absl_dynamic_annotations
- absl_spinlock_wait
- EXPORT_NAME
+absl_cc_library(
+ NAME
base
+ HDRS
+ "call_once.h"
+ "casts.h"
+ "internal/atomic_hook.h"
+ "internal/cycleclock.h"
+ "internal/low_level_scheduling.h"
+ "internal/per_thread_tls.h"
+ "internal/raw_logging.h"
+ "internal/spinlock.h"
+ "internal/sysinfo.h"
+ "internal/thread_identity.h"
+ "internal/tsan_mutex_interface.h"
+ "internal/unscaledcycleclock.h"
+ "log_severity.h"
+ SRCS
+ "internal/cycleclock.cc"
+ "internal/raw_logging.cc"
+ "internal/spinlock.cc"
+ "internal/sysinfo.cc"
+ "internal/thread_identity.cc"
+ "internal/unscaledcycleclock.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base_internal
+ absl::config
+ absl::core_headers
+ absl::dynamic_annotations
+ absl::spinlock_wait
+ PUBLIC
)
-# throw delegate library
-set(THROW_DELEGATE_SRC "internal/throw_delegate.cc")
-
-absl_library(
- TARGET
- absl_throw_delegate
- SOURCES
- ${THROW_DELEGATE_SRC}
- PUBLIC_LIBRARIES
- ${THROW_DELEGATE_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_EXCEPTIONS_FLAG}
- EXPORT_NAME
+absl_cc_library(
+ NAME
throw_delegate
+ HDRS
+ "internal/throw_delegate.h"
+ SRCS
+ "internal/throw_delegate.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ ${ABSL_EXCEPTIONS_FLAG}
+ DEPS
+ absl::base
+)
+
+absl_cc_library(
+ NAME
+ exception_testing
+ HDRS
+ "internal/exception_testing.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ gtest
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ pretty_function
+ HDRS
+ "internal/pretty_function.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
)
-if(BUILD_TESTING)
- # exception-safety testing library
- set(EXCEPTION_SAFETY_TESTING_SRC
+absl_cc_library(
+ NAME
+ exception_safety_testing
+ HDRS
"internal/exception_safety_testing.h"
+ SRCS
"internal/exception_safety_testing.cc"
- )
- set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES
- ${ABSL_TEST_COMMON_LIBRARIES}
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ ${ABSL_EXCEPTIONS_FLAG}
+ DEPS
absl::base
+ absl::config
+ absl::pretty_function
absl::memory
absl::meta
absl::strings
- absl::optional
+ absl::utility
gtest
- )
-
-absl_library(
- TARGET
- absl_base_internal_exception_safety_testing
- SOURCES
- ${EXCEPTION_SAFETY_TESTING_SRC}
- PUBLIC_LIBRARIES
- ${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_EXCEPTIONS_FLAG}
-)
-endif()
-
-
-# dynamic_annotations library
-set(DYNAMIC_ANNOTATIONS_SRC "dynamic_annotations.cc")
-
-absl_library(
- TARGET
- absl_dynamic_annotations
- SOURCES
- ${DYNAMIC_ANNOTATIONS_SRC}
-)
-
-
-# spinlock_wait library
-set(SPINLOCK_WAIT_SRC "internal/spinlock_wait.cc")
-
-absl_library(
- TARGET
- absl_spinlock_wait
- SOURCES
- ${SPINLOCK_WAIT_SRC}
-)
-
-
-# malloc_internal library
-list(APPEND MALLOC_INTERNAL_SRC
- "internal/low_level_alloc.cc"
+ TESTONLY
)
-absl_library(
- TARGET
- absl_malloc_internal
- SOURCES
- ${MALLOC_INTERNAL_SRC}
- PUBLIC_LIBRARIES
- absl_dynamic_annotations
+absl_cc_test(
+ NAME
+ absl_exception_safety_testing_test
+ SRCS
+ "exception_safety_testing_test.cc"
+ COPTS
+ ${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::exception_safety_testing
+ absl::memory
+ gtest_main
)
-
-
-#
-## TESTS
-#
-
-# call once test
-set(ATOMIC_HOOK_TEST_SRC "internal/atomic_hook_test.cc")
-set(ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
atomic_hook_test
- SOURCES
- ${ATOMIC_HOOK_TEST_SRC}
- PUBLIC_LIBRARIES
- ${ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# call once test
-set(CALL_ONCE_TEST_SRC "call_once_test.cc")
-set(CALL_ONCE_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- call_once_test
- SOURCES
- ${CALL_ONCE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${CALL_ONCE_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "internal/atomic_hook_test.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ gtest_main
)
-
-# test bit_cast_test
-set(BIT_CAST_TEST_SRC "bit_cast_test.cc")
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
bit_cast_test
- SOURCES
- ${BIT_CAST_TEST_SRC}
+ SRCS
+ "bit_cast_test.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ gtest_main
)
-
-# test absl_throw_delegate_test
-set(THROW_DELEGATE_TEST_SRC "throw_delegate_test.cc")
-set(THROW_DELEGATE_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
throw_delegate_test
- SOURCES
- ${THROW_DELEGATE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${THROW_DELEGATE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test invoke_test
-set(INVOKE_TEST_SRC "invoke_test.cc")
-set(INVOKE_TEST_PUBLIC_LIBRARIES absl::strings)
-
-absl_test(
- TARGET
- invoke_test
- SOURCES
- ${INVOKE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INVOKE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test inline_variable_test
-list(APPEND INLINE_VARIABLE_TEST_SRC
- "internal/inline_variable_testing.h"
- "inline_variable_test.cc"
- "inline_variable_test_a.cc"
- "inline_variable_test_b.cc"
+ SRCS
+ "throw_delegate_test.cc"
+ DEPS
+ absl::base
+ absl_internal_throw_delegate
+ gtest_main
)
-set(INLINE_VARIABLE_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
inline_variable_test
- SOURCES
- ${INLINE_VARIABLE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INLINE_VARIABLE_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "internal/inline_variable_testing.h"
+ "inline_variable_test.cc"
+ "inline_variable_test_a.cc"
+ "inline_variable_test_b.cc"
+ DEPS
+ absl::base_internal
+ gtest_main
)
+absl_cc_test(
+ NAME
+ invoke_test
+ SRCS
+ "invoke_test.cc"
+ DEPS
+ absl::base_internal
+ absl::memory
+ absl::strings
+ gmock
+ gtest_main
+)
-# test spinlock_test_common
-set(SPINLOCK_TEST_COMMON_SRC "spinlock_test_common.cc")
-set(SPINLOCK_TEST_COMMON_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
+absl_cc_library(
+ NAME
spinlock_test_common
- SOURCES
- ${SPINLOCK_TEST_COMMON_SRC}
- PUBLIC_LIBRARIES
- ${SPINLOCK_TEST_COMMON_PUBLIC_LIBRARIES}
+ SRCS
+ "spinlock_test_common.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::spinlock_wait
+ absl::synchronization
+ gtest
+ TESTONLY
)
-
-# test spinlock_test
-set(SPINLOCK_TEST_SRC "spinlock_test_common.cc")
-set(SPINLOCK_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
+# On bazel BUILD this target use "alwayslink = 1" which is not implemented here
+absl_cc_test(
+ NAME
spinlock_test
- SOURCES
- ${SPINLOCK_TEST_SRC}
- PUBLIC_LIBRARIES
- ${SPINLOCK_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "spinlock_test_common.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::spinlock_wait
+ absl::synchronization
+ gtest_main
)
+absl_cc_library(
+ NAME
+ endian
+ HDRS
+ "internal/endian.h"
+ "internal/unaligned_access.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ absl::core_headers
+ PUBLIC
+)
-# test endian_test
-set(ENDIAN_TEST_SRC "internal/endian_test.cc")
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
endian_test
- SOURCES
- ${ENDIAN_TEST_SRC}
+ SRCS
+ "internal/endian_test.cc"
+ DEPS
+ absl::base
+ absl::config
+ absl::endian
+ gtest_main
)
-
-# test config_test
-set(CONFIG_TEST_SRC "config_test.cc")
-set(CONFIG_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
config_test
- SOURCES
- ${CONFIG_TEST_SRC}
- PUBLIC_LIBRARIES
- ${CONFIG_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "config_test.cc"
+ DEPS
+ absl::config
+ absl::synchronization
+ gtest_main
)
+absl_cc_test(
+ NAME
+ call_once_test
+ SRCS
+ "call_once_test.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::synchronization
+ gtest_main
+)
-# test raw_logging_test
-set(RAW_LOGGING_TEST_SRC "raw_logging_test.cc")
-set(RAW_LOGGING_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
raw_logging_test
- SOURCES
- ${RAW_LOGGING_TEST_SRC}
- PUBLIC_LIBRARIES
- ${RAW_LOGGING_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "raw_logging_test.cc"
+ DEPS
+ absl::base
+ absl::strings
+ gtest_main
)
-
-# test sysinfo_test
-set(SYSINFO_TEST_SRC "internal/sysinfo_test.cc")
-set(SYSINFO_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
sysinfo_test
- SOURCES
- ${SYSINFO_TEST_SRC}
- PUBLIC_LIBRARIES
- ${SYSINFO_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "internal/sysinfo_test.cc"
+ DEPS
+ absl::base
+ absl::synchronization
+ gtest_main
)
-
-# test low_level_alloc_test
-set(LOW_LEVEL_ALLOC_TEST_SRC "internal/low_level_alloc_test.cc")
-set(LOW_LEVEL_ALLOC_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
low_level_alloc_test
- SOURCES
- ${LOW_LEVEL_ALLOC_TEST_SRC}
- PUBLIC_LIBRARIES
- ${LOW_LEVEL_ALLOC_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "internal/low_level_alloc_test.cc"
+ DEPS
+ absl::malloc_internal
+ Threads::Threads
)
-
-# test thread_identity_test
-set(THREAD_IDENTITY_TEST_SRC "internal/thread_identity_test.cc")
-set(THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
thread_identity_test
- SOURCES
- ${THREAD_IDENTITY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "internal/thread_identity_test.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::synchronization
+ Threads::Threads
+ gtest_main
)
-#test exceptions_safety_testing_test
-set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc")
-set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES
- absl::base
- absl_base_internal_exception_safety_testing
- absl::memory
- absl::meta
- absl::strings
- absl::optional
+absl_cc_library(
+ NAME
+ bits
+ HDRS
+ "internal/bits.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
)
-absl_test(
- TARGET
- absl_exception_safety_testing_test
- SOURCES
- ${EXCEPTION_SAFETY_TESTING_TEST_SRC}
- PUBLIC_LIBRARIES
- ${EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_EXCEPTIONS_FLAG}
+absl_cc_test(
+ NAME
+ bits_test
+ SRCS
+ "internal/bits_test.cc"
+ DEPS
+ absl::bits
+ gtest_main
)
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index b1883b6d..291ad89e 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -100,7 +100,7 @@
// ABSL_PRINTF_ATTRIBUTE
// ABSL_SCANF_ATTRIBUTE
//
-// Tells the compiler to perform `printf` format std::string checking if the
+// Tells the compiler to perform `printf` format string checking if the
// compiler supports it; see the 'format' attribute in
// <http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>.
//
@@ -155,7 +155,12 @@
// ABSL_ATTRIBUTE_WEAK
//
// Tags a function as weak for the purposes of compilation and linking.
-#if ABSL_HAVE_ATTRIBUTE(weak) || (defined(__GNUC__) && !defined(__clang__))
+// Weak attributes currently do not work properly in LLVM's Windows backend,
+// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
+// for futher information.
+#if (ABSL_HAVE_ATTRIBUTE(weak) || \
+ (defined(__GNUC__) && !defined(__clang__))) && \
+ !(defined(__llvm__) && defined(_WIN32))
#undef ABSL_ATTRIBUTE_WEAK
#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
#define ABSL_HAVE_ATTRIBUTE_WEAK 1
@@ -296,13 +301,13 @@
// ABSL_HAVE_ATTRIBUTE_SECTION
//
-// Indicates whether labeled sections are supported. Labeled sections are not
-// supported on Darwin/iOS.
+// 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__)
+ !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK
#define ABSL_HAVE_ATTRIBUTE_SECTION 1
// ABSL_ATTRIBUTE_SECTION
@@ -397,17 +402,28 @@
// ABSL_MUST_USE_RESULT
//
-// Tells the compiler to warn about unused return values for functions declared
-// with this macro. The macro must appear as the very first part of a function
-// declaration or definition:
+// Tells the compiler to warn about unused results.
//
-// Example:
+// 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:
//
-// This placement has the broadest compatibility with GCC, Clang, and MSVC, with
-// both defs and decls, and with GCC-style attributes, MSVC declspec, C++11
-// and C++17 attributes.
+// 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.
@@ -494,14 +510,27 @@
#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 or optimizing away variables
-// that appear unused.
+// Prevents the compiler from complaining about variables that appear unused.
#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
#undef ABSL_ATTRIBUTE_UNUSED
#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__))
diff --git a/absl/base/bit_cast_test.cc b/absl/base/bit_cast_test.cc
index 71bb368f..5af036df 100644
--- a/absl/base/bit_cast_test.cc
+++ b/absl/base/bit_cast_test.cc
@@ -22,7 +22,7 @@
#include "absl/base/macros.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
template <int N>
@@ -105,5 +105,5 @@ TEST(BitCast, Double) {
}
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 37b6608a..aea9197b 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -39,7 +39,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
class once_flag;
@@ -151,12 +151,8 @@ void CallOnceImpl(std::atomic<uint32_t>* control,
old_control != kOnceRunning &&
old_control != kOnceWaiter &&
old_control != kOnceDone) {
- ABSL_RAW_LOG(
- FATAL,
- "Unexpected value for control word: %lx. Either the control word "
- "has non-static storage duration (where GoogleOnceDynamic might "
- "be appropriate), or there's been a memory corruption.",
- static_cast<unsigned long>(old_control)); // NOLINT
+ ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",
+ static_cast<unsigned long>(old_control)); // NOLINT
}
}
#endif // NDEBUG
@@ -212,7 +208,7 @@ void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) {
}
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_CALL_ONCE_H_
diff --git a/absl/base/call_once_test.cc b/absl/base/call_once_test.cc
index 43a71656..4d98a405 100644
--- a/absl/base/call_once_test.cc
+++ b/absl/base/call_once_test.cc
@@ -22,7 +22,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
absl::once_flag once;
@@ -100,5 +100,5 @@ TEST(CallOnceTest, ExecutionCount) {
}
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/casts.h b/absl/base/casts.h
index cd07d8f6..bba623b4 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -25,12 +25,36 @@
#define ABSL_BASE_CASTS_H_
#include <cstring>
+#include <memory>
#include <type_traits>
#include "absl/base/internal/identity.h"
+#include "absl/base/macros.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
+
+namespace internal_casts {
+
+// NOTE: Not a fully compliant implementation of `std::is_trivially_copyable`.
+// TODO(calabrese) Branch on implementations that directly provide
+// `std::is_trivially_copyable`, create a more rigorous workaround, and publicly
+// expose in meta/type_traits.
+template <class T>
+struct is_trivially_copyable
+ : std::integral_constant<
+ bool, std::is_destructible<T>::value&& __has_trivial_destructor(T) &&
+ __has_trivial_copy(T) && __has_trivial_assign(T)> {};
+
+template <class Dest, class Source>
+struct is_bitcastable
+ : std::integral_constant<bool,
+ sizeof(Dest) == sizeof(Source) &&
+ is_trivially_copyable<Source>::value &&
+ is_trivially_copyable<Dest>::value &&
+ std::is_default_constructible<Dest>::value> {};
+
+} // namespace internal_casts
// implicit_cast()
//
@@ -82,7 +106,7 @@ inline namespace lts_2018_06_20 {
//
// Such implicit cast chaining may be useful within template logic.
template <typename To>
-inline To implicit_cast(typename absl::internal::identity_t<To> to) {
+constexpr To implicit_cast(typename absl::internal::identity_t<To> to) {
return to;
}
@@ -126,7 +150,32 @@ inline To implicit_cast(typename absl::internal::identity_t<To> to) {
// and reading its bits back using a different type. A `bit_cast()` avoids this
// issue by implementing its casts using `memcpy()`, which avoids introducing
// this undefined behavior.
-template <typename Dest, typename Source>
+//
+// NOTE: The requirements here are more strict than the bit_cast of standard
+// proposal p0476 due to the need for workarounds and lack of intrinsics.
+// Specifically, this implementation also requires `Dest` to be
+// default-constructible.
+template <
+ typename Dest, typename Source,
+ typename std::enable_if<internal_casts::is_bitcastable<Dest, Source>::value,
+ int>::type = 0>
+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;
+}
+
+// NOTE: This overload is only picked if the requirements of bit_cast are not
+// met. It is therefore UB, but is provided temporarily as previous versions of
+// this function template were unchecked. Do not use this in new code.
+template <
+ typename Dest, typename Source,
+ typename std::enable_if<
+ !internal_casts::is_bitcastable<Dest, Source>::value, int>::type = 0>
+ABSL_DEPRECATED(
+ "absl::bit_cast type requirements were violated. Update the types being "
+ "used such that they are the same size and are both TriviallyCopyable.")
inline Dest bit_cast(const Source& source) {
static_assert(sizeof(Dest) == sizeof(Source),
"Source and destination types should have equal sizes.");
@@ -136,7 +185,7 @@ inline Dest bit_cast(const Source& source) {
return dest;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_CASTS_H_
diff --git a/absl/base/config.h b/absl/base/config.h
index 2f5f1595..db4c4539 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -139,12 +139,18 @@
#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. Also, Xcode 9.3 started disallowing
-// `thread_local` for 32-bit iOS simulator targeting iOS 9.x.
-// `__has_feature` is only supported by Clang so it has be inside
+// 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 __has_feature unreliable there.
+//
+// Otherwise, `__has_feature` is only supported by Clang so it has be inside
// `defined(__APPLE__)` check.
-#if __has_feature(cxx_thread_local)
+#if __has_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__)
@@ -199,7 +205,7 @@
#define ABSL_HAVE_INTRINSIC_INT128 1
#elif defined(__CUDACC__)
// __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a
-// std::string explaining that it has been removed starting with CUDA 9. We use
+// 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
@@ -268,7 +274,8 @@
#error ABSL_HAVE_MMAP cannot be directly set
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
- defined(__wasm__) || defined(__Fuchsia__)
+ defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
+ defined(__ASYLO__)
#define ABSL_HAVE_MMAP 1
#endif
@@ -322,6 +329,8 @@
#define ABSL_HAVE_ALARM 1
#elif defined(_MSC_VER)
// feature tests for Microsoft's library
+#elif defined(__EMSCRIPTEN__)
+// emscripten doesn't support signals
#elif defined(__native_client__)
#else
// other standard libraries
@@ -356,6 +365,18 @@
#error "absl endian detection needs to be set up for your compiler"
#endif
+// MacOS 10.13 doesn't let you use <any>, <optional>, or <variant> even though
+// the headers exist and are publicly noted to work. See
+// https://github.com/abseil/abseil-cpp/issues/207 and
+// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes
+#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \
+ defined(__MAC_OS_X_VERSION_MIN_REQUIRED__) && \
+ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400
+#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 1
+#else
+#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 0
+#endif
+
// ABSL_HAVE_STD_ANY
//
// Checks whether C++17 std::any is available by checking whether <any> exists.
@@ -364,7 +385,8 @@
#endif
#ifdef __has_include
-#if __has_include(<any>) && __cplusplus >= 201703L
+#if __has_include(<any>) && __cplusplus >= 201703L && \
+ ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES
#define ABSL_HAVE_STD_ANY 1
#endif
#endif
@@ -377,7 +399,8 @@
#endif
#ifdef __has_include
-#if __has_include(<optional>) && __cplusplus >= 201703L
+#if __has_include(<optional>) && __cplusplus >= 201703L && \
+ ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES
#define ABSL_HAVE_STD_OPTIONAL 1
#endif
#endif
@@ -390,7 +413,8 @@
#endif
#ifdef __has_include
-#if __has_include(<variant>) && __cplusplus >= 201703L
+#if __has_include(<variant>) && __cplusplus >= 201703L && \
+ ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES
#define ABSL_HAVE_STD_VARIANT 1
#endif
#endif
@@ -414,14 +438,21 @@
// <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`,
-// `std::string_view`.
+// TODO(zhangxy): fix tests before enabling aliasing for `std::any`.
#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __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
+#define ABSL_HAVE_STD_STRING_VIEW 1
+#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
#endif // ABSL_BASE_CONFIG_H_
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
index 97c8d6f8..7518264d 100644
--- a/absl/base/exception_safety_testing_test.cc
+++ b/absl/base/exception_safety_testing_test.cc
@@ -38,7 +38,7 @@ template <typename F>
void ExpectNoThrow(const F& f) {
try {
f();
- } catch (TestException e) {
+ } catch (const TestException& e) {
ADD_FAILURE() << "Unexpected exception thrown from " << e.what();
}
}
@@ -179,7 +179,7 @@ TEST(ThrowingValueTest, ThrowingStreamOps) {
}
// Tests the operator<< of ThrowingValue by forcing ConstructorTracker to emit
-// a nonfatal failure that contains the std::string representation of the Thrower
+// a nonfatal failure that contains the string representation of the Thrower
TEST(ThrowingValueTest, StreamOpsOutput) {
using ::testing::TypeSpec;
exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
@@ -548,21 +548,21 @@ TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) {
// Test that providing operation and inveriants still does not allow for the
// the invocation of .Test() and .Test(op) because it lacks a factory
auto without_fac =
- testing::MakeExceptionSafetyTester().WithOperation(op).WithInvariants(
+ testing::MakeExceptionSafetyTester().WithOperation(op).WithContracts(
inv, testing::strong_guarantee);
EXPECT_FALSE(HasNullaryTest(without_fac));
EXPECT_FALSE(HasUnaryTest(without_fac));
- // Test that providing invariants and factory allows the invocation of
+ // Test that providing contracts and factory allows the invocation of
// .Test(op) but does not allow for .Test() because it lacks an operation
auto without_op = testing::MakeExceptionSafetyTester()
- .WithInvariants(inv, testing::strong_guarantee)
+ .WithContracts(inv, testing::strong_guarantee)
.WithFactory(fac);
EXPECT_FALSE(HasNullaryTest(without_op));
EXPECT_TRUE(HasUnaryTest(without_op));
// Test that providing operation and factory still does not allow for the
- // the invocation of .Test() and .Test(op) because it lacks invariants
+ // the invocation of .Test() and .Test(op) because it lacks contracts
auto without_inv =
testing::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac);
EXPECT_FALSE(HasNullaryTest(without_inv));
@@ -577,7 +577,7 @@ std::unique_ptr<ExampleStruct> ExampleFunctionFactory() {
void ExampleFunctionOperation(ExampleStruct*) {}
-testing::AssertionResult ExampleFunctionInvariant(ExampleStruct*) {
+testing::AssertionResult ExampleFunctionContract(ExampleStruct*) {
return testing::AssertionSuccess();
}
@@ -593,16 +593,16 @@ struct {
struct {
testing::AssertionResult operator()(ExampleStruct* example_struct) const {
- return ExampleFunctionInvariant(example_struct);
+ return ExampleFunctionContract(example_struct);
}
-} example_struct_invariant;
+} example_struct_contract;
auto example_lambda_factory = []() { return ExampleFunctionFactory(); };
auto example_lambda_operation = [](ExampleStruct*) {};
-auto example_lambda_invariant = [](ExampleStruct* example_struct) {
- return ExampleFunctionInvariant(example_struct);
+auto example_lambda_contract = [](ExampleStruct* example_struct) {
+ return ExampleFunctionContract(example_struct);
};
// Testing that function references, pointers, structs with operator() and
@@ -612,28 +612,28 @@ TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) {
EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithFactory(ExampleFunctionFactory)
.WithOperation(ExampleFunctionOperation)
- .WithInvariants(ExampleFunctionInvariant)
+ .WithContracts(ExampleFunctionContract)
.Test());
// function pointer
EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithFactory(&ExampleFunctionFactory)
.WithOperation(&ExampleFunctionOperation)
- .WithInvariants(&ExampleFunctionInvariant)
+ .WithContracts(&ExampleFunctionContract)
.Test());
// struct
EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithFactory(example_struct_factory)
.WithOperation(example_struct_operation)
- .WithInvariants(example_struct_invariant)
+ .WithContracts(example_struct_contract)
.Test());
// lambda
EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithFactory(example_lambda_factory)
.WithOperation(example_lambda_operation)
- .WithInvariants(example_lambda_invariant)
+ .WithContracts(example_lambda_contract)
.Test());
}
@@ -658,9 +658,9 @@ struct {
} invoker;
auto tester =
- testing::MakeExceptionSafetyTester().WithOperation(invoker).WithInvariants(
+ testing::MakeExceptionSafetyTester().WithOperation(invoker).WithContracts(
CheckNonNegativeInvariants);
-auto strong_tester = tester.WithInvariants(testing::strong_guarantee);
+auto strong_tester = tester.WithContracts(testing::strong_guarantee);
struct FailsBasicGuarantee : public NonNegative {
void operator()() {
@@ -690,7 +690,7 @@ TEST(ExceptionCheckTest, StrongGuaranteeFailure) {
EXPECT_FALSE(strong_tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
}
-struct BasicGuaranteeWithExtraInvariants : public NonNegative {
+struct BasicGuaranteeWithExtraContracts : public NonNegative {
// After operator(), i is incremented. If operator() throws, i is set to 9999
void operator()() {
int old_i = i;
@@ -701,21 +701,21 @@ struct BasicGuaranteeWithExtraInvariants : public NonNegative {
static constexpr int kExceptionSentinel = 9999;
};
-constexpr int BasicGuaranteeWithExtraInvariants::kExceptionSentinel;
+constexpr int BasicGuaranteeWithExtraContracts::kExceptionSentinel;
-TEST(ExceptionCheckTest, BasicGuaranteeWithExtraInvariants) {
+TEST(ExceptionCheckTest, BasicGuaranteeWithExtraContracts) {
auto tester_with_val =
- tester.WithInitialValue(BasicGuaranteeWithExtraInvariants{});
+ tester.WithInitialValue(BasicGuaranteeWithExtraContracts{});
EXPECT_TRUE(tester_with_val.Test());
EXPECT_TRUE(
tester_with_val
- .WithInvariants([](BasicGuaranteeWithExtraInvariants* o) {
- if (o->i == BasicGuaranteeWithExtraInvariants::kExceptionSentinel) {
+ .WithContracts([](BasicGuaranteeWithExtraContracts* o) {
+ if (o->i == BasicGuaranteeWithExtraContracts::kExceptionSentinel) {
return testing::AssertionSuccess();
}
return testing::AssertionFailure()
<< "i should be "
- << BasicGuaranteeWithExtraInvariants::kExceptionSentinel
+ << BasicGuaranteeWithExtraContracts::kExceptionSentinel
<< ", but is " << o->i;
})
.Test());
@@ -740,7 +740,7 @@ struct HasReset : public NonNegative {
void reset() { i = 0; }
};
-testing::AssertionResult CheckHasResetInvariants(HasReset* h) {
+testing::AssertionResult CheckHasResetContracts(HasReset* h) {
h->reset();
return testing::AssertionResult(h->i == 0);
}
@@ -759,17 +759,29 @@ TEST(ExceptionCheckTest, ModifyingChecker) {
};
EXPECT_FALSE(tester.WithInitialValue(FollowsBasicGuarantee{})
- .WithInvariants(set_to_1000, is_1000)
+ .WithContracts(set_to_1000, is_1000)
.Test());
EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{})
- .WithInvariants(increment)
+ .WithContracts(increment)
.Test());
EXPECT_TRUE(testing::MakeExceptionSafetyTester()
.WithInitialValue(HasReset{})
- .WithInvariants(CheckHasResetInvariants)
+ .WithContracts(CheckHasResetContracts)
.Test(invoker));
}
+TEST(ExceptionSafetyTesterTest, ResetsCountdown) {
+ auto test =
+ testing::MakeExceptionSafetyTester()
+ .WithInitialValue(ThrowingValue<>())
+ .WithContracts([](ThrowingValue<>*) { return AssertionSuccess(); })
+ .WithOperation([](ThrowingValue<>*) {});
+ ASSERT_TRUE(test.Test());
+ // If the countdown isn't reset because there were no exceptions thrown, then
+ // this will fail with a termination from an unhandled exception
+ EXPECT_TRUE(test.Test());
+}
+
struct NonCopyable : public NonNegative {
NonCopyable(const NonCopyable&) = delete;
NonCopyable() : NonNegative{0} {}
@@ -799,7 +811,7 @@ TEST(ExceptionCheckTest, NonEqualityComparable) {
return testing::AssertionResult(nec->i == NonEqualityComparable().i);
};
auto strong_nec_tester = tester.WithInitialValue(NonEqualityComparable{})
- .WithInvariants(nec_is_strong);
+ .WithContracts(nec_is_strong);
EXPECT_TRUE(strong_nec_tester.Test());
EXPECT_FALSE(strong_nec_tester.Test(
@@ -833,14 +845,14 @@ struct {
testing::AssertionResult operator()(ExhaustivenessTester<T>*) const {
return testing::AssertionSuccess();
}
-} CheckExhaustivenessTesterInvariants;
+} CheckExhaustivenessTesterContracts;
template <typename T>
unsigned char ExhaustivenessTester<T>::successes = 0;
TEST(ExceptionCheckTest, Exhaustiveness) {
auto exhaust_tester = testing::MakeExceptionSafetyTester()
- .WithInvariants(CheckExhaustivenessTesterInvariants)
+ .WithContracts(CheckExhaustivenessTesterContracts)
.WithOperation(invoker);
EXPECT_TRUE(
@@ -849,7 +861,7 @@ TEST(ExceptionCheckTest, Exhaustiveness) {
EXPECT_TRUE(
exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{})
- .WithInvariants(testing::strong_guarantee)
+ .WithContracts(testing::strong_guarantee)
.Test());
EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF);
}
@@ -931,8 +943,8 @@ TEST(ThrowingValueTraitsTest, RelationalOperators) {
}
TEST(ThrowingAllocatorTraitsTest, Assignablility) {
- EXPECT_TRUE(std::is_move_assignable<ThrowingAllocator<int>>::value);
- EXPECT_TRUE(std::is_copy_assignable<ThrowingAllocator<int>>::value);
+ EXPECT_TRUE(absl::is_move_assignable<ThrowingAllocator<int>>::value);
+ EXPECT_TRUE(absl::is_copy_assignable<ThrowingAllocator<int>>::value);
EXPECT_TRUE(std::is_nothrow_move_assignable<ThrowingAllocator<int>>::value);
EXPECT_TRUE(std::is_nothrow_copy_assignable<ThrowingAllocator<int>>::value);
}
diff --git a/absl/base/inline_variable_test.cc b/absl/base/inline_variable_test.cc
index b34aebd8..b968b10f 100644
--- a/absl/base/inline_variable_test.cc
+++ b/absl/base/inline_variable_test.cc
@@ -20,7 +20,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace inline_variable_testing_internal {
namespace {
@@ -60,5 +60,5 @@ TEST(InlineVariableTest, FunPtrType) {
} // namespace
} // namespace inline_variable_testing_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/inline_variable_test_a.cc b/absl/base/inline_variable_test_a.cc
index 0ea363af..a51b1d81 100644
--- a/absl/base/inline_variable_test_a.cc
+++ b/absl/base/inline_variable_test_a.cc
@@ -15,7 +15,7 @@
#include "absl/base/internal/inline_variable_testing.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace inline_variable_testing_internal {
const Foo& get_foo_a() { return inline_variable_foo; }
@@ -23,5 +23,5 @@ const Foo& get_foo_a() { return inline_variable_foo; }
const int& get_int_a() { return inline_variable_int; }
} // namespace inline_variable_testing_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/inline_variable_test_b.cc b/absl/base/inline_variable_test_b.cc
index 32704cf1..5041e20a 100644
--- a/absl/base/inline_variable_test_b.cc
+++ b/absl/base/inline_variable_test_b.cc
@@ -15,7 +15,7 @@
#include "absl/base/internal/inline_variable_testing.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace inline_variable_testing_internal {
const Foo& get_foo_b() { return inline_variable_foo; }
@@ -23,5 +23,5 @@ const Foo& get_foo_b() { return inline_variable_foo; }
const int& get_int_b() { return inline_variable_int; }
} // namespace inline_variable_testing_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h
index 7d0ee2fa..58ddf272 100644
--- a/absl/base/internal/atomic_hook.h
+++ b/absl/base/internal/atomic_hook.h
@@ -28,7 +28,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
template <typename T>
@@ -161,7 +161,7 @@ class AtomicHook<ReturnType (*)(Args...)> {
#undef ABSL_HAVE_WORKING_ATOMIC_POINTER
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h
new file mode 100644
index 00000000..29657426
--- /dev/null
+++ b/absl/base/internal/bits.h
@@ -0,0 +1,195 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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.
+
+#ifndef ABSL_BASE_INTERNAL_BITS_H_
+#define ABSL_BASE_INTERNAL_BITS_H_
+
+// This file contains bitwise ops which are implementation details of various
+// absl libraries.
+
+#include <cstdint>
+
+// Clang on Windows has __builtin_clzll; otherwise we need to use the
+// windows intrinsic functions.
+#if defined(_MSC_VER)
+#include <intrin.h>
+#if defined(_M_X64)
+#pragma intrinsic(_BitScanReverse64)
+#pragma intrinsic(_BitScanForward64)
+#endif
+#pragma intrinsic(_BitScanReverse)
+#pragma intrinsic(_BitScanForward)
+#endif
+
+#include "absl/base/attributes.h"
+
+#if defined(_MSC_VER)
+// We can achieve something similar to attribute((always_inline)) with MSVC by
+// using the __forceinline keyword, however this is not perfect. MSVC is
+// much less aggressive about inlining, and even with the __forceinline keyword.
+#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline
+#else
+// Use default attribute inline.
+#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE
+#endif
+
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace base_internal {
+
+ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) {
+ int zeroes = 60;
+ if (n >> 32) zeroes -= 32, n >>= 32;
+ if (n >> 16) zeroes -= 16, n >>= 16;
+ if (n >> 8) zeroes -= 8, n >>= 8;
+ if (n >> 4) zeroes -= 4, n >>= 4;
+ return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
+}
+
+ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
+#if defined(_MSC_VER) && defined(_M_X64)
+ // MSVC does not have __buitin_clzll. Use _BitScanReverse64.
+ unsigned long result = 0; // NOLINT(runtime/int)
+ if (_BitScanReverse64(&result, n)) {
+ return 63 - result;
+ }
+ return 64;
+#elif defined(_MSC_VER)
+ // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
+ unsigned long result = 0; // NOLINT(runtime/int)
+ if ((n >> 32) && _BitScanReverse(&result, n >> 32)) {
+ return 31 - result;
+ }
+ if (_BitScanReverse(&result, n)) {
+ return 63 - result;
+ }
+ return 64;
+#elif defined(__GNUC__)
+ // Use __builtin_clzll, which uses the following instructions:
+ // x86: bsr
+ // ARM64: clz
+ // PPC: cntlzd
+ static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
+ "__builtin_clzll does not take 64-bit arg");
+
+ // Handle 0 as a special case because __builtin_clzll(0) is undefined.
+ if (n == 0) {
+ return 64;
+ }
+ return __builtin_clzll(n);
+#else
+ return CountLeadingZeros64Slow(n);
+#endif
+}
+
+ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
+ int zeroes = 28;
+ if (n >> 16) zeroes -= 16, n >>= 16;
+ if (n >> 8) zeroes -= 8, n >>= 8;
+ if (n >> 4) zeroes -= 4, n >>= 4;
+ return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
+}
+
+ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
+#if defined(_MSC_VER)
+ unsigned long result = 0; // NOLINT(runtime/int)
+ if (_BitScanReverse(&result, n)) {
+ return 31 - result;
+ }
+ return 32;
+#elif defined(__GNUC__)
+ // Use __builtin_clz, which uses the following instructions:
+ // x86: bsr
+ // ARM64: clz
+ // PPC: cntlzd
+ static_assert(sizeof(int) == sizeof(n),
+ "__builtin_clz does not take 32-bit arg");
+
+ // Handle 0 as a special case because __builtin_clz(0) is undefined.
+ if (n == 0) {
+ return 32;
+ }
+ return __builtin_clz(n);
+#else
+ return CountLeadingZeros32Slow(n);
+#endif
+}
+
+ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) {
+ int c = 63;
+ n &= ~n + 1;
+ if (n & 0x00000000FFFFFFFF) c -= 32;
+ if (n & 0x0000FFFF0000FFFF) c -= 16;
+ if (n & 0x00FF00FF00FF00FF) c -= 8;
+ if (n & 0x0F0F0F0F0F0F0F0F) c -= 4;
+ if (n & 0x3333333333333333) c -= 2;
+ if (n & 0x5555555555555555) c -= 1;
+ return c;
+}
+
+ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
+#if defined(_MSC_VER) && defined(_M_X64)
+ unsigned long result = 0; // NOLINT(runtime/int)
+ _BitScanForward64(&result, n);
+ return result;
+#elif defined(_MSC_VER)
+ unsigned long result = 0; // NOLINT(runtime/int)
+ if (static_cast<uint32_t>(n) == 0) {
+ _BitScanForward(&result, n >> 32);
+ return result + 32;
+ }
+ _BitScanForward(&result, n);
+ return result;
+#elif defined(__GNUC__)
+ static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
+ "__builtin_ctzll does not take 64-bit arg");
+ return __builtin_ctzll(n);
+#else
+ return CountTrailingZerosNonZero64Slow(n);
+#endif
+}
+
+ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) {
+ int c = 31;
+ n &= ~n + 1;
+ if (n & 0x0000FFFF) c -= 16;
+ if (n & 0x00FF00FF) c -= 8;
+ if (n & 0x0F0F0F0F) c -= 4;
+ if (n & 0x33333333) c -= 2;
+ if (n & 0x55555555) c -= 1;
+ return c;
+}
+
+ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
+#if defined(_MSC_VER)
+ unsigned long result = 0; // NOLINT(runtime/int)
+ _BitScanForward(&result, n);
+ return result;
+#elif defined(__GNUC__)
+ static_assert(sizeof(int) == sizeof(n),
+ "__builtin_ctz does not take 32-bit arg");
+ return __builtin_ctz(n);
+#else
+ return CountTrailingZerosNonZero32Slow(n);
+#endif
+}
+
+#undef ABSL_BASE_INTERNAL_FORCEINLINE
+
+} // namespace base_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_BASE_INTERNAL_BITS_H_
diff --git a/absl/base/internal/bits_test.cc b/absl/base/internal/bits_test.cc
new file mode 100644
index 00000000..e5d991d6
--- /dev/null
+++ b/absl/base/internal/bits_test.cc
@@ -0,0 +1,97 @@
+// 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
+//
+// 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.
+
+#include "absl/base/internal/bits.h"
+
+#include "gtest/gtest.h"
+
+namespace {
+
+int CLZ64(uint64_t n) {
+ int fast = absl::base_internal::CountLeadingZeros64(n);
+ int slow = absl::base_internal::CountLeadingZeros64Slow(n);
+ EXPECT_EQ(fast, slow) << n;
+ return fast;
+}
+
+TEST(BitsTest, CountLeadingZeros64) {
+ EXPECT_EQ(64, CLZ64(uint64_t{}));
+ EXPECT_EQ(0, CLZ64(~uint64_t{}));
+
+ for (int index = 0; index < 64; index++) {
+ uint64_t x = static_cast<uint64_t>(1) << index;
+ const auto cnt = 63 - index;
+ ASSERT_EQ(cnt, CLZ64(x)) << index;
+ ASSERT_EQ(cnt, CLZ64(x + x - 1)) << index;
+ }
+}
+
+int CLZ32(uint32_t n) {
+ int fast = absl::base_internal::CountLeadingZeros32(n);
+ int slow = absl::base_internal::CountLeadingZeros32Slow(n);
+ EXPECT_EQ(fast, slow) << n;
+ return fast;
+}
+
+TEST(BitsTest, CountLeadingZeros32) {
+ EXPECT_EQ(32, CLZ32(uint32_t{}));
+ EXPECT_EQ(0, CLZ32(~uint32_t{}));
+
+ for (int index = 0; index < 32; index++) {
+ uint32_t x = static_cast<uint32_t>(1) << index;
+ const auto cnt = 31 - index;
+ ASSERT_EQ(cnt, CLZ32(x)) << index;
+ ASSERT_EQ(cnt, CLZ32(x + x - 1)) << index;
+ ASSERT_EQ(CLZ64(x), CLZ32(x) + 32);
+ }
+}
+
+int CTZ64(uint64_t n) {
+ int fast = absl::base_internal::CountTrailingZerosNonZero64(n);
+ int slow = absl::base_internal::CountTrailingZerosNonZero64Slow(n);
+ EXPECT_EQ(fast, slow) << n;
+ return fast;
+}
+
+TEST(BitsTest, CountTrailingZerosNonZero64) {
+ EXPECT_EQ(0, CTZ64(~uint64_t{}));
+
+ for (int index = 0; index < 64; index++) {
+ uint64_t x = static_cast<uint64_t>(1) << index;
+ const auto cnt = index;
+ ASSERT_EQ(cnt, CTZ64(x)) << index;
+ ASSERT_EQ(cnt, CTZ64(~(x - 1))) << index;
+ }
+}
+
+int CTZ32(uint32_t n) {
+ int fast = absl::base_internal::CountTrailingZerosNonZero32(n);
+ int slow = absl::base_internal::CountTrailingZerosNonZero32Slow(n);
+ EXPECT_EQ(fast, slow) << n;
+ return fast;
+}
+
+TEST(BitsTest, CountTrailingZerosNonZero32) {
+ EXPECT_EQ(0, CTZ32(~uint32_t{}));
+
+ for (int index = 0; index < 32; index++) {
+ uint32_t x = static_cast<uint32_t>(1) << index;
+ const auto cnt = index;
+ ASSERT_EQ(cnt, CTZ32(x)) << index;
+ ASSERT_EQ(cnt, CTZ32(~(x - 1))) << index;
+ }
+}
+
+
+} // namespace
diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc
index a4f1fc21..d99b63d3 100644
--- a/absl/base/internal/cycleclock.cc
+++ b/absl/base/internal/cycleclock.cc
@@ -27,7 +27,7 @@
#include "absl/base/internal/unscaledcycleclock.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
#if ABSL_USE_UNSCALED_CYCLECLOCK
@@ -79,5 +79,5 @@ double CycleClock::Frequency() {
#endif
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h
index 5fd8b348..ae5ede3e 100644
--- a/absl/base/internal/cycleclock.h
+++ b/absl/base/internal/cycleclock.h
@@ -46,7 +46,7 @@
#include <cstdint>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// -----------------------------------------------------------------------------
@@ -73,7 +73,7 @@ class CycleClock {
};
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_CYCLECLOCK_H_
diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h
index e98c9960..f064e363 100644
--- a/absl/base/internal/direct_mmap.h
+++ b/absl/base/internal/direct_mmap.h
@@ -62,7 +62,7 @@ extern "C" void* __mmap2(void*, size_t, int, int, int, size_t);
#endif // __BIONIC__
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// Platform specific logic extracted from
@@ -76,7 +76,11 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
// 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;
@@ -93,11 +97,13 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
#endif
#elif defined(__s390x__)
// On s390x, mmap() arguments are passed in memory.
- uint32_t buf[6] = {
- reinterpret_cast<uint32_t>(start), static_cast<uint32_t>(length),
- static_cast<uint32_t>(prot), static_cast<uint32_t>(flags),
- static_cast<uint32_t>(fd), static_cast<uint32_t>(offset)};
- return reintrepret_cast<void*>(syscall(SYS_mmap, buf));
+ 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
@@ -123,7 +129,7 @@ inline int DirectMunmap(void* start, size_t length) {
}
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else // !__linux__
@@ -132,7 +138,7 @@ inline int DirectMunmap(void* start, size_t length) {
// actual mmap()/munmap() methods.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
@@ -145,7 +151,7 @@ inline int DirectMunmap(void* start, size_t length) {
}
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // __linux__
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h
index 00447110..52c09c5b 100644
--- a/absl/base/internal/endian.h
+++ b/absl/base/internal/endian.h
@@ -34,7 +34,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// Use compiler byte-swapping intrinsics if they are available. 32-bit
// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0.
@@ -83,14 +83,14 @@ inline uint64_t gbswap_64(uint64_t host_int) {
#elif defined(__GLIBC__)
return bswap_64(host_int);
#else
- return (((x & uint64_t{(0xFF}) << 56) |
- ((x & uint64_t{(0xFF00}) << 40) |
- ((x & uint64_t{(0xFF0000}) << 24) |
- ((x & uint64_t{(0xFF000000}) << 8) |
- ((x & uint64_t{(0xFF00000000}) >> 8) |
- ((x & uint64_t{(0xFF0000000000}) >> 24) |
- ((x & uint64_t{(0xFF000000000000}) >> 40) |
- ((x & uint64_t{(0xFF00000000000000}) >> 56));
+ 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 // bswap_64
}
@@ -98,8 +98,10 @@ inline uint32_t gbswap_32(uint32_t host_int) {
#if defined(__GLIBC__)
return bswap_32(host_int);
#else
- return (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) |
- ((x & 0xFF000000) >> 24));
+ 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
}
@@ -107,7 +109,8 @@ inline uint16_t gbswap_16(uint16_t host_int) {
#if defined(__GLIBC__)
return bswap_16(host_int);
#else
- return uint16_t{((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)};
+ return (((host_int & uint16_t{0xFF}) << 8) |
+ ((host_int & uint16_t{0xFF00}) >> 8));
#endif
}
@@ -265,7 +268,7 @@ inline void Store64(void *p, uint64_t v) {
} // namespace big_endian
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_ENDIAN_H_
diff --git a/absl/base/internal/endian_test.cc b/absl/base/internal/endian_test.cc
index 66ccd45a..14ac4765 100644
--- a/absl/base/internal/endian_test.cc
+++ b/absl/base/internal/endian_test.cc
@@ -24,7 +24,7 @@
#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
const uint64_t kInitialNumber{0x0123456789abcdef};
@@ -34,32 +34,16 @@ const uint16_t k16Value{0x0123};
const int kNumValuesToTest = 1000000;
const int kRandomSeed = 12345;
-#ifdef ABSL_IS_BIG_ENDIAN
+#if defined(ABSL_IS_BIG_ENDIAN)
const uint64_t kInitialInNetworkOrder{kInitialNumber};
const uint64_t k64ValueLE{0xefcdab8967452301};
const uint32_t k32ValueLE{0x67452301};
const uint16_t k16ValueLE{0x2301};
-const uint8_t k8ValueLE{k8Value};
-const uint64_t k64IValueLE{0xefcdab89674523a1};
-const uint32_t k32IValueLE{0x67452391};
-const uint16_t k16IValueLE{0x85ff};
-const uint8_t k8IValueLE{0xff};
-const uint64_t kDoubleValueLE{0x6e861bf0f9210940};
-const uint32_t kFloatValueLE{0xd00f4940};
-const uint8_t kBoolValueLE{0x1};
const uint64_t k64ValueBE{kInitialNumber};
const uint32_t k32ValueBE{k32Value};
const uint16_t k16ValueBE{k16Value};
-const uint8_t k8ValueBE{k8Value};
-const uint64_t k64IValueBE{0xa123456789abcdef};
-const uint32_t k32IValueBE{0x91234567};
-const uint16_t k16IValueBE{0xff85};
-const uint8_t k8IValueBE{0xff};
-const uint64_t kDoubleValueBE{0x400921f9f01b866e};
-const uint32_t kFloatValueBE{0x40490fd0};
-const uint8_t kBoolValueBE{0x1};
-#elif defined ABSL_IS_LITTLE_ENDIAN
+#elif defined(ABSL_IS_LITTLE_ENDIAN)
const uint64_t kInitialInNetworkOrder{0xefcdab8967452301};
const uint64_t k64ValueLE{kInitialNumber};
const uint32_t k32ValueLE{k32Value};
@@ -277,5 +261,5 @@ TEST(EndianessTest, big_endian) {
}
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc
index f1d081f7..8207b7d7 100644
--- a/absl/base/internal/exception_safety_testing.cc
+++ b/absl/base/internal/exception_safety_testing.cc
@@ -23,6 +23,10 @@ exceptions_internal::NoThrowTag nothrow_ctor;
exceptions_internal::StrongGuaranteeTagType strong_guarantee;
+exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester() {
+ return {};
+}
+
namespace exceptions_internal {
int countdown = -1;
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index 8c2f5093..d4d41a8a 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -33,7 +33,7 @@
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
-#include "absl/types/optional.h"
+#include "absl/utility/utility.h"
namespace testing {
@@ -127,10 +127,8 @@ class ConstructorTracker {
void* address = it.first;
TrackedAddress& tracked_address = it.second;
if (tracked_address.is_alive) {
- ADD_FAILURE() << "Object at address " << address
- << " with countdown of " << countdown_
- << " was not destroyed [" << tracked_address.description
- << "]";
+ ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
+ countdown_, "Object was not destroyed.");
}
}
}
@@ -141,11 +139,11 @@ class ConstructorTracker {
TrackedAddress& tracked_address =
current_tracker_instance_->address_map_[address];
if (tracked_address.is_alive) {
- ADD_FAILURE() << "Object at address " << address << " with countdown of "
- << current_tracker_instance_->countdown_
- << " was re-constructed. Previously: ["
- << tracked_address.description << "] Now: [" << description
- << "]";
+ ADD_FAILURE() << ErrorMessage(
+ address, tracked_address.description,
+ current_tracker_instance_->countdown_,
+ "Object was re-constructed. Current object was constructed by " +
+ description);
}
tracked_address = {true, std::move(description)};
}
@@ -159,10 +157,9 @@ class ConstructorTracker {
TrackedAddress& tracked_address = it->second;
if (!tracked_address.is_alive) {
- ADD_FAILURE() << "Object at address " << address << " with countdown of "
- << current_tracker_instance_->countdown_
- << " was re-destroyed or created prior to construction "
- << "tracking [" << tracked_address.description << "]";
+ ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
+ current_tracker_instance_->countdown_,
+ "Object was re-destroyed.");
}
tracked_address.is_alive = false;
}
@@ -172,6 +169,16 @@ class ConstructorTracker {
return current_tracker_instance_ != nullptr;
}
+ static std::string ErrorMessage(void* address, const std::string& address_description,
+ int countdown, const std::string& error_description) {
+ return absl::Substitute(
+ "With coundtown at $0:\n"
+ " $1\n"
+ " Object originally constructed by $2\n"
+ " Object address: $3\n",
+ countdown, error_description, address_description, address);
+ }
+
std::unordered_map<void*, TrackedAddress> address_map_;
int countdown_;
@@ -190,70 +197,6 @@ class TrackedObject {
~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); }
};
-
-template <typename Factory, typename Operation, typename Invariant>
-absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
- const Factory& factory, const Operation& operation, int count,
- const Invariant& invariant) {
- auto t_ptr = factory();
- absl::optional<testing::AssertionResult> current_res;
- SetCountdown(count);
- try {
- operation(t_ptr.get());
- } catch (const exceptions_internal::TestException& e) {
- current_res.emplace(invariant(t_ptr.get()));
- if (!current_res.value()) {
- *current_res << e.what() << " failed invariant check";
- }
- }
- UnsetCountdown();
- return current_res;
-}
-
-template <typename Factory, typename Operation>
-absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
- const Factory& factory, const Operation& operation, int count,
- StrongGuaranteeTagType) {
- using TPtr = typename decltype(factory())::pointer;
- auto t_is_strong = [&](TPtr t) { return *t == *factory(); };
- return TestSingleInvariantAtCountdownImpl(factory, operation, count,
- t_is_strong);
-}
-
-template <typename Factory, typename Operation, typename Invariant>
-int TestSingleInvariantAtCountdown(
- const Factory& factory, const Operation& operation, int count,
- const Invariant& invariant,
- absl::optional<testing::AssertionResult>* reduced_res) {
- // If reduced_res is empty, it means the current call to
- // TestSingleInvariantAtCountdown(...) is the first test being run so we do
- // want to run it. Alternatively, if it's not empty (meaning a previous test
- // has run) we want to check if it passed. If the previous test did pass, we
- // want to contine running tests so we do want to run the current one. If it
- // failed, we want to short circuit so as not to overwrite the AssertionResult
- // output. If that's the case, we do not run the current test and instead we
- // simply return.
- if (!reduced_res->has_value() || reduced_res->value()) {
- *reduced_res = TestSingleInvariantAtCountdownImpl(factory, operation, count,
- invariant);
- }
- return 0;
-}
-
-template <typename Factory, typename Operation, typename... Invariants>
-inline absl::optional<testing::AssertionResult> TestAllInvariantsAtCountdown(
- const Factory& factory, const Operation& operation, int count,
- const Invariants&... invariants) {
- absl::optional<testing::AssertionResult> reduced_res;
-
- // Run each checker, short circuiting after the first failure
- int dummy[] = {
- 0, (TestSingleInvariantAtCountdown(factory, operation, count, invariants,
- &reduced_res))...};
- static_cast<void>(dummy);
- return reduced_res;
-}
-
} // namespace exceptions_internal
extern exceptions_internal::NoThrowTag nothrow_ctor;
@@ -773,7 +716,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
}
size_type max_size() const noexcept {
- return std::numeric_limits<difference_type>::max() / sizeof(value_type);
+ return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
}
ThrowingAllocator select_on_container_copy_construction() noexcept(
@@ -858,7 +801,7 @@ testing::AssertionResult TestNothrowOp(const Operation& operation) {
try {
operation();
return testing::AssertionSuccess();
- } catch (exceptions_internal::TestException) {
+ } catch (const exceptions_internal::TestException&) {
return testing::AssertionFailure()
<< "TestException thrown during call to operation() when nothrow "
"guarantee was expected.";
@@ -871,7 +814,7 @@ testing::AssertionResult TestNothrowOp(const Operation& operation) {
namespace exceptions_internal {
-// Dummy struct for ExceptionSafetyTester<> partial state.
+// Dummy struct for ExceptionSafetyTestBuilder<> partial state.
struct UninitializedT {};
template <typename T>
@@ -884,29 +827,106 @@ class DefaultFactory {
T t_;
};
-template <size_t LazyInvariantsCount, typename LazyFactory,
+template <size_t LazyContractsCount, typename LazyFactory,
typename LazyOperation>
using EnableIfTestable = typename absl::enable_if_t<
- LazyInvariantsCount != 0 &&
+ LazyContractsCount != 0 &&
!std::is_same<LazyFactory, UninitializedT>::value &&
!std::is_same<LazyOperation, UninitializedT>::value>;
template <typename Factory = UninitializedT,
- typename Operation = UninitializedT, typename... Invariants>
-class ExceptionSafetyTester;
+ typename Operation = UninitializedT, typename... Contracts>
+class ExceptionSafetyTestBuilder;
} // namespace exceptions_internal
-exceptions_internal::ExceptionSafetyTester<> MakeExceptionSafetyTester();
+/*
+ * Constructs an empty ExceptionSafetyTestBuilder. All
+ * ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation
+ * methods return new instances of ExceptionSafetyTestBuilder.
+ *
+ * In order to test a T for exception safety, a factory for that T, a testable
+ * operation, and at least one contract callback returning an assertion
+ * result must be applied using the respective methods.
+ */
+exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester();
namespace exceptions_internal {
+template <typename T>
+struct IsUniquePtr : std::false_type {};
+
+template <typename T, typename D>
+struct IsUniquePtr<std::unique_ptr<T, D>> : std::true_type {};
+
+template <typename Factory>
+struct FactoryPtrTypeHelper {
+ using type = decltype(std::declval<const Factory&>()());
+
+ static_assert(IsUniquePtr<type>::value, "Factories must return a unique_ptr");
+};
+
+template <typename Factory>
+using FactoryPtrType = typename FactoryPtrTypeHelper<Factory>::type;
+
+template <typename Factory>
+using FactoryElementType = typename FactoryPtrType<Factory>::element_type;
+
+template <typename T>
+class ExceptionSafetyTest {
+ using Factory = std::function<std::unique_ptr<T>()>;
+ using Operation = std::function<void(T*)>;
+ using Contract = std::function<AssertionResult(T*)>;
+
+ public:
+ template <typename... Contracts>
+ explicit ExceptionSafetyTest(const Factory& f, const Operation& op,
+ const Contracts&... contracts)
+ : factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {}
+
+ AssertionResult Test() const {
+ for (int count = 0;; ++count) {
+ exceptions_internal::ConstructorTracker ct(count);
+
+ for (const auto& contract : contracts_) {
+ auto t_ptr = factory_();
+ try {
+ SetCountdown(count);
+ operation_(t_ptr.get());
+ // Unset for the case that the operation throws no exceptions, which
+ // would leave the countdown set and break the *next* exception safety
+ // test after this one.
+ UnsetCountdown();
+ return AssertionSuccess();
+ } catch (const exceptions_internal::TestException& e) {
+ if (!contract(t_ptr.get())) {
+ return AssertionFailure() << e.what() << " failed contract check";
+ }
+ }
+ }
+ }
+ }
+
+ private:
+ template <typename ContractFn>
+ Contract WrapContract(const ContractFn& contract) {
+ return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); };
+ }
+
+ Contract WrapContract(StrongGuaranteeTagType) {
+ return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); };
+ }
+
+ Factory factory_;
+ Operation operation_;
+ std::vector<Contract> contracts_;
+};
/*
* Builds a tester object that tests if performing a operation on a T follows
- * exception safety guarantees. Verification is done via invariant assertion
+ * exception safety guarantees. Verification is done via contract assertion
* callbacks applied to T instances post-throw.
*
- * Template parameters for ExceptionSafetyTester:
+ * Template parameters for ExceptionSafetyTestBuilder:
*
* - Factory: The factory object (passed in via tester.WithFactory(...) or
* tester.WithInitialValue(...)) must be invocable with the signature
@@ -921,25 +941,25 @@ namespace exceptions_internal {
* fresh T instance so it's free to modify and destroy the T instances as it
* pleases.
*
- * - Invariants...: The invariant assertion callback objects (passed in via
- * tester.WithInvariants(...)) must be invocable with the signature
+ * - Contracts...: The contract assertion callback objects (passed in via
+ * tester.WithContracts(...)) must be invocable with the signature
* `testing::AssertionResult operator()(T*) const` where T is the type being
- * tested. Invariant assertion callbacks are provided T instances post-throw.
- * They must return testing::AssertionSuccess when the type invariants of the
- * provided T instance hold. If the type invariants of the T instance do not
+ * tested. Contract assertion callbacks are provided T instances post-throw.
+ * They must return testing::AssertionSuccess when the type contracts of the
+ * provided T instance hold. If the type contracts of the T instance do not
* hold, they must return testing::AssertionFailure. Execution order of
- * Invariants... is unspecified. They will each individually get a fresh T
+ * Contracts... is unspecified. They will each individually get a fresh T
* instance so they are free to modify and destroy the T instances as they
* please.
*/
-template <typename Factory, typename Operation, typename... Invariants>
-class ExceptionSafetyTester {
+template <typename Factory, typename Operation, typename... Contracts>
+class ExceptionSafetyTestBuilder {
public:
/*
- * Returns a new ExceptionSafetyTester with an included T factory based on the
- * provided T instance. The existing factory will not be included in the newly
- * created tester instance. The created factory returns a new T instance by
- * copy-constructing the provided const T& t.
+ * Returns a new ExceptionSafetyTestBuilder with an included T factory based
+ * on the provided T instance. The existing factory will not be included in
+ * the newly created tester instance. The created factory returns a new T
+ * instance by copy-constructing the provided const T& t.
*
* Preconditions for tester.WithInitialValue(const T& t):
*
@@ -948,63 +968,63 @@ class ExceptionSafetyTester {
* tester.WithFactory(...).
*/
template <typename T>
- ExceptionSafetyTester<DefaultFactory<T>, Operation, Invariants...>
+ ExceptionSafetyTestBuilder<DefaultFactory<T>, Operation, Contracts...>
WithInitialValue(const T& t) const {
return WithFactory(DefaultFactory<T>(t));
}
/*
- * Returns a new ExceptionSafetyTester with the provided T factory included.
- * The existing factory will not be included in the newly-created tester
- * instance. This method is intended for use with types lacking a copy
+ * Returns a new ExceptionSafetyTestBuilder with the provided T factory
+ * included. The existing factory will not be included in the newly-created
+ * tester instance. This method is intended for use with types lacking a copy
* constructor. Types that can be copy-constructed should instead use the
* method tester.WithInitialValue(...).
*/
template <typename NewFactory>
- ExceptionSafetyTester<absl::decay_t<NewFactory>, Operation, Invariants...>
+ ExceptionSafetyTestBuilder<absl::decay_t<NewFactory>, Operation, Contracts...>
WithFactory(const NewFactory& new_factory) const {
- return {new_factory, operation_, invariants_};
+ return {new_factory, operation_, contracts_};
}
/*
- * Returns a new ExceptionSafetyTester with the provided testable operation
- * included. The existing operation will not be included in the newly created
- * tester.
+ * Returns a new ExceptionSafetyTestBuilder with the provided testable
+ * operation included. The existing operation will not be included in the
+ * newly created tester.
*/
template <typename NewOperation>
- ExceptionSafetyTester<Factory, absl::decay_t<NewOperation>, Invariants...>
+ ExceptionSafetyTestBuilder<Factory, absl::decay_t<NewOperation>, Contracts...>
WithOperation(const NewOperation& new_operation) const {
- return {factory_, new_operation, invariants_};
+ return {factory_, new_operation, contracts_};
}
/*
- * Returns a new ExceptionSafetyTester with the provided MoreInvariants...
- * combined with the Invariants... that were already included in the instance
- * on which the method was called. Invariants... cannot be removed or replaced
- * once added to an ExceptionSafetyTester instance. A fresh object must be
- * created in order to get an empty Invariants... list.
+ * Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts...
+ * combined with the Contracts... that were already included in the instance
+ * on which the method was called. Contracts... cannot be removed or replaced
+ * once added to an ExceptionSafetyTestBuilder instance. A fresh object must
+ * be created in order to get an empty Contracts... list.
*
- * In addition to passing in custom invariant assertion callbacks, this method
+ * In addition to passing in custom contract assertion callbacks, this method
* accepts `testing::strong_guarantee` as an argument which checks T instances
* post-throw against freshly created T instances via operator== to verify
* that any state changes made during the execution of the operation were
* properly rolled back.
*/
- template <typename... MoreInvariants>
- ExceptionSafetyTester<Factory, Operation, Invariants...,
- absl::decay_t<MoreInvariants>...>
- WithInvariants(const MoreInvariants&... more_invariants) const {
- return {factory_, operation_,
- std::tuple_cat(invariants_,
- std::tuple<absl::decay_t<MoreInvariants>...>(
- more_invariants...))};
+ template <typename... MoreContracts>
+ ExceptionSafetyTestBuilder<Factory, Operation, Contracts...,
+ absl::decay_t<MoreContracts>...>
+ WithContracts(const MoreContracts&... more_contracts) const {
+ return {
+ factory_, operation_,
+ std::tuple_cat(contracts_, std::tuple<absl::decay_t<MoreContracts>...>(
+ more_contracts...))};
}
/*
* Returns a testing::AssertionResult that is the reduced result of the
* exception safety algorithm. The algorithm short circuits and returns
- * AssertionFailure after the first invariant callback returns an
- * AssertionFailure. Otherwise, if all invariant callbacks return an
+ * AssertionFailure after the first contract callback returns an
+ * AssertionFailure. Otherwise, if all contract callbacks return an
* AssertionSuccess, the reduced result is AssertionSuccess.
*
* The passed-in testable operation will not be saved in a new tester instance
@@ -1013,97 +1033,62 @@ class ExceptionSafetyTester {
*
* Preconditions for tester.Test(const NewOperation& new_operation):
*
- * - May only be called after at least one invariant assertion callback and a
+ * - May only be called after at least one contract assertion callback and a
* factory or initial value have been provided.
*/
template <
typename NewOperation,
- typename = EnableIfTestable<sizeof...(Invariants), Factory, NewOperation>>
+ typename = EnableIfTestable<sizeof...(Contracts), Factory, NewOperation>>
testing::AssertionResult Test(const NewOperation& new_operation) const {
- return TestImpl(new_operation, absl::index_sequence_for<Invariants...>());
+ return TestImpl(new_operation, absl::index_sequence_for<Contracts...>());
}
/*
* Returns a testing::AssertionResult that is the reduced result of the
* exception safety algorithm. The algorithm short circuits and returns
- * AssertionFailure after the first invariant callback returns an
- * AssertionFailure. Otherwise, if all invariant callbacks return an
+ * AssertionFailure after the first contract callback returns an
+ * AssertionFailure. Otherwise, if all contract callbacks return an
* AssertionSuccess, the reduced result is AssertionSuccess.
*
* Preconditions for tester.Test():
*
- * - May only be called after at least one invariant assertion callback, a
+ * - May only be called after at least one contract assertion callback, a
* factory or initial value and a testable operation have been provided.
*/
- template <typename LazyOperation = Operation,
- typename =
- EnableIfTestable<sizeof...(Invariants), Factory, LazyOperation>>
+ template <
+ typename LazyOperation = Operation,
+ typename = EnableIfTestable<sizeof...(Contracts), Factory, LazyOperation>>
testing::AssertionResult Test() const {
- return TestImpl(operation_, absl::index_sequence_for<Invariants...>());
+ return Test(operation_);
}
private:
template <typename, typename, typename...>
- friend class ExceptionSafetyTester;
+ friend class ExceptionSafetyTestBuilder;
- friend ExceptionSafetyTester<> testing::MakeExceptionSafetyTester();
+ friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester();
- ExceptionSafetyTester() {}
+ ExceptionSafetyTestBuilder() {}
- ExceptionSafetyTester(const Factory& f, const Operation& o,
- const std::tuple<Invariants...>& i)
- : factory_(f), operation_(o), invariants_(i) {}
+ ExceptionSafetyTestBuilder(const Factory& f, const Operation& o,
+ const std::tuple<Contracts...>& i)
+ : factory_(f), operation_(o), contracts_(i) {}
template <typename SelectedOperation, size_t... Indices>
- testing::AssertionResult TestImpl(const SelectedOperation& selected_operation,
+ testing::AssertionResult TestImpl(SelectedOperation selected_operation,
absl::index_sequence<Indices...>) const {
- // Starting from 0 and counting upwards until one of the exit conditions is
- // hit...
- for (int count = 0;; ++count) {
- exceptions_internal::ConstructorTracker ct(count);
-
- // Run the full exception safety test algorithm for the current countdown
- auto reduced_res =
- TestAllInvariantsAtCountdown(factory_, selected_operation, count,
- std::get<Indices>(invariants_)...);
- // If there is no value in the optional, no invariants were run because no
- // exception was thrown. This means that the test is complete and the loop
- // can exit successfully.
- if (!reduced_res.has_value()) {
- return testing::AssertionSuccess();
- }
- // If the optional is not empty and the value is falsy, an invariant check
- // failed so the test must exit to propegate the failure.
- if (!reduced_res.value()) {
- return reduced_res.value();
- }
- // If the optional is not empty and the value is not falsy, it means
- // exceptions were thrown but the invariants passed so the test must
- // continue to run.
- }
+ return ExceptionSafetyTest<FactoryElementType<Factory>>(
+ factory_, selected_operation, std::get<Indices>(contracts_)...)
+ .Test();
}
Factory factory_;
Operation operation_;
- std::tuple<Invariants...> invariants_;
+ std::tuple<Contracts...> contracts_;
};
} // namespace exceptions_internal
-/*
- * Constructs an empty ExceptionSafetyTester. All ExceptionSafetyTester
- * objects are immutable and all With[thing] mutation methods return new
- * instances of ExceptionSafetyTester.
- *
- * In order to test a T for exception safety, a factory for that T, a testable
- * operation, and at least one invariant callback returning an assertion
- * result must be applied using the respective methods.
- */
-inline exceptions_internal::ExceptionSafetyTester<>
-MakeExceptionSafetyTester() {
- return {};
-}
-
} // namespace testing
#endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
diff --git a/absl/base/internal/exception_testing.h b/absl/base/internal/exception_testing.h
index fd89a3f6..0cf7918e 100644
--- a/absl/base/internal/exception_testing.h
+++ b/absl/base/internal/exception_testing.h
@@ -35,7 +35,7 @@
EXPECT_DEATH(expr, ".*")
#else
#define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \
- EXPECT_DEATH(expr, text)
+ EXPECT_DEATH_IF_SUPPORTED(expr, text)
#endif
diff --git a/absl/base/internal/hide_ptr.h b/absl/base/internal/hide_ptr.h
index a2694508..ce390dc4 100644
--- a/absl/base/internal/hide_ptr.h
+++ b/absl/base/internal/hide_ptr.h
@@ -18,7 +18,7 @@
#include <cstdint>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// Arbitrary value with high bits set. Xor'ing with it is unlikely
@@ -43,7 +43,7 @@ inline T* UnhidePtr(uintptr_t hidden) {
}
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_
diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h
index 2eaab453..d57c83f4 100644
--- a/absl/base/internal/identity.h
+++ b/absl/base/internal/identity.h
@@ -17,7 +17,7 @@
#define ABSL_BASE_INTERNAL_IDENTITY_H_
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace internal {
template <typename T>
@@ -29,7 +29,7 @@ template <typename T>
using identity_t = typename identity<T>::type;
} // namespace internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_IDENTITY_H_
diff --git a/absl/base/internal/inline_variable_testing.h b/absl/base/internal/inline_variable_testing.h
index 49fa4ade..be0b0b96 100644
--- a/absl/base/internal/inline_variable_testing.h
+++ b/absl/base/internal/inline_variable_testing.h
@@ -18,7 +18,7 @@
#include "absl/base/internal/inline_variable.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace inline_variable_testing_internal {
struct Foo {
@@ -40,7 +40,7 @@ const int& get_int_a();
const int& get_int_b();
} // namespace inline_variable_testing_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_
diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h
index bc05a0a3..1372ef50 100644
--- a/absl/base/internal/invoke.h
+++ b/absl/base/internal/invoke.h
@@ -43,7 +43,7 @@
// top of this file for the API documentation.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// The five classes below each implement one of the clauses from the definition
@@ -184,7 +184,7 @@ InvokeT<F, Args...> Invoke(F&& f, Args&&... args) {
std::forward<Args>(args)...);
}
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_INVOKE_H_
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index ca6239ad..10d805cc 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -63,7 +63,7 @@
#endif // __APPLE__
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// A first-fit allocator with amortized logarithmic free() time.
@@ -209,7 +209,7 @@ struct LowLevelAlloc::Arena {
int32_t allocation_count GUARDED_BY(mu);
// flags passed to NewArena
const uint32_t flags;
- // Result of getpagesize()
+ // Result of sysconf(_SC_PAGESIZE)
const size_t pagesize;
// Lowest power of two >= max(16, sizeof(AllocList))
const size_t roundup;
@@ -325,8 +325,10 @@ size_t GetPageSize() {
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
return std::max(system_info.dwPageSize, system_info.dwAllocationGranularity);
-#else
+#elif defined(__wasm__) || defined(__asmjs__)
return getpagesize();
+#else
+ return sysconf(_SC_PAGESIZE);
#endif
}
@@ -402,16 +404,20 @@ bool LowLevelAlloc::DeleteArena(Arena *arena) {
ABSL_RAW_CHECK(munmap_result != 0,
"LowLevelAlloc::DeleteArena: VitualFree failed");
#else
+#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
munmap_result = munmap(region, size);
} else {
munmap_result = base_internal::DirectMunmap(region, size);
}
+#else
+ munmap_result = munmap(region, size);
+#endif // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if (munmap_result != 0) {
ABSL_RAW_LOG(FATAL, "LowLevelAlloc::DeleteArena: munmap failed: %d",
errno);
}
-#endif
+#endif // _WIN32
}
section.Leave();
arena->~Arena();
@@ -546,6 +552,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed");
#else
+#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
new_pages = base_internal::DirectMmap(nullptr, new_pages_size,
PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
@@ -553,10 +560,15 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
}
+#else
+ new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+#endif // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if (new_pages == MAP_FAILED) {
ABSL_RAW_LOG(FATAL, "mmap error: %d", errno);
}
-#endif
+
+#endif // _WIN32
arena->mu.Lock();
s = reinterpret_cast<AllocList *>(new_pages);
s->header.size = new_pages_size;
@@ -600,7 +612,7 @@ void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) {
}
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h
index 3f289571..87cfc934 100644
--- a/absl/base/internal/low_level_alloc.h
+++ b/absl/base/internal/low_level_alloc.h
@@ -39,10 +39,13 @@
#define ABSL_LOW_LEVEL_ALLOC_MISSING 1
#endif
-// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows.
+// 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)
+#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__)
#define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1
#endif
@@ -51,7 +54,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
class LowLevelAlloc {
@@ -116,6 +119,6 @@ class LowLevelAlloc {
};
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_
diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc
index 15ffe298..65bb519d 100644
--- a/absl/base/internal/low_level_alloc_test.cc
+++ b/absl/base/internal/low_level_alloc_test.cc
@@ -22,7 +22,7 @@
#include <utility>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
namespace {
@@ -149,7 +149,7 @@ static struct BeforeMain {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
int main(int argc, char *argv[]) {
diff --git a/absl/base/internal/low_level_scheduling.h b/absl/base/internal/low_level_scheduling.h
index 2ae464b4..7cb6117e 100644
--- a/absl/base/internal/low_level_scheduling.h
+++ b/absl/base/internal/low_level_scheduling.h
@@ -28,7 +28,7 @@ extern "C" bool __google_disable_rescheduling(void);
extern "C" void __google_enable_rescheduling(bool disable_result);
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
class SchedulingHelper; // To allow use of SchedulingGuard.
@@ -101,6 +101,6 @@ inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) {
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index cd1bbc09..ed8b8d7c 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -139,7 +139,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line,
#endif
#ifdef ABSL_MIN_LOG_LEVEL
- if (static_cast<int>(severity) < ABSL_MIN_LOG_LEVEL &&
+ if (severity < static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) &&
severity < absl::LogSeverity::kFatal) {
enabled = false;
}
@@ -181,7 +181,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line,
} // namespace
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace raw_logging_internal {
void SafeWriteToStderr(const char *s, size_t len) {
#if defined(ABSL_HAVE_SYSCALL_WRITE)
@@ -207,6 +207,15 @@ void RawLog(absl::LogSeverity severity, const char* file, int line,
va_end(ap);
}
+// Non-formatting version of RawLog().
+//
+// TODO(gfalcon): When string_view no longer depends on base, change this
+// interface to take its message as a string_view instead.
+static void DefaultInternalLog(absl::LogSeverity severity, const char* file,
+ int line, const std::string& message) {
+ RawLog(severity, file, line, "%s", message.c_str());
+}
+
bool RawLoggingFullySupported() {
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
return true;
@@ -215,6 +224,13 @@ bool RawLoggingFullySupported() {
#endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
}
+ABSL_CONST_INIT absl::base_internal::AtomicHook<InternalLogFunction>
+ internal_log_function(DefaultInternalLog);
+
+void RegisterInternalLogFunction(InternalLogFunction func) {
+ internal_log_function.Store(func);
+}
+
} // namespace raw_logging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index 04ff0607..2786a3de 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -19,7 +19,10 @@
#ifndef ABSL_BASE_INTERNAL_RAW_LOGGING_H_
#define ABSL_BASE_INTERNAL_RAW_LOGGING_H_
+#include <string>
+
#include "absl/base/attributes.h"
+#include "absl/base/internal/atomic_hook.h"
#include "absl/base/log_severity.h"
#include "absl/base/macros.h"
#include "absl/base/port.h"
@@ -57,6 +60,34 @@
} \
} 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_basename = \
+ ::absl::raw_logging_internal::Basename(__FILE__, \
+ sizeof(__FILE__) - 1); \
+ ::absl::raw_logging_internal::internal_log_function( \
+ ABSL_RAW_LOGGING_INTERNAL_##severity, \
+ absl_raw_logging_internal_basename, __LINE__, message); \
+ } 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
@@ -65,7 +96,7 @@
::absl::NormalizeLogSeverity(severity)
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace raw_logging_internal {
// Helper function to implement ABSL_RAW_LOG
@@ -84,7 +115,7 @@ void SafeWriteToStderr(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 std::string; the second parameter is the length of the std::string.
+// 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
@@ -132,8 +163,20 @@ using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file,
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);
+
+extern base_internal::AtomicHook<InternalLogFunction> internal_log_function;
+
+void RegisterInternalLogFunction(InternalLogFunction func);
+
} // namespace raw_logging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_RAW_LOGGING_H_
diff --git a/absl/base/internal/scheduling_mode.h b/absl/base/internal/scheduling_mode.h
index dd7df6bb..19a7514c 100644
--- a/absl/base/internal/scheduling_mode.h
+++ b/absl/base/internal/scheduling_mode.h
@@ -19,7 +19,7 @@
#define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// Used to describe how a thread may be scheduled. Typically associated with
@@ -50,7 +50,7 @@ enum SchedulingMode {
};
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_
diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc
index 9d90b3cb..8f8eef82 100644
--- a/absl/base/internal/spinlock.cc
+++ b/absl/base/internal/spinlock.cc
@@ -54,7 +54,7 @@
// holder to acquire the lock. There may be outstanding waiter(s).
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
ABSL_CONST_INIT static base_internal::AtomicHook<void (*)(const void *lock,
@@ -96,13 +96,9 @@ void SpinLock::InitLinkerInitializedAndCooperative() {
}
// Monitor the lock to see if its value changes within some time period
-// (adaptive_spin_count loop iterations). A timestamp indicating
-// when the thread initially started waiting for the lock is passed in via
-// the initial_wait_timestamp value. The total wait time in cycles for the
-// lock is returned in the wait_cycles parameter. The last value read
-// from the lock is returned from the method.
-uint32_t SpinLock::SpinLoop(int64_t initial_wait_timestamp,
- uint32_t *wait_cycles) {
+// (adaptive_spin_count loop iterations). The last value read from the lock
+// is returned from the method.
+uint32_t SpinLock::SpinLoop() {
// We are already in the slow path of SpinLock, initialize the
// adaptive_spin_count here.
ABSL_CONST_INIT static absl::once_flag init_adaptive_spin_count;
@@ -116,22 +112,21 @@ uint32_t SpinLock::SpinLoop(int64_t initial_wait_timestamp,
do {
lock_value = lockword_.load(std::memory_order_relaxed);
} while ((lock_value & kSpinLockHeld) != 0 && --c > 0);
- uint32_t spin_loop_wait_cycles =
- EncodeWaitCycles(initial_wait_timestamp, CycleClock::Now());
- *wait_cycles = spin_loop_wait_cycles;
-
- return TryLockInternal(lock_value, spin_loop_wait_cycles);
+ return lock_value;
}
void SpinLock::SlowLock() {
+ uint32_t lock_value = SpinLoop();
+ lock_value = TryLockInternal(lock_value, 0);
+ if ((lock_value & kSpinLockHeld) == 0) {
+ return;
+ }
// The lock was not obtained initially, so this thread needs to wait for
// it. Record the current timestamp in the local variable wait_start_time
// so the total wait time can be stored in the lockword once this thread
// obtains the lock.
int64_t wait_start_time = CycleClock::Now();
- uint32_t wait_cycles;
- uint32_t lock_value = SpinLoop(wait_start_time, &wait_cycles);
-
+ uint32_t wait_cycles = 0;
int lock_wait_call_count = 0;
while ((lock_value & kSpinLockHeld) != 0) {
// If the lock is currently held, but not marked as having a sleeper, mark
@@ -142,7 +137,7 @@ void SpinLock::SlowLock() {
// owner to think it experienced contention.
if (lockword_.compare_exchange_strong(
lock_value, lock_value | kSpinLockSleeper,
- std::memory_order_acquire, std::memory_order_relaxed)) {
+ std::memory_order_relaxed, std::memory_order_relaxed)) {
// Successfully transitioned to kSpinLockSleeper. Pass
// kSpinLockSleeper to the SpinLockWait routine to properly indicate
// the last lock_value observed.
@@ -171,7 +166,9 @@ void SpinLock::SlowLock() {
ABSL_TSAN_MUTEX_POST_DIVERT(this, 0);
// Spin again after returning from the wait routine to give this thread
// some chance of obtaining the lock.
- lock_value = SpinLoop(wait_start_time, &wait_cycles);
+ lock_value = SpinLoop();
+ wait_cycles = EncodeWaitCycles(wait_start_time, CycleClock::Now());
+ lock_value = TryLockInternal(lock_value, wait_cycles);
}
}
@@ -207,14 +204,20 @@ uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time,
(wait_end_time - wait_start_time) >> PROFILE_TIMESTAMP_SHIFT;
// Return a representation of the time spent waiting that can be stored in
- // the lock word's upper bits. bit_cast is required as Atomic32 is signed.
- const uint32_t clamped = static_cast<uint32_t>(
+ // the lock word's upper bits.
+ uint32_t clamped = static_cast<uint32_t>(
std::min(scaled_wait_time, kMaxWaitTime) << LOCKWORD_RESERVED_SHIFT);
- // bump up value if necessary to avoid returning kSpinLockSleeper.
- const uint32_t after_spinlock_sleeper =
- kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT);
- return clamped == kSpinLockSleeper ? after_spinlock_sleeper : clamped;
+ if (clamped == 0) {
+ return kSpinLockSleeper; // Just wake waiters, but don't record contention.
+ }
+ // Bump up value if necessary to avoid returning kSpinLockSleeper.
+ const uint32_t kMinWaitTime =
+ kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT);
+ if (clamped == kSpinLockSleeper) {
+ return kMinWaitTime;
+ }
+ return clamped;
}
uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) {
@@ -226,5 +229,5 @@ uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) {
}
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h
index 7b59fe26..d53878b2 100644
--- a/absl/base/internal/spinlock.h
+++ b/absl/base/internal/spinlock.h
@@ -45,7 +45,7 @@
#include "absl/base/thread_annotations.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
class LOCKABLE SpinLock {
@@ -102,8 +102,8 @@ class LOCKABLE SpinLock {
inline void Unlock() UNLOCK_FUNCTION() {
ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0);
uint32_t lock_value = lockword_.load(std::memory_order_relaxed);
- lockword_.store(lock_value & kSpinLockCooperative,
- std::memory_order_release);
+ lock_value = lockword_.exchange(lock_value & kSpinLockCooperative,
+ std::memory_order_release);
if ((lock_value & kSpinLockDisabledScheduling) != 0) {
base_internal::SchedulingGuard::EnableRescheduling(true);
@@ -162,7 +162,7 @@ class LOCKABLE SpinLock {
void InitLinkerInitializedAndCooperative();
void SlowLock() ABSL_ATTRIBUTE_COLD;
void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD;
- uint32_t SpinLoop(int64_t initial_wait_timestamp, uint32_t* wait_cycles);
+ uint32_t SpinLoop();
inline bool TryLockImpl() {
uint32_t lock_value = lockword_.load(std::memory_order_relaxed);
@@ -235,7 +235,7 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value,
}
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_SPINLOCK_H_
diff --git a/absl/base/internal/spinlock_benchmark.cc b/absl/base/internal/spinlock_benchmark.cc
new file mode 100644
index 00000000..907d3e27
--- /dev/null
+++ b/absl/base/internal/spinlock_benchmark.cc
@@ -0,0 +1,52 @@
+// 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
+//
+// 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.
+
+// See also //absl/synchronization:mutex_benchmark for a comparison of SpinLock
+// and Mutex performance under varying levels of contention.
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/scheduling_mode.h"
+#include "absl/base/internal/spinlock.h"
+#include "absl/synchronization/internal/create_thread_identity.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+template <absl::base_internal::SchedulingMode scheduling_mode>
+static void BM_SpinLock(benchmark::State& state) {
+ // Ensure a ThreadIdentity is installed.
+ ABSL_INTERNAL_CHECK(
+ absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() !=
+ nullptr,
+ "GetOrCreateCurrentThreadIdentity() failed");
+
+ static auto* spinlock = new absl::base_internal::SpinLock(scheduling_mode);
+ for (auto _ : state) {
+ absl::base_internal::SpinLockHolder holder(spinlock);
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_SpinLock,
+ absl::base_internal::SCHEDULE_KERNEL_ONLY)
+ ->UseRealTime()
+ ->Threads(1)
+ ->ThreadPerCpu();
+
+BENCHMARK_TEMPLATE(BM_SpinLock,
+ absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL)
+ ->UseRealTime()
+ ->Threads(1)
+ ->ThreadPerCpu();
+
+} // namespace
diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc
new file mode 100644
index 00000000..94c861dc
--- /dev/null
+++ b/absl/base/internal/spinlock_linux.inc
@@ -0,0 +1,72 @@
+// 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
+//
+// 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.
+//
+// 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 <cerrno>
+#include <climits>
+#include <cstdint>
+#include <ctime>
+
+#include "absl/base/attributes.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
+
+extern "C" {
+
+ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
+ std::atomic<uint32_t> *w, uint32_t value, int loop,
+ absl::base_internal::SchedulingMode) {
+ if (loop != 0) {
+ int save_errno = errno;
+ struct timespec tm;
+ tm.tv_sec = 0;
+ // Increase the delay; we expect (but do not rely on) explicit wakeups.
+ // We don't rely on explicit wakeups because we intentionally allow for
+ // a race on the kSpinLockSleeper bit.
+ tm.tv_nsec = 16 * absl::base_internal::SpinLockSuggestedDelayNS(loop);
+ syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
+ errno = save_errno;
+ }
+}
+
+ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w,
+ bool all) {
+ syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0);
+}
+
+} // extern "C"
diff --git a/absl/base/internal/spinlock_wait.cc b/absl/base/internal/spinlock_wait.cc
index 6709d01e..3f8e43f0 100644
--- a/absl/base/internal/spinlock_wait.cc
+++ b/absl/base/internal/spinlock_wait.cc
@@ -13,7 +13,7 @@
// limitations under the License.
// The OS-specific header included below must provide two calls:
-// base::subtle::SpinLockDelay() and base::subtle::SpinLockWake().
+// AbslInternalSpinLockDelay() and AbslInternalSpinLockWake().
// See spinlock_wait.h for the specs.
#include <atomic>
@@ -23,6 +23,8 @@
#if defined(_WIN32)
#include "absl/base/internal/spinlock_win32.inc"
+#elif defined(__linux__)
+#include "absl/base/internal/spinlock_linux.inc"
#elif defined(__akaros__)
#include "absl/base/internal/spinlock_akaros.inc"
#else
@@ -30,21 +32,22 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// See spinlock_wait.h for spec.
uint32_t SpinLockWait(std::atomic<uint32_t> *w, int n,
const SpinLockWaitTransition trans[],
base_internal::SchedulingMode scheduling_mode) {
- for (int loop = 0; ; loop++) {
+ int loop = 0;
+ for (;;) {
uint32_t v = w->load(std::memory_order_acquire);
int i;
for (i = 0; i != n && v != trans[i].from; i++) {
}
if (i == n) {
- SpinLockDelay(w, v, loop, scheduling_mode); // no matching transition
- } else if (trans[i].to == v || // null transition
+ SpinLockDelay(w, v, ++loop, scheduling_mode); // no matching transition
+ } else if (trans[i].to == v || // null transition
w->compare_exchange_strong(v, trans[i].to,
std::memory_order_acquire,
std::memory_order_relaxed)) {
@@ -77,5 +80,5 @@ int SpinLockSuggestedDelayNS(int loop) {
}
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h
index 31aaa8c6..5eb727f1 100644
--- a/absl/base/internal/spinlock_wait.h
+++ b/absl/base/internal/spinlock_wait.h
@@ -24,7 +24,7 @@
#include "absl/base/internal/scheduling_mode.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// SpinLockWait() waits until it can perform one of several transitions from
@@ -63,7 +63,7 @@ void SpinLockDelay(std::atomic<uint32_t> *w, uint32_t value, int loop,
int SpinLockSuggestedDelayNS(int loop);
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// In some build configurations we pass --detect-odr-violations to the
diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc
index 7db2e001..ce14fc0f 100644
--- a/absl/base/internal/sysinfo.cc
+++ b/absl/base/internal/sysinfo.cc
@@ -56,7 +56,7 @@
#include "absl/base/internal/unscaledcycleclock.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
static once_flag init_system_info_once;
@@ -402,5 +402,5 @@ pid_t GetTID() {
#endif
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h
index 18aa2e29..79100f61 100644
--- a/absl/base/internal/sysinfo.h
+++ b/absl/base/internal/sysinfo.h
@@ -33,7 +33,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// Nominal core processor cycles per second of each processor. This is _not_
@@ -59,7 +59,7 @@ using pid_t = DWORD;
pid_t GetTID();
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_SYSINFO_H_
diff --git a/absl/base/internal/sysinfo_test.cc b/absl/base/internal/sysinfo_test.cc
index fdbbdf88..c072ebc2 100644
--- a/absl/base/internal/sysinfo_test.cc
+++ b/absl/base/internal/sysinfo_test.cc
@@ -28,7 +28,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
namespace {
@@ -96,5 +96,5 @@ TEST(SysinfoTest, LinuxGetTID) {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc
index aa1add8b..b35bee34 100644
--- a/absl/base/internal/thread_identity.cc
+++ b/absl/base/internal/thread_identity.cc
@@ -28,7 +28,7 @@
#include "absl/base/internal/spinlock.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
#if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11
@@ -69,6 +69,14 @@ void SetCurrentThreadIdentity(
// NOTE: Not async-safe. But can be open-coded.
absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
reclaimer);
+
+#ifdef __EMSCRIPTEN__
+ // Emscripten PThread implementation does not support signals.
+ // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
+ // for more information.
+ pthread_setspecific(thread_identity_pthread_key,
+ reinterpret_cast<void*>(identity));
+#else
// We must mask signals around the call to setspecific as with current glibc,
// a concurrent getspecific (needed for GetCurrentThreadIdentityIfPresent())
// may zero our value.
@@ -82,6 +90,8 @@ void SetCurrentThreadIdentity(
pthread_setspecific(thread_identity_pthread_key,
reinterpret_cast<void*>(identity));
pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr);
+#endif // !__EMSCRIPTEN__
+
#elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS
// NOTE: Not async-safe. But can be open-coded.
absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
@@ -121,5 +131,5 @@ ThreadIdentity* CurrentThreadIdentityIfPresent() {
#endif
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h
index 18a5a750..17ac2a7c 100644
--- a/absl/base/internal/thread_identity.h
+++ b/absl/base/internal/thread_identity.h
@@ -33,7 +33,7 @@
#include "absl/base/internal/per_thread_tls.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
struct SynchLocksHeld;
struct SynchWaitParams;
@@ -237,6 +237,6 @@ inline ThreadIdentity* CurrentThreadIdentityIfPresent() {
#endif
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_
diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc
index f39f11d2..ec93fc27 100644
--- a/absl/base/internal/thread_identity_test.cc
+++ b/absl/base/internal/thread_identity_test.cc
@@ -25,7 +25,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
namespace {
@@ -124,5 +124,5 @@ TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc
index 5466e0f3..0f73c3eb 100644
--- a/absl/base/internal/throw_delegate.cc
+++ b/absl/base/internal/throw_delegate.cc
@@ -22,7 +22,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
namespace {
@@ -31,8 +31,8 @@ template <typename T>
#ifdef ABSL_HAVE_EXCEPTIONS
throw error;
#else
- ABSL_RAW_LOG(ERROR, "%s", error.what());
- abort();
+ ABSL_RAW_LOG(FATAL, "%s", error.what());
+ std::abort();
#endif
}
} // namespace
@@ -104,5 +104,5 @@ void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); }
void ThrowStdBadAlloc() { Throw(std::bad_alloc()); }
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/internal/throw_delegate.h b/absl/base/internal/throw_delegate.h
index d34ff79c..7e5510c0 100644
--- a/absl/base/internal/throw_delegate.h
+++ b/absl/base/internal/throw_delegate.h
@@ -20,7 +20,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// Helper functions that allow throwing exceptions consistently from anywhere.
@@ -67,7 +67,7 @@ namespace base_internal {
// [[noreturn]] void ThrowStdBadArrayNewLength();
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_
diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h
index f20a8694..07a64bba 100644
--- a/absl/base/internal/unaligned_access.h
+++ b/absl/base/internal/unaligned_access.h
@@ -65,7 +65,8 @@ void __sanitizer_unaligned_store64(void *p, uint64_t v);
} // extern "C"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
+namespace base_internal {
inline uint16_t UnalignedLoad16(const void *p) {
return __sanitizer_unaligned_load16(p);
@@ -91,19 +92,71 @@ inline void UnalignedStore64(void *p, uint64_t v) {
__sanitizer_unaligned_store64(p, v);
}
-} // inline namespace lts_2018_06_20
+} // namespace base_internal
+} // inline namespace lts_2018_12_18
} // namespace absl
-#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) (absl::UnalignedLoad16(_p))
-#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) (absl::UnalignedLoad32(_p))
-#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) (absl::UnalignedLoad64(_p))
+#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))
+
+#elif defined(UNDEFINED_BEHAVIOR_SANITIZER)
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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::UnalignedStore16(_p, _val))
+ (absl::base_internal::UnalignedStore16(_p, _val))
#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
- (absl::UnalignedStore32(_p, _val))
+ (absl::base_internal::UnalignedStore32(_p, _val))
#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
- (absl::UnalignedStore64(_p, _val))
+ (absl::base_internal::UnalignedStore64(_p, _val))
#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \
defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \
@@ -160,8 +213,8 @@ inline void UnalignedStore64(void *p, uint64_t v) {
// so we do that.
namespace absl {
-inline namespace lts_2018_06_20 {
-namespace internal {
+inline namespace lts_2018_12_18 {
+namespace base_internal {
struct Unaligned16Struct {
uint16_t value;
@@ -173,24 +226,27 @@ struct Unaligned32Struct {
uint8_t dummy; // To make the size non-power-of-two.
} ABSL_ATTRIBUTE_PACKED;
-} // namespace internal
-} // inline namespace lts_2018_06_20
+} // namespace base_internal
+} // inline namespace lts_2018_12_18
} // namespace absl
-#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
- ((reinterpret_cast<const ::absl::internal::Unaligned16Struct *>(_p))->value)
-#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
- ((reinterpret_cast<const ::absl::internal::Unaligned32Struct *>(_p))->value)
+#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
+ ((reinterpret_cast<const ::absl::base_internal::Unaligned16Struct *>(_p)) \
+ ->value)
+#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
+ ((reinterpret_cast<const ::absl::base_internal::Unaligned32Struct *>(_p)) \
+ ->value)
-#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
- ((reinterpret_cast< ::absl::internal::Unaligned16Struct *>(_p))->value = \
- (_val))
-#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
- ((reinterpret_cast< ::absl::internal::Unaligned32Struct *>(_p))->value = \
- (_val))
+#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
+ ((reinterpret_cast< ::absl::base_internal::Unaligned16Struct *>(_p)) \
+ ->value = (_val))
+#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
+ ((reinterpret_cast< ::absl::base_internal::Unaligned32Struct *>(_p)) \
+ ->value = (_val))
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
+namespace base_internal {
inline uint64_t UnalignedLoad64(const void *p) {
uint64_t t;
@@ -200,12 +256,14 @@ inline uint64_t UnalignedLoad64(const void *p) {
inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
-} // inline namespace lts_2018_06_20
+} // namespace base_internal
+} // inline namespace lts_2018_12_18
} // namespace absl
-#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) (absl::UnalignedLoad64(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
+ (absl::base_internal::UnalignedLoad64(_p))
#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
- (absl::UnalignedStore64(_p, _val))
+ (absl::base_internal::UnalignedStore64(_p, _val))
#else
@@ -217,7 +275,8 @@ inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
// unaligned loads and stores.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
+namespace base_internal {
inline uint16_t UnalignedLoad16(const void *p) {
uint16_t t;
@@ -243,19 +302,23 @@ 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); }
-} // inline namespace lts_2018_06_20
+} // namespace base_internal
+} // inline namespace lts_2018_12_18
} // namespace absl
-#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) (absl::UnalignedLoad16(_p))
-#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) (absl::UnalignedLoad32(_p))
-#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) (absl::UnalignedLoad64(_p))
+#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::UnalignedStore16(_p, _val))
+ (absl::base_internal::UnalignedStore16(_p, _val))
#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
- (absl::UnalignedStore32(_p, _val))
+ (absl::base_internal::UnalignedStore32(_p, _val))
#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
- (absl::UnalignedStore64(_p, _val))
+ (absl::base_internal::UnalignedStore64(_p, _val))
#endif
diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc
index e0798eb9..888caf1e 100644
--- a/absl/base/internal/unscaledcycleclock.cc
+++ b/absl/base/internal/unscaledcycleclock.cc
@@ -27,7 +27,7 @@
#include "absl/base/internal/sysinfo.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
#if defined(__i386__)
@@ -97,7 +97,7 @@ double UnscaledCycleClock::Frequency() {
#endif
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_USE_UNSCALED_CYCLECLOCK
diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h
index 9da14d0b..c71674f3 100644
--- a/absl/base/internal/unscaledcycleclock.h
+++ b/absl/base/internal/unscaledcycleclock.h
@@ -84,7 +84,7 @@
#define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
class UnscaledCycleClockWrapperForGetCurrentTime;
} // namespace time_internal
@@ -114,7 +114,7 @@ class UnscaledCycleClock {
};
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_USE_UNSCALED_CYCLECLOCK
diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc
index 2c04b591..4df637ac 100644
--- a/absl/base/invoke_test.cc
+++ b/absl/base/invoke_test.cc
@@ -25,7 +25,7 @@
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
namespace {
@@ -198,5 +198,5 @@ TEST(InvokeTest, SfinaeFriendly) {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h
index 8b7a66ba..c24fad79 100644
--- a/absl/base/log_severity.h
+++ b/absl/base/log_severity.h
@@ -21,7 +21,7 @@
#include "absl/base/attributes.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// Four severity levels are defined. Logging APIs should terminate the program
// when a message is logged at severity `kFatal`; the other levels have no
@@ -40,7 +40,7 @@ constexpr std::array<absl::LogSeverity, 4> LogSeverities() {
absl::LogSeverity::kError, absl::LogSeverity::kFatal}};
}
-// Returns the all-caps std::string representation (e.g. "INFO") of the specified
+// Returns the all-caps string representation (e.g. "INFO") of the specified
// severity level if it is one of the normal levels and "UNKNOWN" otherwise.
constexpr const char* LogSeverityName(absl::LogSeverity s) {
return s == absl::LogSeverity::kInfo
@@ -63,7 +63,7 @@ constexpr absl::LogSeverity NormalizeLogSeverity(int s) {
return NormalizeLogSeverity(static_cast<absl::LogSeverity>(s));
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
diff --git a/absl/base/macros.h b/absl/base/macros.h
index aabe8db4..14c4b0a3 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -43,14 +43,14 @@
(sizeof(::absl::macros_internal::ArraySizeHelper(array)))
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
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
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// kLinkerInitialized
@@ -74,13 +74,13 @@ auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
// // Invocation
// static MyClass my_global(absl::base_internal::kLinkerInitialized);
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
enum LinkerInitialized {
kLinkerInitialized = 0,
};
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// ABSL_FALLTHROUGH_INTENDED
@@ -203,4 +203,14 @@ enum LinkerInitialized {
: [] { assert(false && #expr); }()) // NOLINT
#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
+
#endif // ABSL_BASE_MACROS_H_
diff --git a/absl/base/raw_logging_test.cc b/absl/base/raw_logging_test.cc
index dae4b351..b21cf651 100644
--- a/absl/base/raw_logging_test.cc
+++ b/absl/base/raw_logging_test.cc
@@ -18,12 +18,20 @@
#include "absl/base/internal/raw_logging.h"
+#include <tuple>
+
#include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
namespace {
TEST(RawLoggingCompilationTest, Log) {
ABSL_RAW_LOG(INFO, "RAW INFO: %d", 1);
+ ABSL_RAW_LOG(INFO, "RAW INFO: %d %d", 1, 2);
+ ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d", 1, 2, 3);
+ ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d", 1, 2, 3, 4);
+ ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d %d", 1, 2, 3, 4, 5);
+ ABSL_RAW_LOG(WARNING, "RAW WARNING: %d", 1);
ABSL_RAW_LOG(ERROR, "RAW ERROR: %d", 1);
}
@@ -32,7 +40,7 @@ TEST(RawLoggingCompilationTest, PassingCheck) {
}
// Not all platforms support output from raw log, so we don't verify any
-// particular output for RAW check failures (expecting the empty std::string
+// particular output for RAW check failures (expecting the empty string
// accomplishes this). This test is primarily a compilation test, but we
// are verifying process death when EXPECT_DEATH works for a platform.
const char kExpectedDeathOutput[] = "";
@@ -47,4 +55,25 @@ TEST(RawLoggingDeathTest, LogFatal) {
kExpectedDeathOutput);
}
+TEST(InternalLog, CompilationTest) {
+ ABSL_INTERNAL_LOG(INFO, "Internal Log");
+ std::string log_msg = "Internal Log";
+ ABSL_INTERNAL_LOG(INFO, log_msg);
+
+ ABSL_INTERNAL_LOG(INFO, log_msg + " 2");
+
+ float d = 1.1f;
+ ABSL_INTERNAL_LOG(INFO, absl::StrCat("Internal log ", 3, " + ", d));
+}
+
+TEST(InternalLogDeathTest, FailingCheck) {
+ EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_CHECK(1 == 0, "explanation"),
+ kExpectedDeathOutput);
+}
+
+TEST(InternalLogDeathTest, LogFatal) {
+ EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_LOG(FATAL, "my dog has fleas"),
+ kExpectedDeathOutput);
+}
+
} // namespace
diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc
index d04ee366..95382977 100644
--- a/absl/base/spinlock_test_common.cc
+++ b/absl/base/spinlock_test_common.cc
@@ -36,7 +36,7 @@ constexpr int32_t kNumThreads = 10;
constexpr int32_t kIters = 1000;
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace base_internal {
// This is defined outside of anonymous namespace so that it can be
@@ -156,7 +156,8 @@ TEST(SpinLock, WaitCyclesEncoding) {
// Test corner cases
int64_t start_time = time_distribution(generator);
- EXPECT_EQ(0, SpinLockTest::EncodeWaitCycles(start_time, start_time));
+ EXPECT_EQ(kSpinLockSleeper,
+ SpinLockTest::EncodeWaitCycles(start_time, start_time));
EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(0));
EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(kLockwordReservedMask));
EXPECT_EQ(kMaxCycles & ~kProfileTimestampMask,
@@ -264,5 +265,5 @@ TEST(SpinLockWithThreads, DoesNotDeadlock) {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h
index 8d30b932..2241ace4 100644
--- a/absl/base/thread_annotations.h
+++ b/absl/base/thread_annotations.h
@@ -31,7 +31,6 @@
// 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_
@@ -109,13 +108,23 @@
// 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 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__))
diff --git a/absl/compiler_config_setting.bzl b/absl/compiler_config_setting.bzl
new file mode 100644
index 00000000..b77c4f56
--- /dev/null
+++ b/absl/compiler_config_setting.bzl
@@ -0,0 +1,39 @@
+#
+# 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
+#
+# 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.
+#
+
+"""Creates config_setting that allows selecting based on 'compiler' value."""
+
+def create_llvm_config(name, visibility):
+ # The "do_not_use_tools_cpp_compiler_present" attribute exists to
+ # distinguish between older versions of Bazel that do not support
+ # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do.
+ # In the future, the only way to select on the compiler will be through
+ # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can
+ # be removed.
+ if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"):
+ native.config_setting(
+ name = name,
+ flag_values = {
+ "@bazel_tools//tools/cpp:compiler": "llvm",
+ },
+ visibility = visibility,
+ )
+ else:
+ native.config_setting(
+ name = name,
+ values = {"compiler": "llvm"},
+ visibility = visibility,
+ )
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index 119d5c88..afc869f4 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -19,6 +19,7 @@ load(
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
"ABSL_EXCEPTIONS_FLAG",
+ "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
)
package(default_visibility = ["//visibility:public"])
@@ -26,10 +27,30 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
cc_library(
+ name = "compressed_tuple",
+ hdrs = ["internal/compressed_tuple.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/utility",
+ ],
+)
+
+cc_test(
+ name = "compressed_tuple_test",
+ srcs = ["internal/compressed_tuple_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":compressed_tuple",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
name = "fixed_array",
hdrs = ["fixed_array.h"],
copts = ABSL_DEFAULT_COPTS,
deps = [
+ ":compressed_tuple",
"//absl/algorithm",
"//absl/base:core_headers",
"//absl/base:dynamic_annotations",
@@ -42,9 +63,11 @@ cc_test(
name = "fixed_array_test",
srcs = ["fixed_array_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":fixed_array",
"//absl/base:exception_testing",
+ "//absl/hash:hash_testing",
"//absl/memory",
"@com_google_googletest//:gtest_main",
],
@@ -57,12 +80,25 @@ cc_test(
deps = [
":fixed_array",
"//absl/base:exception_testing",
+ "//absl/hash:hash_testing",
"//absl/memory",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
+ name = "fixed_array_exception_safety_test",
+ srcs = ["fixed_array_exception_safety_test.cc"],
+ copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
+ deps = [
+ ":fixed_array",
+ "//absl/base:exception_safety_testing",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
name = "fixed_array_benchmark",
srcs = ["fixed_array_benchmark.cc"],
copts = ABSL_TEST_COPTS + ["$(STACK_FRAME_UNLIMITED)"],
@@ -89,12 +125,14 @@ cc_test(
name = "inlined_vector_test",
srcs = ["inlined_vector_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":inlined_vector",
":test_instance_tracker",
"//absl/base",
"//absl/base:core_headers",
"//absl/base:exception_testing",
+ "//absl/hash:hash_testing",
"//absl/memory",
"//absl/strings",
"@com_google_googletest//:gtest_main",
@@ -111,6 +149,7 @@ cc_test(
"//absl/base",
"//absl/base:core_headers",
"//absl/base:exception_testing",
+ "//absl/hash:hash_testing",
"//absl/memory",
"//absl/strings",
"@com_google_googletest//:gtest_main",
@@ -150,3 +189,462 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+NOTEST_TAGS_NONMOBILE = [
+ "no_test_darwin_x86_64",
+ "no_test_loonix",
+]
+
+NOTEST_TAGS_MOBILE = [
+ "no_test_android_arm",
+ "no_test_android_arm64",
+ "no_test_android_x86",
+ "no_test_ios_x86_64",
+]
+
+NOTEST_TAGS = NOTEST_TAGS_MOBILE + NOTEST_TAGS_NONMOBILE
+
+cc_library(
+ name = "flat_hash_map",
+ hdrs = ["flat_hash_map.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":container_memory",
+ ":hash_function_defaults",
+ ":raw_hash_map",
+ "//absl/algorithm:container",
+ "//absl/memory",
+ ],
+)
+
+cc_test(
+ name = "flat_hash_map_test",
+ srcs = ["flat_hash_map_test.cc"],
+ copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"],
+ tags = NOTEST_TAGS_NONMOBILE,
+ deps = [
+ ":flat_hash_map",
+ ":hash_generator_testing",
+ ":unordered_map_constructor_test",
+ ":unordered_map_lookup_test",
+ ":unordered_map_modifiers_test",
+ "//absl/types:any",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "flat_hash_set",
+ hdrs = ["flat_hash_set.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":container_memory",
+ ":hash_function_defaults",
+ ":raw_hash_set",
+ "//absl/algorithm:container",
+ "//absl/base:core_headers",
+ "//absl/memory",
+ ],
+)
+
+cc_test(
+ name = "flat_hash_set_test",
+ srcs = ["flat_hash_set_test.cc"],
+ copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
+ tags = NOTEST_TAGS_NONMOBILE,
+ deps = [
+ ":flat_hash_set",
+ ":hash_generator_testing",
+ ":unordered_set_constructor_test",
+ ":unordered_set_lookup_test",
+ ":unordered_set_modifiers_test",
+ "//absl/memory",
+ "//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "node_hash_map",
+ hdrs = ["node_hash_map.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":container_memory",
+ ":hash_function_defaults",
+ ":node_hash_policy",
+ ":raw_hash_map",
+ "//absl/algorithm:container",
+ "//absl/memory",
+ ],
+)
+
+cc_test(
+ name = "node_hash_map_test",
+ srcs = ["node_hash_map_test.cc"],
+ copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"],
+ tags = NOTEST_TAGS_NONMOBILE,
+ deps = [
+ ":hash_generator_testing",
+ ":node_hash_map",
+ ":tracked",
+ ":unordered_map_constructor_test",
+ ":unordered_map_lookup_test",
+ ":unordered_map_modifiers_test",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "node_hash_set",
+ hdrs = ["node_hash_set.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":hash_function_defaults",
+ ":node_hash_policy",
+ ":raw_hash_set",
+ "//absl/algorithm:container",
+ "//absl/memory",
+ ],
+)
+
+cc_test(
+ name = "node_hash_set_test",
+ srcs = ["node_hash_set_test.cc"],
+ copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
+ tags = NOTEST_TAGS_NONMOBILE,
+ deps = [
+ ":hash_generator_testing",
+ ":node_hash_set",
+ ":unordered_set_constructor_test",
+ ":unordered_set_lookup_test",
+ ":unordered_set_modifiers_test",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "container_memory",
+ hdrs = ["internal/container_memory.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/memory",
+ "//absl/utility",
+ ],
+)
+
+cc_test(
+ name = "container_memory_test",
+ srcs = ["internal/container_memory_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ tags = NOTEST_TAGS_NONMOBILE,
+ deps = [
+ ":container_memory",
+ "//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "hash_function_defaults",
+ hdrs = ["internal/hash_function_defaults.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/base:config",
+ "//absl/hash",
+ "//absl/strings",
+ ],
+)
+
+cc_test(
+ name = "hash_function_defaults_test",
+ srcs = ["internal/hash_function_defaults_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ tags = NOTEST_TAGS,
+ deps = [
+ ":hash_function_defaults",
+ "//absl/hash",
+ "//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "hash_generator_testing",
+ testonly = 1,
+ srcs = ["internal/hash_generator_testing.cc"],
+ hdrs = ["internal/hash_generator_testing.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_policy_testing",
+ "//absl/meta:type_traits",
+ "//absl/strings",
+ ],
+)
+
+cc_library(
+ name = "hash_policy_testing",
+ testonly = 1,
+ hdrs = ["internal/hash_policy_testing.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ "//absl/hash",
+ "//absl/strings",
+ ],
+)
+
+cc_test(
+ name = "hash_policy_testing_test",
+ srcs = ["internal/hash_policy_testing_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_policy_testing",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "hash_policy_traits",
+ hdrs = ["internal/hash_policy_traits.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = ["//absl/meta:type_traits"],
+)
+
+cc_test(
+ name = "hash_policy_traits_test",
+ srcs = ["internal/hash_policy_traits_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_policy_traits",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "hashtable_debug",
+ hdrs = ["internal/hashtable_debug.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":hashtable_debug_hooks",
+ ],
+)
+
+cc_library(
+ name = "hashtable_debug_hooks",
+ hdrs = ["internal/hashtable_debug_hooks.h"],
+ copts = ABSL_DEFAULT_COPTS,
+)
+
+cc_library(
+ name = "node_hash_policy",
+ hdrs = ["internal/node_hash_policy.h"],
+ copts = ABSL_DEFAULT_COPTS,
+)
+
+cc_test(
+ name = "node_hash_policy_test",
+ srcs = ["internal/node_hash_policy_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_policy_traits",
+ ":node_hash_policy",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "raw_hash_map",
+ hdrs = ["internal/raw_hash_map.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":container_memory",
+ ":raw_hash_set",
+ ],
+)
+
+cc_library(
+ name = "raw_hash_set",
+ srcs = ["internal/raw_hash_set.cc"],
+ hdrs = ["internal/raw_hash_set.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":compressed_tuple",
+ ":container_memory",
+ ":hash_policy_traits",
+ ":hashtable_debug_hooks",
+ ":layout",
+ "//absl/base:bits",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/base:endian",
+ "//absl/memory",
+ "//absl/meta:type_traits",
+ "//absl/types:optional",
+ "//absl/utility",
+ ],
+)
+
+cc_test(
+ name = "raw_hash_set_test",
+ srcs = ["internal/raw_hash_set_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkstatic = 1,
+ tags = NOTEST_TAGS,
+ deps = [
+ ":container_memory",
+ ":hash_function_defaults",
+ ":hash_policy_testing",
+ ":hashtable_debug",
+ ":raw_hash_set",
+ "//absl/base",
+ "//absl/base:core_headers",
+ "//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "raw_hash_set_allocator_test",
+ size = "small",
+ srcs = ["internal/raw_hash_set_allocator_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":raw_hash_set",
+ ":tracked",
+ "//absl/base:core_headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "layout",
+ hdrs = ["internal/layout.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/base:core_headers",
+ "//absl/meta:type_traits",
+ "//absl/strings",
+ "//absl/types:span",
+ "//absl/utility",
+ ],
+)
+
+cc_test(
+ name = "layout_test",
+ size = "small",
+ srcs = ["internal/layout_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ tags = NOTEST_TAGS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":layout",
+ "//absl/base",
+ "//absl/base:core_headers",
+ "//absl/types:span",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "tracked",
+ testonly = 1,
+ hdrs = ["internal/tracked.h"],
+ copts = ABSL_TEST_COPTS,
+)
+
+cc_library(
+ name = "unordered_map_constructor_test",
+ testonly = 1,
+ hdrs = ["internal/unordered_map_constructor_test.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_generator_testing",
+ ":hash_policy_testing",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "unordered_map_lookup_test",
+ testonly = 1,
+ hdrs = ["internal/unordered_map_lookup_test.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_generator_testing",
+ ":hash_policy_testing",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "unordered_map_modifiers_test",
+ testonly = 1,
+ hdrs = ["internal/unordered_map_modifiers_test.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_generator_testing",
+ ":hash_policy_testing",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "unordered_set_constructor_test",
+ testonly = 1,
+ hdrs = ["internal/unordered_set_constructor_test.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_generator_testing",
+ ":hash_policy_testing",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "unordered_set_lookup_test",
+ testonly = 1,
+ hdrs = ["internal/unordered_set_lookup_test.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_generator_testing",
+ ":hash_policy_testing",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "unordered_set_modifiers_test",
+ testonly = 1,
+ hdrs = ["internal/unordered_set_modifiers_test.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash_generator_testing",
+ ":hash_policy_testing",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_test(
+ name = "unordered_set_test",
+ srcs = ["internal/unordered_set_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ tags = NOTEST_TAGS_NONMOBILE,
+ deps = [
+ ":unordered_set_constructor_test",
+ ":unordered_set_lookup_test",
+ ":unordered_set_modifiers_test",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "unordered_map_test",
+ srcs = ["internal/unordered_map_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ tags = NOTEST_TAGS_NONMOBILE,
+ deps = [
+ ":unordered_map_constructor_test",
+ ":unordered_map_lookup_test",
+ ":unordered_map_modifiers_test",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index f56ce92d..8605facc 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -14,113 +14,674 @@
# limitations under the License.
#
+# This is deprecated and will be removed in the future. It also doesn't do
+# anything anyways. Prefer to use the library associated with the API you are
+# using.
+absl_cc_library(
+ NAME
+ container
+ SRCS
+ "internal/raw_hash_set.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
+)
-list(APPEND CONTAINER_PUBLIC_HEADERS
- "fixed_array.h"
- "inlined_vector.h"
+absl_cc_library(
+ NAME
+ compressed_tuple
+ HDRS
+ "internal/compressed_tuple.h"
+ DEPS
+ absl::utility
+ PUBLIC
)
+absl_cc_test(
+ NAME
+ compressed_tuple_test
+ SRCS
+ "internal/compressed_tuple_test.cc"
+ DEPS
+ absl::compressed_tuple
+ gmock_main
+)
-list(APPEND CONTAINER_INTERNAL_HEADERS
- "internal/test_instance_tracker.h"
+absl_cc_library(
+ NAME
+ fixed_array
+ HDRS
+ "fixed_array.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::compressed_tuple
+ absl::algorithm
+ absl::core_headers
+ absl::dynamic_annotations
+ absl::throw_delegate
+ absl::memory
+ PUBLIC
)
+absl_cc_test(
+ NAME
+ fixed_array_test
+ SRCS
+ "fixed_array_test.cc"
+ COPTS
+ ${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::fixed_array
+ absl::exception_testing
+ absl::hash_testing
+ absl::memory
+ gmock_main
+)
-absl_header_library(
- TARGET
- absl_container
- EXPORT_NAME
- container
+absl_cc_test(
+ NAME
+ fixed_array_test_noexceptions
+ SRCS
+ "fixed_array_test.cc"
+ DEPS
+ absl::fixed_array
+ absl::exception_testing
+ absl::hash_testing
+ absl::memory
+ gmock_main
)
+absl_cc_test(
+ NAME
+ fixed_array_exception_safety_test
+ SRCS
+ "fixed_array_exception_safety_test.cc"
+ COPTS
+ ${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::fixed_array
+ absl::exception_safety_testing
+ gmock_main
+)
-#
-## TESTS
-#
+absl_cc_library(
+ NAME
+ inlined_vector
+ HDRS
+ "inlined_vector.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::algorithm
+ absl::core_headers
+ absl::throw_delegate
+ absl::memory
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ inlined_vector_test
+ SRCS
+ "inlined_vector_test.cc"
+ COPTS
+ ${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::inlined_vector
+ absl::test_instance_tracker
+ absl::base
+ absl::core_headers
+ absl::exception_testing
+ absl::hash_testing
+ absl::memory
+ absl::strings
+ gmock_main
+)
+
+absl_cc_test(
+ NAME
+ inlined_vector_test_noexceptions
+ SRCS
+ "inlined_vector_test.cc"
+ DEPS
+ absl::inlined_vector
+ absl::test_instance_tracker
+ absl::base
+ absl::core_headers
+ absl::exception_testing
+ absl::hash_testing
+ absl::memory
+ absl::strings
+ gmock_main
+)
-list(APPEND TEST_INSTANCE_TRACKER_LIB_SRC
- "internal/test_instance_tracker.cc"
- ${CONTAINER_PUBLIC_HEADERS}
- ${CONTAINER_INTERNAL_HEADERS}
+absl_cc_library(
+ NAME
+ test_instance_tracker
+ HDRS
+ "internal/test_instance_tracker.h"
+ SRCS
+ "internal/test_instance_tracker.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ TESTONLY
)
+absl_cc_test(
+ NAME
+ test_instance_tracker_test
+ SRCS
+ "internal/test_instance_tracker_test.cc"
+ DEPS
+ absl::test_instance_tracker
+ gmock_main
+)
-absl_library(
- TARGET
- test_instance_tracker_lib
- SOURCES
- ${TEST_INSTANCE_TRACKER_LIB_SRC}
- PUBLIC_LIBRARIES
- absl::container
- DISABLE_INSTALL
+absl_cc_library(
+ NAME
+ flat_hash_map
+ HDRS
+ "flat_hash_map.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::container_memory
+ absl::hash_function_defaults
+ absl::raw_hash_map
+ absl::algorithm_container
+ absl::memory
+ PUBLIC
)
+absl_cc_test(
+ NAME
+ flat_hash_map_test
+ SRCS
+ "flat_hash_map_test.cc"
+ COPTS
+ "-DUNORDERED_MAP_CXX17"
+ DEPS
+ absl::flat_hash_map
+ absl::hash_generator_testing
+ absl::unordered_map_constructor_test
+ absl::unordered_map_lookup_test
+ absl::unordered_map_modifiers_test
+ absl::any
+ gmock_main
+)
+absl_cc_library(
+ NAME
+ flat_hash_set
+ HDRS
+ "flat_hash_set.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::container_memory
+ absl::hash_function_defaults
+ absl::raw_hash_set
+ absl::algorithm_container
+ absl::core_headers
+ absl::memory
+ PUBLIC
+)
-# test fixed_array_test
-set(FIXED_ARRAY_TEST_SRC "fixed_array_test.cc")
-set(FIXED_ARRAY_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib)
+absl_cc_test(
+ NAME
+ flat_hash_set_test
+ SRCS
+ "flat_hash_set_test.cc"
+ COPTS
+ "-DUNORDERED_SET_CXX17"
+ DEPS
+ absl::flat_hash_set
+ absl::hash_generator_testing
+ absl::unordered_set_constructor_test
+ absl::unordered_set_lookup_test
+ absl::unordered_set_modifiers_test
+ absl::memory
+ absl::strings
+ gmock_main
+)
-absl_test(
- TARGET
- fixed_array_test
- SOURCES
- ${FIXED_ARRAY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${FIXED_ARRAY_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_EXCEPTIONS_FLAG}
+absl_cc_library(
+ NAME
+ node_hash_map
+ HDRS
+ "node_hash_map.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::container_memory
+ absl::hash_function_defaults
+ absl::node_hash_policy
+ absl::raw_hash_map
+ absl::algorithm_container
+ absl::memory
+ PUBLIC
)
+absl_cc_test(
+ NAME
+ node_hash_map_test
+ SRCS
+ "node_hash_map_test.cc"
+ COPTS
+ "-DUNORDERED_MAP_CXX17"
+ DEPS
+ absl::hash_generator_testing
+ absl::node_hash_map
+ absl::tracked
+ absl::unordered_map_constructor_test
+ absl::unordered_map_lookup_test
+ absl::unordered_map_modifiers_test
+ gmock_main
+)
+absl_cc_library(
+ NAME
+ node_hash_set
+ HDRS
+ "node_hash_set.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::hash_function_defaults
+ absl::node_hash_policy
+ absl::raw_hash_set
+ absl::algorithm_container
+ absl::memory
+ PUBLIC
+)
-absl_test(
- TARGET
- fixed_array_test_noexceptions
- SOURCES
- ${FIXED_ARRAY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${FIXED_ARRAY_TEST_PUBLIC_LIBRARIES}
+absl_cc_test(
+ NAME
+ node_hash_set_test
+ SRCS
+ "node_hash_set_test.cc"
+ COPTS
+ "-DUNORDERED_SET_CXX17"
+ DEPS
+ absl::hash_generator_testing
+ absl::node_hash_set
+ absl::unordered_set_constructor_test
+ absl::unordered_set_lookup_test
+ absl::unordered_set_modifiers_test
+ gmock_main
)
+absl_cc_library(
+ NAME
+ container_memory
+ HDRS
+ "internal/container_memory.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::memory
+ absl::utility
+ PUBLIC
+)
-# test inlined_vector_test
-set(INLINED_VECTOR_TEST_SRC "inlined_vector_test.cc")
-set(INLINED_VECTOR_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib)
+absl_cc_test(
+ NAME
+ container_memory_test
+ SRCS
+ "internal/container_memory_test.cc"
+ DEPS
+ absl::container_memory
+ absl::strings
+ gmock_main
+)
-absl_test(
- TARGET
- inlined_vector_test
- SOURCES
- ${INLINED_VECTOR_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INLINED_VECTOR_TEST_PUBLIC_LIBRARIES}
+absl_cc_library(
+ NAME
+ hash_function_defaults
+ HDRS
+ "internal/hash_function_defaults.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ absl::hash
+ absl::strings
+ PUBLIC
)
-absl_test(
- TARGET
- inlined_vector_test_noexceptions
- SOURCES
- ${INLINED_VECTOR_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INLINED_VECTOR_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_NOEXCEPTION_CXXFLAGS}
+absl_cc_test(
+ NAME
+ hash_function_defaults_test
+ SRCS
+ "internal/hash_function_defaults_test.cc"
+ DEPS
+ absl::hash_function_defaults
+ absl::hash
+ absl::strings
+ gmock_main
)
+absl_cc_library(
+ NAME
+ hash_generator_testing
+ HDRS
+ "internal/hash_generator_testing.h"
+ SRCS
+ "internal/hash_generator_testing.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_policy_testing
+ absl::meta
+ absl::strings
+ TESTONLY
+)
-# test test_instance_tracker_test
-set(TEST_INSTANCE_TRACKER_TEST_SRC "internal/test_instance_tracker_test.cc")
-set(TEST_INSTANCE_TRACKER_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib)
+absl_cc_library(
+ NAME
+ hash_policy_testing
+ HDRS
+ "internal/hash_policy_testing.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash
+ absl::strings
+ TESTONLY
+)
+absl_cc_test(
+ NAME
+ hash_policy_testing_test
+ SRCS
+ "internal/hash_policy_testing_test.cc"
+ DEPS
+ absl::hash_policy_testing
+ gmock_main
+)
-absl_test(
- TARGET
- test_instance_tracker_test
- SOURCES
- ${TEST_INSTANCE_TRACKER_TEST_SRC}
- PUBLIC_LIBRARIES
- ${TEST_INSTANCE_TRACKER_TEST_PUBLIC_LIBRARIES}
+absl_cc_library(
+ NAME
+ hash_policy_traits
+ HDRS
+ "internal/hash_policy_traits.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::meta
+ PUBLIC
)
+absl_cc_test(
+ NAME
+ hash_policy_traits_test
+ SRCS
+ "internal/hash_policy_traits_test.cc"
+ DEPS
+ absl::hash_policy_traits
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ hashtable_debug
+ HDRS
+ "internal/hashtable_debug.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::hashtable_debug_hooks
+)
+
+absl_cc_library(
+ NAME
+ hashtable_debug_hooks
+ HDRS
+ "internal/hashtable_debug_hooks.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ node_hash_policy
+ HDRS
+ "internal/node_hash_policy.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ node_hash_policy_test
+ SRCS
+ "internal/node_hash_policy_test.cc"
+ DEPS
+ absl::hash_policy_traits
+ absl::node_hash_policy
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ raw_hash_map
+ HDRS
+ "internal/raw_hash_map.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::container_memory
+ absl::raw_hash_set
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ raw_hash_set
+ HDRS
+ "internal/raw_hash_set.h"
+ SRCS
+ "internal/raw_hash_set.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::compressed_tuple
+ absl::container_memory
+ absl::hash_policy_traits
+ absl::hashtable_debug_hooks
+ absl::layout
+ absl::bits
+ absl::config
+ absl::core_headers
+ absl::endian
+ absl::memory
+ absl::meta
+ absl::optional
+ absl::utility
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ raw_hash_set_test
+ SRCS
+ "internal/raw_hash_set_test.cc"
+ DEPS
+ absl::container_memory
+ absl::hash_function_defaults
+ absl::hash_policy_testing
+ absl::hashtable_debug
+ absl::raw_hash_set
+ absl::base
+ absl::core_headers
+ absl::strings
+ gmock_main
+)
+
+absl_cc_test(
+ NAME
+ raw_hash_set_allocator_test
+ SRCS
+ "internal/raw_hash_set_allocator_test.cc"
+ DEPS
+ absl::raw_hash_set
+ absl::tracked
+ absl::core_headers
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ layout
+ HDRS
+ "internal/layout.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ absl::meta
+ absl::strings
+ absl::span
+ absl::utility
+ PUBLIC
+)
+absl_cc_test(
+ NAME
+ layout_test
+ SRCS
+ "internal/layout_test.cc"
+ DEPS
+ absl::layout
+ absl::base
+ absl::core_headers
+ absl::span
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ tracked
+ HDRS
+ "internal/tracked.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_map_constructor_test
+ HDRS
+ "internal/unordered_map_constructor_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_map_lookup_test
+ HDRS
+ "internal/unordered_map_lookup_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_map_modifiers_test
+ HDRS
+ "internal/unordered_map_modifiers_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_set_constructor_test
+ HDRS
+ "internal/unordered_set_constructor_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_set_lookup_test
+ HDRS
+ "internal/unordered_set_lookup_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_set_modifiers_test
+ HDRS
+ "internal/unordered_set_modifiers_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_test(
+ NAME
+ unordered_set_test
+ SRCS
+ "internal/unordered_set_test.cc"
+ DEPS
+ absl::unordered_set_constructor_test
+ absl::unordered_set_lookup_test
+ absl::unordered_set_modifiers_test
+ gmock_main
+)
+
+absl_cc_test(
+ NAME
+ unordered_map_test
+ SRCS
+ "internal/unordered_map_test.cc"
+ DEPS
+ absl::unordered_map_constructor_test
+ absl::unordered_map_lookup_test
+ absl::unordered_map_modifiers_test
+ gmock_main
+)
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index daa4eb22..7f6a3afd 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Abseil Authors.
+// 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.
@@ -47,10 +47,11 @@
#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 {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
@@ -58,13 +59,13 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
// FixedArray
// -----------------------------------------------------------------------------
//
-// A `FixedArray` provides a run-time fixed-size array, allocating small arrays
-// inline for efficiency and correctness.
+// 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
+// `FixedArray` automatically determine the number of elements
// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
-// `FixedArray<>` implementation will inline arrays of
+// `FixedArray` implementation will use inline storage for arrays with a
// length <= `inline_elements`.
//
// Note that a `FixedArray` constructed with a `size_type` argument will
@@ -77,65 +78,100 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
// heap allocation, it will do so with global `::operator new[]()` and
// `::operator delete[]()`, even if T provides class-scope overrides for these
// operators.
-template <typename T, size_t inlined = kFixedArrayUseDefault>
+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 Iter>
- using EnableIfForwardIterator = typename std::enable_if<
- std::is_convertible<
- typename std::iterator_traits<Iter>::iterator_category,
- std::forward_iterator_tag>::value,
- int>::type;
+ 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:
- // For playing nicely with stl:
- using value_type = T;
- using iterator = T*;
- using const_iterator = const T*;
+ using allocator_type = typename AllocatorTraits::allocator_type;
+ using value_type = typename allocator_type::value_type;
+ using pointer = typename allocator_type::pointer;
+ using const_pointer = typename allocator_type::const_pointer;
+ using reference = typename allocator_type::reference;
+ using const_reference = typename allocator_type::const_reference;
+ using size_type = typename allocator_type::size_type;
+ using difference_type = typename allocator_type::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>;
- using reference = T&;
- using const_reference = const T&;
- using pointer = T*;
- using const_pointer = const T*;
- using difference_type = ptrdiff_t;
- using size_type = size_t;
static constexpr size_type inline_elements =
- inlined == kFixedArrayUseDefault
- ? kInlineBytesDefault / sizeof(value_type)
- : inlined;
-
- FixedArray(const FixedArray& other) : rep_(other.begin(), other.end()) {}
- FixedArray(FixedArray&& other) noexcept(
- // clang-format off
- absl::allocator_is_nothrow<std::allocator<value_type>>::value &&
- // clang-format on
- std::is_nothrow_move_constructible<value_type>::value)
- : rep_(std::make_move_iterator(other.begin()),
- std::make_move_iterator(other.end())) {}
+ (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) : rep_(n) {}
+ 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) : rep_(n, 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: Iter must be a forward_iterator or better.
- template <typename Iter, EnableIfForwardIterator<Iter> = 0>
- FixedArray(Iter first, Iter last) : rep_(first, last) {}
-
- // Creates the array from an initializer_list.
- FixedArray(std::initializer_list<T> init_list)
- : FixedArray(init_list.begin(), init_list.end()) {}
+ // 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() {}
+ ~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.
@@ -145,7 +181,7 @@ class FixedArray {
// FixedArray::size()
//
// Returns the length of the fixed array.
- size_type size() const { return rep_.size(); }
+ size_type size() const { return storage_.size(); }
// FixedArray::max_size()
//
@@ -153,7 +189,7 @@ class FixedArray {
// `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);
+ return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
}
// FixedArray::empty()
@@ -170,12 +206,12 @@ class FixedArray {
//
// 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 AsValue(rep_.begin()); }
+ 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 AsValue(rep_.begin()); }
+ pointer data() { return AsValueType(storage_.begin()); }
// FixedArray::operator[]
//
@@ -295,7 +331,7 @@ class FixedArray {
// FixedArray::fill()
//
// Assigns the given `value` to all elements in the fixed array.
- void fill(const T& value) { std::fill(begin(), end(), value); }
+ 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.
@@ -324,18 +360,25 @@ class FixedArray {
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:
- // HolderTraits
+ // StorageElement
//
- // Wrapper to hold elements of type T for the case where T is an array type.
- // If 'T' is an array type, HolderTraits::type is a struct with a 'T v;'.
- // Otherwise, HolderTraits::type is simply 'T'.
+ // 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 T in a
- // struct whether it's an array or not: 'struct Holder { T v; };', but
- // that causes some paranoid diagnostics to misfire about uses of data(),
- // believing that 'data()' (aka '&rep_.begin().v') is a pointer to a single
- // element, rather than the packed array that it really is.
+ // 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);
@@ -344,157 +387,134 @@ class FixedArray {
// error: call to int __builtin___sprintf_chk(etc...)
// will always overflow destination buffer [-Werror]
//
- class HolderTraits {
- template <typename U>
- struct SelectImpl {
- using type = U;
- static pointer AsValue(type* p) { return p; }
- };
-
- // Partial specialization for elements of array type.
- template <typename U, size_t N>
- struct SelectImpl<U[N]> {
- struct Holder { U v[N]; };
- using type = Holder;
- static pointer AsValue(type* p) { return &p->v; }
- };
- using Impl = SelectImpl<value_type>;
-
- public:
- using type = typename Impl::type;
-
- static pointer AsValue(type *p) { return Impl::AsValue(p); }
-
- // TODO(billydonahue): fix the type aliasing violation
- // this assertion hints at.
- static_assert(sizeof(type) == sizeof(value_type),
- "Holder must be same size as value_type");
+ template <typename OuterT = value_type,
+ typename InnerT = absl::remove_extent_t<OuterT>,
+ size_t InnerN = std::extent<OuterT>::value>
+ struct StorageElementWrapper {
+ InnerT array[InnerN];
};
- using Holder = typename HolderTraits::type;
- static pointer AsValue(Holder *p) { return HolderTraits::AsValue(p); }
+ using StorageElement =
+ absl::conditional_t<std::is_array<value_type>::value,
+ StorageElementWrapper<value_type>, value_type>;
+ using StorageElementBuffer =
+ absl::aligned_storage_t<sizeof(StorageElement), alignof(StorageElement)>;
- // InlineSpace
- //
- // Allocate some space, not an array of elements of type T, so that we can
- // skip calling the T constructors and destructors for space we never use.
- // How many elements should we store inline?
- // a. If not specified, use a default of kInlineBytesDefault bytes (This is
- // currently 256 bytes, which seems small enough to not cause stack overflow
- // or unnecessary stack pollution, while still allowing stack allocation for
- // reasonably long character arrays).
- // b. Never use 0 length arrays (not ISO C++)
- //
- template <size_type N, typename = void>
- class InlineSpace {
- public:
- Holder* data() { return reinterpret_cast<Holder*>(space_.data()); }
- void AnnotateConstruct(size_t n) const { Annotate(n, true); }
- void AnnotateDestruct(size_t n) const { Annotate(n, false); }
+ static pointer AsValueType(pointer ptr) { return ptr; }
+ static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
+ return std::addressof(ptr->array);
+ }
- private:
-#ifndef ADDRESS_SANITIZER
- void Annotate(size_t, bool) const { }
-#else
- void Annotate(size_t n, bool creating) const {
- if (!n) return;
- const void* bot = &left_redzone_;
- const void* beg = space_.data();
- const void* end = space_.data() + n;
- const void* top = &right_redzone_ + 1;
- // args: (beg, end, old_mid, new_mid)
- if (creating) {
- ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, top, end);
- ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, beg, bot);
- } else {
- ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, end, top);
- ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, bot, beg);
- }
+ static_assert(sizeof(StorageElement) == sizeof(value_type), "");
+ static_assert(alignof(StorageElement) == alignof(value_type), "");
+
+ struct NonEmptyInlinedStorage {
+ StorageElement* data() {
+ return reinterpret_cast<StorageElement*>(inlined_storage_.data());
}
+
+#ifdef ADDRESS_SANITIZER
+ void* RedzoneBegin() { return &redzone_begin_; }
+ void* RedzoneEnd() { return &redzone_end_ + 1; }
#endif // ADDRESS_SANITIZER
- using Buffer =
- typename std::aligned_storage<sizeof(Holder), alignof(Holder)>::type;
+ void AnnotateConstruct(size_type);
+ void AnnotateDestruct(size_type);
- ADDRESS_SANITIZER_REDZONE(left_redzone_);
- std::array<Buffer, N> space_;
- ADDRESS_SANITIZER_REDZONE(right_redzone_);
+ ADDRESS_SANITIZER_REDZONE(redzone_begin_);
+ std::array<StorageElementBuffer, inline_elements> inlined_storage_;
+ ADDRESS_SANITIZER_REDZONE(redzone_end_);
};
- // specialization when N = 0.
- template <typename U>
- class InlineSpace<0, U> {
- public:
- Holder* data() { return nullptr; }
- void AnnotateConstruct(size_t) const {}
- void AnnotateDestruct(size_t) const {}
+ struct EmptyInlinedStorage {
+ StorageElement* data() { return nullptr; }
+ void AnnotateConstruct(size_type) {}
+ void AnnotateDestruct(size_type) {}
};
- // Rep
+ using InlinedStorage =
+ absl::conditional_t<inline_elements == 0, EmptyInlinedStorage,
+ NonEmptyInlinedStorage>;
+
+ // Storage
//
- // A const Rep object holds FixedArray's size and data pointer.
+ // 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 Rep : public InlineSpace<inline_elements> {
+ class Storage : public InlinedStorage {
public:
- Rep(size_type n, const value_type& val) : n_(n), p_(MakeHolder(n)) {
- std::uninitialized_fill_n(p_, n, val);
- }
+ Storage(size_type n, const allocator_type& a)
+ : size_alloc_(n, a), data_(InitializeData()) {}
- explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) {
- // Loop optimizes to nothing for trivially constructible T.
- for (Holder* p = p_; p != p_ + n; ++p)
- // Note: no parens: default init only.
- // Also note '::' to avoid Holder class placement new operator.
- ::new (static_cast<void*>(p)) Holder;
+ ~Storage() noexcept {
+ if (UsingInlinedStorage(size())) {
+ InlinedStorage::AnnotateDestruct(size());
+ } else {
+ AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
+ }
}
- template <typename Iter>
- Rep(Iter first, Iter last)
- : n_(std::distance(first, last)), p_(MakeHolder(n_)) {
- std::uninitialized_copy(first, last, AsValue(p_));
+ 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>();
}
- ~Rep() {
- // Destruction must be in reverse order.
- // Loop optimizes to nothing for trivially destructible T.
- for (Holder* p = end(); p != begin();) (--p)->~Holder();
- if (IsAllocated(size())) {
- std::allocator<Holder>().deallocate(p_, n_);
- } else {
- this->AnnotateDestruct(size());
- }
+ private:
+ static bool UsingInlinedStorage(size_type n) {
+ return n <= inline_elements;
}
- Holder* begin() const { return p_; }
- Holder* end() const { return p_ + n_; }
- size_type size() const { return n_; }
- private:
- Holder* MakeHolder(size_type n) {
- if (IsAllocated(n)) {
- return std::allocator<Holder>().allocate(n);
+ StorageElement* InitializeData() {
+ if (UsingInlinedStorage(size())) {
+ InlinedStorage::AnnotateConstruct(size());
+ return InlinedStorage::data();
} else {
- this->AnnotateConstruct(n);
- return this->data();
+ return reinterpret_cast<StorageElement*>(
+ AllocatorTraits::allocate(alloc(), size()));
}
}
- bool IsAllocated(size_type n) const { return n > inline_elements; }
-
- const size_type n_;
- Holder* const p_;
+ // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s
+ container_internal::CompressedTuple<size_type, allocator_type> size_alloc_;
+ StorageElement* data_;
};
-
- // Data members
- Rep rep_;
+ Storage storage_;
};
-template <typename T, size_t N>
-constexpr size_t FixedArray<T, N>::inline_elements;
-
-template <typename T, size_t N>
-constexpr size_t FixedArray<T, N>::kInlineBytesDefault;
-
-} // inline namespace lts_2018_06_20
+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;
+
+template <typename T, size_t N, typename A>
+void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct(
+ typename FixedArray<T, N, A>::size_type n) {
+#ifdef ADDRESS_SANITIZER
+ if (!n) return;
+ ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n);
+ ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin());
+#endif // 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 ADDRESS_SANITIZER
+ if (!n) return;
+ ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd());
+ ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data());
+#endif // ADDRESS_SANITIZER
+ static_cast<void>(n); // Mark used when not in asan mode
+}
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_CONTAINER_FIXED_ARRAY_H_
diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc
new file mode 100644
index 00000000..4d0430b3
--- /dev/null
+++ b/absl/container/fixed_array_exception_safety_test.cc
@@ -0,0 +1,119 @@
+// 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
+//
+// 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.
+
+#include <initializer_list>
+
+#include "absl/container/fixed_array.h"
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/exception_safety_testing.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+namespace {
+
+constexpr size_t kInlined = 25;
+constexpr size_t kSmallSize = kInlined / 2;
+constexpr size_t kLargeSize = kInlined * 2;
+
+constexpr int kInitialValue = 5;
+constexpr int kUpdatedValue = 10;
+
+using ::testing::TestThrowingCtor;
+
+using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
+using FixedArr = absl::FixedArray<Thrower, kInlined>;
+
+using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
+using MoveFixedArr = absl::FixedArray<MoveThrower, kInlined>;
+
+TEST(FixedArrayExceptionSafety, CopyConstructor) {
+ auto small = FixedArr(kSmallSize);
+ TestThrowingCtor<FixedArr>(small);
+
+ auto large = FixedArr(kLargeSize);
+ TestThrowingCtor<FixedArr>(large);
+}
+
+TEST(FixedArrayExceptionSafety, MoveConstructor) {
+ TestThrowingCtor<FixedArr>(FixedArr(kSmallSize));
+ TestThrowingCtor<FixedArr>(FixedArr(kLargeSize));
+
+ // TypeSpec::kNoThrowMove
+ TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kSmallSize));
+ TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kLargeSize));
+}
+
+TEST(FixedArrayExceptionSafety, SizeConstructor) {
+ TestThrowingCtor<FixedArr>(kSmallSize);
+ TestThrowingCtor<FixedArr>(kLargeSize);
+}
+
+TEST(FixedArrayExceptionSafety, SizeValueConstructor) {
+ TestThrowingCtor<FixedArr>(kSmallSize, Thrower());
+ TestThrowingCtor<FixedArr>(kLargeSize, Thrower());
+}
+
+TEST(FixedArrayExceptionSafety, IteratorConstructor) {
+ auto small = FixedArr(kSmallSize);
+ TestThrowingCtor<FixedArr>(small.begin(), small.end());
+
+ auto large = FixedArr(kLargeSize);
+ TestThrowingCtor<FixedArr>(large.begin(), large.end());
+}
+
+TEST(FixedArrayExceptionSafety, InitListConstructor) {
+ constexpr int small_inlined = 3;
+ using SmallFixedArr = absl::FixedArray<Thrower, small_inlined>;
+
+ TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{});
+ // Test inlined allocation
+ TestThrowingCtor<SmallFixedArr>(
+ std::initializer_list<Thrower>{Thrower{}, Thrower{}});
+ // Test out of line allocation
+ TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{
+ Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}});
+}
+
+testing::AssertionResult ReadMemory(FixedArr* fixed_arr) {
+ // Marked volatile to prevent optimization. Used for running asan tests.
+ volatile int sum = 0;
+ for (const auto& thrower : *fixed_arr) {
+ sum += thrower.Get();
+ }
+ return testing::AssertionSuccess() << "Values sum to [" << sum << "]";
+}
+
+TEST(FixedArrayExceptionSafety, Fill) {
+ auto test_fill = testing::MakeExceptionSafetyTester()
+ .WithContracts(ReadMemory)
+ .WithOperation([&](FixedArr* fixed_arr_ptr) {
+ auto thrower =
+ Thrower(kUpdatedValue, testing::nothrow_ctor);
+ fixed_arr_ptr->fill(thrower);
+ });
+
+ EXPECT_TRUE(
+ test_fill.WithInitialValue(FixedArr(kSmallSize, Thrower(kInitialValue)))
+ .Test());
+ EXPECT_TRUE(
+ test_fill.WithInitialValue(FixedArr(kLargeSize, Thrower(kInitialValue)))
+ .Test());
+}
+
+} // namespace
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc
index 2142132d..205ff41f 100644
--- a/absl/container/fixed_array_test.cc
+++ b/absl/container/fixed_array_test.cc
@@ -15,9 +15,11 @@
#include "absl/container/fixed_array.h"
#include <stdio.h>
+#include <cstring>
#include <list>
#include <memory>
#include <numeric>
+#include <scoped_allocator>
#include <stdexcept>
#include <string>
#include <vector>
@@ -25,6 +27,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/exception_testing.h"
+#include "absl/hash/hash_testing.h"
#include "absl/memory/memory.h"
using ::testing::ElementsAreArray;
@@ -607,6 +610,216 @@ TEST(FixedArrayTest, Fill) {
empty.fill(fill_val);
}
+// TODO(johnsoncj): Investigate InlinedStorage default initialization in GCC 4.x
+#ifndef __GNUC__
+TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) {
+ using T = char;
+ constexpr auto capacity = 10;
+ using FixedArrType = absl::FixedArray<T, capacity>;
+ using FixedArrBuffType =
+ absl::aligned_storage_t<sizeof(FixedArrType), alignof(FixedArrType)>;
+ constexpr auto scrubbed_bits = 0x95;
+ constexpr auto length = capacity / 2;
+
+ FixedArrBuffType buff;
+ std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrBuffType));
+
+ FixedArrType* arr =
+ ::new (static_cast<void*>(std::addressof(buff))) FixedArrType(length);
+ EXPECT_THAT(*arr, testing::Each(scrubbed_bits));
+ arr->~FixedArrType();
+}
+#endif // __GNUC__
+
+// 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 std::allocator<T> {
+ public:
+ using Alloc = std::allocator<T>;
+ using pointer = typename Alloc::pointer;
+ using size_type = typename Alloc::size_type;
+
+ CountingAllocator() : bytes_used_(nullptr), instance_count_(nullptr) {}
+ explicit CountingAllocator(int64_t* b)
+ : bytes_used_(b), instance_count_(nullptr) {}
+ CountingAllocator(int64_t* b, int64_t* a)
+ : bytes_used_(b), instance_count_(a) {}
+
+ template <typename U>
+ explicit CountingAllocator(const CountingAllocator<U>& x)
+ : Alloc(x),
+ bytes_used_(x.bytes_used_),
+ instance_count_(x.instance_count_) {}
+
+ pointer allocate(size_type n, const void* const hint = nullptr) {
+ assert(bytes_used_ != nullptr);
+ *bytes_used_ += n * sizeof(T);
+ return Alloc::allocate(n, hint);
+ }
+
+ void deallocate(pointer p, size_type n) {
+ Alloc::deallocate(p, n);
+ assert(bytes_used_ != nullptr);
+ *bytes_used_ -= n * sizeof(T);
+ }
+
+ template <typename... Args>
+ void construct(pointer p, Args&&... args) {
+ Alloc::construct(p, absl::forward<Args>(args)...);
+ if (instance_count_) {
+ *instance_count_ += 1;
+ }
+ }
+
+ void destroy(pointer p) {
+ Alloc::destroy(p);
+ if (instance_count_) {
+ *instance_count_ -= 1;
+ }
+ }
+
+ template <typename U>
+ class rebind {
+ public:
+ using other = CountingAllocator<U>;
+ };
+
+ int64_t* bytes_used_;
+ int64_t* instance_count_;
+};
+
+TEST(AllocatorSupportTest, CountInlineAllocations) {
+ constexpr size_t inlined_size = 4;
+ using Alloc = CountingAllocator<int>;
+ using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
+
+ int64_t allocated = 0;
+ int64_t active_instances = 0;
+
+ {
+ const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+ Alloc alloc(&allocated, &active_instances);
+
+ AllocFxdArr arr(ia, ia + inlined_size, alloc);
+ static_cast<void>(arr);
+ }
+
+ EXPECT_EQ(allocated, 0);
+ EXPECT_EQ(active_instances, 0);
+}
+
+TEST(AllocatorSupportTest, CountOutoflineAllocations) {
+ constexpr size_t inlined_size = 4;
+ using Alloc = CountingAllocator<int>;
+ using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
+
+ int64_t allocated = 0;
+ int64_t active_instances = 0;
+
+ {
+ const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
+ Alloc alloc(&allocated, &active_instances);
+
+ AllocFxdArr arr(ia, ia + ABSL_ARRAYSIZE(ia), alloc);
+
+ EXPECT_EQ(allocated, arr.size() * sizeof(int));
+ static_cast<void>(arr);
+ }
+
+ EXPECT_EQ(active_instances, 0);
+}
+
+TEST(AllocatorSupportTest, CountCopyInlineAllocations) {
+ constexpr size_t inlined_size = 4;
+ using Alloc = CountingAllocator<int>;
+ using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
+
+ int64_t allocated1 = 0;
+ int64_t allocated2 = 0;
+ int64_t active_instances = 0;
+ Alloc alloc(&allocated1, &active_instances);
+ Alloc alloc2(&allocated2, &active_instances);
+
+ {
+ int initial_value = 1;
+
+ AllocFxdArr arr1(inlined_size / 2, initial_value, alloc);
+
+ EXPECT_EQ(allocated1, 0);
+
+ AllocFxdArr arr2(arr1, alloc2);
+
+ EXPECT_EQ(allocated2, 0);
+ static_cast<void>(arr1);
+ static_cast<void>(arr2);
+ }
+
+ EXPECT_EQ(active_instances, 0);
+}
+
+TEST(AllocatorSupportTest, CountCopyOutoflineAllocations) {
+ constexpr size_t inlined_size = 4;
+ using Alloc = CountingAllocator<int>;
+ using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
+
+ int64_t allocated1 = 0;
+ int64_t allocated2 = 0;
+ int64_t active_instances = 0;
+ Alloc alloc(&allocated1, &active_instances);
+ Alloc alloc2(&allocated2, &active_instances);
+
+ {
+ int initial_value = 1;
+
+ AllocFxdArr arr1(inlined_size * 2, initial_value, alloc);
+
+ EXPECT_EQ(allocated1, arr1.size() * sizeof(int));
+
+ AllocFxdArr arr2(arr1, alloc2);
+
+ EXPECT_EQ(allocated2, inlined_size * 2 * sizeof(int));
+ static_cast<void>(arr1);
+ static_cast<void>(arr2);
+ }
+
+ EXPECT_EQ(active_instances, 0);
+}
+
+TEST(AllocatorSupportTest, SizeValAllocConstructor) {
+ using testing::AllOf;
+ using testing::Each;
+ using testing::SizeIs;
+
+ constexpr size_t inlined_size = 4;
+ using Alloc = CountingAllocator<int>;
+ using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
+
+ {
+ auto len = inlined_size / 2;
+ auto val = 0;
+ int64_t allocated = 0;
+ AllocFxdArr arr(len, val, Alloc(&allocated));
+
+ EXPECT_EQ(allocated, 0);
+ EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
+ }
+
+ {
+ auto len = inlined_size * 2;
+ auto val = 0;
+ int64_t allocated = 0;
+ AllocFxdArr arr(len, val, Alloc(&allocated));
+
+ EXPECT_EQ(allocated, len * sizeof(int));
+ EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
+ }
+}
+
#ifdef ADDRESS_SANITIZER
TEST(FixedArrayTest, AddressSanitizerAnnotations1) {
absl::FixedArray<int, 32> a(10);
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h
new file mode 100644
index 00000000..ed453348
--- /dev/null
+++ b/absl/container/flat_hash_map.h
@@ -0,0 +1,582 @@
+// 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
+//
+// 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.
+//
+// -----------------------------------------------------------------------------
+// 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/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 {
+inline namespace lts_2018_12_18 {
+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.
+//
+// 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 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: 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.
+ 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 init_type& k, Args&&... args):
+ // iterator try_emplace(const_iterator hint, init_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.
+ 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.
+ 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 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;
+
+ // 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;
+};
+
+namespace container_internal {
+
+template <class K, class V>
+struct FlatHashMapPolicy {
+ using slot_type = container_internal::slot_type<K, V>;
+ 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_type::construct(alloc, slot, std::forward<Args>(args)...);
+ }
+
+ template <class Allocator>
+ static void destroy(Allocator* alloc, slot_type* slot) {
+ slot_type::destroy(alloc, slot);
+ }
+
+ template <class Allocator>
+ static void transfer(Allocator* alloc, slot_type* new_slot,
+ slot_type* old_slot) {
+ slot_type::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
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_
diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc
new file mode 100644
index 00000000..02d2fa81
--- /dev/null
+++ b/absl/container/flat_hash_map_test.cc
@@ -0,0 +1,243 @@
+// 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
+//
+// 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.
+
+#include "absl/container/flat_hash_map.h"
+
+#include "absl/container/internal/hash_generator_testing.h"
+#include "absl/container/internal/unordered_map_constructor_test.h"
+#include "absl/container/internal/unordered_map_lookup_test.h"
+#include "absl/container/internal/unordered_map_modifiers_test.h"
+#include "absl/types/any.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+using ::absl::container_internal::hash_internal::Enum;
+using ::absl::container_internal::hash_internal::EnumClass;
+using ::testing::_;
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+
+template <class K, class V>
+using Map =
+ flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual, Alloc<>>;
+
+static_assert(!std::is_standard_layout<NonStandardLayout>(), "");
+
+using MapTypes =
+ ::testing::Types<Map<int, int>, Map<std::string, int>, Map<Enum, std::string>,
+ Map<EnumClass, int>, Map<int, NonStandardLayout>,
+ Map<NonStandardLayout, int>>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ConstructorTest, MapTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, LookupTest, MapTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ModifiersTest, MapTypes);
+
+TEST(FlatHashMap, StandardLayout) {
+ struct Int {
+ explicit Int(size_t value) : value(value) {}
+ Int() : value(0) { ADD_FAILURE(); }
+ Int(const Int& other) : value(other.value) { ADD_FAILURE(); }
+ Int(Int&&) = default;
+ bool operator==(const Int& other) const { return value == other.value; }
+ size_t value;
+ };
+ static_assert(std::is_standard_layout<Int>(), "");
+
+ struct Hash {
+ size_t operator()(const Int& obj) const { return obj.value; }
+ };
+
+ // Verify that neither the key nor the value get default-constructed or
+ // copy-constructed.
+ {
+ flat_hash_map<Int, Int, Hash> m;
+ m.try_emplace(Int(1), Int(2));
+ m.try_emplace(Int(3), Int(4));
+ m.erase(Int(1));
+ m.rehash(2 * m.bucket_count());
+ }
+ {
+ flat_hash_map<Int, Int, Hash> m;
+ m.try_emplace(Int(1), Int(2));
+ m.try_emplace(Int(3), Int(4));
+ m.erase(Int(1));
+ m.clear();
+ }
+}
+
+// gcc becomes unhappy if this is inside the method, so pull it out here.
+struct balast {};
+
+TEST(FlatHashMap, IteratesMsan) {
+ // Because SwissTable randomizes on pointer addresses, we keep old tables
+ // around to ensure we don't reuse old memory.
+ std::vector<absl::flat_hash_map<int, balast>> garbage;
+ for (int i = 0; i < 100; ++i) {
+ absl::flat_hash_map<int, balast> t;
+ for (int j = 0; j < 100; ++j) {
+ t[j];
+ for (const auto& p : t) EXPECT_THAT(p, Pair(_, _));
+ }
+ garbage.push_back(std::move(t));
+ }
+}
+
+// Demonstration of the "Lazy Key" pattern. This uses heterogeneous insert to
+// avoid creating expensive key elements when the item is already present in the
+// map.
+struct LazyInt {
+ explicit LazyInt(size_t value, int* tracker)
+ : value(value), tracker(tracker) {}
+
+ explicit operator size_t() const {
+ ++*tracker;
+ return value;
+ }
+
+ size_t value;
+ int* tracker;
+};
+
+struct Hash {
+ using is_transparent = void;
+ int* tracker;
+ size_t operator()(size_t obj) const {
+ ++*tracker;
+ return obj;
+ }
+ size_t operator()(const LazyInt& obj) const {
+ ++*tracker;
+ return obj.value;
+ }
+};
+
+struct Eq {
+ using is_transparent = void;
+ bool operator()(size_t lhs, size_t rhs) const {
+ return lhs == rhs;
+ }
+ bool operator()(size_t lhs, const LazyInt& rhs) const {
+ return lhs == rhs.value;
+ }
+};
+
+TEST(FlatHashMap, LazyKeyPattern) {
+ // hashes are only guaranteed in opt mode, we use assertions to track internal
+ // state that can cause extra calls to hash.
+ int conversions = 0;
+ int hashes = 0;
+ flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes});
+
+ m[LazyInt(1, &conversions)] = 1;
+ EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1)));
+ EXPECT_EQ(conversions, 1);
+#ifdef NDEBUG
+ EXPECT_EQ(hashes, 1);
+#endif
+
+ m[LazyInt(1, &conversions)] = 2;
+ EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2)));
+ EXPECT_EQ(conversions, 1);
+#ifdef NDEBUG
+ EXPECT_EQ(hashes, 2);
+#endif
+
+ m.try_emplace(LazyInt(2, &conversions), 3);
+ EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
+ EXPECT_EQ(conversions, 2);
+#ifdef NDEBUG
+ EXPECT_EQ(hashes, 3);
+#endif
+
+ m.try_emplace(LazyInt(2, &conversions), 4);
+ EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
+ EXPECT_EQ(conversions, 2);
+#ifdef NDEBUG
+ EXPECT_EQ(hashes, 4);
+#endif
+}
+
+TEST(FlatHashMap, BitfieldArgument) {
+ union {
+ int n : 1;
+ };
+ n = 0;
+ flat_hash_map<int, int> m;
+ m.erase(n);
+ m.count(n);
+ m.prefetch(n);
+ m.find(n);
+ m.contains(n);
+ m.equal_range(n);
+ m.insert_or_assign(n, n);
+ m.insert_or_assign(m.end(), n, n);
+ m.try_emplace(n);
+ m.try_emplace(m.end(), n);
+ m.at(n);
+ m[n];
+}
+
+TEST(FlatHashMap, MergeExtractInsert) {
+ // We can't test mutable keys, or non-copyable keys with flat_hash_map.
+ // Test that the nodes have the proper API.
+ absl::flat_hash_map<int, int> m = {{1, 7}, {2, 9}};
+ auto node = m.extract(1);
+ EXPECT_TRUE(node);
+ EXPECT_EQ(node.key(), 1);
+ EXPECT_EQ(node.mapped(), 7);
+ EXPECT_THAT(m, UnorderedElementsAre(Pair(2, 9)));
+
+ node.mapped() = 17;
+ m.insert(std::move(node));
+ EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9)));
+}
+#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
+TEST(FlatHashMap, Any) {
+ absl::flat_hash_map<int, absl::any> m;
+ m.emplace(1, 7);
+ auto it = m.find(1);
+ ASSERT_NE(it, m.end());
+ EXPECT_EQ(7, absl::any_cast<int>(it->second));
+
+ m.emplace(std::piecewise_construct, std::make_tuple(2), std::make_tuple(8));
+ it = m.find(2);
+ ASSERT_NE(it, m.end());
+ EXPECT_EQ(8, absl::any_cast<int>(it->second));
+
+ m.emplace(std::piecewise_construct, std::make_tuple(3),
+ std::make_tuple(absl::any(9)));
+ it = m.find(3);
+ ASSERT_NE(it, m.end());
+ EXPECT_EQ(9, absl::any_cast<int>(it->second));
+
+ struct H {
+ size_t operator()(const absl::any&) const { return 0; }
+ };
+ struct E {
+ bool operator()(const absl::any&, const absl::any&) const { return true; }
+ };
+ absl::flat_hash_map<absl::any, int, H, E> m2;
+ m2.emplace(1, 7);
+ auto it2 = m2.find(1);
+ ASSERT_NE(it2, m2.end());
+ EXPECT_EQ(7, it2->second);
+}
+#endif // __ANDROID__
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
new file mode 100644
index 00000000..b175b1bf
--- /dev/null
+++ b/absl/container/flat_hash_set.h
@@ -0,0 +1,491 @@
+// 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
+//
+// 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.
+//
+// -----------------------------------------------------------------------------
+// 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 {
+inline namespace lts_2018_12_18 {
+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()`, `operator[]()` 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_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.
+//
+// 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_map`
+ // 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_map` 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: 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.
+ 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 map 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 map, 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 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_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 map 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 map).
+ 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;
+};
+
+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
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_
diff --git a/absl/container/flat_hash_set_test.cc b/absl/container/flat_hash_set_test.cc
new file mode 100644
index 00000000..cabc2b59
--- /dev/null
+++ b/absl/container/flat_hash_set_test.cc
@@ -0,0 +1,128 @@
+// 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
+//
+// 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.
+
+#include "absl/container/flat_hash_set.h"
+
+#include <vector>
+
+#include "absl/container/internal/hash_generator_testing.h"
+#include "absl/container/internal/unordered_set_constructor_test.h"
+#include "absl/container/internal/unordered_set_lookup_test.h"
+#include "absl/container/internal/unordered_set_modifiers_test.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using ::absl::container_internal::hash_internal::Enum;
+using ::absl::container_internal::hash_internal::EnumClass;
+using ::testing::Pointee;
+using ::testing::UnorderedElementsAre;
+using ::testing::UnorderedElementsAreArray;
+
+template <class T>
+using Set =
+ absl::flat_hash_set<T, StatefulTestingHash, StatefulTestingEqual, Alloc<T>>;
+
+using SetTypes =
+ ::testing::Types<Set<int>, Set<std::string>, Set<Enum>, Set<EnumClass>>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ConstructorTest, SetTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, LookupTest, SetTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ModifiersTest, SetTypes);
+
+TEST(FlatHashSet, EmplaceString) {
+ std::vector<std::string> v = {"a", "b"};
+ absl::flat_hash_set<absl::string_view> hs(v.begin(), v.end());
+ EXPECT_THAT(hs, UnorderedElementsAreArray(v));
+}
+
+TEST(FlatHashSet, BitfieldArgument) {
+ union {
+ int n : 1;
+ };
+ n = 0;
+ absl::flat_hash_set<int> s = {n};
+ s.insert(n);
+ s.insert(s.end(), n);
+ s.insert({n});
+ s.erase(n);
+ s.count(n);
+ s.prefetch(n);
+ s.find(n);
+ s.contains(n);
+ s.equal_range(n);
+}
+
+TEST(FlatHashSet, MergeExtractInsert) {
+ struct Hash {
+ size_t operator()(const std::unique_ptr<int>& p) const { return *p; }
+ };
+ struct Eq {
+ bool operator()(const std::unique_ptr<int>& a,
+ const std::unique_ptr<int>& b) const {
+ return *a == *b;
+ }
+ };
+ absl::flat_hash_set<std::unique_ptr<int>, Hash, Eq> set1, set2;
+ set1.insert(absl::make_unique<int>(7));
+ set1.insert(absl::make_unique<int>(17));
+
+ set2.insert(absl::make_unique<int>(7));
+ set2.insert(absl::make_unique<int>(19));
+
+ EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17)));
+ EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(19)));
+
+ set1.merge(set2);
+
+ EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17), Pointee(19)));
+ EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7)));
+
+ auto node = set1.extract(absl::make_unique<int>(7));
+ EXPECT_TRUE(node);
+ EXPECT_THAT(node.value(), Pointee(7));
+ EXPECT_THAT(set1, UnorderedElementsAre(Pointee(17), Pointee(19)));
+
+ auto insert_result = set2.insert(std::move(node));
+ EXPECT_FALSE(node);
+ EXPECT_FALSE(insert_result.inserted);
+ EXPECT_TRUE(insert_result.node);
+ EXPECT_THAT(insert_result.node.value(), Pointee(7));
+ EXPECT_EQ(**insert_result.position, 7);
+ EXPECT_NE(insert_result.position->get(), insert_result.node.value().get());
+ EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7)));
+
+ node = set1.extract(absl::make_unique<int>(17));
+ EXPECT_TRUE(node);
+ EXPECT_THAT(node.value(), Pointee(17));
+ EXPECT_THAT(set1, UnorderedElementsAre(Pointee(19)));
+
+ node.value() = absl::make_unique<int>(23);
+
+ insert_result = set2.insert(std::move(node));
+ EXPECT_FALSE(node);
+ EXPECT_TRUE(insert_result.inserted);
+ EXPECT_FALSE(insert_result.node);
+ EXPECT_EQ(**insert_result.position, 23);
+ EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23)));
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 183ade54..37714baf 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Abseil Authors.
+// 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.
@@ -20,17 +20,17 @@
// 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 size N at which to inline as one
-// of its template parameters. Vectors of length <= N are provided inline.
-// Typically N is very small (e.g., 4) so that sequences that are expected to be
-// short do not require allocations.
-
-// An `absl::InlinedVector` does not usually require a specific allocator; if
+//
+// 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) and it will generally use the
-// default allocator in that case; optionally, a custom allocator may be
-// specified using an `absl::InlinedVector<T,N,A>` construction.
+// 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_
@@ -53,7 +53,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// InlinedVector
@@ -62,12 +62,30 @@ inline namespace lts_2018_06_20 {
// 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
-// size, 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
+// 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> >
+template <typename T, size_t N, typename A = std::allocator<T>>
class InlinedVector {
- using AllocatorTraits = std::allocator_traits<A>;
+ static_assert(N > 0, "InlinedVector requires inline capacity greater than 0");
+ constexpr static typename A::size_type inlined_capacity() {
+ return static_cast<typename A::size_type>(N);
+ }
+
+ template <typename Iterator>
+ using DisableIfIntegral =
+ absl::enable_if_t<!std::is_integral<Iterator>::value>;
+
+ template <typename Iterator>
+ using EnableIfInputIterator = absl::enable_if_t<std::is_convertible<
+ typename std::iterator_traits<Iterator>::iterator_category,
+ std::input_iterator_tag>::value>;
+
+ template <typename Iterator>
+ using IteratorCategory =
+ typename std::iterator_traits<Iterator>::iterator_category;
+
+ using rvalue_reference = typename A::value_type&&;
public:
using allocator_type = A;
@@ -83,51 +101,64 @@ class InlinedVector {
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ // ---------------------------------------------------------------------------
+ // InlinedVector Constructors and Destructor
+ // ---------------------------------------------------------------------------
+
+ // Creates an empty inlined vector with a default initialized allocator.
InlinedVector() noexcept(noexcept(allocator_type()))
: allocator_and_tag_(allocator_type()) {}
+ // Creates an empty inlined vector with a specified allocator.
explicit InlinedVector(const allocator_type& alloc) noexcept
: allocator_and_tag_(alloc) {}
- // Create a vector with n copies of value_type().
- explicit InlinedVector(size_type n) : allocator_and_tag_(allocator_type()) {
+ // Creates an inlined vector with `n` copies of `value_type()`.
+ explicit InlinedVector(size_type n,
+ const allocator_type& alloc = allocator_type())
+ : allocator_and_tag_(alloc) {
InitAssign(n);
}
- // Create a vector with n copies of elem
- InlinedVector(size_type n, const value_type& elem,
+ // Creates an inlined vector with `n` copies of `v`.
+ InlinedVector(size_type n, const_reference v,
const allocator_type& alloc = allocator_type())
: allocator_and_tag_(alloc) {
- InitAssign(n, elem);
+ InitAssign(n, v);
}
- // Create and initialize with the elements [first .. last).
- // The unused enable_if argument restricts this constructor so that it is
- // elided when value_type is an integral type. This prevents ambiguous
- // interpretation between a call to this constructor with two integral
- // arguments and a call to the preceding (n, elem) constructor.
- template <typename InputIterator>
- InlinedVector(
- InputIterator first, InputIterator last,
- const allocator_type& alloc = allocator_type(),
- typename std::enable_if<!std::is_integral<InputIterator>::value>::type* =
- nullptr)
+ // Creates an inlined vector of copies of the values in `init_list`.
+ InlinedVector(std::initializer_list<value_type> init_list,
+ const allocator_type& alloc = allocator_type())
: allocator_and_tag_(alloc) {
- AppendRange(first, last);
+ AppendRange(init_list.begin(), init_list.end(),
+ IteratorCategory<decltype(init_list.begin())>{});
}
- InlinedVector(std::initializer_list<value_type> init,
+ // Creates an inlined vector with elements constructed from the provided
+ // 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 InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
+ InlinedVector(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type())
: allocator_and_tag_(alloc) {
- AppendRange(init.begin(), init.end());
+ AppendRange(first, last, IteratorCategory<InputIterator>{});
}
- InlinedVector(const InlinedVector& v);
- InlinedVector(const InlinedVector& v, const allocator_type& alloc);
+ // Creates a copy of `other` using `other`'s allocator.
+ InlinedVector(const InlinedVector& other);
- // This move constructor does not allocate and only moves the underlying
+ // Creates a copy of `other` but with a specified allocator.
+ InlinedVector(const InlinedVector& other, const allocator_type& alloc);
+
+ // Creates an inlined vector by moving in the contents of `other`.
+ //
+ // NOTE: This move constructor does not allocate and only moves the underlying
// objects, so its `noexcept` specification depends on whether moving the
- // underlying objects can throw or not. We assume
+ // underlying objects can throw or not. We assume:
// a) move constructors should only throw due to allocation failure and
// b) if `value_type`'s move constructor allocates, it uses the same
// allocation function as the `InlinedVector`'s allocator, so the move
@@ -137,408 +168,422 @@ class InlinedVector {
absl::allocator_is_nothrow<allocator_type>::value ||
std::is_nothrow_move_constructible<value_type>::value);
- // This move constructor allocates and also moves the underlying objects, so
- // its `noexcept` specification depends on whether the allocation can throw
- // and whether moving the underlying objects can throw. Based on the same
- // assumptions above, the `noexcept` specification is dominated by whether the
- // allocation can throw regardless of whether `value_type`'s move constructor
- // is specified as `noexcept`.
+ // Creates an inlined vector by moving in the contents of `other`.
+ //
+ // NOTE: This move constructor allocates and subsequently moves the underlying
+ // objects, so its `noexcept` specification depends on whether the allocation
+ // can throw and whether moving the underlying objects can throw. Based on the
+ // same assumptions as above, the `noexcept` specification is dominated by
+ // whether the allocation can throw regardless of whether `value_type`'s move
+ // constructor is specified as `noexcept`.
InlinedVector(InlinedVector&& v, const allocator_type& alloc) noexcept(
absl::allocator_is_nothrow<allocator_type>::value);
~InlinedVector() { clear(); }
- InlinedVector& operator=(const InlinedVector& v) {
- if (this == &v) {
- return *this;
- }
- // Optimized to avoid reallocation.
- // Prefer reassignment to copy construction for elements.
- if (size() < v.size()) { // grow
- reserve(v.size());
- std::copy(v.begin(), v.begin() + size(), begin());
- std::copy(v.begin() + size(), v.end(), std::back_inserter(*this));
- } else { // maybe shrink
- erase(begin() + v.size(), end());
- std::copy(v.begin(), v.end(), begin());
- }
- return *this;
- }
-
- InlinedVector& operator=(InlinedVector&& v) {
- if (this == &v) {
- return *this;
- }
- if (v.allocated()) {
- clear();
- tag().set_allocated_size(v.size());
- init_allocation(v.allocation());
- v.tag() = Tag();
- } else {
- if (allocated()) clear();
- // Both are inlined now.
- if (size() < v.size()) {
- auto mid = std::make_move_iterator(v.begin() + size());
- std::copy(std::make_move_iterator(v.begin()), mid, begin());
- UninitializedCopy(mid, std::make_move_iterator(v.end()), end());
- } else {
- auto new_end = std::copy(std::make_move_iterator(v.begin()),
- std::make_move_iterator(v.end()), begin());
- Destroy(new_end, end());
- }
- tag().set_inline_size(v.size());
- }
- return *this;
- }
-
- InlinedVector& operator=(std::initializer_list<value_type> init) {
- AssignRange(init.begin(), init.end());
- return *this;
- }
+ // ---------------------------------------------------------------------------
+ // InlinedVector Member Accessors
+ // ---------------------------------------------------------------------------
- // InlinedVector::assign()
+ // `InlinedVector::empty()`
//
- // Replaces the contents of the inlined vector with copies of those in the
- // iterator range [first, last).
- template <typename InputIterator>
- void assign(
- InputIterator first, InputIterator last,
- typename std::enable_if<!std::is_integral<InputIterator>::value>::type* =
- nullptr) {
- AssignRange(first, last);
- }
-
- // Overload of `InlinedVector::assign()` to take values from elements of an
- // initializer list
- void assign(std::initializer_list<value_type> init) {
- AssignRange(init.begin(), init.end());
- }
-
- // Overload of `InlinedVector::assign()` to replace the first `n` elements of
- // the inlined vector with `elem` values.
- void assign(size_type n, const value_type& elem) {
- if (n <= size()) { // Possibly shrink
- std::fill_n(begin(), n, elem);
- erase(begin() + n, end());
- return;
- }
- // Grow
- reserve(n);
- std::fill_n(begin(), size(), elem);
- if (allocated()) {
- UninitializedFill(allocated_space() + size(), allocated_space() + n,
- elem);
- tag().set_allocated_size(n);
- } else {
- UninitializedFill(inlined_space() + size(), inlined_space() + n, elem);
- tag().set_inline_size(n);
- }
- }
+ // Checks if the inlined vector has no elements.
+ bool empty() const noexcept { return !size(); }
- // InlinedVector::size()
+ // `InlinedVector::size()`
//
// Returns the number of elements in the inlined vector.
size_type size() const noexcept { return tag().size(); }
- // InlinedVector::empty()
- //
- // Checks if the inlined vector has no elements.
- bool empty() const noexcept { return (size() == 0); }
-
- // InlinedVector::capacity()
- //
- // Returns the number of elements that can be stored in an inlined vector
- // without requiring a reallocation of underlying memory. Note that for
- // most inlined vectors, `capacity()` should equal its initial size `N`; for
- // inlined vectors which exceed this capacity, they will no longer be inlined,
- // and `capacity()` will equal its capacity on the allocated heap.
- size_type capacity() const noexcept {
- return allocated() ? allocation().capacity() : N;
- }
-
- // InlinedVector::max_size()
+ // `InlinedVector::max_size()`
//
// Returns the maximum number of elements the vector can hold.
size_type max_size() const noexcept {
// One bit of the size storage is used to indicate whether the inlined
- // vector is allocated; as a result, the maximum size of the container that
- // we can express is half of the max for our size type.
- return std::numeric_limits<size_type>::max() / 2;
+ // vector is allocated. As a result, the maximum size of the container that
+ // we can express is half of the max for `size_type`.
+ return (std::numeric_limits<size_type>::max)() / 2;
}
- // InlinedVector::data()
+ // `InlinedVector::capacity()`
//
- // Returns a const T* pointer to elements of the inlined vector. This pointer
- // can be used to access (but not modify) the contained elements.
- // Only results within the range `[0,size())` are defined.
- const_pointer data() const noexcept {
- return allocated() ? allocated_space() : inlined_space();
+ // Returns the number of elements that can be stored in the inlined vector
+ // without requiring a reallocation of underlying memory.
+ //
+ // NOTE: For most inlined vectors, `capacity()` should equal
+ // `inlined_capacity()`. For inlined vectors which exceed this capacity, they
+ // will no longer be inlined and `capacity()` will equal its capacity on the
+ // allocated heap.
+ size_type capacity() const noexcept {
+ return allocated() ? allocation().capacity() : inlined_capacity();
}
- // Overload of InlinedVector::data() to return a T* pointer to elements of the
- // inlined vector. This pointer can be used to access and modify the contained
- // elements.
+ // `InlinedVector::data()`
+ //
+ // Returns a `pointer` to elements of the inlined vector. This pointer can be
+ // used to access and modify the contained elements.
+ // Only results within the range [`0`, `size()`) are defined.
pointer data() noexcept {
return allocated() ? allocated_space() : inlined_space();
}
- // InlinedVector::clear()
- //
- // Removes all elements from the inlined vector.
- void clear() noexcept {
- size_type s = size();
- if (allocated()) {
- Destroy(allocated_space(), allocated_space() + s);
- allocation().Dealloc(allocator());
- } else if (s != 0) { // do nothing for empty vectors
- Destroy(inlined_space(), inlined_space() + s);
- }
- tag() = Tag();
+ // Overload of `InlinedVector::data()` to return a `const_pointer` to elements
+ // of the inlined vector. This pointer can be used to access (but not modify)
+ // the contained elements.
+ const_pointer data() const noexcept {
+ return allocated() ? allocated_space() : inlined_space();
}
- // InlinedVector::at()
+ // `InlinedVector::operator[]()`
//
- // Returns the ith element of an inlined vector.
- const value_type& at(size_type i) const {
- if (ABSL_PREDICT_FALSE(i >= size())) {
- base_internal::ThrowStdOutOfRange(
- "InlinedVector::at failed bounds check");
- }
+ // Returns a `reference` to the `i`th element of the inlined vector using the
+ // array operator.
+ reference operator[](size_type i) {
+ assert(i < size());
return data()[i];
}
- // InlinedVector::operator[]
- //
- // Returns the ith element of an inlined vector using the array operator.
- const value_type& operator[](size_type i) const {
+ // Overload of `InlinedVector::operator[]()` to return a `const_reference` to
+ // the `i`th element of the inlined vector.
+ const_reference operator[](size_type i) const {
assert(i < size());
return data()[i];
}
- // Overload of InlinedVector::at() to return the ith element of an inlined
- // vector.
- value_type& at(size_type i) {
- if (i >= size()) {
+ // `InlinedVector::at()`
+ //
+ // Returns a `reference` to the `i`th element of the inlined vector.
+ reference at(size_type i) {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
base_internal::ThrowStdOutOfRange(
- "InlinedVector::at failed bounds check");
+ "InlinedVector::at() failed bounds check");
}
return data()[i];
}
- // Overload of InlinedVector::operator[] to return the ith element of an
- // inlined vector.
- value_type& operator[](size_type i) {
- assert(i < size());
+ // Overload of `InlinedVector::at()` to return a `const_reference` to the
+ // `i`th element of the inlined vector.
+ const_reference at(size_type i) const {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
+ base_internal::ThrowStdOutOfRange(
+ "InlinedVector::at() failed bounds check");
+ }
return data()[i];
}
- // InlinedVector::back()
- //
- // Returns a reference to the last element of an inlined vector.
- value_type& back() {
- assert(!empty());
- return at(size() - 1);
- }
-
- // Overload of InlinedVector::back() returns a reference to the last element
- // of an inlined vector of const values.
- const value_type& back() const {
- assert(!empty());
- return at(size() - 1);
- }
-
- // InlinedVector::front()
+ // `InlinedVector::front()`
//
- // Returns a reference to the first element of an inlined vector.
- value_type& front() {
+ // Returns a `reference` to the first element of the inlined vector.
+ reference front() {
assert(!empty());
return at(0);
}
- // Overload of InlinedVector::front() returns a reference to the first element
- // of an inlined vector of const values.
- const value_type& front() const {
+ // Overload of `InlinedVector::front()` returns a `const_reference` to the
+ // first element of the inlined vector.
+ const_reference front() const {
assert(!empty());
return at(0);
}
- // InlinedVector::emplace_back()
+ // `InlinedVector::back()`
//
- // Constructs and appends an object to the inlined vector.
- //
- // Returns a reference to the inserted element.
- template <typename... Args>
- value_type& emplace_back(Args&&... args) {
- size_type s = size();
- assert(s <= capacity());
- if (ABSL_PREDICT_FALSE(s == capacity())) {
- return GrowAndEmplaceBack(std::forward<Args>(args)...);
- }
- assert(s < capacity());
-
- value_type* space;
- if (allocated()) {
- tag().set_allocated_size(s + 1);
- space = allocated_space();
- } else {
- tag().set_inline_size(s + 1);
- space = inlined_space();
- }
- return Construct(space + s, std::forward<Args>(args)...);
+ // Returns a `reference` to the last element of the inlined vector.
+ reference back() {
+ assert(!empty());
+ return at(size() - 1);
}
- // InlinedVector::push_back()
- //
- // Appends a const element to the inlined vector.
- void push_back(const value_type& t) { emplace_back(t); }
-
- // Overload of InlinedVector::push_back() to append a move-only element to the
- // inlined vector.
- void push_back(value_type&& t) { emplace_back(std::move(t)); }
-
- // InlinedVector::pop_back()
- //
- // Removes the last element (which is destroyed) in the inlined vector.
- void pop_back() {
+ // Overload of `InlinedVector::back()` to return a `const_reference` to the
+ // last element of the inlined vector.
+ const_reference back() const {
assert(!empty());
- size_type s = size();
- if (allocated()) {
- Destroy(allocated_space() + s - 1, allocated_space() + s);
- tag().set_allocated_size(s - 1);
- } else {
- Destroy(inlined_space() + s - 1, inlined_space() + s);
- tag().set_inline_size(s - 1);
- }
+ return at(size() - 1);
}
- // InlinedVector::resize()
+ // `InlinedVector::begin()`
//
- // Resizes the inlined vector to contain `n` elements. If `n` is smaller than
- // the inlined vector's current size, extra elements are destroyed. If `n` is
- // larger than the initial size, new elements are value-initialized.
- void resize(size_type n);
-
- // Overload of InlinedVector::resize() to resize the inlined vector to contain
- // `n` elements. If `n` is larger than the current size, enough copies of
- // `elem` are appended to increase its size to `n`.
- void resize(size_type n, const value_type& elem);
-
- // InlinedVector::begin()
- //
- // Returns an iterator to the beginning of the inlined vector.
+ // Returns an `iterator` to the beginning of the inlined vector.
iterator begin() noexcept { return data(); }
- // Overload of InlinedVector::begin() for returning a const iterator to the
- // beginning of the inlined vector.
+ // Overload of `InlinedVector::begin()` to return a `const_iterator` to
+ // the beginning of the inlined vector.
const_iterator begin() const noexcept { return data(); }
- // InlinedVector::cbegin()
- //
- // Returns a const iterator to the beginning of the inlined vector.
- const_iterator cbegin() const noexcept { return begin(); }
-
- // InlinedVector::end()
+ // `InlinedVector::end()`
//
- // Returns an iterator to the end of the inlined vector.
+ // Returns an `iterator` to the end of the inlined vector.
iterator end() noexcept { return data() + size(); }
- // Overload of InlinedVector::end() for returning a const iterator to the end
- // of the inlined vector.
+ // Overload of `InlinedVector::end()` to return a `const_iterator` to the
+ // end of the inlined vector.
const_iterator end() const noexcept { return data() + size(); }
- // InlinedVector::cend()
+ // `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.
+ // Returns a `const_iterator` to the end of the inlined vector.
const_iterator cend() const noexcept { return end(); }
- // InlinedVector::rbegin()
+ // `InlinedVector::rbegin()`
//
- // Returns a reverse iterator from the end of the inlined vector.
+ // Returns a `reverse_iterator` from the end of the inlined vector.
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
- // Overload of InlinedVector::rbegin() for returning a const reverse iterator
- // from the end of the inlined vector.
+ // Overload of `InlinedVector::rbegin()` to return a
+ // `const_reverse_iterator` from the end of the inlined vector.
const_reverse_iterator rbegin() const noexcept {
return const_reverse_iterator(end());
}
- // InlinedVector::crbegin()
+ // `InlinedVector::rend()`
//
- // Returns a const reverse iterator from the end of the inlined vector.
- const_reverse_iterator crbegin() const noexcept { return rbegin(); }
-
- // InlinedVector::rend()
- //
- // Returns a reverse iterator from the beginning of the inlined vector.
+ // Returns a `reverse_iterator` from the beginning of the inlined vector.
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
- // Overload of InlinedVector::rend() for returning a const reverse iterator
+ // Overload of `InlinedVector::rend()` to return a `const_reverse_iterator`
// from the beginning of the inlined vector.
const_reverse_iterator rend() const noexcept {
return const_reverse_iterator(begin());
}
- // InlinedVector::crend()
+ // `InlinedVector::crbegin()`
//
- // Returns a reverse iterator from the beginning of the inlined vector.
+ // 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::emplace()
+ // `InlinedVector::get_allocator()`
//
- // Constructs and inserts an object to the inlined vector at the given
- // `position`, returning an iterator pointing to the newly emplaced element.
- template <typename... Args>
- iterator emplace(const_iterator position, Args&&... args);
+ // Returns a copy of the allocator of the inlined vector.
+ allocator_type get_allocator() const { return allocator(); }
+
+ // ---------------------------------------------------------------------------
+ // InlinedVector Member Mutators
+ // ---------------------------------------------------------------------------
+
+ // `InlinedVector::operator=()`
+ //
+ // Replaces the contents of the inlined vector with copies of the elements in
+ // the provided `std::initializer_list`.
+ InlinedVector& operator=(std::initializer_list<value_type> init_list) {
+ AssignRange(init_list.begin(), init_list.end(),
+ IteratorCategory<decltype(init_list.begin())>{});
+ return *this;
+ }
+
+ // Overload of `InlinedVector::operator=()` to replace the contents of the
+ // inlined vector with the contents of `other`.
+ InlinedVector& operator=(const InlinedVector& other) {
+ if (ABSL_PREDICT_FALSE(this == &other)) return *this;
+
+ // Optimized to avoid reallocation.
+ // Prefer reassignment to copy construction for elements.
+ if (size() < other.size()) { // grow
+ reserve(other.size());
+ std::copy(other.begin(), other.begin() + size(), begin());
+ std::copy(other.begin() + size(), other.end(), std::back_inserter(*this));
+ } else { // maybe shrink
+ erase(begin() + other.size(), end());
+ std::copy(other.begin(), other.end(), begin());
+ }
+ return *this;
+ }
- // InlinedVector::insert()
+ // Overload of `InlinedVector::operator=()` to replace the contents of the
+ // inlined vector with the contents of `other`.
//
- // Inserts an element of the specified value at `position`, returning an
- // iterator pointing to the newly inserted element.
- iterator insert(const_iterator position, const value_type& v) {
+ // NOTE: As a result of calling this overload, `other` may be empty or it's
+ // contents may be left in a moved-from state.
+ InlinedVector& operator=(InlinedVector&& other) {
+ if (ABSL_PREDICT_FALSE(this == &other)) return *this;
+
+ if (other.allocated()) {
+ clear();
+ tag().set_allocated_size(other.size());
+ init_allocation(other.allocation());
+ other.tag() = Tag();
+ } else {
+ if (allocated()) clear();
+ // Both are inlined now.
+ if (size() < other.size()) {
+ auto mid = std::make_move_iterator(other.begin() + size());
+ std::copy(std::make_move_iterator(other.begin()), mid, begin());
+ UninitializedCopy(mid, std::make_move_iterator(other.end()), end());
+ } else {
+ auto new_end = std::copy(std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()), begin());
+ Destroy(new_end, end());
+ }
+ tag().set_inline_size(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) {
+ if (n <= size()) { // Possibly shrink
+ std::fill_n(begin(), n, v);
+ erase(begin() + n, end());
+ return;
+ }
+ // Grow
+ reserve(n);
+ std::fill_n(begin(), size(), v);
+ if (allocated()) {
+ UninitializedFill(allocated_space() + size(), allocated_space() + n, v);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space() + size(), inlined_space() + n, v);
+ tag().set_inline_size(n);
+ }
+ }
+
+ // Overload of `InlinedVector::assign()` to replace the contents of the
+ // inlined vector with copies of the values in the provided
+ // `std::initializer_list`.
+ void assign(std::initializer_list<value_type> init_list) {
+ AssignRange(init_list.begin(), init_list.end(),
+ IteratorCategory<decltype(init_list.begin())>{});
+ }
+
+ // Overload of `InlinedVector::assign()` to replace the contents of the
+ // inlined vector with values constructed from the range [`first`, `last`).
+ template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
+ void assign(InputIterator first, InputIterator last) {
+ AssignRange(first, last, IteratorCategory<InputIterator>{});
+ }
+
+ // `InlinedVector::resize()`
+ //
+ // Resizes the inlined vector to contain `n` elements. If `n` is smaller than
+ // the inlined vector's current size, extra elements are destroyed. If `n` is
+ // larger than the initial size, new elements are value-initialized.
+ void resize(size_type n);
+
+ // Overload of `InlinedVector::resize()` to resize the inlined vector to
+ // contain `n` elements where, if `n` is larger than `size()`, the new values
+ // will be copy-constructed from `v`.
+ void resize(size_type n, const_reference v);
+
+ // `InlinedVector::insert()`
+ //
+ // Copies `v` into `position`, returning an `iterator` pointing to the newly
+ // inserted element.
+ iterator insert(const_iterator position, const_reference v) {
return emplace(position, v);
}
- // Overload of InlinedVector::insert() for inserting an element of the
- // specified rvalue, returning an iterator pointing to the newly inserted
- // element.
- iterator insert(const_iterator position, value_type&& v) {
+ // Overload of `InlinedVector::insert()` for moving `v` into `position`,
+ // returning an iterator pointing to the newly inserted element.
+ iterator insert(const_iterator position, rvalue_reference v) {
return emplace(position, std::move(v));
}
- // Overload of InlinedVector::insert() for inserting `n` elements of the
- // specified value at `position`, returning an iterator pointing to the first
+ // Overload of `InlinedVector::insert()` for inserting `n` contiguous copies
+ // of `v` starting at `position`. Returns an `iterator` pointing to the first
// of the newly inserted elements.
- iterator insert(const_iterator position, size_type n, const value_type& v) {
+ iterator insert(const_iterator position, size_type n, const_reference v) {
return InsertWithCount(position, n, v);
}
- // Overload of `InlinedVector::insert()` to disambiguate the two
- // three-argument overloads of `insert()`, returning an iterator pointing to
- // the first of the newly inserted elements.
+ // Overload of `InlinedVector::insert()` for copying the contents of the
+ // `std::initializer_list` into the vector starting at `position`. Returns an
+ // `iterator` pointing to the first of the newly inserted elements.
+ iterator insert(const_iterator position,
+ std::initializer_list<value_type> init_list) {
+ return insert(position, init_list.begin(), init_list.end());
+ }
+
+ // Overload of `InlinedVector::insert()` for inserting elements constructed
+ // from the range [`first`, `last`). Returns an `iterator` pointing to the
+ // first of the newly inserted elements.
+ //
+ // NOTE: The `enable_if` is intended to disambiguate the two three-argument
+ // overloads of `insert()`.
template <typename InputIterator,
- typename = typename std::enable_if<std::is_convertible<
- typename std::iterator_traits<InputIterator>::iterator_category,
- std::input_iterator_tag>::value>::type>
+ typename = EnableIfInputIterator<InputIterator>>
iterator insert(const_iterator position, InputIterator first,
InputIterator last) {
- using IterType =
- typename std::iterator_traits<InputIterator>::iterator_category;
- return InsertWithRange(position, first, last, IterType());
+ return InsertWithRange(position, first, last,
+ IteratorCategory<InputIterator>());
}
- // Overload of InlinedVector::insert() for inserting a list of elements at
- // `position`, returning an iterator pointing to the first of the newly
- // inserted elements.
- iterator insert(const_iterator position,
- std::initializer_list<value_type> init) {
- return insert(position, init.begin(), init.end());
+ // `InlinedVector::emplace()`
+ //
+ // Constructs and inserts an object in the inlined vector at the given
+ // `position`, returning an `iterator` pointing to the newly emplaced element.
+ template <typename... Args>
+ iterator emplace(const_iterator position, Args&&... args);
+
+ // `InlinedVector::emplace_back()`
+ //
+ // Constructs and appends a new element to the end of the inlined vector,
+ // returning a `reference` to the emplaced element.
+ template <typename... Args>
+ reference emplace_back(Args&&... args) {
+ size_type s = size();
+ assert(s <= capacity());
+ if (ABSL_PREDICT_FALSE(s == capacity())) {
+ return GrowAndEmplaceBack(std::forward<Args>(args)...);
+ }
+ assert(s < capacity());
+
+ pointer space;
+ if (allocated()) {
+ tag().set_allocated_size(s + 1);
+ space = allocated_space();
+ } else {
+ tag().set_inline_size(s + 1);
+ space = inlined_space();
+ }
+ return Construct(space + s, std::forward<Args>(args)...);
+ }
+
+ // `InlinedVector::push_back()`
+ //
+ // Appends a copy of `v` to the end of the inlined vector.
+ void push_back(const_reference v) { static_cast<void>(emplace_back(v)); }
+
+ // Overload of `InlinedVector::push_back()` for moving `v` into a newly
+ // appended element.
+ void push_back(rvalue_reference v) {
+ static_cast<void>(emplace_back(std::move(v)));
+ }
+
+ // `InlinedVector::pop_back()`
+ //
+ // Destroys the element at the end of the inlined vector and shrinks the size
+ // by `1` (unless the inlined vector is empty, in which case this is a no-op).
+ void pop_back() noexcept {
+ assert(!empty());
+ size_type s = size();
+ if (allocated()) {
+ Destroy(allocated_space() + s - 1, allocated_space() + s);
+ tag().set_allocated_size(s - 1);
+ } else {
+ Destroy(inlined_space() + s - 1, inlined_space() + s);
+ tag().set_inline_size(s - 1);
+ }
}
- // InlinedVector::erase()
+ // `InlinedVector::erase()`
//
// Erases the element at `position` of the inlined vector, returning an
- // iterator pointing to the following element or the container's end if the
- // last element was erased.
+ // `iterator` pointing to the first element following the erased element.
+ //
+ // NOTE: May return the end iterator, which is not dereferencable.
iterator erase(const_iterator position) {
assert(position >= begin());
assert(position < end());
@@ -549,23 +594,36 @@ class InlinedVector {
return pos;
}
- // Overload of InlinedVector::erase() for erasing all elements in the
- // iterator range [first, last) in the inlined vector, returning an iterator
- // pointing to the first element following the range erased, or the
- // container's end if range included the container's last element.
- iterator erase(const_iterator first, const_iterator last);
+ // Overload of `InlinedVector::erase()` for erasing all elements in the
+ // range [`from`, `to`) in the inlined vector. Returns an `iterator` pointing
+ // to the first element following the range erased or the end iterator if `to`
+ // was the end iterator.
+ iterator erase(const_iterator from, const_iterator to);
+
+ // `InlinedVector::clear()`
+ //
+ // Destroys all elements in the inlined vector, sets the size of `0` and
+ // deallocates the heap allocation if the inlined vector was allocated.
+ void clear() noexcept {
+ size_type s = size();
+ if (allocated()) {
+ Destroy(allocated_space(), allocated_space() + s);
+ allocation().Dealloc(allocator());
+ } else if (s != 0) { // do nothing for empty vectors
+ Destroy(inlined_space(), inlined_space() + s);
+ }
+ tag() = Tag();
+ }
- // InlinedVector::reserve()
+ // `InlinedVector::reserve()`
//
// Enlarges the underlying representation of the inlined vector so it can hold
// at least `n` elements. This method does not change `size()` or the actual
// contents of the vector.
//
- // Note that if `n` does not exceed the inlined vector's initial size `N`,
- // `reserve()` will have no effect; if it does exceed its initial size,
- // `reserve()` will trigger an initial allocation and move the inlined vector
- // onto the heap. If the vector already exists on the heap and the requested
- // size exceeds it, a reallocation will be performed.
+ // NOTE: If `n` does not exceed `capacity()`, `reserve()` will have no
+ // effects. Otherwise, `reserve()` will reallocate, performing an n-time
+ // element-wise move of everything contained.
void reserve(size_type n) {
if (n > capacity()) {
// Make room for new elements
@@ -573,26 +631,25 @@ class InlinedVector {
}
}
- // InlinedVector::shrink_to_fit()
+ // `InlinedVector::shrink_to_fit()`
//
- // Reduces memory usage by freeing unused memory.
- // After this call `capacity()` will be equal to `max(N, size())`.
+ // Reduces memory usage by freeing unused memory. After this call, calls to
+ // `capacity()` will be equal to `(std::max)(inlined_capacity(), size())`.
//
- // If `size() <= N` and the elements are currently stored on the heap, they
- // will be moved to the inlined storage and the heap memory deallocated.
- // If `size() > N` and `size() < capacity()` the elements will be moved to
- // a reallocated storage on heap.
+ // If `size() <= inlined_capacity()` and the elements are currently stored on
+ // the heap, they will be moved to the inlined storage and the heap memory
+ // will be deallocated.
+ //
+ // If `size() > inlined_capacity()` and `size() < capacity()` the elements
+ // will be moved to a smaller heap allocation.
void shrink_to_fit() {
const auto s = size();
- if (!allocated() || s == capacity()) {
- // There's nothing to deallocate.
- return;
- }
+ if (ABSL_PREDICT_FALSE(!allocated() || s == capacity())) return;
- if (s <= N) {
+ if (s <= inlined_capacity()) {
// Move the elements to the inlined storage.
- // We have to do this using a temporary, because inlined_storage and
- // allocation_storage are in a union field.
+ // We have to do this using a temporary, because `inlined_storage` and
+ // `allocation_storage` are in a union field.
auto temp = std::move(*this);
assign(std::make_move_iterator(temp.begin()),
std::make_move_iterator(temp.end()));
@@ -600,8 +657,8 @@ class InlinedVector {
}
// Reallocate storage and move elements.
- // We can't simply use the same approach as above, because assign() would
- // call into reserve() internally and reserve larger capacity than we need.
+ // We can't simply use the same approach as above, because `assign()` would
+ // call into `reserve()` internally and reserve larger capacity than we need
Allocation new_allocation(allocator(), s);
UninitializedCopy(std::make_move_iterator(allocated_space()),
std::make_move_iterator(allocated_space() + s),
@@ -609,118 +666,126 @@ class InlinedVector {
ResetAllocation(new_allocation, s);
}
- // InlinedVector::swap()
+ // `InlinedVector::swap()`
//
// Swaps the contents of this inlined vector with the contents of `other`.
void swap(InlinedVector& other);
- // InlinedVector::get_allocator()
- //
- // Returns the allocator of this inlined vector.
- allocator_type get_allocator() const { return allocator(); }
+ template <typename Hash>
+ friend Hash AbslHashValue(Hash hash, const InlinedVector& inlined_vector) {
+ const_pointer p = inlined_vector.data();
+ size_type n = inlined_vector.size();
+ return Hash::combine(Hash::combine_contiguous(std::move(hash), p, n), n);
+ }
private:
- static_assert(N > 0, "inlined vector with nonpositive size");
-
- // It holds whether the vector is allocated or not in the lowest bit.
- // The size is held in the high bits:
- // size_ = (size << 1) | is_allocated;
+ // Holds whether the vector is allocated or not in the lowest bit and the size
+ // in the high bits:
+ // `size_ = (size << 1) | is_allocated;`
class Tag {
public:
Tag() : size_(0) {}
- size_type size() const { return size_ >> 1; }
- void add_size(size_type n) { size_ += n << 1; }
- void set_inline_size(size_type n) { size_ = n << 1; }
- void set_allocated_size(size_type n) { size_ = (n << 1) | 1; }
- bool allocated() const { return size_ & 1; }
+ size_type size() const { return size_ / 2; }
+ void add_size(size_type n) { size_ += n * 2; }
+ void set_inline_size(size_type n) { size_ = n * 2; }
+ void set_allocated_size(size_type n) { size_ = (n * 2) + 1; }
+ bool allocated() const { return size_ % 2; }
private:
size_type size_;
};
- // Derives from allocator_type to use the empty base class optimization.
- // If the allocator_type is stateless, we can 'store'
- // our instance of it for free.
+ // Derives from `allocator_type` to use the empty base class optimization.
+ // If the `allocator_type` is stateless, we can store our instance for free.
class AllocatorAndTag : private allocator_type {
public:
- explicit AllocatorAndTag(const allocator_type& a, Tag t = Tag())
- : allocator_type(a), tag_(t) {
- }
+ explicit AllocatorAndTag(const allocator_type& a) : allocator_type(a) {}
+
Tag& tag() { return tag_; }
const Tag& tag() const { return tag_; }
+
allocator_type& allocator() { return *this; }
const allocator_type& allocator() const { return *this; }
+
private:
Tag tag_;
};
class Allocation {
public:
- Allocation(allocator_type& a, // NOLINT(runtime/references)
- size_type capacity)
- : capacity_(capacity),
- buffer_(AllocatorTraits::allocate(a, capacity_)) {}
+ Allocation(allocator_type& a, size_type capacity)
+ : capacity_(capacity), buffer_(Create(a, capacity)) {}
- void Dealloc(allocator_type& a) { // NOLINT(runtime/references)
- AllocatorTraits::deallocate(a, buffer(), capacity());
+ void Dealloc(allocator_type& a) {
+ std::allocator_traits<allocator_type>::deallocate(a, buffer_, capacity_);
}
size_type capacity() const { return capacity_; }
- const value_type* buffer() const { return buffer_; }
- value_type* buffer() { return buffer_; }
+
+ const_pointer buffer() const { return buffer_; }
+
+ pointer buffer() { return buffer_; }
private:
+ static pointer Create(allocator_type& a, size_type n) {
+ return std::allocator_traits<allocator_type>::allocate(a, n);
+ }
+
size_type capacity_;
- value_type* buffer_;
+ pointer buffer_;
};
const Tag& tag() const { return allocator_and_tag_.tag(); }
+
Tag& tag() { return allocator_and_tag_.tag(); }
Allocation& allocation() {
return reinterpret_cast<Allocation&>(rep_.allocation_storage.allocation);
}
+
const Allocation& allocation() const {
return reinterpret_cast<const Allocation&>(
rep_.allocation_storage.allocation);
}
+
void init_allocation(const Allocation& allocation) {
new (&rep_.allocation_storage.allocation) Allocation(allocation);
}
- value_type* inlined_space() {
- return reinterpret_cast<value_type*>(&rep_.inlined_storage.inlined);
- }
- const value_type* inlined_space() const {
- return reinterpret_cast<const value_type*>(&rep_.inlined_storage.inlined);
+ // TODO(absl-team): investigate whether the reinterpret_cast is appropriate.
+ pointer inlined_space() {
+ return reinterpret_cast<pointer>(
+ std::addressof(rep_.inlined_storage.inlined[0]));
}
- value_type* allocated_space() {
- return allocation().buffer();
- }
- const value_type* allocated_space() const {
- return allocation().buffer();
+ const_pointer inlined_space() const {
+ return reinterpret_cast<const_pointer>(
+ std::addressof(rep_.inlined_storage.inlined[0]));
}
+ pointer allocated_space() { return allocation().buffer(); }
+
+ const_pointer allocated_space() const { return allocation().buffer(); }
+
const allocator_type& allocator() const {
return allocator_and_tag_.allocator();
}
- allocator_type& allocator() {
- return allocator_and_tag_.allocator();
- }
+
+ allocator_type& allocator() { return allocator_and_tag_.allocator(); }
bool allocated() const { return tag().allocated(); }
- // Enlarge the underlying representation so we can store size_ + delta elems.
- // The size is not changed, and any newly added memory is not initialized.
+ // Enlarge the underlying representation so we can store `size_ + delta` elems
+ // in allocated space. The size is not changed, and any newly added memory is
+ // not initialized.
void EnlargeBy(size_type delta);
- // Shift all elements from position to end() n places to the right.
+ // Shift all elements from `position` to `end()` by `n` places to the right.
// If the vector needs to be enlarged, memory will be allocated.
- // Returns iterators pointing to the start of the previously-initialized
+ // Returns `iterator`s pointing to the start of the previously-initialized
// portion and the start of the uninitialized portion of the created gap.
- // The number of initialized spots is pair.second - pair.first;
- // the number of raw spots is n - (pair.second - pair.first).
+ // The number of initialized spots is `pair.second - pair.first`. The number
+ // of raw spots is `n - (pair.second - pair.first)`.
//
// Updates the size of the InlinedVector internally.
std::pair<iterator, iterator> ShiftRight(const_iterator position,
@@ -740,13 +805,13 @@ class InlinedVector {
}
template <typename... Args>
- value_type& GrowAndEmplaceBack(Args&&... args) {
+ reference GrowAndEmplaceBack(Args&&... args) {
assert(size() == capacity());
const size_type s = size();
Allocation new_allocation(allocator(), 2 * capacity());
- value_type& new_element =
+ reference new_element =
Construct(new_allocation.buffer() + s, std::forward<Args>(args)...);
UninitializedCopy(std::make_move_iterator(data()),
std::make_move_iterator(data() + s),
@@ -758,98 +823,91 @@ class InlinedVector {
}
void InitAssign(size_type n);
- void InitAssign(size_type n, const value_type& t);
+
+ void InitAssign(size_type n, const_reference v);
template <typename... Args>
- value_type& Construct(pointer p, Args&&... args) {
- AllocatorTraits::construct(allocator(), p, std::forward<Args>(args)...);
+ reference Construct(pointer p, Args&&... args) {
+ std::allocator_traits<allocator_type>::construct(
+ allocator(), p, std::forward<Args>(args)...);
return *p;
}
- template <typename Iter>
- void UninitializedCopy(Iter src, Iter src_last, value_type* dst) {
+ template <typename Iterator>
+ void UninitializedCopy(Iterator src, Iterator src_last, pointer dst) {
for (; src != src_last; ++dst, ++src) Construct(dst, *src);
}
template <typename... Args>
- void UninitializedFill(value_type* dst, value_type* dst_last,
- const Args&... args) {
+ void UninitializedFill(pointer dst, pointer dst_last, const Args&... args) {
for (; dst != dst_last; ++dst) Construct(dst, args...);
}
- // Destroy [ptr, ptr_last) in place.
- void Destroy(value_type* ptr, value_type* ptr_last);
+ // Destroy [`from`, `to`) in place.
+ void Destroy(pointer from, pointer to);
- template <typename Iter>
- void AppendRange(Iter first, Iter last, std::input_iterator_tag) {
- std::copy(first, last, std::back_inserter(*this));
- }
+ template <typename Iterator>
+ void AppendRange(Iterator first, Iterator last, std::forward_iterator_tag);
- // Faster path for forward iterators.
- template <typename Iter>
- void AppendRange(Iter first, Iter last, std::forward_iterator_tag);
+ template <typename Iterator>
+ void AppendRange(Iterator first, Iterator last, std::input_iterator_tag);
- template <typename Iter>
- void AppendRange(Iter first, Iter last) {
- using IterTag = typename std::iterator_traits<Iter>::iterator_category;
- AppendRange(first, last, IterTag());
- }
+ template <typename Iterator>
+ void AssignRange(Iterator first, Iterator last, std::forward_iterator_tag);
- template <typename Iter>
- void AssignRange(Iter first, Iter last, std::input_iterator_tag);
-
- // Faster path for forward iterators.
- template <typename Iter>
- void AssignRange(Iter first, Iter last, std::forward_iterator_tag);
-
- template <typename Iter>
- void AssignRange(Iter first, Iter last) {
- using IterTag = typename std::iterator_traits<Iter>::iterator_category;
- AssignRange(first, last, IterTag());
- }
+ template <typename Iterator>
+ void AssignRange(Iterator first, Iterator last, std::input_iterator_tag);
iterator InsertWithCount(const_iterator position, size_type n,
- const value_type& v);
+ const_reference v);
- template <typename InputIter>
- iterator InsertWithRange(const_iterator position, InputIter first,
- InputIter last, std::input_iterator_tag);
- template <typename ForwardIter>
- iterator InsertWithRange(const_iterator position, ForwardIter first,
- ForwardIter last, std::forward_iterator_tag);
+ template <typename ForwardIterator>
+ iterator InsertWithRange(const_iterator position, ForwardIterator first,
+ ForwardIterator last, std::forward_iterator_tag);
- AllocatorAndTag allocator_and_tag_;
+ template <typename InputIterator>
+ iterator InsertWithRange(const_iterator position, InputIterator first,
+ InputIterator last, std::input_iterator_tag);
- // Either the inlined or allocated representation
+ // Stores either the inlined or allocated representation
union Rep {
- // Use struct to perform indirection that solves a bizarre compilation
- // error on Visual Studio (all known versions).
- struct {
- typename std::aligned_storage<sizeof(value_type),
- alignof(value_type)>::type inlined[N];
- } inlined_storage;
- struct {
- typename std::aligned_storage<sizeof(Allocation),
- alignof(Allocation)>::type allocation;
- } allocation_storage;
- } rep_;
+ using ValueTypeBuffer =
+ absl::aligned_storage_t<sizeof(value_type), alignof(value_type)>;
+ using AllocationBuffer =
+ absl::aligned_storage_t<sizeof(Allocation), alignof(Allocation)>;
+
+ // Structs wrap the buffers to perform indirection that solves a bizarre
+ // compilation error on Visual Studio (all known versions).
+ struct InlinedRep {
+ ValueTypeBuffer inlined[N];
+ };
+ struct AllocatedRep {
+ AllocationBuffer allocation;
+ };
+
+ InlinedRep inlined_storage;
+ AllocatedRep allocation_storage;
+ };
+
+ AllocatorAndTag allocator_and_tag_;
+ Rep rep_;
};
// -----------------------------------------------------------------------------
// InlinedVector Non-Member Functions
// -----------------------------------------------------------------------------
-// swap()
+// `swap()`
//
// Swaps the contents of two inlined vectors. This convenience function
-// simply calls InlinedVector::swap(other_inlined_vector).
+// simply calls `InlinedVector::swap()`.
template <typename T, size_t N, typename A>
void swap(InlinedVector<T, N, A>& a,
InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) {
a.swap(b);
}
-// operator==()
+// `operator==()`
//
// Tests the equivalency of the contents of two inlined vectors.
template <typename T, size_t N, typename A>
@@ -858,7 +916,7 @@ bool operator==(const InlinedVector<T, N, A>& a,
return absl::equal(a.begin(), a.end(), b.begin(), b.end());
}
-// operator!=()
+// `operator!=()`
//
// Tests the inequality of the contents of two inlined vectors.
template <typename T, size_t N, typename A>
@@ -867,7 +925,7 @@ bool operator!=(const InlinedVector<T, N, A>& a,
return !(a == b);
}
-// operator<()
+// `operator<()`
//
// Tests whether the contents of one inlined vector are less than the contents
// of another through a lexicographical comparison operation.
@@ -877,7 +935,7 @@ bool operator<(const InlinedVector<T, N, A>& a,
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
}
-// operator>()
+// `operator>()`
//
// Tests whether the contents of one inlined vector are greater than the
// contents of another through a lexicographical comparison operation.
@@ -887,7 +945,7 @@ bool operator>(const InlinedVector<T, N, A>& a,
return b < a;
}
-// operator<=()
+// `operator<=()`
//
// Tests whether the contents of one inlined vector are less than or equal to
// the contents of another through a lexicographical comparison operation.
@@ -897,7 +955,7 @@ bool operator<=(const InlinedVector<T, N, A>& a,
return !(b < a);
}
-// operator>=()
+// `operator>=()`
//
// Tests whether the contents of one inlined vector are greater than or equal to
// the contents of another through a lexicographical comparison operation.
@@ -909,97 +967,99 @@ bool operator>=(const InlinedVector<T, N, A>& a,
// -----------------------------------------------------------------------------
// Implementation of InlinedVector
-// -----------------------------------------------------------------------------
//
-// Do not depend on any implementation details below this line.
+// Do not depend on any below implementation details!
+// -----------------------------------------------------------------------------
template <typename T, size_t N, typename A>
-InlinedVector<T, N, A>::InlinedVector(const InlinedVector& v)
- : allocator_and_tag_(v.allocator()) {
- reserve(v.size());
+InlinedVector<T, N, A>::InlinedVector(const InlinedVector& other)
+ : allocator_and_tag_(other.allocator()) {
+ reserve(other.size());
if (allocated()) {
- UninitializedCopy(v.begin(), v.end(), allocated_space());
- tag().set_allocated_size(v.size());
+ UninitializedCopy(other.begin(), other.end(), allocated_space());
+ tag().set_allocated_size(other.size());
} else {
- UninitializedCopy(v.begin(), v.end(), inlined_space());
- tag().set_inline_size(v.size());
+ UninitializedCopy(other.begin(), other.end(), inlined_space());
+ tag().set_inline_size(other.size());
}
}
template <typename T, size_t N, typename A>
-InlinedVector<T, N, A>::InlinedVector(const InlinedVector& v,
+InlinedVector<T, N, A>::InlinedVector(const InlinedVector& other,
const allocator_type& alloc)
: allocator_and_tag_(alloc) {
- reserve(v.size());
+ reserve(other.size());
if (allocated()) {
- UninitializedCopy(v.begin(), v.end(), allocated_space());
- tag().set_allocated_size(v.size());
+ UninitializedCopy(other.begin(), other.end(), allocated_space());
+ tag().set_allocated_size(other.size());
} else {
- UninitializedCopy(v.begin(), v.end(), inlined_space());
- tag().set_inline_size(v.size());
+ UninitializedCopy(other.begin(), other.end(), inlined_space());
+ tag().set_inline_size(other.size());
}
}
template <typename T, size_t N, typename A>
-InlinedVector<T, N, A>::InlinedVector(InlinedVector&& v) noexcept(
+InlinedVector<T, N, A>::InlinedVector(InlinedVector&& other) noexcept(
absl::allocator_is_nothrow<allocator_type>::value ||
std::is_nothrow_move_constructible<value_type>::value)
- : allocator_and_tag_(v.allocator_and_tag_) {
- if (v.allocated()) {
+ : allocator_and_tag_(other.allocator_and_tag_) {
+ if (other.allocated()) {
// We can just steal the underlying buffer from the source.
// That leaves the source empty, so we clear its size.
- init_allocation(v.allocation());
- v.tag() = Tag();
+ init_allocation(other.allocation());
+ other.tag() = Tag();
} else {
- UninitializedCopy(std::make_move_iterator(v.inlined_space()),
- std::make_move_iterator(v.inlined_space() + v.size()),
- inlined_space());
+ UninitializedCopy(
+ std::make_move_iterator(other.inlined_space()),
+ std::make_move_iterator(other.inlined_space() + other.size()),
+ inlined_space());
}
}
template <typename T, size_t N, typename A>
-InlinedVector<T, N, A>::InlinedVector(
- InlinedVector&& v,
- const allocator_type&
- alloc) noexcept(absl::allocator_is_nothrow<allocator_type>::value)
+InlinedVector<T, N, A>::InlinedVector(InlinedVector&& other,
+ const allocator_type& alloc) noexcept( //
+ absl::allocator_is_nothrow<allocator_type>::value)
: allocator_and_tag_(alloc) {
- if (v.allocated()) {
- if (alloc == v.allocator()) {
+ if (other.allocated()) {
+ if (alloc == other.allocator()) {
// We can just steal the allocation from the source.
- tag() = v.tag();
- init_allocation(v.allocation());
- v.tag() = Tag();
+ tag() = other.tag();
+ init_allocation(other.allocation());
+ other.tag() = Tag();
} else {
// We need to use our own allocator
- reserve(v.size());
- UninitializedCopy(std::make_move_iterator(v.begin()),
- std::make_move_iterator(v.end()), allocated_space());
- tag().set_allocated_size(v.size());
+ reserve(other.size());
+ UninitializedCopy(std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()),
+ allocated_space());
+ tag().set_allocated_size(other.size());
}
} else {
- UninitializedCopy(std::make_move_iterator(v.inlined_space()),
- std::make_move_iterator(v.inlined_space() + v.size()),
- inlined_space());
- tag().set_inline_size(v.size());
+ UninitializedCopy(
+ std::make_move_iterator(other.inlined_space()),
+ std::make_move_iterator(other.inlined_space() + other.size()),
+ inlined_space());
+ tag().set_inline_size(other.size());
}
}
template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::InitAssign(size_type n, const value_type& t) {
- if (n > static_cast<size_type>(N)) {
+void InlinedVector<T, N, A>::InitAssign(size_type n, const_reference v) {
+ if (n > inlined_capacity()) {
Allocation new_allocation(allocator(), n);
init_allocation(new_allocation);
- UninitializedFill(allocated_space(), allocated_space() + n, t);
+ UninitializedFill(allocated_space(), allocated_space() + n, v);
tag().set_allocated_size(n);
} else {
- UninitializedFill(inlined_space(), inlined_space() + n, t);
+ UninitializedFill(inlined_space(), inlined_space() + n, v);
tag().set_inline_size(n);
}
}
template <typename T, size_t N, typename A>
void InlinedVector<T, N, A>::InitAssign(size_type n) {
- if (n > static_cast<size_type>(N)) {
+ if (n > inlined_capacity()) {
Allocation new_allocation(allocator(), n);
init_allocation(new_allocation);
UninitializedFill(allocated_space(), allocated_space() + n);
@@ -1031,7 +1091,7 @@ void InlinedVector<T, N, A>::resize(size_type n) {
}
template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::resize(size_type n, const value_type& elem) {
+void InlinedVector<T, N, A>::resize(size_type n, const_reference v) {
size_type s = size();
if (n < s) {
erase(begin() + n, end());
@@ -1040,23 +1100,23 @@ void InlinedVector<T, N, A>::resize(size_type n, const value_type& elem) {
reserve(n);
assert(capacity() >= n);
- // Fill new space with copies of 'elem'.
+ // Fill new space with copies of 'v'.
if (allocated()) {
- UninitializedFill(allocated_space() + s, allocated_space() + n, elem);
+ UninitializedFill(allocated_space() + s, allocated_space() + n, v);
tag().set_allocated_size(n);
} else {
- UninitializedFill(inlined_space() + s, inlined_space() + n, elem);
+ UninitializedFill(inlined_space() + s, inlined_space() + n, v);
tag().set_inline_size(n);
}
}
template <typename T, size_t N, typename A>
template <typename... Args>
-typename InlinedVector<T, N, A>::iterator InlinedVector<T, N, A>::emplace(
- const_iterator position, Args&&... args) {
+auto InlinedVector<T, N, A>::emplace(const_iterator position, Args&&... args)
+ -> iterator {
assert(position >= begin());
assert(position <= end());
- if (position == end()) {
+ if (ABSL_PREDICT_FALSE(position == end())) {
emplace_back(std::forward<Args>(args)...);
return end() - 1;
}
@@ -1076,14 +1136,14 @@ typename InlinedVector<T, N, A>::iterator InlinedVector<T, N, A>::emplace(
}
template <typename T, size_t N, typename A>
-typename InlinedVector<T, N, A>::iterator InlinedVector<T, N, A>::erase(
- const_iterator first, const_iterator last) {
- assert(begin() <= first);
- assert(first <= last);
- assert(last <= end());
+auto InlinedVector<T, N, A>::erase(const_iterator from, const_iterator to)
+ -> iterator {
+ assert(begin() <= from);
+ assert(from <= to);
+ assert(to <= end());
- iterator range_start = const_cast<iterator>(first);
- iterator range_end = const_cast<iterator>(last);
+ iterator range_start = const_cast<iterator>(from);
+ iterator range_end = const_cast<iterator>(to);
size_type s = size();
ptrdiff_t erase_gap = std::distance(range_start, range_end);
@@ -1104,10 +1164,9 @@ typename InlinedVector<T, N, A>::iterator InlinedVector<T, N, A>::erase(
template <typename T, size_t N, typename A>
void InlinedVector<T, N, A>::swap(InlinedVector& other) {
- using std::swap; // Augment ADL with std::swap.
- if (&other == this) {
- return;
- }
+ using std::swap; // Augment ADL with `std::swap`.
+ if (ABSL_PREDICT_FALSE(this == &other)) return;
+
if (allocated() && other.allocated()) {
// Both out of line, so just swap the tag, allocation, and allocator.
swap(tag(), other.tag());
@@ -1126,12 +1185,12 @@ void InlinedVector<T, N, A>::swap(InlinedVector& other) {
const size_type a_size = a->size();
const size_type b_size = b->size();
assert(a_size >= b_size);
- // 'a' is larger. Swap the elements up to the smaller array size.
- std::swap_ranges(a->inlined_space(),
- a->inlined_space() + b_size,
+ // `a` is larger. Swap the elements up to the smaller array size.
+ std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size,
b->inlined_space());
- // Move the remaining elements: A[b_size,a_size) -> B[b_size,a_size)
+ // Move the remaining elements:
+ // [`b_size`, `a_size`) from `a` -> [`b_size`, `a_size`) from `b`
b->UninitializedCopy(a->inlined_space() + b_size,
a->inlined_space() + a_size,
b->inlined_space() + b_size);
@@ -1143,6 +1202,7 @@ void InlinedVector<T, N, A>::swap(InlinedVector& other) {
assert(a->size() == b_size);
return;
}
+
// One is out of line, one is inline.
// We first move the elements from the inlined vector into the
// inlined space in the other vector. We then put the other vector's
@@ -1157,13 +1217,13 @@ void InlinedVector<T, N, A>::swap(InlinedVector& other) {
assert(b->allocated());
const size_type a_size = a->size();
const size_type b_size = b->size();
- // In an optimized build, b_size would be unused.
- (void)b_size;
+ // In an optimized build, `b_size` would be unused.
+ static_cast<void>(b_size);
- // Made Local copies of size(), don't need tag() accurate anymore
+ // Made Local copies of `size()`, don't need `tag()` accurate anymore
swap(a->tag(), b->tag());
- // Copy b_allocation out before b's union gets clobbered by inline_space.
+ // Copy `b_allocation` out before `b`'s union gets clobbered by `inline_space`
Allocation b_allocation = b->allocation();
b->UninitializedCopy(a->inlined_space(), a->inlined_space() + a_size,
@@ -1185,7 +1245,7 @@ void InlinedVector<T, N, A>::EnlargeBy(size_type delta) {
const size_type s = size();
assert(s <= capacity());
- size_type target = std::max(static_cast<size_type>(N), s + delta);
+ size_type target = std::max(inlined_capacity(), s + delta);
// Compute new capacity by repeatedly doubling current capacity
// TODO(psrc): Check and avoid overflow?
@@ -1217,7 +1277,7 @@ auto InlinedVector<T, N, A>::ShiftRight(const_iterator position, size_type n)
while (new_capacity < required_size) {
new_capacity <<= 1;
}
- // Move everyone into the new allocation, leaving a gap of n for the
+ // Move everyone into the new allocation, leaving a gap of `n` for the
// requested shift.
Allocation new_allocation(allocator(), new_capacity);
size_type index = position - begin();
@@ -1235,8 +1295,8 @@ auto InlinedVector<T, N, A>::ShiftRight(const_iterator position, size_type n)
start_used = start_raw;
} else {
// If we had enough space, it's a two-part move. Elements going into
- // previously-unoccupied space need an UninitializedCopy. Elements
- // going into a previously-occupied space are just a move.
+ // previously-unoccupied space need an `UninitializedCopy()`. Elements
+ // going into a previously-occupied space are just a `std::move()`.
iterator pos = const_cast<iterator>(position);
iterator raw_space = end();
size_type slots_in_used_space = raw_space - pos;
@@ -1262,28 +1322,26 @@ auto InlinedVector<T, N, A>::ShiftRight(const_iterator position, size_type n)
}
template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::Destroy(value_type* ptr, value_type* ptr_last) {
- for (value_type* p = ptr; p != ptr_last; ++p) {
- AllocatorTraits::destroy(allocator(), p);
+void InlinedVector<T, N, A>::Destroy(pointer from, pointer to) {
+ for (pointer cur = from; cur != to; ++cur) {
+ std::allocator_traits<allocator_type>::destroy(allocator(), cur);
}
-
- // Overwrite unused memory with 0xab so we can catch uninitialized usage.
- // Cast to void* to tell the compiler that we don't care that we might be
- // scribbling on a vtable pointer.
#ifndef NDEBUG
- if (ptr != ptr_last) {
- memset(reinterpret_cast<void*>(ptr), 0xab,
- sizeof(*ptr) * (ptr_last - ptr));
+ // Overwrite unused memory with `0xab` so we can catch uninitialized usage.
+ // Cast to `void*` to tell the compiler that we don't care that we might be
+ // scribbling on a vtable pointer.
+ if (from != to) {
+ auto len = sizeof(value_type) * std::distance(from, to);
+ std::memset(reinterpret_cast<void*>(from), 0xab, len);
}
#endif
}
template <typename T, size_t N, typename A>
-template <typename Iter>
-void InlinedVector<T, N, A>::AppendRange(Iter first, Iter last,
+template <typename Iterator>
+void InlinedVector<T, N, A>::AppendRange(Iterator first, Iterator last,
std::forward_iterator_tag) {
- using Length = typename std::iterator_traits<Iter>::difference_type;
- Length length = std::distance(first, last);
+ auto length = std::distance(first, last);
reserve(size() + length);
if (allocated()) {
UninitializedCopy(first, last, allocated_space() + size());
@@ -1295,24 +1353,17 @@ void InlinedVector<T, N, A>::AppendRange(Iter first, Iter last,
}
template <typename T, size_t N, typename A>
-template <typename Iter>
-void InlinedVector<T, N, A>::AssignRange(Iter first, Iter last,
+template <typename Iterator>
+void InlinedVector<T, N, A>::AppendRange(Iterator first, Iterator last,
std::input_iterator_tag) {
- // Optimized to avoid reallocation.
- // Prefer reassignment to copy construction for elements.
- iterator out = begin();
- for ( ; first != last && out != end(); ++first, ++out)
- *out = *first;
- erase(out, end());
std::copy(first, last, std::back_inserter(*this));
}
template <typename T, size_t N, typename A>
-template <typename Iter>
-void InlinedVector<T, N, A>::AssignRange(Iter first, Iter last,
+template <typename Iterator>
+void InlinedVector<T, N, A>::AssignRange(Iterator first, Iterator last,
std::forward_iterator_tag) {
- using Length = typename std::iterator_traits<Iter>::difference_type;
- Length length = std::distance(first, last);
+ auto length = std::distance(first, last);
// Prefer reassignment to copy construction for elements.
if (static_cast<size_type>(length) <= size()) {
erase(std::copy(first, last, begin()), end());
@@ -1331,11 +1382,25 @@ void InlinedVector<T, N, A>::AssignRange(Iter first, Iter last,
}
template <typename T, size_t N, typename A>
+template <typename Iterator>
+void InlinedVector<T, N, A>::AssignRange(Iterator first, Iterator last,
+ std::input_iterator_tag) {
+ // Optimized to avoid reallocation.
+ // Prefer reassignment to copy construction for elements.
+ iterator out = begin();
+ for (; first != last && out != end(); ++first, ++out) {
+ *out = *first;
+ }
+ erase(out, end());
+ std::copy(first, last, std::back_inserter(*this));
+}
+
+template <typename T, size_t N, typename A>
auto InlinedVector<T, N, A>::InsertWithCount(const_iterator position,
- size_type n, const value_type& v)
+ size_type n, const_reference v)
-> iterator {
assert(position >= begin() && position <= end());
- if (n == 0) return const_cast<iterator>(position);
+ if (ABSL_PREDICT_FALSE(n == 0)) return const_cast<iterator>(position);
value_type copy = v;
std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
@@ -1346,41 +1411,39 @@ auto InlinedVector<T, N, A>::InsertWithCount(const_iterator position,
}
template <typename T, size_t N, typename A>
-template <typename InputIter>
+template <typename ForwardIterator>
auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
- InputIter first, InputIter last,
- std::input_iterator_tag)
- -> iterator {
- assert(position >= begin() && position <= end());
- size_type index = position - cbegin();
- size_type i = index;
- while (first != last) insert(begin() + i++, *first++);
- return begin() + index;
-}
-
-// Overload of InlinedVector::InsertWithRange()
-template <typename T, size_t N, typename A>
-template <typename ForwardIter>
-auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
- ForwardIter first,
- ForwardIter last,
+ ForwardIterator first,
+ ForwardIterator last,
std::forward_iterator_tag)
-> iterator {
assert(position >= begin() && position <= end());
- if (first == last) {
- return const_cast<iterator>(position);
- }
- using Length = typename std::iterator_traits<ForwardIter>::difference_type;
- Length n = std::distance(first, last);
+ if (ABSL_PREDICT_FALSE(first == last)) return const_cast<iterator>(position);
+
+ auto n = std::distance(first, last);
std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
size_type used_spots = it_pair.second - it_pair.first;
- ForwardIter open_spot = std::next(first, used_spots);
+ ForwardIterator open_spot = std::next(first, used_spots);
std::copy(first, open_spot, it_pair.first);
UninitializedCopy(open_spot, last, it_pair.second);
return it_pair.first;
}
-} // inline namespace lts_2018_06_20
+template <typename T, size_t N, typename A>
+template <typename InputIterator>
+auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
+ InputIterator first,
+ InputIterator last,
+ std::input_iterator_tag)
+ -> iterator {
+ assert(position >= begin() && position <= end());
+ size_type index = position - cbegin();
+ size_type i = index;
+ while (first != last) insert(begin() + i++, *first++);
+ return begin() + index;
+}
+
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_CONTAINER_INLINED_VECTOR_H_
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
index 24f21749..a3ad0f8a 100644
--- a/absl/container/inlined_vector_benchmark.cc
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -66,7 +66,7 @@ BENCHMARK(BM_StdVectorFill)->Range(0, 1024);
// The purpose of the next two benchmarks is to verify that
// absl::InlinedVector is efficient when moving is more efficent than
// copying. To do so, we use strings that are larger than the short
-// std::string optimization.
+// string optimization.
bool StringRepresentedInline(std::string s) {
const char* chars = s.data();
std::string s1 = std::move(s);
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index 26a7d5bc..3a1ea8ac 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -31,6 +31,7 @@
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
#include "absl/container/internal/test_instance_tracker.h"
+#include "absl/hash/hash_testing.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
@@ -905,6 +906,8 @@ TYPED_TEST_P(InstanceTest, Swap) {
InstanceTracker tracker;
InstanceVec a, b;
const size_t inlined_capacity = a.capacity();
+ auto min_len = std::min(l1, l2);
+ auto max_len = std::max(l1, l2);
for (int i = 0; i < l1; i++) a.push_back(Instance(i));
for (int i = 0; i < l2; i++) b.push_back(Instance(100+i));
EXPECT_EQ(tracker.instances(), l1 + l2);
@@ -918,15 +921,15 @@ TYPED_TEST_P(InstanceTest, Swap) {
EXPECT_EQ(tracker.swaps(), 0); // Allocations are swapped.
EXPECT_EQ(tracker.moves(), 0);
} else if (a.size() <= inlined_capacity && b.size() <= inlined_capacity) {
- EXPECT_EQ(tracker.swaps(), std::min(l1, l2));
- // TODO(bsamwel): This should use moves when the type is movable.
- EXPECT_EQ(tracker.copies(), std::max(l1, l2) - std::min(l1, l2));
+ EXPECT_EQ(tracker.swaps(), min_len);
+ EXPECT_EQ((tracker.moves() ? tracker.moves() : tracker.copies()),
+ max_len - min_len);
} else {
// One is allocated and the other isn't. The allocation is transferred
// without copying elements, and the inlined instances are copied/moved.
EXPECT_EQ(tracker.swaps(), 0);
- // TODO(bsamwel): This should use moves when the type is movable.
- EXPECT_EQ(tracker.copies(), std::min(l1, l2));
+ EXPECT_EQ((tracker.moves() ? tracker.moves() : tracker.copies()),
+ min_len);
}
EXPECT_EQ(l1, b.size());
@@ -1725,42 +1728,87 @@ TEST(AllocatorSupportTest, ScopedAllocatorWorks) {
std::scoped_allocator_adaptor<CountingAllocator<StdVector>>;
using AllocVec = absl::InlinedVector<StdVector, 4, MyAlloc>;
+ // MSVC 2017's std::vector allocates different amounts of memory in debug
+ // versus opt mode.
+ int64_t test_allocated = 0;
+ StdVector v(CountingAllocator<int>{&test_allocated});
+ // The amount of memory allocated by a default constructed vector<int>
+ auto default_std_vec_allocated = test_allocated;
+ v.push_back(1);
+ // The amound of memory allocated by a copy-constructed vector<int> with one
+ // element.
+ int64_t one_element_std_vec_copy_allocated = test_allocated;
+
int64_t allocated = 0;
AllocVec vec(MyAlloc{CountingAllocator<StdVector>{&allocated}});
EXPECT_EQ(allocated, 0);
// This default constructs a vector<int>, but the allocator should pass itself
- // into the vector<int>.
+ // into the vector<int>, so check allocation compared to that.
// The absl::InlinedVector does not allocate any memory.
- // The vector<int> does not allocate any memory.
+ // The vector<int> may allocate any memory.
+ auto expected = default_std_vec_allocated;
vec.resize(1);
- EXPECT_EQ(allocated, 0);
+ EXPECT_EQ(allocated, expected);
// We make vector<int> allocate memory.
// It must go through the allocator even though we didn't construct the
- // vector directly.
+ // vector directly. This assumes that vec[0] doesn't need to grow its
+ // allocation.
+ expected += sizeof(int);
vec[0].push_back(1);
- EXPECT_EQ(allocated, sizeof(int) * 1);
+ EXPECT_EQ(allocated, expected);
// Another allocating vector.
+ expected += one_element_std_vec_copy_allocated;
vec.push_back(vec[0]);
- EXPECT_EQ(allocated, sizeof(int) * 2);
+ EXPECT_EQ(allocated, expected);
// Overflow the inlined memory.
// The absl::InlinedVector will now allocate.
+ expected += sizeof(StdVector) * 8 + default_std_vec_allocated * 3;
vec.resize(5);
- EXPECT_EQ(allocated, sizeof(int) * 2 + sizeof(StdVector) * 8);
+ EXPECT_EQ(allocated, expected);
// Adding one more in external mode should also work.
+ expected += one_element_std_vec_copy_allocated;
vec.push_back(vec[0]);
- EXPECT_EQ(allocated, sizeof(int) * 3 + sizeof(StdVector) * 8);
+ EXPECT_EQ(allocated, expected);
- // And extending these should still work.
+ // And extending these should still work. This assumes that vec[0] does not
+ // need to grow its allocation.
+ expected += sizeof(int);
vec[0].push_back(1);
- EXPECT_EQ(allocated, sizeof(int) * 4 + sizeof(StdVector) * 8);
+ EXPECT_EQ(allocated, expected);
vec.clear();
EXPECT_EQ(allocated, 0);
}
+TEST(AllocatorSupportTest, SizeAllocConstructor) {
+ constexpr int inlined_size = 4;
+ using Alloc = CountingAllocator<int>;
+ using AllocVec = absl::InlinedVector<int, inlined_size, Alloc>;
+
+ {
+ auto len = inlined_size / 2;
+ int64_t allocated = 0;
+ auto v = AllocVec(len, Alloc(&allocated));
+
+ // Inline storage used; allocator should not be invoked
+ EXPECT_THAT(allocated, 0);
+ EXPECT_THAT(v, AllOf(SizeIs(len), Each(0)));
+ }
+
+ {
+ auto len = inlined_size * 2;
+ int64_t allocated = 0;
+ auto v = AllocVec(len, Alloc(&allocated));
+
+ // Out of line storage used; allocation of 8 elements expected
+ EXPECT_THAT(allocated, len * sizeof(int));
+ EXPECT_THAT(v, AllOf(SizeIs(len), Each(0)));
+ }
+}
+
} // anonymous namespace
diff --git a/absl/container/internal/compressed_tuple.h b/absl/container/internal/compressed_tuple.h
new file mode 100644
index 00000000..29fe7c12
--- /dev/null
+++ b/absl/container/internal/compressed_tuple.h
@@ -0,0 +1,177 @@
+// 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
+//
+// 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.
+//
+// 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>();
+// ...
+//
+// http://en.cppreference.com/w/cpp/language/ebo
+
+#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
+#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "absl/utility/utility.h"
+
+#ifdef _MSC_VER
+// We need to mark these classes with this declspec to ensure that
+// CompressedTuple happens.
+#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases)
+#else // _MSC_VER
+#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
+#endif // _MSC_VER
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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
+}
+
+template <typename T>
+constexpr bool ShouldUseBase() {
+ return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>();
+}
+
+// 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 D, size_t I, bool = ShouldUseBase<ElemT<D, I>>()>
+struct Storage {
+ using T = ElemT<D, I>;
+ T value;
+ constexpr Storage() = default;
+ explicit constexpr Storage(T&& v) : value(absl::forward<T>(v)) {}
+ constexpr const T& get() const { return value; }
+ T& get() { return value; }
+};
+
+template <typename D, size_t I>
+struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<D, I, true>
+ : ElemT<D, I> {
+ using T = internal_compressed_tuple::ElemT<D, I>;
+ constexpr Storage() = default;
+ explicit constexpr Storage(T&& v) : T(absl::forward<T>(v)) {}
+ constexpr const T& get() const { return *this; }
+ T& get() { return *this; }
+};
+
+template <typename D, typename I>
+struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl;
+
+template <typename... Ts, size_t... I>
+struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
+ CompressedTupleImpl<CompressedTuple<Ts...>, absl::index_sequence<I...>>
+ // 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
+ : Storage<CompressedTuple<Ts...>,
+ std::integral_constant<size_t, I>::value>... {
+ constexpr CompressedTupleImpl() = default;
+ explicit constexpr CompressedTupleImpl(Ts&&... args)
+ : Storage<CompressedTuple<Ts...>, I>(absl::forward<Ts>(args))... {}
+};
+
+} // 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.
+//
+// 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>();
+// ...
+//
+// http://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...>> {
+ private:
+ template <int I>
+ using ElemT = internal_compressed_tuple::ElemT<CompressedTuple, I>;
+
+ public:
+ constexpr CompressedTuple() = default;
+ explicit constexpr CompressedTuple(Ts... base)
+ : CompressedTuple::CompressedTupleImpl(absl::forward<Ts>(base)...) {}
+
+ template <int I>
+ ElemT<I>& get() {
+ return internal_compressed_tuple::Storage<CompressedTuple, I>::get();
+ }
+
+ template <int I>
+ constexpr const ElemT<I>& get() const {
+ return internal_compressed_tuple::Storage<CompressedTuple, 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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
+
+#endif // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
diff --git a/absl/container/internal/compressed_tuple_test.cc b/absl/container/internal/compressed_tuple_test.cc
new file mode 100644
index 00000000..2b5ed4a4
--- /dev/null
+++ b/absl/container/internal/compressed_tuple_test.cc
@@ -0,0 +1,168 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/compressed_tuple.h"
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+template <int>
+struct Empty {};
+
+template <typename T>
+struct NotEmpty {
+ T value;
+};
+
+template <typename T, typename U>
+struct TwoValues {
+ T value1;
+ U value2;
+};
+
+TEST(CompressedTupleTest, Sizeof) {
+ EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
+ EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>));
+ EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>, Empty<1>>));
+ EXPECT_EQ(sizeof(int),
+ sizeof(CompressedTuple<int, Empty<0>, Empty<1>, Empty<2>>));
+
+ EXPECT_EQ(sizeof(TwoValues<int, double>),
+ sizeof(CompressedTuple<int, NotEmpty<double>>));
+ EXPECT_EQ(sizeof(TwoValues<int, double>),
+ sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>>));
+ EXPECT_EQ(sizeof(TwoValues<int, double>),
+ sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>));
+}
+
+TEST(CompressedTupleTest, Access) {
+ struct S {
+ std::string x;
+ };
+ CompressedTuple<int, Empty<0>, S> x(7, {}, S{"ABC"});
+ EXPECT_EQ(sizeof(x), sizeof(TwoValues<int, S>));
+ EXPECT_EQ(7, x.get<0>());
+ EXPECT_EQ("ABC", x.get<2>().x);
+}
+
+TEST(CompressedTupleTest, NonClasses) {
+ CompressedTuple<int, const char*> x(7, "ABC");
+ EXPECT_EQ(7, x.get<0>());
+ EXPECT_STREQ("ABC", x.get<1>());
+}
+
+TEST(CompressedTupleTest, MixClassAndNonClass) {
+ CompressedTuple<int, const char*, Empty<0>, NotEmpty<double>> x(7, "ABC", {},
+ {1.25});
+ struct Mock {
+ int v;
+ const char* p;
+ double d;
+ };
+ EXPECT_EQ(sizeof(x), sizeof(Mock));
+ EXPECT_EQ(7, x.get<0>());
+ EXPECT_STREQ("ABC", x.get<1>());
+ EXPECT_EQ(1.25, x.get<3>().value);
+}
+
+TEST(CompressedTupleTest, Nested) {
+ CompressedTuple<int, CompressedTuple<int>,
+ CompressedTuple<int, CompressedTuple<int>>>
+ x(1, CompressedTuple<int>(2),
+ CompressedTuple<int, CompressedTuple<int>>(3, CompressedTuple<int>(4)));
+ EXPECT_EQ(1, x.get<0>());
+ EXPECT_EQ(2, x.get<1>().get<0>());
+ EXPECT_EQ(3, x.get<2>().get<0>());
+ EXPECT_EQ(4, x.get<2>().get<1>().get<0>());
+
+ CompressedTuple<Empty<0>, Empty<0>,
+ CompressedTuple<Empty<0>, CompressedTuple<Empty<0>>>>
+ y;
+ std::set<Empty<0>*> empties{&y.get<0>(), &y.get<1>(), &y.get<2>().get<0>(),
+ &y.get<2>().get<1>().get<0>()};
+#ifdef _MSC_VER
+ // MSVC has a bug where many instances of the same base class are layed out in
+ // the same address when using __declspec(empty_bases).
+ // This will be fixed in a future version of MSVC.
+ int expected = 1;
+#else
+ int expected = 4;
+#endif
+ EXPECT_EQ(expected, sizeof(y));
+ EXPECT_EQ(expected, empties.size());
+ EXPECT_EQ(sizeof(y), sizeof(Empty<0>) * empties.size());
+
+ EXPECT_EQ(4 * sizeof(char),
+ sizeof(CompressedTuple<CompressedTuple<char, char>,
+ CompressedTuple<char, char>>));
+ EXPECT_TRUE(
+ (std::is_empty<CompressedTuple<CompressedTuple<Empty<0>>,
+ CompressedTuple<Empty<1>>>>::value));
+}
+
+TEST(CompressedTupleTest, Reference) {
+ int i = 7;
+ std::string s = "Very long std::string that goes in the heap";
+ CompressedTuple<int, int&, std::string, std::string&> x(i, i, s, s);
+
+ // Sanity check. We should have not moved from `s`
+ EXPECT_EQ(s, "Very long std::string that goes in the heap");
+
+ EXPECT_EQ(x.get<0>(), x.get<1>());
+ EXPECT_NE(&x.get<0>(), &x.get<1>());
+ EXPECT_EQ(&x.get<1>(), &i);
+
+ EXPECT_EQ(x.get<2>(), x.get<3>());
+ EXPECT_NE(&x.get<2>(), &x.get<3>());
+ EXPECT_EQ(&x.get<3>(), &s);
+}
+
+TEST(CompressedTupleTest, NoElements) {
+ CompressedTuple<> x;
+ static_cast<void>(x); // Silence -Wunused-variable.
+ EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value);
+}
+
+TEST(CompressedTupleTest, Constexpr) {
+ constexpr CompressedTuple<int, double, CompressedTuple<int>> x(
+ 7, 1.25, CompressedTuple<int>(5));
+ constexpr int x0 = x.get<0>();
+ constexpr double x1 = x.get<1>();
+ constexpr int x2 = x.get<2>().get<0>();
+ EXPECT_EQ(x0, 7);
+ EXPECT_EQ(x1, 1.25);
+ EXPECT_EQ(x2, 5);
+}
+
+#if defined(__clang__) || defined(__GNUC__)
+TEST(CompressedTupleTest, EmptyFinalClass) {
+ struct S final {
+ int f() const { return 5; }
+ };
+ CompressedTuple<S> x;
+ EXPECT_EQ(x.get<0>().f(), 5);
+}
+#endif
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h
new file mode 100644
index 00000000..ddccbe05
--- /dev/null
+++ b/absl/container/internal/container_memory.h
@@ -0,0 +1,407 @@
+// 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
+//
+// 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.
+
+#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
+#define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
+
+#ifdef ADDRESS_SANITIZER
+#include <sanitizer/asan_interface.h>
+#endif
+
+#ifdef MEMORY_SANITIZER
+#include <sanitizer/msan_interface.h>
+#endif
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+// 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");
+ struct alignas(Alignment) M {};
+ using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>;
+ using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>;
+ A mem_alloc(*alloc);
+ void* p = AT::allocate(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");
+ struct alignas(Alignment) M {};
+ using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>;
+ using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>;
+ A mem_alloc(*alloc);
+ AT::deallocate(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(p.first), std::move(p.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 ADDRESS_SANITIZER
+ ASAN_POISON_MEMORY_REGION(m, s);
+#endif
+#ifdef MEMORY_SANITIZER
+ __msan_poison(m, s);
+#endif
+ (void)m;
+ (void)s;
+}
+
+inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) {
+#ifdef ADDRESS_SANITIZER
+ ASAN_UNPOISON_MEMORY_REGION(m, s);
+#endif
+#ifdef 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 = -1;
+ static constexpr size_t kSecond = -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
+
+// If kMutableKeys is false, only the value member is accessed.
+//
+// If kMutableKeys is true, key is accessed through all slots while value and
+// mutable_value are accessed only via INITIALIZED slots. Slots are created and
+// destroyed via mutable_value so that the key can be moved later.
+template <class K, class V>
+union slot_type {
+ 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 =
+ std::integral_constant<bool,
+ memory_internal::IsLayoutCompatible<K, V>::value>;
+
+ public:
+ slot_type() {}
+ ~slot_type() = delete;
+ using value_type = std::pair<const K, V>;
+ using mutable_value_type = std::pair<K, V>;
+
+ value_type value;
+ mutable_value_type mutable_value;
+ K key;
+
+ 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));
+ }
+ }
+
+ 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);
+ }
+
+ template <class Allocator>
+ static void swap(Allocator* alloc, slot_type* a, slot_type* b) {
+ if (kMutableKeys::value) {
+ using std::swap;
+ swap(a->mutable_value, b->mutable_value);
+ } else {
+ value_type tmp = std::move(a->value);
+ absl::allocator_traits<Allocator>::destroy(*alloc, &a->value);
+ absl::allocator_traits<Allocator>::construct(*alloc, &a->value,
+ std::move(b->value));
+ absl::allocator_traits<Allocator>::destroy(*alloc, &b->value);
+ absl::allocator_traits<Allocator>::construct(*alloc, &b->value,
+ std::move(tmp));
+ }
+ }
+
+ template <class Allocator>
+ static void move(Allocator* alloc, slot_type* src, slot_type* dest) {
+ if (kMutableKeys::value) {
+ dest->mutable_value = std::move(src->mutable_value);
+ } else {
+ absl::allocator_traits<Allocator>::destroy(*alloc, &dest->value);
+ absl::allocator_traits<Allocator>::construct(*alloc, &dest->value,
+ std::move(src->value));
+ }
+ }
+
+ template <class Allocator>
+ static void move(Allocator* alloc, slot_type* first, slot_type* last,
+ slot_type* result) {
+ for (slot_type *src = first, *dest = result; src != last; ++src, ++dest)
+ move(alloc, src, dest);
+ }
+};
+
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
diff --git a/absl/container/internal/container_memory_test.cc b/absl/container/internal/container_memory_test.cc
new file mode 100644
index 00000000..da87ca20
--- /dev/null
+++ b/absl/container/internal/container_memory_test.cc
@@ -0,0 +1,190 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/container_memory.h"
+
+#include <cstdint>
+#include <tuple>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using ::testing::Pair;
+
+TEST(Memory, AlignmentLargerThanBase) {
+ std::allocator<int8_t> alloc;
+ void* mem = Allocate<2>(&alloc, 3);
+ EXPECT_EQ(0, reinterpret_cast<uintptr_t>(mem) % 2);
+ memcpy(mem, "abc", 3);
+ Deallocate<2>(&alloc, mem, 3);
+}
+
+TEST(Memory, AlignmentSmallerThanBase) {
+ std::allocator<int64_t> alloc;
+ void* mem = Allocate<2>(&alloc, 3);
+ EXPECT_EQ(0, reinterpret_cast<uintptr_t>(mem) % 2);
+ memcpy(mem, "abc", 3);
+ Deallocate<2>(&alloc, mem, 3);
+}
+
+class Fixture : public ::testing::Test {
+ using Alloc = std::allocator<std::string>;
+
+ public:
+ Fixture() { ptr_ = std::allocator_traits<Alloc>::allocate(*alloc(), 1); }
+ ~Fixture() override {
+ std::allocator_traits<Alloc>::destroy(*alloc(), ptr_);
+ std::allocator_traits<Alloc>::deallocate(*alloc(), ptr_, 1);
+ }
+ std::string* ptr() { return ptr_; }
+ Alloc* alloc() { return &alloc_; }
+
+ private:
+ Alloc alloc_;
+ std::string* ptr_;
+};
+
+TEST_F(Fixture, ConstructNoArgs) {
+ ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple());
+ EXPECT_EQ(*ptr(), "");
+}
+
+TEST_F(Fixture, ConstructOneArg) {
+ ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple("abcde"));
+ EXPECT_EQ(*ptr(), "abcde");
+}
+
+TEST_F(Fixture, ConstructTwoArg) {
+ ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple(5, 'a'));
+ EXPECT_EQ(*ptr(), "aaaaa");
+}
+
+TEST(PairArgs, NoArgs) {
+ EXPECT_THAT(PairArgs(),
+ Pair(std::forward_as_tuple(), std::forward_as_tuple()));
+}
+
+TEST(PairArgs, TwoArgs) {
+ EXPECT_EQ(
+ std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')),
+ PairArgs(1, 'A'));
+}
+
+TEST(PairArgs, Pair) {
+ EXPECT_EQ(
+ std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')),
+ PairArgs(std::make_pair(1, 'A')));
+}
+
+TEST(PairArgs, Piecewise) {
+ EXPECT_EQ(
+ std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')),
+ PairArgs(std::piecewise_construct, std::forward_as_tuple(1),
+ std::forward_as_tuple('A')));
+}
+
+TEST(WithConstructed, Simple) {
+ EXPECT_EQ(1, WithConstructed<absl::string_view>(
+ std::make_tuple(std::string("a")),
+ [](absl::string_view str) { return str.size(); }));
+}
+
+template <class F, class Arg>
+decltype(DecomposeValue(std::declval<F>(), std::declval<Arg>()))
+DecomposeValueImpl(int, F&& f, Arg&& arg) {
+ return DecomposeValue(std::forward<F>(f), std::forward<Arg>(arg));
+}
+
+template <class F, class Arg>
+const char* DecomposeValueImpl(char, F&& f, Arg&& arg) {
+ return "not decomposable";
+}
+
+template <class F, class Arg>
+decltype(DecomposeValueImpl(0, std::declval<F>(), std::declval<Arg>()))
+TryDecomposeValue(F&& f, Arg&& arg) {
+ return DecomposeValueImpl(0, std::forward<F>(f), std::forward<Arg>(arg));
+}
+
+TEST(DecomposeValue, Decomposable) {
+ auto f = [](const int& x, int&& y) {
+ EXPECT_EQ(&x, &y);
+ EXPECT_EQ(42, x);
+ return 'A';
+ };
+ EXPECT_EQ('A', TryDecomposeValue(f, 42));
+}
+
+TEST(DecomposeValue, NotDecomposable) {
+ auto f = [](void*) {
+ ADD_FAILURE() << "Must not be called";
+ return 'A';
+ };
+ EXPECT_STREQ("not decomposable", TryDecomposeValue(f, 42));
+}
+
+template <class F, class... Args>
+decltype(DecomposePair(std::declval<F>(), std::declval<Args>()...))
+DecomposePairImpl(int, F&& f, Args&&... args) {
+ return DecomposePair(std::forward<F>(f), std::forward<Args>(args)...);
+}
+
+template <class F, class... Args>
+const char* DecomposePairImpl(char, F&& f, Args&&... args) {
+ return "not decomposable";
+}
+
+template <class F, class... Args>
+decltype(DecomposePairImpl(0, std::declval<F>(), std::declval<Args>()...))
+TryDecomposePair(F&& f, Args&&... args) {
+ return DecomposePairImpl(0, std::forward<F>(f), std::forward<Args>(args)...);
+}
+
+TEST(DecomposePair, Decomposable) {
+ auto f = [](const int& x, std::piecewise_construct_t, std::tuple<int&&> k,
+ std::tuple<double>&& v) {
+ EXPECT_EQ(&x, &std::get<0>(k));
+ EXPECT_EQ(42, x);
+ EXPECT_EQ(0.5, std::get<0>(v));
+ return 'A';
+ };
+ EXPECT_EQ('A', TryDecomposePair(f, 42, 0.5));
+ EXPECT_EQ('A', TryDecomposePair(f, std::make_pair(42, 0.5)));
+ EXPECT_EQ('A', TryDecomposePair(f, std::piecewise_construct,
+ std::make_tuple(42), std::make_tuple(0.5)));
+}
+
+TEST(DecomposePair, NotDecomposable) {
+ auto f = [](...) {
+ ADD_FAILURE() << "Must not be called";
+ return 'A';
+ };
+ EXPECT_STREQ("not decomposable",
+ TryDecomposePair(f));
+ EXPECT_STREQ("not decomposable",
+ TryDecomposePair(f, std::piecewise_construct, std::make_tuple(),
+ std::make_tuple(0.5)));
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/hash_function_defaults.h b/absl/container/internal/hash_function_defaults.h
new file mode 100644
index 00000000..72c75fa0
--- /dev/null
+++ b/absl/container/internal/hash_function_defaults.h
@@ -0,0 +1,145 @@
+// 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
+//
+// 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.
+//
+// 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<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/string_view.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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);
+ }
+};
+
+// Supports heterogeneous lookup for string-like elements.
+struct StringHashEq {
+ using Hash = StringHash;
+ struct Eq {
+ using is_transparent = void;
+ bool operator()(absl::string_view lhs, absl::string_view rhs) const {
+ return lhs == rhs;
+ }
+ };
+};
+template <>
+struct HashEq<std::string> : StringHashEq {};
+template <>
+struct HashEq<absl::string_view> : 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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
diff --git a/absl/container/internal/hash_function_defaults_test.cc b/absl/container/internal/hash_function_defaults_test.cc
new file mode 100644
index 00000000..4610843a
--- /dev/null
+++ b/absl/container/internal/hash_function_defaults_test.cc
@@ -0,0 +1,303 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/hash_function_defaults.h"
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using ::testing::Types;
+
+TEST(Eq, Int32) {
+ hash_default_eq<int32_t> eq;
+ EXPECT_TRUE(eq(1, 1u));
+ EXPECT_TRUE(eq(1, char{1}));
+ EXPECT_TRUE(eq(1, true));
+ EXPECT_TRUE(eq(1, double{1.1}));
+ EXPECT_FALSE(eq(1, char{2}));
+ EXPECT_FALSE(eq(1, 2u));
+ EXPECT_FALSE(eq(1, false));
+ EXPECT_FALSE(eq(1, 2.));
+}
+
+TEST(Hash, Int32) {
+ hash_default_hash<int32_t> hash;
+ auto h = hash(1);
+ EXPECT_EQ(h, hash(1u));
+ EXPECT_EQ(h, hash(char{1}));
+ EXPECT_EQ(h, hash(true));
+ EXPECT_EQ(h, hash(double{1.1}));
+ EXPECT_NE(h, hash(2u));
+ EXPECT_NE(h, hash(char{2}));
+ EXPECT_NE(h, hash(false));
+ EXPECT_NE(h, hash(2.));
+}
+
+enum class MyEnum { A, B, C, D };
+
+TEST(Eq, Enum) {
+ hash_default_eq<MyEnum> eq;
+ EXPECT_TRUE(eq(MyEnum::A, MyEnum::A));
+ EXPECT_FALSE(eq(MyEnum::A, MyEnum::B));
+}
+
+TEST(Hash, Enum) {
+ hash_default_hash<MyEnum> hash;
+
+ for (MyEnum e : {MyEnum::A, MyEnum::B, MyEnum::C}) {
+ auto h = hash(e);
+ EXPECT_EQ(h, hash_default_hash<int>{}(static_cast<int>(e)));
+ EXPECT_NE(h, hash(MyEnum::D));
+ }
+}
+
+using StringTypes = ::testing::Types<std::string, absl::string_view>;
+
+template <class T>
+struct EqString : ::testing::Test {
+ hash_default_eq<T> key_eq;
+};
+
+TYPED_TEST_CASE(EqString, StringTypes);
+
+template <class T>
+struct HashString : ::testing::Test {
+ hash_default_hash<T> hasher;
+};
+
+TYPED_TEST_CASE(HashString, StringTypes);
+
+TYPED_TEST(EqString, Works) {
+ auto eq = this->key_eq;
+ EXPECT_TRUE(eq("a", "a"));
+ EXPECT_TRUE(eq("a", absl::string_view("a")));
+ EXPECT_TRUE(eq("a", std::string("a")));
+ EXPECT_FALSE(eq("a", "b"));
+ EXPECT_FALSE(eq("a", absl::string_view("b")));
+ EXPECT_FALSE(eq("a", std::string("b")));
+}
+
+TYPED_TEST(HashString, Works) {
+ auto hash = this->hasher;
+ auto h = hash("a");
+ EXPECT_EQ(h, hash(absl::string_view("a")));
+ EXPECT_EQ(h, hash(std::string("a")));
+ EXPECT_NE(h, hash(absl::string_view("b")));
+ EXPECT_NE(h, hash(std::string("b")));
+}
+
+struct NoDeleter {
+ template <class T>
+ void operator()(const T* ptr) const {}
+};
+
+using PointerTypes =
+ ::testing::Types<const int*, int*, std::unique_ptr<const int>,
+ std::unique_ptr<const int, NoDeleter>,
+ std::unique_ptr<int>, std::unique_ptr<int, NoDeleter>,
+ std::shared_ptr<const int>, std::shared_ptr<int>>;
+
+template <class T>
+struct EqPointer : ::testing::Test {
+ hash_default_eq<T> key_eq;
+};
+
+TYPED_TEST_CASE(EqPointer, PointerTypes);
+
+template <class T>
+struct HashPointer : ::testing::Test {
+ hash_default_hash<T> hasher;
+};
+
+TYPED_TEST_CASE(HashPointer, PointerTypes);
+
+TYPED_TEST(EqPointer, Works) {
+ int dummy;
+ auto eq = this->key_eq;
+ auto sptr = std::make_shared<int>();
+ std::shared_ptr<const int> csptr = sptr;
+ int* ptr = sptr.get();
+ const int* cptr = ptr;
+ std::unique_ptr<int, NoDeleter> uptr(ptr);
+ std::unique_ptr<const int, NoDeleter> cuptr(ptr);
+
+ EXPECT_TRUE(eq(ptr, cptr));
+ EXPECT_TRUE(eq(ptr, sptr));
+ EXPECT_TRUE(eq(ptr, uptr));
+ EXPECT_TRUE(eq(ptr, csptr));
+ EXPECT_TRUE(eq(ptr, cuptr));
+ EXPECT_FALSE(eq(&dummy, cptr));
+ EXPECT_FALSE(eq(&dummy, sptr));
+ EXPECT_FALSE(eq(&dummy, uptr));
+ EXPECT_FALSE(eq(&dummy, csptr));
+ EXPECT_FALSE(eq(&dummy, cuptr));
+}
+
+TEST(Hash, DerivedAndBase) {
+ struct Base {};
+ struct Derived : Base {};
+
+ hash_default_hash<Base*> hasher;
+
+ Base base;
+ Derived derived;
+ EXPECT_NE(hasher(&base), hasher(&derived));
+ EXPECT_EQ(hasher(static_cast<Base*>(&derived)), hasher(&derived));
+
+ auto dp = std::make_shared<Derived>();
+ EXPECT_EQ(hasher(static_cast<Base*>(dp.get())), hasher(dp));
+}
+
+TEST(Hash, FunctionPointer) {
+ using Func = int (*)();
+ hash_default_hash<Func> hasher;
+ hash_default_eq<Func> eq;
+
+ Func p1 = [] { return 1; }, p2 = [] { return 2; };
+ EXPECT_EQ(hasher(p1), hasher(p1));
+ EXPECT_TRUE(eq(p1, p1));
+
+ EXPECT_NE(hasher(p1), hasher(p2));
+ EXPECT_FALSE(eq(p1, p2));
+}
+
+TYPED_TEST(HashPointer, Works) {
+ int dummy;
+ auto hash = this->hasher;
+ auto sptr = std::make_shared<int>();
+ std::shared_ptr<const int> csptr = sptr;
+ int* ptr = sptr.get();
+ const int* cptr = ptr;
+ std::unique_ptr<int, NoDeleter> uptr(ptr);
+ std::unique_ptr<const int, NoDeleter> cuptr(ptr);
+
+ EXPECT_EQ(hash(ptr), hash(cptr));
+ EXPECT_EQ(hash(ptr), hash(sptr));
+ EXPECT_EQ(hash(ptr), hash(uptr));
+ EXPECT_EQ(hash(ptr), hash(csptr));
+ EXPECT_EQ(hash(ptr), hash(cuptr));
+ EXPECT_NE(hash(&dummy), hash(cptr));
+ EXPECT_NE(hash(&dummy), hash(sptr));
+ EXPECT_NE(hash(&dummy), hash(uptr));
+ EXPECT_NE(hash(&dummy), hash(csptr));
+ EXPECT_NE(hash(&dummy), hash(cuptr));
+}
+
+// Cartesian product of (string, std::string, absl::string_view)
+// with (string, std::string, absl::string_view, const char*).
+using StringTypesCartesianProduct = Types<
+ // clang-format off
+
+ std::pair<std::string, std::string>,
+ std::pair<std::string, absl::string_view>,
+ std::pair<std::string, const char*>,
+
+ std::pair<absl::string_view, std::string>,
+ std::pair<absl::string_view, absl::string_view>,
+ std::pair<absl::string_view, const char*>>;
+// clang-format on
+
+constexpr char kFirstString[] = "abc123";
+constexpr char kSecondString[] = "ijk456";
+
+template <typename T>
+struct StringLikeTest : public ::testing::Test {
+ typename T::first_type a1{kFirstString};
+ typename T::second_type b1{kFirstString};
+ typename T::first_type a2{kSecondString};
+ typename T::second_type b2{kSecondString};
+ hash_default_eq<typename T::first_type> eq;
+ hash_default_hash<typename T::first_type> hash;
+};
+
+TYPED_TEST_CASE_P(StringLikeTest);
+
+TYPED_TEST_P(StringLikeTest, Eq) {
+ EXPECT_TRUE(this->eq(this->a1, this->b1));
+ EXPECT_TRUE(this->eq(this->b1, this->a1));
+}
+
+TYPED_TEST_P(StringLikeTest, NotEq) {
+ EXPECT_FALSE(this->eq(this->a1, this->b2));
+ EXPECT_FALSE(this->eq(this->b2, this->a1));
+}
+
+TYPED_TEST_P(StringLikeTest, HashEq) {
+ EXPECT_EQ(this->hash(this->a1), this->hash(this->b1));
+ EXPECT_EQ(this->hash(this->a2), this->hash(this->b2));
+ // It would be a poor hash function which collides on these strings.
+ EXPECT_NE(this->hash(this->a1), this->hash(this->b2));
+}
+
+TYPED_TEST_CASE(StringLikeTest, StringTypesCartesianProduct);
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+enum Hash : size_t {
+ kStd = 0x2, // std::hash
+#ifdef _MSC_VER
+ kExtension = kStd, // In MSVC, std::hash == ::hash
+#else // _MSC_VER
+ kExtension = 0x4, // ::hash (GCC extension)
+#endif // _MSC_VER
+};
+
+// H is a bitmask of Hash enumerations.
+// Hashable<H> is hashable via all means specified in H.
+template <int H>
+struct Hashable {
+ static constexpr bool HashableBy(Hash h) { return h & H; }
+};
+
+namespace std {
+template <int H>
+struct hash<Hashable<H>> {
+ template <class E = Hashable<H>,
+ class = typename std::enable_if<E::HashableBy(kStd)>::type>
+ size_t operator()(E) const {
+ return kStd;
+ }
+};
+} // namespace std
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+template <class T>
+size_t Hash(const T& v) {
+ return hash_default_hash<T>()(v);
+}
+
+TEST(Delegate, HashDispatch) {
+ EXPECT_EQ(Hash(kStd), Hash(Hashable<kStd>()));
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc
new file mode 100644
index 00000000..aef41d72
--- /dev/null
+++ b/absl/container/internal/hash_generator_testing.cc
@@ -0,0 +1,74 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/hash_generator_testing.h"
+
+#include <deque>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace hash_internal {
+namespace {
+
+class RandomDeviceSeedSeq {
+ public:
+ using result_type = typename std::random_device::result_type;
+
+ template <class Iterator>
+ void generate(Iterator start, Iterator end) {
+ while (start != end) {
+ *start = gen_();
+ ++start;
+ }
+ }
+
+ private:
+ std::random_device gen_;
+};
+
+} // namespace
+
+std::mt19937_64* GetSharedRng() {
+ RandomDeviceSeedSeq seed_seq;
+ static auto* rng = new std::mt19937_64(seed_seq);
+ return rng;
+}
+
+std::string Generator<std::string>::operator()() const {
+ // NOLINTNEXTLINE(runtime/int)
+ std::uniform_int_distribution<short> chars(0x20, 0x7E);
+ std::string res;
+ res.resize(32);
+ std::generate(res.begin(), res.end(),
+ [&]() { return chars(*GetSharedRng()); });
+ return res;
+}
+
+absl::string_view Generator<absl::string_view>::operator()() const {
+ static auto* arena = new std::deque<std::string>();
+ // NOLINTNEXTLINE(runtime/int)
+ std::uniform_int_distribution<short> chars(0x20, 0x7E);
+ arena->emplace_back();
+ auto& res = arena->back();
+ res.resize(32);
+ std::generate(res.begin(), res.end(),
+ [&]() { return chars(*GetSharedRng()); });
+ return res;
+}
+
+} // namespace hash_internal
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h
new file mode 100644
index 00000000..65e88964
--- /dev/null
+++ b/absl/container/internal/hash_generator_testing.h
@@ -0,0 +1,152 @@
+// 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
+//
+// 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.
+//
+// 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 <iosfwd>
+#include <random>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "absl/container/internal/hash_policy_testing.h"
+#include "absl/meta/type_traits.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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 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>&>()());
+
+} // namespace hash_internal
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
diff --git a/absl/container/internal/hash_policy_testing.h b/absl/container/internal/hash_policy_testing.h
new file mode 100644
index 00000000..9c310ad4
--- /dev/null
+++ b/absl/container/internal/hash_policy_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
+//
+// 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.
+//
+// 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 {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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_
diff --git a/absl/container/internal/hash_policy_testing_test.cc b/absl/container/internal/hash_policy_testing_test.cc
new file mode 100644
index 00000000..00c436b3
--- /dev/null
+++ b/absl/container/internal/hash_policy_testing_test.cc
@@ -0,0 +1,45 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/hash_policy_testing.h"
+
+#include "gtest/gtest.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+TEST(_, Hash) {
+ StatefulTestingHash h1;
+ EXPECT_EQ(1, h1.id());
+ StatefulTestingHash h2;
+ EXPECT_EQ(2, h2.id());
+ StatefulTestingHash h1c(h1);
+ EXPECT_EQ(1, h1c.id());
+ StatefulTestingHash h2m(std::move(h2));
+ EXPECT_EQ(2, h2m.id());
+ EXPECT_EQ(0, h2.id());
+ StatefulTestingHash h3;
+ EXPECT_EQ(3, h3.id());
+ h3 = StatefulTestingHash();
+ EXPECT_EQ(4, h3.id());
+ h3 = std::move(h1);
+ EXPECT_EQ(1, h3.id());
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/hash_policy_traits.h b/absl/container/internal/hash_policy_traits.h
new file mode 100644
index 00000000..41e26212
--- /dev/null
+++ b/absl/container/internal/hash_policy_traits.h
@@ -0,0 +1,191 @@
+// 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
+//
+// 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.
+
+#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
+#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+// Defines how slots are initialized/destroyed/moved.
+template <class Policy, class = void>
+struct hash_policy_traits {
+ private:
+ struct ReturnKey {
+ // We return `Key` here.
+ // 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>
+ Key operator()(Key&& k, const Args&...) const {
+ return std::forward<Key>(k);
+ }
+ };
+
+ 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 type of the keys stored in the hashtable.
+ using key_type = typename Policy::key_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 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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
diff --git a/absl/container/internal/hash_policy_traits_test.cc b/absl/container/internal/hash_policy_traits_test.cc
new file mode 100644
index 00000000..07cecdfa
--- /dev/null
+++ b/absl/container/internal/hash_policy_traits_test.cc
@@ -0,0 +1,144 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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.
+
+#include "absl/container/internal/hash_policy_traits.h"
+
+#include <functional>
+#include <memory>
+#include <new>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using ::testing::MockFunction;
+using ::testing::Return;
+using ::testing::ReturnRef;
+
+using Alloc = std::allocator<int>;
+using Slot = int;
+
+struct PolicyWithoutOptionalOps {
+ using slot_type = Slot;
+ using key_type = Slot;
+ using init_type = Slot;
+
+ static std::function<void(void*, Slot*, Slot)> construct;
+ static std::function<void(void*, Slot*)> destroy;
+
+ static std::function<Slot&(Slot*)> element;
+ static int apply(int v) { return apply_impl(v); }
+ static std::function<int(int)> apply_impl;
+ static std::function<Slot&(Slot*)> value;
+};
+
+std::function<void(void*, Slot*, Slot)> PolicyWithoutOptionalOps::construct;
+std::function<void(void*, Slot*)> PolicyWithoutOptionalOps::destroy;
+
+std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::element;
+std::function<int(int)> PolicyWithoutOptionalOps::apply_impl;
+std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::value;
+
+struct PolicyWithOptionalOps : PolicyWithoutOptionalOps {
+ static std::function<void(void*, Slot*, Slot*)> transfer;
+};
+
+std::function<void(void*, Slot*, Slot*)> PolicyWithOptionalOps::transfer;
+
+struct Test : ::testing::Test {
+ Test() {
+ PolicyWithoutOptionalOps::construct = [&](void* a1, Slot* a2, Slot a3) {
+ construct.Call(a1, a2, std::move(a3));
+ };
+ PolicyWithoutOptionalOps::destroy = [&](void* a1, Slot* a2) {
+ destroy.Call(a1, a2);
+ };
+
+ PolicyWithoutOptionalOps::element = [&](Slot* a1) -> Slot& {
+ return element.Call(a1);
+ };
+ PolicyWithoutOptionalOps::apply_impl = [&](int a1) -> int {
+ return apply.Call(a1);
+ };
+ PolicyWithoutOptionalOps::value = [&](Slot* a1) -> Slot& {
+ return value.Call(a1);
+ };
+
+ PolicyWithOptionalOps::transfer = [&](void* a1, Slot* a2, Slot* a3) {
+ return transfer.Call(a1, a2, a3);
+ };
+ }
+
+ std::allocator<int> alloc;
+ int a = 53;
+
+ MockFunction<void(void*, Slot*, Slot)> construct;
+ MockFunction<void(void*, Slot*)> destroy;
+
+ MockFunction<Slot&(Slot*)> element;
+ MockFunction<int(int)> apply;
+ MockFunction<Slot&(Slot*)> value;
+
+ MockFunction<void(void*, Slot*, Slot*)> transfer;
+};
+
+TEST_F(Test, construct) {
+ EXPECT_CALL(construct, Call(&alloc, &a, 53));
+ hash_policy_traits<PolicyWithoutOptionalOps>::construct(&alloc, &a, 53);
+}
+
+TEST_F(Test, destroy) {
+ EXPECT_CALL(destroy, Call(&alloc, &a));
+ hash_policy_traits<PolicyWithoutOptionalOps>::destroy(&alloc, &a);
+}
+
+TEST_F(Test, element) {
+ int b = 0;
+ EXPECT_CALL(element, Call(&a)).WillOnce(ReturnRef(b));
+ EXPECT_EQ(&b, &hash_policy_traits<PolicyWithoutOptionalOps>::element(&a));
+}
+
+TEST_F(Test, apply) {
+ EXPECT_CALL(apply, Call(42)).WillOnce(Return(1337));
+ EXPECT_EQ(1337, (hash_policy_traits<PolicyWithoutOptionalOps>::apply(42)));
+}
+
+TEST_F(Test, value) {
+ int b = 0;
+ EXPECT_CALL(value, Call(&a)).WillOnce(ReturnRef(b));
+ EXPECT_EQ(&b, &hash_policy_traits<PolicyWithoutOptionalOps>::value(&a));
+}
+
+TEST_F(Test, without_transfer) {
+ int b = 42;
+ EXPECT_CALL(element, Call(&b)).WillOnce(::testing::ReturnRef(b));
+ EXPECT_CALL(construct, Call(&alloc, &a, b));
+ EXPECT_CALL(destroy, Call(&alloc, &b));
+ hash_policy_traits<PolicyWithoutOptionalOps>::transfer(&alloc, &a, &b);
+}
+
+TEST_F(Test, with_transfer) {
+ int b = 42;
+ EXPECT_CALL(transfer, Call(&alloc, &a, &b));
+ hash_policy_traits<PolicyWithOptionalOps>::transfer(&alloc, &a, &b);
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h
new file mode 100644
index 00000000..b6a43512
--- /dev/null
+++ b/absl/container/internal/hashtable_debug.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
+//
+// 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.
+//
+// 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 {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
diff --git a/absl/container/internal/hashtable_debug_hooks.h b/absl/container/internal/hashtable_debug_hooks.h
new file mode 100644
index 00000000..50ba6ba5
--- /dev/null
+++ b/absl/container/internal/hashtable_debug_hooks.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
+//
+// 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.
+//
+// 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>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_
diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h
new file mode 100644
index 00000000..f11a6ad2
--- /dev/null
+++ b/absl/container/internal/layout.h
@@ -0,0 +1,740 @@
+// 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
+//
+// 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.
+//
+// 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>
+
+#ifdef ADDRESS_SANITIZER
+#include <sanitizer/asan_interface.h>
+#endif
+
+#include "absl/meta/type_traits.h"
+#include "absl/strings/str_cat.h"
+#include "absl/types/span.h"
+#include "absl/utility/utility.h"
+
+#if defined(__GXX_RTTI)
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#endif
+
+#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#include <cxxabi.h>
+#endif
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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>>() * 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>>() * 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 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>>() * 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>>()...};
+ 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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_
diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc
new file mode 100644
index 00000000..b9f98471
--- /dev/null
+++ b/absl/container/internal/layout_test.cc
@@ -0,0 +1,1557 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/layout.h"
+
+// We need ::max_align_t because some libstdc++ versions don't provide
+// std::max_align_t
+#include <stddef.h>
+#include <cstdint>
+#include <memory>
+#include <sstream>
+#include <type_traits>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/types/span.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using ::absl::Span;
+using ::testing::ElementsAre;
+
+size_t Distance(const void* from, const void* to) {
+ ABSL_RAW_CHECK(from <= to, "Distance must be non-negative");
+ return static_cast<const char*>(to) - static_cast<const char*>(from);
+}
+
+template <class Expected, class Actual>
+Expected Type(Actual val) {
+ static_assert(std::is_same<Expected, Actual>(), "");
+ return val;
+}
+
+// Helper class to test different size and alignments.
+struct alignas(8) Int128 {
+ uint64_t a, b;
+ friend bool operator==(Int128 lhs, Int128 rhs) {
+ return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b);
+ }
+
+ static std::string Name() {
+ return internal_layout::adl_barrier::TypeName<Int128>();
+ }
+};
+
+// Properties of types that this test relies on.
+static_assert(sizeof(int8_t) == 1, "");
+static_assert(alignof(int8_t) == 1, "");
+static_assert(sizeof(int16_t) == 2, "");
+static_assert(alignof(int16_t) == 2, "");
+static_assert(sizeof(int32_t) == 4, "");
+static_assert(alignof(int32_t) == 4, "");
+static_assert(sizeof(Int128) == 16, "");
+static_assert(alignof(Int128) == 8, "");
+
+template <class Expected, class Actual>
+void SameType() {
+ static_assert(std::is_same<Expected, Actual>(), "");
+}
+
+TEST(Layout, ElementType) {
+ {
+ using L = Layout<int32_t>;
+ SameType<int32_t, L::ElementType<0>>();
+ SameType<int32_t, decltype(L::Partial())::ElementType<0>>();
+ SameType<int32_t, decltype(L::Partial(0))::ElementType<0>>();
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ SameType<int32_t, L::ElementType<0>>();
+ SameType<int32_t, L::ElementType<1>>();
+ SameType<int32_t, decltype(L::Partial())::ElementType<0>>();
+ SameType<int32_t, decltype(L::Partial())::ElementType<1>>();
+ SameType<int32_t, decltype(L::Partial(0))::ElementType<0>>();
+ SameType<int32_t, decltype(L::Partial(0))::ElementType<1>>();
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ SameType<int8_t, L::ElementType<0>>();
+ SameType<int32_t, L::ElementType<1>>();
+ SameType<Int128, L::ElementType<2>>();
+ SameType<int8_t, decltype(L::Partial())::ElementType<0>>();
+ SameType<int8_t, decltype(L::Partial(0))::ElementType<0>>();
+ SameType<int32_t, decltype(L::Partial(0))::ElementType<1>>();
+ SameType<int8_t, decltype(L::Partial(0, 0))::ElementType<0>>();
+ SameType<int32_t, decltype(L::Partial(0, 0))::ElementType<1>>();
+ SameType<Int128, decltype(L::Partial(0, 0))::ElementType<2>>();
+ SameType<int8_t, decltype(L::Partial(0, 0, 0))::ElementType<0>>();
+ SameType<int32_t, decltype(L::Partial(0, 0, 0))::ElementType<1>>();
+ SameType<Int128, decltype(L::Partial(0, 0, 0))::ElementType<2>>();
+ }
+}
+
+TEST(Layout, ElementTypes) {
+ {
+ using L = Layout<int32_t>;
+ SameType<std::tuple<int32_t>, L::ElementTypes>();
+ SameType<std::tuple<int32_t>, decltype(L::Partial())::ElementTypes>();
+ SameType<std::tuple<int32_t>, decltype(L::Partial(0))::ElementTypes>();
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ SameType<std::tuple<int32_t, int32_t>, L::ElementTypes>();
+ SameType<std::tuple<int32_t, int32_t>, decltype(L::Partial())::ElementTypes>();
+ SameType<std::tuple<int32_t, int32_t>, decltype(L::Partial(0))::ElementTypes>();
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ SameType<std::tuple<int8_t, int32_t, Int128>, L::ElementTypes>();
+ SameType<std::tuple<int8_t, int32_t, Int128>,
+ decltype(L::Partial())::ElementTypes>();
+ SameType<std::tuple<int8_t, int32_t, Int128>,
+ decltype(L::Partial(0))::ElementTypes>();
+ SameType<std::tuple<int8_t, int32_t, Int128>,
+ decltype(L::Partial(0, 0))::ElementTypes>();
+ SameType<std::tuple<int8_t, int32_t, Int128>,
+ decltype(L::Partial(0, 0, 0))::ElementTypes>();
+ }
+}
+
+TEST(Layout, OffsetByIndex) {
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial().Offset<0>());
+ EXPECT_EQ(0, L::Partial(3).Offset<0>());
+ EXPECT_EQ(0, L(3).Offset<0>());
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(0, L::Partial().Offset<0>());
+ EXPECT_EQ(0, L::Partial(3).Offset<0>());
+ EXPECT_EQ(12, L::Partial(3).Offset<1>());
+ EXPECT_EQ(0, L::Partial(3, 5).Offset<0>());
+ EXPECT_EQ(12, L::Partial(3, 5).Offset<1>());
+ EXPECT_EQ(0, L(3, 5).Offset<0>());
+ EXPECT_EQ(12, L(3, 5).Offset<1>());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(0, L::Partial().Offset<0>());
+ EXPECT_EQ(0, L::Partial(0).Offset<0>());
+ EXPECT_EQ(0, L::Partial(0).Offset<1>());
+ EXPECT_EQ(0, L::Partial(1).Offset<0>());
+ EXPECT_EQ(4, L::Partial(1).Offset<1>());
+ EXPECT_EQ(0, L::Partial(5).Offset<0>());
+ EXPECT_EQ(8, L::Partial(5).Offset<1>());
+ EXPECT_EQ(0, L::Partial(0, 0).Offset<0>());
+ EXPECT_EQ(0, L::Partial(0, 0).Offset<1>());
+ EXPECT_EQ(0, L::Partial(0, 0).Offset<2>());
+ EXPECT_EQ(0, L::Partial(1, 0).Offset<0>());
+ EXPECT_EQ(4, L::Partial(1, 0).Offset<1>());
+ EXPECT_EQ(8, L::Partial(1, 0).Offset<2>());
+ EXPECT_EQ(0, L::Partial(5, 3).Offset<0>());
+ EXPECT_EQ(8, L::Partial(5, 3).Offset<1>());
+ EXPECT_EQ(24, L::Partial(5, 3).Offset<2>());
+ EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<0>());
+ EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<1>());
+ EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<2>());
+ EXPECT_EQ(0, L::Partial(1, 0, 0).Offset<0>());
+ EXPECT_EQ(4, L::Partial(1, 0, 0).Offset<1>());
+ EXPECT_EQ(8, L::Partial(1, 0, 0).Offset<2>());
+ EXPECT_EQ(0, L::Partial(5, 3, 1).Offset<0>());
+ EXPECT_EQ(24, L::Partial(5, 3, 1).Offset<2>());
+ EXPECT_EQ(8, L::Partial(5, 3, 1).Offset<1>());
+ EXPECT_EQ(0, L(5, 3, 1).Offset<0>());
+ EXPECT_EQ(24, L(5, 3, 1).Offset<2>());
+ EXPECT_EQ(8, L(5, 3, 1).Offset<1>());
+ }
+}
+
+TEST(Layout, OffsetByType) {
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial().Offset<int32_t>());
+ EXPECT_EQ(0, L::Partial(3).Offset<int32_t>());
+ EXPECT_EQ(0, L(3).Offset<int32_t>());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(0, L::Partial().Offset<int8_t>());
+ EXPECT_EQ(0, L::Partial(0).Offset<int8_t>());
+ EXPECT_EQ(0, L::Partial(0).Offset<int32_t>());
+ EXPECT_EQ(0, L::Partial(1).Offset<int8_t>());
+ EXPECT_EQ(4, L::Partial(1).Offset<int32_t>());
+ EXPECT_EQ(0, L::Partial(5).Offset<int8_t>());
+ EXPECT_EQ(8, L::Partial(5).Offset<int32_t>());
+ EXPECT_EQ(0, L::Partial(0, 0).Offset<int8_t>());
+ EXPECT_EQ(0, L::Partial(0, 0).Offset<int32_t>());
+ EXPECT_EQ(0, L::Partial(0, 0).Offset<Int128>());
+ EXPECT_EQ(0, L::Partial(1, 0).Offset<int8_t>());
+ EXPECT_EQ(4, L::Partial(1, 0).Offset<int32_t>());
+ EXPECT_EQ(8, L::Partial(1, 0).Offset<Int128>());
+ EXPECT_EQ(0, L::Partial(5, 3).Offset<int8_t>());
+ EXPECT_EQ(8, L::Partial(5, 3).Offset<int32_t>());
+ EXPECT_EQ(24, L::Partial(5, 3).Offset<Int128>());
+ EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<int8_t>());
+ EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<int32_t>());
+ EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<Int128>());
+ EXPECT_EQ(0, L::Partial(1, 0, 0).Offset<int8_t>());
+ EXPECT_EQ(4, L::Partial(1, 0, 0).Offset<int32_t>());
+ EXPECT_EQ(8, L::Partial(1, 0, 0).Offset<Int128>());
+ EXPECT_EQ(0, L::Partial(5, 3, 1).Offset<int8_t>());
+ EXPECT_EQ(24, L::Partial(5, 3, 1).Offset<Int128>());
+ EXPECT_EQ(8, L::Partial(5, 3, 1).Offset<int32_t>());
+ EXPECT_EQ(0, L(5, 3, 1).Offset<int8_t>());
+ EXPECT_EQ(24, L(5, 3, 1).Offset<Int128>());
+ EXPECT_EQ(8, L(5, 3, 1).Offset<int32_t>());
+ }
+}
+
+TEST(Layout, Offsets) {
+ {
+ using L = Layout<int32_t>;
+ EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
+ EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0));
+ EXPECT_THAT(L(3).Offsets(), ElementsAre(0));
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
+ EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0, 12));
+ EXPECT_THAT(L::Partial(3, 5).Offsets(), ElementsAre(0, 12));
+ EXPECT_THAT(L(3, 5).Offsets(), ElementsAre(0, 12));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
+ EXPECT_THAT(L::Partial(1).Offsets(), ElementsAre(0, 4));
+ EXPECT_THAT(L::Partial(5).Offsets(), ElementsAre(0, 8));
+ EXPECT_THAT(L::Partial(0, 0).Offsets(), ElementsAre(0, 0, 0));
+ EXPECT_THAT(L::Partial(1, 0).Offsets(), ElementsAre(0, 4, 8));
+ EXPECT_THAT(L::Partial(5, 3).Offsets(), ElementsAre(0, 8, 24));
+ EXPECT_THAT(L::Partial(0, 0, 0).Offsets(), ElementsAre(0, 0, 0));
+ EXPECT_THAT(L::Partial(1, 0, 0).Offsets(), ElementsAre(0, 4, 8));
+ EXPECT_THAT(L::Partial(5, 3, 1).Offsets(), ElementsAre(0, 8, 24));
+ EXPECT_THAT(L(5, 3, 1).Offsets(), ElementsAre(0, 8, 24));
+ }
+}
+
+TEST(Layout, AllocSize) {
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial(0).AllocSize());
+ EXPECT_EQ(12, L::Partial(3).AllocSize());
+ EXPECT_EQ(12, L(3).AllocSize());
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(32, L::Partial(3, 5).AllocSize());
+ EXPECT_EQ(32, L(3, 5).AllocSize());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(0, L::Partial(0, 0, 0).AllocSize());
+ EXPECT_EQ(8, L::Partial(1, 0, 0).AllocSize());
+ EXPECT_EQ(8, L::Partial(0, 1, 0).AllocSize());
+ EXPECT_EQ(16, L::Partial(0, 0, 1).AllocSize());
+ EXPECT_EQ(24, L::Partial(1, 1, 1).AllocSize());
+ EXPECT_EQ(136, L::Partial(3, 5, 7).AllocSize());
+ EXPECT_EQ(136, L(3, 5, 7).AllocSize());
+ }
+}
+
+TEST(Layout, SizeByIndex) {
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial(0).Size<0>());
+ EXPECT_EQ(3, L::Partial(3).Size<0>());
+ EXPECT_EQ(3, L(3).Size<0>());
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(0, L::Partial(0).Size<0>());
+ EXPECT_EQ(3, L::Partial(3).Size<0>());
+ EXPECT_EQ(3, L::Partial(3, 5).Size<0>());
+ EXPECT_EQ(5, L::Partial(3, 5).Size<1>());
+ EXPECT_EQ(3, L(3, 5).Size<0>());
+ EXPECT_EQ(5, L(3, 5).Size<1>());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(3, L::Partial(3).Size<0>());
+ EXPECT_EQ(3, L::Partial(3, 5).Size<0>());
+ EXPECT_EQ(5, L::Partial(3, 5).Size<1>());
+ EXPECT_EQ(3, L::Partial(3, 5, 7).Size<0>());
+ EXPECT_EQ(5, L::Partial(3, 5, 7).Size<1>());
+ EXPECT_EQ(7, L::Partial(3, 5, 7).Size<2>());
+ EXPECT_EQ(3, L(3, 5, 7).Size<0>());
+ EXPECT_EQ(5, L(3, 5, 7).Size<1>());
+ EXPECT_EQ(7, L(3, 5, 7).Size<2>());
+ }
+}
+
+TEST(Layout, SizeByType) {
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial(0).Size<int32_t>());
+ EXPECT_EQ(3, L::Partial(3).Size<int32_t>());
+ EXPECT_EQ(3, L(3).Size<int32_t>());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(3, L::Partial(3).Size<int8_t>());
+ EXPECT_EQ(3, L::Partial(3, 5).Size<int8_t>());
+ EXPECT_EQ(5, L::Partial(3, 5).Size<int32_t>());
+ EXPECT_EQ(3, L::Partial(3, 5, 7).Size<int8_t>());
+ EXPECT_EQ(5, L::Partial(3, 5, 7).Size<int32_t>());
+ EXPECT_EQ(7, L::Partial(3, 5, 7).Size<Int128>());
+ EXPECT_EQ(3, L(3, 5, 7).Size<int8_t>());
+ EXPECT_EQ(5, L(3, 5, 7).Size<int32_t>());
+ EXPECT_EQ(7, L(3, 5, 7).Size<Int128>());
+ }
+}
+
+TEST(Layout, Sizes) {
+ {
+ using L = Layout<int32_t>;
+ EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
+ EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
+ EXPECT_THAT(L(3).Sizes(), ElementsAre(3));
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
+ EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
+ EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5));
+ EXPECT_THAT(L(3, 5).Sizes(), ElementsAre(3, 5));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
+ EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
+ EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5));
+ EXPECT_THAT(L::Partial(3, 5, 7).Sizes(), ElementsAre(3, 5, 7));
+ EXPECT_THAT(L(3, 5, 7).Sizes(), ElementsAre(3, 5, 7));
+ }
+}
+
+TEST(Layout, PointerByIndex) {
+ alignas(max_align_t) const unsigned char p[100] = {};
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<0>(p))));
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
+ EXPECT_EQ(12, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<1>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<0>(p))));
+ EXPECT_EQ(12,
+ Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<0>(p))));
+ EXPECT_EQ(12, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<1>(p))));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<0>(p))));
+ EXPECT_EQ(4, Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<0>(p))));
+ EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<1>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<0>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<1>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<2>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<0>(p))));
+ EXPECT_EQ(4,
+ Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<1>(p))));
+ EXPECT_EQ(8,
+ Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<2>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<0>(p))));
+ EXPECT_EQ(8,
+ Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
+ EXPECT_EQ(24,
+ Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<2>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const Int128*>(L::Partial(0, 0, 0).Pointer<2>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p))));
+ EXPECT_EQ(
+ 4, Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
+ EXPECT_EQ(
+ 8, Distance(p, Type<const Int128*>(L::Partial(1, 0, 0).Pointer<2>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const int8_t*>(L::Partial(5, 3, 1).Pointer<0>(p))));
+ EXPECT_EQ(
+ 24,
+ Distance(p, Type<const Int128*>(L::Partial(5, 3, 1).Pointer<2>(p))));
+ EXPECT_EQ(
+ 8, Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L(5, 3, 1).Pointer<0>(p))));
+ EXPECT_EQ(24, Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<2>(p))));
+ EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<1>(p))));
+ }
+}
+
+TEST(Layout, PointerByType) {
+ alignas(max_align_t) const unsigned char p[100] = {};
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0,
+ Distance(p, Type<const int32_t*>(L::Partial().Pointer<int32_t>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<int32_t>(p))));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<int8_t>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
+ EXPECT_EQ(4,
+ Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
+ EXPECT_EQ(8,
+ Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p))));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
+ EXPECT_EQ(
+ 4, Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p))));
+ EXPECT_EQ(
+ 8,
+ Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
+ EXPECT_EQ(
+ 8, Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p))));
+ EXPECT_EQ(
+ 24,
+ Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<const int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p))));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<const Int128*>(
+ L::Partial(0, 0, 0).Pointer<Int128>(p))));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<const int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p))));
+ EXPECT_EQ(
+ 4,
+ Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p))));
+ EXPECT_EQ(8, Distance(p, Type<const Int128*>(
+ L::Partial(1, 0, 0).Pointer<Int128>(p))));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<const int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p))));
+ EXPECT_EQ(24, Distance(p, Type<const Int128*>(
+ L::Partial(5, 3, 1).Pointer<Int128>(p))));
+ EXPECT_EQ(
+ 8,
+ Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p))));
+ EXPECT_EQ(24,
+ Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<Int128>(p))));
+ EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
+ }
+}
+
+TEST(Layout, MutablePointerByIndex) {
+ alignas(max_align_t) unsigned char p[100];
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3).Pointer<0>(p))));
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<0>(p))));
+ EXPECT_EQ(12, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3, 5).Pointer<0>(p))));
+ EXPECT_EQ(12, Distance(p, Type<int32_t*>(L::Partial(3, 5).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3, 5).Pointer<0>(p))));
+ EXPECT_EQ(12, Distance(p, Type<int32_t*>(L(3, 5).Pointer<1>(p))));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial().Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0).Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1).Pointer<0>(p))));
+ EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5).Pointer<0>(p))));
+ EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0).Pointer<2>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<0>(p))));
+ EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<1>(p))));
+ EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0).Pointer<2>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<0>(p))));
+ EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
+ EXPECT_EQ(24, Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<2>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<2>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p))));
+ EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
+ EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<2>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<0>(p))));
+ EXPECT_EQ(24,
+ Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<2>(p))));
+ EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<0>(p))));
+ EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<2>(p))));
+ EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<1>(p))));
+ }
+}
+
+TEST(Layout, MutablePointerByType) {
+ alignas(max_align_t) unsigned char p[100];
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<int32_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3).Pointer<int32_t>(p))));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial().Pointer<int8_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
+ EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
+ EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
+ EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p))));
+ EXPECT_EQ(8,
+ Distance(p, Type<Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
+ EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p))));
+ EXPECT_EQ(24,
+ Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p))));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<Int128>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p))));
+ EXPECT_EQ(4,
+ Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p))));
+ EXPECT_EQ(
+ 8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<Int128>(p))));
+ EXPECT_EQ(0,
+ Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p))));
+ EXPECT_EQ(
+ 24, Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<Int128>(p))));
+ EXPECT_EQ(8,
+ Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p))));
+ EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<int8_t>(p))));
+ EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<Int128>(p))));
+ EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
+ }
+}
+
+TEST(Layout, Pointers) {
+ alignas(max_align_t) const unsigned char p[100] = {};
+ using L = Layout<int8_t, int8_t, Int128>;
+ {
+ const auto x = L::Partial();
+ EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)),
+ Type<std::tuple<const int8_t*>>(x.Pointers(p)));
+ }
+ {
+ const auto x = L::Partial(1);
+ EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)),
+ (Type<std::tuple<const int8_t*, const int8_t*>>(x.Pointers(p))));
+ }
+ {
+ const auto x = L::Partial(1, 2);
+ EXPECT_EQ(
+ std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
+ (Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
+ x.Pointers(p))));
+ }
+ {
+ const auto x = L::Partial(1, 2, 3);
+ EXPECT_EQ(
+ std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
+ (Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
+ x.Pointers(p))));
+ }
+ {
+ const L x(1, 2, 3);
+ EXPECT_EQ(
+ std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
+ (Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
+ x.Pointers(p))));
+ }
+}
+
+TEST(Layout, MutablePointers) {
+ alignas(max_align_t) unsigned char p[100];
+ using L = Layout<int8_t, int8_t, Int128>;
+ {
+ const auto x = L::Partial();
+ EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)),
+ Type<std::tuple<int8_t*>>(x.Pointers(p)));
+ }
+ {
+ const auto x = L::Partial(1);
+ EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)),
+ (Type<std::tuple<int8_t*, int8_t*>>(x.Pointers(p))));
+ }
+ {
+ const auto x = L::Partial(1, 2);
+ EXPECT_EQ(
+ std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
+ (Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
+ }
+ {
+ const auto x = L::Partial(1, 2, 3);
+ EXPECT_EQ(
+ std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
+ (Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
+ }
+ {
+ const L x(1, 2, 3);
+ EXPECT_EQ(
+ std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
+ (Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
+ }
+}
+
+TEST(Layout, SliceByIndexSize) {
+ alignas(max_align_t) const unsigned char p[100] = {};
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size());
+ EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
+ EXPECT_EQ(3, L(3).Slice<0>(p).size());
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
+ EXPECT_EQ(5, L(3, 5).Slice<1>(p).size());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
+ EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
+ EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size());
+ EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size());
+ EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size());
+ EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size());
+ EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size());
+ }
+}
+
+TEST(Layout, SliceByTypeSize) {
+ alignas(max_align_t) const unsigned char p[100] = {};
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial(0).Slice<int32_t>(p).size());
+ EXPECT_EQ(3, L::Partial(3).Slice<int32_t>(p).size());
+ EXPECT_EQ(3, L(3).Slice<int32_t>(p).size());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(3, L::Partial(3).Slice<int8_t>(p).size());
+ EXPECT_EQ(3, L::Partial(3, 5).Slice<int8_t>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5).Slice<int32_t>(p).size());
+ EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<int8_t>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<int32_t>(p).size());
+ EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<Int128>(p).size());
+ EXPECT_EQ(3, L(3, 5, 7).Slice<int8_t>(p).size());
+ EXPECT_EQ(5, L(3, 5, 7).Slice<int32_t>(p).size());
+ EXPECT_EQ(7, L(3, 5, 7).Slice<Int128>(p).size());
+ }
+}
+
+TEST(Layout, MutableSliceByIndexSize) {
+ alignas(max_align_t) unsigned char p[100];
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size());
+ EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
+ EXPECT_EQ(3, L(3).Slice<0>(p).size());
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
+ EXPECT_EQ(5, L(3, 5).Slice<1>(p).size());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
+ EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
+ EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size());
+ EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size());
+ EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size());
+ EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size());
+ EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size());
+ }
+}
+
+TEST(Layout, MutableSliceByTypeSize) {
+ alignas(max_align_t) unsigned char p[100];
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0, L::Partial(0).Slice<int32_t>(p).size());
+ EXPECT_EQ(3, L::Partial(3).Slice<int32_t>(p).size());
+ EXPECT_EQ(3, L(3).Slice<int32_t>(p).size());
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(3, L::Partial(3).Slice<int8_t>(p).size());
+ EXPECT_EQ(3, L::Partial(3, 5).Slice<int8_t>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5).Slice<int32_t>(p).size());
+ EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<int8_t>(p).size());
+ EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<int32_t>(p).size());
+ EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<Int128>(p).size());
+ EXPECT_EQ(3, L(3, 5, 7).Slice<int8_t>(p).size());
+ EXPECT_EQ(5, L(3, 5, 7).Slice<int32_t>(p).size());
+ EXPECT_EQ(7, L(3, 5, 7).Slice<Int128>(p).size());
+ }
+}
+
+TEST(Layout, SliceByIndexData) {
+ alignas(max_align_t) const unsigned char p[100] = {};
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<const int32_t>>(L::Partial(0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
+ EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(L(3).Slice<0>(p)).data()));
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p,
+ Type<Span<const int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 12,
+ Distance(p,
+ Type<Span<const int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
+ EXPECT_EQ(0,
+ Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<0>(p)).data()));
+ EXPECT_EQ(12,
+ Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<1>(p)).data()));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<const int8_t>>(L::Partial(0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<const int8_t>>(L::Partial(1).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<const int8_t>>(L::Partial(5).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p,
+ Type<Span<const int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 4,
+ Distance(p,
+ Type<Span<const int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 8,
+ Distance(p,
+ Type<Span<const int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p,
+ Type<Span<const int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p,
+ Type<Span<const Int128>>(L::Partial(0, 0, 0).Slice<2>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 4,
+ Distance(
+ p,
+ Type<Span<const int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 8,
+ Distance(
+ p,
+ Type<Span<const Int128>>(L::Partial(1, 0, 0).Slice<2>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 24,
+ Distance(
+ p,
+ Type<Span<const Int128>>(L::Partial(5, 3, 1).Slice<2>(p)).data()));
+ EXPECT_EQ(
+ 8,
+ Distance(
+ p,
+ Type<Span<const int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 24,
+ Distance(p, Type<Span<const Int128>>(L(5, 3, 1).Slice<2>(p)).data()));
+ EXPECT_EQ(
+ 8, Distance(p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
+ }
+}
+
+TEST(Layout, SliceByTypeData) {
+ alignas(max_align_t) const unsigned char p[100] = {};
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<const int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<const int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<const int32_t>>(L(3).Slice<int32_t>(p)).data()));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<const int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<const int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<const int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p,
+ Type<Span<const int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 4,
+ Distance(
+ p,
+ Type<Span<const int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 8,
+ Distance(
+ p,
+ Type<Span<const int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p,
+ Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<const int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p))
+ .data()));
+ EXPECT_EQ(0, Distance(p, Type<Span<const Int128>>(
+ L::Partial(0, 0, 0).Slice<Int128>(p))
+ .data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p,
+ Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 4,
+ Distance(p, Type<Span<const int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p))
+ .data()));
+ EXPECT_EQ(8, Distance(p, Type<Span<const Int128>>(
+ L::Partial(1, 0, 0).Slice<Int128>(p))
+ .data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p,
+ Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(24, Distance(p, Type<Span<const Int128>>(
+ L::Partial(5, 3, 1).Slice<Int128>(p))
+ .data()));
+ EXPECT_EQ(
+ 8,
+ Distance(p, Type<Span<const int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p))
+ .data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 24,
+ Distance(p,
+ Type<Span<const Int128>>(L(5, 3, 1).Slice<Int128>(p)).data()));
+ EXPECT_EQ(
+ 8, Distance(
+ p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
+ }
+}
+
+TEST(Layout, MutableSliceByIndexData) {
+ alignas(max_align_t) unsigned char p[100];
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(0,
+ Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<0>(p)).data()));
+ EXPECT_EQ(0,
+ Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
+ EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3).Slice<0>(p)).data()));
+ }
+ {
+ using L = Layout<int32_t, int32_t>;
+ EXPECT_EQ(0,
+ Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 12,
+ Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
+ EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3, 5).Slice<0>(p)).data()));
+ EXPECT_EQ(12, Distance(p, Type<Span<int32_t>>(L(3, 5).Slice<1>(p)).data()));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<0>(p)).data()));
+ EXPECT_EQ(0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<0>(p)).data()));
+ EXPECT_EQ(0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 4, Distance(p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 8, Distance(p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<2>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 4,
+ Distance(p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data()));
+ EXPECT_EQ(
+ 8, Distance(
+ p, Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<2>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
+ EXPECT_EQ(
+ 24, Distance(
+ p, Type<Span<Int128>>(L::Partial(5, 3, 1).Slice<2>(p)).data()));
+ EXPECT_EQ(
+ 8,
+ Distance(p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
+ EXPECT_EQ(0, Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
+ EXPECT_EQ(24,
+ Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<2>(p)).data()));
+ EXPECT_EQ(8, Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
+ }
+}
+
+TEST(Layout, MutableSliceByTypeData) {
+ alignas(max_align_t) unsigned char p[100];
+ {
+ using L = Layout<int32_t>;
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3).Slice<int32_t>(p)).data()));
+ }
+ {
+ using L = Layout<int8_t, int32_t, Int128>;
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 4, Distance(
+ p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 8, Distance(
+ p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 0,
+ Distance(
+ p,
+ Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<Int128>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 4,
+ Distance(
+ p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(
+ 8,
+ Distance(
+ p,
+ Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<Int128>(p)).data()));
+ EXPECT_EQ(
+ 0, Distance(
+ p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 24,
+ Distance(
+ p,
+ Type<Span<Int128>>(L::Partial(5, 3, 1).Slice<Int128>(p)).data()));
+ EXPECT_EQ(
+ 8,
+ Distance(
+ p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p)).data()));
+ EXPECT_EQ(0,
+ Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
+ EXPECT_EQ(
+ 24,
+ Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<Int128>(p)).data()));
+ EXPECT_EQ(
+ 8, Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
+ }
+}
+
+MATCHER_P(IsSameSlice, slice, "") {
+ return arg.size() == slice.size() && arg.data() == slice.data();
+}
+
+template <typename... M>
+class TupleMatcher {
+ public:
+ explicit TupleMatcher(M... matchers) : matchers_(std::move(matchers)...) {}
+
+ template <typename Tuple>
+ bool MatchAndExplain(const Tuple& p,
+ testing::MatchResultListener* /* listener */) const {
+ static_assert(std::tuple_size<Tuple>::value == sizeof...(M), "");
+ return MatchAndExplainImpl(
+ p, absl::make_index_sequence<std::tuple_size<Tuple>::value>{});
+ }
+
+ // For the matcher concept. Left empty as we don't really need the diagnostics
+ // right now.
+ void DescribeTo(::std::ostream* os) const {}
+ void DescribeNegationTo(::std::ostream* os) const {}
+
+ private:
+ template <typename Tuple, size_t... Is>
+ bool MatchAndExplainImpl(const Tuple& p, absl::index_sequence<Is...>) const {
+ // Using std::min as a simple variadic "and".
+ return std::min(
+ {true, testing::SafeMatcherCast<
+ const typename std::tuple_element<Is, Tuple>::type&>(
+ std::get<Is>(matchers_))
+ .Matches(std::get<Is>(p))...});
+ }
+
+ std::tuple<M...> matchers_;
+};
+
+template <typename... M>
+testing::PolymorphicMatcher<TupleMatcher<M...>> Tuple(M... matchers) {
+ return testing::MakePolymorphicMatcher(
+ TupleMatcher<M...>(std::move(matchers)...));
+}
+
+TEST(Layout, Slices) {
+ alignas(max_align_t) const unsigned char p[100] = {};
+ using L = Layout<int8_t, int8_t, Int128>;
+ {
+ const auto x = L::Partial();
+ EXPECT_THAT(Type<std::tuple<>>(x.Slices(p)), Tuple());
+ }
+ {
+ const auto x = L::Partial(1);
+ EXPECT_THAT(Type<std::tuple<Span<const int8_t>>>(x.Slices(p)),
+ Tuple(IsSameSlice(x.Slice<0>(p))));
+ }
+ {
+ const auto x = L::Partial(1, 2);
+ EXPECT_THAT(
+ (Type<std::tuple<Span<const int8_t>, Span<const int8_t>>>(x.Slices(p))),
+ Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p))));
+ }
+ {
+ const auto x = L::Partial(1, 2, 3);
+ EXPECT_THAT((Type<std::tuple<Span<const int8_t>, Span<const int8_t>,
+ Span<const Int128>>>(x.Slices(p))),
+ Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
+ IsSameSlice(x.Slice<2>(p))));
+ }
+ {
+ const L x(1, 2, 3);
+ EXPECT_THAT((Type<std::tuple<Span<const int8_t>, Span<const int8_t>,
+ Span<const Int128>>>(x.Slices(p))),
+ Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
+ IsSameSlice(x.Slice<2>(p))));
+ }
+}
+
+TEST(Layout, MutableSlices) {
+ alignas(max_align_t) unsigned char p[100] = {};
+ using L = Layout<int8_t, int8_t, Int128>;
+ {
+ const auto x = L::Partial();
+ EXPECT_THAT(Type<std::tuple<>>(x.Slices(p)), Tuple());
+ }
+ {
+ const auto x = L::Partial(1);
+ EXPECT_THAT(Type<std::tuple<Span<int8_t>>>(x.Slices(p)),
+ Tuple(IsSameSlice(x.Slice<0>(p))));
+ }
+ {
+ const auto x = L::Partial(1, 2);
+ EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>>>(x.Slices(p))),
+ Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p))));
+ }
+ {
+ const auto x = L::Partial(1, 2, 3);
+ EXPECT_THAT(
+ (Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(x.Slices(p))),
+ Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
+ IsSameSlice(x.Slice<2>(p))));
+ }
+ {
+ const L x(1, 2, 3);
+ EXPECT_THAT(
+ (Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(x.Slices(p))),
+ Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
+ IsSameSlice(x.Slice<2>(p))));
+ }
+}
+
+TEST(Layout, UnalignedTypes) {
+ constexpr Layout<unsigned char, unsigned char, unsigned char> x(1, 2, 3);
+ alignas(max_align_t) unsigned char p[x.AllocSize() + 1];
+ EXPECT_THAT(x.Pointers(p + 1), Tuple(p + 1, p + 2, p + 4));
+}
+
+TEST(Layout, CustomAlignment) {
+ constexpr Layout<unsigned char, Aligned<unsigned char, 8>> x(1, 2);
+ alignas(max_align_t) unsigned char p[x.AllocSize()];
+ EXPECT_EQ(10, x.AllocSize());
+ EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 8));
+}
+
+TEST(Layout, OverAligned) {
+ constexpr size_t M = alignof(max_align_t);
+ constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3);
+ alignas(2 * M) unsigned char p[x.AllocSize()];
+ EXPECT_EQ(2 * M + 3, x.AllocSize());
+ EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M));
+}
+
+TEST(Layout, Alignment) {
+ static_assert(Layout<int8_t>::Alignment() == 1, "");
+ static_assert(Layout<int32_t>::Alignment() == 4, "");
+ static_assert(Layout<int64_t>::Alignment() == 8, "");
+ static_assert(Layout<Aligned<int8_t, 64>>::Alignment() == 64, "");
+ static_assert(Layout<int8_t, int32_t, int64_t>::Alignment() == 8, "");
+ static_assert(Layout<int8_t, int64_t, int32_t>::Alignment() == 8, "");
+ static_assert(Layout<int32_t, int8_t, int64_t>::Alignment() == 8, "");
+ static_assert(Layout<int32_t, int64_t, int8_t>::Alignment() == 8, "");
+ static_assert(Layout<int64_t, int8_t, int32_t>::Alignment() == 8, "");
+ static_assert(Layout<int64_t, int32_t, int8_t>::Alignment() == 8, "");
+}
+
+TEST(Layout, ConstexprPartial) {
+ constexpr size_t M = alignof(max_align_t);
+ constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3);
+ static_assert(x.Partial(1).template Offset<1>() == 2 * M, "");
+}
+// [from, to)
+struct Region {
+ size_t from;
+ size_t to;
+};
+
+void ExpectRegionPoisoned(const unsigned char* p, size_t n, bool poisoned) {
+#ifdef ADDRESS_SANITIZER
+ for (size_t i = 0; i != n; ++i) {
+ EXPECT_EQ(poisoned, __asan_address_is_poisoned(p + i));
+ }
+#endif
+}
+
+template <size_t N>
+void ExpectPoisoned(const unsigned char (&buf)[N],
+ std::initializer_list<Region> reg) {
+ size_t prev = 0;
+ for (const Region& r : reg) {
+ ExpectRegionPoisoned(buf + prev, r.from - prev, false);
+ ExpectRegionPoisoned(buf + r.from, r.to - r.from, true);
+ prev = r.to;
+ }
+ ExpectRegionPoisoned(buf + prev, N - prev, false);
+}
+
+TEST(Layout, PoisonPadding) {
+ using L = Layout<int8_t, int64_t, int32_t, Int128>;
+
+ constexpr size_t n = L::Partial(1, 2, 3, 4).AllocSize();
+ {
+ constexpr auto x = L::Partial();
+ alignas(max_align_t) const unsigned char c[n] = {};
+ x.PoisonPadding(c);
+ EXPECT_EQ(x.Slices(c), x.Slices(c));
+ ExpectPoisoned(c, {});
+ }
+ {
+ constexpr auto x = L::Partial(1);
+ alignas(max_align_t) const unsigned char c[n] = {};
+ x.PoisonPadding(c);
+ EXPECT_EQ(x.Slices(c), x.Slices(c));
+ ExpectPoisoned(c, {{1, 8}});
+ }
+ {
+ constexpr auto x = L::Partial(1, 2);
+ alignas(max_align_t) const unsigned char c[n] = {};
+ x.PoisonPadding(c);
+ EXPECT_EQ(x.Slices(c), x.Slices(c));
+ ExpectPoisoned(c, {{1, 8}});
+ }
+ {
+ constexpr auto x = L::Partial(1, 2, 3);
+ alignas(max_align_t) const unsigned char c[n] = {};
+ x.PoisonPadding(c);
+ EXPECT_EQ(x.Slices(c), x.Slices(c));
+ ExpectPoisoned(c, {{1, 8}, {36, 40}});
+ }
+ {
+ constexpr auto x = L::Partial(1, 2, 3, 4);
+ alignas(max_align_t) const unsigned char c[n] = {};
+ x.PoisonPadding(c);
+ EXPECT_EQ(x.Slices(c), x.Slices(c));
+ ExpectPoisoned(c, {{1, 8}, {36, 40}});
+ }
+ {
+ constexpr L x(1, 2, 3, 4);
+ alignas(max_align_t) const unsigned char c[n] = {};
+ x.PoisonPadding(c);
+ EXPECT_EQ(x.Slices(c), x.Slices(c));
+ ExpectPoisoned(c, {{1, 8}, {36, 40}});
+ }
+}
+
+TEST(Layout, DebugString) {
+ {
+ constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial();
+ EXPECT_EQ("@0<signed char>(1)", x.DebugString());
+ }
+ {
+ constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1);
+ EXPECT_EQ("@0<signed char>(1)[1]; @4<int>(4)", x.DebugString());
+ }
+ {
+ constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2);
+ EXPECT_EQ("@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)",
+ x.DebugString());
+ }
+ {
+ constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3);
+ EXPECT_EQ(
+ "@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
+ "@16" +
+ Int128::Name() + "(16)",
+ x.DebugString());
+ }
+ {
+ constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3, 4);
+ EXPECT_EQ(
+ "@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
+ "@16" +
+ Int128::Name() + "(16)[4]",
+ x.DebugString());
+ }
+ {
+ constexpr Layout<int8_t, int32_t, int8_t, Int128> x(1, 2, 3, 4);
+ EXPECT_EQ(
+ "@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
+ "@16" +
+ Int128::Name() + "(16)[4]",
+ x.DebugString());
+ }
+}
+
+TEST(Layout, CharTypes) {
+ constexpr Layout<int32_t> x(1);
+ alignas(max_align_t) char c[x.AllocSize()] = {};
+ alignas(max_align_t) unsigned char uc[x.AllocSize()] = {};
+ alignas(max_align_t) signed char sc[x.AllocSize()] = {};
+ alignas(max_align_t) const char cc[x.AllocSize()] = {};
+ alignas(max_align_t) const unsigned char cuc[x.AllocSize()] = {};
+ alignas(max_align_t) const signed char csc[x.AllocSize()] = {};
+
+ Type<int32_t*>(x.Pointer<0>(c));
+ Type<int32_t*>(x.Pointer<0>(uc));
+ Type<int32_t*>(x.Pointer<0>(sc));
+ Type<const int32_t*>(x.Pointer<0>(cc));
+ Type<const int32_t*>(x.Pointer<0>(cuc));
+ Type<const int32_t*>(x.Pointer<0>(csc));
+
+ Type<int32_t*>(x.Pointer<int32_t>(c));
+ Type<int32_t*>(x.Pointer<int32_t>(uc));
+ Type<int32_t*>(x.Pointer<int32_t>(sc));
+ Type<const int32_t*>(x.Pointer<int32_t>(cc));
+ Type<const int32_t*>(x.Pointer<int32_t>(cuc));
+ Type<const int32_t*>(x.Pointer<int32_t>(csc));
+
+ Type<std::tuple<int32_t*>>(x.Pointers(c));
+ Type<std::tuple<int32_t*>>(x.Pointers(uc));
+ Type<std::tuple<int32_t*>>(x.Pointers(sc));
+ Type<std::tuple<const int32_t*>>(x.Pointers(cc));
+ Type<std::tuple<const int32_t*>>(x.Pointers(cuc));
+ Type<std::tuple<const int32_t*>>(x.Pointers(csc));
+
+ Type<Span<int32_t>>(x.Slice<0>(c));
+ Type<Span<int32_t>>(x.Slice<0>(uc));
+ Type<Span<int32_t>>(x.Slice<0>(sc));
+ Type<Span<const int32_t>>(x.Slice<0>(cc));
+ Type<Span<const int32_t>>(x.Slice<0>(cuc));
+ Type<Span<const int32_t>>(x.Slice<0>(csc));
+
+ Type<std::tuple<Span<int32_t>>>(x.Slices(c));
+ Type<std::tuple<Span<int32_t>>>(x.Slices(uc));
+ Type<std::tuple<Span<int32_t>>>(x.Slices(sc));
+ Type<std::tuple<Span<const int32_t>>>(x.Slices(cc));
+ Type<std::tuple<Span<const int32_t>>>(x.Slices(cuc));
+ Type<std::tuple<Span<const int32_t>>>(x.Slices(csc));
+}
+
+TEST(Layout, ConstElementType) {
+ constexpr Layout<const int32_t> x(1);
+ alignas(int32_t) char c[x.AllocSize()] = {};
+ const char* cc = c;
+ const int32_t* p = reinterpret_cast<const int32_t*>(cc);
+
+ EXPECT_EQ(alignof(int32_t), x.Alignment());
+
+ EXPECT_EQ(0, x.Offset<0>());
+ EXPECT_EQ(0, x.Offset<const int32_t>());
+
+ EXPECT_THAT(x.Offsets(), ElementsAre(0));
+
+ EXPECT_EQ(1, x.Size<0>());
+ EXPECT_EQ(1, x.Size<const int32_t>());
+
+ EXPECT_THAT(x.Sizes(), ElementsAre(1));
+
+ EXPECT_EQ(sizeof(int32_t), x.AllocSize());
+
+ EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<0>(c)));
+ EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<0>(cc)));
+
+ EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<const int32_t>(c)));
+ EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<const int32_t>(cc)));
+
+ EXPECT_THAT(Type<std::tuple<const int32_t*>>(x.Pointers(c)), Tuple(p));
+ EXPECT_THAT(Type<std::tuple<const int32_t*>>(x.Pointers(cc)), Tuple(p));
+
+ EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<0>(c)),
+ IsSameSlice(Span<const int32_t>(p, 1)));
+ EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<0>(cc)),
+ IsSameSlice(Span<const int32_t>(p, 1)));
+
+ EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<const int32_t>(c)),
+ IsSameSlice(Span<const int32_t>(p, 1)));
+ EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<const int32_t>(cc)),
+ IsSameSlice(Span<const int32_t>(p, 1)));
+
+ EXPECT_THAT(Type<std::tuple<Span<const int32_t>>>(x.Slices(c)),
+ Tuple(IsSameSlice(Span<const int32_t>(p, 1))));
+ EXPECT_THAT(Type<std::tuple<Span<const int32_t>>>(x.Slices(cc)),
+ Tuple(IsSameSlice(Span<const int32_t>(p, 1))));
+}
+
+namespace example {
+
+// Immutable move-only string with sizeof equal to sizeof(void*). The string
+// size and the characters are kept in the same heap allocation.
+class CompactString {
+ public:
+ CompactString(const char* s = "") { // NOLINT
+ const size_t size = strlen(s);
+ // size_t[1], followed by char[size + 1].
+ // This statement doesn't allocate memory.
+ const L layout(1, size + 1);
+ // AllocSize() tells us how much memory we need to allocate for all our
+ // data.
+ p_.reset(new unsigned char[layout.AllocSize()]);
+ // If running under ASAN, mark the padding bytes, if any, to catch memory
+ // errors.
+ layout.PoisonPadding(p_.get());
+ // Store the size in the allocation.
+ // Pointer<size_t>() is a synonym for Pointer<0>().
+ *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_;
+};
+
+TEST(CompactString, Works) {
+ CompactString s = "hello";
+ EXPECT_EQ(5, s.size());
+ EXPECT_STREQ("hello", s.c_str());
+}
+
+} // namespace example
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/node_hash_policy.h b/absl/container/internal/node_hash_policy.h
new file mode 100644
index 00000000..e8d89f63
--- /dev/null
+++ b/absl/container/internal/node_hash_policy.h
@@ -0,0 +1,90 @@
+// 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
+//
+// 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.
+//
+// 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_HASH_POLICY_H_
+#define ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+template <class Reference, class Policy>
+struct node_hash_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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_
diff --git a/absl/container/internal/node_hash_policy_test.cc b/absl/container/internal/node_hash_policy_test.cc
new file mode 100644
index 00000000..a73c7bba
--- /dev/null
+++ b/absl/container/internal/node_hash_policy_test.cc
@@ -0,0 +1,69 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/node_hash_policy.h"
+
+#include <memory>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/container/internal/hash_policy_traits.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using ::testing::Pointee;
+
+struct Policy : node_hash_policy<int&, Policy> {
+ using key_type = int;
+ using init_type = int;
+
+ template <class Alloc>
+ static int* new_element(Alloc* alloc, int value) {
+ return new int(value);
+ }
+
+ template <class Alloc>
+ static void delete_element(Alloc* alloc, int* elem) {
+ delete elem;
+ }
+};
+
+using NodePolicy = hash_policy_traits<Policy>;
+
+struct NodeTest : ::testing::Test {
+ std::allocator<int> alloc;
+ int n = 53;
+ int* a = &n;
+};
+
+TEST_F(NodeTest, ConstructDestroy) {
+ NodePolicy::construct(&alloc, &a, 42);
+ EXPECT_THAT(a, Pointee(42));
+ NodePolicy::destroy(&alloc, &a);
+}
+
+TEST_F(NodeTest, transfer) {
+ int s = 42;
+ int* b = &s;
+ NodePolicy::transfer(&alloc, &a, &b);
+ EXPECT_EQ(&s, a);
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
new file mode 100644
index 00000000..53d4619a
--- /dev/null
+++ b/absl/container/internal/raw_hash_map.h
@@ -0,0 +1,187 @@
+// 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
+//
+// 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.
+
+#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/container/internal/container_memory.h"
+#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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 = container_internal::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(alkis): remove this assertion and verify that reference mapped_type 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;
+ }
+
+ 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()) std::abort();
+ 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()) std::abort();
+ 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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
new file mode 100644
index 00000000..4e690dac
--- /dev/null
+++ b/absl/container/internal/raw_hash_set.cc
@@ -0,0 +1,48 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/raw_hash_set.h"
+
+#include <atomic>
+#include <cstddef>
+
+#include "absl/base/config.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+constexpr size_t Group::kWidth;
+
+// Returns "random" seed.
+inline size_t RandomSeed() {
+#if ABSL_HAVE_THREAD_LOCAL
+ static thread_local size_t counter = 0;
+ size_t value = ++counter;
+#else // ABSL_HAVE_THREAD_LOCAL
+ static std::atomic<size_t> counter(0);
+ size_t value = counter.fetch_add(1, std::memory_order_relaxed);
+#endif // ABSL_HAVE_THREAD_LOCAL
+ return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
+}
+
+bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) {
+ // To avoid problems with weak hashes and single bit tests, we use % 13.
+ // TODO(kfm,sbenza): revisit after we do unconditional mixing
+ return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6;
+}
+
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
new file mode 100644
index 00000000..0c42e4ae
--- /dev/null
+++ b/absl/container/internal/raw_hash_set.h
@@ -0,0 +1,1950 @@
+// 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
+//
+// 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.
+//
+// An open-addressing
+// hashtable with quadratic probing.
+//
+// This is a low level hashtable on top of which different interfaces can be
+// implemented, like flat_hash_set, node_hash_set, string_hash_set, etc.
+//
+// The table interface is similar to that of std::unordered_set. Notable
+// differences are that most member functions support heterogeneous keys when
+// BOTH the hash and eq functions are marked as transparent. They do so by
+// providing a typedef called `is_transparent`.
+//
+// When heterogeneous lookup is enabled, functions that take key_type act as if
+// they have an overload set like:
+//
+// iterator find(const key_type& key);
+// template <class K>
+// iterator find(const K& key);
+//
+// size_type erase(const key_type& key);
+// template <class K>
+// size_type erase(const K& key);
+//
+// std::pair<iterator, iterator> equal_range(const key_type& key);
+// template <class K>
+// std::pair<iterator, iterator> equal_range(const K& key);
+//
+// When heterogeneous lookup is disabled, only the explicit `key_type` overloads
+// exist.
+//
+// find() also supports passing the hash explicitly:
+//
+// iterator find(const key_type& key, size_t hash);
+// template <class U>
+// iterator find(const U& key, size_t hash);
+//
+// In addition the pointer to element and iterator stability guarantees are
+// weaker: all iterators and pointers are invalidated after a new element is
+// inserted.
+//
+// IMPLEMENTATION DETAILS
+//
+// The table stores elements inline in a slot array. In addition to the slot
+// array the table maintains some control state per slot. The extra state is one
+// byte per slot and stores empty or deleted marks, or alternatively 7 bits from
+// the hash of an occupied slot. The table is split into logical groups of
+// slots, like so:
+//
+// Group 1 Group 2 Group 3
+// +---------------+---------------+---------------+
+// | | | | | | | | | | | | | | | | | | | | | | | | |
+// +---------------+---------------+---------------+
+//
+// On lookup the hash is split into two parts:
+// - H2: 7 bits (those stored in the control bytes)
+// - H1: the rest of the bits
+// The groups are probed using H1. For each group the slots are matched to H2 in
+// parallel. Because H2 is 7 bits (128 states) and the number of slots per group
+// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit.
+//
+// On insert, once the right group is found (as in lookup), its slots are
+// filled in order.
+//
+// On erase a slot is cleared. In case the group did not have any empty slots
+// before the erase, the erased slot is marked as deleted.
+//
+// Groups without empty slots (but maybe with deleted slots) extend the probe
+// sequence. The probing algorithm is quadratic. Given N the number of groups,
+// the probing function for the i'th probe is:
+//
+// P(0) = H1 % N
+//
+// P(i) = (P(i - 1) + i) % N
+//
+// This probing function guarantees that after N probes, all the groups of the
+// table will be probed exactly once.
+
+#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
+#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
+
+#ifndef SWISSTABLE_HAVE_SSE2
+#if defined(__SSE2__) || \
+ (defined(_MSC_VER) && \
+ (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2)))
+#define SWISSTABLE_HAVE_SSE2 1
+#else
+#define SWISSTABLE_HAVE_SSE2 0
+#endif
+#endif
+
+#ifndef SWISSTABLE_HAVE_SSSE3
+#ifdef __SSSE3__
+#define SWISSTABLE_HAVE_SSSE3 1
+#else
+#define SWISSTABLE_HAVE_SSSE3 0
+#endif
+#endif
+
+#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2
+#error "Bad configuration!"
+#endif
+
+#if SWISSTABLE_HAVE_SSE2
+#include <emmintrin.h>
+#endif
+
+#if SWISSTABLE_HAVE_SSSE3
+#include <tmmintrin.h>
+#endif
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/internal/bits.h"
+#include "absl/base/internal/endian.h"
+#include "absl/base/port.h"
+#include "absl/container/internal/compressed_tuple.h"
+#include "absl/container/internal/container_memory.h"
+#include "absl/container/internal/hash_policy_traits.h"
+#include "absl/container/internal/hashtable_debug_hooks.h"
+#include "absl/container/internal/layout.h"
+#include "absl/memory/memory.h"
+#include "absl/meta/type_traits.h"
+#include "absl/types/optional.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+template <size_t Width>
+class probe_seq {
+ public:
+ probe_seq(size_t hash, size_t mask) {
+ assert(((mask + 1) & mask) == 0 && "not a mask");
+ mask_ = mask;
+ offset_ = hash & mask_;
+ }
+ size_t offset() const { return offset_; }
+ size_t offset(size_t i) const { return (offset_ + i) & mask_; }
+
+ void next() {
+ index_ += Width;
+ offset_ += index_;
+ offset_ &= mask_;
+ }
+ // 0-based probe index. The i-th probe in the probe sequence.
+ size_t index() const { return index_; }
+
+ private:
+ size_t mask_;
+ size_t offset_;
+ size_t index_ = 0;
+};
+
+template <class ContainerKey, class Hash, class Eq>
+struct RequireUsableKey {
+ template <class PassedKey, class... Args>
+ std::pair<
+ decltype(std::declval<const Hash&>()(std::declval<const PassedKey&>())),
+ decltype(std::declval<const Eq&>()(std::declval<const ContainerKey&>(),
+ std::declval<const PassedKey&>()))>*
+ operator()(const PassedKey&, const Args&...) const;
+};
+
+template <class E, class Policy, class Hash, class Eq, class... Ts>
+struct IsDecomposable : std::false_type {};
+
+template <class Policy, class Hash, class Eq, class... Ts>
+struct IsDecomposable<
+ absl::void_t<decltype(
+ Policy::apply(RequireUsableKey<typename Policy::key_type, Hash, Eq>(),
+ std::declval<Ts>()...))>,
+ Policy, Hash, Eq, Ts...> : std::true_type {};
+
+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 {};
+
+// TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it.
+template <class T>
+constexpr bool IsNoThrowSwappable() {
+ using std::swap;
+ return noexcept(swap(std::declval<T&>(), std::declval<T&>()));
+}
+
+template <typename T>
+int TrailingZeros(T x) {
+ return sizeof(T) == 8 ? base_internal::CountTrailingZerosNonZero64(
+ static_cast<uint64_t>(x))
+ : base_internal::CountTrailingZerosNonZero32(
+ static_cast<uint32_t>(x));
+}
+
+template <typename T>
+int LeadingZeros(T x) {
+ return sizeof(T) == 8
+ ? base_internal::CountLeadingZeros64(static_cast<uint64_t>(x))
+ : base_internal::CountLeadingZeros32(static_cast<uint32_t>(x));
+}
+
+// An abstraction over a bitmask. It provides an easy way to iterate through the
+// indexes of the set bits of a bitmask. When Shift=0 (platforms with SSE),
+// this is a true bitmask. On non-SSE, platforms the arithematic used to
+// emulate the SSE behavior works in bytes (Shift=3) and leaves each bytes as
+// either 0x00 or 0x80.
+//
+// For example:
+// for (int i : BitMask<uint32_t, 16>(0x5)) -> yields 0, 2
+// for (int i : BitMask<uint64_t, 8, 3>(0x0000000080800000)) -> yields 2, 3
+template <class T, int SignificantBits, int Shift = 0>
+class BitMask {
+ static_assert(std::is_unsigned<T>::value, "");
+ static_assert(Shift == 0 || Shift == 3, "");
+
+ public:
+ // These are useful for unit tests (gunit).
+ using value_type = int;
+ using iterator = BitMask;
+ using const_iterator = BitMask;
+
+ explicit BitMask(T mask) : mask_(mask) {}
+ BitMask& operator++() {
+ mask_ &= (mask_ - 1);
+ return *this;
+ }
+ explicit operator bool() const { return mask_ != 0; }
+ int operator*() const { return LowestBitSet(); }
+ int LowestBitSet() const {
+ return container_internal::TrailingZeros(mask_) >> Shift;
+ }
+ int HighestBitSet() const {
+ return (sizeof(T) * CHAR_BIT - container_internal::LeadingZeros(mask_) -
+ 1) >>
+ Shift;
+ }
+
+ BitMask begin() const { return *this; }
+ BitMask end() const { return BitMask(0); }
+
+ int TrailingZeros() const {
+ return container_internal::TrailingZeros(mask_) >> Shift;
+ }
+
+ int LeadingZeros() const {
+ constexpr int total_significant_bits = SignificantBits << Shift;
+ constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
+ return container_internal::LeadingZeros(mask_ << extra_bits) >> Shift;
+ }
+
+ private:
+ friend bool operator==(const BitMask& a, const BitMask& b) {
+ return a.mask_ == b.mask_;
+ }
+ friend bool operator!=(const BitMask& a, const BitMask& b) {
+ return a.mask_ != b.mask_;
+ }
+
+ T mask_;
+};
+
+using ctrl_t = signed char;
+using h2_t = uint8_t;
+
+// The values here are selected for maximum performance. See the static asserts
+// below for details.
+enum Ctrl : ctrl_t {
+ kEmpty = -128, // 0b10000000
+ kDeleted = -2, // 0b11111110
+ kSentinel = -1, // 0b11111111
+};
+static_assert(
+ kEmpty & kDeleted & kSentinel & 0x80,
+ "Special markers need to have the MSB to make checking for them efficient");
+static_assert(kEmpty < kSentinel && kDeleted < kSentinel,
+ "kEmpty and kDeleted must be smaller than kSentinel to make the "
+ "SIMD test of IsEmptyOrDeleted() efficient");
+static_assert(kSentinel == -1,
+ "kSentinel must be -1 to elide loading it from memory into SIMD "
+ "registers (pcmpeqd xmm, xmm)");
+static_assert(kEmpty == -128,
+ "kEmpty must be -128 to make the SIMD check for its "
+ "existence efficient (psignb xmm, xmm)");
+static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F,
+ "kEmpty and kDeleted must share an unset bit that is not shared "
+ "by kSentinel to make the scalar test for MatchEmptyOrDeleted() "
+ "efficient");
+static_assert(kDeleted == -2,
+ "kDeleted must be -2 to make the implementation of "
+ "ConvertSpecialToEmptyAndFullToDeleted efficient");
+
+// A single block of empty control bytes for tables without any slots allocated.
+// This enables removing a branch in the hot path of find().
+inline ctrl_t* EmptyGroup() {
+ alignas(16) static constexpr ctrl_t empty_group[] = {
+ kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
+ kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty};
+ return const_cast<ctrl_t*>(empty_group);
+}
+
+// Mixes a randomly generated per-process seed with `hash` and `ctrl` to
+// randomize insertion order within groups.
+bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl);
+
+// Returns a hash seed.
+//
+// The seed consists of the ctrl_ pointer, which adds enough entropy to ensure
+// non-determinism of iteration order in most cases.
+inline size_t HashSeed(const ctrl_t* ctrl) {
+ // The low bits of the pointer have little or no entropy because of
+ // alignment. We shift the pointer to try to use higher entropy bits. A
+ // good number seems to be 12 bits, because that aligns with page size.
+ return reinterpret_cast<uintptr_t>(ctrl) >> 12;
+}
+
+inline size_t H1(size_t hash, const ctrl_t* ctrl) {
+ return (hash >> 7) ^ HashSeed(ctrl);
+}
+inline ctrl_t H2(size_t hash) { return hash & 0x7F; }
+
+inline bool IsEmpty(ctrl_t c) { return c == kEmpty; }
+inline bool IsFull(ctrl_t c) { return c >= 0; }
+inline bool IsDeleted(ctrl_t c) { return c == kDeleted; }
+inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; }
+
+#if SWISSTABLE_HAVE_SSE2
+
+// https://github.com/abseil/abseil-cpp/issues/209
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853
+// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char
+// Work around this by using the portable implementation of Group
+// when using -funsigned-char under GCC.
+inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) {
+#if defined(__GNUC__) && !defined(__clang__)
+ if (std::is_unsigned<char>::value) {
+ const __m128i mask = _mm_set1_epi8(0x80);
+ const __m128i diff = _mm_subs_epi8(b, a);
+ return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask);
+ }
+#endif
+ return _mm_cmpgt_epi8(a, b);
+}
+
+struct GroupSse2Impl {
+ static constexpr size_t kWidth = 16; // the number of slots per group
+
+ explicit GroupSse2Impl(const ctrl_t* pos) {
+ ctrl = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pos));
+ }
+
+ // Returns a bitmask representing the positions of slots that match hash.
+ BitMask<uint32_t, kWidth> Match(h2_t hash) const {
+ auto match = _mm_set1_epi8(hash);
+ return BitMask<uint32_t, kWidth>(
+ _mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)));
+ }
+
+ // Returns a bitmask representing the positions of empty slots.
+ BitMask<uint32_t, kWidth> MatchEmpty() const {
+#if SWISSTABLE_HAVE_SSSE3
+ // This only works because kEmpty is -128.
+ return BitMask<uint32_t, kWidth>(
+ _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)));
+#else
+ return Match(kEmpty);
+#endif
+ }
+
+ // Returns a bitmask representing the positions of empty or deleted slots.
+ BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const {
+ auto special = _mm_set1_epi8(kSentinel);
+ return BitMask<uint32_t, kWidth>(
+ _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)));
+ }
+
+ // Returns the number of trailing empty or deleted elements in the group.
+ uint32_t CountLeadingEmptyOrDeleted() const {
+ auto special = _mm_set1_epi8(kSentinel);
+ return TrailingZeros(
+ _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1);
+ }
+
+ void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
+ auto msbs = _mm_set1_epi8(static_cast<char>(-128));
+ auto x126 = _mm_set1_epi8(126);
+#if SWISSTABLE_HAVE_SSSE3
+ auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs);
+#else
+ auto zero = _mm_setzero_si128();
+ auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl);
+ auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126));
+#endif
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res);
+ }
+
+ __m128i ctrl;
+};
+#endif // SWISSTABLE_HAVE_SSE2
+
+struct GroupPortableImpl {
+ static constexpr size_t kWidth = 8;
+
+ explicit GroupPortableImpl(const ctrl_t* pos)
+ : ctrl(little_endian::Load64(pos)) {}
+
+ BitMask<uint64_t, kWidth, 3> Match(h2_t hash) const {
+ // For the technique, see:
+ // http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord
+ // (Determine if a word has a byte equal to n).
+ //
+ // Caveat: there are false positives but:
+ // - they only occur if there is a real match
+ // - they never occur on kEmpty, kDeleted, kSentinel
+ // - they will be handled gracefully by subsequent checks in code
+ //
+ // Example:
+ // v = 0x1716151413121110
+ // hash = 0x12
+ // retval = (v - lsbs) & ~v & msbs = 0x0000000080800000
+ constexpr uint64_t msbs = 0x8080808080808080ULL;
+ constexpr uint64_t lsbs = 0x0101010101010101ULL;
+ auto x = ctrl ^ (lsbs * hash);
+ return BitMask<uint64_t, kWidth, 3>((x - lsbs) & ~x & msbs);
+ }
+
+ BitMask<uint64_t, kWidth, 3> MatchEmpty() const {
+ constexpr uint64_t msbs = 0x8080808080808080ULL;
+ return BitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 6)) & msbs);
+ }
+
+ BitMask<uint64_t, kWidth, 3> MatchEmptyOrDeleted() const {
+ constexpr uint64_t msbs = 0x8080808080808080ULL;
+ return BitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 7)) & msbs);
+ }
+
+ uint32_t CountLeadingEmptyOrDeleted() const {
+ constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL;
+ return (TrailingZeros(((~ctrl & (ctrl >> 7)) | gaps) + 1) + 7) >> 3;
+ }
+
+ void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
+ constexpr uint64_t msbs = 0x8080808080808080ULL;
+ constexpr uint64_t lsbs = 0x0101010101010101ULL;
+ auto x = ctrl & msbs;
+ auto res = (~x + (x >> 7)) & ~lsbs;
+ little_endian::Store64(dst, res);
+ }
+
+ uint64_t ctrl;
+};
+
+#if SWISSTABLE_HAVE_SSE2
+using Group = GroupSse2Impl;
+#else
+using Group = GroupPortableImpl;
+#endif
+
+template <class Policy, class Hash, class Eq, class Alloc>
+class raw_hash_set;
+
+inline bool IsValidCapacity(size_t n) {
+ return ((n + 1) & n) == 0 && n >= Group::kWidth - 1;
+}
+
+// PRECONDITION:
+// IsValidCapacity(capacity)
+// ctrl[capacity] == kSentinel
+// ctrl[i] != kSentinel for all i < capacity
+// Applies mapping for every byte in ctrl:
+// DELETED -> EMPTY
+// EMPTY -> EMPTY
+// FULL -> DELETED
+inline void ConvertDeletedToEmptyAndFullToDeleted(
+ ctrl_t* ctrl, size_t capacity) {
+ assert(ctrl[capacity] == kSentinel);
+ assert(IsValidCapacity(capacity));
+ for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
+ Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
+ }
+ // Copy the cloned ctrl bytes.
+ std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
+ ctrl[capacity] = kSentinel;
+}
+
+// Rounds up the capacity to the next power of 2 minus 1 and ensures it is
+// greater or equal to Group::kWidth - 1.
+inline size_t NormalizeCapacity(size_t n) {
+ constexpr size_t kMinCapacity = Group::kWidth - 1;
+ return n <= kMinCapacity
+ ? kMinCapacity
+ : (std::numeric_limits<size_t>::max)() >> LeadingZeros(n);
+}
+
+// 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 Policy, typename Alloc>
+class node_handle_base {
+ protected:
+ using PolicyTraits = hash_policy_traits<Policy>;
+ using slot_type = typename PolicyTraits::slot_type;
+
+ public:
+ using allocator_type = Alloc;
+
+ constexpr node_handle_base() {}
+ node_handle_base(node_handle_base&& other) noexcept {
+ *this = std::move(other);
+ }
+ ~node_handle_base() { destroy(); }
+ node_handle_base& operator=(node_handle_base&& other) {
+ 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:
+ template <typename, typename, typename, typename>
+ friend class raw_hash_set;
+
+ node_handle_base(const allocator_type& a, slot_type* s) : alloc_(a) {
+ PolicyTraits::transfer(alloc(), slot(), s);
+ }
+
+ 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_;
+ mutable absl::aligned_storage_t<sizeof(slot_type), alignof(slot_type)>
+ slot_space_;
+};
+
+// For sets.
+template <typename Policy, typename Alloc, typename = void>
+class node_handle : public node_handle_base<Policy, Alloc> {
+ using Base = typename node_handle::node_handle_base;
+
+ public:
+ using value_type = typename Base::PolicyTraits::value_type;
+
+ constexpr node_handle() {}
+
+ value_type& value() const {
+ return Base::PolicyTraits::element(this->slot());
+ }
+
+ private:
+ template <typename, typename, typename, typename>
+ friend class raw_hash_set;
+
+ node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
+};
+
+// For maps.
+template <typename Policy, typename Alloc>
+class node_handle<Policy, Alloc, absl::void_t<typename Policy::mapped_type>>
+ : public node_handle_base<Policy, Alloc> {
+ using Base = typename node_handle::node_handle_base;
+
+ public:
+ using key_type = typename Policy::key_type;
+ using mapped_type = typename Policy::mapped_type;
+
+ constexpr node_handle() {}
+
+ auto key() const -> decltype(Base::PolicyTraits::key(this->slot())) {
+ return Base::PolicyTraits::key(this->slot());
+ }
+
+ mapped_type& mapped() const {
+ return Base::PolicyTraits::value(
+ &Base::PolicyTraits::element(this->slot()));
+ }
+
+ private:
+ template <typename, typename, typename, typename>
+ friend class raw_hash_set;
+
+ node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
+};
+
+// Implement the insert_return_type<> concept of C++17.
+template <class Iterator, class NodeType>
+struct insert_return_type {
+ Iterator position;
+ bool inserted;
+ NodeType node;
+};
+
+// Helper trait to allow or disallow arbitrary keys when the hash and
+// eq functions are transparent.
+// It is very important that the inner template is an alias and that the type it
+// produces is not a dependent type. Otherwise, type deduction would fail.
+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;
+};
+
+// Policy: a policy defines how to perform different operations on
+// the slots of the hashtable (see hash_policy_traits.h for the full interface
+// of policy).
+//
+// Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The
+// functor should accept a key and return size_t as hash. For best performance
+// it is important that the hash function provides high entropy across all bits
+// of the hash.
+//
+// Eq: a (possibly polymorphic) functor that compares two keys for equality. It
+// should accept two (of possibly different type) keys and return a bool: true
+// if they are equal, false if they are not. If two keys compare equal, then
+// their hash values as defined by Hash MUST be equal.
+//
+// Allocator: an Allocator [http://devdocs.io/cpp/concept/allocator] with which
+// the storage of the hashtable will be allocated and the elements will be
+// constructed and destroyed.
+template <class Policy, class Hash, class Eq, class Alloc>
+class raw_hash_set {
+ using PolicyTraits = hash_policy_traits<Policy>;
+ using KeyArgImpl = container_internal::KeyArg<IsTransparent<Eq>::value &&
+ IsTransparent<Hash>::value>;
+
+ public:
+ using init_type = typename PolicyTraits::init_type;
+ using key_type = typename PolicyTraits::key_type;
+ // TODO(sbenza): Hide slot_type as it is an implementation detail. Needs user
+ // code fixes!
+ using slot_type = typename PolicyTraits::slot_type;
+ using allocator_type = Alloc;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+ using hasher = Hash;
+ using key_equal = Eq;
+ using policy_type = Policy;
+ using value_type = typename PolicyTraits::value_type;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using pointer = typename absl::allocator_traits<
+ allocator_type>::template rebind_traits<value_type>::pointer;
+ using const_pointer = typename absl::allocator_traits<
+ allocator_type>::template rebind_traits<value_type>::const_pointer;
+
+ // 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 KeyArgImpl::template type<K, key_type>;
+
+ private:
+ // Give an early error when key_type is not hashable/eq.
+ auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k));
+ auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k));
+
+ using Layout = absl::container_internal::Layout<ctrl_t, slot_type>;
+
+ static Layout MakeLayout(size_t capacity) {
+ assert(IsValidCapacity(capacity));
+ return Layout(capacity + Group::kWidth + 1, capacity);
+ }
+
+ using AllocTraits = absl::allocator_traits<allocator_type>;
+ using SlotAlloc = typename absl::allocator_traits<
+ allocator_type>::template rebind_alloc<slot_type>;
+ using SlotAllocTraits = typename absl::allocator_traits<
+ allocator_type>::template rebind_traits<slot_type>;
+
+ static_assert(std::is_lvalue_reference<reference>::value,
+ "Policy::element() must return a reference");
+
+ template <typename T>
+ struct SameAsElementReference
+ : std::is_same<typename std::remove_cv<
+ typename std::remove_reference<reference>::type>::type,
+ typename std::remove_cv<
+ typename std::remove_reference<T>::type>::type> {};
+
+ // An enabler for insert(T&&): T must be convertible to init_type or be the
+ // same as [cv] value_type [ref].
+ // Note: we separate SameAsElementReference into its own type to avoid using
+ // reference unless we need to. MSVC doesn't seem to like it in some
+ // cases.
+ template <class T>
+ using RequiresInsertable = typename std::enable_if<
+ absl::disjunction<std::is_convertible<T, init_type>,
+ SameAsElementReference<T>>::value,
+ int>::type;
+
+ // RequiresNotInit is a workaround for gcc prior to 7.1.
+ // See https://godbolt.org/g/Y4xsUh.
+ template <class T>
+ using RequiresNotInit =
+ typename std::enable_if<!std::is_same<T, init_type>::value, int>::type;
+
+ template <class... Ts>
+ using IsDecomposable = IsDecomposable<void, PolicyTraits, Hash, Eq, Ts...>;
+
+ public:
+ static_assert(std::is_same<pointer, value_type*>::value,
+ "Allocators with custom pointer types are not supported");
+ static_assert(std::is_same<const_pointer, const value_type*>::value,
+ "Allocators with custom pointer types are not supported");
+
+ class iterator {
+ friend class raw_hash_set;
+
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = typename raw_hash_set::value_type;
+ using reference =
+ absl::conditional_t<PolicyTraits::constant_iterators::value,
+ const value_type&, value_type&>;
+ using pointer = absl::remove_reference_t<reference>*;
+ using difference_type = typename raw_hash_set::difference_type;
+
+ iterator() {}
+
+ // PRECONDITION: not an end() iterator.
+ reference operator*() const { return PolicyTraits::element(slot_); }
+
+ // PRECONDITION: not an end() iterator.
+ pointer operator->() const { return &operator*(); }
+
+ // PRECONDITION: not an end() iterator.
+ iterator& operator++() {
+ ++ctrl_;
+ ++slot_;
+ skip_empty_or_deleted();
+ return *this;
+ }
+ // PRECONDITION: not an end() iterator.
+ iterator operator++(int) {
+ auto tmp = *this;
+ ++*this;
+ return tmp;
+ }
+
+ friend bool operator==(const iterator& a, const iterator& b) {
+ return a.ctrl_ == b.ctrl_;
+ }
+ friend bool operator!=(const iterator& a, const iterator& b) {
+ return !(a == b);
+ }
+
+ private:
+ iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end()
+ iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {}
+
+ void skip_empty_or_deleted() {
+ while (IsEmptyOrDeleted(*ctrl_)) {
+ // ctrl is not necessarily aligned to Group::kWidth. It is also likely
+ // to read past the space for ctrl bytes and into slots. This is ok
+ // because ctrl has sizeof() == 1 and slot has sizeof() >= 1 so there
+ // is no way to read outside the combined slot array.
+ uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted();
+ ctrl_ += shift;
+ slot_ += shift;
+ }
+ }
+
+ ctrl_t* ctrl_ = nullptr;
+ slot_type* slot_;
+ };
+
+ class const_iterator {
+ friend class raw_hash_set;
+
+ public:
+ using iterator_category = typename iterator::iterator_category;
+ using value_type = typename raw_hash_set::value_type;
+ using reference = typename raw_hash_set::const_reference;
+ using pointer = typename raw_hash_set::const_pointer;
+ using difference_type = typename raw_hash_set::difference_type;
+
+ const_iterator() {}
+ // Implicit construction from iterator.
+ const_iterator(iterator i) : inner_(std::move(i)) {}
+
+ reference operator*() const { return *inner_; }
+ pointer operator->() const { return inner_.operator->(); }
+
+ const_iterator& operator++() {
+ ++inner_;
+ return *this;
+ }
+ const_iterator operator++(int) { return inner_++; }
+
+ friend bool operator==(const const_iterator& a, const const_iterator& b) {
+ return a.inner_ == b.inner_;
+ }
+ friend bool operator!=(const const_iterator& a, const const_iterator& b) {
+ return !(a == b);
+ }
+
+ private:
+ const_iterator(const ctrl_t* ctrl, const slot_type* slot)
+ : inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot)) {}
+
+ iterator inner_;
+ };
+
+ using node_type = container_internal::node_handle<Policy, Alloc>;
+
+ raw_hash_set() noexcept(
+ std::is_nothrow_default_constructible<hasher>::value&&
+ std::is_nothrow_default_constructible<key_equal>::value&&
+ std::is_nothrow_default_constructible<allocator_type>::value) {}
+
+ explicit raw_hash_set(size_t bucket_count, const hasher& hash = hasher(),
+ const key_equal& eq = key_equal(),
+ const allocator_type& alloc = allocator_type())
+ : ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) {
+ if (bucket_count) {
+ capacity_ = NormalizeCapacity(bucket_count);
+ growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor);
+ initialize_slots();
+ }
+ }
+
+ raw_hash_set(size_t bucket_count, const hasher& hash,
+ const allocator_type& alloc)
+ : raw_hash_set(bucket_count, hash, key_equal(), alloc) {}
+
+ raw_hash_set(size_t bucket_count, const allocator_type& alloc)
+ : raw_hash_set(bucket_count, hasher(), key_equal(), alloc) {}
+
+ explicit raw_hash_set(const allocator_type& alloc)
+ : raw_hash_set(0, hasher(), key_equal(), alloc) {}
+
+ template <class InputIter>
+ raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0,
+ const hasher& hash = hasher(), const key_equal& eq = key_equal(),
+ const allocator_type& alloc = allocator_type())
+ : raw_hash_set(bucket_count, hash, eq, alloc) {
+ insert(first, last);
+ }
+
+ template <class InputIter>
+ raw_hash_set(InputIter first, InputIter last, size_t bucket_count,
+ const hasher& hash, const allocator_type& alloc)
+ : raw_hash_set(first, last, bucket_count, hash, key_equal(), alloc) {}
+
+ template <class InputIter>
+ raw_hash_set(InputIter first, InputIter last, size_t bucket_count,
+ const allocator_type& alloc)
+ : raw_hash_set(first, last, bucket_count, hasher(), key_equal(), alloc) {}
+
+ template <class InputIter>
+ raw_hash_set(InputIter first, InputIter last, const allocator_type& alloc)
+ : raw_hash_set(first, last, 0, hasher(), key_equal(), alloc) {}
+
+ // Instead of accepting std::initializer_list<value_type> as the first
+ // argument like std::unordered_set<value_type> does, we have two overloads
+ // that accept std::initializer_list<T> and std::initializer_list<init_type>.
+ // This is advantageous for performance.
+ //
+ // // Turns {"abc", "def"} into std::initializer_list<std::string>, then copies
+ // // the strings into the set.
+ // std::unordered_set<std::string> s = {"abc", "def"};
+ //
+ // // Turns {"abc", "def"} into std::initializer_list<const char*>, then
+ // // copies the strings into the set.
+ // absl::flat_hash_set<std::string> s = {"abc", "def"};
+ //
+ // The same trick is used in insert().
+ //
+ // The enabler is necessary to prevent this constructor from triggering where
+ // the copy constructor is meant to be called.
+ //
+ // absl::flat_hash_set<int> a, b{a};
+ //
+ // RequiresNotInit<T> is a workaround for gcc prior to 7.1.
+ template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
+ raw_hash_set(std::initializer_list<T> init, size_t bucket_count = 0,
+ const hasher& hash = hasher(), const key_equal& eq = key_equal(),
+ const allocator_type& alloc = allocator_type())
+ : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {}
+
+ raw_hash_set(std::initializer_list<init_type> init, size_t bucket_count = 0,
+ const hasher& hash = hasher(), const key_equal& eq = key_equal(),
+ const allocator_type& alloc = allocator_type())
+ : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {}
+
+ template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
+ raw_hash_set(std::initializer_list<T> init, size_t bucket_count,
+ const hasher& hash, const allocator_type& alloc)
+ : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {}
+
+ raw_hash_set(std::initializer_list<init_type> init, size_t bucket_count,
+ const hasher& hash, const allocator_type& alloc)
+ : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {}
+
+ template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
+ raw_hash_set(std::initializer_list<T> init, size_t bucket_count,
+ const allocator_type& alloc)
+ : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {}
+
+ raw_hash_set(std::initializer_list<init_type> init, size_t bucket_count,
+ const allocator_type& alloc)
+ : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {}
+
+ template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
+ raw_hash_set(std::initializer_list<T> init, const allocator_type& alloc)
+ : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {}
+
+ raw_hash_set(std::initializer_list<init_type> init,
+ const allocator_type& alloc)
+ : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {}
+
+ raw_hash_set(const raw_hash_set& that)
+ : raw_hash_set(that, AllocTraits::select_on_container_copy_construction(
+ that.alloc_ref())) {}
+
+ raw_hash_set(const raw_hash_set& that, const allocator_type& a)
+ : raw_hash_set(0, that.hash_ref(), that.eq_ref(), a) {
+ reserve(that.size());
+ // Because the table is guaranteed to be empty, we can do something faster
+ // than a full `insert`.
+ for (const auto& v : that) {
+ const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v);
+ const size_t i = find_first_non_full(hash);
+ set_ctrl(i, H2(hash));
+ emplace_at(i, v);
+ }
+ size_ = that.size();
+ growth_left() -= that.size();
+ }
+
+ raw_hash_set(raw_hash_set&& that) noexcept(
+ std::is_nothrow_copy_constructible<hasher>::value&&
+ std::is_nothrow_copy_constructible<key_equal>::value&&
+ std::is_nothrow_copy_constructible<allocator_type>::value)
+ : ctrl_(absl::exchange(that.ctrl_, EmptyGroup())),
+ slots_(absl::exchange(that.slots_, nullptr)),
+ size_(absl::exchange(that.size_, 0)),
+ capacity_(absl::exchange(that.capacity_, 0)),
+ // Hash, equality and allocator are copied instead of moved because
+ // `that` must be left valid. If Hash is std::function<Key>, moving it
+ // would create a nullptr functor that cannot be called.
+ settings_(that.settings_) {
+ // growth_left was copied above, reset the one from `that`.
+ that.growth_left() = 0;
+ }
+
+ raw_hash_set(raw_hash_set&& that, const allocator_type& a)
+ : ctrl_(EmptyGroup()),
+ slots_(nullptr),
+ size_(0),
+ capacity_(0),
+ settings_(0, that.hash_ref(), that.eq_ref(), a) {
+ if (a == that.alloc_ref()) {
+ std::swap(ctrl_, that.ctrl_);
+ std::swap(slots_, that.slots_);
+ std::swap(size_, that.size_);
+ std::swap(capacity_, that.capacity_);
+ std::swap(growth_left(), that.growth_left());
+ } else {
+ reserve(that.size());
+ // Note: this will copy elements of dense_set and unordered_set instead of
+ // moving them. This can be fixed if it ever becomes an issue.
+ for (auto& elem : that) insert(std::move(elem));
+ }
+ }
+
+ raw_hash_set& operator=(const raw_hash_set& that) {
+ raw_hash_set tmp(that,
+ AllocTraits::propagate_on_container_copy_assignment::value
+ ? that.alloc_ref()
+ : alloc_ref());
+ swap(tmp);
+ return *this;
+ }
+
+ raw_hash_set& operator=(raw_hash_set&& that) noexcept(
+ absl::allocator_traits<allocator_type>::is_always_equal::value&&
+ std::is_nothrow_move_assignable<hasher>::value&&
+ std::is_nothrow_move_assignable<key_equal>::value) {
+ // TODO(sbenza): We should only use the operations from the noexcept clause
+ // to make sure we actually adhere to that contract.
+ return move_assign(
+ std::move(that),
+ typename AllocTraits::propagate_on_container_move_assignment());
+ }
+
+ ~raw_hash_set() { destroy_slots(); }
+
+ iterator begin() {
+ auto it = iterator_at(0);
+ it.skip_empty_or_deleted();
+ return it;
+ }
+ iterator end() { return {ctrl_ + capacity_}; }
+
+ const_iterator begin() const {
+ return const_cast<raw_hash_set*>(this)->begin();
+ }
+ const_iterator end() const { return const_cast<raw_hash_set*>(this)->end(); }
+ const_iterator cbegin() const { return begin(); }
+ const_iterator cend() const { return end(); }
+
+ bool empty() const { return !size(); }
+ size_t size() const { return size_; }
+ size_t capacity() const { return capacity_; }
+ size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
+
+ void clear() {
+ // Iterating over this container is O(bucket_count()). When bucket_count()
+ // is much greater than size(), iteration becomes prohibitively expensive.
+ // For clear() it is more important to reuse the allocated array when the
+ // container is small because allocation takes comparatively long time
+ // compared to destruction of the elements of the container. So we pick the
+ // largest bucket_count() threshold for which iteration is still fast and
+ // past that we simply deallocate the array.
+ if (capacity_ > 127) {
+ destroy_slots();
+ } else if (capacity_) {
+ for (size_t i = 0; i != capacity_; ++i) {
+ if (IsFull(ctrl_[i])) {
+ PolicyTraits::destroy(&alloc_ref(), slots_ + i);
+ }
+ }
+ size_ = 0;
+ reset_ctrl();
+ growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor);
+ }
+ assert(empty());
+ }
+
+ // This overload kicks in when the argument is an rvalue of insertable and
+ // decomposable type other than init_type.
+ //
+ // flat_hash_map<std::string, int> m;
+ // m.insert(std::make_pair("abc", 42));
+ template <class T, RequiresInsertable<T> = 0,
+ typename std::enable_if<IsDecomposable<T>::value, int>::type = 0,
+ T* = nullptr>
+ std::pair<iterator, bool> insert(T&& value) {
+ return emplace(std::forward<T>(value));
+ }
+
+ // This overload kicks in when the argument is a bitfield or an lvalue of
+ // insertable and decomposable type.
+ //
+ // union { int n : 1; };
+ // flat_hash_set<int> s;
+ // s.insert(n);
+ //
+ // flat_hash_set<std::string> s;
+ // const char* p = "hello";
+ // s.insert(p);
+ //
+ // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
+ // RequiresInsertable<T> with RequiresInsertable<const T&>.
+ // We are hitting this bug: https://godbolt.org/g/1Vht4f.
+ template <
+ class T, RequiresInsertable<T> = 0,
+ typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
+ std::pair<iterator, bool> insert(const T& value) {
+ return emplace(value);
+ }
+
+ // This overload kicks in when the argument is an rvalue of init_type. Its
+ // purpose is to handle brace-init-list arguments.
+ //
+ // flat_hash_set<std::string, int> s;
+ // s.insert({"abc", 42});
+ std::pair<iterator, bool> insert(init_type&& value) {
+ return emplace(std::move(value));
+ }
+
+ template <class T, RequiresInsertable<T> = 0,
+ typename std::enable_if<IsDecomposable<T>::value, int>::type = 0,
+ T* = nullptr>
+ iterator insert(const_iterator, T&& value) {
+ return insert(std::forward<T>(value)).first;
+ }
+
+ // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
+ // RequiresInsertable<T> with RequiresInsertable<const T&>.
+ // We are hitting this bug: https://godbolt.org/g/1Vht4f.
+ template <
+ class T, RequiresInsertable<T> = 0,
+ typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
+ iterator insert(const_iterator, const T& value) {
+ return insert(value).first;
+ }
+
+ iterator insert(const_iterator, init_type&& value) {
+ return insert(std::move(value)).first;
+ }
+
+ template <class InputIt>
+ void insert(InputIt first, InputIt last) {
+ for (; first != last; ++first) insert(*first);
+ }
+
+ template <class T, RequiresNotInit<T> = 0, RequiresInsertable<const T&> = 0>
+ void insert(std::initializer_list<T> ilist) {
+ insert(ilist.begin(), ilist.end());
+ }
+
+ void insert(std::initializer_list<init_type> ilist) {
+ insert(ilist.begin(), ilist.end());
+ }
+
+ insert_return_type<iterator, node_type> insert(node_type&& node) {
+ if (!node) return {end(), false, node_type()};
+ const auto& elem = PolicyTraits::element(node.slot());
+ auto res = PolicyTraits::apply(
+ InsertSlot<false>{*this, std::move(*node.slot())}, elem);
+ if (res.second) {
+ node.reset();
+ return {res.first, true, node_type()};
+ } else {
+ return {res.first, false, std::move(node)};
+ }
+ }
+
+ iterator insert(const_iterator, node_type&& node) {
+ return insert(std::move(node)).first;
+ }
+
+ // This overload kicks in if we can deduce the key from args. This enables us
+ // to avoid constructing value_type if an entry with the same key already
+ // exists.
+ //
+ // For example:
+ //
+ // flat_hash_map<std::string, std::string> m = {{"abc", "def"}};
+ // // Creates no std::string copies and makes no heap allocations.
+ // m.emplace("abc", "xyz");
+ template <class... Args, typename std::enable_if<
+ IsDecomposable<Args...>::value, int>::type = 0>
+ std::pair<iterator, bool> emplace(Args&&... args) {
+ return PolicyTraits::apply(EmplaceDecomposable{*this},
+ std::forward<Args>(args)...);
+ }
+
+ // This overload kicks in if we cannot deduce the key from args. It constructs
+ // value_type unconditionally and then either moves it into the table or
+ // destroys.
+ template <class... Args, typename std::enable_if<
+ !IsDecomposable<Args...>::value, int>::type = 0>
+ std::pair<iterator, bool> emplace(Args&&... args) {
+ typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type
+ raw;
+ slot_type* slot = reinterpret_cast<slot_type*>(&raw);
+
+ PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
+ const auto& elem = PolicyTraits::element(slot);
+ return PolicyTraits::apply(InsertSlot<true>{*this, std::move(*slot)}, elem);
+ }
+
+ template <class... Args>
+ iterator emplace_hint(const_iterator, Args&&... args) {
+ return emplace(std::forward<Args>(args)...).first;
+ }
+
+ // Extension API: support for lazy emplace.
+ //
+ // Looks up key in the table. If found, returns the iterator to the element.
+ // Otherwise calls f with one argument of type raw_hash_set::constructor. f
+ // MUST call raw_hash_set::constructor with arguments as if a
+ // raw_hash_set::value_type is constructed, otherwise the behavior is
+ // undefined.
+ //
+ // For example:
+ //
+ // std::unordered_set<ArenaString> s;
+ // // Makes ArenaStr even if "abc" is in the map.
+ // s.insert(ArenaString(&arena, "abc"));
+ //
+ // flat_hash_set<ArenaStr> s;
+ // // Makes ArenaStr only if "abc" is not in the map.
+ // s.lazy_emplace("abc", [&](const constructor& ctor) {
+ // ctor(&arena, "abc");
+ // });
+ //
+ // WARNING: This API is currently experimental. If there is a way to implement
+ // the same thing with the rest of the API, prefer that.
+ class constructor {
+ friend class raw_hash_set;
+
+ public:
+ template <class... Args>
+ void operator()(Args&&... args) const {
+ assert(*slot_);
+ PolicyTraits::construct(alloc_, *slot_, std::forward<Args>(args)...);
+ *slot_ = nullptr;
+ }
+
+ private:
+ constructor(allocator_type* a, slot_type** slot) : alloc_(a), slot_(slot) {}
+
+ allocator_type* alloc_;
+ slot_type** slot_;
+ };
+
+ template <class K = key_type, class F>
+ iterator lazy_emplace(const key_arg<K>& key, F&& f) {
+ auto res = find_or_prepare_insert(key);
+ if (res.second) {
+ slot_type* slot = slots_ + res.first;
+ std::forward<F>(f)(constructor(&alloc_ref(), &slot));
+ assert(!slot);
+ }
+ return iterator_at(res.first);
+ }
+
+ // Extension API: support for heterogeneous keys.
+ //
+ // std::unordered_set<std::string> s;
+ // // Turns "abc" into std::string.
+ // s.erase("abc");
+ //
+ // flat_hash_set<std::string> s;
+ // // Uses "abc" directly without copying it into std::string.
+ // s.erase("abc");
+ template <class K = key_type>
+ size_type erase(const key_arg<K>& key) {
+ auto it = find(key);
+ if (it == end()) return 0;
+ erase(it);
+ return 1;
+ }
+
+ // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`,
+ // this method returns void to reduce algorithmic complexity to O(1). In
+ // order to erase while iterating across a map, use the following idiom (which
+ // also works for standard containers):
+ //
+ // for (auto it = m.begin(), end = m.end(); it != end;) {
+ // if (<pred>) {
+ // m.erase(it++);
+ // } else {
+ // ++it;
+ // }
+ // }
+ void erase(const_iterator cit) { erase(cit.inner_); }
+
+ // This overload is necessary because otherwise erase<K>(const K&) would be
+ // a better match if non-const iterator is passed as an argument.
+ void erase(iterator it) {
+ assert(it != end());
+ PolicyTraits::destroy(&alloc_ref(), it.slot_);
+ erase_meta_only(it);
+ }
+
+ iterator erase(const_iterator first, const_iterator last) {
+ while (first != last) {
+ erase(first++);
+ }
+ return last.inner_;
+ }
+
+ // Moves elements from `src` into `this`.
+ // If the element already exists in `this`, it is left unmodified in `src`.
+ template <typename H, typename E>
+ void merge(raw_hash_set<Policy, H, E, Alloc>& src) { // NOLINT
+ assert(this != &src);
+ for (auto it = src.begin(), e = src.end(); it != e; ++it) {
+ if (PolicyTraits::apply(InsertSlot<false>{*this, std::move(*it.slot_)},
+ PolicyTraits::element(it.slot_))
+ .second) {
+ src.erase_meta_only(it);
+ }
+ }
+ }
+
+ template <typename H, typename E>
+ void merge(raw_hash_set<Policy, H, E, Alloc>&& src) {
+ merge(src);
+ }
+
+ node_type extract(const_iterator position) {
+ node_type node(alloc_ref(), position.inner_.slot_);
+ erase_meta_only(position);
+ return node;
+ }
+
+ template <
+ class K = key_type,
+ typename std::enable_if<!std::is_same<K, iterator>::value, int>::type = 0>
+ node_type extract(const key_arg<K>& key) {
+ auto it = find(key);
+ return it == end() ? node_type() : extract(const_iterator{it});
+ }
+
+ void swap(raw_hash_set& that) noexcept(
+ IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
+ (!AllocTraits::propagate_on_container_swap::value ||
+ IsNoThrowSwappable<allocator_type>())) {
+ using std::swap;
+ swap(ctrl_, that.ctrl_);
+ swap(slots_, that.slots_);
+ swap(size_, that.size_);
+ swap(capacity_, that.capacity_);
+ swap(growth_left(), that.growth_left());
+ swap(hash_ref(), that.hash_ref());
+ swap(eq_ref(), that.eq_ref());
+ if (AllocTraits::propagate_on_container_swap::value) {
+ swap(alloc_ref(), that.alloc_ref());
+ } else {
+ // If the allocators do not compare equal it is officially undefined
+ // behavior. We choose to do nothing.
+ }
+ }
+
+ void rehash(size_t n) {
+ if (n == 0 && capacity_ == 0) return;
+ if (n == 0 && size_ == 0) return destroy_slots();
+ auto m = NormalizeCapacity(std::max(n, NumSlotsFast(size())));
+ // n == 0 unconditionally rehashes as per the standard.
+ if (n == 0 || m > capacity_) {
+ resize(m);
+ }
+ }
+
+ void reserve(size_t n) {
+ rehash(NumSlotsFast(n));
+ }
+
+ // Extension API: support for heterogeneous keys.
+ //
+ // std::unordered_set<std::string> s;
+ // // Turns "abc" into std::string.
+ // s.count("abc");
+ //
+ // ch_set<std::string> s;
+ // // Uses "abc" directly without copying it into std::string.
+ // s.count("abc");
+ template <class K = key_type>
+ size_t count(const key_arg<K>& key) const {
+ return find(key) == end() ? 0 : 1;
+ }
+
+ // Issues CPU prefetch instructions for the memory needed to find or insert
+ // a key. Like all lookup functions, this support heterogeneous keys.
+ //
+ // NOTE: This is a very low level operation and should not be used without
+ // specific benchmarks indicating its importance.
+ template <class K = key_type>
+ void prefetch(const key_arg<K>& key) const {
+ (void)key;
+#if defined(__GNUC__)
+ auto seq = probe(hash_ref()(key));
+ __builtin_prefetch(static_cast<const void*>(ctrl_ + seq.offset()));
+ __builtin_prefetch(static_cast<const void*>(slots_ + seq.offset()));
+#endif // __GNUC__
+ }
+
+ // The API of find() has two extensions.
+ //
+ // 1. The hash can be passed by the user. It must be equal to the hash of the
+ // key.
+ //
+ // 2. The type of the key argument doesn't have to be key_type. This is so
+ // called heterogeneous key support.
+ template <class K = key_type>
+ iterator find(const key_arg<K>& key, size_t hash) {
+ auto seq = probe(hash);
+ while (true) {
+ Group g{ctrl_ + seq.offset()};
+ for (int i : g.Match(H2(hash))) {
+ if (ABSL_PREDICT_TRUE(PolicyTraits::apply(
+ EqualElement<K>{key, eq_ref()},
+ PolicyTraits::element(slots_ + seq.offset(i)))))
+ return iterator_at(seq.offset(i));
+ }
+ if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end();
+ seq.next();
+ }
+ }
+ template <class K = key_type>
+ iterator find(const key_arg<K>& key) {
+ return find(key, hash_ref()(key));
+ }
+
+ template <class K = key_type>
+ const_iterator find(const key_arg<K>& key, size_t hash) const {
+ return const_cast<raw_hash_set*>(this)->find(key, hash);
+ }
+ template <class K = key_type>
+ const_iterator find(const key_arg<K>& key) const {
+ return find(key, hash_ref()(key));
+ }
+
+ template <class K = key_type>
+ bool contains(const key_arg<K>& key) const {
+ return find(key) != end();
+ }
+
+ template <class K = key_type>
+ std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
+ auto it = find(key);
+ if (it != end()) return {it, std::next(it)};
+ return {it, it};
+ }
+ template <class K = key_type>
+ std::pair<const_iterator, const_iterator> equal_range(
+ const key_arg<K>& key) const {
+ auto it = find(key);
+ if (it != end()) return {it, std::next(it)};
+ return {it, it};
+ }
+
+ size_t bucket_count() const { return capacity_; }
+ float load_factor() const {
+ return capacity_ ? static_cast<double>(size()) / capacity_ : 0.0;
+ }
+ float max_load_factor() const { return 1.0f; }
+ void max_load_factor(float) {
+ // Does nothing.
+ }
+
+ hasher hash_function() const { return hash_ref(); }
+ key_equal key_eq() const { return eq_ref(); }
+ allocator_type get_allocator() const { return alloc_ref(); }
+
+ friend bool operator==(const raw_hash_set& a, const raw_hash_set& b) {
+ if (a.size() != b.size()) return false;
+ const raw_hash_set* outer = &a;
+ const raw_hash_set* inner = &b;
+ if (outer->capacity() > inner->capacity()) std::swap(outer, inner);
+ for (const value_type& elem : *outer)
+ if (!inner->has_element(elem)) return false;
+ return true;
+ }
+
+ friend bool operator!=(const raw_hash_set& a, const raw_hash_set& b) {
+ return !(a == b);
+ }
+
+ friend void swap(raw_hash_set& a,
+ raw_hash_set& b) noexcept(noexcept(a.swap(b))) {
+ a.swap(b);
+ }
+
+ private:
+ template <class Container, typename Enabler>
+ friend struct absl::container_internal::hashtable_debug_internal::
+ HashtableDebugAccess;
+
+ struct FindElement {
+ template <class K, class... Args>
+ const_iterator operator()(const K& key, Args&&...) const {
+ return s.find(key);
+ }
+ const raw_hash_set& s;
+ };
+
+ struct HashElement {
+ template <class K, class... Args>
+ size_t operator()(const K& key, Args&&...) const {
+ return h(key);
+ }
+ const hasher& h;
+ };
+
+ template <class K1>
+ struct EqualElement {
+ template <class K2, class... Args>
+ bool operator()(const K2& lhs, Args&&...) const {
+ return eq(lhs, rhs);
+ }
+ const K1& rhs;
+ const key_equal& eq;
+ };
+
+ struct EmplaceDecomposable {
+ template <class K, class... Args>
+ std::pair<iterator, bool> operator()(const K& key, Args&&... args) const {
+ auto res = s.find_or_prepare_insert(key);
+ if (res.second) {
+ s.emplace_at(res.first, std::forward<Args>(args)...);
+ }
+ return {s.iterator_at(res.first), res.second};
+ }
+ raw_hash_set& s;
+ };
+
+ template <bool do_destroy>
+ struct InsertSlot {
+ template <class K, class... Args>
+ std::pair<iterator, bool> operator()(const K& key, Args&&...) && {
+ auto res = s.find_or_prepare_insert(key);
+ if (res.second) {
+ PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot);
+ } else if (do_destroy) {
+ PolicyTraits::destroy(&s.alloc_ref(), &slot);
+ }
+ return {s.iterator_at(res.first), res.second};
+ }
+ raw_hash_set& s;
+ // Constructed slot. Either moved into place or destroyed.
+ slot_type&& slot;
+ };
+
+ // Computes std::ceil(n / kMaxLoadFactor). Faster than calling std::ceil.
+ static inline size_t NumSlotsFast(size_t n) {
+ return static_cast<size_t>(
+ (n * kMaxLoadFactorDenominator + (kMaxLoadFactorNumerator - 1)) /
+ kMaxLoadFactorNumerator);
+ }
+
+ // "erases" the object from the container, except that it doesn't actually
+ // destroy the object. It only updates all the metadata of the class.
+ // This can be used in conjunction with Policy::transfer to move the object to
+ // another place.
+ void erase_meta_only(const_iterator it) {
+ assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator");
+ --size_;
+ const size_t index = it.inner_.ctrl_ - ctrl_;
+ const size_t index_before = (index - Group::kWidth) & capacity_;
+ const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty();
+ const auto empty_before = Group(ctrl_ + index_before).MatchEmpty();
+
+ // We count how many consecutive non empties we have to the right and to the
+ // left of `it`. If the sum is >= kWidth then there is at least one probe
+ // window that might have seen a full group.
+ bool was_never_full =
+ empty_before && empty_after &&
+ static_cast<size_t>(empty_after.TrailingZeros() +
+ empty_before.LeadingZeros()) < Group::kWidth;
+
+ set_ctrl(index, was_never_full ? kEmpty : kDeleted);
+ growth_left() += was_never_full;
+ }
+
+ void initialize_slots() {
+ assert(capacity_);
+ auto layout = MakeLayout(capacity_);
+ char* mem = static_cast<char*>(
+ Allocate<Layout::Alignment()>(&alloc_ref(), layout.AllocSize()));
+ ctrl_ = reinterpret_cast<ctrl_t*>(layout.template Pointer<0>(mem));
+ slots_ = layout.template Pointer<1>(mem);
+ reset_ctrl();
+ growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor) - size_;
+ }
+
+ void destroy_slots() {
+ if (!capacity_) return;
+ for (size_t i = 0; i != capacity_; ++i) {
+ if (IsFull(ctrl_[i])) {
+ PolicyTraits::destroy(&alloc_ref(), slots_ + i);
+ }
+ }
+ auto layout = MakeLayout(capacity_);
+ // Unpoison before returning the memory to the allocator.
+ SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
+ Deallocate<Layout::Alignment()>(&alloc_ref(), ctrl_, layout.AllocSize());
+ ctrl_ = EmptyGroup();
+ slots_ = nullptr;
+ size_ = 0;
+ capacity_ = 0;
+ growth_left() = 0;
+ }
+
+ void resize(size_t new_capacity) {
+ assert(IsValidCapacity(new_capacity));
+ auto* old_ctrl = ctrl_;
+ auto* old_slots = slots_;
+ const size_t old_capacity = capacity_;
+ capacity_ = new_capacity;
+ initialize_slots();
+
+ for (size_t i = 0; i != old_capacity; ++i) {
+ if (IsFull(old_ctrl[i])) {
+ size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
+ PolicyTraits::element(old_slots + i));
+ size_t new_i = find_first_non_full(hash);
+ set_ctrl(new_i, H2(hash));
+ PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i);
+ }
+ }
+ if (old_capacity) {
+ SanitizerUnpoisonMemoryRegion(old_slots,
+ sizeof(slot_type) * old_capacity);
+ auto layout = MakeLayout(old_capacity);
+ Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl,
+ layout.AllocSize());
+ }
+ }
+
+ void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE {
+ assert(IsValidCapacity(capacity_));
+ // Algorithm:
+ // - mark all DELETED slots as EMPTY
+ // - mark all FULL slots as DELETED
+ // - for each slot marked as DELETED
+ // hash = Hash(element)
+ // target = find_first_non_full(hash)
+ // if target is in the same group
+ // mark slot as FULL
+ // else if target is EMPTY
+ // transfer element to target
+ // mark slot as EMPTY
+ // mark target as FULL
+ // else if target is DELETED
+ // swap current element with target element
+ // mark target as FULL
+ // repeat procedure for current slot with moved from element (target)
+ ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_);
+ typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type
+ raw;
+ slot_type* slot = reinterpret_cast<slot_type*>(&raw);
+ for (size_t i = 0; i != capacity_; ++i) {
+ if (!IsDeleted(ctrl_[i])) continue;
+ size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
+ PolicyTraits::element(slots_ + i));
+ size_t new_i = find_first_non_full(hash);
+
+ // Verify if the old and new i fall within the same group wrt the hash.
+ // If they do, we don't need to move the object as it falls already in the
+ // best probe we can.
+ const auto probe_index = [&](size_t pos) {
+ return ((pos - probe(hash).offset()) & capacity_) / Group::kWidth;
+ };
+
+ // Element doesn't move.
+ if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
+ set_ctrl(i, H2(hash));
+ continue;
+ }
+ if (IsEmpty(ctrl_[new_i])) {
+ // Transfer element to the empty spot.
+ // set_ctrl poisons/unpoisons the slots so we have to call it at the
+ // right time.
+ set_ctrl(new_i, H2(hash));
+ PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i);
+ set_ctrl(i, kEmpty);
+ } else {
+ assert(IsDeleted(ctrl_[new_i]));
+ set_ctrl(new_i, H2(hash));
+ // Until we are done rehashing, DELETED marks previously FULL slots.
+ // Swap i and new_i elements.
+ PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i);
+ PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i);
+ PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot);
+ --i; // repeat
+ }
+ }
+ growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor) - size_;
+ }
+
+ void rehash_and_grow_if_necessary() {
+ if (capacity_ == 0) {
+ resize(Group::kWidth - 1);
+ } else if (size() <= kMaxLoadFactor / 2 * capacity_) {
+ // Squash DELETED without growing if there is enough capacity.
+ drop_deletes_without_resize();
+ } else {
+ // Otherwise grow the container.
+ resize(capacity_ * 2 + 1);
+ }
+ }
+
+ bool has_element(const value_type& elem) const {
+ size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem);
+ auto seq = probe(hash);
+ while (true) {
+ Group g{ctrl_ + seq.offset()};
+ for (int i : g.Match(H2(hash))) {
+ if (ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) ==
+ elem))
+ return true;
+ }
+ if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false;
+ seq.next();
+ assert(seq.index() < capacity_ && "full table!");
+ }
+ return false;
+ }
+
+ // Probes the raw_hash_set with the probe sequence for hash and returns the
+ // pointer to the first empty or deleted slot.
+ // NOTE: this function must work with tables having both kEmpty and kDelete
+ // in one group. Such tables appears during drop_deletes_without_resize.
+ //
+ // This function is very useful when insertions happen and:
+ // - the input is already a set
+ // - there are enough slots
+ // - the element with the hash is not in the table
+ size_t find_first_non_full(size_t hash) {
+ auto seq = probe(hash);
+ while (true) {
+ Group g{ctrl_ + seq.offset()};
+ auto mask = g.MatchEmptyOrDeleted();
+ if (mask) {
+#if !defined(NDEBUG)
+ // We want to force small tables to have random entries too, so
+ // in debug build we will randomly insert in either the front or back of
+ // the group.
+ // TODO(kfm,sbenza): revisit after we do unconditional mixing
+ if (ShouldInsertBackwards(hash, ctrl_))
+ return seq.offset(mask.HighestBitSet());
+ else
+ return seq.offset(mask.LowestBitSet());
+#else
+ return seq.offset(mask.LowestBitSet());
+#endif
+ }
+ assert(seq.index() < capacity_ && "full table!");
+ seq.next();
+ }
+ }
+
+ // TODO(alkis): Optimize this assuming *this and that don't overlap.
+ raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) {
+ raw_hash_set tmp(std::move(that));
+ swap(tmp);
+ return *this;
+ }
+ raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) {
+ raw_hash_set tmp(std::move(that), alloc_ref());
+ swap(tmp);
+ return *this;
+ }
+
+ protected:
+ template <class K>
+ std::pair<size_t, bool> find_or_prepare_insert(const K& key) {
+ auto hash = hash_ref()(key);
+ auto seq = probe(hash);
+ while (true) {
+ Group g{ctrl_ + seq.offset()};
+ for (int i : g.Match(H2(hash))) {
+ if (ABSL_PREDICT_TRUE(PolicyTraits::apply(
+ EqualElement<K>{key, eq_ref()},
+ PolicyTraits::element(slots_ + seq.offset(i)))))
+ return {seq.offset(i), false};
+ }
+ if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break;
+ seq.next();
+ }
+ return {prepare_insert(hash), true};
+ }
+
+ size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE {
+ size_t target = find_first_non_full(hash);
+ if (ABSL_PREDICT_FALSE(growth_left() == 0 && !IsDeleted(ctrl_[target]))) {
+ rehash_and_grow_if_necessary();
+ target = find_first_non_full(hash);
+ }
+ ++size_;
+ growth_left() -= IsEmpty(ctrl_[target]);
+ set_ctrl(target, H2(hash));
+ return target;
+ }
+
+ // Constructs the value in the space pointed by the iterator. This only works
+ // after an unsuccessful find_or_prepare_insert() and before any other
+ // modifications happen in the raw_hash_set.
+ //
+ // PRECONDITION: i is an index returned from find_or_prepare_insert(k), where
+ // k is the key decomposed from `forward<Args>(args)...`, and the bool
+ // returned by find_or_prepare_insert(k) was true.
+ // POSTCONDITION: *m.iterator_at(i) == value_type(forward<Args>(args)...).
+ template <class... Args>
+ void emplace_at(size_t i, Args&&... args) {
+ PolicyTraits::construct(&alloc_ref(), slots_ + i,
+ std::forward<Args>(args)...);
+
+ assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) ==
+ iterator_at(i) &&
+ "constructed value does not match the lookup key");
+ }
+
+ iterator iterator_at(size_t i) { return {ctrl_ + i, slots_ + i}; }
+ const_iterator iterator_at(size_t i) const { return {ctrl_ + i, slots_ + i}; }
+
+ private:
+ friend struct RawHashSetTestOnlyAccess;
+
+ probe_seq<Group::kWidth> probe(size_t hash) const {
+ return probe_seq<Group::kWidth>(H1(hash, ctrl_), capacity_);
+ }
+
+ // Reset all ctrl bytes back to kEmpty, except the sentinel.
+ void reset_ctrl() {
+ std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth);
+ ctrl_[capacity_] = kSentinel;
+ SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
+ }
+
+ // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at
+ // the end too.
+ void set_ctrl(size_t i, ctrl_t h) {
+ assert(i < capacity_);
+
+ if (IsFull(h)) {
+ SanitizerUnpoisonObject(slots_ + i);
+ } else {
+ SanitizerPoisonObject(slots_ + i);
+ }
+
+ ctrl_[i] = h;
+ ctrl_[((i - Group::kWidth) & capacity_) + Group::kWidth] = h;
+ }
+
+ size_t& growth_left() { return settings_.template get<0>(); }
+
+ hasher& hash_ref() { return settings_.template get<1>(); }
+ const hasher& hash_ref() const { return settings_.template get<1>(); }
+ key_equal& eq_ref() { return settings_.template get<2>(); }
+ const key_equal& eq_ref() const { return settings_.template get<2>(); }
+ allocator_type& alloc_ref() { return settings_.template get<3>(); }
+ const allocator_type& alloc_ref() const {
+ return settings_.template get<3>();
+ }
+
+ // On average each group has 2 empty slot (for the vectorized case).
+ static constexpr int64_t kMaxLoadFactorNumerator = 14;
+ static constexpr int64_t kMaxLoadFactorDenominator = 16;
+ static constexpr float kMaxLoadFactor =
+ 1.0 * kMaxLoadFactorNumerator / kMaxLoadFactorDenominator;
+
+ // TODO(alkis): Investigate removing some of these fields:
+ // - ctrl/slots can be derived from each other
+ // - size can be moved into the slot array
+ ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t]
+ slot_type* slots_ = nullptr; // [capacity * slot_type]
+ size_t size_ = 0; // number of full slots
+ size_t capacity_ = 0; // total number of slots
+ absl::container_internal::CompressedTuple<size_t /* growth_left */, hasher,
+ key_equal, allocator_type>
+ settings_{0, hasher{}, key_equal{}, allocator_type{}};
+};
+
+namespace hashtable_debug_internal {
+template <typename Set>
+struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
+ using Traits = typename Set::PolicyTraits;
+ using Slot = typename Traits::slot_type;
+
+ static size_t GetNumProbes(const Set& set,
+ const typename Set::key_type& key) {
+ size_t num_probes = 0;
+ size_t hash = set.hash_ref()(key);
+ auto seq = set.probe(hash);
+ while (true) {
+ container_internal::Group g{set.ctrl_ + seq.offset()};
+ for (int i : g.Match(container_internal::H2(hash))) {
+ if (Traits::apply(
+ typename Set::template EqualElement<typename Set::key_type>{
+ key, set.eq_ref()},
+ Traits::element(set.slots_ + seq.offset(i))))
+ return num_probes;
+ ++num_probes;
+ }
+ if (g.MatchEmpty()) return num_probes;
+ seq.next();
+ ++num_probes;
+ }
+ }
+
+ static size_t AllocatedByteSize(const Set& c) {
+ size_t capacity = c.capacity_;
+ if (capacity == 0) return 0;
+ auto layout = Set::MakeLayout(capacity);
+ size_t m = layout.AllocSize();
+
+ size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
+ if (per_slot != ~size_t{}) {
+ m += per_slot * c.size();
+ } else {
+ for (size_t i = 0; i != capacity; ++i) {
+ if (container_internal::IsFull(c.ctrl_[i])) {
+ m += Traits::space_used(c.slots_ + i);
+ }
+ }
+ }
+ return m;
+ }
+
+ static size_t LowerBoundAllocatedByteSize(size_t size) {
+ size_t capacity = container_internal::NormalizeCapacity(
+ std::ceil(size / Set::kMaxLoadFactor));
+ if (capacity == 0) return 0;
+ auto layout = Set::MakeLayout(capacity);
+ size_t m = layout.AllocSize();
+ size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
+ if (per_slot != ~size_t{}) {
+ m += per_slot * size;
+ }
+ return m;
+ }
+};
+
+} // namespace hashtable_debug_internal
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc
new file mode 100644
index 00000000..f5779d62
--- /dev/null
+++ b/absl/container/internal/raw_hash_set_allocator_test.cc
@@ -0,0 +1,430 @@
+// 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
+//
+// 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.
+
+#include <limits>
+#include <scoped_allocator>
+
+#include "gtest/gtest.h"
+#include "absl/container/internal/raw_hash_set.h"
+#include "absl/container/internal/tracked.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+enum AllocSpec {
+ kPropagateOnCopy = 1,
+ kPropagateOnMove = 2,
+ kPropagateOnSwap = 4,
+};
+
+struct AllocState {
+ size_t num_allocs = 0;
+ std::set<void*> owned;
+};
+
+template <class T,
+ int Spec = kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>
+class CheckedAlloc {
+ public:
+ template <class, int>
+ friend class CheckedAlloc;
+
+ using value_type = T;
+
+ CheckedAlloc() {}
+ explicit CheckedAlloc(size_t id) : id_(id) {}
+ CheckedAlloc(const CheckedAlloc&) = default;
+ CheckedAlloc& operator=(const CheckedAlloc&) = default;
+
+ template <class U>
+ CheckedAlloc(const CheckedAlloc<U, Spec>& that)
+ : id_(that.id_), state_(that.state_) {}
+
+ template <class U>
+ struct rebind {
+ using other = CheckedAlloc<U, Spec>;
+ };
+
+ using propagate_on_container_copy_assignment =
+ std::integral_constant<bool, (Spec & kPropagateOnCopy) != 0>;
+
+ using propagate_on_container_move_assignment =
+ std::integral_constant<bool, (Spec & kPropagateOnMove) != 0>;
+
+ using propagate_on_container_swap =
+ std::integral_constant<bool, (Spec & kPropagateOnSwap) != 0>;
+
+ CheckedAlloc select_on_container_copy_construction() const {
+ if (Spec & kPropagateOnCopy) return *this;
+ return {};
+ }
+
+ T* allocate(size_t n) {
+ T* ptr = std::allocator<T>().allocate(n);
+ track_alloc(ptr);
+ return ptr;
+ }
+ void deallocate(T* ptr, size_t n) {
+ memset(ptr, 0, n * sizeof(T)); // The freed memory must be unpoisoned.
+ track_dealloc(ptr);
+ return std::allocator<T>().deallocate(ptr, n);
+ }
+
+ friend bool operator==(const CheckedAlloc& a, const CheckedAlloc& b) {
+ return a.id_ == b.id_;
+ }
+ friend bool operator!=(const CheckedAlloc& a, const CheckedAlloc& b) {
+ return !(a == b);
+ }
+
+ size_t num_allocs() const { return state_->num_allocs; }
+
+ void swap(CheckedAlloc& that) {
+ using std::swap;
+ swap(id_, that.id_);
+ swap(state_, that.state_);
+ }
+
+ friend void swap(CheckedAlloc& a, CheckedAlloc& b) { a.swap(b); }
+
+ friend std::ostream& operator<<(std::ostream& o, const CheckedAlloc& a) {
+ return o << "alloc(" << a.id_ << ")";
+ }
+
+ private:
+ void track_alloc(void* ptr) {
+ AllocState* state = state_.get();
+ ++state->num_allocs;
+ if (!state->owned.insert(ptr).second)
+ ADD_FAILURE() << *this << " got previously allocated memory: " << ptr;
+ }
+ void track_dealloc(void* ptr) {
+ if (state_->owned.erase(ptr) != 1)
+ ADD_FAILURE() << *this
+ << " deleting memory owned by another allocator: " << ptr;
+ }
+
+ size_t id_ = std::numeric_limits<size_t>::max();
+
+ std::shared_ptr<AllocState> state_ = std::make_shared<AllocState>();
+};
+
+struct Identity {
+ int32_t operator()(int32_t v) const { return v; }
+};
+
+struct Policy {
+ using slot_type = Tracked<int32_t>;
+ using init_type = Tracked<int32_t>;
+ using key_type = int32_t;
+
+ template <class allocator_type, class... Args>
+ static void construct(allocator_type* alloc, slot_type* slot,
+ Args&&... args) {
+ std::allocator_traits<allocator_type>::construct(
+ *alloc, slot, std::forward<Args>(args)...);
+ }
+
+ template <class allocator_type>
+ static void destroy(allocator_type* alloc, slot_type* slot) {
+ std::allocator_traits<allocator_type>::destroy(*alloc, slot);
+ }
+
+ template <class allocator_type>
+ static void transfer(allocator_type* alloc, slot_type* new_slot,
+ slot_type* old_slot) {
+ construct(alloc, new_slot, std::move(*old_slot));
+ destroy(alloc, old_slot);
+ }
+
+ template <class F>
+ static auto apply(F&& f, int32_t v) -> decltype(std::forward<F>(f)(v, v)) {
+ return std::forward<F>(f)(v, v);
+ }
+
+ template <class F>
+ static auto apply(F&& f, const slot_type& v)
+ -> decltype(std::forward<F>(f)(v.val(), v)) {
+ return std::forward<F>(f)(v.val(), v);
+ }
+
+ template <class F>
+ static auto apply(F&& f, slot_type&& v)
+ -> decltype(std::forward<F>(f)(v.val(), std::move(v))) {
+ return std::forward<F>(f)(v.val(), std::move(v));
+ }
+
+ static slot_type& element(slot_type* slot) { return *slot; }
+};
+
+template <int Spec>
+struct PropagateTest : public ::testing::Test {
+ using Alloc = CheckedAlloc<Tracked<int32_t>, Spec>;
+
+ using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, Alloc>;
+
+ PropagateTest() {
+ EXPECT_EQ(a1, t1.get_allocator());
+ EXPECT_NE(a2, t1.get_allocator());
+ }
+
+ Alloc a1 = Alloc(1);
+ Table t1 = Table(0, a1);
+ Alloc a2 = Alloc(2);
+};
+
+using PropagateOnAll =
+ PropagateTest<kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>;
+using NoPropagateOnCopy = PropagateTest<kPropagateOnMove | kPropagateOnSwap>;
+using NoPropagateOnMove = PropagateTest<kPropagateOnCopy | kPropagateOnSwap>;
+
+TEST_F(PropagateOnAll, Empty) { EXPECT_EQ(0, a1.num_allocs()); }
+
+TEST_F(PropagateOnAll, InsertAllocates) {
+ auto it = t1.insert(0).first;
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, InsertDecomposes) {
+ auto it = t1.insert(0).first;
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+
+ EXPECT_FALSE(t1.insert(0).second);
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, RehashMoves) {
+ auto it = t1.insert(0).first;
+ EXPECT_EQ(0, it->num_moves());
+ t1.rehash(2 * t1.capacity());
+ EXPECT_EQ(2, a1.num_allocs());
+ it = t1.find(0);
+ EXPECT_EQ(1, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, CopyConstructor) {
+ auto it = t1.insert(0).first;
+ Table u(t1);
+ EXPECT_EQ(2, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(NoPropagateOnCopy, CopyConstructor) {
+ auto it = t1.insert(0).first;
+ Table u(t1);
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(1, u.get_allocator().num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, CopyConstructorWithSameAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(t1, a1);
+ EXPECT_EQ(2, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(NoPropagateOnCopy, CopyConstructorWithSameAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(t1, a1);
+ EXPECT_EQ(2, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, CopyConstructorWithDifferentAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(t1, a2);
+ EXPECT_EQ(a2, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(1, a2.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(NoPropagateOnCopy, CopyConstructorWithDifferentAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(t1, a2);
+ EXPECT_EQ(a2, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(1, a2.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, MoveConstructor) {
+ auto it = t1.insert(0).first;
+ Table u(std::move(t1));
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(NoPropagateOnMove, MoveConstructor) {
+ auto it = t1.insert(0).first;
+ Table u(std::move(t1));
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, MoveConstructorWithSameAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(std::move(t1), a1);
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(NoPropagateOnMove, MoveConstructorWithSameAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(std::move(t1), a1);
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, MoveConstructorWithDifferentAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(std::move(t1), a2);
+ it = u.find(0);
+ EXPECT_EQ(a2, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(1, a2.num_allocs());
+ EXPECT_EQ(1, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(NoPropagateOnMove, MoveConstructorWithDifferentAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(std::move(t1), a2);
+ it = u.find(0);
+ EXPECT_EQ(a2, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(1, a2.num_allocs());
+ EXPECT_EQ(1, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, CopyAssignmentWithSameAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(0, a1);
+ u = t1;
+ EXPECT_EQ(2, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(NoPropagateOnCopy, CopyAssignmentWithSameAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(0, a1);
+ u = t1;
+ EXPECT_EQ(2, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, CopyAssignmentWithDifferentAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(0, a2);
+ u = t1;
+ EXPECT_EQ(a1, u.get_allocator());
+ EXPECT_EQ(2, a1.num_allocs());
+ EXPECT_EQ(0, a2.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(NoPropagateOnCopy, CopyAssignmentWithDifferentAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(0, a2);
+ u = t1;
+ EXPECT_EQ(a2, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(1, a2.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(1, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, MoveAssignmentWithSameAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(0, a1);
+ u = std::move(t1);
+ EXPECT_EQ(a1, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(NoPropagateOnMove, MoveAssignmentWithSameAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(0, a1);
+ u = std::move(t1);
+ EXPECT_EQ(a1, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, MoveAssignmentWithDifferentAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(0, a2);
+ u = std::move(t1);
+ EXPECT_EQ(a1, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, a2.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(NoPropagateOnMove, MoveAssignmentWithDifferentAlloc) {
+ auto it = t1.insert(0).first;
+ Table u(0, a2);
+ u = std::move(t1);
+ it = u.find(0);
+ EXPECT_EQ(a2, u.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(1, a2.num_allocs());
+ EXPECT_EQ(1, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+TEST_F(PropagateOnAll, Swap) {
+ auto it = t1.insert(0).first;
+ Table u(0, a2);
+ u.swap(t1);
+ EXPECT_EQ(a1, u.get_allocator());
+ EXPECT_EQ(a2, t1.get_allocator());
+ EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_EQ(0, a2.num_allocs());
+ EXPECT_EQ(0, it->num_moves());
+ EXPECT_EQ(0, it->num_copies());
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
new file mode 100644
index 00000000..302f9758
--- /dev/null
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -0,0 +1,1830 @@
+// 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
+//
+// 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.
+
+#include "absl/container/internal/raw_hash_set.h"
+
+#include <cmath>
+#include <cstdint>
+#include <deque>
+#include <functional>
+#include <memory>
+#include <numeric>
+#include <random>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/base/internal/cycleclock.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/container/internal/container_memory.h"
+#include "absl/container/internal/hash_function_defaults.h"
+#include "absl/container/internal/hash_policy_testing.h"
+#include "absl/container/internal/hashtable_debug.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+struct RawHashSetTestOnlyAccess {
+ template <typename C>
+ static auto GetSlots(const C& c) -> decltype(c.slots_) {
+ return c.slots_;
+ }
+};
+
+namespace {
+
+using ::testing::DoubleNear;
+using ::testing::ElementsAre;
+using ::testing::Optional;
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+
+TEST(Util, NormalizeCapacity) {
+ constexpr size_t kMinCapacity = Group::kWidth - 1;
+ EXPECT_EQ(kMinCapacity, NormalizeCapacity(0));
+ EXPECT_EQ(kMinCapacity, NormalizeCapacity(1));
+ EXPECT_EQ(kMinCapacity, NormalizeCapacity(2));
+ EXPECT_EQ(kMinCapacity, NormalizeCapacity(kMinCapacity));
+ EXPECT_EQ(kMinCapacity * 2 + 1, NormalizeCapacity(kMinCapacity + 1));
+ EXPECT_EQ(kMinCapacity * 2 + 1, NormalizeCapacity(kMinCapacity + 2));
+}
+
+TEST(Util, probe_seq) {
+ probe_seq<16> seq(0, 127);
+ auto gen = [&]() {
+ size_t res = seq.offset();
+ seq.next();
+ return res;
+ };
+ std::vector<size_t> offsets(8);
+ std::generate_n(offsets.begin(), 8, gen);
+ EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
+ seq = probe_seq<16>(128, 127);
+ std::generate_n(offsets.begin(), 8, gen);
+ EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
+}
+
+TEST(BitMask, Smoke) {
+ EXPECT_FALSE((BitMask<uint8_t, 8>(0)));
+ EXPECT_TRUE((BitMask<uint8_t, 8>(5)));
+
+ EXPECT_THAT((BitMask<uint8_t, 8>(0)), ElementsAre());
+ EXPECT_THAT((BitMask<uint8_t, 8>(0x1)), ElementsAre(0));
+ EXPECT_THAT((BitMask<uint8_t, 8>(0x2)), ElementsAre(1));
+ EXPECT_THAT((BitMask<uint8_t, 8>(0x3)), ElementsAre(0, 1));
+ EXPECT_THAT((BitMask<uint8_t, 8>(0x4)), ElementsAre(2));
+ EXPECT_THAT((BitMask<uint8_t, 8>(0x5)), ElementsAre(0, 2));
+ EXPECT_THAT((BitMask<uint8_t, 8>(0x55)), ElementsAre(0, 2, 4, 6));
+ EXPECT_THAT((BitMask<uint8_t, 8>(0xAA)), ElementsAre(1, 3, 5, 7));
+}
+
+TEST(BitMask, WithShift) {
+ // See the non-SSE version of Group for details on what this math is for.
+ uint64_t ctrl = 0x1716151413121110;
+ uint64_t hash = 0x12;
+ constexpr uint64_t msbs = 0x8080808080808080ULL;
+ constexpr uint64_t lsbs = 0x0101010101010101ULL;
+ auto x = ctrl ^ (lsbs * hash);
+ uint64_t mask = (x - lsbs) & ~x & msbs;
+ EXPECT_EQ(0x0000000080800000, mask);
+
+ BitMask<uint64_t, 8, 3> b(mask);
+ EXPECT_EQ(*b, 2);
+}
+
+TEST(BitMask, LeadingTrailing) {
+ EXPECT_EQ((BitMask<uint32_t, 16>(0b0001101001000000).LeadingZeros()), 3);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0b0001101001000000).TrailingZeros()), 6);
+
+ EXPECT_EQ((BitMask<uint32_t, 16>(0b0000000000000001).LeadingZeros()), 15);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0b0000000000000001).TrailingZeros()), 0);
+
+ EXPECT_EQ((BitMask<uint32_t, 16>(0b1000000000000000).LeadingZeros()), 0);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0b1000000000000000).TrailingZeros()), 15);
+
+ EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).LeadingZeros()), 3);
+ EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).TrailingZeros()), 1);
+
+ EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).LeadingZeros()), 7);
+ EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).TrailingZeros()), 0);
+
+ EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).LeadingZeros()), 0);
+ EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).TrailingZeros()), 7);
+}
+
+TEST(Group, EmptyGroup) {
+ for (h2_t h = 0; h != 128; ++h) EXPECT_FALSE(Group{EmptyGroup()}.Match(h));
+}
+
+TEST(Group, Match) {
+ if (Group::kWidth == 16) {
+ ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
+ 7, 5, 3, 1, 1, 1, 1, 1};
+ EXPECT_THAT(Group{group}.Match(0), ElementsAre());
+ EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
+ EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
+ EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
+ EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
+ } else if (Group::kWidth == 8) {
+ ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+ EXPECT_THAT(Group{group}.Match(0), ElementsAre());
+ EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
+ EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
+ } else {
+ FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
+ }
+}
+
+TEST(Group, MatchEmpty) {
+ if (Group::kWidth == 16) {
+ ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
+ 7, 5, 3, 1, 1, 1, 1, 1};
+ EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4));
+ } else if (Group::kWidth == 8) {
+ ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+ EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0));
+ } else {
+ FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
+ }
+}
+
+TEST(Group, MatchEmptyOrDeleted) {
+ if (Group::kWidth == 16) {
+ ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
+ 7, 5, 3, 1, 1, 1, 1, 1};
+ EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4));
+ } else if (Group::kWidth == 8) {
+ ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+ EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3));
+ } else {
+ FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
+ }
+}
+
+TEST(Batch, DropDeletes) {
+ constexpr size_t kCapacity = 63;
+ constexpr size_t kGroupWidth = container_internal::Group::kWidth;
+ std::vector<ctrl_t> ctrl(kCapacity + 1 + kGroupWidth);
+ ctrl[kCapacity] = kSentinel;
+ std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted};
+ for (size_t i = 0; i != kCapacity; ++i) {
+ ctrl[i] = pattern[i % pattern.size()];
+ if (i < kGroupWidth - 1)
+ ctrl[i + kCapacity + 1] = pattern[i % pattern.size()];
+ }
+ ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity);
+ ASSERT_EQ(ctrl[kCapacity], kSentinel);
+ for (size_t i = 0; i < kCapacity + 1 + kGroupWidth; ++i) {
+ ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()];
+ if (i == kCapacity) expected = kSentinel;
+ if (expected == kDeleted) expected = kEmpty;
+ if (IsFull(expected)) expected = kDeleted;
+ EXPECT_EQ(ctrl[i], expected)
+ << i << " " << int{pattern[i % pattern.size()]};
+ }
+}
+
+TEST(Group, CountLeadingEmptyOrDeleted) {
+ const std::vector<ctrl_t> empty_examples = {kEmpty, kDeleted};
+ const std::vector<ctrl_t> full_examples = {0, 1, 2, 3, 5, 9, 127, kSentinel};
+
+ for (ctrl_t empty : empty_examples) {
+ std::vector<ctrl_t> e(Group::kWidth, empty);
+ EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted());
+ for (ctrl_t full : full_examples) {
+ for (size_t i = 0; i != Group::kWidth; ++i) {
+ std::vector<ctrl_t> f(Group::kWidth, empty);
+ f[i] = full;
+ EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted());
+ }
+ std::vector<ctrl_t> f(Group::kWidth, empty);
+ f[Group::kWidth * 2 / 3] = full;
+ f[Group::kWidth / 2] = full;
+ EXPECT_EQ(
+ Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted());
+ }
+ }
+}
+
+struct IntPolicy {
+ using slot_type = int64_t;
+ using key_type = int64_t;
+ using init_type = int64_t;
+
+ static void construct(void*, int64_t* slot, int64_t v) { *slot = v; }
+ static void destroy(void*, int64_t*) {}
+ static void transfer(void*, int64_t* new_slot, int64_t* old_slot) {
+ *new_slot = *old_slot;
+ }
+
+ static int64_t& element(slot_type* slot) { return *slot; }
+
+ template <class F>
+ static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) {
+ return std::forward<F>(f)(x, x);
+ }
+};
+
+class StringPolicy {
+ template <class F, class K, class V,
+ class = typename std::enable_if<
+ std::is_convertible<const K&, absl::string_view>::value>::type>
+ decltype(std::declval<F>()(
+ std::declval<const absl::string_view&>(), std::piecewise_construct,
+ std::declval<std::tuple<K>>(),
+ std::declval<V>())) static apply_impl(F&& f,
+ std::pair<std::tuple<K>, V> p) {
+ const absl::string_view& key = std::get<0>(p.first);
+ return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
+ std::move(p.second));
+ }
+
+ public:
+ struct slot_type {
+ struct ctor {};
+
+ template <class... Ts>
+ slot_type(ctor, Ts&&... ts) : pair(std::forward<Ts>(ts)...) {}
+
+ std::pair<std::string, std::string> pair;
+ };
+
+ using key_type = std::string;
+ using init_type = std::pair<std::string, std::string>;
+
+ template <class allocator_type, class... Args>
+ static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
+ std::allocator_traits<allocator_type>::construct(
+ *alloc, slot, typename slot_type::ctor(), std::forward<Args>(args)...);
+ }
+
+ template <class allocator_type>
+ static void destroy(allocator_type* alloc, slot_type* slot) {
+ std::allocator_traits<allocator_type>::destroy(*alloc, slot);
+ }
+
+ template <class allocator_type>
+ static void transfer(allocator_type* alloc, slot_type* new_slot,
+ slot_type* old_slot) {
+ construct(alloc, new_slot, std::move(old_slot->pair));
+ destroy(alloc, old_slot);
+ }
+
+ static std::pair<std::string, std::string>& element(slot_type* slot) {
+ return slot->pair;
+ }
+
+ template <class F, class... Args>
+ static auto apply(F&& f, Args&&... args)
+ -> decltype(apply_impl(std::forward<F>(f),
+ PairArgs(std::forward<Args>(args)...))) {
+ return apply_impl(std::forward<F>(f),
+ PairArgs(std::forward<Args>(args)...));
+ }
+};
+
+struct StringHash : absl::Hash<absl::string_view> {
+ using is_transparent = void;
+};
+struct StringEq : std::equal_to<absl::string_view> {
+ using is_transparent = void;
+};
+
+struct StringTable
+ : raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> {
+ using Base = typename StringTable::raw_hash_set;
+ StringTable() {}
+ using Base::Base;
+};
+
+struct IntTable
+ : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
+ std::equal_to<int64_t>, std::allocator<int64_t>> {
+ using Base = typename IntTable::raw_hash_set;
+ IntTable() {}
+ using Base::Base;
+};
+
+struct BadFastHash {
+ template <class T>
+ size_t operator()(const T&) const {
+ return 0;
+ }
+};
+
+struct BadTable : raw_hash_set<IntPolicy, BadFastHash, std::equal_to<int>,
+ std::allocator<int>> {
+ using Base = typename BadTable::raw_hash_set;
+ BadTable() {}
+ using Base::Base;
+};
+
+TEST(Table, EmptyFunctorOptimization) {
+ static_assert(std::is_empty<std::equal_to<absl::string_view>>::value, "");
+ static_assert(std::is_empty<std::allocator<int>>::value, "");
+
+ struct MockTable {
+ void* ctrl;
+ void* slots;
+ size_t size;
+ size_t capacity;
+ size_t growth_left;
+ };
+ struct StatelessHash {
+ size_t operator()(absl::string_view) const { return 0; }
+ };
+ struct StatefulHash : StatelessHash {
+ size_t dummy;
+ };
+
+ EXPECT_EQ(
+ sizeof(MockTable),
+ sizeof(
+ raw_hash_set<StringPolicy, StatelessHash,
+ std::equal_to<absl::string_view>, std::allocator<int>>));
+
+ EXPECT_EQ(
+ sizeof(MockTable) + sizeof(StatefulHash),
+ sizeof(
+ raw_hash_set<StringPolicy, StatefulHash,
+ std::equal_to<absl::string_view>, std::allocator<int>>));
+}
+
+TEST(Table, Empty) {
+ IntTable t;
+ EXPECT_EQ(0, t.size());
+ EXPECT_TRUE(t.empty());
+}
+
+#ifdef __GNUC__
+template <class T>
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void DoNotOptimize(const T& v) {
+ asm volatile("" : : "r,m"(v) : "memory");
+}
+#endif
+
+TEST(Table, Prefetch) {
+ IntTable t;
+ t.emplace(1);
+ // Works for both present and absent keys.
+ t.prefetch(1);
+ t.prefetch(2);
+
+ // Do not run in debug mode, when prefetch is not implemented, or when
+ // sanitizers are enabled.
+#if defined(NDEBUG) && defined(__GNUC__) && !defined(ADDRESS_SANITIZER) && \
+ !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) && \
+ !defined(UNDEFINED_BEHAVIOR_SANITIZER)
+ const auto now = [] { return absl::base_internal::CycleClock::Now(); };
+
+ // Make size enough to not fit in L2 cache (16.7 Mb)
+ static constexpr int size = 1 << 22;
+ for (int i = 0; i < size; ++i) t.insert(i);
+
+ int64_t no_prefetch = 0, prefetch = 0;
+ for (int iter = 0; iter < 10; ++iter) {
+ int64_t time = now();
+ for (int i = 0; i < size; ++i) {
+ DoNotOptimize(t.find(i));
+ }
+ no_prefetch += now() - time;
+
+ time = now();
+ for (int i = 0; i < size; ++i) {
+ t.prefetch(i + 20);
+ DoNotOptimize(t.find(i));
+ }
+ prefetch += now() - time;
+ }
+
+ // no_prefetch is at least 30% slower.
+ EXPECT_GE(1.0 * no_prefetch / prefetch, 1.3);
+#endif
+}
+
+TEST(Table, LookupEmpty) {
+ IntTable t;
+ auto it = t.find(0);
+ EXPECT_TRUE(it == t.end());
+}
+
+TEST(Table, Insert1) {
+ IntTable t;
+ EXPECT_TRUE(t.find(0) == t.end());
+ auto res = t.emplace(0);
+ EXPECT_TRUE(res.second);
+ EXPECT_THAT(*res.first, 0);
+ EXPECT_EQ(1, t.size());
+ EXPECT_THAT(*t.find(0), 0);
+}
+
+TEST(Table, Insert2) {
+ IntTable t;
+ EXPECT_TRUE(t.find(0) == t.end());
+ auto res = t.emplace(0);
+ EXPECT_TRUE(res.second);
+ EXPECT_THAT(*res.first, 0);
+ EXPECT_EQ(1, t.size());
+ EXPECT_TRUE(t.find(1) == t.end());
+ res = t.emplace(1);
+ EXPECT_TRUE(res.second);
+ EXPECT_THAT(*res.first, 1);
+ EXPECT_EQ(2, t.size());
+ EXPECT_THAT(*t.find(0), 0);
+ EXPECT_THAT(*t.find(1), 1);
+}
+
+TEST(Table, InsertCollision) {
+ BadTable t;
+ EXPECT_TRUE(t.find(1) == t.end());
+ auto res = t.emplace(1);
+ EXPECT_TRUE(res.second);
+ EXPECT_THAT(*res.first, 1);
+ EXPECT_EQ(1, t.size());
+
+ EXPECT_TRUE(t.find(2) == t.end());
+ res = t.emplace(2);
+ EXPECT_THAT(*res.first, 2);
+ EXPECT_TRUE(res.second);
+ EXPECT_EQ(2, t.size());
+
+ EXPECT_THAT(*t.find(1), 1);
+ EXPECT_THAT(*t.find(2), 2);
+}
+
+// Test that we do not add existent element in case we need to search through
+// many groups with deleted elements
+TEST(Table, InsertCollisionAndFindAfterDelete) {
+ BadTable t; // all elements go to the same group.
+ // Have at least 2 groups with Group::kWidth collisions
+ // plus some extra collisions in the last group.
+ constexpr size_t kNumInserts = Group::kWidth * 2 + 5;
+ for (size_t i = 0; i < kNumInserts; ++i) {
+ auto res = t.emplace(i);
+ EXPECT_TRUE(res.second);
+ EXPECT_THAT(*res.first, i);
+ EXPECT_EQ(i + 1, t.size());
+ }
+
+ // Remove elements one by one and check
+ // that we still can find all other elements.
+ for (size_t i = 0; i < kNumInserts; ++i) {
+ EXPECT_EQ(1, t.erase(i)) << i;
+ for (size_t j = i + 1; j < kNumInserts; ++j) {
+ EXPECT_THAT(*t.find(j), j);
+ auto res = t.emplace(j);
+ EXPECT_FALSE(res.second) << i << " " << j;
+ EXPECT_THAT(*res.first, j);
+ EXPECT_EQ(kNumInserts - i - 1, t.size());
+ }
+ }
+ EXPECT_TRUE(t.empty());
+}
+
+TEST(Table, LazyEmplace) {
+ StringTable t;
+ bool called = false;
+ auto it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
+ called = true;
+ f("abc", "ABC");
+ });
+ EXPECT_TRUE(called);
+ EXPECT_THAT(*it, Pair("abc", "ABC"));
+ called = false;
+ it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
+ called = true;
+ f("abc", "DEF");
+ });
+ EXPECT_FALSE(called);
+ EXPECT_THAT(*it, Pair("abc", "ABC"));
+}
+
+TEST(Table, ContainsEmpty) {
+ IntTable t;
+
+ EXPECT_FALSE(t.contains(0));
+}
+
+TEST(Table, Contains1) {
+ IntTable t;
+
+ EXPECT_TRUE(t.insert(0).second);
+ EXPECT_TRUE(t.contains(0));
+ EXPECT_FALSE(t.contains(1));
+
+ EXPECT_EQ(1, t.erase(0));
+ EXPECT_FALSE(t.contains(0));
+}
+
+TEST(Table, Contains2) {
+ IntTable t;
+
+ EXPECT_TRUE(t.insert(0).second);
+ EXPECT_TRUE(t.contains(0));
+ EXPECT_FALSE(t.contains(1));
+
+ t.clear();
+ EXPECT_FALSE(t.contains(0));
+}
+
+int decompose_constructed;
+struct DecomposeType {
+ DecomposeType(int i) : i(i) { // NOLINT
+ ++decompose_constructed;
+ }
+
+ explicit DecomposeType(const char* d) : DecomposeType(*d) {}
+
+ int i;
+};
+
+struct DecomposeHash {
+ using is_transparent = void;
+ size_t operator()(DecomposeType a) const { return a.i; }
+ size_t operator()(int a) const { return a; }
+ size_t operator()(const char* a) const { return *a; }
+};
+
+struct DecomposeEq {
+ using is_transparent = void;
+ bool operator()(DecomposeType a, DecomposeType b) const { return a.i == b.i; }
+ bool operator()(DecomposeType a, int b) const { return a.i == b; }
+ bool operator()(DecomposeType a, const char* b) const { return a.i == *b; }
+};
+
+struct DecomposePolicy {
+ using slot_type = DecomposeType;
+ using key_type = DecomposeType;
+ using init_type = DecomposeType;
+
+ template <typename T>
+ static void construct(void*, DecomposeType* slot, T&& v) {
+ *slot = DecomposeType(std::forward<T>(v));
+ }
+ static void destroy(void*, DecomposeType*) {}
+ static DecomposeType& element(slot_type* slot) { return *slot; }
+
+ template <class F, class T>
+ static auto apply(F&& f, const T& x) -> decltype(std::forward<F>(f)(x, x)) {
+ return std::forward<F>(f)(x, x);
+ }
+};
+
+template <typename Hash, typename Eq>
+void TestDecompose(bool construct_three) {
+ DecomposeType elem{0};
+ const int one = 1;
+ const char* three_p = "3";
+ const auto& three = three_p;
+
+ raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>> set1;
+
+ decompose_constructed = 0;
+ int expected_constructed = 0;
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ set1.insert(elem);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ set1.insert(1);
+ EXPECT_EQ(++expected_constructed, decompose_constructed);
+ set1.emplace("3");
+ EXPECT_EQ(++expected_constructed, decompose_constructed);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+
+ { // insert(T&&)
+ set1.insert(1);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ }
+
+ { // insert(const T&)
+ set1.insert(one);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ }
+
+ { // insert(hint, T&&)
+ set1.insert(set1.begin(), 1);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ }
+
+ { // insert(hint, const T&)
+ set1.insert(set1.begin(), one);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ }
+
+ { // emplace(...)
+ set1.emplace(1);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ set1.emplace("3");
+ expected_constructed += construct_three;
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ set1.emplace(one);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ set1.emplace(three);
+ expected_constructed += construct_three;
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ }
+
+ { // emplace_hint(...)
+ set1.emplace_hint(set1.begin(), 1);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ set1.emplace_hint(set1.begin(), "3");
+ expected_constructed += construct_three;
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ set1.emplace_hint(set1.begin(), one);
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ set1.emplace_hint(set1.begin(), three);
+ expected_constructed += construct_three;
+ EXPECT_EQ(expected_constructed, decompose_constructed);
+ }
+}
+
+TEST(Table, Decompose) {
+ TestDecompose<DecomposeHash, DecomposeEq>(false);
+
+ struct TransparentHashIntOverload {
+ size_t operator()(DecomposeType a) const { return a.i; }
+ size_t operator()(int a) const { return a; }
+ };
+ struct TransparentEqIntOverload {
+ bool operator()(DecomposeType a, DecomposeType b) const {
+ return a.i == b.i;
+ }
+ bool operator()(DecomposeType a, int b) const { return a.i == b; }
+ };
+ TestDecompose<TransparentHashIntOverload, DecomposeEq>(true);
+ TestDecompose<TransparentHashIntOverload, TransparentEqIntOverload>(true);
+ TestDecompose<DecomposeHash, TransparentEqIntOverload>(true);
+}
+
+// Returns the largest m such that a table with m elements has the same number
+// of buckets as a table with n elements.
+size_t MaxDensitySize(size_t n) {
+ IntTable t;
+ t.reserve(n);
+ for (size_t i = 0; i != n; ++i) t.emplace(i);
+ const size_t c = t.bucket_count();
+ while (c == t.bucket_count()) t.emplace(n++);
+ return t.size() - 1;
+}
+
+struct Modulo1000Hash {
+ size_t operator()(int x) const { return x % 1000; }
+};
+
+struct Modulo1000HashTable
+ : public raw_hash_set<IntPolicy, Modulo1000Hash, std::equal_to<int>,
+ std::allocator<int>> {};
+
+// Test that rehash with no resize happen in case of many deleted slots.
+TEST(Table, RehashWithNoResize) {
+ Modulo1000HashTable t;
+ // Adding the same length (and the same hash) strings
+ // to have at least kMinFullGroups groups
+ // with Group::kWidth collisions. Then fill up to MaxDensitySize;
+ const size_t kMinFullGroups = 7;
+ std::vector<int> keys;
+ for (size_t i = 0; i < MaxDensitySize(Group::kWidth * kMinFullGroups); ++i) {
+ int k = i * 1000;
+ t.emplace(k);
+ keys.push_back(k);
+ }
+ const size_t capacity = t.capacity();
+
+ // Remove elements from all groups except the first and the last one.
+ // All elements removed from full groups will be marked as kDeleted.
+ const size_t erase_begin = Group::kWidth / 2;
+ const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth;
+ for (size_t i = erase_begin; i < erase_end; ++i) {
+ EXPECT_EQ(1, t.erase(keys[i])) << i;
+ }
+ keys.erase(keys.begin() + erase_begin, keys.begin() + erase_end);
+
+ auto last_key = keys.back();
+ size_t last_key_num_probes = GetHashtableDebugNumProbes(t, last_key);
+
+ // Make sure that we have to make a lot of probes for last key.
+ ASSERT_GT(last_key_num_probes, kMinFullGroups);
+
+ int x = 1;
+ // Insert and erase one element, before inplace rehash happen.
+ while (last_key_num_probes == GetHashtableDebugNumProbes(t, last_key)) {
+ t.emplace(x);
+ ASSERT_EQ(capacity, t.capacity());
+ // All elements should be there.
+ ASSERT_TRUE(t.find(x) != t.end()) << x;
+ for (const auto& k : keys) {
+ ASSERT_TRUE(t.find(k) != t.end()) << k;
+ }
+ t.erase(x);
+ ++x;
+ }
+}
+
+TEST(Table, InsertEraseStressTest) {
+ IntTable t;
+ const size_t kMinElementCount = 250;
+ std::deque<int> keys;
+ size_t i = 0;
+ for (; i < MaxDensitySize(kMinElementCount); ++i) {
+ t.emplace(i);
+ keys.push_back(i);
+ }
+ const size_t kNumIterations = 1000000;
+ for (; i < kNumIterations; ++i) {
+ ASSERT_EQ(1, t.erase(keys.front()));
+ keys.pop_front();
+ t.emplace(i);
+ keys.push_back(i);
+ }
+}
+
+TEST(Table, InsertOverloads) {
+ StringTable t;
+ // These should all trigger the insert(init_type) overload.
+ t.insert({{}, {}});
+ t.insert({"ABC", {}});
+ t.insert({"DEF", "!!!"});
+
+ EXPECT_THAT(t, UnorderedElementsAre(Pair("", ""), Pair("ABC", ""),
+ Pair("DEF", "!!!")));
+}
+
+TEST(Table, LargeTable) {
+ IntTable t;
+ for (int64_t i = 0; i != 100000; ++i) t.emplace(i << 40);
+ for (int64_t i = 0; i != 100000; ++i) ASSERT_EQ(i << 40, *t.find(i << 40));
+}
+
+// Timeout if copy is quadratic as it was in Rust.
+TEST(Table, EnsureNonQuadraticAsInRust) {
+ static const size_t kLargeSize = 1 << 15;
+
+ IntTable t;
+ for (size_t i = 0; i != kLargeSize; ++i) {
+ t.insert(i);
+ }
+
+ // If this is quadratic, the test will timeout.
+ IntTable t2;
+ for (const auto& entry : t) t2.insert(entry);
+}
+
+TEST(Table, ClearBug) {
+ IntTable t;
+ constexpr size_t capacity = container_internal::Group::kWidth - 1;
+ constexpr size_t max_size = capacity / 2;
+ for (size_t i = 0; i < max_size; ++i) {
+ t.insert(i);
+ }
+ ASSERT_EQ(capacity, t.capacity());
+ intptr_t original = reinterpret_cast<intptr_t>(&*t.find(2));
+ t.clear();
+ ASSERT_EQ(capacity, t.capacity());
+ for (size_t i = 0; i < max_size; ++i) {
+ t.insert(i);
+ }
+ ASSERT_EQ(capacity, t.capacity());
+ intptr_t second = reinterpret_cast<intptr_t>(&*t.find(2));
+ // We are checking that original and second are close enough to each other
+ // that they are probably still in the same group. This is not strictly
+ // guaranteed.
+ EXPECT_LT(std::abs(original - second),
+ capacity * sizeof(IntTable::value_type));
+}
+
+TEST(Table, Erase) {
+ IntTable t;
+ EXPECT_TRUE(t.find(0) == t.end());
+ auto res = t.emplace(0);
+ EXPECT_TRUE(res.second);
+ EXPECT_EQ(1, t.size());
+ t.erase(res.first);
+ EXPECT_EQ(0, t.size());
+ EXPECT_TRUE(t.find(0) == t.end());
+}
+
+// Collect N bad keys by following algorithm:
+// 1. Create an empty table and reserve it to 2 * N.
+// 2. Insert N random elements.
+// 3. Take first Group::kWidth - 1 to bad_keys array.
+// 4. Clear the table without resize.
+// 5. Go to point 2 while N keys not collected
+std::vector<int64_t> CollectBadMergeKeys(size_t N) {
+ static constexpr int kGroupSize = Group::kWidth - 1;
+
+ auto topk_range = [](size_t b, size_t e, IntTable* t) -> std::vector<int64_t> {
+ for (size_t i = b; i != e; ++i) {
+ t->emplace(i);
+ }
+ std::vector<int64_t> res;
+ res.reserve(kGroupSize);
+ auto it = t->begin();
+ for (size_t i = b; i != e && i != b + kGroupSize; ++i, ++it) {
+ res.push_back(*it);
+ }
+ return res;
+ };
+
+ std::vector<int64_t> bad_keys;
+ bad_keys.reserve(N);
+ IntTable t;
+ t.reserve(N * 2);
+
+ for (size_t b = 0; bad_keys.size() < N; b += N) {
+ auto keys = topk_range(b, b + N, &t);
+ bad_keys.insert(bad_keys.end(), keys.begin(), keys.end());
+ t.erase(t.begin(), t.end());
+ EXPECT_TRUE(t.empty());
+ }
+ return bad_keys;
+}
+
+struct ProbeStats {
+ // Number of elements with specific probe length over all tested tables.
+ std::vector<size_t> all_probes_histogram;
+ // Ratios total_probe_length/size for every tested table.
+ std::vector<double> single_table_ratios;
+
+ friend ProbeStats operator+(const ProbeStats& a, const ProbeStats& b) {
+ ProbeStats res = a;
+ res.all_probes_histogram.resize(std::max(res.all_probes_histogram.size(),
+ b.all_probes_histogram.size()));
+ std::transform(b.all_probes_histogram.begin(), b.all_probes_histogram.end(),
+ res.all_probes_histogram.begin(),
+ res.all_probes_histogram.begin(), std::plus<size_t>());
+ res.single_table_ratios.insert(res.single_table_ratios.end(),
+ b.single_table_ratios.begin(),
+ b.single_table_ratios.end());
+ return res;
+ }
+
+ // Average ratio total_probe_length/size over tables.
+ double AvgRatio() const {
+ return std::accumulate(single_table_ratios.begin(),
+ single_table_ratios.end(), 0.0) /
+ single_table_ratios.size();
+ }
+
+ // Maximum ratio total_probe_length/size over tables.
+ double MaxRatio() const {
+ return *std::max_element(single_table_ratios.begin(),
+ single_table_ratios.end());
+ }
+
+ // Percentile ratio total_probe_length/size over tables.
+ double PercentileRatio(double Percentile = 0.95) const {
+ auto r = single_table_ratios;
+ auto mid = r.begin() + static_cast<size_t>(r.size() * Percentile);
+ if (mid != r.end()) {
+ std::nth_element(r.begin(), mid, r.end());
+ return *mid;
+ } else {
+ return MaxRatio();
+ }
+ }
+
+ // Maximum probe length over all elements and all tables.
+ size_t MaxProbe() const { return all_probes_histogram.size(); }
+
+ // Fraction of elements with specified probe length.
+ std::vector<double> ProbeNormalizedHistogram() const {
+ double total_elements = std::accumulate(all_probes_histogram.begin(),
+ all_probes_histogram.end(), 0ull);
+ std::vector<double> res;
+ for (size_t p : all_probes_histogram) {
+ res.push_back(p / total_elements);
+ }
+ return res;
+ }
+
+ size_t PercentileProbe(double Percentile = 0.99) const {
+ size_t idx = 0;
+ for (double p : ProbeNormalizedHistogram()) {
+ if (Percentile > p) {
+ Percentile -= p;
+ ++idx;
+ } else {
+ return idx;
+ }
+ }
+ return idx;
+ }
+
+ friend std::ostream& operator<<(std::ostream& out, const ProbeStats& s) {
+ out << "{AvgRatio:" << s.AvgRatio() << ", MaxRatio:" << s.MaxRatio()
+ << ", PercentileRatio:" << s.PercentileRatio()
+ << ", MaxProbe:" << s.MaxProbe() << ", Probes=[";
+ for (double p : s.ProbeNormalizedHistogram()) {
+ out << p << ",";
+ }
+ out << "]}";
+
+ return out;
+ }
+};
+
+struct ExpectedStats {
+ double avg_ratio;
+ double max_ratio;
+ std::vector<std::pair<double, double>> pecentile_ratios;
+ std::vector<std::pair<double, double>> pecentile_probes;
+
+ friend std::ostream& operator<<(std::ostream& out, const ExpectedStats& s) {
+ out << "{AvgRatio:" << s.avg_ratio << ", MaxRatio:" << s.max_ratio
+ << ", PercentileRatios: [";
+ for (auto el : s.pecentile_ratios) {
+ out << el.first << ":" << el.second << ", ";
+ }
+ out << "], PercentileProbes: [";
+ for (auto el : s.pecentile_probes) {
+ out << el.first << ":" << el.second << ", ";
+ }
+ out << "]}";
+
+ return out;
+ }
+};
+
+void VerifyStats(size_t size, const ExpectedStats& exp,
+ const ProbeStats& stats) {
+ EXPECT_LT(stats.AvgRatio(), exp.avg_ratio) << size << " " << stats;
+ EXPECT_LT(stats.MaxRatio(), exp.max_ratio) << size << " " << stats;
+ for (auto pr : exp.pecentile_ratios) {
+ EXPECT_LE(stats.PercentileRatio(pr.first), pr.second)
+ << size << " " << pr.first << " " << stats;
+ }
+
+ for (auto pr : exp.pecentile_probes) {
+ EXPECT_LE(stats.PercentileProbe(pr.first), pr.second)
+ << size << " " << pr.first << " " << stats;
+ }
+}
+
+using ProbeStatsPerSize = std::map<size_t, ProbeStats>;
+
+// Collect total ProbeStats on num_iters iterations of the following algorithm:
+// 1. Create new table and reserve it to keys.size() * 2
+// 2. Insert all keys xored with seed
+// 3. Collect ProbeStats from final table.
+ProbeStats CollectProbeStatsOnKeysXoredWithSeed(const std::vector<int64_t>& keys,
+ size_t num_iters) {
+ const size_t reserve_size = keys.size() * 2;
+
+ ProbeStats stats;
+
+ int64_t seed = 0x71b1a19b907d6e33;
+ while (num_iters--) {
+ seed = static_cast<int64_t>(static_cast<uint64_t>(seed) * 17 + 13);
+ IntTable t1;
+ t1.reserve(reserve_size);
+ for (const auto& key : keys) {
+ t1.emplace(key ^ seed);
+ }
+
+ auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
+ stats.all_probes_histogram.resize(
+ std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
+ std::transform(probe_histogram.begin(), probe_histogram.end(),
+ stats.all_probes_histogram.begin(),
+ stats.all_probes_histogram.begin(), std::plus<size_t>());
+
+ size_t total_probe_seq_length = 0;
+ for (size_t i = 0; i < probe_histogram.size(); ++i) {
+ total_probe_seq_length += i * probe_histogram[i];
+ }
+ stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
+ keys.size());
+ t1.erase(t1.begin(), t1.end());
+ }
+ return stats;
+}
+
+ExpectedStats XorSeedExpectedStats() {
+ constexpr bool kRandomizesInserts =
+#if NDEBUG
+ false;
+#else // NDEBUG
+ true;
+#endif // NDEBUG
+
+ // The effective load factor is larger in non-opt mode because we insert
+ // elements out of order.
+ switch (container_internal::Group::kWidth) {
+ case 8:
+ if (kRandomizesInserts) {
+ return {0.05,
+ 1.0,
+ {{0.95, 0.5}},
+ {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
+ } else {
+ return {0.05,
+ 2.0,
+ {{0.95, 0.1}},
+ {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
+ }
+ case 16:
+ if (kRandomizesInserts) {
+ return {0.1,
+ 1.0,
+ {{0.95, 0.1}},
+ {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
+ } else {
+ return {0.05,
+ 1.0,
+ {{0.95, 0.05}},
+ {{0.95, 0}, {0.99, 1}, {0.999, 4}, {0.9999, 10}}};
+ }
+ }
+ ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
+ return {};
+}
+TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) {
+ ProbeStatsPerSize stats;
+ std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
+ for (size_t size : sizes) {
+ stats[size] =
+ CollectProbeStatsOnKeysXoredWithSeed(CollectBadMergeKeys(size), 200);
+ }
+ auto expected = XorSeedExpectedStats();
+ for (size_t size : sizes) {
+ auto& stat = stats[size];
+ VerifyStats(size, expected, stat);
+ }
+}
+
+// Collect total ProbeStats on num_iters iterations of the following algorithm:
+// 1. Create new table
+// 2. Select 10% of keys and insert 10 elements key * 17 + j * 13
+// 3. Collect ProbeStats from final table
+ProbeStats CollectProbeStatsOnLinearlyTransformedKeys(
+ const std::vector<int64_t>& keys, size_t num_iters) {
+ ProbeStats stats;
+
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ auto linear_transform = [](size_t x, size_t y) { return x * 17 + y * 13; };
+ std::uniform_int_distribution<size_t> dist(0, keys.size()-1);
+ while (num_iters--) {
+ IntTable t1;
+ size_t num_keys = keys.size() / 10;
+ size_t start = dist(rng);
+ for (size_t i = 0; i != num_keys; ++i) {
+ for (size_t j = 0; j != 10; ++j) {
+ t1.emplace(linear_transform(keys[(i + start) % keys.size()], j));
+ }
+ }
+
+ auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
+ stats.all_probes_histogram.resize(
+ std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
+ std::transform(probe_histogram.begin(), probe_histogram.end(),
+ stats.all_probes_histogram.begin(),
+ stats.all_probes_histogram.begin(), std::plus<size_t>());
+
+ size_t total_probe_seq_length = 0;
+ for (size_t i = 0; i < probe_histogram.size(); ++i) {
+ total_probe_seq_length += i * probe_histogram[i];
+ }
+ stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
+ t1.size());
+ t1.erase(t1.begin(), t1.end());
+ }
+ return stats;
+}
+
+ExpectedStats LinearTransformExpectedStats() {
+ constexpr bool kRandomizesInserts =
+#if NDEBUG
+ false;
+#else // NDEBUG
+ true;
+#endif // NDEBUG
+
+ // The effective load factor is larger in non-opt mode because we insert
+ // elements out of order.
+ switch (container_internal::Group::kWidth) {
+ case 8:
+ if (kRandomizesInserts) {
+ return {0.1,
+ 0.5,
+ {{0.95, 0.3}},
+ {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
+ } else {
+ return {0.15,
+ 0.5,
+ {{0.95, 0.3}},
+ {{0.95, 0}, {0.99, 3}, {0.999, 15}, {0.9999, 25}}};
+ }
+ case 16:
+ if (kRandomizesInserts) {
+ return {0.1,
+ 0.4,
+ {{0.95, 0.3}},
+ {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
+ } else {
+ return {0.05,
+ 0.2,
+ {{0.95, 0.1}},
+ {{0.95, 0}, {0.99, 1}, {0.999, 6}, {0.9999, 10}}};
+ }
+ }
+ ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
+ return {};
+}
+TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) {
+ ProbeStatsPerSize stats;
+ std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
+ for (size_t size : sizes) {
+ stats[size] = CollectProbeStatsOnLinearlyTransformedKeys(
+ CollectBadMergeKeys(size), 300);
+ }
+ auto expected = LinearTransformExpectedStats();
+ for (size_t size : sizes) {
+ auto& stat = stats[size];
+ VerifyStats(size, expected, stat);
+ }
+}
+
+TEST(Table, EraseCollision) {
+ BadTable t;
+
+ // 1 2 3
+ t.emplace(1);
+ t.emplace(2);
+ t.emplace(3);
+ EXPECT_THAT(*t.find(1), 1);
+ EXPECT_THAT(*t.find(2), 2);
+ EXPECT_THAT(*t.find(3), 3);
+ EXPECT_EQ(3, t.size());
+
+ // 1 DELETED 3
+ t.erase(t.find(2));
+ EXPECT_THAT(*t.find(1), 1);
+ EXPECT_TRUE(t.find(2) == t.end());
+ EXPECT_THAT(*t.find(3), 3);
+ EXPECT_EQ(2, t.size());
+
+ // DELETED DELETED 3
+ t.erase(t.find(1));
+ EXPECT_TRUE(t.find(1) == t.end());
+ EXPECT_TRUE(t.find(2) == t.end());
+ EXPECT_THAT(*t.find(3), 3);
+ EXPECT_EQ(1, t.size());
+
+ // DELETED DELETED DELETED
+ t.erase(t.find(3));
+ EXPECT_TRUE(t.find(1) == t.end());
+ EXPECT_TRUE(t.find(2) == t.end());
+ EXPECT_TRUE(t.find(3) == t.end());
+ EXPECT_EQ(0, t.size());
+}
+
+TEST(Table, EraseInsertProbing) {
+ BadTable t(100);
+
+ // 1 2 3 4
+ t.emplace(1);
+ t.emplace(2);
+ t.emplace(3);
+ t.emplace(4);
+
+ // 1 DELETED 3 DELETED
+ t.erase(t.find(2));
+ t.erase(t.find(4));
+
+ // 1 10 3 11 12
+ t.emplace(10);
+ t.emplace(11);
+ t.emplace(12);
+
+ EXPECT_EQ(5, t.size());
+ EXPECT_THAT(t, UnorderedElementsAre(1, 10, 3, 11, 12));
+}
+
+TEST(Table, Clear) {
+ IntTable t;
+ EXPECT_TRUE(t.find(0) == t.end());
+ t.clear();
+ EXPECT_TRUE(t.find(0) == t.end());
+ auto res = t.emplace(0);
+ EXPECT_TRUE(res.second);
+ EXPECT_EQ(1, t.size());
+ t.clear();
+ EXPECT_EQ(0, t.size());
+ EXPECT_TRUE(t.find(0) == t.end());
+}
+
+TEST(Table, Swap) {
+ IntTable t;
+ EXPECT_TRUE(t.find(0) == t.end());
+ auto res = t.emplace(0);
+ EXPECT_TRUE(res.second);
+ EXPECT_EQ(1, t.size());
+ IntTable u;
+ t.swap(u);
+ EXPECT_EQ(0, t.size());
+ EXPECT_EQ(1, u.size());
+ EXPECT_TRUE(t.find(0) == t.end());
+ EXPECT_THAT(*u.find(0), 0);
+}
+
+TEST(Table, Rehash) {
+ IntTable t;
+ EXPECT_TRUE(t.find(0) == t.end());
+ t.emplace(0);
+ t.emplace(1);
+ EXPECT_EQ(2, t.size());
+ t.rehash(128);
+ EXPECT_EQ(2, t.size());
+ EXPECT_THAT(*t.find(0), 0);
+ EXPECT_THAT(*t.find(1), 1);
+}
+
+TEST(Table, RehashDoesNotRehashWhenNotNecessary) {
+ IntTable t;
+ t.emplace(0);
+ t.emplace(1);
+ auto* p = &*t.find(0);
+ t.rehash(1);
+ EXPECT_EQ(p, &*t.find(0));
+}
+
+TEST(Table, RehashZeroDoesNotAllocateOnEmptyTable) {
+ IntTable t;
+ t.rehash(0);
+ EXPECT_EQ(0, t.bucket_count());
+}
+
+TEST(Table, RehashZeroDeallocatesEmptyTable) {
+ IntTable t;
+ t.emplace(0);
+ t.clear();
+ EXPECT_NE(0, t.bucket_count());
+ t.rehash(0);
+ EXPECT_EQ(0, t.bucket_count());
+}
+
+TEST(Table, RehashZeroForcesRehash) {
+ IntTable t;
+ t.emplace(0);
+ t.emplace(1);
+ auto* p = &*t.find(0);
+ t.rehash(0);
+ EXPECT_NE(p, &*t.find(0));
+}
+
+TEST(Table, ConstructFromInitList) {
+ using P = std::pair<std::string, std::string>;
+ struct Q {
+ operator P() const { return {}; }
+ };
+ StringTable t = {P(), Q(), {}, {{}, {}}};
+}
+
+TEST(Table, CopyConstruct) {
+ IntTable t;
+ t.max_load_factor(.321f);
+ t.emplace(0);
+ EXPECT_EQ(1, t.size());
+ {
+ IntTable u(t);
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
+ EXPECT_THAT(*u.find(0), 0);
+ }
+ {
+ IntTable u{t};
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
+ EXPECT_THAT(*u.find(0), 0);
+ }
+ {
+ IntTable u = t;
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
+ EXPECT_THAT(*u.find(0), 0);
+ }
+}
+
+TEST(Table, CopyConstructWithAlloc) {
+ StringTable t;
+ t.max_load_factor(.321f);
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+ StringTable u(t, Alloc<std::pair<std::string, std::string>>());
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
+ EXPECT_THAT(*u.find("a"), Pair("a", "b"));
+}
+
+struct ExplicitAllocIntTable
+ : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
+ std::equal_to<int64_t>, Alloc<int64_t>> {
+ ExplicitAllocIntTable() {}
+};
+
+TEST(Table, AllocWithExplicitCtor) {
+ ExplicitAllocIntTable t;
+ EXPECT_EQ(0, t.size());
+}
+
+TEST(Table, MoveConstruct) {
+ {
+ StringTable t;
+ t.max_load_factor(.321f);
+ const float lf = t.max_load_factor();
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+
+ StringTable u(std::move(t));
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(lf, u.max_load_factor());
+ EXPECT_THAT(*u.find("a"), Pair("a", "b"));
+ }
+ {
+ StringTable t;
+ t.max_load_factor(.321f);
+ const float lf = t.max_load_factor();
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+
+ StringTable u{std::move(t)};
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(lf, u.max_load_factor());
+ EXPECT_THAT(*u.find("a"), Pair("a", "b"));
+ }
+ {
+ StringTable t;
+ t.max_load_factor(.321f);
+ const float lf = t.max_load_factor();
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+
+ StringTable u = std::move(t);
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(lf, u.max_load_factor());
+ EXPECT_THAT(*u.find("a"), Pair("a", "b"));
+ }
+}
+
+TEST(Table, MoveConstructWithAlloc) {
+ StringTable t;
+ t.max_load_factor(.321f);
+ const float lf = t.max_load_factor();
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+ StringTable u(std::move(t), Alloc<std::pair<std::string, std::string>>());
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(lf, u.max_load_factor());
+ EXPECT_THAT(*u.find("a"), Pair("a", "b"));
+}
+
+TEST(Table, CopyAssign) {
+ StringTable t;
+ t.max_load_factor(.321f);
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+ StringTable u;
+ u = t;
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
+ EXPECT_THAT(*u.find("a"), Pair("a", "b"));
+}
+
+TEST(Table, CopySelfAssign) {
+ StringTable t;
+ t.max_load_factor(.321f);
+ const float lf = t.max_load_factor();
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+ t = *&t;
+ EXPECT_EQ(1, t.size());
+ EXPECT_EQ(lf, t.max_load_factor());
+ EXPECT_THAT(*t.find("a"), Pair("a", "b"));
+}
+
+TEST(Table, MoveAssign) {
+ StringTable t;
+ t.max_load_factor(.321f);
+ const float lf = t.max_load_factor();
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+ StringTable u;
+ u = std::move(t);
+ EXPECT_EQ(1, u.size());
+ EXPECT_EQ(lf, u.max_load_factor());
+ EXPECT_THAT(*u.find("a"), Pair("a", "b"));
+}
+
+TEST(Table, Equality) {
+ StringTable t;
+ std::vector<std::pair<std::string, std::string>> v = {{"a", "b"}, {"aa", "bb"}};
+ t.insert(std::begin(v), std::end(v));
+ StringTable u = t;
+ EXPECT_EQ(u, t);
+}
+
+TEST(Table, Equality2) {
+ StringTable t;
+ std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"}, {"aa", "bb"}};
+ t.insert(std::begin(v1), std::end(v1));
+ StringTable u;
+ std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"}, {"aa", "aa"}};
+ u.insert(std::begin(v2), std::end(v2));
+ EXPECT_NE(u, t);
+}
+
+TEST(Table, Equality3) {
+ StringTable t;
+ std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"}, {"bb", "bb"}};
+ t.insert(std::begin(v1), std::end(v1));
+ StringTable u;
+ std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"}, {"aa", "aa"}};
+ u.insert(std::begin(v2), std::end(v2));
+ EXPECT_NE(u, t);
+}
+
+TEST(Table, NumDeletedRegression) {
+ IntTable t;
+ t.emplace(0);
+ t.erase(t.find(0));
+ // construct over a deleted slot.
+ t.emplace(0);
+ t.clear();
+}
+
+TEST(Table, FindFullDeletedRegression) {
+ IntTable t;
+ for (int i = 0; i < 1000; ++i) {
+ t.emplace(i);
+ t.erase(t.find(i));
+ }
+ EXPECT_EQ(0, t.size());
+}
+
+TEST(Table, ReplacingDeletedSlotDoesNotRehash) {
+ size_t n;
+ {
+ // Compute n such that n is the maximum number of elements before rehash.
+ IntTable t;
+ t.emplace(0);
+ size_t c = t.bucket_count();
+ for (n = 1; c == t.bucket_count(); ++n) t.emplace(n);
+ --n;
+ }
+ IntTable t;
+ t.rehash(n);
+ const size_t c = t.bucket_count();
+ for (size_t i = 0; i != n; ++i) t.emplace(i);
+ EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
+ t.erase(0);
+ t.emplace(0);
+ EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
+}
+
+TEST(Table, NoThrowMoveConstruct) {
+ ASSERT_TRUE(
+ std::is_nothrow_copy_constructible<absl::Hash<absl::string_view>>::value);
+ ASSERT_TRUE(std::is_nothrow_copy_constructible<
+ std::equal_to<absl::string_view>>::value);
+ ASSERT_TRUE(std::is_nothrow_copy_constructible<std::allocator<int>>::value);
+ EXPECT_TRUE(std::is_nothrow_move_constructible<StringTable>::value);
+}
+
+TEST(Table, NoThrowMoveAssign) {
+ ASSERT_TRUE(
+ std::is_nothrow_move_assignable<absl::Hash<absl::string_view>>::value);
+ ASSERT_TRUE(
+ std::is_nothrow_move_assignable<std::equal_to<absl::string_view>>::value);
+ ASSERT_TRUE(std::is_nothrow_move_assignable<std::allocator<int>>::value);
+ ASSERT_TRUE(
+ absl::allocator_traits<std::allocator<int>>::is_always_equal::value);
+ EXPECT_TRUE(std::is_nothrow_move_assignable<StringTable>::value);
+}
+
+TEST(Table, NoThrowSwappable) {
+ ASSERT_TRUE(
+ container_internal::IsNoThrowSwappable<absl::Hash<absl::string_view>>());
+ ASSERT_TRUE(container_internal::IsNoThrowSwappable<
+ std::equal_to<absl::string_view>>());
+ ASSERT_TRUE(container_internal::IsNoThrowSwappable<std::allocator<int>>());
+ EXPECT_TRUE(container_internal::IsNoThrowSwappable<StringTable>());
+}
+
+TEST(Table, HeterogeneousLookup) {
+ struct Hash {
+ size_t operator()(int64_t i) const { return i; }
+ size_t operator()(double i) const {
+ ADD_FAILURE();
+ return i;
+ }
+ };
+ struct Eq {
+ bool operator()(int64_t a, int64_t b) const { return a == b; }
+ bool operator()(double a, int64_t b) const {
+ ADD_FAILURE();
+ return a == b;
+ }
+ bool operator()(int64_t a, double b) const {
+ ADD_FAILURE();
+ return a == b;
+ }
+ bool operator()(double a, double b) const {
+ ADD_FAILURE();
+ return a == b;
+ }
+ };
+
+ struct THash {
+ using is_transparent = void;
+ size_t operator()(int64_t i) const { return i; }
+ size_t operator()(double i) const { return i; }
+ };
+ struct TEq {
+ using is_transparent = void;
+ bool operator()(int64_t a, int64_t b) const { return a == b; }
+ bool operator()(double a, int64_t b) const { return a == b; }
+ bool operator()(int64_t a, double b) const { return a == b; }
+ bool operator()(double a, double b) const { return a == b; }
+ };
+
+ raw_hash_set<IntPolicy, Hash, Eq, Alloc<int64_t>> s{0, 1, 2};
+ // It will convert to int64_t before the query.
+ EXPECT_EQ(1, *s.find(double{1.1}));
+
+ raw_hash_set<IntPolicy, THash, TEq, Alloc<int64_t>> ts{0, 1, 2};
+ // It will try to use the double, and fail to find the object.
+ EXPECT_TRUE(ts.find(1.1) == ts.end());
+}
+
+template <class Table>
+using CallFind = decltype(std::declval<Table&>().find(17));
+
+template <class Table>
+using CallErase = decltype(std::declval<Table&>().erase(17));
+
+template <class Table>
+using CallExtract = decltype(std::declval<Table&>().extract(17));
+
+template <class Table>
+using CallPrefetch = decltype(std::declval<Table&>().prefetch(17));
+
+template <class Table>
+using CallCount = decltype(std::declval<Table&>().count(17));
+
+template <template <typename> class C, class Table, class = void>
+struct VerifyResultOf : std::false_type {};
+
+template <template <typename> class C, class Table>
+struct VerifyResultOf<C, Table, absl::void_t<C<Table>>> : std::true_type {};
+
+TEST(Table, HeterogeneousLookupOverloads) {
+ using NonTransparentTable =
+ raw_hash_set<StringPolicy, absl::Hash<absl::string_view>,
+ std::equal_to<absl::string_view>, std::allocator<int>>;
+
+ EXPECT_FALSE((VerifyResultOf<CallFind, NonTransparentTable>()));
+ EXPECT_FALSE((VerifyResultOf<CallErase, NonTransparentTable>()));
+ EXPECT_FALSE((VerifyResultOf<CallExtract, NonTransparentTable>()));
+ EXPECT_FALSE((VerifyResultOf<CallPrefetch, NonTransparentTable>()));
+ EXPECT_FALSE((VerifyResultOf<CallCount, NonTransparentTable>()));
+
+ using TransparentTable = raw_hash_set<
+ StringPolicy,
+ absl::container_internal::hash_default_hash<absl::string_view>,
+ absl::container_internal::hash_default_eq<absl::string_view>,
+ std::allocator<int>>;
+
+ EXPECT_TRUE((VerifyResultOf<CallFind, TransparentTable>()));
+ EXPECT_TRUE((VerifyResultOf<CallErase, TransparentTable>()));
+ EXPECT_TRUE((VerifyResultOf<CallExtract, TransparentTable>()));
+ EXPECT_TRUE((VerifyResultOf<CallPrefetch, TransparentTable>()));
+ EXPECT_TRUE((VerifyResultOf<CallCount, TransparentTable>()));
+}
+
+// TODO(alkis): Expand iterator tests.
+TEST(Iterator, IsDefaultConstructible) {
+ StringTable::iterator i;
+ EXPECT_TRUE(i == StringTable::iterator());
+}
+
+TEST(ConstIterator, IsDefaultConstructible) {
+ StringTable::const_iterator i;
+ EXPECT_TRUE(i == StringTable::const_iterator());
+}
+
+TEST(Iterator, ConvertsToConstIterator) {
+ StringTable::iterator i;
+ EXPECT_TRUE(i == StringTable::const_iterator());
+}
+
+TEST(Iterator, Iterates) {
+ IntTable t;
+ for (size_t i = 3; i != 6; ++i) EXPECT_TRUE(t.emplace(i).second);
+ EXPECT_THAT(t, UnorderedElementsAre(3, 4, 5));
+}
+
+TEST(Table, Merge) {
+ StringTable t1, t2;
+ t1.emplace("0", "-0");
+ t1.emplace("1", "-1");
+ t2.emplace("0", "~0");
+ t2.emplace("2", "~2");
+
+ EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1")));
+ EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0"), Pair("2", "~2")));
+
+ t1.merge(t2);
+ EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1"),
+ Pair("2", "~2")));
+ EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0")));
+}
+
+TEST(Nodes, EmptyNodeType) {
+ using node_type = StringTable::node_type;
+ node_type n;
+ EXPECT_FALSE(n);
+ EXPECT_TRUE(n.empty());
+
+ EXPECT_TRUE((std::is_same<node_type::allocator_type,
+ StringTable::allocator_type>::value));
+}
+
+TEST(Nodes, ExtractInsert) {
+ constexpr char k0[] = "Very long std::string zero.";
+ constexpr char k1[] = "Very long std::string one.";
+ constexpr char k2[] = "Very long std::string two.";
+ StringTable t = {{k0, ""}, {k1, ""}, {k2, ""}};
+ EXPECT_THAT(t,
+ UnorderedElementsAre(Pair(k0, ""), Pair(k1, ""), Pair(k2, "")));
+
+ auto node = t.extract(k0);
+ EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
+ EXPECT_TRUE(node);
+ EXPECT_FALSE(node.empty());
+
+ StringTable t2;
+ auto res = t2.insert(std::move(node));
+ EXPECT_TRUE(res.inserted);
+ EXPECT_THAT(*res.position, Pair(k0, ""));
+ EXPECT_FALSE(res.node);
+ EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
+
+ // Not there.
+ EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
+ node = t.extract("Not there!");
+ EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
+ EXPECT_FALSE(node);
+
+ // Inserting nothing.
+ res = t2.insert(std::move(node));
+ EXPECT_FALSE(res.inserted);
+ EXPECT_EQ(res.position, t2.end());
+ EXPECT_FALSE(res.node);
+ EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
+
+ t.emplace(k0, "1");
+ node = t.extract(k0);
+
+ // Insert duplicate.
+ res = t2.insert(std::move(node));
+ EXPECT_FALSE(res.inserted);
+ EXPECT_THAT(*res.position, Pair(k0, ""));
+ EXPECT_TRUE(res.node);
+ EXPECT_FALSE(node);
+}
+
+StringTable MakeSimpleTable(size_t size) {
+ StringTable t;
+ for (size_t i = 0; i < size; ++i) t.emplace(std::string(1, 'A' + i), "");
+ return t;
+}
+
+std::string OrderOfIteration(const StringTable& t) {
+ std::string order;
+ for (auto& p : t) order += p.first;
+ return order;
+}
+
+TEST(Table, IterationOrderChangesByInstance) {
+ // Needs to be more than kWidth elements to be able to affect order.
+ const StringTable reference = MakeSimpleTable(20);
+
+ // Since order is non-deterministic we can't just try once and verify.
+ // We'll try until we find that order changed. It should not take many tries
+ // for that.
+ // Important: we have to keep the old tables around. Otherwise tcmalloc will
+ // just give us the same blocks and we would be doing the same order again.
+ std::vector<StringTable> garbage;
+ for (int i = 0; i < 10; ++i) {
+ auto trial = MakeSimpleTable(20);
+ if (OrderOfIteration(trial) != OrderOfIteration(reference)) {
+ // We are done.
+ return;
+ }
+ garbage.push_back(std::move(trial));
+ }
+ FAIL();
+}
+
+TEST(Table, IterationOrderChangesOnRehash) {
+ // Since order is non-deterministic we can't just try once and verify.
+ // We'll try until we find that order changed. It should not take many tries
+ // for that.
+ // Important: we have to keep the old tables around. Otherwise tcmalloc will
+ // just give us the same blocks and we would be doing the same order again.
+ std::vector<StringTable> garbage;
+ for (int i = 0; i < 10; ++i) {
+ // Needs to be more than kWidth elements to be able to affect order.
+ StringTable t = MakeSimpleTable(20);
+ const std::string reference = OrderOfIteration(t);
+ // Force rehash to the same size.
+ t.rehash(0);
+ std::string trial = OrderOfIteration(t);
+ if (trial != reference) {
+ // We are done.
+ return;
+ }
+ garbage.push_back(std::move(t));
+ }
+ FAIL();
+}
+
+TEST(Table, IterationOrderChangesForSmallTables) {
+ // Since order is non-deterministic we can't just try once and verify.
+ // We'll try until we find that order changed.
+ // Important: we have to keep the old tables around. Otherwise tcmalloc will
+ // just give us the same blocks and we would be doing the same order again.
+ StringTable reference_table = MakeSimpleTable(5);
+ const std::string reference = OrderOfIteration(reference_table);
+ std::vector<StringTable> garbage;
+ for (int i = 0; i < 50; ++i) {
+ StringTable t = MakeSimpleTable(5);
+ std::string trial = OrderOfIteration(t);
+ if (trial != reference) {
+ // We are done.
+ return;
+ }
+ garbage.push_back(std::move(t));
+ }
+ FAIL() << "Iteration order remained the same across many attempts.";
+}
+
+// Confirm that we assert if we try to erase() end().
+TEST(TableDeathTest, EraseOfEndAsserts) {
+ // Use an assert with side-effects to figure out if they are actually enabled.
+ bool assert_enabled = false;
+ assert([&]() {
+ assert_enabled = true;
+ return true;
+ }());
+ if (!assert_enabled) return;
+
+ IntTable t;
+ // Extra simple "regexp" as regexp support is highly varied across platforms.
+ constexpr char kDeathMsg[] = "it != end";
+ EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg);
+}
+
+#ifdef ADDRESS_SANITIZER
+TEST(Sanitizer, PoisoningUnused) {
+ IntTable t;
+ // Insert something to force an allocation.
+ int64_t& v1 = *t.insert(0).first;
+
+ // Make sure there is something to test.
+ ASSERT_GT(t.capacity(), 1);
+
+ int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
+ for (size_t i = 0; i < t.capacity(); ++i) {
+ EXPECT_EQ(slots + i != &v1, __asan_address_is_poisoned(slots + i));
+ }
+}
+
+TEST(Sanitizer, PoisoningOnErase) {
+ IntTable t;
+ int64_t& v = *t.insert(0).first;
+
+ EXPECT_FALSE(__asan_address_is_poisoned(&v));
+ t.erase(0);
+ EXPECT_TRUE(__asan_address_is_poisoned(&v));
+}
+#endif // ADDRESS_SANITIZER
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/test_instance_tracker.cc b/absl/container/internal/test_instance_tracker.cc
index d1aa0978..91441729 100644
--- a/absl/container/internal/test_instance_tracker.cc
+++ b/absl/container/internal/test_instance_tracker.cc
@@ -15,14 +15,15 @@
#include "absl/container/internal/test_instance_tracker.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace test_internal {
int BaseCountedInstance::num_instances_ = 0;
int BaseCountedInstance::num_live_instances_ = 0;
int BaseCountedInstance::num_moves_ = 0;
int BaseCountedInstance::num_copies_ = 0;
int BaseCountedInstance::num_swaps_ = 0;
+int BaseCountedInstance::num_comparisons_ = 0;
} // namespace test_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/container/internal/test_instance_tracker.h b/absl/container/internal/test_instance_tracker.h
index b4a84656..060077d0 100644
--- a/absl/container/internal/test_instance_tracker.h
+++ b/absl/container/internal/test_instance_tracker.h
@@ -19,12 +19,12 @@
#include <ostream>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace test_internal {
// A type that counts number of occurences of the type, the live occurrences of
-// the type, as well as the number of copies, moves, and swaps that have
-// occurred on the type. This is used as a base class for the copyable,
+// 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 {
@@ -67,6 +67,36 @@ class BaseCountedInstance {
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_;
+ }
+
int value() const {
if (!is_live_) std::abort();
return value_;
@@ -109,6 +139,9 @@ class BaseCountedInstance {
// 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
@@ -153,13 +186,21 @@ class InstanceTracker {
// construction or the last call to ResetCopiesMovesSwaps().
int swaps() const { return BaseCountedInstance::num_swaps_ - start_swaps_; }
- // Resets the base values for moves, copies and swaps to the current values,
- // so that subsequent Get*() calls for moves, copies and swaps will compare to
- // the situation at the point of this call.
+ // 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:
@@ -168,6 +209,7 @@ class InstanceTracker {
int start_moves_;
int start_copies_;
int start_swaps_;
+ int start_comparisons_;
};
// Copyable, not movable.
@@ -216,7 +258,7 @@ class MovableOnlyInstance : public BaseCountedInstance {
};
} // namespace test_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
diff --git a/absl/container/internal/test_instance_tracker_test.cc b/absl/container/internal/test_instance_tracker_test.cc
index 9efb6771..0ae57636 100644
--- a/absl/container/internal/test_instance_tracker_test.cc
+++ b/absl/container/internal/test_instance_tracker_test.cc
@@ -157,4 +157,26 @@ TEST(TestInstanceTracker, ExistingInstances) {
EXPECT_EQ(1, tracker.moves());
}
+TEST(TestInstanceTracker, Comparisons) {
+ InstanceTracker tracker;
+ MovableOnlyInstance one(1), two(2);
+
+ EXPECT_EQ(0, tracker.comparisons());
+ EXPECT_FALSE(one == two);
+ EXPECT_EQ(1, tracker.comparisons());
+ EXPECT_TRUE(one != two);
+ EXPECT_EQ(2, tracker.comparisons());
+ EXPECT_TRUE(one < two);
+ EXPECT_EQ(3, tracker.comparisons());
+ EXPECT_FALSE(one > two);
+ EXPECT_EQ(4, tracker.comparisons());
+ EXPECT_TRUE(one <= two);
+ EXPECT_EQ(5, tracker.comparisons());
+ EXPECT_FALSE(one >= two);
+ EXPECT_EQ(6, tracker.comparisons());
+
+ tracker.ResetCopiesMovesSwaps();
+ EXPECT_EQ(0, tracker.comparisons());
+}
+
} // namespace
diff --git a/absl/container/internal/tracked.h b/absl/container/internal/tracked.h
new file mode 100644
index 00000000..f72c46ea
--- /dev/null
+++ b/absl/container/internal/tracked.h
@@ -0,0 +1,80 @@
+// 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
+//
+// 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.
+
+#ifndef ABSL_CONTAINER_INTERNAL_TRACKED_H_
+#define ABSL_CONTAINER_INTERNAL_TRACKED_H_
+
+#include <stddef.h>
+#include <memory>
+#include <utility>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_TRACKED_H_
diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h
new file mode 100644
index 00000000..14ceeecb
--- /dev/null
+++ b/absl/container/internal/unordered_map_constructor_test.h
@@ -0,0 +1,407 @@
+// 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
+//
+// 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.
+
+#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
+#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
+
+#include <algorithm>
+#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 {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+template <class UnordMap>
+class ConstructorTest : public ::testing::Test {};
+
+TYPED_TEST_CASE_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);
+}
+
+TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
+#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+ 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);
+#endif
+}
+
+TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
+#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+ 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);
+#endif
+}
+
+TYPED_TEST_P(ConstructorTest, BucketAlloc) {
+#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+ 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());
+#endif
+}
+
+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::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(items(m), ::testing::UnorderedElementsAreArray(values));
+ EXPECT_GE(m.bucket_count(), 123);
+}
+
+TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
+#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+ 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::Generator<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);
+#endif
+}
+
+TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
+#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+ 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::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(items(m), ::testing::UnorderedElementsAreArray(values));
+ EXPECT_GE(m.bucket_count(), 123);
+#endif
+}
+
+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);
+}
+
+TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
+#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+ 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);
+#endif
+}
+
+// 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);
+}
+
+TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
+#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+ 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);
+#endif
+}
+
+// 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(items(m), ::testing::UnorderedElementsAreArray(values));
+ EXPECT_GE(m.bucket_count(), 123);
+}
+
+TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
+#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+ 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(items(m), ::testing::UnorderedElementsAreArray(values));
+ EXPECT_GE(m.bucket_count(), 123);
+#endif
+}
+
+TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
+#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+ 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(items(m), ::testing::UnorderedElementsAreArray(values));
+ EXPECT_GE(m.bucket_count(), 123);
+#endif
+}
+
+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::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(items(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(items(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(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_CASE_P(
+ ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual,
+ BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc,
+ BucketAlloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
+ InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc,
+ MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc,
+ InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment,
+ MoveAssignment, AssignmentFromInitializerList,
+ AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting,
+ AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
+
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
diff --git a/absl/container/internal/unordered_map_lookup_test.h b/absl/container/internal/unordered_map_lookup_test.h
new file mode 100644
index 00000000..d767aa8d
--- /dev/null
+++ b/absl/container/internal/unordered_map_lookup_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
+//
+// 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.
+
+#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 {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+template <class UnordMap>
+class LookupTest : public ::testing::Test {};
+
+TYPED_TEST_CASE_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_CASE_P(LookupTest, At, OperatorBracket, Count, Find,
+ EqualRange);
+
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_
diff --git a/absl/container/internal/unordered_map_modifiers_test.h b/absl/container/internal/unordered_map_modifiers_test.h
new file mode 100644
index 00000000..5d7f1fe3
--- /dev/null
+++ b/absl/container/internal/unordered_map_modifiers_test.h
@@ -0,0 +1,275 @@
+// 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
+//
+// 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.
+
+#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
+#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_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 {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+template <class UnordMap>
+class ModifiersTest : public ::testing::Test {};
+
+TYPED_TEST_CASE_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, 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_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
+ InsertRange, InsertOrAssign, InsertOrAssignHint,
+ Emplace, EmplaceHint, TryEmplace, TryEmplaceHint,
+ Erase, EraseRange, EraseKey, Swap);
+
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
diff --git a/absl/container/internal/unordered_map_test.cc b/absl/container/internal/unordered_map_test.cc
new file mode 100644
index 00000000..548f69f7
--- /dev/null
+++ b/absl/container/internal/unordered_map_test.cc
@@ -0,0 +1,40 @@
+// 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
+//
+// 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.
+
+#include <unordered_map>
+
+#include "absl/container/internal/unordered_map_constructor_test.h"
+#include "absl/container/internal/unordered_map_lookup_test.h"
+#include "absl/container/internal/unordered_map_modifiers_test.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using MapTypes = ::testing::Types<
+ std::unordered_map<int, int, StatefulTestingHash, StatefulTestingEqual,
+ Alloc<std::pair<const int, int>>>,
+ std::unordered_map<std::string, std::string, StatefulTestingHash,
+ StatefulTestingEqual,
+ Alloc<std::pair<const std::string, std::string>>>>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, ConstructorTest, MapTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, LookupTest, MapTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, ModifiersTest, MapTypes);
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/internal/unordered_set_constructor_test.h b/absl/container/internal/unordered_set_constructor_test.h
new file mode 100644
index 00000000..f370b249
--- /dev/null
+++ b/absl/container/internal/unordered_set_constructor_test.h
@@ -0,0 +1,411 @@
+// 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
+//
+// 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.
+
+#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
+#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
+
+#include <algorithm>
+#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 {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+template <class UnordMap>
+class ConstructorTest : public ::testing::Test {};
+
+TYPED_TEST_CASE_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);
+}
+
+TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
+#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+ 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);
+#endif
+}
+
+TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
+#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+ 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);
+#endif
+}
+
+TYPED_TEST_P(ConstructorTest, BucketAlloc) {
+#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+ 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());
+#endif
+}
+
+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);
+}
+
+TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
+#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+ 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);
+#endif
+}
+
+TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
+#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+ 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);
+#endif
+}
+
+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);
+}
+
+TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
+#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+ 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);
+#endif
+}
+
+// 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);
+}
+
+TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
+#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+ 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);
+#endif
+}
+
+// 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);
+}
+
+TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
+#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+ 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);
+#endif
+}
+
+TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
+#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+ 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);
+#endif
+}
+
+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::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_CASE_P(
+ ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual,
+ BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc,
+ BucketAlloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
+ InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc,
+ MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc,
+ InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment,
+ MoveAssignment, AssignmentFromInitializerList,
+ AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting,
+ AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
+
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
diff --git a/absl/container/internal/unordered_set_lookup_test.h b/absl/container/internal/unordered_set_lookup_test.h
new file mode 100644
index 00000000..9174279a
--- /dev/null
+++ b/absl/container/internal/unordered_set_lookup_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
+//
+// 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.
+
+#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 {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+template <class UnordSet>
+class LookupTest : public ::testing::Test {};
+
+TYPED_TEST_CASE_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_CASE_P(LookupTest, Count, Find, EqualRange);
+
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
diff --git a/absl/container/internal/unordered_set_modifiers_test.h b/absl/container/internal/unordered_set_modifiers_test.h
new file mode 100644
index 00000000..0a1e9b1b
--- /dev/null
+++ b/absl/container/internal/unordered_set_modifiers_test.h
@@ -0,0 +1,190 @@
+// 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
+//
+// 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.
+
+#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 {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+
+template <class UnordSet>
+class ModifiersTest : public ::testing::Test {};
+
+TYPED_TEST_CASE_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, 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_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
+ InsertRange, Emplace, EmplaceHint, Erase, EraseRange,
+ EraseKey, Swap);
+
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
diff --git a/absl/container/internal/unordered_set_test.cc b/absl/container/internal/unordered_set_test.cc
new file mode 100644
index 00000000..263059eb
--- /dev/null
+++ b/absl/container/internal/unordered_set_test.cc
@@ -0,0 +1,39 @@
+// 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
+//
+// 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.
+
+#include <unordered_set>
+
+#include "absl/container/internal/unordered_set_constructor_test.h"
+#include "absl/container/internal/unordered_set_lookup_test.h"
+#include "absl/container/internal/unordered_set_modifiers_test.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using SetTypes =
+ ::testing::Types<std::unordered_set<int, StatefulTestingHash,
+ StatefulTestingEqual, Alloc<int>>,
+ std::unordered_set<std::string, StatefulTestingHash,
+ StatefulTestingEqual, Alloc<std::string>>>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, ConstructorTest, SetTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, LookupTest, SetTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, ModifiersTest, SetTypes);
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h
new file mode 100644
index 00000000..48c7752e
--- /dev/null
+++ b/absl/container/node_hash_map.h
@@ -0,0 +1,584 @@
+// 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
+//
+// 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.
+//
+// -----------------------------------------------------------------------------
+// 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/container/internal/container_memory.h"
+#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
+#include "absl/container/internal/node_hash_policy.h"
+#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export
+#include "absl/memory/memory.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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.
+//
+// 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.
+ 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 init_type& k, Args&&... args):
+ // iterator try_emplace(const_iterator hint, init_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.
+ 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.
+ 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;
+
+ ABSL_DEPRECATED("Call `hash_function()` instead.")
+ typename Base::hasher hash_funct() { return this->hash_function(); }
+
+ ABSL_DEPRECATED("Call `rehash()` instead.")
+ void resize(typename Base::size_type hint) { this->rehash(hint); }
+};
+
+namespace container_internal {
+
+template <class Key, class Value>
+class NodeHashMapPolicy
+ : public absl::container_internal::node_hash_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
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_NODE_HASH_MAP_H_
diff --git a/absl/container/node_hash_map_test.cc b/absl/container/node_hash_map_test.cc
new file mode 100644
index 00000000..76a387b8
--- /dev/null
+++ b/absl/container/node_hash_map_test.cc
@@ -0,0 +1,220 @@
+// 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
+//
+// 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.
+
+#include "absl/container/node_hash_map.h"
+
+#include "absl/container/internal/tracked.h"
+#include "absl/container/internal/unordered_map_constructor_test.h"
+#include "absl/container/internal/unordered_map_lookup_test.h"
+#include "absl/container/internal/unordered_map_modifiers_test.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+
+using ::testing::Field;
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+
+using MapTypes = ::testing::Types<
+ absl::node_hash_map<int, int, StatefulTestingHash, StatefulTestingEqual,
+ Alloc<std::pair<const int, int>>>,
+ absl::node_hash_map<std::string, std::string, StatefulTestingHash,
+ StatefulTestingEqual,
+ Alloc<std::pair<const std::string, std::string>>>>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, ConstructorTest, MapTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, LookupTest, MapTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, ModifiersTest, MapTypes);
+
+using M = absl::node_hash_map<std::string, Tracked<int>>;
+
+TEST(NodeHashMap, Emplace) {
+ M m;
+ Tracked<int> t(53);
+ m.emplace("a", t);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(1, t.num_copies());
+
+ m.emplace(std::string("a"), t);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(1, t.num_copies());
+
+ std::string a("a");
+ m.emplace(a, t);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(1, t.num_copies());
+
+ const std::string ca("a");
+ m.emplace(a, t);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(1, t.num_copies());
+
+ m.emplace(std::make_pair("a", t));
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(2, t.num_copies());
+
+ m.emplace(std::make_pair(std::string("a"), t));
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(3, t.num_copies());
+
+ std::pair<std::string, Tracked<int>> p("a", t);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(4, t.num_copies());
+ m.emplace(p);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(4, t.num_copies());
+
+ const std::pair<std::string, Tracked<int>> cp("a", t);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(5, t.num_copies());
+ m.emplace(cp);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(5, t.num_copies());
+
+ std::pair<const std::string, Tracked<int>> pc("a", t);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(6, t.num_copies());
+ m.emplace(pc);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(6, t.num_copies());
+
+ const std::pair<const std::string, Tracked<int>> cpc("a", t);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(7, t.num_copies());
+ m.emplace(cpc);
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(7, t.num_copies());
+
+ m.emplace(std::piecewise_construct, std::forward_as_tuple("a"),
+ std::forward_as_tuple(t));
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(7, t.num_copies());
+
+ m.emplace(std::piecewise_construct, std::forward_as_tuple(std::string("a")),
+ std::forward_as_tuple(t));
+ ASSERT_EQ(0, t.num_moves());
+ ASSERT_EQ(7, t.num_copies());
+}
+
+TEST(NodeHashMap, AssignRecursive) {
+ struct Tree {
+ // Verify that unordered_map<K, IncompleteType> can be instantiated.
+ absl::node_hash_map<int, Tree> children;
+ };
+ Tree root;
+ const Tree& child = root.children.emplace().first->second;
+ // Verify that `lhs = rhs` doesn't read rhs after clearing lhs.
+ root = child;
+}
+
+TEST(FlatHashMap, MoveOnlyKey) {
+ struct Key {
+ Key() = default;
+ Key(Key&&) = default;
+ Key& operator=(Key&&) = default;
+ };
+ struct Eq {
+ bool operator()(const Key&, const Key&) const { return true; }
+ };
+ struct Hash {
+ size_t operator()(const Key&) const { return 0; }
+ };
+ absl::node_hash_map<Key, int, Hash, Eq> m;
+ m[Key()];
+}
+
+struct NonMovableKey {
+ explicit NonMovableKey(int i) : i(i) {}
+ NonMovableKey(NonMovableKey&&) = delete;
+ int i;
+};
+struct NonMovableKeyHash {
+ using is_transparent = void;
+ size_t operator()(const NonMovableKey& k) const { return k.i; }
+ size_t operator()(int k) const { return k; }
+};
+struct NonMovableKeyEq {
+ using is_transparent = void;
+ bool operator()(const NonMovableKey& a, const NonMovableKey& b) const {
+ return a.i == b.i;
+ }
+ bool operator()(const NonMovableKey& a, int b) const { return a.i == b; }
+};
+
+TEST(NodeHashMap, MergeExtractInsert) {
+ absl::node_hash_map<NonMovableKey, int, NonMovableKeyHash, NonMovableKeyEq>
+ set1, set2;
+ set1.emplace(std::piecewise_construct, std::make_tuple(7),
+ std::make_tuple(-7));
+ set1.emplace(std::piecewise_construct, std::make_tuple(17),
+ std::make_tuple(-17));
+
+ set2.emplace(std::piecewise_construct, std::make_tuple(7),
+ std::make_tuple(-70));
+ set2.emplace(std::piecewise_construct, std::make_tuple(19),
+ std::make_tuple(-190));
+
+ auto Elem = [](int key, int value) {
+ return Pair(Field(&NonMovableKey::i, key), value);
+ };
+
+ EXPECT_THAT(set1, UnorderedElementsAre(Elem(7, -7), Elem(17, -17)));
+ EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70), Elem(19, -190)));
+
+ // NonMovableKey is neither copyable nor movable. We should still be able to
+ // move nodes around.
+ static_assert(!std::is_move_constructible<NonMovableKey>::value, "");
+ set1.merge(set2);
+
+ EXPECT_THAT(set1,
+ UnorderedElementsAre(Elem(7, -7), Elem(17, -17), Elem(19, -190)));
+ EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70)));
+
+ auto node = set1.extract(7);
+ EXPECT_TRUE(node);
+ EXPECT_EQ(node.key().i, 7);
+ EXPECT_EQ(node.mapped(), -7);
+ EXPECT_THAT(set1, UnorderedElementsAre(Elem(17, -17), Elem(19, -190)));
+
+ auto insert_result = set2.insert(std::move(node));
+ EXPECT_FALSE(node);
+ EXPECT_FALSE(insert_result.inserted);
+ EXPECT_TRUE(insert_result.node);
+ EXPECT_EQ(insert_result.node.key().i, 7);
+ EXPECT_EQ(insert_result.node.mapped(), -7);
+ EXPECT_THAT(*insert_result.position, Elem(7, -70));
+ EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70)));
+
+ node = set1.extract(17);
+ EXPECT_TRUE(node);
+ EXPECT_EQ(node.key().i, 17);
+ EXPECT_EQ(node.mapped(), -17);
+ EXPECT_THAT(set1, UnorderedElementsAre(Elem(19, -190)));
+
+ node.mapped() = 23;
+
+ insert_result = set2.insert(std::move(node));
+ EXPECT_FALSE(node);
+ EXPECT_TRUE(insert_result.inserted);
+ EXPECT_FALSE(insert_result.node);
+ EXPECT_THAT(*insert_result.position, Elem(17, 23));
+ EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70), Elem(17, 23)));
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h
new file mode 100644
index 00000000..c4179195
--- /dev/null
+++ b/absl/container/node_hash_set.h
@@ -0,0 +1,490 @@
+// 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
+//
+// 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.
+//
+// -----------------------------------------------------------------------------
+// File: node_hash_set.h
+// -----------------------------------------------------------------------------
+//
+// An `absl::node_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 map elements can be done
+// as an `O(1)` operation. However, `node_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 table should be a map of type
+// `flat_hash_map` or a set of type `flat_hash_set`. However, if you need
+// pointer stability, a `node_hash_set` should be your preferred choice. As
+// well, if you are migrating your code from using `std::unordered_set`, a
+// `node_hash_set` should be an easy migration. Consider migrating to
+// `node_hash_set` and perhaps converting to a more efficient `flat_hash_set`
+// upon further review.
+
+#ifndef ABSL_CONTAINER_NODE_HASH_SET_H_
+#define ABSL_CONTAINER_NODE_HASH_SET_H_
+
+#include <type_traits>
+
+#include "absl/algorithm/container.h"
+#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
+#include "absl/container/internal/node_hash_policy.h"
+#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
+#include "absl/memory/memory.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+template <typename T>
+struct NodeHashSetPolicy;
+} // namespace container_internal
+
+// -----------------------------------------------------------------------------
+// absl::node_hash_set
+// -----------------------------------------------------------------------------
+//
+// An `absl::node_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:
+//
+// * 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 set.
+// * Returns `void` from the `erase(iterator)` overload.
+//
+// By default, `node_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 `node_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.
+//
+// Example:
+//
+// // Create a node hash set of three strings
+// absl::node_hash_map<std::string, std::string> ducks =
+// {"huey", "dewey"}, "louie"};
+//
+// // Insert a new element into the node hash map
+// ducks.insert("donald"};
+//
+// // Force a rehash of the node hash map
+// 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 Alloc = std::allocator<T>>
+class node_hash_set
+ : public absl::container_internal::raw_hash_set<
+ absl::container_internal::NodeHashSetPolicy<T>, Hash, Eq, Alloc> {
+ using Base = typename node_hash_set::raw_hash_set;
+
+ public:
+ // Constructors and Assignment Operators
+ //
+ // A node_hash_set 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_set<std::string> set1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::node_hash_set<std::string> set2 =
+ // {{"huey"}, {"dewey"}, {"louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::node_hash_set<std::string> set3(set2);
+ //
+ // * Copy assignment operator
+ //
+ // // Hash functor and Comparator are copied as well
+ // absl::node_hash_set<std::string> set4;
+ // set4 = set3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::node_hash_set<std::string> set5(std::move(set4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::node_hash_set<std::string> set6;
+ // set6 = std::move(set5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::string> v = {"a", "b"};
+ // absl::node_hash_set<std::string> set7(v.begin(), v.end());
+ node_hash_set() {}
+ using Base::Base;
+
+ // node_hash_set::begin()
+ //
+ // Returns an iterator to the beginning of the `node_hash_set`.
+ using Base::begin;
+
+ // node_hash_set::cbegin()
+ //
+ // Returns a const iterator to the beginning of the `node_hash_set`.
+ using Base::cbegin;
+
+ // node_hash_set::cend()
+ //
+ // Returns a const iterator to the end of the `node_hash_set`.
+ using Base::cend;
+
+ // node_hash_set::end()
+ //
+ // Returns an iterator to the end of the `node_hash_set`.
+ using Base::end;
+
+ // node_hash_set::capacity()
+ //
+ // Returns the number of element slots (assigned, deleted, and empty)
+ // available within the `node_hash_set`.
+ //
+ // NOTE: this member function is particular to `absl::node_hash_set` and is
+ // not provided in the `std::unordered_map` API.
+ using Base::capacity;
+
+ // node_hash_set::empty()
+ //
+ // Returns whether or not the `node_hash_set` is empty.
+ using Base::empty;
+
+ // node_hash_set::max_size()
+ //
+ // Returns the largest theoretical possible number of elements within a
+ // `node_hash_set` under current memory constraints. This value can be thought
+ // of the largest value of `std::distance(begin(), end())` for a
+ // `node_hash_set<T>`.
+ using Base::max_size;
+
+ // node_hash_set::size()
+ //
+ // Returns the number of elements currently within the `node_hash_set`.
+ using Base::size;
+
+ // node_hash_set::clear()
+ //
+ // Removes all elements from the `node_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;
+
+ // node_hash_set::erase()
+ //
+ // Erases elements within the `node_hash_set`. Erasing does not trigger a
+ // rehash. Overloads are listed below.
+ //
+ // void erase(const_iterator pos):
+ //
+ // Erases the element at `position` of the `node_hash_set`, 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.
+ using Base::erase;
+
+ // node_hash_set::insert()
+ //
+ // Inserts an element of the specified value into the `node_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 `node_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 `node_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 `node_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
+ // `node_hash_set` we guarantee the first match is inserted.
+ using Base::insert;
+
+ // node_hash_set::emplace()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `node_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;
+
+ // node_hash_set::emplace_hint()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `node_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;
+
+ // node_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 `node_hash_set`
+ // does not contain an element with a matching key, this function returns an
+ // empty node handle.
+ using Base::extract;
+
+ // node_hash_set::merge()
+ //
+ // Extracts elements from a given `source` flat hash map into this
+ // `node_hash_set`. If the destination `node_hash_set` already contains an
+ // element with an equivalent key, that element is not extracted.
+ using Base::merge;
+
+ // node_hash_set::swap(node_hash_set& other)
+ //
+ // Exchanges the contents of this `node_hash_set` 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 `node_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 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_set::rehash(count)
+ //
+ // Rehashes the `node_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;
+
+ // node_hash_set::reserve(count)
+ //
+ // Sets the number of slots in the `node_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;
+
+ // node_hash_set::contains()
+ //
+ // Determines whether an element comparing equal to the given `key` exists
+ // within the `node_hash_set`, returning `true` if so or `false` otherwise.
+ using Base::contains;
+
+ // node_hash_set::count(const Key& key) const
+ //
+ // Returns the number of elements comparing equal to the given `key` within
+ // the `node_hash_set`. note that this function will return either `1` or `0`
+ // since duplicate elements are not allowed within a `node_hash_set`.
+ using Base::count;
+
+ // node_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
+ // `node_hash_set`.
+ using Base::equal_range;
+
+ // node_hash_set::find()
+ //
+ // Finds an element with the passed `key` within the `node_hash_set`.
+ using Base::find;
+
+ // node_hash_set::bucket_count()
+ //
+ // Returns the number of "buckets" within the `node_hash_set`. Note that
+ // because a flat hash map contains all elements within its internal storage,
+ // this value simply equals the current capacity of the `node_hash_set`.
+ using Base::bucket_count;
+
+ // node_hash_set::load_factor()
+ //
+ // Returns the current load factor of the `node_hash_set` (the average number
+ // of slots occupied with a value within the hash map).
+ using Base::load_factor;
+
+ // node_hash_set::max_load_factor()
+ //
+ // Manages the maximum load factor of the `node_hash_set`. Overloads are
+ // listed below.
+ //
+ // float node_hash_set::max_load_factor()
+ //
+ // Returns the current maximum load factor of the `node_hash_set`.
+ //
+ // void node_hash_set::max_load_factor(float ml)
+ //
+ // Sets the maximum load factor of the `node_hash_set` to the passed value.
+ //
+ // NOTE: This overload is provided only for API compatibility with the STL;
+ // `node_hash_set` will ignore any set load factor and manage its rehashing
+ // internally as an implementation detail.
+ using Base::max_load_factor;
+
+ // node_hash_set::get_allocator()
+ //
+ // Returns the allocator function associated with this `node_hash_set`.
+ using Base::get_allocator;
+
+ // node_hash_set::hash_function()
+ //
+ // Returns the hashing function used to hash the keys within this
+ // `node_hash_set`.
+ using Base::hash_function;
+
+ // node_hash_set::key_eq()
+ //
+ // Returns the function used for comparing keys equality.
+ using Base::key_eq;
+
+ ABSL_DEPRECATED("Call `hash_function()` instead.")
+ typename Base::hasher hash_funct() { return this->hash_function(); }
+
+ ABSL_DEPRECATED("Call `rehash()` instead.")
+ void resize(typename Base::size_type hint) { this->rehash(hint); }
+};
+
+namespace container_internal {
+
+template <class T>
+struct NodeHashSetPolicy
+ : absl::container_internal::node_hash_policy<T&, NodeHashSetPolicy<T>> {
+ using key_type = T;
+ using init_type = T;
+ using constant_iterators = std::true_type;
+
+ template <class Allocator, class... Args>
+ static T* new_element(Allocator* alloc, Args&&... args) {
+ using ValueAlloc =
+ typename absl::allocator_traits<Allocator>::template rebind_alloc<T>;
+ ValueAlloc value_alloc(*alloc);
+ T* res = absl::allocator_traits<ValueAlloc>::allocate(value_alloc, 1);
+ absl::allocator_traits<ValueAlloc>::construct(value_alloc, res,
+ std::forward<Args>(args)...);
+ return res;
+ }
+
+ template <class Allocator>
+ static void delete_element(Allocator* alloc, T* elem) {
+ using ValueAlloc =
+ typename absl::allocator_traits<Allocator>::template rebind_alloc<T>;
+ ValueAlloc value_alloc(*alloc);
+ absl::allocator_traits<ValueAlloc>::destroy(value_alloc, elem);
+ absl::allocator_traits<ValueAlloc>::deallocate(value_alloc, elem, 1);
+ }
+
+ 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 element_space_used(const T*) { return sizeof(T); }
+};
+} // 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::node_hash_set<Key, Hash, KeyEqual, Allocator>>
+ : std::true_type {};
+
+} // namespace container_algorithm_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_CONTAINER_NODE_HASH_SET_H_
diff --git a/absl/container/node_hash_set_test.cc b/absl/container/node_hash_set_test.cc
new file mode 100644
index 00000000..59f25285
--- /dev/null
+++ b/absl/container/node_hash_set_test.cc
@@ -0,0 +1,105 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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.
+
+#include "absl/container/node_hash_set.h"
+
+#include "absl/container/internal/unordered_set_constructor_test.h"
+#include "absl/container/internal/unordered_set_lookup_test.h"
+#include "absl/container/internal/unordered_set_modifiers_test.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace container_internal {
+namespace {
+using ::absl::container_internal::hash_internal::Enum;
+using ::absl::container_internal::hash_internal::EnumClass;
+using ::testing::Pointee;
+using ::testing::UnorderedElementsAre;
+
+using SetTypes = ::testing::Types<
+ node_hash_set<int, StatefulTestingHash, StatefulTestingEqual, Alloc<int>>,
+ node_hash_set<std::string, StatefulTestingHash, StatefulTestingEqual,
+ Alloc<int>>,
+ node_hash_set<Enum, StatefulTestingHash, StatefulTestingEqual, Alloc<Enum>>,
+ node_hash_set<EnumClass, StatefulTestingHash, StatefulTestingEqual,
+ Alloc<EnumClass>>>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, ConstructorTest, SetTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, LookupTest, SetTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, ModifiersTest, SetTypes);
+
+TEST(NodeHashSet, MoveableNotCopyableCompiles) {
+ node_hash_set<std::unique_ptr<void*>> t;
+ node_hash_set<std::unique_ptr<void*>> u;
+ u = std::move(t);
+}
+
+TEST(NodeHashSet, MergeExtractInsert) {
+ struct Hash {
+ size_t operator()(const std::unique_ptr<int>& p) const { return *p; }
+ };
+ struct Eq {
+ bool operator()(const std::unique_ptr<int>& a,
+ const std::unique_ptr<int>& b) const {
+ return *a == *b;
+ }
+ };
+ absl::node_hash_set<std::unique_ptr<int>, Hash, Eq> set1, set2;
+ set1.insert(absl::make_unique<int>(7));
+ set1.insert(absl::make_unique<int>(17));
+
+ set2.insert(absl::make_unique<int>(7));
+ set2.insert(absl::make_unique<int>(19));
+
+ EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17)));
+ EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(19)));
+
+ set1.merge(set2);
+
+ EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17), Pointee(19)));
+ EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7)));
+
+ auto node = set1.extract(absl::make_unique<int>(7));
+ EXPECT_TRUE(node);
+ EXPECT_THAT(node.value(), Pointee(7));
+ EXPECT_THAT(set1, UnorderedElementsAre(Pointee(17), Pointee(19)));
+
+ auto insert_result = set2.insert(std::move(node));
+ EXPECT_FALSE(node);
+ EXPECT_FALSE(insert_result.inserted);
+ EXPECT_TRUE(insert_result.node);
+ EXPECT_THAT(insert_result.node.value(), Pointee(7));
+ EXPECT_EQ(**insert_result.position, 7);
+ EXPECT_NE(insert_result.position->get(), insert_result.node.value().get());
+ EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7)));
+
+ node = set1.extract(absl::make_unique<int>(17));
+ EXPECT_TRUE(node);
+ EXPECT_THAT(node.value(), Pointee(17));
+ EXPECT_THAT(set1, UnorderedElementsAre(Pointee(19)));
+
+ node.value() = absl::make_unique<int>(23);
+
+ insert_result = set2.insert(std::move(node));
+ EXPECT_FALSE(node);
+ EXPECT_TRUE(insert_result.inserted);
+ EXPECT_FALSE(insert_result.node);
+ EXPECT_EQ(**insert_result.position, 23);
+ EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23)));
+}
+
+} // namespace
+} // namespace container_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/copts.bzl b/absl/copts.bzl
index 20c9b619..49c4c9e0 100644
--- a/absl/copts.bzl
+++ b/absl/copts.bzl
@@ -3,6 +3,8 @@
Flags specified here must not impact ABI. Code compiled with and without these
opts will be linked together, and in some cases headers compiled with and
without these options will be part of the same program.
+
+DO NOT CHANGE THIS FILE WITHOUT CHANGING THE SAME FLAG IN absl/CMake/AbseilConfigureCopts.cmake!!
"""
GCC_FLAGS = [
"-Wall",
@@ -31,12 +33,10 @@ GCC_TEST_FLAGS = [
"-Wno-unused-private-field",
]
-
# Docs on single flags is preceded by a comment.
# Docs on groups of flags is preceded by ###.
LLVM_FLAGS = [
- # All warnings are treated as errors by implicit -Werror flag
"-Wall",
"-Wextra",
"-Weverything",
@@ -55,6 +55,10 @@ LLVM_FLAGS = [
"-Wno-packed",
"-Wno-padded",
###
+ # Google style does not use unsigned integers, though STL containers
+ # have unsigned types.
+ "-Wno-sign-compare",
+ ###
"-Wno-float-conversion",
"-Wno-float-equal",
"-Wno-format-nonliteral",
@@ -102,6 +106,7 @@ LLVM_TEST_FLAGS = [
"-Wno-c99-extensions",
"-Wno-missing-noreturn",
"-Wno-missing-prototypes",
+ "-Wno-missing-variable-declarations",
"-Wno-null-conversion",
"-Wno-shadow",
"-Wno-shift-sign-overflow",
@@ -113,19 +118,25 @@ LLVM_TEST_FLAGS = [
"-Wno-unused-template",
"-Wno-used-but-marked-unused",
"-Wno-zero-as-null-pointer-constant",
+ # gtest depends on this GNU extension being offered.
+ "-Wno-gnu-zero-variadic-macro-arguments",
]
MSVC_FLAGS = [
"/W3",
- "/WX",
"/wd4005", # macro-redefinition
"/wd4068", # unknown pragma
+ "/wd4180", # qualifier applied to function type has no meaning; ignored
"/wd4244", # conversion from 'type1' to 'type2', possible loss of data
"/wd4267", # conversion from 'size_t' to 'type', possible loss of data
"/wd4800", # forcing value to bool 'true' or 'false' (performance warning)
"/DNOMINMAX", # Don't define min and max macros (windows.h)
"/DWIN32_LEAN_AND_MEAN", # Don't bloat namespace with incompatible winsock versions.
- "/D_CRT_SECURE_NO_WARNINGS", # Don't warn about usage of insecure C functions
+ "/D_CRT_SECURE_NO_WARNINGS", # Don't warn about usage of insecure C functions.
+ "/D_SCL_SECURE_NO_WARNINGS", # Don't warm when the compiler encounters a function or
+ # variable that is marked as deprecated (same as /wd4996).
+ "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", # Introduced in VS 2017 15.8,
+ # before the member type would non-conformingly have an alignment of only alignof(max_align_t).
]
MSVC_TEST_FLAGS = [
@@ -153,3 +164,7 @@ ABSL_EXCEPTIONS_FLAG = select({
"//absl:windows": ["/U_HAS_EXCEPTIONS", "/D_HAS_EXCEPTIONS=1", "/EHsc"],
"//conditions:default": ["-fexceptions"],
})
+
+ABSL_EXCEPTIONS_FLAG_LINKOPTS = select({
+ "//conditions:default": [],
+})
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index e1e7fced..a8ebaea4 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -149,6 +149,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
deps = [
"//absl/base",
+ "//absl/base:core_headers",
"//absl/base:dynamic_annotations",
],
)
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt
index 4af2ec8a..f66688ba 100644
--- a/absl/debugging/CMakeLists.txt
+++ b/absl/debugging/CMakeLists.txt
@@ -14,204 +14,297 @@
# limitations under the License.
#
-list(APPEND DEBUGGING_PUBLIC_HEADERS
- "failure_signal_handler.h"
- "leak_check.h"
- "stacktrace.h"
- "symbolize.h"
-)
-
-# TODO(cohenjon) The below is all kinds of wrong. Make this match what we do in
-# Bazel
-list(APPEND DEBUGGING_INTERNAL_HEADERS
- "internal/address_is_readable.h"
- "internal/demangle.h"
- "internal/elf_mem_image.h"
- "internal/examine_stack.h"
- "internal/stacktrace_config.h"
- "internal/symbolize.h"
- "internal/vdso_support.h"
-)
-
-list(APPEND DEBUGGING_INTERNAL_SRC
- "internal/address_is_readable.cc"
- "internal/elf_mem_image.cc"
- "internal/vdso_support.cc"
-)
-
-
-list(APPEND STACKTRACE_SRC
- "stacktrace.cc"
- ${DEBUGGING_INTERNAL_SRC}
- ${DEBUGGING_PUBLIC_HEADERS}
- ${DEBUGGING_INTERNAL_HEADERS}
+absl_cc_library(
+ NAME
+ stacktrace
+ HDRS
+ "stacktrace.h"
+ SRCS
+ "stacktrace.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::debugging_internal
+ absl::base
+ absl::core_headers
+ PUBLIC
)
-list(APPEND SYMBOLIZE_SRC
- "symbolize.cc"
- "symbolize_elf.inc"
- "symbolize_unimplemented.inc"
- "symbolize_win32.inc"
- "internal/demangle.cc"
- ${DEBUGGING_PUBLIC_HEADERS}
- ${DEBUGGING_INTERNAL_HEADERS}
- ${DEBUGGING_INTERNAL_SRC}
+absl_cc_library(
+ NAME
+ symbolize
+ HDRS
+ "symbolize.h"
+ "internal/symbolize.h"
+ SRCS
+ "symbolize.cc"
+ "symbolize_elf.inc"
+ "symbolize_unimplemented.inc"
+ "symbolize_win32.inc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::debugging_internal
+ absl::demangle_internal
+ absl::base
+ absl::core_headers
+ absl::malloc_internal
+ PUBLIC
)
-list(APPEND FAILURE_SIGNAL_HANDLER_SRC
- "failure_signal_handler.cc"
- ${DEBUGGING_PUBLIC_HEADERS}
+absl_cc_test(
+ NAME
+ symbolize_test
+ SRCS
+ "symbolize_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::stack_consumption
+ absl::symbolize
+ absl::base
+ absl::core_headers
+ absl::memory
+ gmock
)
-list(APPEND EXAMINE_STACK_SRC
- "internal/examine_stack.cc"
- ${DEBUGGING_PUBLIC_HEADERS}
- ${DEBUGGING_INTERNAL_HEADERS}
+absl_cc_library(
+ NAME
+ examine_stack
+ HDRS
+ "internal/examine_stack.h"
+ SRCS
+ "internal/examine_stack.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::stacktrace
+ absl::symbolize
+ absl::base
+ absl::core_headers
)
-absl_library(
- TARGET
- absl_stacktrace
- SOURCES
- ${STACKTRACE_SRC}
- EXPORT_NAME
- stacktrace
+absl_cc_library(
+ NAME
+ failure_signal_handler
+ HDRS
+ "failure_signal_handler.h"
+ SRCS
+ "failure_signal_handler.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::examine_stack
+ absl::stacktrace
+ absl::base
+ absl::config
+ absl::core_headers
+ PUBLIC
)
-absl_library(
- TARGET
- absl_symbolize
- SOURCES
- ${SYMBOLIZE_SRC}
- PUBLIC_LIBRARIES
+absl_cc_test(
+ NAME
+ failure_signal_handler_test
+ SRCS
+ "failure_signal_handler_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::failure_signal_handler
+ absl::stacktrace
+ absl::symbolize
absl::base
- absl_malloc_internal
- EXPORT_NAME
- symbolize
+ absl::strings
+ Threads::Threads
+ gmock
)
-absl_library(
- TARGET
- absl_failure_signal_handler
- SOURCES
- ${FAILURE_SIGNAL_HANDLER_SRC}
- PUBLIC_LIBRARIES
- absl_base absl::examine_stack absl::stacktrace absl_synchronization
- EXPORT_NAME
- failure_signal_handler
+absl_cc_library(
+ NAME
+ debugging_internal
+ HDRS
+ "internal/address_is_readable.h"
+ "internal/elf_mem_image.h"
+ "internal/stacktrace_aarch64-inl.inc"
+ "internal/stacktrace_arm-inl.inc"
+ "internal/stacktrace_config.h"
+ "internal/stacktrace_generic-inl.inc"
+ "internal/stacktrace_powerpc-inl.inc"
+ "internal/stacktrace_unimplemented-inl.inc"
+ "internal/stacktrace_win32-inl.inc"
+ "internal/stacktrace_x86-inl.inc"
+ "internal/vdso_support.h"
+ SRCS
+ "internal/address_is_readable.cc"
+ "internal/elf_mem_image.cc"
+ "internal/vdso_support.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::dynamic_annotations
)
-# Internal-only. Projects external to Abseil should not depend
-# directly on this library.
-absl_library(
- TARGET
- absl_examine_stack
- SOURCES
- ${EXAMINE_STACK_SRC}
- EXPORT_NAME
- examine_stack
+absl_cc_library(
+ NAME
+ demangle_internal
+ HDRS
+ "internal/demangle.h"
+ SRCS
+ "internal/demangle.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base
+ absl::core_headers
+ PUBLIC
)
-list(APPEND LEAK_CHECK_SRC
- "leak_check.cc"
+absl_cc_test(
+ NAME
+ demangle_test
+ SRCS
+ "internal/demangle_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::demangle_internal
+ absl::stack_consumption
+ absl::base
+ absl::core_headers
+ absl::memory
+ gmock_main
)
-
-# leak_check library
-absl_library(
- TARGET
- absl_leak_check
- SOURCES
- ${LEAK_CHECK_SRC}
- PUBLIC_LIBRARIES
- absl_base
- EXPORT_NAME
+absl_cc_library(
+ NAME
leak_check
+ HDRS
+ "$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:leak_check.h>"
+ SRCS
+ "$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:leak_check.cc>"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ PUBLIC
)
-
-# component target
-absl_header_library(
- TARGET
- absl_debugging
- PUBLIC_LIBRARIES
- absl_stacktrace absl_leak_check
- EXPORT_NAME
- debugging
+absl_cc_library(
+ NAME
+ leak_check_disable
+ SRCS
+ "leak_check_disable.cc"
+ PUBLIC
)
-#
-## TESTS
-#
+# TODO(cohenjon) Move into the copts code.
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(ABSL_LSAN_LINKOPTS "-fsanitize=leak")
+endif()
-list(APPEND STACK_CONSUMPTION_SRC
- "internal/stack_consumption.cc"
- "internal/stack_consumption.h"
+absl_cc_library(
+ NAME
+ leak_check_api_enabled_for_testing
+ HDRS
+ "leak_check.h"
+ SRCS
+ "leak_check.cc"
+ COPTS
+ $<$<BOOL:${ABSL_USING_CLANG}>:-DLEAK_SANITIZER>
+ TESTONLY
)
-absl_library(
- TARGET
- absl_stack_consumption
- SOURCES
- ${STACK_CONSUMPTION_SRC}
+absl_cc_library(
+ NAME
+ leak_check_api_disabled_for_testing
+ HDRS
+ "leak_check.h"
+ SRCS
+ "leak_check.cc"
+ COPTS
+ "-ULEAK_SANITIZER"
+ TESTONLY
)
-absl_test(
- TARGET
- absl_stack_consumption_test
- SOURCES
- "internal/stack_consumption_test.cc"
- PUBLIC_LIBRARIES
- absl_stack_consumption
+absl_cc_test(
+ NAME
+ leak_check_test
+ SRCS
+ "leak_check_test.cc"
+ COPTS
+ "$<$<CXX_COMPILER_ID:Clang>:-DABSL_EXPECT_LEAK_SANITIZER>"
+ LINKOPTS
+ "${ABSL_LSAN_LINKOPTS}"
+ DEPS
+ absl::leak_check_api_enabled_for_testing
absl::base
+ gmock_main
)
-list(APPEND DEMANGLE_TEST_SRC "internal/demangle_test.cc")
-
-absl_test(
- TARGET
- demangle_test
- SOURCES
- ${DEMANGLE_TEST_SRC}
- PUBLIC_LIBRARIES
- absl_symbolize absl_stack_consumption
+absl_cc_test(
+ NAME
+ leak_check_no_lsan_test
+ SRCS
+ "leak_check_test.cc"
+ COPTS
+ "-UABSL_EXPECT_LEAK_SANITIZER"
+ DEPS
+ absl::leak_check_api_disabled_for_testing
+ absl::base
+ gmock_main
)
-list(APPEND SYMBOLIZE_TEST_SRC "symbolize_test.cc")
-
-absl_test(
- TARGET
- symbolize_test
- SOURCES
- ${SYMBOLIZE_TEST_SRC}
- PUBLIC_LIBRARIES
- absl::base absl::memory absl_symbolize absl_stack_consumption
+absl_cc_test(
+ NAME
+ disabled_leak_check_test
+ SRCS
+ "leak_check_fail_test.cc"
+ LINKOPTS
+ "${ABSL_LSAN_LINKOPTS}"
+ DEPS
+ absl::leak_check_api_enabled_for_testing
+ absl::leak_check_disable
+ absl::base
+ gmock_main
)
-list(APPEND FAILURE_SIGNAL_HANDLER_TEST_SRC "failure_signal_handler_test.cc")
-
-absl_test(
- TARGET
- failure_signal_handler_test
- SOURCES
- ${FAILURE_SIGNAL_HANDLER_TEST_SRC}
- PUBLIC_LIBRARIES
- absl_examine_stack
- absl_failure_signal_handler
- absl_stacktrace
- absl_symbolize
+absl_cc_library(
+ NAME
+ stack_consumption
+ HDRS
+ "internal/stack_consumption.h"
+ SRCS
+ "internal/stack_consumption.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
absl::base
- absl::strings
+ absl::core_headers
+ TESTONLY
)
-# test leak_check_test
-list(APPEND LEAK_CHECK_TEST_SRC "leak_check_test.cc")
+absl_cc_test(
+ NAME
+ stack_consumption_test
+ SRCS
+ "internal/stack_consumption_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::stack_consumption
+ absl::base
+ absl::core_headers
+ gmock_main
+)
-absl_test(
- TARGET
- leak_check_test
- SOURCES
- ${LEAK_CHECK_TEST_SRC}
- PUBLIC_LIBRARIES
- absl_leak_check
+# component target
+absl_cc_library(
+ NAME
+ debugging
+ DEPS
+ absl::stacktrace
+ absl::leak_check
+ PUBLIC
)
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index b2035148..a3a36f64 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -47,7 +47,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options;
@@ -120,7 +120,11 @@ const char* FailureSignalToString(int signo) {
#ifndef _WIN32
static bool SetupAlternateStackOnce() {
+#if defined(__wasm__) || defined (__asjms__)
const size_t page_mask = getpagesize() - 1;
+#else
+ const size_t page_mask = sysconf(_SC_PAGESIZE) - 1;
+#endif
size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
@@ -253,7 +257,7 @@ ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace(
depth, min_dropped_frames, symbolize_stacktrace, writerfn, writerfn_arg);
}
-// Called by FailureSignalHandler() to write the failure info. It is
+// Called by AbslFailureSignalHandler() to write the failure info. It is
// called once with writerfn set to WriteToStderr() and then possibly
// with writerfn set to the user provided function.
static void WriteFailureInfo(int signo, void* ucontext,
@@ -279,9 +283,9 @@ static void PortableSleepForSeconds(int seconds) {
}
#ifdef ABSL_HAVE_ALARM
-// FailureSignalHandler() installs this as a signal handler for
+// AbslFailureSignalHandler() installs this as a signal handler for
// SIGALRM, then sets an alarm to be delivered to the program after a
-// set amount of time. If FailureSignalHandler() hangs for more than
+// set amount of time. If AbslFailureSignalHandler() hangs for more than
// the alarm timeout, ImmediateAbortSignalHandler() will abort the
// program.
static void ImmediateAbortSignalHandler(int) {
@@ -295,11 +299,10 @@ using GetTidType = decltype(absl::base_internal::GetTID());
ABSL_CONST_INIT static std::atomic<GetTidType> failed_tid(0);
#ifndef ABSL_HAVE_SIGACTION
-static void FailureSignalHandler(int signo) {
+static void AbslFailureSignalHandler(int signo) {
void* ucontext = nullptr;
#else
-static void FailureSignalHandler(int signo, siginfo_t*,
- void* ucontext) {
+static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) {
#endif
const GetTidType this_tid = absl::base_internal::GetTID();
@@ -309,10 +312,10 @@ static void FailureSignalHandler(int signo, siginfo_t*,
std::memory_order_acq_rel, std::memory_order_relaxed)) {
ABSL_RAW_LOG(
ERROR,
- "Signal %d raised at PC=%p while already in FailureSignalHandler()",
+ "Signal %d raised at PC=%p while already in AbslFailureSignalHandler()",
signo, absl::debugging_internal::GetProgramCounter(ucontext));
if (this_tid != previous_failed_tid) {
- // Another thread is already in FailureSignalHandler(), so wait
+ // Another thread is already in AbslFailureSignalHandler(), so wait
// a bit for it to finish. If the other thread doesn't kill us,
// we do so after sleeping.
PortableSleepForSeconds(3);
@@ -350,9 +353,9 @@ static void FailureSignalHandler(int signo, siginfo_t*,
void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) {
fsh_options = options;
for (auto& it : failure_signal_data) {
- InstallOneFailureHandler(&it, FailureSignalHandler);
+ InstallOneFailureHandler(&it, AbslFailureSignalHandler);
}
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h
index fb4de102..0aeb287f 100644
--- a/absl/debugging/failure_signal_handler.h
+++ b/absl/debugging/failure_signal_handler.h
@@ -45,7 +45,7 @@
#define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// FailureSignalHandlerOptions
//
@@ -113,7 +113,7 @@ namespace debugging_internal {
const char* FailureSignalToString(int signo);
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc
index 6282bd6a..2a83f4c8 100644
--- a/absl/debugging/internal/address_is_readable.cc
+++ b/absl/debugging/internal/address_is_readable.cc
@@ -20,14 +20,14 @@
#if !defined(__linux__) || defined(__ANDROID__)
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// On platforms other than Linux, just return true.
bool AddressIsReadable(const void* /* addr */) { return true; }
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else
@@ -42,7 +42,7 @@ bool AddressIsReadable(const void* /* addr */) { return true; }
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// Pack a pid and two file descriptors into a 64-bit word,
@@ -131,7 +131,7 @@ bool AddressIsReadable(const void *addr) {
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif
diff --git a/absl/debugging/internal/address_is_readable.h b/absl/debugging/internal/address_is_readable.h
index 7e58ce55..3942f44b 100644
--- a/absl/debugging/internal/address_is_readable.h
+++ b/absl/debugging/internal/address_is_readable.h
@@ -17,7 +17,7 @@
#define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// Return whether the byte at *addr is readable, without faulting.
@@ -25,7 +25,7 @@ namespace debugging_internal {
bool AddressIsReadable(const void *addr);
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index 15b7c7c3..57b9393f 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -24,7 +24,7 @@
#include <limits>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
typedef struct {
@@ -341,7 +341,7 @@ static bool ZeroOrMore(ParseFunc parse_func, State *state) {
}
// Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is
-// set to out_end_idx+1. The output std::string is ensured to
+// set to out_end_idx+1. The output string is ensured to
// always terminate with '\0' as long as there is no overflow.
static void Append(State *state, const char *const str, const int length) {
for (int i = 0; i < length; ++i) {
@@ -841,7 +841,7 @@ static bool ParseNumber(State *state, int *number_out) {
}
// Floating-point literals are encoded using a fixed-length lowercase
-// hexadecimal std::string.
+// hexadecimal string.
static bool ParseFloatNumber(State *state) {
ComplexityGuard guard(state);
if (guard.IsTooComplex()) return false;
@@ -1637,6 +1637,15 @@ static bool ParseExpression(State *state) {
}
state->parse_state = copy;
+ // Pointer-to-member access expressions. This parses the same as a binary
+ // operator, but it's implemented separately because "ds" shouldn't be
+ // accepted in other contexts that parse an operator name.
+ if (ParseTwoCharToken(state, "ds") && ParseExpression(state) &&
+ ParseExpression(state)) {
+ return true;
+ }
+ state->parse_state = copy;
+
// Parameter pack expansion
if (ParseTwoCharToken(state, "sp") && ParseExpression(state)) {
return true;
@@ -1860,5 +1869,5 @@ bool Demangle(const char *mangled, char *out, int out_size) {
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h
index 7faa5a2f..1f8722c7 100644
--- a/absl/debugging/internal/demangle.h
+++ b/absl/debugging/internal/demangle.h
@@ -54,7 +54,7 @@
#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// Demangle `mangled`. On success, return true and write the
@@ -63,7 +63,7 @@ namespace debugging_internal {
bool Demangle(const char *mangled, char *out, int out_size);
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index 5ff64a37..fa89fb80 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -23,7 +23,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
namespace {
@@ -189,5 +189,5 @@ TEST(DemangleRegression, DeeplyNestedArrayType) {
} // namespace
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc
index a2bea919..e7e35e9c 100644
--- a/absl/debugging/internal/elf_mem_image.cc
+++ b/absl/debugging/internal/elf_mem_image.cc
@@ -38,7 +38,7 @@
#define VERSYM_VERSION 0x7fff
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
namespace {
@@ -376,7 +376,7 @@ void ElfMemImage::SymbolIterator::Update(int increment) {
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_ELF_MEM_IMAGE
diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h
index ea4f5f0c..0adb5f5d 100644
--- a/absl/debugging/internal/elf_mem_image.h
+++ b/absl/debugging/internal/elf_mem_image.h
@@ -39,7 +39,7 @@
#include <link.h> // for ElfW
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// An in-memory ELF image (may not exist on disk).
@@ -124,7 +124,7 @@ class ElfMemImage {
};
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_ELF_MEM_IMAGE
diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc
index 1a2b7a42..4764355a 100644
--- a/absl/debugging/internal/examine_stack.cc
+++ b/absl/debugging/internal/examine_stack.cc
@@ -30,7 +30,7 @@
#include "absl/debugging/symbolize.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// Returns the program counter from signal context, nullptr if
@@ -53,6 +53,10 @@ void* GetProgramCounter(void* vuc) {
return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]);
#elif defined(__powerpc__)
return reinterpret_cast<void*>(context->uc_mcontext.regs->nip);
+#elif defined(__s390__) && !defined(__s390x__)
+ return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff);
+#elif defined(__s390__) && defined(__s390x__)
+ return reinterpret_cast<void*>(context->uc_mcontext.psw.addr);
#elif defined(__x86_64__)
if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
@@ -147,5 +151,5 @@ void DumpPCAndFrameSizesAndStackTrace(
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h
index a51a8b35..474fdd5e 100644
--- a/absl/debugging/internal/examine_stack.h
+++ b/absl/debugging/internal/examine_stack.h
@@ -18,7 +18,7 @@
#define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// Returns the program counter from signal context, or nullptr if
@@ -34,7 +34,7 @@ void DumpPCAndFrameSizesAndStackTrace(
void (*writerfn)(const char*, void*), void* writerfn_arg);
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc
index 8724007d..94b43e1d 100644
--- a/absl/debugging/internal/stack_consumption.cc
+++ b/absl/debugging/internal/stack_consumption.cc
@@ -27,7 +27,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
namespace {
@@ -168,7 +168,7 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/absl/debugging/internal/stack_consumption.h b/absl/debugging/internal/stack_consumption.h
index 584a4aa9..2defdf0d 100644
--- a/absl/debugging/internal/stack_consumption.h
+++ b/absl/debugging/internal/stack_consumption.h
@@ -27,7 +27,7 @@
#define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// Returns the stack consumption in bytes for the code exercised by
@@ -39,7 +39,7 @@ namespace debugging_internal {
int GetSignalHandlerStackConsumption(void (*signal_handler)(int));
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/absl/debugging/internal/stack_consumption_test.cc b/absl/debugging/internal/stack_consumption_test.cc
index 4f1351b3..022e508a 100644
--- a/absl/debugging/internal/stack_consumption_test.cc
+++ b/absl/debugging/internal/stack_consumption_test.cc
@@ -23,7 +23,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
namespace {
@@ -44,7 +44,7 @@ TEST(SignalHandlerStackConsumptionTest, MeasuresStackConsumption) {
} // namespace
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc
index 0e3fda57..2ed7ae1f 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -180,13 +180,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
bool StackTraceWorksForTest() {
return true;
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_
diff --git a/absl/debugging/internal/stacktrace_arm-inl.inc b/absl/debugging/internal/stacktrace_arm-inl.inc
index ef76782c..eb8ca77c 100644
--- a/absl/debugging/internal/stacktrace_arm-inl.inc
+++ b/absl/debugging/internal/stacktrace_arm-inl.inc
@@ -113,13 +113,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
bool StackTraceWorksForTest() {
return false;
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h
index 48adfccc..578e4968 100644
--- a/absl/debugging/internal/stacktrace_config.h
+++ b/absl/debugging/internal/stacktrace_config.h
@@ -21,26 +21,16 @@
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
-// First, test platforms which only support a stub.
-#if ABSL_STACKTRACE_INL_HEADER
+#if defined(ABSL_STACKTRACE_INL_HEADER)
#error ABSL_STACKTRACE_INL_HEADER cannot be directly set
-#elif defined(__native_client__) || defined(__APPLE__) || \
- defined(__FreeBSD__) || defined(__ANDROID__) || defined(__myriad2__) || \
- defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__)
-#define ABSL_STACKTRACE_INL_HEADER \
- "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
-// Next, test for Mips and Windows.
-// TODO(marmstrong): Mips case, remove the check for ABSL_STACKTRACE_INL_HEADER
-#elif defined(__mips__) && !defined(ABSL_STACKTRACE_INL_HEADER)
-#define ABSL_STACKTRACE_INL_HEADER \
- "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
-#elif defined(_WIN32) // windows
+#elif defined(_WIN32)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_win32-inl.inc"
-// Finally, test NO_FRAME_POINTER.
-#elif !defined(NO_FRAME_POINTER)
+#elif defined(__linux__) && !defined(__ANDROID__)
+
+#if !defined(NO_FRAME_POINTER)
# if defined(__i386__) || defined(__x86_64__)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_x86-inl.inc"
@@ -51,24 +41,30 @@
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_aarch64-inl.inc"
# elif defined(__arm__)
+// Note: When using glibc this may require -funwind-tables to function properly.
#define ABSL_STACKTRACE_INL_HEADER \
- "absl/debugging/internal/stacktrace_arm-inl.inc"
+ "absl/debugging/internal/stacktrace_generic-inl.inc"
+# else
+#define ABSL_STACKTRACE_INL_HEADER \
+ "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
# endif
#else // defined(NO_FRAME_POINTER)
# if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
#define ABSL_STACKTRACE_INL_HEADER \
- "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
+ "absl/debugging/internal/stacktrace_generic-inl.inc"
# elif defined(__ppc__) || defined(__PPC__)
-// Use glibc's backtrace.
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"
-# elif defined(__arm__)
-# error stacktrace without frame pointer is not supported on ARM
+# else
+#define ABSL_STACKTRACE_INL_HEADER \
+ "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
# endif
#endif // NO_FRAME_POINTER
-#if !defined(ABSL_STACKTRACE_INL_HEADER)
-#error Not supported yet
+#else
+#define ABSL_STACKTRACE_INL_HEADER \
+ "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
+
#endif
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
diff --git a/absl/debugging/internal/stacktrace_generic-inl.inc b/absl/debugging/internal/stacktrace_generic-inl.inc
index 5d7492a4..823942af 100644
--- a/absl/debugging/internal/stacktrace_generic-inl.inc
+++ b/absl/debugging/internal/stacktrace_generic-inl.inc
@@ -19,6 +19,7 @@
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
const void *ucp, int *min_dropped_frames) {
+ static_cast<void>(ucp); // Unused.
static const int kStackLength = 64;
void * stack[kStackLength];
int size;
@@ -49,13 +50,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
bool StackTraceWorksForTest() {
return true;
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_
diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc
index 86ac8534..aff2d516 100644
--- a/absl/debugging/internal/stacktrace_powerpc-inl.inc
+++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc
@@ -31,6 +31,8 @@
#include <cstdint>
#include <cstdio>
+#include "absl/base/attributes.h"
+#include "absl/base/optimization.h"
#include "absl/base/port.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/internal/address_is_readable.h"
@@ -150,8 +152,9 @@ static void **NextStackFrame(void **old_sp, const void *uc) {
}
// This ensures that absl::GetStackTrace sets up the Link Register properly.
-void StacktracePowerPCDummyFunction() __attribute__((noinline));
-void StacktracePowerPCDummyFunction() { __asm__ volatile(""); }
+ABSL_ATTRIBUTE_NOINLINE static void AbslStacktracePowerPCDummyFunction() {
+ ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
+}
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
@@ -176,7 +179,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
// want here. While the compiler will always(?) set up LR for
// subroutine calls, it may not for leaf functions (such as this one).
// This routine forces the compiler (at least gcc) to push it anyway.
- StacktracePowerPCDummyFunction();
+ AbslStacktracePowerPCDummyFunction();
// The LR save area is used by the callee, so the top entry is bogus.
skip_count++;
@@ -233,13 +236,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
bool StackTraceWorksForTest() {
return true;
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
diff --git a/absl/debugging/internal/stacktrace_unimplemented-inl.inc b/absl/debugging/internal/stacktrace_unimplemented-inl.inc
index 8c3ed4d7..65345efc 100644
--- a/absl/debugging/internal/stacktrace_unimplemented-inl.inc
+++ b/absl/debugging/internal/stacktrace_unimplemented-inl.inc
@@ -12,13 +12,13 @@ static int UnwindImpl(void** /* result */, int* /* sizes */,
}
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
bool StackTraceWorksForTest() {
return false;
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
diff --git a/absl/debugging/internal/stacktrace_win32-inl.inc b/absl/debugging/internal/stacktrace_win32-inl.inc
index ceca299d..59e72785 100644
--- a/absl/debugging/internal/stacktrace_win32-inl.inc
+++ b/absl/debugging/internal/stacktrace_win32-inl.inc
@@ -73,13 +73,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
bool StackTraceWorksForTest() {
return false;
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc
index 13afce7b..d29cd84b 100644
--- a/absl/debugging/internal/stacktrace_x86-inl.inc
+++ b/absl/debugging/internal/stacktrace_x86-inl.inc
@@ -327,13 +327,13 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
bool StackTraceWorksForTest() {
return true;
}
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h
index fd10a637..2791105e 100644
--- a/absl/debugging/internal/symbolize.h
+++ b/absl/debugging/internal/symbolize.h
@@ -34,7 +34,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// Iterates over all sections, invoking callback on each with the section name
@@ -53,13 +53,13 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
ElfW(Shdr) *out);
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
struct SymbolDecoratorArgs {
@@ -121,7 +121,7 @@ bool GetFileMappingHint(const void** start,
const char** filename);
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_
diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc
index fd1fb05e..85b52bc8 100644
--- a/absl/debugging/internal/vdso_support.cc
+++ b/absl/debugging/internal/vdso_support.cc
@@ -38,7 +38,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
ABSL_CONST_INIT
@@ -188,7 +188,7 @@ static class VDSOInitHelper {
} vdso_init_helper;
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_VDSO_SUPPORT
diff --git a/absl/debugging/internal/vdso_support.h b/absl/debugging/internal/vdso_support.h
index 7fdd7527..035e5964 100644
--- a/absl/debugging/internal/vdso_support.h
+++ b/absl/debugging/internal/vdso_support.h
@@ -53,7 +53,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
// NOTE: this class may be used from within tcmalloc, and can not
@@ -150,7 +150,7 @@ class VDSOSupport {
int GetCPU();
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_ELF_MEM_IMAGE
diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc
index 7eaaf495..cf65280a 100644
--- a/absl/debugging/leak_check.cc
+++ b/absl/debugging/leak_check.cc
@@ -20,14 +20,14 @@
#ifndef LEAK_SANITIZER
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
bool HaveLeakSanitizer() { return false; }
void DoIgnoreLeak(const void*) { }
void RegisterLivePointers(const void*, size_t) { }
void UnRegisterLivePointers(const void*, size_t) { }
LeakCheckDisabler::LeakCheckDisabler() { }
LeakCheckDisabler::~LeakCheckDisabler() { }
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else
@@ -35,7 +35,7 @@ LeakCheckDisabler::~LeakCheckDisabler() { }
#include <sanitizer/lsan_interface.h>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
bool HaveLeakSanitizer() { return true; }
void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); }
void RegisterLivePointers(const void* ptr, size_t size) {
@@ -46,7 +46,7 @@ void UnRegisterLivePointers(const void* ptr, size_t size) {
}
LeakCheckDisabler::LeakCheckDisabler() { __lsan_disable(); }
LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); }
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // LEAK_SANITIZER
diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h
index ddded2ae..f5e4fd87 100644
--- a/absl/debugging/leak_check.h
+++ b/absl/debugging/leak_check.h
@@ -33,7 +33,7 @@
#include <cstddef>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// HaveLeakSanitizer()
//
@@ -105,7 +105,7 @@ void RegisterLivePointers(const void* ptr, size_t size);
// `RegisterLivePointers()`, enabling leak checking of those pointers.
void UnRegisterLivePointers(const void* ptr, size_t size);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_LEAK_CHECK_H_
diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc
index 55edad83..e991fc59 100644
--- a/absl/debugging/stacktrace.cc
+++ b/absl/debugging/stacktrace.cc
@@ -56,7 +56,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
typedef int (*Unwinder)(void**, int*, int, int, const void*, int*);
@@ -81,25 +81,29 @@ ABSL_ATTRIBUTE_ALWAYS_INLINE inline int Unwind(void** result, int* sizes,
} // anonymous namespace
-int GetStackFrames(void** result, int* sizes, int max_depth, int skip_count) {
+ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackFrames(
+ void** result, int* sizes, int max_depth, int skip_count) {
return Unwind<true, false>(result, sizes, max_depth, skip_count, nullptr,
nullptr);
}
-int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
- int skip_count, const void* uc,
- int* min_dropped_frames) {
+ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int
+GetStackFramesWithContext(void** result, int* sizes, int max_depth,
+ int skip_count, const void* uc,
+ int* min_dropped_frames) {
return Unwind<true, true>(result, sizes, max_depth, skip_count, uc,
min_dropped_frames);
}
-int GetStackTrace(void** result, int max_depth, int skip_count) {
+ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackTrace(
+ void** result, int max_depth, int skip_count) {
return Unwind<false, false>(result, nullptr, max_depth, skip_count, nullptr,
nullptr);
}
-int GetStackTraceWithContext(void** result, int max_depth, int skip_count,
- const void* uc, int* min_dropped_frames) {
+ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int
+GetStackTraceWithContext(void** result, int max_depth, int skip_count,
+ const void* uc, int* min_dropped_frames) {
return Unwind<false, true>(result, nullptr, max_depth, skip_count, uc,
min_dropped_frames);
}
@@ -131,5 +135,5 @@ int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip,
return n;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/debugging/stacktrace.h b/absl/debugging/stacktrace.h
index a6213cf4..7baf83bc 100644
--- a/absl/debugging/stacktrace.h
+++ b/absl/debugging/stacktrace.h
@@ -32,7 +32,7 @@
#define ABSL_DEBUGGING_STACKTRACE_H_
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// GetStackFrames()
//
@@ -221,7 +221,7 @@ namespace debugging_internal {
// working.
extern bool StackTraceWorksForTest();
} // namespace debugging_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_STACKTRACE_H_
diff --git a/absl/debugging/symbolize.h b/absl/debugging/symbolize.h
index 6198b1fd..fc606e87 100644
--- a/absl/debugging/symbolize.h
+++ b/absl/debugging/symbolize.h
@@ -55,7 +55,7 @@
#include "absl/debugging/internal/symbolize.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// InitializeSymbolizer()
//
@@ -93,7 +93,7 @@ void InitializeSymbolizer(const char* argv0);
// }
bool Symbolize(const void *pc, char *out, int out_size);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_DEBUGGING_SYMBOLIZE_H_
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc
index b8dc23b7..37f77ca6 100644
--- a/absl/debugging/symbolize_elf.inc
+++ b/absl/debugging/symbolize_elf.inc
@@ -76,7 +76,7 @@
#include "absl/debugging/internal/vdso_support.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// Value of argv[0]. Used by MaybeInitializeObjFile().
static char *argv0_value = nullptr;
@@ -334,7 +334,11 @@ static std::atomic<Symbolizer *> g_cached_symbolizer;
} // namespace
static int SymbolizerSize() {
+#if defined(__wasm__) || defined(__asmjs__)
int pagesize = getpagesize();
+#else
+ int pagesize = sysconf(_SC_PAGESIZE);
+#endif
return ((sizeof(Symbolizer) - 1) / pagesize + 1) * pagesize;
}
@@ -1471,5 +1475,5 @@ bool Symbolize(const void *pc, char *out, int out_size) {
return ok;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc
index 5f2af47e..8029fbe9 100644
--- a/absl/debugging/symbolize_test.cc
+++ b/absl/debugging/symbolize_test.cc
@@ -321,7 +321,7 @@ TEST(Symbolize, SymbolizeWithMultipleMaps) {
}
}
-// Appends std::string(*args->arg) to args->symbol_buf.
+// Appends string(*args->arg) to args->symbol_buf.
static void DummySymbolDecorator(
const absl::debugging_internal::SymbolDecoratorArgs *args) {
std::string *message = static_cast<std::string *>(args->arg);
diff --git a/absl/debugging/symbolize_unimplemented.inc b/absl/debugging/symbolize_unimplemented.inc
index 26dec0fb..874a4228 100644
--- a/absl/debugging/symbolize_unimplemented.inc
+++ b/absl/debugging/symbolize_unimplemented.inc
@@ -17,7 +17,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace debugging_internal {
@@ -33,5 +33,5 @@ bool RegisterFileMappingHint(const void *, const void *, uint64_t, const char *)
void InitializeSymbolizer(const char*) {}
bool Symbolize(const void *, char *, int) { return false; }
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc
index 549b995a..ee8fd55c 100644
--- a/absl/debugging/symbolize_win32.inc
+++ b/absl/debugging/symbolize_win32.inc
@@ -25,7 +25,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
static HANDLE process = NULL;
@@ -72,5 +72,5 @@ bool Symbolize(const void *pc, char *out, int out_size) {
return true;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel
new file mode 100644
index 00000000..4f7c94ce
--- /dev/null
+++ b/absl/hash/BUILD.bazel
@@ -0,0 +1,113 @@
+#
+# 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
+#
+# 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.
+#
+
+load(
+ "//absl:copts.bzl",
+ "ABSL_DEFAULT_COPTS",
+ "ABSL_TEST_COPTS",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+cc_library(
+ name = "hash",
+ srcs = [
+ "internal/hash.cc",
+ "internal/hash.h",
+ ],
+ hdrs = ["hash.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":city",
+ "//absl/base:core_headers",
+ "//absl/base:endian",
+ "//absl/container:fixed_array",
+ "//absl/meta:type_traits",
+ "//absl/numeric:int128",
+ "//absl/strings",
+ "//absl/types:optional",
+ "//absl/types:variant",
+ "//absl/utility",
+ ],
+)
+
+cc_library(
+ name = "hash_testing",
+ testonly = 1,
+ hdrs = ["hash_testing.h"],
+ deps = [
+ ":spy_hash_state",
+ "//absl/meta:type_traits",
+ "//absl/strings",
+ "//absl/types:variant",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_test(
+ name = "hash_test",
+ srcs = ["hash_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":hash",
+ ":hash_testing",
+ "//absl/base:core_headers",
+ "//absl/container:flat_hash_set",
+ "//absl/hash:spy_hash_state",
+ "//absl/meta:type_traits",
+ "//absl/numeric:int128",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "spy_hash_state",
+ testonly = 1,
+ hdrs = ["internal/spy_hash_state.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":hash",
+ "//absl/strings",
+ "//absl/strings:str_format",
+ ],
+)
+
+cc_library(
+ name = "city",
+ srcs = ["internal/city.cc"],
+ hdrs = [
+ "internal/city.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/base:endian",
+ ],
+)
+
+cc_test(
+ name = "city_test",
+ srcs = ["internal/city_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":city",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt
new file mode 100644
index 00000000..8f97d7cc
--- /dev/null
+++ b/absl/hash/CMakeLists.txt
@@ -0,0 +1,111 @@
+#
+# 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
+#
+# 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.
+#
+
+absl_cc_library(
+ NAME
+ hash
+ HDRS
+ "hash.h"
+ SRCS
+ "internal/hash.cc"
+ "internal/hash.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ absl::endian
+ absl::fixed_array
+ absl::meta
+ absl::int128
+ absl::strings
+ absl::optional
+ absl::variant
+ absl::utility
+ absl::city
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ hash_testing
+ HDRS
+ "hash_testing.h"
+ DEPS
+ absl::spy_hash_state
+ absl::meta
+ absl::strings
+ absl::variant
+ gmock
+ TESTONLY
+)
+
+absl_cc_test(
+ NAME
+ hash_test
+ SRCS
+ "hash_test.cc"
+ DEPS
+ absl::hash
+ absl::hash_testing
+ absl::core_headers
+ absl::flat_hash_set
+ absl::spy_hash_state
+ absl::meta
+ absl::int128
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ spy_hash_state
+ HDRS
+ "internal/spy_hash_state.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::hash
+ absl::strings
+ absl::str_format
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ city
+ HDRS
+ "internal/city.h"
+ SRCS
+ "internal/city.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ absl::core_headers
+ absl::endian
+)
+
+absl_cc_test(
+ NAME
+ city_test
+ SRCS
+ "internal/city_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::city
+ gmock_main
+)
+
diff --git a/absl/hash/hash.h b/absl/hash/hash.h
new file mode 100644
index 00000000..2c8982b8
--- /dev/null
+++ b/absl/hash/hash.h
@@ -0,0 +1,314 @@
+// 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
+//
+// 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.
+//
+// -----------------------------------------------------------------------------
+// File: hash.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the Abseil `hash` library and the Abseil hashing
+// framework. This framework consists of the following:
+//
+// * The `absl::Hash` functor, which is used to invoke the hasher within the
+// Abseil hashing framework. `absl::Hash<T>` supports most basic types and
+// a number of Abseil types out of the box.
+// * `AbslHashValue`, an extension point that allows you to extend types to
+// support Abseil hashing without requiring you to define a hashing
+// algorithm.
+// * `HashState`, a type-erased class which implements the manipulation of the
+// hash state (H) itself, contains member functions `combine()` and
+// `combine_contiguous()`, which you can use to contribute to an existing
+// hash state when hashing your types.
+//
+// Unlike `std::hash` or other hashing frameworks, the Abseil hashing framework
+// provides most of its utility by abstracting away the hash algorithm (and its
+// implementation) entirely. Instead, a type invokes the Abseil hashing
+// framework by simply combining its state with the state of known, hashable
+// types. Hashing of that combined state is separately done by `absl::Hash`.
+//
+// Example:
+//
+// // Suppose we have a class `Circle` for which we want to add hashing
+// class Circle {
+// public:
+// ...
+// private:
+// std::pair<int, int> center_;
+// int radius_;
+// };
+//
+// // To add hashing support to `Circle`, we simply need to add an ordinary
+// // function `AbslHashValue()`, and return the combined hash state of the
+// // existing hash state and the class state:
+//
+// template <typename H>
+// friend H AbslHashValue(H h, const Circle& c) {
+// return H::combine(std::move(h), c.center_, c.radius_);
+// }
+//
+// For more information, see Adding Type Support to `absl::Hash` below.
+//
+#ifndef ABSL_HASH_HASH_H_
+#define ABSL_HASH_HASH_H_
+
+#include "absl/hash/internal/hash.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+// -----------------------------------------------------------------------------
+// `absl::Hash`
+// -----------------------------------------------------------------------------
+//
+// `absl::Hash<T>` is a convenient general-purpose hash functor for any type `T`
+// satisfying any of the following conditions (in order):
+//
+// * T is an arithmetic or pointer type
+// * T defines an overload for `AbslHashValue(H, const T&)` for an arbitrary
+// hash state `H`.
+// - T defines a specialization of `HASH_NAMESPACE::hash<T>`
+// - T defines a specialization of `std::hash<T>`
+//
+// `absl::Hash` intrinsically supports the following types:
+//
+// * All integral types (including bool)
+// * All enum types
+// * All floating-point types (although hashing them is discouraged)
+// * All pointer types, including nullptr_t
+// * std::pair<T1, T2>, if T1 and T2 are hashable
+// * std::tuple<Ts...>, if all the Ts... are hashable
+// * std::unique_ptr and std::shared_ptr
+// * All string-like types including:
+// * std::string
+// * std::string_view (as well as any instance of std::basic_string that
+// uses char and std::char_traits)
+// * All the standard sequence containers (provided the elements are hashable)
+// * All the standard ordered associative containers (provided the elements are
+// hashable)
+// * absl types such as the following:
+// * absl::string_view
+// * absl::InlinedVector
+// * absl::FixedArray
+// * absl::uint128
+// * absl::Time, absl::Duration, and absl::TimeZone
+//
+// Note: the list above is not meant to be exhaustive. Additional type support
+// may be added, in which case the above list will be updated.
+//
+// -----------------------------------------------------------------------------
+// absl::Hash Invocation Evaluation
+// -----------------------------------------------------------------------------
+//
+// When invoked, `absl::Hash<T>` searches for supplied hash functions in the
+// following order:
+//
+// * Natively supported types out of the box (see above)
+// * Types for which an `AbslHashValue()` overload is provided (such as
+// user-defined types). See "Adding Type Support to `absl::Hash`" below.
+// * Types which define a `HASH_NAMESPACE::hash<T>` specialization (aka
+// `__gnu_cxx::hash<T>` for gcc/Clang or `stdext::hash<T>` for MSVC)
+// * Types which define a `std::hash<T>` specialization
+//
+// The fallback to legacy hash functions exists mainly for backwards
+// compatibility. If you have a choice, prefer defining an `AbslHashValue`
+// overload instead of specializing any legacy hash functors.
+//
+// -----------------------------------------------------------------------------
+// The Hash State Concept, and using `HashState` for Type Erasure
+// -----------------------------------------------------------------------------
+//
+// The `absl::Hash` framework relies on the Concept of a "hash state." Such a
+// hash state is used in several places:
+//
+// * Within existing implementations of `absl::Hash<T>` to store the hashed
+// state of an object. Note that it is up to the implementation how it stores
+// such state. A hash table, for example, may mix the state to produce an
+// integer value; a testing framework may simply hold a vector of that state.
+// * Within implementations of `AbslHashValue()` used to extend user-defined
+// types. (See "Adding Type Support to absl::Hash" below.)
+// * Inside a `HashState`, providing type erasure for the concept of a hash
+// state, which you can use to extend the `absl::Hash` framework for types
+// that are otherwise difficult to extend using `AbslHashValue()`. (See the
+// `HashState` class below.)
+//
+// The "hash state" concept contains two member functions for mixing hash state:
+//
+// * `H::combine(state, values...)`
+//
+// Combines an arbitrary number of values into a hash state, returning the
+// updated state. Note that the existing hash state is move-only and must be
+// passed by value.
+//
+// Each of the value types T must be hashable by H.
+//
+// NOTE:
+//
+// state = H::combine(std::move(state), value1, value2, value3);
+//
+// must be guaranteed to produce the same hash expansion as
+//
+// state = H::combine(std::move(state), value1);
+// state = H::combine(std::move(state), value2);
+// state = H::combine(std::move(state), value3);
+//
+// * `H::combine_contiguous(state, data, size)`
+//
+// Combines a contiguous array of `size` elements into a hash state,
+// returning the updated state. Note that the existing hash state is
+// move-only and must be passed by value.
+//
+// NOTE:
+//
+// state = H::combine_contiguous(std::move(state), data, size);
+//
+// need NOT be guaranteed to produce the same hash expansion as a loop
+// (it may perform internal optimizations). If you need this guarantee, use a
+// loop instead.
+//
+// -----------------------------------------------------------------------------
+// Adding Type Support to `absl::Hash`
+// -----------------------------------------------------------------------------
+//
+// To add support for your user-defined type, add a proper `AbslHashValue()`
+// overload as a free (non-member) function. The overload will take an
+// existing hash state and should combine that state with state from the type.
+//
+// Example:
+//
+// template <typename H>
+// H AbslHashValue(H state, const MyType& v) {
+// return H::combine(std::move(state), v.field1, ..., v.fieldN);
+// }
+//
+// where `(field1, ..., fieldN)` are the members you would use on your
+// `operator==` to define equality.
+//
+// Notice that `AbslHashValue` is not a class member, but an ordinary function.
+// An `AbslHashValue` overload for a type should only be declared in the same
+// file and namespace as said type. The proper `AbslHashValue` implementation
+// for a given type will be discovered via ADL.
+//
+// Note: unlike `std::hash', `absl::Hash` should never be specialized. It must
+// only be extended by adding `AbslHashValue()` overloads.
+//
+template <typename T>
+using Hash = absl::hash_internal::Hash<T>;
+
+// HashState
+//
+// A type erased version of the hash state concept, for use in user-defined
+// `AbslHashValue` implementations that can't use templates (such as PImpl
+// classes, virtual functions, etc.). The type erasure adds overhead so it
+// should be avoided unless necessary.
+//
+// Note: This wrapper will only erase calls to:
+// combine_contiguous(H, const unsigned char*, size_t)
+//
+// All other calls will be handled internally and will not invoke overloads
+// provided by the wrapped class.
+//
+// Users of this class should still define a template `AbslHashValue` function,
+// but can use `absl::HashState::Create(&state)` to erase the type of the hash
+// state and dispatch to their private hashing logic.
+//
+// This state can be used like any other hash state. In particular, you can call
+// `HashState::combine()` and `HashState::combine_contiguous()` on it.
+//
+// Example:
+//
+// class Interface {
+// public:
+// template <typename H>
+// friend H AbslHashValue(H state, const Interface& value) {
+// state = H::combine(std::move(state), std::type_index(typeid(*this)));
+// value.HashValue(absl::HashState::Create(&state));
+// return state;
+// }
+// private:
+// virtual void HashValue(absl::HashState state) const = 0;
+// };
+//
+// class Impl : Interface {
+// private:
+// void HashValue(absl::HashState state) const override {
+// absl::HashState::combine(std::move(state), v1_, v2_);
+// }
+// int v1_;
+// string v2_;
+// };
+class HashState : public hash_internal::HashStateBase<HashState> {
+ public:
+ // HashState::Create()
+ //
+ // Create a new `HashState` instance that wraps `state`. All calls to
+ // `combine()` and `combine_contiguous()` on the new instance will be
+ // redirected to the original `state` object. The `state` object must outlive
+ // the `HashState` instance.
+ template <typename T>
+ static HashState Create(T* state) {
+ HashState s;
+ s.Init(state);
+ return s;
+ }
+
+ HashState(const HashState&) = delete;
+ HashState& operator=(const HashState&) = delete;
+ HashState(HashState&&) = default;
+ HashState& operator=(HashState&&) = default;
+
+ // HashState::combine()
+ //
+ // Combines an arbitrary number of values into a hash state, returning the
+ // updated state.
+ using HashState::HashStateBase::combine;
+
+ // HashState::combine_contiguous()
+ //
+ // Combines a contiguous array of `size` elements into a hash state, returning
+ // the updated state.
+ static HashState combine_contiguous(HashState hash_state,
+ const unsigned char* first, size_t size) {
+ hash_state.combine_contiguous_(hash_state.state_, first, size);
+ return hash_state;
+ }
+ using HashState::HashStateBase::combine_contiguous;
+
+ private:
+ HashState() = default;
+
+ template <typename T>
+ static void CombineContiguousImpl(void* p, const unsigned char* first,
+ size_t size) {
+ T& state = *static_cast<T*>(p);
+ state = T::combine_contiguous(std::move(state), first, size);
+ }
+
+ template <typename T>
+ void Init(T* state) {
+ state_ = state;
+ combine_contiguous_ = &CombineContiguousImpl<T>;
+ }
+
+ // Do not erase an already erased state.
+ void Init(HashState* state) {
+ state_ = state->state_;
+ combine_contiguous_ = state->combine_contiguous_;
+ }
+
+ void* state_;
+ void (*combine_contiguous_)(void*, const unsigned char*, size_t);
+};
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+#endif // ABSL_HASH_HASH_H_
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc
new file mode 100644
index 00000000..4a1a98d5
--- /dev/null
+++ b/absl/hash/hash_test.cc
@@ -0,0 +1,427 @@
+// 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
+//
+// 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.
+
+#include "absl/hash/hash.h"
+
+#include <array>
+#include <cstring>
+#include <deque>
+#include <forward_list>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <list>
+#include <map>
+#include <memory>
+#include <numeric>
+#include <random>
+#include <set>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/hash/hash_testing.h"
+#include "absl/hash/internal/spy_hash_state.h"
+#include "absl/meta/type_traits.h"
+#include "absl/numeric/int128.h"
+
+namespace {
+
+using absl::Hash;
+using absl::hash_internal::SpyHashState;
+
+template <typename T>
+class HashValueIntTest : public testing::Test {
+};
+TYPED_TEST_CASE_P(HashValueIntTest);
+
+template <typename T>
+SpyHashState SpyHash(const T& value) {
+ return SpyHashState::combine(SpyHashState(), value);
+}
+
+// Helper trait to verify if T is hashable. We use absl::Hash's poison status to
+// detect it.
+template <typename T>
+using is_hashable = std::is_default_constructible<absl::Hash<T>>;
+
+TYPED_TEST_P(HashValueIntTest, BasicUsage) {
+ EXPECT_TRUE((is_hashable<TypeParam>::value));
+
+ TypeParam n = 42;
+ EXPECT_EQ(SpyHash(n), SpyHash(TypeParam{42}));
+ EXPECT_NE(SpyHash(n), SpyHash(TypeParam{0}));
+ EXPECT_NE(SpyHash(std::numeric_limits<TypeParam>::max()),
+ SpyHash(std::numeric_limits<TypeParam>::min()));
+}
+
+TYPED_TEST_P(HashValueIntTest, FastPath) {
+ // Test the fast-path to make sure the values are the same.
+ TypeParam n = 42;
+ EXPECT_EQ(absl::Hash<TypeParam>{}(n),
+ absl::Hash<std::tuple<TypeParam>>{}(std::tuple<TypeParam>(n)));
+}
+
+REGISTER_TYPED_TEST_CASE_P(HashValueIntTest, BasicUsage, FastPath);
+using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t, uint32_t,
+ uint64_t, size_t>;
+INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueIntTest, IntTypes);
+
+template <typename T, typename = void>
+struct IsHashCallble : std::false_type {};
+
+template <typename T>
+struct IsHashCallble<T, absl::void_t<decltype(std::declval<absl::Hash<T>>()(
+ std::declval<const T&>()))>> : std::true_type {};
+
+template <typename T, typename = void>
+struct IsAggregateInitializable : std::false_type {};
+
+template <typename T>
+struct IsAggregateInitializable<T, absl::void_t<decltype(T{})>>
+ : std::true_type {};
+
+TEST(IsHashableTest, ValidHash) {
+ EXPECT_TRUE((is_hashable<int>::value));
+ EXPECT_TRUE(std::is_default_constructible<absl::Hash<int>>::value);
+ EXPECT_TRUE(std::is_copy_constructible<absl::Hash<int>>::value);
+ EXPECT_TRUE(std::is_move_constructible<absl::Hash<int>>::value);
+ EXPECT_TRUE(absl::is_copy_assignable<absl::Hash<int>>::value);
+ EXPECT_TRUE(absl::is_move_assignable<absl::Hash<int>>::value);
+ EXPECT_TRUE(IsHashCallble<int>::value);
+ EXPECT_TRUE(IsAggregateInitializable<absl::Hash<int>>::value);
+}
+#if ABSL_HASH_INTERNAL_CAN_POISON_ && !defined(__APPLE__)
+TEST(IsHashableTest, PoisonHash) {
+ struct X {};
+ EXPECT_FALSE((is_hashable<X>::value));
+ EXPECT_FALSE(std::is_default_constructible<absl::Hash<X>>::value);
+ EXPECT_FALSE(std::is_copy_constructible<absl::Hash<X>>::value);
+ EXPECT_FALSE(std::is_move_constructible<absl::Hash<X>>::value);
+ EXPECT_FALSE(absl::is_copy_assignable<absl::Hash<X>>::value);
+ EXPECT_FALSE(absl::is_move_assignable<absl::Hash<X>>::value);
+ EXPECT_FALSE(IsHashCallble<X>::value);
+ EXPECT_FALSE(IsAggregateInitializable<absl::Hash<X>>::value);
+}
+#endif // ABSL_HASH_INTERNAL_CAN_POISON_
+
+// Hashable types
+//
+// These types exist simply to exercise various AbslHashValue behaviors, so
+// they are named by what their AbslHashValue overload does.
+struct NoOp {
+ template <typename HashCode>
+ friend HashCode AbslHashValue(HashCode h, NoOp n) {
+ return std::move(h);
+ }
+};
+
+struct EmptyCombine {
+ template <typename HashCode>
+ friend HashCode AbslHashValue(HashCode h, EmptyCombine e) {
+ return HashCode::combine(std::move(h));
+ }
+};
+
+template <typename Int>
+struct CombineIterative {
+ template <typename HashCode>
+ friend HashCode AbslHashValue(HashCode h, CombineIterative c) {
+ for (int i = 0; i < 5; ++i) {
+ h = HashCode::combine(std::move(h), Int(i));
+ }
+ return h;
+ }
+};
+
+template <typename Int>
+struct CombineVariadic {
+ template <typename HashCode>
+ friend HashCode AbslHashValue(HashCode h, CombineVariadic c) {
+ return HashCode::combine(std::move(h), Int(0), Int(1), Int(2), Int(3),
+ Int(4));
+ }
+};
+
+using InvokeTag = absl::hash_internal::InvokeHashTag;
+template <InvokeTag T>
+using InvokeTagConstant = std::integral_constant<InvokeTag, T>;
+
+template <InvokeTag... Tags>
+struct MinTag;
+
+template <InvokeTag a, InvokeTag b, InvokeTag... Tags>
+struct MinTag<a, b, Tags...> : MinTag<(a < b ? a : b), Tags...> {};
+
+template <InvokeTag a>
+struct MinTag<a> : InvokeTagConstant<a> {};
+
+template <InvokeTag... Tags>
+struct CustomHashType {
+ size_t value;
+};
+
+template <InvokeTag allowed, InvokeTag... tags>
+struct EnableIfContained
+ : std::enable_if<absl::disjunction<
+ std::integral_constant<bool, allowed == tags>...>::value> {};
+
+template <
+ typename H, InvokeTag... Tags,
+ typename = typename EnableIfContained<InvokeTag::kHashValue, Tags...>::type>
+H AbslHashValue(H state, CustomHashType<Tags...> t) {
+ static_assert(MinTag<Tags...>::value == InvokeTag::kHashValue, "");
+ return H::combine(std::move(state),
+ t.value + static_cast<int>(InvokeTag::kHashValue));
+}
+
+} // namespace
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace hash_internal {
+template <InvokeTag... Tags>
+struct is_uniquely_represented<
+ CustomHashType<Tags...>,
+ typename EnableIfContained<InvokeTag::kUniquelyRepresented, Tags...>::type>
+ : std::true_type {};
+} // namespace hash_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+namespace ABSL_INTERNAL_LEGACY_HASH_NAMESPACE {
+template <InvokeTag... Tags>
+struct hash<CustomHashType<Tags...>> {
+ template <InvokeTag... TagsIn, typename = typename EnableIfContained<
+ InvokeTag::kLegacyHash, TagsIn...>::type>
+ size_t operator()(CustomHashType<TagsIn...> t) const {
+ static_assert(MinTag<Tags...>::value == InvokeTag::kLegacyHash, "");
+ return t.value + static_cast<int>(InvokeTag::kLegacyHash);
+ }
+};
+} // namespace ABSL_INTERNAL_LEGACY_HASH_NAMESPACE
+#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+
+namespace std {
+template <InvokeTag... Tags> // NOLINT
+struct hash<CustomHashType<Tags...>> {
+ template <InvokeTag... TagsIn, typename = typename EnableIfContained<
+ InvokeTag::kStdHash, TagsIn...>::type>
+ size_t operator()(CustomHashType<TagsIn...> t) const {
+ static_assert(MinTag<Tags...>::value == InvokeTag::kStdHash, "");
+ return t.value + static_cast<int>(InvokeTag::kStdHash);
+ }
+};
+} // namespace std
+
+namespace {
+
+template <typename... T>
+void TestCustomHashType(InvokeTagConstant<InvokeTag::kNone>, T...) {
+ using type = CustomHashType<T::value...>;
+ SCOPED_TRACE(testing::PrintToString(std::vector<InvokeTag>{T::value...}));
+ EXPECT_TRUE(is_hashable<type>());
+ EXPECT_TRUE(is_hashable<const type>());
+ EXPECT_TRUE(is_hashable<const type&>());
+
+ const size_t offset = static_cast<int>(std::min({T::value...}));
+ EXPECT_EQ(SpyHash(type{7}), SpyHash(size_t{7 + offset}));
+}
+
+void TestCustomHashType(InvokeTagConstant<InvokeTag::kNone>) {
+#if ABSL_HASH_INTERNAL_CAN_POISON_
+ // is_hashable is false if we don't support any of the hooks.
+ using type = CustomHashType<>;
+ EXPECT_FALSE(is_hashable<type>());
+ EXPECT_FALSE(is_hashable<const type>());
+ EXPECT_FALSE(is_hashable<const type&>());
+#endif // ABSL_HASH_INTERNAL_CAN_POISON_
+}
+
+template <InvokeTag Tag, typename... T>
+void TestCustomHashType(InvokeTagConstant<Tag> tag, T... t) {
+ constexpr auto next = static_cast<InvokeTag>(static_cast<int>(Tag) + 1);
+ TestCustomHashType(InvokeTagConstant<next>(), tag, t...);
+ TestCustomHashType(InvokeTagConstant<next>(), t...);
+}
+
+TEST(HashTest, CustomHashType) {
+ TestCustomHashType(InvokeTagConstant<InvokeTag{}>());
+}
+
+TEST(HashTest, NoOpsAreEquivalent) {
+ EXPECT_EQ(Hash<NoOp>()({}), Hash<NoOp>()({}));
+ EXPECT_EQ(Hash<NoOp>()({}), Hash<EmptyCombine>()({}));
+}
+
+template <typename T>
+class HashIntTest : public testing::Test {
+};
+TYPED_TEST_CASE_P(HashIntTest);
+
+TYPED_TEST_P(HashIntTest, BasicUsage) {
+ EXPECT_NE(Hash<NoOp>()({}), Hash<TypeParam>()(0));
+ EXPECT_NE(Hash<NoOp>()({}),
+ Hash<TypeParam>()(std::numeric_limits<TypeParam>::max()));
+ if (std::numeric_limits<TypeParam>::min() != 0) {
+ EXPECT_NE(Hash<NoOp>()({}),
+ Hash<TypeParam>()(std::numeric_limits<TypeParam>::min()));
+ }
+
+ EXPECT_EQ(Hash<CombineIterative<TypeParam>>()({}),
+ Hash<CombineVariadic<TypeParam>>()({}));
+}
+
+REGISTER_TYPED_TEST_CASE_P(HashIntTest, BasicUsage);
+using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t, uint32_t,
+ uint64_t, size_t>;
+INSTANTIATE_TYPED_TEST_CASE_P(My, HashIntTest, IntTypes);
+
+struct StructWithPadding {
+ char c;
+ int i;
+
+ template <typename H>
+ friend H AbslHashValue(H hash_state, const StructWithPadding& s) {
+ return H::combine(std::move(hash_state), s.c, s.i);
+ }
+};
+
+static_assert(sizeof(StructWithPadding) > sizeof(char) + sizeof(int),
+ "StructWithPadding doesn't have padding");
+static_assert(std::is_standard_layout<StructWithPadding>::value, "");
+
+// This check has to be disabled because libstdc++ doesn't support it.
+// static_assert(std::is_trivially_constructible<StructWithPadding>::value, "");
+
+template <typename T>
+struct ArraySlice {
+ T* begin;
+ T* end;
+
+ template <typename H>
+ friend H AbslHashValue(H hash_state, const ArraySlice& slice) {
+ for (auto t = slice.begin; t != slice.end; ++t) {
+ hash_state = H::combine(std::move(hash_state), *t);
+ }
+ return hash_state;
+ }
+};
+
+TEST(HashTest, HashNonUniquelyRepresentedType) {
+ // Create equal StructWithPadding objects that are known to have non-equal
+ // padding bytes.
+ static const size_t kNumStructs = 10;
+ unsigned char buffer1[kNumStructs * sizeof(StructWithPadding)];
+ std::memset(buffer1, 0, sizeof(buffer1));
+ auto* s1 = reinterpret_cast<StructWithPadding*>(buffer1);
+
+ unsigned char buffer2[kNumStructs * sizeof(StructWithPadding)];
+ std::memset(buffer2, 255, sizeof(buffer2));
+ auto* s2 = reinterpret_cast<StructWithPadding*>(buffer2);
+ for (int i = 0; i < kNumStructs; ++i) {
+ SCOPED_TRACE(i);
+ s1[i].c = s2[i].c = '0' + i;
+ s1[i].i = s2[i].i = i;
+ ASSERT_FALSE(memcmp(buffer1 + i * sizeof(StructWithPadding),
+ buffer2 + i * sizeof(StructWithPadding),
+ sizeof(StructWithPadding)) == 0)
+ << "Bug in test code: objects do not have unequal"
+ << " object representations";
+ }
+
+ EXPECT_EQ(Hash<StructWithPadding>()(s1[0]), Hash<StructWithPadding>()(s2[0]));
+ EXPECT_EQ(Hash<ArraySlice<StructWithPadding>>()({s1, s1 + kNumStructs}),
+ Hash<ArraySlice<StructWithPadding>>()({s2, s2 + kNumStructs}));
+}
+
+TEST(HashTest, StandardHashContainerUsage) {
+ std::unordered_map<int, std::string, Hash<int>> map = {{0, "foo"}, { 42, "bar" }};
+
+ EXPECT_NE(map.find(0), map.end());
+ EXPECT_EQ(map.find(1), map.end());
+ EXPECT_NE(map.find(0u), map.end());
+}
+
+struct ConvertibleFromNoOp {
+ ConvertibleFromNoOp(NoOp) {} // NOLINT(runtime/explicit)
+
+ template <typename H>
+ friend H AbslHashValue(H hash_state, ConvertibleFromNoOp) {
+ return H::combine(std::move(hash_state), 1);
+ }
+};
+
+TEST(HashTest, HeterogeneousCall) {
+ EXPECT_NE(Hash<ConvertibleFromNoOp>()(NoOp()),
+ Hash<NoOp>()(NoOp()));
+}
+
+TEST(IsUniquelyRepresentedTest, SanityTest) {
+ using absl::hash_internal::is_uniquely_represented;
+
+ EXPECT_TRUE(is_uniquely_represented<unsigned char>::value);
+ EXPECT_TRUE(is_uniquely_represented<int>::value);
+ EXPECT_FALSE(is_uniquely_represented<bool>::value);
+ EXPECT_FALSE(is_uniquely_represented<int*>::value);
+}
+
+struct IntAndString {
+ int i;
+ std::string s;
+
+ template <typename H>
+ friend H AbslHashValue(H hash_state, IntAndString int_and_string) {
+ return H::combine(std::move(hash_state), int_and_string.s,
+ int_and_string.i);
+ }
+};
+
+TEST(HashTest, SmallValueOn64ByteBoundary) {
+ Hash<IntAndString>()(IntAndString{0, std::string(63, '0')});
+}
+
+struct TypeErased {
+ size_t n;
+
+ template <typename H>
+ friend H AbslHashValue(H hash_state, const TypeErased& v) {
+ v.HashValue(absl::HashState::Create(&hash_state));
+ return hash_state;
+ }
+
+ void HashValue(absl::HashState state) const {
+ absl::HashState::combine(std::move(state), n);
+ }
+};
+
+TEST(HashTest, TypeErased) {
+ EXPECT_TRUE((is_hashable<TypeErased>::value));
+ EXPECT_TRUE((is_hashable<std::pair<TypeErased, int>>::value));
+
+ EXPECT_EQ(SpyHash(TypeErased{7}), SpyHash(size_t{7}));
+ EXPECT_NE(SpyHash(TypeErased{7}), SpyHash(size_t{13}));
+
+ EXPECT_EQ(SpyHash(std::make_pair(TypeErased{7}, 17)),
+ SpyHash(std::make_pair(size_t{7}, 17)));
+}
+
+} // namespace
diff --git a/absl/hash/hash_testing.h b/absl/hash/hash_testing.h
new file mode 100644
index 00000000..1c4db26a
--- /dev/null
+++ b/absl/hash/hash_testing.h
@@ -0,0 +1,376 @@
+// 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
+//
+// 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.
+
+#ifndef ABSL_HASH_HASH_TESTING_H_
+#define ABSL_HASH_HASH_TESTING_H_
+
+#include <initializer_list>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/hash/internal/spy_hash_state.h"
+#include "absl/meta/type_traits.h"
+#include "absl/strings/str_cat.h"
+#include "absl/types/variant.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+// Run the absl::Hash algorithm over all the elements passed in and verify that
+// their hash expansion is congruent with their `==` operator.
+//
+// It is used in conjunction with EXPECT_TRUE. Failures will output information
+// on what requirement failed and on which objects.
+//
+// Users should pass a collection of types as either an initializer list or a
+// container of cases.
+//
+// EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
+// {v1, v2, ..., vN}));
+//
+// std::vector<MyType> cases;
+// // Fill cases...
+// EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
+//
+// Users can pass a variety of types for testing heterogeneous lookup with
+// `std::make_tuple`:
+//
+// EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
+// std::make_tuple(v1, v2, ..., vN)));
+//
+//
+// Ideally, the values passed should provide enough coverage of the `==`
+// operator and the AbslHashValue implementations.
+// For dynamically sized types, the empty state should usually be included in
+// the values.
+//
+// The function accepts an optional comparator function, in case that `==` is
+// not enough for the values provided.
+//
+// Usage:
+//
+// EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
+// std::make_tuple(v1, v2, ..., vN), MyCustomEq{}));
+//
+// It checks the following requirements:
+// 1. The expansion for a value is deterministic.
+// 2. For any two objects `a` and `b` in the sequence, if `a == b` evaluates
+// to true, then their hash expansion must be equal.
+// 3. If `a == b` evaluates to false their hash expansion must be unequal.
+// 4. If `a == b` evaluates to false neither hash expansion can be a
+// suffix of the other.
+// 5. AbslHashValue overloads should not be called by the user. They are only
+// meant to be called by the framework. Users should call H::combine() and
+// H::combine_contiguous().
+// 6. No moved-from instance of the hash state is used in the implementation
+// of AbslHashValue.
+//
+// The values do not have to have the same type. This can be useful for
+// equivalent types that support heterogeneous lookup.
+//
+// A possible reason for breaking (2) is combining state in the hash expansion
+// that was not used in `==`.
+// For example:
+//
+// struct Bad2 {
+// int a, b;
+// template <typename H>
+// friend H AbslHashValue(H state, Bad2 x) {
+// // Uses a and b.
+// return H::combine(std::move(state), x.a, x.b);
+// }
+// friend bool operator==(Bad2 x, Bad2 y) {
+// // Only uses a.
+// return x.a == y.a;
+// }
+// };
+//
+// As for (3), breaking this usually means that there is state being passed to
+// the `==` operator that is not used in the hash expansion.
+// For example:
+//
+// struct Bad3 {
+// int a, b;
+// template <typename H>
+// friend H AbslHashValue(H state, Bad3 x) {
+// // Only uses a.
+// return H::combine(std::move(state), x.a);
+// }
+// friend bool operator==(Bad3 x, Bad3 y) {
+// // Uses a and b.
+// return x.a == y.a && x.b == y.b;
+// }
+// };
+//
+// Finally, a common way to break 4 is by combining dynamic ranges without
+// combining the size of the range.
+// For example:
+//
+// struct Bad4 {
+// int *p, size;
+// template <typename H>
+// friend H AbslHashValue(H state, Bad4 x) {
+// return H::combine_contiguous(std::move(state), x.p, x.p + x.size);
+// }
+// friend bool operator==(Bad4 x, Bad4 y) {
+// // Compare two ranges for equality. C++14 code can instead use std::equal.
+// return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size);
+// }
+// };
+//
+// An easy solution to this is to combine the size after combining the range,
+// like so:
+// template <typename H>
+// friend H AbslHashValue(H state, Bad4 x) {
+// return H::combine(
+// H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);
+// }
+//
+template <int&... ExplicitBarrier, typename Container>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(const Container& values);
+
+template <int&... ExplicitBarrier, typename Container, typename Eq>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals);
+
+template <int&..., typename T>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values);
+
+template <int&..., typename T, typename Eq>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
+ Eq equals);
+
+namespace hash_internal {
+
+struct PrintVisitor {
+ size_t index;
+ template <typename T>
+ std::string operator()(const T* value) const {
+ return absl::StrCat("#", index, "(", testing::PrintToString(*value), ")");
+ }
+};
+
+template <typename Eq>
+struct EqVisitor {
+ Eq eq;
+ template <typename T, typename U>
+ bool operator()(const T* t, const U* u) const {
+ return eq(*t, *u);
+ }
+};
+
+struct ExpandVisitor {
+ template <typename T>
+ SpyHashState operator()(const T* value) const {
+ return SpyHashState::combine(SpyHashState(), *value);
+ }
+};
+
+template <typename Container, typename Eq>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
+ using V = typename Container::value_type;
+
+ struct Info {
+ const V& value;
+ size_t index;
+ std::string ToString() const { return absl::visit(PrintVisitor{index}, value); }
+ SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); }
+ };
+
+ using EqClass = std::vector<Info>;
+ std::vector<EqClass> classes;
+
+ // Gather the values in equivalence classes.
+ size_t i = 0;
+ for (const auto& value : values) {
+ EqClass* c = nullptr;
+ for (auto& eqclass : classes) {
+ if (absl::visit(EqVisitor<Eq>{equals}, value, eqclass[0].value)) {
+ c = &eqclass;
+ break;
+ }
+ }
+ if (c == nullptr) {
+ classes.emplace_back();
+ c = &classes.back();
+ }
+ c->push_back({value, i});
+ ++i;
+
+ // Verify potential errors captured by SpyHashState.
+ if (auto error = c->back().expand().error()) {
+ return testing::AssertionFailure() << *error;
+ }
+ }
+
+ if (classes.size() < 2) {
+ return testing::AssertionFailure()
+ << "At least two equivalence classes are expected.";
+ }
+
+ // We assume that equality is correctly implemented.
+ // Now we verify that AbslHashValue is also correctly implemented.
+
+ for (const auto& c : classes) {
+ // All elements of the equivalence class must have the same hash
+ // expansion.
+ const SpyHashState expected = c[0].expand();
+ for (const Info& v : c) {
+ if (v.expand() != v.expand()) {
+ return testing::AssertionFailure()
+ << "Hash expansion for " << v.ToString()
+ << " is non-deterministic.";
+ }
+ if (v.expand() != expected) {
+ return testing::AssertionFailure()
+ << "Values " << c[0].ToString() << " and " << v.ToString()
+ << " evaluate as equal but have an unequal hash expansion.";
+ }
+ }
+
+ // Elements from other classes must have different hash expansion.
+ for (const auto& c2 : classes) {
+ if (&c == &c2) continue;
+ const SpyHashState c2_hash = c2[0].expand();
+ switch (SpyHashState::Compare(expected, c2_hash)) {
+ case SpyHashState::CompareResult::kEqual:
+ return testing::AssertionFailure()
+ << "Values " << c[0].ToString() << " and " << c2[0].ToString()
+ << " evaluate as unequal but have an equal hash expansion.";
+ case SpyHashState::CompareResult::kBSuffixA:
+ return testing::AssertionFailure()
+ << "Hash expansion of " << c2[0].ToString()
+ << " is a suffix of the hash expansion of " << c[0].ToString()
+ << ".";
+ case SpyHashState::CompareResult::kASuffixB:
+ return testing::AssertionFailure()
+ << "Hash expansion of " << c[0].ToString()
+ << " is a suffix of the hash expansion of " << c2[0].ToString()
+ << ".";
+ case SpyHashState::CompareResult::kUnequal:
+ break;
+ }
+ }
+ }
+ return testing::AssertionSuccess();
+}
+
+template <typename... T>
+struct TypeSet {
+ template <typename U, bool = disjunction<std::is_same<T, U>...>::value>
+ struct Insert {
+ using type = TypeSet<U, T...>;
+ };
+ template <typename U>
+ struct Insert<U, true> {
+ using type = TypeSet;
+ };
+
+ template <template <typename...> class C>
+ using apply = C<T...>;
+};
+
+template <typename... T>
+struct MakeTypeSet : TypeSet<> {};
+template <typename T, typename... Ts>
+struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type {};
+
+template <typename... T>
+using VariantForTypes = typename MakeTypeSet<
+ const typename std::decay<T>::type*...>::template apply<absl::variant>;
+
+template <typename Container>
+struct ContainerAsVector {
+ using V = absl::variant<const typename Container::value_type*>;
+ using Out = std::vector<V>;
+
+ static Out Do(const Container& values) {
+ Out out;
+ for (const auto& v : values) out.push_back(&v);
+ return out;
+ }
+};
+
+template <typename... T>
+struct ContainerAsVector<std::tuple<T...>> {
+ using V = VariantForTypes<T...>;
+ using Out = std::vector<V>;
+
+ template <size_t... I>
+ static Out DoImpl(const std::tuple<T...>& tuple, absl::index_sequence<I...>) {
+ return Out{&std::get<I>(tuple)...};
+ }
+
+ static Out Do(const std::tuple<T...>& values) {
+ return DoImpl(values, absl::index_sequence_for<T...>());
+ }
+};
+
+template <>
+struct ContainerAsVector<std::tuple<>> {
+ static std::vector<VariantForTypes<int>> Do(std::tuple<>) { return {}; }
+};
+
+struct DefaultEquals {
+ template <typename T, typename U>
+ bool operator()(const T& t, const U& u) const {
+ return t == u;
+ }
+};
+
+} // namespace hash_internal
+
+template <int&..., typename Container>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(const Container& values) {
+ return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
+ hash_internal::ContainerAsVector<Container>::Do(values),
+ hash_internal::DefaultEquals{});
+}
+
+template <int&..., typename Container, typename Eq>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
+ return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
+ hash_internal::ContainerAsVector<Container>::Do(values), equals);
+}
+
+template <int&..., typename T>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values) {
+ return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
+ hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
+ hash_internal::DefaultEquals{});
+}
+
+template <int&..., typename T, typename Eq>
+ABSL_MUST_USE_RESULT testing::AssertionResult
+VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
+ Eq equals) {
+ return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
+ hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
+ equals);
+}
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_HASH_HASH_TESTING_H_
diff --git a/absl/hash/internal/city.cc b/absl/hash/internal/city.cc
new file mode 100644
index 00000000..5c076fb5
--- /dev/null
+++ b/absl/hash/internal/city.cc
@@ -0,0 +1,346 @@
+// 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
+//
+// 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.
+//
+// This file provides CityHash64() and related functions.
+//
+// It's probably possible to create even faster hash functions by
+// writing a program that systematically explores some of the space of
+// possible hash functions, by using SIMD instructions, or by
+// compromising on hash quality.
+
+#include "absl/hash/internal/city.h"
+
+#include <string.h> // for memcpy and memset
+#include <algorithm>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/endian.h"
+#include "absl/base/internal/unaligned_access.h"
+#include "absl/base/optimization.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace hash_internal {
+
+#ifdef ABSL_IS_BIG_ENDIAN
+#define uint32_in_expected_order(x) (absl::gbswap_32(x))
+#define uint64_in_expected_order(x) (absl::gbswap_64(x))
+#else
+#define uint32_in_expected_order(x) (x)
+#define uint64_in_expected_order(x) (x)
+#endif
+
+static uint64_t Fetch64(const char *p) {
+ return uint64_in_expected_order(ABSL_INTERNAL_UNALIGNED_LOAD64(p));
+}
+
+static uint32_t Fetch32(const char *p) {
+ return uint32_in_expected_order(ABSL_INTERNAL_UNALIGNED_LOAD32(p));
+}
+
+// Some primes between 2^63 and 2^64 for various uses.
+static const uint64_t k0 = 0xc3a5c85c97cb3127ULL;
+static const uint64_t k1 = 0xb492b66fbe98f273ULL;
+static const uint64_t k2 = 0x9ae16a3b2f90404fULL;
+
+// Magic numbers for 32-bit hashing. Copied from Murmur3.
+static const uint32_t c1 = 0xcc9e2d51;
+static const uint32_t c2 = 0x1b873593;
+
+// A 32-bit to 32-bit integer hash copied from Murmur3.
+static uint32_t fmix(uint32_t h) {
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+ return h;
+}
+
+static uint32_t Rotate32(uint32_t val, int shift) {
+ // Avoid shifting by 32: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
+}
+
+#undef PERMUTE3
+#define PERMUTE3(a, b, c) \
+ do { \
+ std::swap(a, b); \
+ std::swap(a, c); \
+ } while (0)
+
+static uint32_t Mur(uint32_t a, uint32_t h) {
+ // Helper from Murmur3 for combining two 32-bit values.
+ a *= c1;
+ a = Rotate32(a, 17);
+ a *= c2;
+ h ^= a;
+ h = Rotate32(h, 19);
+ return h * 5 + 0xe6546b64;
+}
+
+static uint32_t Hash32Len13to24(const char *s, size_t len) {
+ uint32_t a = Fetch32(s - 4 + (len >> 1));
+ uint32_t b = Fetch32(s + 4);
+ uint32_t c = Fetch32(s + len - 8);
+ uint32_t d = Fetch32(s + (len >> 1));
+ uint32_t e = Fetch32(s);
+ uint32_t f = Fetch32(s + len - 4);
+ uint32_t h = len;
+
+ return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));
+}
+
+static uint32_t Hash32Len0to4(const char *s, size_t len) {
+ uint32_t b = 0;
+ uint32_t c = 9;
+ for (size_t i = 0; i < len; i++) {
+ signed char v = s[i];
+ b = b * c1 + v;
+ c ^= b;
+ }
+ return fmix(Mur(b, Mur(len, c)));
+}
+
+static uint32_t Hash32Len5to12(const char *s, size_t len) {
+ uint32_t a = len, b = len * 5, c = 9, d = b;
+ a += Fetch32(s);
+ b += Fetch32(s + len - 4);
+ c += Fetch32(s + ((len >> 1) & 4));
+ return fmix(Mur(c, Mur(b, Mur(a, d))));
+}
+
+uint32_t CityHash32(const char *s, size_t len) {
+ if (len <= 24) {
+ return len <= 12
+ ? (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len))
+ : Hash32Len13to24(s, len);
+ }
+
+ // len > 24
+ uint32_t h = len, g = c1 * len, f = g;
+
+ uint32_t a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
+ uint32_t a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
+ uint32_t a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
+ uint32_t a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
+ uint32_t a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
+ h ^= a0;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ h ^= a2;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= a1;
+ g = Rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ g ^= a3;
+ g = Rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ f += a4;
+ f = Rotate32(f, 19);
+ f = f * 5 + 0xe6546b64;
+ size_t iters = (len - 1) / 20;
+ do {
+ uint32_t b0 = Rotate32(Fetch32(s) * c1, 17) * c2;
+ uint32_t b1 = Fetch32(s + 4);
+ uint32_t b2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;
+ uint32_t b3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;
+ uint32_t b4 = Fetch32(s + 16);
+ h ^= b0;
+ h = Rotate32(h, 18);
+ h = h * 5 + 0xe6546b64;
+ f += b1;
+ f = Rotate32(f, 19);
+ f = f * c1;
+ g += b2;
+ g = Rotate32(g, 18);
+ g = g * 5 + 0xe6546b64;
+ h ^= b3 + b1;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= b4;
+ g = absl::gbswap_32(g) * 5;
+ h += b4 * 5;
+ h = absl::gbswap_32(h);
+ f += b0;
+ PERMUTE3(f, h, g);
+ s += 20;
+ } while (--iters != 0);
+ g = Rotate32(g, 11) * c1;
+ g = Rotate32(g, 17) * c1;
+ f = Rotate32(f, 11) * c1;
+ f = Rotate32(f, 17) * c1;
+ h = Rotate32(h + g, 19);
+ h = h * 5 + 0xe6546b64;
+ h = Rotate32(h, 17) * c1;
+ h = Rotate32(h + f, 19);
+ h = h * 5 + 0xe6546b64;
+ h = Rotate32(h, 17) * c1;
+ return h;
+}
+
+// Bitwise right rotate. Normally this will compile to a single
+// instruction, especially if the shift is a manifest constant.
+static uint64_t Rotate(uint64_t val, int shift) {
+ // Avoid shifting by 64: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
+}
+
+static uint64_t ShiftMix(uint64_t val) { return val ^ (val >> 47); }
+
+static uint64_t HashLen16(uint64_t u, uint64_t v) {
+ return Hash128to64(uint128(u, v));
+}
+
+static uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) {
+ // Murmur-inspired hashing.
+ uint64_t a = (u ^ v) * mul;
+ a ^= (a >> 47);
+ uint64_t b = (v ^ a) * mul;
+ b ^= (b >> 47);
+ b *= mul;
+ return b;
+}
+
+static uint64_t HashLen0to16(const char *s, size_t len) {
+ if (len >= 8) {
+ uint64_t mul = k2 + len * 2;
+ uint64_t a = Fetch64(s) + k2;
+ uint64_t b = Fetch64(s + len - 8);
+ uint64_t c = Rotate(b, 37) * mul + a;
+ uint64_t d = (Rotate(a, 25) + b) * mul;
+ return HashLen16(c, d, mul);
+ }
+ if (len >= 4) {
+ uint64_t mul = k2 + len * 2;
+ uint64_t a = Fetch32(s);
+ return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
+ }
+ if (len > 0) {
+ uint8_t a = s[0];
+ uint8_t b = s[len >> 1];
+ uint8_t c = s[len - 1];
+ uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
+ uint32_t z = len + (static_cast<uint32_t>(c) << 2);
+ return ShiftMix(y * k2 ^ z * k0) * k2;
+ }
+ return k2;
+}
+
+// This probably works well for 16-byte strings as well, but it may be overkill
+// in that case.
+static uint64_t HashLen17to32(const char *s, size_t len) {
+ uint64_t mul = k2 + len * 2;
+ uint64_t a = Fetch64(s) * k1;
+ uint64_t b = Fetch64(s + 8);
+ uint64_t c = Fetch64(s + len - 8) * mul;
+ uint64_t d = Fetch64(s + len - 16) * k2;
+ return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d,
+ a + Rotate(b + k2, 18) + c, mul);
+}
+
+// Return a 16-byte hash for 48 bytes. Quick and dirty.
+// Callers do best to use "random-looking" values for a and b.
+static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(uint64_t w, uint64_t x,
+ uint64_t y, uint64_t z,
+ uint64_t a, uint64_t b) {
+ a += w;
+ b = Rotate(b + a + z, 21);
+ uint64_t c = a;
+ a += x;
+ a += y;
+ b += Rotate(a, 44);
+ return std::make_pair(a + z, b + c);
+}
+
+// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
+static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(const char *s, uint64_t a,
+ uint64_t b) {
+ return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16),
+ Fetch64(s + 24), a, b);
+}
+
+// Return an 8-byte hash for 33 to 64 bytes.
+static uint64_t HashLen33to64(const char *s, size_t len) {
+ uint64_t mul = k2 + len * 2;
+ uint64_t a = Fetch64(s) * k2;
+ uint64_t b = Fetch64(s + 8);
+ uint64_t c = Fetch64(s + len - 24);
+ uint64_t d = Fetch64(s + len - 32);
+ uint64_t e = Fetch64(s + 16) * k2;
+ uint64_t f = Fetch64(s + 24) * 9;
+ uint64_t g = Fetch64(s + len - 8);
+ uint64_t h = Fetch64(s + len - 16) * mul;
+ uint64_t u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
+ uint64_t v = ((a + g) ^ d) + f + 1;
+ uint64_t w = absl::gbswap_64((u + v) * mul) + h;
+ uint64_t x = Rotate(e + f, 42) + c;
+ uint64_t y = (absl::gbswap_64((v + w) * mul) + g) * mul;
+ uint64_t z = e + f + c;
+ a = absl::gbswap_64((x + z) * mul + y) + b;
+ b = ShiftMix((z + a) * mul + d + h) * mul;
+ return b + x;
+}
+
+uint64_t CityHash64(const char *s, size_t len) {
+ if (len <= 32) {
+ if (len <= 16) {
+ return HashLen0to16(s, len);
+ } else {
+ return HashLen17to32(s, len);
+ }
+ } else if (len <= 64) {
+ return HashLen33to64(s, len);
+ }
+
+ // For strings over 64 bytes we hash the end first, and then as we
+ // loop we keep 56 bytes of state: v, w, x, y, and z.
+ uint64_t x = Fetch64(s + len - 40);
+ uint64_t y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
+ uint64_t z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
+ std::pair<uint64_t, uint64_t> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
+ std::pair<uint64_t, uint64_t> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
+ x = x * k1 + Fetch64(s);
+
+ // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
+ len = (len - 1) & ~static_cast<size_t>(63);
+ do {
+ x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+ y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+ x ^= w.second;
+ y += v.first + Fetch64(s + 40);
+ z = Rotate(z + w.first, 33) * k1;
+ v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+ w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+ std::swap(z, x);
+ s += 64;
+ len -= 64;
+ } while (len != 0);
+ return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,
+ HashLen16(v.second, w.second) + x);
+}
+
+uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed) {
+ return CityHash64WithSeeds(s, len, k2, seed);
+}
+
+uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0,
+ uint64_t seed1) {
+ return HashLen16(CityHash64(s, len) - seed0, seed1);
+}
+
+} // namespace hash_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/hash/internal/city.h b/absl/hash/internal/city.h
new file mode 100644
index 00000000..46c18ffa
--- /dev/null
+++ b/absl/hash/internal/city.h
@@ -0,0 +1,94 @@
+// 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
+//
+// 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.
+//
+// http://code.google.com/p/cityhash/
+//
+// This file provides a few functions for hashing strings. All of them are
+// high-quality functions in the sense that they pass standard tests such
+// as Austin Appleby's SMHasher. They are also fast.
+//
+// For 64-bit x86 code, on short strings, we don't know of anything faster than
+// CityHash64 that is of comparable quality. We believe our nearest competitor
+// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash
+// tables and most other hashing (excluding cryptography).
+//
+// For 32-bit x86 code, we don't know of anything faster than CityHash32 that
+// is of comparable quality. We believe our nearest competitor is Murmur3A.
+// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.)
+//
+// Functions in the CityHash family are not suitable for cryptography.
+//
+// Please see CityHash's README file for more details on our performance
+// measurements and so on.
+//
+// WARNING: This code has been only lightly tested on big-endian platforms!
+// It is known to work well on little-endian platforms that have a small penalty
+// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
+// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
+// bug reports are welcome.
+//
+// By the way, for some hash functions, given strings a and b, the hash
+// of a+b is easily derived from the hashes of a and b. This property
+// doesn't hold for any hash functions in this file.
+
+#ifndef ABSL_HASH_INTERNAL_CITY_H_
+#define ABSL_HASH_INTERNAL_CITY_H_
+
+#include <stdint.h>
+#include <stdlib.h> // for size_t.
+#include <utility>
+
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace hash_internal {
+
+typedef std::pair<uint64_t, uint64_t> uint128;
+
+inline uint64_t Uint128Low64(const uint128 &x) { return x.first; }
+inline uint64_t Uint128High64(const uint128 &x) { return x.second; }
+
+// Hash function for a byte array.
+uint64_t CityHash64(const char *s, size_t len);
+
+// Hash function for a byte array. For convenience, a 64-bit seed is also
+// hashed into the result.
+uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed);
+
+// Hash function for a byte array. For convenience, two seeds are also
+// hashed into the result.
+uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0,
+ uint64_t seed1);
+
+// Hash function for a byte array. Most useful in 32-bit binaries.
+uint32_t CityHash32(const char *s, size_t len);
+
+// Hash 128 input bits down to 64 bits of output.
+// This is intended to be a reasonably good hash function.
+inline uint64_t Hash128to64(const uint128 &x) {
+ // Murmur-inspired hashing.
+ const uint64_t kMul = 0x9ddfea08eb382d69ULL;
+ uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
+ a ^= (a >> 47);
+ uint64_t b = (Uint128High64(x) ^ a) * kMul;
+ b ^= (b >> 47);
+ b *= kMul;
+ return b;
+}
+
+} // namespace hash_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_HASH_INTERNAL_CITY_H_
diff --git a/absl/hash/internal/city_test.cc b/absl/hash/internal/city_test.cc
new file mode 100644
index 00000000..f305ed9e
--- /dev/null
+++ b/absl/hash/internal/city_test.cc
@@ -0,0 +1,595 @@
+// 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
+//
+// 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.
+
+#include "absl/hash/internal/city.h"
+
+#include <string.h>
+#include <cstdio>
+#include <iostream>
+#include "gtest/gtest.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace hash_internal {
+
+static const uint64_t k0 = 0xc3a5c85c97cb3127ULL;
+static const uint64_t kSeed0 = 1234567;
+static const uint64_t kSeed1 = k0;
+static const int kDataSize = 1 << 20;
+static const int kTestSize = 300;
+
+static char data[kDataSize];
+
+// Initialize data to pseudorandom values.
+void setup() {
+ uint64_t a = 9;
+ uint64_t b = 777;
+ for (int i = 0; i < kDataSize; i++) {
+ a += b;
+ b += a;
+ a = (a ^ (a >> 41)) * k0;
+ b = (b ^ (b >> 41)) * k0 + i;
+ uint8_t u = b >> 37;
+ memcpy(data + i, &u, 1); // uint8_t -> char
+ }
+}
+
+#define C(x) 0x##x##ULL
+static const uint64_t testdata[kTestSize][4] = {
+ {C(9ae16a3b2f90404f), C(75106db890237a4a), C(3feac5f636039766),
+ C(dc56d17a)},
+ {C(541150e87f415e96), C(1aef0d24b3148a1a), C(bacc300e1e82345a),
+ C(99929334)},
+ {C(f3786a4b25827c1), C(34ee1a2bf767bd1c), C(2f15ca2ebfb631f2), C(4252edb7)},
+ {C(ef923a7a1af78eab), C(79163b1e1e9a9b18), C(df3b2aca6e1e4a30),
+ C(ebc34f3c)},
+ {C(11df592596f41d88), C(843ec0bce9042f9c), C(cce2ea1e08b1eb30),
+ C(26f2b463)},
+ {C(831f448bdc5600b3), C(62a24be3120a6919), C(1b44098a41e010da),
+ C(b042c047)},
+ {C(3eca803e70304894), C(d80de767e4a920a), C(a51cfbb292efd53d), C(e73bb0a8)},
+ {C(1b5a063fb4c7f9f1), C(318dbc24af66dee9), C(10ef7b32d5c719af),
+ C(91dfdd75)},
+ {C(a0f10149a0e538d6), C(69d008c20f87419f), C(41b36376185b3e9e),
+ C(c87f95de)},
+ {C(fb8d9c70660b910b), C(a45b0cc3476bff1b), C(b28d1996144f0207),
+ C(3f5538ef)},
+ {C(236827beae282a46), C(e43970221139c946), C(4f3ac6faa837a3aa),
+ C(70eb1a1f)},
+ {C(c385e435136ecf7c), C(d9d17368ff6c4a08), C(1b31eed4e5251a67),
+ C(cfd63b83)},
+ {C(e3f6828b6017086d), C(21b4d1900554b3b0), C(bef38be1809e24f1),
+ C(894a52ef)},
+ {C(851fff285561dca0), C(4d1277d73cdf416f), C(28ccffa61010ebe2),
+ C(9cde6a54)},
+ {C(61152a63595a96d9), C(d1a3a91ef3a7ba45), C(443b6bb4a493ad0c),
+ C(6c4898d5)},
+ {C(44473e03be306c88), C(30097761f872472a), C(9fd1b669bfad82d7),
+ C(13e1978e)},
+ {C(3ead5f21d344056), C(fb6420393cfb05c3), C(407932394cbbd303), C(51b4ba8)},
+ {C(6abbfde37ee03b5b), C(83febf188d2cc113), C(cda7b62d94d5b8ee),
+ C(b6b06e40)},
+ {C(943e7ed63b3c080), C(1ef207e9444ef7f8), C(ef4a9f9f8c6f9b4a), C(240a2f2)},
+ {C(d72ce05171ef8a1a), C(c6bd6bd869203894), C(c760e6396455d23a),
+ C(5dcefc30)},
+ {C(4182832b52d63735), C(337097e123eea414), C(b5a72ca0456df910),
+ C(7a48b105)},
+ {C(d6cdae892584a2cb), C(58de0fa4eca17dcd), C(43df30b8f5f1cb00),
+ C(fd55007b)},
+ {C(5c8e90bc267c5ee4), C(e9ae044075d992d9), C(f234cbfd1f0a1e59),
+ C(6b95894c)},
+ {C(bbd7f30ac310a6f3), C(b23b570d2666685f), C(fb13fb08c9814fe7),
+ C(3360e827)},
+ {C(36a097aa49519d97), C(8204380a73c4065), C(77c2004bdd9e276a), C(45177e0b)},
+ {C(dc78cb032c49217), C(112464083f83e03a), C(96ae53e28170c0f5), C(7c6fffe4)},
+ {C(441593e0da922dfe), C(936ef46061469b32), C(204a1921197ddd87),
+ C(bbc78da4)},
+ {C(2ba3883d71cc2133), C(72f2bbb32bed1a3c), C(27e1bd96d4843251),
+ C(c5c25d39)},
+ {C(f2b6d2adf8423600), C(7514e2f016a48722), C(43045743a50396ba),
+ C(b6e5d06e)},
+ {C(38fffe7f3680d63c), C(d513325255a7a6d1), C(31ed47790f6ca62f),
+ C(6178504e)},
+ {C(b7477bf0b9ce37c6), C(63b1c580a7fd02a4), C(f6433b9f10a5dac), C(bd4c3637)},
+ {C(55bdb0e71e3edebd), C(c7ab562bcf0568bc), C(43166332f9ee684f),
+ C(6e7ac474)},
+ {C(782fa1b08b475e7), C(fb7138951c61b23b), C(9829105e234fb11e), C(1fb4b518)},
+ {C(c5dc19b876d37a80), C(15ffcff666cfd710), C(e8c30c72003103e2),
+ C(31d13d6d)},
+ {C(5e1141711d2d6706), C(b537f6dee8de6933), C(3af0a1fbbe027c54),
+ C(26fa72e3)},
+ {C(782edf6da001234f), C(f48cbd5c66c48f3), C(808754d1e64e2a32), C(6a7433bf)},
+ {C(d26285842ff04d44), C(8f38d71341eacca9), C(5ca436f4db7a883c),
+ C(4e6df758)},
+ {C(c6ab830865a6bae6), C(6aa8e8dd4b98815c), C(efe3846713c371e5),
+ C(d57f63ea)},
+ {C(44b3a1929232892), C(61dca0e914fc217), C(a607cc142096b964), C(52ef73b3)},
+ {C(4b603d7932a8de4f), C(fae64c464b8a8f45), C(8fafab75661d602a), C(3cb36c3)},
+ {C(4ec0b54cf1566aff), C(30d2c7269b206bf4), C(77c22e82295e1061),
+ C(72c39bea)},
+ {C(ed8b7a4b34954ff7), C(56432de31f4ee757), C(85bd3abaa572b155),
+ C(a65aa25c)},
+ {C(5d28b43694176c26), C(714cc8bc12d060ae), C(3437726273a83fe6),
+ C(74740539)},
+ {C(6a1ef3639e1d202e), C(919bc1bd145ad928), C(30f3f7e48c28a773),
+ C(c3ae3c26)},
+ {C(159f4d9e0307b111), C(3e17914a5675a0c), C(af849bd425047b51), C(f29db8a2)},
+ {C(cc0a840725a7e25b), C(57c69454396e193a), C(976eaf7eee0b4540),
+ C(1ef4cbf4)},
+ {C(a2b27ee22f63c3f1), C(9ebde0ce1b3976b2), C(2fe6a92a257af308),
+ C(a9be6c41)},
+ {C(d8f2f234899bcab3), C(b10b037297c3a168), C(debea2c510ceda7f), C(fa31801)},
+ {C(584f28543864844f), C(d7cee9fc2d46f20d), C(a38dca5657387205),
+ C(8331c5d8)},
+ {C(a94be46dd9aa41af), C(a57e5b7723d3f9bd), C(34bf845a52fd2f), C(e9876db8)},
+ {C(9a87bea227491d20), C(a468657e2b9c43e7), C(af9ba60db8d89ef7),
+ C(27b0604e)},
+ {C(27688c24958d1a5c), C(e3b4a1c9429cf253), C(48a95811f70d64bc),
+ C(dcec07f2)},
+ {C(5d1d37790a1873ad), C(ed9cd4bcc5fa1090), C(ce51cde05d8cd96a),
+ C(cff0a82a)},
+ {C(1f03fd18b711eea9), C(566d89b1946d381a), C(6e96e83fc92563ab),
+ C(fec83621)},
+ {C(f0316f286cf527b6), C(f84c29538de1aa5a), C(7612ed3c923d4a71), C(743d8dc)},
+ {C(297008bcb3e3401d), C(61a8e407f82b0c69), C(a4a35bff0524fa0e),
+ C(64d41d26)},
+ {C(43c6252411ee3be), C(b4ca1b8077777168), C(2746dc3f7da1737f), C(acd90c81)},
+ {C(ce38a9a54fad6599), C(6d6f4a90b9e8755e), C(c3ecc79ff105de3f),
+ C(7c746a4b)},
+ {C(270a9305fef70cf), C(600193999d884f3a), C(f4d49eae09ed8a1), C(b1047e99)},
+ {C(e71be7c28e84d119), C(eb6ace59932736e6), C(70c4397807ba12c5),
+ C(d1fd1068)},
+ {C(b5b58c24b53aaa19), C(d2a6ab0773dd897f), C(ef762fe01ecb5b97),
+ C(56486077)},
+ {C(44dd59bd301995cf), C(3ccabd76493ada1a), C(540db4c87d55ef23),
+ C(6069be80)},
+ {C(b4d4789eb6f2630b), C(bf6973263ce8ef0e), C(d1c75c50844b9d3), C(2078359b)},
+ {C(12807833c463737c), C(58e927ea3b3776b4), C(72dd20ef1c2f8ad0),
+ C(9ea21004)},
+ {C(e88419922b87176f), C(bcf32f41a7ddbf6f), C(d6ebefd8085c1a0f),
+ C(9c9cfe88)},
+ {C(105191e0ec8f7f60), C(5918dbfcca971e79), C(6b285c8a944767b9),
+ C(b70a6ddd)},
+ {C(a5b88bf7399a9f07), C(fca3ddfd96461cc4), C(ebe738fdc0282fc6),
+ C(dea37298)},
+ {C(d08c3f5747d84f50), C(4e708b27d1b6f8ac), C(70f70fd734888606),
+ C(8f480819)},
+ {C(2f72d12a40044b4b), C(889689352fec53de), C(f03e6ad87eb2f36), C(30b3b16)},
+ {C(aa1f61fdc5c2e11e), C(c2c56cd11277ab27), C(a1e73069fdf1f94f),
+ C(f31bc4e8)},
+ {C(9489b36fe2246244), C(3355367033be74b8), C(5f57c2277cbce516),
+ C(419f953b)},
+ {C(358d7c0476a044cd), C(e0b7b47bcbd8854f), C(ffb42ec696705519),
+ C(20e9e76d)},
+ {C(b0c48df14275265a), C(9da4448975905efa), C(d716618e414ceb6d),
+ C(646f0ff8)},
+ {C(daa70bb300956588), C(410ea6883a240c6d), C(f5c8239fb5673eb3),
+ C(eeb7eca8)},
+ {C(4ec97a20b6c4c7c2), C(5913b1cd454f29fd), C(a9629f9daf06d685), C(8112bb9)},
+ {C(5c3323628435a2e8), C(1bea45ce9e72a6e3), C(904f0a7027ddb52e),
+ C(85a6d477)},
+ {C(c1ef26bea260abdb), C(6ee423f2137f9280), C(df2118b946ed0b43),
+ C(56f76c84)},
+ {C(6be7381b115d653a), C(ed046190758ea511), C(de6a45ffc3ed1159),
+ C(9af45d55)},
+ {C(ae3eece1711b2105), C(14fd3f4027f81a4a), C(abb7e45177d151db),
+ C(d1c33760)},
+ {C(376c28588b8fb389), C(6b045e84d8491ed2), C(4e857effb7d4e7dc),
+ C(c56bbf69)},
+ {C(58d943503bb6748f), C(419c6c8e88ac70f6), C(586760cbf3d3d368),
+ C(abecfb9b)},
+ {C(dfff5989f5cfd9a1), C(bcee2e7ea3a96f83), C(681c7874adb29017),
+ C(8de13255)},
+ {C(7fb19eb1a496e8f5), C(d49e5dfdb5c0833f), C(c0d5d7b2f7c48dc7),
+ C(a98ee299)},
+ {C(5dba5b0dadccdbaa), C(4ba8da8ded87fcdc), C(f693fdd25badf2f0),
+ C(3015f556)},
+ {C(688bef4b135a6829), C(8d31d82abcd54e8e), C(f95f8a30d55036d7),
+ C(5a430e29)},
+ {C(d8323be05433a412), C(8d48fa2b2b76141d), C(3d346f23978336a5),
+ C(2797add0)},
+ {C(3b5404278a55a7fc), C(23ca0b327c2d0a81), C(a6d65329571c892c),
+ C(27d55016)},
+ {C(2a96a3f96c5e9bbc), C(8caf8566e212dda8), C(904de559ca16e45e),
+ C(84945a82)},
+ {C(22bebfdcc26d18ff), C(4b4d8dcb10807ba1), C(40265eee30c6b896),
+ C(3ef7e224)},
+ {C(627a2249ec6bbcc2), C(c0578b462a46735a), C(4974b8ee1c2d4f1f),
+ C(35ed8dc8)},
+ {C(3abaf1667ba2f3e0), C(ee78476b5eeadc1), C(7e56ac0a6ca4f3f4), C(6a75e43d)},
+ {C(3931ac68c5f1b2c9), C(efe3892363ab0fb0), C(40b707268337cd36),
+ C(235d9805)},
+ {C(b98fb0606f416754), C(46a6e5547ba99c1e), C(c909d82112a8ed2), C(f7d69572)},
+ {C(7f7729a33e58fcc4), C(2e4bc1e7a023ead4), C(e707008ea7ca6222),
+ C(bacd0199)},
+ {C(42a0aa9ce82848b3), C(57232730e6bee175), C(f89bb3f370782031),
+ C(e428f50e)},
+ {C(6b2c6d38408a4889), C(de3ef6f68fb25885), C(20754f456c203361),
+ C(81eaaad3)},
+ {C(930380a3741e862a), C(348d28638dc71658), C(89dedcfd1654ea0d),
+ C(addbd3e3)},
+ {C(94808b5d2aa25f9a), C(cec72968128195e0), C(d9f4da2bdc1e130f),
+ C(e66dbca0)},
+ {C(b31abb08ae6e3d38), C(9eb9a95cbd9e8223), C(8019e79b7ee94ea9),
+ C(afe11fd5)},
+ {C(dccb5534a893ea1a), C(ce71c398708c6131), C(fe2396315457c164),
+ C(a71a406f)},
+ {C(6369163565814de6), C(8feb86fb38d08c2f), C(4976933485cc9a20),
+ C(9d90eaf5)},
+ {C(edee4ff253d9f9b3), C(96ef76fb279ef0ad), C(a4d204d179db2460),
+ C(6665db10)},
+ {C(941993df6e633214), C(929bc1beca5b72c6), C(141fc52b8d55572d),
+ C(9c977cbf)},
+ {C(859838293f64cd4c), C(484403b39d44ad79), C(bf674e64d64b9339),
+ C(ee83ddd4)},
+ {C(c19b5648e0d9f555), C(328e47b2b7562993), C(e756b92ba4bd6a51), C(26519cc)},
+ {C(f963b63b9006c248), C(9e9bf727ffaa00bc), C(c73bacc75b917e3a),
+ C(a485a53f)},
+ {C(6a8aa0852a8c1f3b), C(c8f1e5e206a21016), C(2aa554aed1ebb524),
+ C(f62bc412)},
+ {C(740428b4d45e5fb8), C(4c95a4ce922cb0a5), C(e99c3ba78feae796),
+ C(8975a436)},
+ {C(658b883b3a872b86), C(2f0e303f0f64827a), C(975337e23dc45e1), C(94ff7f41)},
+ {C(6df0a977da5d27d4), C(891dd0e7cb19508), C(fd65434a0b71e680), C(760aa031)},
+ {C(a900275464ae07ef), C(11f2cfda34beb4a3), C(9abf91e5a1c38e4), C(3bda76df)},
+ {C(810bc8aa0c40bcb0), C(448a019568d01441), C(f60ec52f60d3aeae),
+ C(498e2e65)},
+ {C(22036327deb59ed7), C(adc05ceb97026a02), C(48bff0654262672b),
+ C(d38deb48)},
+ {C(7d14dfa9772b00c8), C(595735efc7eeaed7), C(29872854f94c3507),
+ C(82b3fb6b)},
+ {C(2d777cddb912675d), C(278d7b10722a13f9), C(f5c02bfb7cc078af),
+ C(e500e25f)},
+ {C(f2ec98824e8aa613), C(5eb7e3fb53fe3bed), C(12c22860466e1dd4),
+ C(bd2bb07c)},
+ {C(5e763988e21f487f), C(24189de8065d8dc5), C(d1519d2403b62aa0),
+ C(3a2b431d)},
+ {C(48949dc327bb96ad), C(e1fd21636c5c50b4), C(3f6eb7f13a8712b4),
+ C(7322a83d)},
+ {C(b7c4209fb24a85c5), C(b35feb319c79ce10), C(f0d3de191833b922),
+ C(a645ca1c)},
+ {C(9c9e5be0943d4b05), C(b73dc69e45201cbb), C(aab17180bfe5083d),
+ C(8909a45a)},
+ {C(3898bca4dfd6638d), C(f911ff35efef0167), C(24bdf69e5091fc88),
+ C(bd30074c)},
+ {C(5b5d2557400e68e7), C(98d610033574cee), C(dfd08772ce385deb), C(c17cf001)},
+ {C(a927ed8b2bf09bb6), C(606e52f10ae94eca), C(71c2203feb35a9ee),
+ C(26ffd25a)},
+ {C(8d25746414aedf28), C(34b1629d28b33d3a), C(4d5394aea5f82d7b),
+ C(f1d8ce3c)},
+ {C(b5bbdb73458712f2), C(1ff887b3c2a35137), C(7f7231f702d0ace9),
+ C(3ee8fb17)},
+ {C(3d32a26e3ab9d254), C(fc4070574dc30d3a), C(f02629579c2b27c9),
+ C(a77acc2a)},
+ {C(9371d3c35fa5e9a5), C(42967cf4d01f30), C(652d1eeae704145c), C(f4556dee)},
+ {C(cbaa3cb8f64f54e0), C(76c3b48ee5c08417), C(9f7d24e87e61ce9), C(de287a64)},
+ {C(b2e23e8116c2ba9f), C(7e4d9c0060101151), C(3310da5e5028f367),
+ C(878e55b9)},
+ {C(8aa77f52d7868eb9), C(4d55bd587584e6e2), C(d2db37041f495f5), C(7648486)},
+ {C(858fea922c7fe0c3), C(cfe8326bf733bc6f), C(4e5e2018cf8f7dfc),
+ C(57ac0fb1)},
+ {C(46ef25fdec8392b1), C(e48d7b6d42a5cd35), C(56a6fe1c175299ca),
+ C(d01967ca)},
+ {C(8d078f726b2df464), C(b50ee71cdcabb299), C(f4af300106f9c7ba),
+ C(96ecdf74)},
+ {C(35ea86e6960ca950), C(34fe1fe234fc5c76), C(a00207a3dc2a72b7),
+ C(779f5506)},
+ {C(8aee9edbc15dd011), C(51f5839dc8462695), C(b2213e17c37dca2d),
+ C(3c94c2de)},
+ {C(c3e142ba98432dda), C(911d060cab126188), C(b753fbfa8365b844),
+ C(39f98faf)},
+ {C(123ba6b99c8cd8db), C(448e582672ee07c4), C(cebe379292db9e65),
+ C(7af31199)},
+ {C(ba87acef79d14f53), C(b3e0fcae63a11558), C(d5ac313a593a9f45),
+ C(e341a9d6)},
+ {C(bcd3957d5717dc3), C(2da746741b03a007), C(873816f4b1ece472), C(ca24aeeb)},
+ {C(61442ff55609168e), C(6447c5fc76e8c9cf), C(6a846de83ae15728),
+ C(b2252b57)},
+ {C(dbe4b1b2d174757f), C(506512da18712656), C(6857f3e0b8dd95f), C(72c81da1)},
+ {C(531e8e77b363161c), C(eece0b43e2dae030), C(8294b82c78f34ed1),
+ C(6b9fce95)},
+ {C(f71e9c926d711e2b), C(d77af2853a4ceaa1), C(9aa0d6d76a36fae7),
+ C(19399857)},
+ {C(cb20ac28f52df368), C(e6705ee7880996de), C(9b665cc3ec6972f2),
+ C(3c57a994)},
+ {C(e4a794b4acb94b55), C(89795358057b661b), C(9c4cdcec176d7a70),
+ C(c053e729)},
+ {C(cb942e91443e7208), C(e335de8125567c2a), C(d4d74d268b86df1f),
+ C(51cbbba7)},
+ {C(ecca7563c203f7ba), C(177ae2423ef34bb2), C(f60b7243400c5731),
+ C(1acde79a)},
+ {C(1652cb940177c8b5), C(8c4fe7d85d2a6d6d), C(f6216ad097e54e72),
+ C(2d160d13)},
+ {C(31fed0fc04c13ce8), C(3d5d03dbf7ff240a), C(727c5c9b51581203),
+ C(787f5801)},
+ {C(e7b668947590b9b3), C(baa41ad32938d3fa), C(abcbc8d4ca4b39e4),
+ C(c9629828)},
+ {C(1de2119923e8ef3c), C(6ab27c096cf2fe14), C(8c3658edca958891),
+ C(be139231)},
+ {C(1269df1e69e14fa7), C(992f9d58ac5041b7), C(e97fcf695a7cbbb4),
+ C(7df699ef)},
+ {C(820826d7aba567ff), C(1f73d28e036a52f3), C(41c4c5a73f3b0893),
+ C(8ce6b96d)},
+ {C(ffe0547e4923cef9), C(3534ed49b9da5b02), C(548a273700fba03d),
+ C(6f9ed99c)},
+ {C(72da8d1b11d8bc8b), C(ba94b56b91b681c6), C(4e8cc51bd9b0fc8c),
+ C(e0244796)},
+ {C(d62ab4e3f88fc797), C(ea86c7aeb6283ae4), C(b5b93e09a7fe465), C(4ccf7e75)},
+ {C(d0f06c28c7b36823), C(1008cb0874de4bb8), C(d6c7ff816c7a737b),
+ C(915cef86)},
+ {C(99b7042460d72ec6), C(2a53e5e2b8e795c2), C(53a78132d9e1b3e3),
+ C(5cb59482)},
+ {C(4f4dfcfc0ec2bae5), C(841233148268a1b8), C(9248a76ab8be0d3), C(6ca3f532)},
+ {C(fe86bf9d4422b9ae), C(ebce89c90641ef9c), C(1c84e2292c0b5659),
+ C(e24f3859)},
+ {C(a90d81060932dbb0), C(8acfaa88c5fbe92b), C(7c6f3447e90f7f3f),
+ C(adf5a9c7)},
+ {C(17938a1b0e7f5952), C(22cadd2f56f8a4be), C(84b0d1183d5ed7c1),
+ C(32264b75)},
+ {C(de9e0cb0e16f6e6d), C(238e6283aa4f6594), C(4fb9c914c2f0a13b),
+ C(a64b3376)},
+ {C(6d4b876d9b146d1a), C(aab2d64ce8f26739), C(d315f93600e83fe5), C(d33890e)},
+ {C(e698fa3f54e6ea22), C(bd28e20e7455358c), C(9ace161f6ea76e66),
+ C(926d4b63)},
+ {C(7bc0deed4fb349f7), C(1771aff25dc722fa), C(19ff0644d9681917),
+ C(d51ba539)},
+ {C(db4b15e88533f622), C(256d6d2419b41ce9), C(9d7c5378396765d5),
+ C(7f37636d)},
+ {C(922834735e86ecb2), C(363382685b88328e), C(e9c92960d7144630),
+ C(b98026c0)},
+ {C(30f1d72c812f1eb8), C(b567cd4a69cd8989), C(820b6c992a51f0bc),
+ C(b877767e)},
+ {C(168884267f3817e9), C(5b376e050f637645), C(1c18314abd34497a), C(aefae77)},
+ {C(82e78596ee3e56a7), C(25697d9c87f30d98), C(7600a8342834924d), C(f686911)},
+ {C(aa2d6cf22e3cc252), C(9b4dec4f5e179f16), C(76fb0fba1d99a99a),
+ C(3deadf12)},
+ {C(7bf5ffd7f69385c7), C(fc077b1d8bc82879), C(9c04e36f9ed83a24),
+ C(ccf02a4e)},
+ {C(e89c8ff9f9c6e34b), C(f54c0f669a49f6c4), C(fc3e46f5d846adef),
+ C(176c1722)},
+ {C(a18fbcdccd11e1f4), C(8248216751dfd65e), C(40c089f208d89d7c), C(26f82ad)},
+ {C(2d54f40cc4088b17), C(59d15633b0cd1399), C(a8cc04bb1bffd15b),
+ C(b5244f42)},
+ {C(69276946cb4e87c7), C(62bdbe6183be6fa9), C(3ba9773dac442a1a),
+ C(49a689e5)},
+ {C(668174a3f443df1d), C(407299392da1ce86), C(c2a3f7d7f2c5be28), C(59fcdd3)},
+ {C(5e29be847bd5046), C(b561c7f19c8f80c3), C(5e5abd5021ccaeaf), C(4f4b04e9)},
+ {C(cd0d79f2164da014), C(4c386bb5c5d6ca0c), C(8e771b03647c3b63),
+ C(8b00f891)},
+ {C(e0e6fc0b1628af1d), C(29be5fb4c27a2949), C(1c3f781a604d3630),
+ C(16e114f3)},
+ {C(2058927664adfd93), C(6e8f968c7963baa5), C(af3dced6fff7c394),
+ C(d6b6dadc)},
+ {C(dc107285fd8e1af7), C(a8641a0609321f3f), C(db06e89ffdc54466),
+ C(897e20ac)},
+ {C(fbba1afe2e3280f1), C(755a5f392f07fce), C(9e44a9a15402809a), C(f996e05d)},
+ {C(bfa10785ddc1011b), C(b6e1c4d2f670f7de), C(517d95604e4fcc1f),
+ C(c4306af6)},
+ {C(534cc35f0ee1eb4e), C(b703820f1f3b3dce), C(884aa164cf22363), C(6dcad433)},
+ {C(7ca6e3933995dac), C(fd118c77daa8188), C(3aceb7b5e7da6545), C(3c07374d)},
+ {C(f0d6044f6efd7598), C(e044d6ba4369856e), C(91968e4f8c8a1a4c),
+ C(f0f4602c)},
+ {C(3d69e52049879d61), C(76610636ea9f74fe), C(e9bf5602f89310c0),
+ C(3e1ea071)},
+ {C(79da242a16acae31), C(183c5f438e29d40), C(6d351710ae92f3de), C(67580f0c)},
+ {C(461c82656a74fb57), C(d84b491b275aa0f7), C(8f262cb29a6eb8b2),
+ C(4e109454)},
+ {C(53c1a66d0b13003), C(731f060e6fe797fc), C(daa56811791371e3), C(88a474a7)},
+ {C(d3a2efec0f047e9), C(1cabce58853e58ea), C(7a17b2eae3256be4), C(5b5bedd)},
+ {C(43c64d7484f7f9b2), C(5da002b64aafaeb7), C(b576c1e45800a716),
+ C(1aaddfa7)},
+ {C(a7dec6ad81cf7fa1), C(180c1ab708683063), C(95e0fd7008d67cff),
+ C(5be07fd8)},
+ {C(5408a1df99d4aff), C(b9565e588740f6bd), C(abf241813b08006e), C(cbca8606)},
+ {C(a8b27a6bcaeeed4b), C(aec1eeded6a87e39), C(9daf246d6fed8326),
+ C(bde64d01)},
+ {C(9a952a8246fdc269), C(d0dcfcac74ef278c), C(250f7139836f0f1f),
+ C(ee90cf33)},
+ {C(c930841d1d88684f), C(5eb66eb18b7f9672), C(e455d413008a2546),
+ C(4305c3ce)},
+ {C(94dc6971e3cf071a), C(994c7003b73b2b34), C(ea16e85978694e5), C(4b3a1d76)},
+ {C(7fc98006e25cac9), C(77fee0484cda86a7), C(376ec3d447060456), C(a8bb6d80)},
+ {C(bd781c4454103f6), C(612197322f49c931), C(b9cf17fd7e5462d5), C(1f9fa607)},
+ {C(da60e6b14479f9df), C(3bdccf69ece16792), C(18ebf45c4fecfdc9),
+ C(8d0e4ed2)},
+ {C(4ca56a348b6c4d3), C(60618537c3872514), C(2fbb9f0e65871b09), C(1bf31347)},
+ {C(ebd22d4b70946401), C(6863602bf7139017), C(c0b1ac4e11b00666),
+ C(1ae3fc5b)},
+ {C(3cc4693d6cbcb0c), C(501689ea1c70ffa), C(10a4353e9c89e364), C(459c3930)},
+ {C(38908e43f7ba5ef0), C(1ab035d4e7781e76), C(41d133e8c0a68ff7),
+ C(e00c4184)},
+ {C(34983ccc6aa40205), C(21802cad34e72bc4), C(1943e8fb3c17bb8), C(ffc7a781)},
+ {C(86215c45dcac9905), C(ea546afe851cae4b), C(d85b6457e489e374),
+ C(6a125480)},
+ {C(420fc255c38db175), C(d503cd0f3c1208d1), C(d4684e74c825a0bc),
+ C(88a1512b)},
+ {C(1d7a31f5bc8fe2f9), C(4763991092dcf836), C(ed695f55b97416f4),
+ C(549bbbe5)},
+ {C(94129a84c376a26e), C(c245e859dc231933), C(1b8f74fecf917453),
+ C(c133d38c)},
+ {C(1d3a9809dab05c8d), C(adddeb4f71c93e8), C(ef342eb36631edb), C(fcace348)},
+ {C(90fa3ccbd60848da), C(dfa6e0595b569e11), C(e585d067a1f5135d),
+ C(ed7b6f9a)},
+ {C(2dbb4fc71b554514), C(9650e04b86be0f82), C(60f2304fba9274d3),
+ C(6d907dda)},
+ {C(b98bf4274d18374a), C(1b669fd4c7f9a19a), C(b1f5972b88ba2b7a),
+ C(7a4d48d5)},
+ {C(d6781d0b5e18eb68), C(b992913cae09b533), C(58f6021caaee3a40),
+ C(e686f3db)},
+ {C(226651cf18f4884c), C(595052a874f0f51c), C(c9b75162b23bab42), C(cce7c55)},
+ {C(a734fb047d3162d6), C(e523170d240ba3a5), C(125a6972809730e8), C(f58b96b)},
+ {C(c6df6364a24f75a3), C(c294e2c84c4f5df8), C(a88df65c6a89313b),
+ C(1bbf6f60)},
+ {C(d8d1364c1fbcd10), C(2d7cc7f54832deaa), C(4e22c876a7c57625), C(ce5e0cc2)},
+ {C(aae06f9146db885f), C(3598736441e280d9), C(fba339b117083e55),
+ C(584cfd6f)},
+ {C(8955ef07631e3bcc), C(7d70965ea3926f83), C(39aed4134f8b2db6),
+ C(8f9bbc33)},
+ {C(ad611c609cfbe412), C(d3c00b18bf253877), C(90b2172e1f3d0bfd),
+ C(d7640d95)},
+ {C(d5339adc295d5d69), C(b633cc1dcb8b586a), C(ee84184cf5b1aeaf), C(3d12a2b)},
+ {C(40d0aeff521375a8), C(77ba1ad7ecebd506), C(547c6f1a7d9df427),
+ C(aaeafed0)},
+ {C(8b2d54ae1a3df769), C(11e7adaee3216679), C(3483781efc563e03),
+ C(95b9b814)},
+ {C(99c175819b4eae28), C(932e8ff9f7a40043), C(ec78dcab07ca9f7c),
+ C(45fbe66e)},
+ {C(2a418335779b82fc), C(af0295987849a76b), C(c12bc5ff0213f46e),
+ C(b4baa7a8)},
+ {C(3b1fc6a3d279e67d), C(70ea1e49c226396), C(25505adcf104697c), C(83e962fe)},
+ {C(d97eacdf10f1c3c9), C(b54f4654043a36e0), C(b128f6eb09d1234), C(aac3531c)},
+ {C(293a5c1c4e203cd4), C(6b3329f1c130cefe), C(f2e32f8ec76aac91),
+ C(2b1db7cc)},
+ {C(4290e018ffaedde7), C(a14948545418eb5e), C(72d851b202284636),
+ C(cf00cd31)},
+ {C(f919a59cbde8bf2f), C(a56d04203b2dc5a5), C(38b06753ac871e48),
+ C(7d3c43b8)},
+ {C(1d70a3f5521d7fa4), C(fb97b3fdc5891965), C(299d49bbbe3535af),
+ C(cbd5fac6)},
+ {C(6af98d7b656d0d7c), C(d2e99ae96d6b5c0c), C(f63bd1603ef80627),
+ C(76d0fec4)},
+ {C(395b7a8adb96ab75), C(582df7165b20f4a), C(e52bd30e9ff657f9), C(405e3402)},
+ {C(3822dd82c7df012f), C(b9029b40bd9f122b), C(fd25b988468266c4),
+ C(c732c481)},
+ {C(79f7efe4a80b951a), C(dd3a3fddfc6c9c41), C(ab4c812f9e27aa40),
+ C(a8d123c9)},
+ {C(ae6e59f5f055921a), C(e9d9b7bf68e82), C(5ce4e4a5b269cc59), C(1e80ad7d)},
+ {C(8959dbbf07387d36), C(b4658afce48ea35d), C(8f3f82437d8cb8d6),
+ C(52aeb863)},
+ {C(4739613234278a49), C(99ea5bcd340bf663), C(258640912e712b12),
+ C(ef7c0c18)},
+ {C(420e6c926bc54841), C(96dbbf6f4e7c75cd), C(d8d40fa70c3c67bb),
+ C(b6ad4b68)},
+ {C(c8601bab561bc1b7), C(72b26272a0ff869a), C(56fdfc986d6bc3c4),
+ C(c1e46b17)},
+ {C(b2d294931a0e20eb), C(284ffd9a0815bc38), C(1f8a103aac9bbe6), C(57b8df25)},
+ {C(7966f53c37b6c6d7), C(8e6abcfb3aa2b88f), C(7f2e5e0724e5f345),
+ C(e9fa36d6)},
+ {C(be9bb0abd03b7368), C(13bca93a3031be55), C(e864f4f52b55b472),
+ C(8f8daefc)},
+ {C(a08d128c5f1649be), C(a8166c3dbbe19aad), C(cb9f914f829ec62c), C(6e1bb7e)},
+ {C(7c386f0ffe0465ac), C(530419c9d843dbf3), C(7450e3a4f72b8d8c),
+ C(fd0076f0)},
+ {C(bb362094e7ef4f8), C(ff3c2a48966f9725), C(55152803acd4a7fe), C(899b17b6)},
+ {C(cd80dea24321eea4), C(52b4fdc8130c2b15), C(f3ea100b154bfb82),
+ C(e3e84e31)},
+ {C(d599a04125372c3a), C(313136c56a56f363), C(1e993c3677625832),
+ C(eef79b6b)},
+ {C(dbbf541e9dfda0a), C(1479fceb6db4f844), C(31ab576b59062534), C(868e3315)},
+ {C(c2ee3288be4fe2bf), C(c65d2f5ddf32b92), C(af6ecdf121ba5485), C(4639a426)},
+ {C(d86603ced1ed4730), C(f9de718aaada7709), C(db8b9755194c6535),
+ C(f3213646)},
+ {C(915263c671b28809), C(a815378e7ad762fd), C(abec6dc9b669f559),
+ C(17f148e9)},
+ {C(2b67cdd38c307a5e), C(cb1d45bb5c9fe1c), C(800baf2a02ec18ad), C(bfd94880)},
+ {C(2d107419073b9cd0), C(a96db0740cef8f54), C(ec41ee91b3ecdc1b),
+ C(bb1fa7f3)},
+ {C(f3e9487ec0e26dfc), C(1ab1f63224e837fa), C(119983bb5a8125d8), C(88816b1)},
+ {C(1160987c8fe86f7d), C(879e6db1481eb91b), C(d7dcb802bfe6885d),
+ C(5c2faeb3)},
+ {C(eab8112c560b967b), C(97f550b58e89dbae), C(846ed506d304051f),
+ C(51b5fc6f)},
+ {C(1addcf0386d35351), C(b5f436561f8f1484), C(85d38e22181c9bb1),
+ C(33d94752)},
+ {C(d445ba84bf803e09), C(1216c2497038f804), C(2293216ea2237207),
+ C(b0c92948)},
+ {C(37235a096a8be435), C(d9b73130493589c2), C(3b1024f59378d3be),
+ C(c7171590)},
+ {C(763ad6ea2fe1c99d), C(cf7af5368ac1e26b), C(4d5e451b3bb8d3d4),
+ C(240a67fb)},
+ {C(ea627fc84cd1b857), C(85e372494520071f), C(69ec61800845780b),
+ C(e1843cd5)},
+ {C(1f2ffd79f2cdc0c8), C(726a1bc31b337aaa), C(678b7f275ef96434),
+ C(fda1452b)},
+ {C(39a9e146ec4b3210), C(f63f75802a78b1ac), C(e2e22539c94741c3),
+ C(a2cad330)},
+ {C(74cba303e2dd9d6d), C(692699b83289fad1), C(dfb9aa7874678480),
+ C(53467e16)},
+ {C(4cbc2b73a43071e0), C(56c5db4c4ca4e0b7), C(1b275a162f46bd3d),
+ C(da14a8d0)},
+ {C(875638b9715d2221), C(d9ba0615c0c58740), C(616d4be2dfe825aa),
+ C(67333551)},
+ {C(fb686b2782994a8d), C(edee60693756bb48), C(e6bc3cae0ded2ef5),
+ C(a0ebd66e)},
+ {C(ab21d81a911e6723), C(4c31b07354852f59), C(835da384c9384744),
+ C(4b769593)},
+ {C(33d013cc0cd46ecf), C(3de726423aea122c), C(116af51117fe21a9),
+ C(6aa75624)},
+ {C(8ca92c7cd39fae5d), C(317e620e1bf20f1), C(4f0b33bf2194b97f), C(602a3f96)},
+ {C(fdde3b03f018f43e), C(38f932946c78660), C(c84084ce946851ee), C(cd183c4d)},
+ {C(9c8502050e9c9458), C(d6d2a1a69964beb9), C(1675766f480229b5),
+ C(960a4d07)},
+ {C(348176ca2fa2fdd2), C(3a89c514cc360c2d), C(9f90b8afb318d6d0),
+ C(9ae998c4)},
+ {C(4a3d3dfbbaea130b), C(4e221c920f61ed01), C(553fd6cd1304531f),
+ C(74e2179d)},
+ {C(b371f768cdf4edb9), C(bdef2ace6d2de0f0), C(e05b4100f7f1baec),
+ C(ee9bae25)},
+ {C(7a1d2e96934f61f), C(eb1760ae6af7d961), C(887eb0da063005df), C(b66edf10)},
+ {C(8be53d466d4728f2), C(86a5ac8e0d416640), C(984aa464cdb5c8bb),
+ C(d6209737)},
+ {C(829677eb03abf042), C(43cad004b6bc2c0), C(f2f224756803971a), C(b994a88)},
+ {C(754435bae3496fc), C(5707fc006f094dcf), C(8951c86ab19d8e40), C(a05d43c0)},
+ {C(fda9877ea8e3805f), C(31e868b6ffd521b7), C(b08c90681fb6a0fd),
+ C(c79f73a8)},
+ {C(2e36f523ca8f5eb5), C(8b22932f89b27513), C(331cd6ecbfadc1bb),
+ C(a490aff5)},
+ {C(21a378ef76828208), C(a5c13037fa841da2), C(506d22a53fbe9812),
+ C(dfad65b4)},
+ {C(ccdd5600054b16ca), C(f78846e84204cb7b), C(1f9faec82c24eac9), C(1d07dfb)},
+ {C(7854468f4e0cabd0), C(3a3f6b4f098d0692), C(ae2423ec7799d30d),
+ C(416df9a0)},
+ {C(7f88db5346d8f997), C(88eac9aacc653798), C(68a4d0295f8eefa1),
+ C(1f8fb9cc)},
+ {C(bb3fb5fb01d60fcf), C(1b7cc0847a215eb6), C(1246c994437990a1),
+ C(7abf48e3)},
+ {C(2e783e1761acd84d), C(39158042bac975a0), C(1cd21c5a8071188d),
+ C(dea4e3dd)},
+ {C(392058251cf22acc), C(944ec4475ead4620), C(b330a10b5cb94166),
+ C(c6064f22)},
+ {C(adf5c1e5d6419947), C(2a9747bc659d28aa), C(95c5b8cb1f5d62c), C(743bed9c)},
+ {C(6bc1db2c2bee5aba), C(e63b0ed635307398), C(7b2eca111f30dbbc),
+ C(fce254d5)},
+ {C(b00f898229efa508), C(83b7590ad7f6985c), C(2780e70a0592e41d),
+ C(e47ec9d1)},
+ {C(b56eb769ce0d9a8c), C(ce196117bfbcaf04), C(b26c3c3797d66165),
+ C(334a145c)},
+ {C(70c0637675b94150), C(259e1669305b0a15), C(46e1dd9fd387a58d),
+ C(adec1e3c)},
+ {C(74c0b8a6821faafe), C(abac39d7491370e7), C(faf0b2a48a4e6aed),
+ C(f6a9fbf8)},
+ {C(5fb5e48ac7b7fa4f), C(a96170f08f5acbc7), C(bbf5c63d4f52a1e5),
+ C(5398210c)},
+};
+
+void TestUnchanging(const uint64_t* expected, int offset, int len) {
+ EXPECT_EQ(expected[0], CityHash64(data + offset, len));
+ EXPECT_EQ(expected[3], CityHash32(data + offset, len));
+ EXPECT_EQ(expected[1], CityHash64WithSeed(data + offset, len, kSeed0));
+ EXPECT_EQ(expected[2],
+ CityHash64WithSeeds(data + offset, len, kSeed0, kSeed1));
+}
+
+TEST(CityHashTest, Unchanging) {
+ setup();
+ int i = 0;
+ for (; i < kTestSize - 1; i++) {
+ TestUnchanging(testdata[i], i * i, i);
+ }
+ TestUnchanging(testdata[i], 0, kDataSize);
+}
+
+} // namespace hash_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc
new file mode 100644
index 00000000..3e553625
--- /dev/null
+++ b/absl/hash/internal/hash.cc
@@ -0,0 +1,25 @@
+// 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
+//
+// 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.
+
+#include "absl/hash/internal/hash.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace hash_internal {
+
+ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed;
+
+} // namespace hash_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h
new file mode 100644
index 00000000..a51ca954
--- /dev/null
+++ b/absl/hash/internal/hash.h
@@ -0,0 +1,898 @@
+// 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
+//
+// 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.
+//
+// -----------------------------------------------------------------------------
+// File: hash.h
+// -----------------------------------------------------------------------------
+//
+#ifndef ABSL_HASH_INTERNAL_HASH_H_
+#define ABSL_HASH_INTERNAL_HASH_H_
+
+#include <algorithm>
+#include <array>
+#include <cmath>
+#include <cstring>
+#include <deque>
+#include <forward_list>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "absl/base/internal/endian.h"
+#include "absl/base/port.h"
+#include "absl/container/fixed_array.h"
+#include "absl/meta/type_traits.h"
+#include "absl/numeric/int128.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "absl/types/variant.h"
+#include "absl/utility/utility.h"
+#include "absl/hash/internal/city.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace hash_internal {
+
+// HashStateBase
+//
+// A hash state object represents an intermediate state in the computation
+// of an unspecified hash algorithm. `HashStateBase` provides a CRTP style
+// base class for hash state implementations. Developers adding type support
+// for `absl::Hash` should not rely on any parts of the state object other than
+// the following member functions:
+//
+// * HashStateBase::combine()
+// * HashStateBase::combine_contiguous()
+//
+// A derived hash state class of type `H` must provide a static member function
+// with a signature similar to the following:
+//
+// `static H combine_contiguous(H state, const unsigned char*, size_t)`.
+//
+// `HashStateBase` will provide a complete implementations for a hash state
+// object in terms of this method.
+//
+// Example:
+//
+// // Use CRTP to define your derived class.
+// struct MyHashState : HashStateBase<MyHashState> {
+// static H combine_contiguous(H state, const unsigned char*, size_t);
+// using MyHashState::HashStateBase::combine;
+// using MyHashState::HashStateBase::combine_contiguous;
+// };
+template <typename H>
+class HashStateBase {
+ public:
+ // HashStateBase::combine()
+ //
+ // Combines an arbitrary number of values into a hash state, returning the
+ // updated state.
+ //
+ // Each of the value types `T` must be separately hashable by the Abseil
+ // hashing framework.
+ //
+ // NOTE:
+ //
+ // state = H::combine(std::move(state), value1, value2, value3);
+ //
+ // is guaranteed to produce the same hash expansion as:
+ //
+ // state = H::combine(std::move(state), value1);
+ // state = H::combine(std::move(state), value2);
+ // state = H::combine(std::move(state), value3);
+ template <typename T, typename... Ts>
+ static H combine(H state, const T& value, const Ts&... values);
+ static H combine(H state) { return state; }
+
+ // HashStateBase::combine_contiguous()
+ //
+ // Combines a contiguous array of `size` elements into a hash state, returning
+ // the updated state.
+ //
+ // NOTE:
+ //
+ // state = H::combine_contiguous(std::move(state), data, size);
+ //
+ // is NOT guaranteed to produce the same hash expansion as a for-loop (it may
+ // perform internal optimizations). If you need this guarantee, use the
+ // for-loop instead.
+ template <typename T>
+ static H combine_contiguous(H state, const T* data, size_t size);
+};
+
+// is_uniquely_represented
+//
+// `is_uniquely_represented<T>` is a trait class that indicates whether `T`
+// is uniquely represented.
+//
+// A type is "uniquely represented" if two equal values of that type are
+// guaranteed to have the same bytes in their underlying storage. In other
+// words, if `a == b`, then `memcmp(&a, &b, sizeof(T))` is guaranteed to be
+// zero. This property cannot be detected automatically, so this trait is false
+// by default, but can be specialized by types that wish to assert that they are
+// uniquely represented. This makes them eligible for certain optimizations.
+//
+// If you have any doubt whatsoever, do not specialize this template.
+// The default is completely safe, and merely disables some optimizations
+// that will not matter for most types. Specializing this template,
+// on the other hand, can be very hazardous.
+//
+// To be uniquely represented, a type must not have multiple ways of
+// representing the same value; for example, float and double are not
+// uniquely represented, because they have distinct representations for
+// +0 and -0. Furthermore, the type's byte representation must consist
+// solely of user-controlled data, with no padding bits and no compiler-
+// controlled data such as vptrs or sanitizer metadata. This is usually
+// very difficult to guarantee, because in most cases the compiler can
+// insert data and padding bits at its own discretion.
+//
+// If you specialize this template for a type `T`, you must do so in the file
+// that defines that type (or in this file). If you define that specialization
+// anywhere else, `is_uniquely_represented<T>` could have different meanings
+// in different places.
+//
+// The Enable parameter is meaningless; it is provided as a convenience,
+// to support certain SFINAE techniques when defining specializations.
+template <typename T, typename Enable = void>
+struct is_uniquely_represented : std::false_type {};
+
+// is_uniquely_represented<unsigned char>
+//
+// unsigned char is a synonym for "byte", so it is guaranteed to be
+// uniquely represented.
+template <>
+struct is_uniquely_represented<unsigned char> : std::true_type {};
+
+// is_uniquely_represented for non-standard integral types
+//
+// Integral types other than bool should be uniquely represented on any
+// platform that this will plausibly be ported to.
+template <typename Integral>
+struct is_uniquely_represented<
+ Integral, typename std::enable_if<std::is_integral<Integral>::value>::type>
+ : std::true_type {};
+
+// is_uniquely_represented<bool>
+//
+//
+template <>
+struct is_uniquely_represented<bool> : std::false_type {};
+
+// hash_bytes()
+//
+// Convenience function that combines `hash_state` with the byte representation
+// of `value`.
+template <typename H, typename T>
+H hash_bytes(H hash_state, const T& value) {
+ const unsigned char* start = reinterpret_cast<const unsigned char*>(&value);
+ return H::combine_contiguous(std::move(hash_state), start, sizeof(value));
+}
+
+// -----------------------------------------------------------------------------
+// AbslHashValue for Basic Types
+// -----------------------------------------------------------------------------
+
+// Note: Default `AbslHashValue` implementations live in `hash_internal`. This
+// allows us to block lexical scope lookup when doing an unqualified call to
+// `AbslHashValue` below. User-defined implementations of `AbslHashValue` can
+// only be found via ADL.
+
+// AbslHashValue() for hashing bool values
+//
+// We use SFINAE to ensure that this overload only accepts bool, not types that
+// are convertible to bool.
+template <typename H, typename B>
+typename std::enable_if<std::is_same<B, bool>::value, H>::type AbslHashValue(
+ H hash_state, B value) {
+ return H::combine(std::move(hash_state),
+ static_cast<unsigned char>(value ? 1 : 0));
+}
+
+// AbslHashValue() for hashing enum values
+template <typename H, typename Enum>
+typename std::enable_if<std::is_enum<Enum>::value, H>::type AbslHashValue(
+ H hash_state, Enum e) {
+ // In practice, we could almost certainly just invoke hash_bytes directly,
+ // but it's possible that a sanitizer might one day want to
+ // store data in the unused bits of an enum. To avoid that risk, we
+ // convert to the underlying type before hashing. Hopefully this will get
+ // optimized away; if not, we can reopen discussion with c-toolchain-team.
+ return H::combine(std::move(hash_state),
+ static_cast<typename std::underlying_type<Enum>::type>(e));
+}
+// AbslHashValue() for hashing floating-point values
+template <typename H, typename Float>
+typename std::enable_if<std::is_floating_point<Float>::value, H>::type
+AbslHashValue(H hash_state, Float value) {
+ return hash_internal::hash_bytes(std::move(hash_state),
+ value == 0 ? 0 : value);
+}
+
+// Long double has the property that it might have extra unused bytes in it.
+// For example, in x86 sizeof(long double)==16 but it only really uses 80-bits
+// of it. This means we can't use hash_bytes on a long double and have to
+// convert it to something else first.
+template <typename H>
+H AbslHashValue(H hash_state, long double value) {
+ const int category = std::fpclassify(value);
+ switch (category) {
+ case FP_INFINITE:
+ // Add the sign bit to differentiate between +Inf and -Inf
+ hash_state = H::combine(std::move(hash_state), std::signbit(value));
+ break;
+
+ case FP_NAN:
+ case FP_ZERO:
+ default:
+ // Category is enough for these.
+ break;
+
+ case FP_NORMAL:
+ case FP_SUBNORMAL:
+ // We can't convert `value` directly to double because this would have
+ // undefined behavior if the value is out of range.
+ // std::frexp gives us a value in the range (-1, -.5] or [.5, 1) that is
+ // guaranteed to be in range for `double`. The truncation is
+ // implementation defined, but that works as long as it is deterministic.
+ int exp;
+ auto mantissa = static_cast<double>(std::frexp(value, &exp));
+ hash_state = H::combine(std::move(hash_state), mantissa, exp);
+ }
+
+ return H::combine(std::move(hash_state), category);
+}
+
+// AbslHashValue() for hashing pointers
+template <typename H, typename T>
+H AbslHashValue(H hash_state, T* ptr) {
+ return hash_internal::hash_bytes(std::move(hash_state), ptr);
+}
+
+// AbslHashValue() for hashing nullptr_t
+template <typename H>
+H AbslHashValue(H hash_state, std::nullptr_t) {
+ return H::combine(std::move(hash_state), static_cast<void*>(nullptr));
+}
+
+// -----------------------------------------------------------------------------
+// AbslHashValue for Composite Types
+// -----------------------------------------------------------------------------
+
+// is_hashable()
+//
+// Trait class which returns true if T is hashable by the absl::Hash framework.
+// Used for the AbslHashValue implementations for composite types below.
+template <typename T>
+struct is_hashable;
+
+// AbslHashValue() for hashing pairs
+template <typename H, typename T1, typename T2>
+typename std::enable_if<is_hashable<T1>::value && is_hashable<T2>::value,
+ H>::type
+AbslHashValue(H hash_state, const std::pair<T1, T2>& p) {
+ return H::combine(std::move(hash_state), p.first, p.second);
+}
+
+// hash_tuple()
+//
+// Helper function for hashing a tuple. The third argument should
+// be an index_sequence running from 0 to tuple_size<Tuple> - 1.
+template <typename H, typename Tuple, size_t... Is>
+H hash_tuple(H hash_state, const Tuple& t, absl::index_sequence<Is...>) {
+ return H::combine(std::move(hash_state), std::get<Is>(t)...);
+}
+
+// AbslHashValue for hashing tuples
+template <typename H, typename... Ts>
+#if defined(_MSC_VER)
+// This SFINAE gets MSVC confused under some conditions. Let's just disable it
+// for now.
+H
+#else // _MSC_VER
+typename std::enable_if<absl::conjunction<is_hashable<Ts>...>::value, H>::type
+#endif // _MSC_VER
+AbslHashValue(H hash_state, const std::tuple<Ts...>& t) {
+ return hash_internal::hash_tuple(std::move(hash_state), t,
+ absl::make_index_sequence<sizeof...(Ts)>());
+}
+
+// -----------------------------------------------------------------------------
+// AbslHashValue for Pointers
+// -----------------------------------------------------------------------------
+
+// AbslHashValue for hashing unique_ptr
+template <typename H, typename T, typename D>
+H AbslHashValue(H hash_state, const std::unique_ptr<T, D>& ptr) {
+ return H::combine(std::move(hash_state), ptr.get());
+}
+
+// AbslHashValue for hashing shared_ptr
+template <typename H, typename T>
+H AbslHashValue(H hash_state, const std::shared_ptr<T>& ptr) {
+ return H::combine(std::move(hash_state), ptr.get());
+}
+
+// -----------------------------------------------------------------------------
+// AbslHashValue for String-Like Types
+// -----------------------------------------------------------------------------
+
+// AbslHashValue for hashing strings
+//
+// All the string-like types supported here provide the same hash expansion for
+// the same character sequence. These types are:
+//
+// - `std::string` (and std::basic_string<char, std::char_traits<char>, A> for
+// any allocator A)
+// - `absl::string_view` and `std::string_view`
+//
+// For simplicity, we currently support only `char` strings. This support may
+// be broadened, if necessary, but with some caution - this overload would
+// misbehave in cases where the traits' `eq()` member isn't equivalent to `==`
+// on the underlying character type.
+template <typename H>
+H AbslHashValue(H hash_state, absl::string_view str) {
+ return H::combine(
+ H::combine_contiguous(std::move(hash_state), str.data(), str.size()),
+ str.size());
+}
+
+// -----------------------------------------------------------------------------
+// AbslHashValue for Sequence Containers
+// -----------------------------------------------------------------------------
+
+// AbslHashValue for hashing std::array
+template <typename H, typename T, size_t N>
+typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
+ H hash_state, const std::array<T, N>& array) {
+ return H::combine_contiguous(std::move(hash_state), array.data(),
+ array.size());
+}
+
+// AbslHashValue for hashing std::deque
+template <typename H, typename T, typename Allocator>
+typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
+ H hash_state, const std::deque<T, Allocator>& deque) {
+ // TODO(gromer): investigate a more efficient implementation taking
+ // advantage of the chunk structure.
+ for (const auto& t : deque) {
+ hash_state = H::combine(std::move(hash_state), t);
+ }
+ return H::combine(std::move(hash_state), deque.size());
+}
+
+// AbslHashValue for hashing std::forward_list
+template <typename H, typename T, typename Allocator>
+typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
+ H hash_state, const std::forward_list<T, Allocator>& list) {
+ size_t size = 0;
+ for (const T& t : list) {
+ hash_state = H::combine(std::move(hash_state), t);
+ ++size;
+ }
+ return H::combine(std::move(hash_state), size);
+}
+
+// AbslHashValue for hashing std::list
+template <typename H, typename T, typename Allocator>
+typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
+ H hash_state, const std::list<T, Allocator>& list) {
+ for (const auto& t : list) {
+ hash_state = H::combine(std::move(hash_state), t);
+ }
+ return H::combine(std::move(hash_state), list.size());
+}
+
+// AbslHashValue for hashing std::vector
+//
+// Do not use this for vector<bool>. It does not have a .data(), and a fallback
+// for std::hash<> is most likely faster.
+template <typename H, typename T, typename Allocator>
+typename std::enable_if<is_hashable<T>::value && !std::is_same<T, bool>::value,
+ H>::type
+AbslHashValue(H hash_state, const std::vector<T, Allocator>& vector) {
+ return H::combine(H::combine_contiguous(std::move(hash_state), vector.data(),
+ vector.size()),
+ vector.size());
+}
+
+// -----------------------------------------------------------------------------
+// AbslHashValue for Ordered Associative Containers
+// -----------------------------------------------------------------------------
+
+// AbslHashValue for hashing std::map
+template <typename H, typename Key, typename T, typename Compare,
+ typename Allocator>
+typename std::enable_if<is_hashable<Key>::value && is_hashable<T>::value,
+ H>::type
+AbslHashValue(H hash_state, const std::map<Key, T, Compare, Allocator>& map) {
+ for (const auto& t : map) {
+ hash_state = H::combine(std::move(hash_state), t);
+ }
+ return H::combine(std::move(hash_state), map.size());
+}
+
+// AbslHashValue for hashing std::multimap
+template <typename H, typename Key, typename T, typename Compare,
+ typename Allocator>
+typename std::enable_if<is_hashable<Key>::value && is_hashable<T>::value,
+ H>::type
+AbslHashValue(H hash_state,
+ const std::multimap<Key, T, Compare, Allocator>& map) {
+ for (const auto& t : map) {
+ hash_state = H::combine(std::move(hash_state), t);
+ }
+ return H::combine(std::move(hash_state), map.size());
+}
+
+// AbslHashValue for hashing std::set
+template <typename H, typename Key, typename Compare, typename Allocator>
+typename std::enable_if<is_hashable<Key>::value, H>::type AbslHashValue(
+ H hash_state, const std::set<Key, Compare, Allocator>& set) {
+ for (const auto& t : set) {
+ hash_state = H::combine(std::move(hash_state), t);
+ }
+ return H::combine(std::move(hash_state), set.size());
+}
+
+// AbslHashValue for hashing std::multiset
+template <typename H, typename Key, typename Compare, typename Allocator>
+typename std::enable_if<is_hashable<Key>::value, H>::type AbslHashValue(
+ H hash_state, const std::multiset<Key, Compare, Allocator>& set) {
+ for (const auto& t : set) {
+ hash_state = H::combine(std::move(hash_state), t);
+ }
+ return H::combine(std::move(hash_state), set.size());
+}
+
+// -----------------------------------------------------------------------------
+// AbslHashValue for Wrapper Types
+// -----------------------------------------------------------------------------
+
+// AbslHashValue for hashing absl::optional
+template <typename H, typename T>
+typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
+ H hash_state, const absl::optional<T>& opt) {
+ if (opt) hash_state = H::combine(std::move(hash_state), *opt);
+ return H::combine(std::move(hash_state), opt.has_value());
+}
+
+// VariantVisitor
+template <typename H>
+struct VariantVisitor {
+ H&& hash_state;
+ template <typename T>
+ H operator()(const T& t) const {
+ return H::combine(std::move(hash_state), t);
+ }
+};
+
+// AbslHashValue for hashing absl::variant
+template <typename H, typename... T>
+typename std::enable_if<conjunction<is_hashable<T>...>::value, H>::type
+AbslHashValue(H hash_state, const absl::variant<T...>& v) {
+ if (!v.valueless_by_exception()) {
+ hash_state = absl::visit(VariantVisitor<H>{std::move(hash_state)}, v);
+ }
+ return H::combine(std::move(hash_state), v.index());
+}
+
+// -----------------------------------------------------------------------------
+// AbslHashValue for Other Types
+// -----------------------------------------------------------------------------
+
+// AbslHashValue for hashing std::bitset is not defined, for the same reason as
+// for vector<bool> (see std::vector above): It does not expose the raw bytes,
+// and a fallback to std::hash<> is most likely faster.
+
+// -----------------------------------------------------------------------------
+
+// hash_range_or_bytes()
+//
+// Mixes all values in the range [data, data+size) into the hash state.
+// This overload accepts only uniquely-represented types, and hashes them by
+// hashing the entire range of bytes.
+template <typename H, typename T>
+typename std::enable_if<is_uniquely_represented<T>::value, H>::type
+hash_range_or_bytes(H hash_state, const T* data, size_t size) {
+ const auto* bytes = reinterpret_cast<const unsigned char*>(data);
+ return H::combine_contiguous(std::move(hash_state), bytes, sizeof(T) * size);
+}
+
+// hash_range_or_bytes()
+template <typename H, typename T>
+typename std::enable_if<!is_uniquely_represented<T>::value, H>::type
+hash_range_or_bytes(H hash_state, const T* data, size_t size) {
+ for (const auto end = data + size; data < end; ++data) {
+ hash_state = H::combine(std::move(hash_state), *data);
+ }
+ return hash_state;
+}
+
+// InvokeHashTag
+//
+// InvokeHash(H, const T&) invokes the appropriate hash implementation for a
+// hasher of type `H` and a value of type `T`. If `T` is not hashable, there
+// will be no matching overload of InvokeHash().
+// Note: Some platforms (eg MSVC) do not support the detect idiom on
+// std::hash. In those platforms the last fallback will be std::hash and
+// InvokeHash() will always have a valid overload even if std::hash<T> is not
+// valid.
+//
+// We try the following options in order:
+// * If is_uniquely_represented, hash bytes directly.
+// * ADL AbslHashValue(H, const T&) call.
+// * std::hash<T>
+
+// In MSVC we can't probe std::hash or stdext::hash because it triggers a
+// static_assert instead of failing substitution.
+#if defined(_MSC_VER)
+#define ABSL_HASH_INTERNAL_CAN_POISON_ 0
+#else // _MSC_VER
+#define ABSL_HASH_INTERNAL_CAN_POISON_ 1
+#endif // _MSC_VER
+
+#if defined(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE) && \
+ ABSL_HASH_INTERNAL_CAN_POISON_
+#define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 1
+#else
+#define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 0
+#endif
+
+enum class InvokeHashTag {
+ kUniquelyRepresented,
+ kHashValue,
+#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+ kLegacyHash,
+#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+ kStdHash,
+ kNone
+};
+
+// HashSelect
+//
+// Type trait to select the appropriate hash implementation to use.
+// HashSelect<T>::value is an instance of InvokeHashTag that indicates the best
+// available hashing mechanism.
+// See `Note` above about MSVC.
+template <typename T>
+struct HashSelect {
+ private:
+ struct State : HashStateBase<State> {
+ static State combine_contiguous(State hash_state, const unsigned char*,
+ size_t);
+ using State::HashStateBase::combine_contiguous;
+ };
+
+ // `Probe<V, Tag>::value` evaluates to `V<T>::value` if it is a valid
+ // expression, and `false` otherwise.
+ // `Probe<V, Tag>::tag` always evaluates to `Tag`.
+ template <template <typename> class V, InvokeHashTag Tag>
+ struct Probe {
+ private:
+ template <typename U, typename std::enable_if<V<U>::value, int>::type = 0>
+ static std::true_type Test(int);
+ template <typename U>
+ static std::false_type Test(char);
+
+ public:
+ static constexpr InvokeHashTag kTag = Tag;
+ static constexpr bool value = decltype(
+ Test<absl::remove_const_t<absl::remove_reference_t<T>>>(0))::value;
+ };
+
+ template <typename U>
+ using ProbeUniquelyRepresented = is_uniquely_represented<U>;
+
+ template <typename U>
+ using ProbeHashValue =
+ std::is_same<State, decltype(AbslHashValue(std::declval<State>(),
+ std::declval<const U&>()))>;
+
+#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+ template <typename U>
+ using ProbeLegacyHash =
+ std::is_convertible<decltype(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash<
+ U>()(std::declval<const U&>())),
+ size_t>;
+#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+
+ template <typename U>
+ using ProbeStdHash =
+#if ABSL_HASH_INTERNAL_CAN_POISON_
+ std::is_convertible<decltype(std::hash<U>()(std::declval<const U&>())),
+ size_t>;
+#else // ABSL_HASH_INTERNAL_CAN_POISON_
+ std::true_type;
+#endif // ABSL_HASH_INTERNAL_CAN_POISON_
+
+ template <typename U>
+ using ProbeNone = std::true_type;
+
+ public:
+ // Probe each implementation in order.
+ // disjunction provides short circuting wrt instantiation.
+ static constexpr InvokeHashTag value = absl::disjunction<
+ Probe<ProbeUniquelyRepresented, InvokeHashTag::kUniquelyRepresented>,
+ Probe<ProbeHashValue, InvokeHashTag::kHashValue>,
+#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+ Probe<ProbeLegacyHash, InvokeHashTag::kLegacyHash>,
+#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+ Probe<ProbeStdHash, InvokeHashTag::kStdHash>,
+ Probe<ProbeNone, InvokeHashTag::kNone>>::kTag;
+};
+
+template <typename T>
+struct is_hashable : std::integral_constant<bool, HashSelect<T>::value !=
+ InvokeHashTag::kNone> {};
+
+template <typename H, typename T>
+absl::enable_if_t<HashSelect<T>::value == InvokeHashTag::kUniquelyRepresented,
+ H>
+InvokeHash(H state, const T& value) {
+ return hash_internal::hash_bytes(std::move(state), value);
+}
+
+template <typename H, typename T>
+absl::enable_if_t<HashSelect<T>::value == InvokeHashTag::kHashValue, H>
+InvokeHash(H state, const T& value) {
+ return AbslHashValue(std::move(state), value);
+}
+
+#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+template <typename H, typename T>
+absl::enable_if_t<HashSelect<T>::value == InvokeHashTag::kLegacyHash, H>
+InvokeHash(H state, const T& value) {
+ return hash_internal::hash_bytes(
+ std::move(state), ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash<T>{}(value));
+}
+#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
+
+template <typename H, typename T>
+absl::enable_if_t<HashSelect<T>::value == InvokeHashTag::kStdHash, H>
+InvokeHash(H state, const T& value) {
+ return hash_internal::hash_bytes(std::move(state), std::hash<T>{}(value));
+}
+
+// CityHashState
+class CityHashState : public HashStateBase<CityHashState> {
+ // absl::uint128 is not an alias or a thin wrapper around the intrinsic.
+ // We use the intrinsic when available to improve performance.
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+ using uint128 = __uint128_t;
+#else // ABSL_HAVE_INTRINSIC_INT128
+ using uint128 = absl::uint128;
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+ static constexpr uint64_t kMul =
+ sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51} : uint64_t{0x9ddfea08eb382d69};
+
+ template <typename T>
+ using IntegralFastPath =
+ conjunction<std::is_integral<T>, is_uniquely_represented<T>>;
+
+ public:
+ // Move only
+ CityHashState(CityHashState&&) = default;
+ CityHashState& operator=(CityHashState&&) = default;
+
+ // CityHashState::combine_contiguous()
+ //
+ // Fundamental base case for hash recursion: mixes the given range of bytes
+ // into the hash state.
+ static CityHashState combine_contiguous(CityHashState hash_state,
+ const unsigned char* first,
+ size_t size) {
+ return CityHashState(
+ CombineContiguousImpl(hash_state.state_, first, size,
+ std::integral_constant<int, sizeof(size_t)>{}));
+ }
+ using CityHashState::HashStateBase::combine_contiguous;
+
+ // CityHashState::hash()
+ //
+ // For performance reasons in non-opt mode, we specialize this for
+ // integral types.
+ // Otherwise we would be instantiating and calling dozens of functions for
+ // something that is just one multiplication and a couple xor's.
+ // The result should be the same as running the whole algorithm, but faster.
+ template <typename T, absl::enable_if_t<IntegralFastPath<T>::value, int> = 0>
+ static size_t hash(T value) {
+ return static_cast<size_t>(Mix(Seed(), static_cast<uint64_t>(value)));
+ }
+
+ // Overload of CityHashState::hash()
+ template <typename T, absl::enable_if_t<!IntegralFastPath<T>::value, int> = 0>
+ static size_t hash(const T& value) {
+ return static_cast<size_t>(combine(CityHashState{}, value).state_);
+ }
+
+ private:
+ // Invoked only once for a given argument; that plus the fact that this is
+ // move-only ensures that there is only one non-moved-from object.
+ CityHashState() : state_(Seed()) {}
+
+ // Workaround for MSVC bug.
+ // We make the type copyable to fix the calling convention, even though we
+ // never actually copy it. Keep it private to not affect the public API of the
+ // type.
+ CityHashState(const CityHashState&) = default;
+
+ explicit CityHashState(uint64_t state) : state_(state) {}
+
+ // Implementation of the base case for combine_contiguous where we actually
+ // mix the bytes into the state.
+ // Dispatch to different implementations of the combine_contiguous depending
+ // on the value of `sizeof(size_t)`.
+ static uint64_t CombineContiguousImpl(uint64_t state,
+ const unsigned char* first, size_t len,
+ std::integral_constant<int, 4>
+ /* sizeof_size_t */);
+ static uint64_t CombineContiguousImpl(uint64_t state,
+ const unsigned char* first, size_t len,
+ std::integral_constant<int, 8>
+ /* sizeof_size_t*/);
+
+ // Reads 9 to 16 bytes from p.
+ // The first 8 bytes are in .first, the rest (zero padded) bytes are in
+ // .second.
+ static std::pair<uint64_t, uint64_t> Read9To16(const unsigned char* p,
+ size_t len) {
+ uint64_t high = little_endian::Load64(p + len - 8);
+ return {little_endian::Load64(p), high >> (128 - len * 8)};
+ }
+
+ // Reads 4 to 8 bytes from p. Zero pads to fill uint64_t.
+ static uint64_t Read4To8(const unsigned char* p, size_t len) {
+ return (static_cast<uint64_t>(little_endian::Load32(p + len - 4))
+ << (len - 4) * 8) |
+ little_endian::Load32(p);
+ }
+
+ // Reads 1 to 3 bytes from p. Zero pads to fill uint32_t.
+ static uint32_t Read1To3(const unsigned char* p, size_t len) {
+ return static_cast<uint32_t>((p[0]) | //
+ (p[len / 2] << (len / 2 * 8)) | //
+ (p[len - 1] << ((len - 1) * 8)));
+ }
+
+ ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) {
+ using MultType =
+ absl::conditional_t<sizeof(size_t) == 4, uint64_t, uint128>;
+ // We do the addition in 64-bit space to make sure the 128-bit
+ // multiplication is fast. If we were to do it as MultType the compiler has
+ // to assume that the high word is non-zero and needs to perform 2
+ // multiplications instead of one.
+ MultType m = state + v;
+ m *= kMul;
+ return static_cast<uint64_t>(m ^ (m >> (sizeof(m) * 8 / 2)));
+ }
+
+ // Seed()
+ //
+ // A non-deterministic seed.
+ //
+ // The current purpose of this seed is to generate non-deterministic results
+ // and prevent having users depend on the particular hash values.
+ // It is not meant as a security feature right now, but it leaves the door
+ // open to upgrade it to a true per-process random seed. A true random seed
+ // costs more and we don't need to pay for that right now.
+ //
+ // On platforms with ASLR, we take advantage of it to make a per-process
+ // random value.
+ // See https://en.wikipedia.org/wiki/Address_space_layout_randomization
+ //
+ // On other platforms this is still going to be non-deterministic but most
+ // probably per-build and not per-process.
+ ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Seed() {
+ return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(kSeed));
+ }
+ static const void* const kSeed;
+
+ uint64_t state_;
+};
+
+// CityHashState::CombineContiguousImpl()
+inline uint64_t CityHashState::CombineContiguousImpl(
+ uint64_t state, const unsigned char* first, size_t len,
+ std::integral_constant<int, 4> /* sizeof_size_t */) {
+ // For large values we use CityHash, for small ones we just use a
+ // multiplicative hash.
+ uint64_t v;
+ if (len > 8) {
+ v = absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), len);
+ } else if (len >= 4) {
+ v = Read4To8(first, len);
+ } else if (len > 0) {
+ v = Read1To3(first, len);
+ } else {
+ // Empty ranges have no effect.
+ return state;
+ }
+ return Mix(state, v);
+}
+
+// Overload of CityHashState::CombineContiguousImpl()
+inline uint64_t CityHashState::CombineContiguousImpl(
+ uint64_t state, const unsigned char* first, size_t len,
+ std::integral_constant<int, 8> /* sizeof_size_t */) {
+ // For large values we use CityHash, for small ones we just use a
+ // multiplicative hash.
+ uint64_t v;
+ if (len > 16) {
+ v = absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), len);
+ } else if (len > 8) {
+ auto p = Read9To16(first, len);
+ state = Mix(state, p.first);
+ v = p.second;
+ } else if (len >= 4) {
+ v = Read4To8(first, len);
+ } else if (len > 0) {
+ v = Read1To3(first, len);
+ } else {
+ // Empty ranges have no effect.
+ return state;
+ }
+ return Mix(state, v);
+}
+
+
+struct AggregateBarrier {};
+
+// HashImpl
+
+// Add a private base class to make sure this type is not an aggregate.
+// Aggregates can be aggregate initialized even if the default constructor is
+// deleted.
+struct PoisonedHash : private AggregateBarrier {
+ PoisonedHash() = delete;
+ PoisonedHash(const PoisonedHash&) = delete;
+ PoisonedHash& operator=(const PoisonedHash&) = delete;
+};
+
+template <typename T>
+struct HashImpl {
+ size_t operator()(const T& value) const { return CityHashState::hash(value); }
+};
+
+template <typename T>
+struct Hash
+ : absl::conditional_t<is_hashable<T>::value, HashImpl<T>, PoisonedHash> {};
+
+template <typename H>
+template <typename T, typename... Ts>
+H HashStateBase<H>::combine(H state, const T& value, const Ts&... values) {
+ return H::combine(hash_internal::InvokeHash(std::move(state), value),
+ values...);
+}
+
+// HashStateBase::combine_contiguous()
+template <typename H>
+template <typename T>
+H HashStateBase<H>::combine_contiguous(H state, const T* data, size_t size) {
+ return hash_internal::hash_range_or_bytes(std::move(state), data, size);
+}
+} // namespace hash_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_HASH_INTERNAL_HASH_H_
diff --git a/absl/hash/internal/print_hash_of.cc b/absl/hash/internal/print_hash_of.cc
new file mode 100644
index 00000000..b6df31cc
--- /dev/null
+++ b/absl/hash/internal/print_hash_of.cc
@@ -0,0 +1,23 @@
+// 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
+//
+// 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.
+
+#include <cstdlib>
+
+#include "absl/hash/hash.h"
+
+// Prints the hash of argv[1].
+int main(int argc, char** argv) {
+ if (argc < 2) return 1;
+ printf("%zu\n", absl::Hash<int>{}(std::atoi(argv[1]))); // NOLINT
+}
diff --git a/absl/hash/internal/spy_hash_state.h b/absl/hash/internal/spy_hash_state.h
new file mode 100644
index 00000000..1886d2ef
--- /dev/null
+++ b/absl/hash/internal/spy_hash_state.h
@@ -0,0 +1,220 @@
+// 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
+//
+// 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.
+
+#ifndef ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
+#define ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "absl/hash/hash.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_join.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace hash_internal {
+
+// SpyHashState is an implementation of the HashState API that simply
+// accumulates all input bytes in an internal buffer. This makes it useful
+// for testing AbslHashValue overloads (so long as they are templated on the
+// HashState parameter), since it can report the exact hash representation
+// that the AbslHashValue overload produces.
+//
+// Sample usage:
+// EXPECT_EQ(SpyHashState::combine(SpyHashState(), foo),
+// SpyHashState::combine(SpyHashState(), bar));
+template <typename T>
+class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> {
+ public:
+ SpyHashStateImpl()
+ : error_(std::make_shared<absl::optional<std::string>>()) {
+ static_assert(std::is_void<T>::value, "");
+ }
+
+ // Move-only
+ SpyHashStateImpl(const SpyHashStateImpl&) = delete;
+ SpyHashStateImpl& operator=(const SpyHashStateImpl&) = delete;
+
+ SpyHashStateImpl(SpyHashStateImpl&& other) noexcept {
+ *this = std::move(other);
+ }
+
+ SpyHashStateImpl& operator=(SpyHashStateImpl&& other) noexcept {
+ hash_representation_ = std::move(other.hash_representation_);
+ error_ = other.error_;
+ moved_from_ = other.moved_from_;
+ other.moved_from_ = true;
+ return *this;
+ }
+
+ template <typename U>
+ SpyHashStateImpl(SpyHashStateImpl<U>&& other) { // NOLINT
+ hash_representation_ = std::move(other.hash_representation_);
+ error_ = other.error_;
+ moved_from_ = other.moved_from_;
+ other.moved_from_ = true;
+ }
+
+ template <typename A, typename... Args>
+ static SpyHashStateImpl combine(SpyHashStateImpl s, const A& a,
+ const Args&... args) {
+ // Pass an instance of SpyHashStateImpl<A> when trying to combine `A`. This
+ // allows us to test that the user only uses this instance for combine calls
+ // and does not call AbslHashValue directly.
+ // See AbslHashValue implementation at the bottom.
+ s = SpyHashStateImpl<A>::HashStateBase::combine(std::move(s), a);
+ return SpyHashStateImpl::combine(std::move(s), args...);
+ }
+ static SpyHashStateImpl combine(SpyHashStateImpl s) {
+ if (direct_absl_hash_value_error_) {
+ *s.error_ = "AbslHashValue should not be invoked directly.";
+ } else if (s.moved_from_) {
+ *s.error_ = "Used moved-from instance of the hash state object.";
+ }
+ return s;
+ }
+
+ static void SetDirectAbslHashValueError() {
+ direct_absl_hash_value_error_ = true;
+ }
+
+ // Two SpyHashStateImpl objects are equal if they hold equal hash
+ // representations.
+ friend bool operator==(const SpyHashStateImpl& lhs,
+ const SpyHashStateImpl& rhs) {
+ return lhs.hash_representation_ == rhs.hash_representation_;
+ }
+
+ friend bool operator!=(const SpyHashStateImpl& lhs,
+ const SpyHashStateImpl& rhs) {
+ return !(lhs == rhs);
+ }
+
+ enum class CompareResult {
+ kEqual,
+ kASuffixB,
+ kBSuffixA,
+ kUnequal,
+ };
+
+ static CompareResult Compare(const SpyHashStateImpl& a,
+ const SpyHashStateImpl& b) {
+ const std::string a_flat = absl::StrJoin(a.hash_representation_, "");
+ const std::string b_flat = absl::StrJoin(b.hash_representation_, "");
+ if (a_flat == b_flat) return CompareResult::kEqual;
+ if (absl::EndsWith(a_flat, b_flat)) return CompareResult::kBSuffixA;
+ if (absl::EndsWith(b_flat, a_flat)) return CompareResult::kASuffixB;
+ return CompareResult::kUnequal;
+ }
+
+ // operator<< prints the hash representation as a hex and ASCII dump, to
+ // facilitate debugging.
+ friend std::ostream& operator<<(std::ostream& out,
+ const SpyHashStateImpl& hash_state) {
+ out << "[\n";
+ for (auto& s : hash_state.hash_representation_) {
+ size_t offset = 0;
+ for (char c : s) {
+ if (offset % 16 == 0) {
+ out << absl::StreamFormat("\n0x%04x: ", offset);
+ }
+ if (offset % 2 == 0) {
+ out << " ";
+ }
+ out << absl::StreamFormat("%02x", c);
+ ++offset;
+ }
+ out << "\n";
+ }
+ return out << "]";
+ }
+
+ // The base case of the combine recursion, which writes raw bytes into the
+ // internal buffer.
+ static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state,
+ const unsigned char* begin,
+ size_t size) {
+ hash_state.hash_representation_.emplace_back(
+ reinterpret_cast<const char*>(begin), size);
+ return hash_state;
+ }
+
+ using SpyHashStateImpl::HashStateBase::combine_contiguous;
+
+ absl::optional<std::string> error() const {
+ if (moved_from_) {
+ return "Returned a moved-from instance of the hash state object.";
+ }
+ return *error_;
+ }
+
+ private:
+ template <typename U>
+ friend class SpyHashStateImpl;
+
+ // This is true if SpyHashStateImpl<T> has been passed to a call of
+ // AbslHashValue with the wrong type. This detects that the user called
+ // AbslHashValue directly (because the hash state type does not match).
+ static bool direct_absl_hash_value_error_;
+
+
+ std::vector<std::string> hash_representation_;
+ // This is a shared_ptr because we want all instances of the particular
+ // SpyHashState run to share the field. This way we can set the error for
+ // use-after-move and all the copies will see it.
+ std::shared_ptr<absl::optional<std::string>> error_;
+ bool moved_from_ = false;
+};
+
+template <typename T>
+bool SpyHashStateImpl<T>::direct_absl_hash_value_error_;
+
+template <bool& B>
+struct OdrUse {
+ constexpr OdrUse() {}
+ bool& b = B;
+};
+
+template <void (*)()>
+struct RunOnStartup {
+ static bool run;
+ static constexpr OdrUse<run> kOdrUse{};
+};
+
+template <void (*f)()>
+bool RunOnStartup<f>::run = (f(), true);
+
+template <
+ typename T, typename U,
+ // Only trigger for when (T != U),
+ absl::enable_if_t<!std::is_same<T, U>::value, int> = 0,
+ // This statement works in two ways:
+ // - First, it instantiates RunOnStartup and forces the initialization of
+ // `run`, which set the global variable.
+ // - Second, it triggers a SFINAE error disabling the overload to prevent
+ // compile time errors. If we didn't disable the overload we would get
+ // ambiguous overload errors, which we don't want.
+ int = RunOnStartup<SpyHashStateImpl<T>::SetDirectAbslHashValueError>::run>
+void AbslHashValue(SpyHashStateImpl<T>, const U&);
+
+using SpyHashState = SpyHashStateImpl<void>;
+
+} // namespace hash_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel
index 46f47b12..89a312ea 100644
--- a/absl/memory/BUILD.bazel
+++ b/absl/memory/BUILD.bazel
@@ -19,6 +19,7 @@ load(
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
"ABSL_EXCEPTIONS_FLAG",
+ "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
)
package(default_visibility = ["//visibility:public"])
@@ -53,6 +54,7 @@ cc_test(
"memory_exception_safety_test.cc",
],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":memory",
"//absl/base:exception_safety_testing",
diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt
index 5958f5c5..4b494dc0 100644
--- a/absl/memory/CMakeLists.txt
+++ b/absl/memory/CMakeLists.txt
@@ -14,58 +14,45 @@
# limitations under the License.
#
-list(APPEND MEMORY_PUBLIC_HEADERS
- "memory.h"
-)
-
-
-absl_header_library(
- TARGET
- absl_memory
- EXPORT_NAME
+absl_cc_library(
+ NAME
memory
+ HDRS
+ "memory.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ absl::meta
+ PUBLIC
)
-#
-## TESTS
-#
-
-# test memory_test
-list(APPEND MEMORY_TEST_SRC
- "memory_test.cc"
- ${MEMORY_PUBLIC_HEADERS}
-)
-set(MEMORY_TEST_PUBLIC_LIBRARIES absl::base absl::memory)
-
-
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
memory_test
- SOURCES
- ${MEMORY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${MEMORY_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test memory_exception_safety_test
-set(MEMORY_EXCEPTION_SAFETY_TEST_SRC "memory_exception_safety_test.cc")
-set(MEMORY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
- absl::memory
- absl_base_internal_exception_safety_testing
+ SRCS
+ "memory_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::memory
+ absl::base
+ absl::core_headers
+ gmock_main
)
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
memory_exception_safety_test
- SOURCES
- ${MEMORY_EXCEPTION_SAFETY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${MEMORY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
+ SRCS
+ "memory_exception_safety_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::memory
+ absl::exception_safety_testing
+ gmock_main
)
-
-
-
diff --git a/absl/memory/memory.h b/absl/memory/memory.h
index 54ff2182..34cfb285 100644
--- a/absl/memory/memory.h
+++ b/absl/memory/memory.h
@@ -34,22 +34,36 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// Function Template: WrapUnique()
// -----------------------------------------------------------------------------
//
-// Adopts ownership from a raw pointer and transfers it to the returned
-// `std::unique_ptr`, whose type is deduced. DO NOT specify the template type T
-// when calling WrapUnique.
+// Adopts ownership from a raw pointer and transfers it to the returned
+// `std::unique_ptr`, whose type is deduced. Because of this deduction, *do not*
+// specify the template type `T` when calling `WrapUnique`.
//
// Example:
// X* NewX(int, int);
// auto x = WrapUnique(NewX(1, 2)); // 'x' is std::unique_ptr<X>.
//
-// `absl::WrapUnique` is useful for capturing the output of a raw pointer
-// factory. However, prefer 'absl::make_unique<T>(args...) over
+// The purpose of WrapUnique is to automatically deduce the pointer type. If you
+// wish to make the type explicit, for readability reasons or because you prefer
+// to use a base-class pointer rather than a derived one, just use
+// `std::unique_ptr` directly.
+//
+// Example:
+// X* Factory(int, int);
+// auto x = std::unique_ptr<X>(Factory(1, 2));
+// - or -
+// std::unique_ptr<X> x(Factory(1, 2));
+//
+// This has the added advantage of working whether Factory returns a raw
+// pointer or a `std::unique_ptr`.
+//
+// While `absl::WrapUnique` is useful for capturing the output of a raw
+// pointer factory, prefer 'absl::make_unique<T>(args...)' over
// 'absl::WrapUnique(new T(args...))'.
//
// auto x = WrapUnique(new X(1, 2)); // works, but nonideal.
@@ -84,7 +98,11 @@ struct MakeUniqueResult<T[N]> {
} // namespace memory_internal
-#if __cplusplus >= 201402L || defined(_MSC_VER)
+// gcc 4.8 has __cplusplus at 201301 but doesn't define make_unique. Other
+// supported compilers either just define __cplusplus as 201103 but have
+// make_unique (msvc), or have make_unique whenever __cplusplus > 201103 (clang)
+#if (__cplusplus > 201103L || defined(_MSC_VER)) && \
+ !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8)
using std::make_unique;
#else
// -----------------------------------------------------------------------------
@@ -582,7 +600,7 @@ struct allocator_traits {
return a.max_size();
}
static size_type max_size_impl(char, const Alloc&) {
- return std::numeric_limits<size_type>::max() / sizeof(value_type);
+ return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
}
template <typename A>
@@ -637,7 +655,45 @@ struct default_allocator_is_nothrow : std::true_type {};
struct default_allocator_is_nothrow : std::false_type {};
#endif
-} // inline namespace lts_2018_06_20
+namespace memory_internal {
+template <typename Allocator, typename Iterator, typename... Args>
+void ConstructRange(Allocator& alloc, Iterator first, Iterator last,
+ const Args&... args) {
+ for (Iterator cur = first; cur != last; ++cur) {
+ ABSL_INTERNAL_TRY {
+ std::allocator_traits<Allocator>::construct(alloc, std::addressof(*cur),
+ args...);
+ }
+ ABSL_INTERNAL_CATCH_ANY {
+ while (cur != first) {
+ --cur;
+ std::allocator_traits<Allocator>::destroy(alloc, std::addressof(*cur));
+ }
+ ABSL_INTERNAL_RETHROW;
+ }
+ }
+}
+
+template <typename Allocator, typename Iterator, typename InputIterator>
+void CopyRange(Allocator& alloc, Iterator destination, InputIterator first,
+ InputIterator last) {
+ for (Iterator cur = destination; first != last;
+ static_cast<void>(++cur), static_cast<void>(++first)) {
+ ABSL_INTERNAL_TRY {
+ std::allocator_traits<Allocator>::construct(alloc, std::addressof(*cur),
+ *first);
+ }
+ ABSL_INTERNAL_CATCH_ANY {
+ while (cur != destination) {
+ --cur;
+ std::allocator_traits<Allocator>::destroy(alloc, std::addressof(*cur));
+ }
+ ABSL_INTERNAL_RETHROW;
+ }
+ }
+}
+} // namespace memory_internal
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_MEMORY_MEMORY_H_
diff --git a/absl/memory/memory_exception_safety_test.cc b/absl/memory/memory_exception_safety_test.cc
index dc00f349..9661502d 100644
--- a/absl/memory/memory_exception_safety_test.cc
+++ b/absl/memory/memory_exception_safety_test.cc
@@ -18,19 +18,22 @@
#include "absl/base/internal/exception_safety_testing.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
-using Thrower = ::testing::ThrowingValue<>;
+constexpr int kLength = 50;
+using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
+using ThrowerStorage =
+ absl::aligned_storage_t<sizeof(Thrower), alignof(Thrower)>;
+using ThrowerList = std::array<ThrowerStorage, kLength>;
TEST(MakeUnique, CheckForLeaks) {
constexpr int kValue = 321;
- constexpr size_t kLength = 10;
auto tester = testing::MakeExceptionSafetyTester()
.WithInitialValue(Thrower(kValue))
// Ensures make_unique does not modify the input. The real
// test, though, is ConstructorTracker checking for leaks.
- .WithInvariants(testing::strong_guarantee);
+ .WithContracts(testing::strong_guarantee);
EXPECT_TRUE(tester.Test([](Thrower* thrower) {
static_cast<void>(absl::make_unique<Thrower>(*thrower));
@@ -47,5 +50,5 @@ TEST(MakeUnique, CheckForLeaks) {
}
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/memory/memory_test.cc b/absl/memory/memory_test.cc
index dee9b486..21fe32f9 100644
--- a/absl/memory/memory_test.cc
+++ b/absl/memory/memory_test.cc
@@ -69,6 +69,45 @@ TEST(MakeUniqueTest, Basic) {
EXPECT_EQ("hi", *p);
}
+// InitializationVerifier fills in a pattern when allocated so we can
+// distinguish between its default and value initialized states (without
+// accessing truly uninitialized memory).
+struct InitializationVerifier {
+ static constexpr int kDefaultScalar = 0x43;
+ static constexpr int kDefaultArray = 0x4B;
+
+ static void* operator new(size_t n) {
+ void* ret = ::operator new(n);
+ memset(ret, kDefaultScalar, n);
+ return ret;
+ }
+
+ static void* operator new[](size_t n) {
+ void* ret = ::operator new[](n);
+ memset(ret, kDefaultArray, n);
+ return ret;
+ }
+
+ int a;
+ int b;
+};
+
+TEST(Initialization, MakeUnique) {
+ auto p = absl::make_unique<InitializationVerifier>();
+
+ EXPECT_EQ(0, p->a);
+ EXPECT_EQ(0, p->b);
+}
+
+TEST(Initialization, MakeUniqueArray) {
+ auto p = absl::make_unique<InitializationVerifier[]>(2);
+
+ EXPECT_EQ(0, p[0].a);
+ EXPECT_EQ(0, p[0].b);
+ EXPECT_EQ(0, p[1].a);
+ EXPECT_EQ(0, p[1].b);
+}
+
struct MoveOnly {
MoveOnly() = default;
explicit MoveOnly(int i1) : ip1{new int{i1}} {}
@@ -145,11 +184,10 @@ TEST(Make_UniqueTest, NotAmbiguousWithStdMakeUnique) {
explicit TakesStdType(const std::vector<int> &vec) {}
};
using absl::make_unique;
- make_unique<TakesStdType>(std::vector<int>());
+ (void)make_unique<TakesStdType>(std::vector<int>());
}
#if 0
-// TODO(billydonahue): Make a proper NC test.
// These tests shouldn't compile.
TEST(MakeUniqueTestNC, AcceptMoveOnlyLvalue) {
auto m = MoveOnly();
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index 923234c4..231e08db 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -42,9 +42,10 @@
#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace type_traits_internal {
+
template <typename... Ts>
struct VoidTImpl {
using type = void;
@@ -62,8 +63,68 @@ struct default_alignment_of_aligned_storage<Len,
static constexpr size_t value = Align;
};
+////////////////////////////////
+// Library Fundamentals V2 TS //
+////////////////////////////////
+
+// NOTE: The `is_detected` family of templates here differ from the library
+// fundamentals specification in that for library fundamentals, `Op<Args...>` is
+// evaluated as soon as the type `is_detected<Op, Args...>` undergoes
+// substitution, regardless of whether or not the `::value` is accessed. That
+// is inconsistent with all other standard traits and prevents lazy evaluation
+// in larger contexts (such as if the `is_detected` check is a trailing argument
+// of a `conjunction`. This implementation opts to instead be lazy in the same
+// way that the standard traits are (this "defect" of the detection idiom
+// specifications has been reported).
+
+template <class Enabler, template <class...> class Op, class... Args>
+struct is_detected_impl {
+ using type = std::false_type;
+};
+
+template <template <class...> class Op, class... Args>
+struct is_detected_impl<typename VoidTImpl<Op<Args...>>::type, Op, Args...> {
+ using type = std::true_type;
+};
+
+template <template <class...> class Op, class... Args>
+struct is_detected : is_detected_impl<void, Op, Args...>::type {};
+
+template <class Enabler, class To, template <class...> class Op, class... Args>
+struct is_detected_convertible_impl {
+ using type = std::false_type;
+};
+
+template <class To, template <class...> class Op, class... Args>
+struct is_detected_convertible_impl<
+ typename std::enable_if<std::is_convertible<Op<Args...>, To>::value>::type,
+ To, Op, Args...> {
+ using type = std::true_type;
+};
+
+template <class To, template <class...> class Op, class... Args>
+struct is_detected_convertible
+ : is_detected_convertible_impl<void, To, Op, Args...>::type {};
+
+template <typename T>
+using IsCopyAssignableImpl =
+ decltype(std::declval<T&>() = std::declval<const T&>());
+
+template <typename T>
+using IsMoveAssignableImpl = decltype(std::declval<T&>() = std::declval<T&&>());
+
} // namespace type_traits_internal
+template <typename T>
+struct is_copy_assignable : type_traits_internal::is_detected<
+ type_traits_internal::IsCopyAssignableImpl, T> {
+};
+
+template <typename T>
+struct is_move_assignable : type_traits_internal::is_detected<
+ type_traits_internal::IsMoveAssignableImpl, T> {
+};
+
// void_t()
//
// Ignores the type of any its arguments and returns `void`. In general, this
@@ -264,8 +325,9 @@ struct is_trivially_copy_constructible
// `is_trivially_assignable<T&, const T&>`.
template <typename T>
struct is_trivially_copy_assignable
- : std::integral_constant<bool, __has_trivial_assign(T) &&
- std::is_copy_assignable<T>::value> {
+ : std::integral_constant<
+ bool, __has_trivial_assign(typename std::remove_reference<T>::type) &&
+ absl::is_copy_assignable<T>::value> {
#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
private:
static constexpr bool compliant =
@@ -365,10 +427,12 @@ struct IsHashEnabled
: absl::conjunction<std::is_default_constructible<std::hash<Key>>,
std::is_copy_constructible<std::hash<Key>>,
std::is_destructible<std::hash<Key>>,
- std::is_copy_assignable<std::hash<Key>>,
+ absl::is_copy_assignable<std::hash<Key>>,
IsHashable<Key>> {};
+
} // namespace type_traits_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+
#endif // ABSL_META_TYPE_TRAITS_H_
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc
index c44d1c5f..f51f5ded 100644
--- a/absl/meta/type_traits_test.cc
+++ b/absl/meta/type_traits_test.cc
@@ -34,6 +34,83 @@ struct simple_pair {
struct Dummy {};
+struct ReturnType {};
+struct ConvertibleToReturnType {
+ operator ReturnType() const; // NOLINT
+};
+
+// Unique types used as parameter types for testing the detection idiom.
+struct StructA {};
+struct StructB {};
+struct StructC {};
+
+struct TypeWithBarFunction {
+ template <class T,
+ absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0>
+ ReturnType bar(T&&, const StructB&, StructC&&) &&; // NOLINT
+};
+
+struct TypeWithBarFunctionAndConvertibleReturnType {
+ template <class T,
+ absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0>
+ ConvertibleToReturnType bar(T&&, const StructB&, StructC&&) &&; // NOLINT
+};
+
+template <class Class, class... Ts>
+using BarIsCallableImpl =
+ decltype(std::declval<Class>().bar(std::declval<Ts>()...));
+
+template <class Class, class... T>
+using BarIsCallable =
+ absl::type_traits_internal::is_detected<BarIsCallableImpl, Class, T...>;
+
+template <class Class, class... T>
+using BarIsCallableConv = absl::type_traits_internal::is_detected_convertible<
+ ReturnType, BarIsCallableImpl, Class, T...>;
+
+// NOTE: Test of detail type_traits_internal::is_detected.
+TEST(IsDetectedTest, BasicUsage) {
+ EXPECT_TRUE((BarIsCallable<TypeWithBarFunction, StructA&, const StructB&,
+ StructC>::value));
+ EXPECT_TRUE(
+ (BarIsCallable<TypeWithBarFunction, StructA&, StructB&, StructC>::value));
+ EXPECT_TRUE(
+ (BarIsCallable<TypeWithBarFunction, StructA&, StructB, StructC>::value));
+
+ EXPECT_FALSE((BarIsCallable<int, StructA&, const StructB&, StructC>::value));
+ EXPECT_FALSE((BarIsCallable<TypeWithBarFunction&, StructA&, const StructB&,
+ StructC>::value));
+ EXPECT_FALSE((BarIsCallable<TypeWithBarFunction, StructA, const StructB&,
+ StructC>::value));
+}
+
+// NOTE: Test of detail type_traits_internal::is_detected_convertible.
+TEST(IsDetectedConvertibleTest, BasicUsage) {
+ EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, const StructB&,
+ StructC>::value));
+ EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, StructB&,
+ StructC>::value));
+ EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, StructB,
+ StructC>::value));
+ EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
+ StructA&, const StructB&, StructC>::value));
+ EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
+ StructA&, StructB&, StructC>::value));
+ EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
+ StructA&, StructB, StructC>::value));
+
+ EXPECT_FALSE(
+ (BarIsCallableConv<int, StructA&, const StructB&, StructC>::value));
+ EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunction&, StructA&,
+ const StructB&, StructC>::value));
+ EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunction, StructA, const StructB&,
+ StructC>::value));
+ EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType&,
+ StructA&, const StructB&, StructC>::value));
+ EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
+ StructA, const StructB&, StructC>::value));
+}
+
TEST(VoidTTest, BasicUsage) {
StaticAssertTypeEq<void, absl::void_t<Dummy>>();
StaticAssertTypeEq<void, absl::void_t<Dummy, Dummy, Dummy>>();
@@ -528,6 +605,10 @@ TEST(TypeTraitsTest, TestTrivialCopyAssign) {
// Verify that arrays are not trivially copy assignable
using int10 = int[10];
EXPECT_FALSE(absl::is_trivially_copy_assignable<int10>::value);
+
+ // Verify that references are handled correctly
+ EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial&&>::value);
+ EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial&>::value);
}
#define ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(trait_name, ...) \
@@ -714,8 +795,8 @@ TEST(TypeTraitsTest, TestDecay) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[][1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int());
- ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(float));
- ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(char, ...));
+ ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(float)); // NOLINT
+ ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(char, ...)); // NOLINT
}
struct TypeA {};
@@ -796,4 +877,80 @@ TEST(TypeTraitsTest, TestResultOf) {
EXPECT_EQ(TypeEnum::D, GetTypeExt(Wrap<TypeD>()));
}
+template <typename T>
+bool TestCopyAssign() {
+ return absl::is_copy_assignable<T>::value ==
+ std::is_copy_assignable<T>::value;
+}
+
+TEST(TypeTraitsTest, IsCopyAssignable) {
+ EXPECT_TRUE(TestCopyAssign<int>());
+ EXPECT_TRUE(TestCopyAssign<int&>());
+ EXPECT_TRUE(TestCopyAssign<int&&>());
+
+ struct S {};
+ EXPECT_TRUE(TestCopyAssign<S>());
+ EXPECT_TRUE(TestCopyAssign<S&>());
+ EXPECT_TRUE(TestCopyAssign<S&&>());
+
+ class C {
+ public:
+ explicit C(C* c) : c_(c) {}
+ ~C() { delete c_; }
+
+ private:
+ C* c_;
+ };
+ EXPECT_TRUE(TestCopyAssign<C>());
+ EXPECT_TRUE(TestCopyAssign<C&>());
+ EXPECT_TRUE(TestCopyAssign<C&&>());
+
+ // Reason for ifndef: add_lvalue_reference<T> in libc++ breaks for these cases
+#ifndef _LIBCPP_VERSION
+ EXPECT_TRUE(TestCopyAssign<int()>());
+ EXPECT_TRUE(TestCopyAssign<int(int) const>());
+ EXPECT_TRUE(TestCopyAssign<int(...) volatile&>());
+ EXPECT_TRUE(TestCopyAssign<int(int, ...) const volatile&&>());
+#endif // _LIBCPP_VERSION
+}
+
+template <typename T>
+bool TestMoveAssign() {
+ return absl::is_move_assignable<T>::value ==
+ std::is_move_assignable<T>::value;
+}
+
+TEST(TypeTraitsTest, IsMoveAssignable) {
+ EXPECT_TRUE(TestMoveAssign<int>());
+ EXPECT_TRUE(TestMoveAssign<int&>());
+ EXPECT_TRUE(TestMoveAssign<int&&>());
+
+ struct S {};
+ EXPECT_TRUE(TestMoveAssign<S>());
+ EXPECT_TRUE(TestMoveAssign<S&>());
+ EXPECT_TRUE(TestMoveAssign<S&&>());
+
+ class C {
+ public:
+ explicit C(C* c) : c_(c) {}
+ ~C() { delete c_; }
+ void operator=(const C&) = delete;
+ void operator=(C&&) = delete;
+
+ private:
+ C* c_;
+ };
+ EXPECT_TRUE(TestMoveAssign<C>());
+ EXPECT_TRUE(TestMoveAssign<C&>());
+ EXPECT_TRUE(TestMoveAssign<C&&>());
+
+ // Reason for ifndef: add_lvalue_reference<T> in libc++ breaks for these cases
+#ifndef _LIBCPP_VERSION
+ EXPECT_TRUE(TestMoveAssign<int()>());
+ EXPECT_TRUE(TestMoveAssign<int(int) const>());
+ EXPECT_TRUE(TestMoveAssign<int(...) volatile&>());
+ EXPECT_TRUE(TestMoveAssign<int(int, ...) const volatile&&>());
+#endif // _LIBCPP_VERSION
+}
+
} // namespace
diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel
index f49571eb..324ce669 100644
--- a/absl/numeric/BUILD.bazel
+++ b/absl/numeric/BUILD.bazel
@@ -49,6 +49,7 @@ cc_test(
":int128",
"//absl/base",
"//absl/base:core_headers",
+ "//absl/hash:hash_testing",
"//absl/meta:type_traits",
"@com_google_googletest//:gtest_main",
],
diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc
index 315cae10..98af84b7 100644
--- a/absl/numeric/int128.cc
+++ b/absl/numeric/int128.cc
@@ -17,13 +17,13 @@
#include <stddef.h>
#include <cassert>
#include <iomanip>
-#include <iostream> // NOLINT(readability/streams)
+#include <ostream> // NOLINT(readability/streams)
#include <sstream>
#include <string>
#include <type_traits>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
const uint128 kuint128max = MakeUint128(std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max());
@@ -223,5 +223,31 @@ std::ostream& operator<<(std::ostream& os, uint128 v) {
return os << rep;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+
+namespace std {
+constexpr bool numeric_limits<absl::uint128>::is_specialized;
+constexpr bool numeric_limits<absl::uint128>::is_signed;
+constexpr bool numeric_limits<absl::uint128>::is_integer;
+constexpr bool numeric_limits<absl::uint128>::is_exact;
+constexpr bool numeric_limits<absl::uint128>::has_infinity;
+constexpr bool numeric_limits<absl::uint128>::has_quiet_NaN;
+constexpr bool numeric_limits<absl::uint128>::has_signaling_NaN;
+constexpr float_denorm_style numeric_limits<absl::uint128>::has_denorm;
+constexpr bool numeric_limits<absl::uint128>::has_denorm_loss;
+constexpr float_round_style numeric_limits<absl::uint128>::round_style;
+constexpr bool numeric_limits<absl::uint128>::is_iec559;
+constexpr bool numeric_limits<absl::uint128>::is_bounded;
+constexpr bool numeric_limits<absl::uint128>::is_modulo;
+constexpr int numeric_limits<absl::uint128>::digits;
+constexpr int numeric_limits<absl::uint128>::digits10;
+constexpr int numeric_limits<absl::uint128>::max_digits10;
+constexpr int numeric_limits<absl::uint128>::radix;
+constexpr int numeric_limits<absl::uint128>::min_exponent;
+constexpr int numeric_limits<absl::uint128>::min_exponent10;
+constexpr int numeric_limits<absl::uint128>::max_exponent;
+constexpr int numeric_limits<absl::uint128>::max_exponent10;
+constexpr bool numeric_limits<absl::uint128>::traps;
+constexpr bool numeric_limits<absl::uint128>::tinyness_before;
+} // namespace std
diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h
index 96f42a83..cb4776f1 100644
--- a/absl/numeric/int128.h
+++ b/absl/numeric/int128.h
@@ -31,13 +31,19 @@
#include <cstring>
#include <iosfwd>
#include <limits>
+#include <utility>
#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/base/port.h"
+#if defined(_MSC_VER) && defined(_WIN64)
+#include <intrin.h>
+#pragma intrinsic(_umul128)
+#endif // defined(_MSC_VER) && defined(_WIN64)
+
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// uint128
@@ -192,6 +198,12 @@ class
// Returns the highest value for a 128-bit unsigned integer.
friend constexpr uint128 Uint128Max();
+ // Support for absl::Hash.
+ template <typename H>
+ friend H AbslHashValue(H h, uint128 v) {
+ return H::combine(std::move(h), Uint128High64(v), Uint128Low64(v));
+ }
+
private:
constexpr uint128(uint64_t high, uint64_t low);
@@ -220,21 +232,71 @@ std::ostream& operator<<(std::ostream& os, uint128 v);
// TODO(strel) add operator>>(std::istream&, uint128)
+constexpr uint128 Uint128Max() {
+ return uint128((std::numeric_limits<uint64_t>::max)(),
+ (std::numeric_limits<uint64_t>::max)());
+}
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+// Specialized numeric_limits for uint128.
+namespace std {
+template <>
+class numeric_limits<absl::uint128> {
+ public:
+ static constexpr bool is_specialized = true;
+ static constexpr bool is_signed = false;
+ static constexpr bool is_integer = true;
+ static constexpr bool is_exact = true;
+ static constexpr bool has_infinity = false;
+ static constexpr bool has_quiet_NaN = false;
+ static constexpr bool has_signaling_NaN = false;
+ static constexpr float_denorm_style has_denorm = denorm_absent;
+ static constexpr bool has_denorm_loss = false;
+ static constexpr float_round_style round_style = round_toward_zero;
+ static constexpr bool is_iec559 = false;
+ static constexpr bool is_bounded = true;
+ static constexpr bool is_modulo = true;
+ static constexpr int digits = 128;
+ static constexpr int digits10 = 38;
+ static constexpr int max_digits10 = 0;
+ static constexpr int radix = 2;
+ static constexpr int min_exponent = 0;
+ static constexpr int min_exponent10 = 0;
+ static constexpr int max_exponent = 0;
+ static constexpr int max_exponent10 = 0;
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+ static constexpr bool traps = numeric_limits<unsigned __int128>::traps;
+#else // ABSL_HAVE_INTRINSIC_INT128
+ static constexpr bool traps = numeric_limits<uint64_t>::traps;
+#endif // ABSL_HAVE_INTRINSIC_INT128
+ static constexpr bool tinyness_before = false;
+
+ static constexpr absl::uint128 min() { return 0; }
+ static constexpr absl::uint128 lowest() { return 0; }
+ static constexpr absl::uint128 max() { return absl::Uint128Max(); }
+ static constexpr absl::uint128 epsilon() { return 0; }
+ static constexpr absl::uint128 round_error() { return 0; }
+ static constexpr absl::uint128 infinity() { return 0; }
+ static constexpr absl::uint128 quiet_NaN() { return 0; }
+ static constexpr absl::uint128 signaling_NaN() { return 0; }
+ static constexpr absl::uint128 denorm_min() { return 0; }
+};
+} // namespace std
+
// TODO(absl-team): Implement signed 128-bit type
// --------------------------------------------------------------------------
// Implementation details follow
// --------------------------------------------------------------------------
+namespace absl {
+inline namespace lts_2018_12_18 {
constexpr uint128 MakeUint128(uint64_t high, uint64_t low) {
return uint128(high, low);
}
-constexpr uint128 Uint128Max() {
- return uint128(std::numeric_limits<uint64_t>::max(),
- std::numeric_limits<uint64_t>::max());
-}
-
// Assignment from integer types.
inline uint128& uint128::operator=(int v) { return *this = uint128(v); }
@@ -330,13 +392,13 @@ constexpr uint128::uint128(uint64_t high, uint64_t low)
constexpr uint128::uint128(int v)
: lo_{static_cast<uint64_t>(v)},
- hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0} {}
+ hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0} {}
constexpr uint128::uint128(long v) // NOLINT(runtime/int)
: lo_{static_cast<uint64_t>(v)},
- hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0} {}
+ hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0} {}
constexpr uint128::uint128(long long v) // NOLINT(runtime/int)
: lo_{static_cast<uint64_t>(v)},
- hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0} {}
+ hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0} {}
constexpr uint128::uint128(unsigned int v) : lo_{v}, hi_{0} {}
// NOLINTNEXTLINE(runtime/int)
@@ -359,13 +421,13 @@ constexpr uint128::uint128(uint64_t high, uint64_t low)
: hi_{high}, lo_{low} {}
constexpr uint128::uint128(int v)
- : hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0},
+ : hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0},
lo_{static_cast<uint64_t>(v)} {}
constexpr uint128::uint128(long v) // NOLINT(runtime/int)
- : hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0},
+ : hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0},
lo_{static_cast<uint64_t>(v)} {}
constexpr uint128::uint128(long long v) // NOLINT(runtime/int)
- : hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0},
+ : hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0},
lo_{static_cast<uint64_t>(v)} {}
constexpr uint128::uint128(unsigned int v) : hi_{0}, lo_{v} {}
@@ -607,6 +669,12 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) {
// can be used for uint128 storage.
return static_cast<unsigned __int128>(lhs) *
static_cast<unsigned __int128>(rhs);
+#elif defined(_MSC_VER) && defined(_WIN64)
+ uint64_t carry;
+ uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry);
+ return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) +
+ Uint128High64(lhs) * Uint128Low64(rhs) + carry,
+ low);
#else // ABSL_HAVE_INTRINSIC128
uint64_t a32 = Uint128Low64(lhs) >> 32;
uint64_t a00 = Uint128Low64(lhs) & 0xffffffff;
@@ -652,7 +720,7 @@ inline uint128& uint128::operator--() {
#include "absl/numeric/int128_no_intrinsic.inc"
#endif // ABSL_HAVE_INTRINSIC_INT128
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_NUMERIC_INT128_H_
diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc
index 79bcca90..dfe3475a 100644
--- a/absl/numeric/int128_test.cc
+++ b/absl/numeric/int128_test.cc
@@ -23,6 +23,7 @@
#include "gtest/gtest.h"
#include "absl/base/internal/cycleclock.h"
+#include "absl/hash/hash_testing.h"
#include "absl/meta/type_traits.h"
#if defined(_MSC_VER) && _MSC_VER == 1900
@@ -428,4 +429,15 @@ TEST(Uint128, ConstexprTest) {
EXPECT_EQ(minus_two, absl::MakeUint128(-1, -2));
}
+TEST(Uint128, NumericLimitsTest) {
+ static_assert(std::numeric_limits<absl::uint128>::is_specialized, "");
+ static_assert(!std::numeric_limits<absl::uint128>::is_signed, "");
+ static_assert(std::numeric_limits<absl::uint128>::is_integer, "");
+ EXPECT_EQ(static_cast<int>(128 * std::log10(2)),
+ std::numeric_limits<absl::uint128>::digits10);
+ EXPECT_EQ(0, std::numeric_limits<absl::uint128>::min());
+ EXPECT_EQ(0, std::numeric_limits<absl::uint128>::lowest());
+ EXPECT_EQ(absl::Uint128Max(), std::numeric_limits<absl::uint128>::max());
+}
+
} // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 328f52f3..3b85f1b4 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -19,6 +19,7 @@ load(
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
"ABSL_EXCEPTIONS_FLAG",
+ "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
)
package(
@@ -69,6 +70,7 @@ cc_library(
deps = [
":internal",
"//absl/base",
+ "//absl/base:bits",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
@@ -86,7 +88,6 @@ cc_library(
"internal/utf8.cc",
],
hdrs = [
- "internal/bits.h",
"internal/char_map.h",
"internal/ostringstream.h",
"internal/resize_uninitialized.h",
@@ -159,6 +160,18 @@ cc_test(
)
cc_test(
+ name = "ascii_benchmark",
+ srcs = ["ascii_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":strings",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
+cc_test(
name = "memutil_benchmark",
srcs = [
"internal/memutil.h",
@@ -200,7 +213,6 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":internal",
- ":strings",
"//absl/base:core_headers",
"@com_google_googletest//:gtest_main",
],
@@ -225,6 +237,7 @@ cc_test(
size = "small",
srcs = ["string_view_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
":strings",
@@ -361,7 +374,6 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
- "//absl/memory",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -401,6 +413,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
+ ":pow10_helper",
":strings",
"//absl/base",
"//absl/base:core_headers",
@@ -409,6 +422,19 @@ cc_test(
)
cc_test(
+ name = "numbers_benchmark",
+ srcs = ["numbers_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":strings",
+ "//absl/base",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
+cc_test(
name = "strip_test",
size = "small",
srcs = ["strip_test.cc"],
@@ -446,6 +472,8 @@ cc_test(
srcs = ["charconv_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
+ ":pow10_helper",
+ ":str_format",
":strings",
"//absl/base",
"@com_google_googletest//:gtest_main",
@@ -486,9 +514,171 @@ cc_test(
srcs = [
"charconv_benchmark.cc",
],
+ tags = [
+ "benchmark",
+ ],
deps = [
":strings",
"//absl/base",
"@com_github_google_benchmark//:benchmark_main",
],
)
+
+cc_library(
+ name = "str_format",
+ hdrs = [
+ "str_format.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":str_format_internal",
+ ],
+)
+
+cc_library(
+ name = "str_format_internal",
+ srcs = [
+ "internal/str_format/arg.cc",
+ "internal/str_format/bind.cc",
+ "internal/str_format/extension.cc",
+ "internal/str_format/float_conversion.cc",
+ "internal/str_format/output.cc",
+ "internal/str_format/parser.cc",
+ ],
+ hdrs = [
+ "internal/str_format/arg.h",
+ "internal/str_format/bind.h",
+ "internal/str_format/checker.h",
+ "internal/str_format/extension.h",
+ "internal/str_format/float_conversion.h",
+ "internal/str_format/output.h",
+ "internal/str_format/parser.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":strings",
+ "//absl/base:core_headers",
+ "//absl/container:inlined_vector",
+ "//absl/meta:type_traits",
+ "//absl/numeric:int128",
+ "//absl/types:span",
+ ],
+)
+
+cc_test(
+ name = "str_format_test",
+ srcs = ["str_format_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":str_format",
+ ":strings",
+ "//absl/base:core_headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "str_format_extension_test",
+ srcs = [
+ "internal/str_format/extension_test.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":str_format",
+ ":str_format_internal",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "str_format_arg_test",
+ srcs = ["internal/str_format/arg_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":str_format",
+ ":str_format_internal",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "str_format_bind_test",
+ srcs = ["internal/str_format/bind_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":str_format_internal",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "str_format_checker_test",
+ srcs = ["internal/str_format/checker_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":str_format",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "str_format_convert_test",
+ size = "small",
+ srcs = ["internal/str_format/convert_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":str_format_internal",
+ "//absl/numeric:int128",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "str_format_output_test",
+ srcs = ["internal/str_format/output_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":str_format_internal",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "str_format_parser_test",
+ srcs = ["internal/str_format/parser_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":str_format_internal",
+ "//absl/base:core_headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "pow10_helper",
+ testonly = True,
+ srcs = ["internal/pow10_helper.cc"],
+ hdrs = ["internal/pow10_helper.h"],
+ visibility = ["//visibility:private"],
+)
+
+cc_test(
+ name = "pow10_helper_test",
+ srcs = ["internal/pow10_helper_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":pow10_helper",
+ ":str_format",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index cab2c456..5b877ad1 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -32,7 +32,6 @@ list(APPEND STRINGS_PUBLIC_HEADERS
list(APPEND STRINGS_INTERNAL_HEADERS
- "internal/bits.h"
"internal/char_map.h"
"internal/charconv_bigint.h"
"internal/charconv_parse.h"
@@ -68,7 +67,7 @@ list(APPEND STRINGS_SRC
${STRINGS_PUBLIC_HEADERS}
${STRINGS_INTERNAL_HEADERS}
)
-set(STRINGS_PUBLIC_LIBRARIES absl::base absl_throw_delegate)
+set(STRINGS_PUBLIC_LIBRARIES absl::base absl_internal_throw_delegate)
absl_library(
TARGET
@@ -81,6 +80,65 @@ absl_library(
strings
)
+# add str_format library
+absl_header_library(
+ TARGET
+ absl_str_format
+ PUBLIC_LIBRARIES
+ str_format_internal
+ EXPORT_NAME
+ str_format
+)
+
+# str_format_internal
+absl_library(
+ TARGET
+ str_format_internal
+ SOURCES
+ "internal/str_format/arg.cc"
+ "internal/str_format/bind.cc"
+ "internal/str_format/extension.cc"
+ "internal/str_format/float_conversion.cc"
+ "internal/str_format/output.cc"
+ "internal/str_format/parser.cc"
+ "internal/str_format/arg.h"
+ "internal/str_format/bind.h"
+ "internal/str_format/checker.h"
+ "internal/str_format/extension.h"
+ "internal/str_format/float_conversion.h"
+ "internal/str_format/output.h"
+ "internal/str_format/parser.h"
+ PUBLIC_LIBRARIES
+ str_format_extension_internal
+ absl::strings
+ absl::base
+ absl::numeric
+ absl::inlined_vector
+ absl::span
+)
+
+# str_format_extension_internal
+absl_library(
+ TARGET
+ str_format_extension_internal
+ SOURCES
+ "internal/str_format/extension.cc"
+ "internal/str_format/extension.h"
+ "internal/str_format/output.cc"
+ "internal/str_format/output.h"
+ PUBLIC_LIBRARIES
+ absl::base
+ absl::strings
+)
+
+# pow10_helper
+absl_library(
+ TARGET
+ pow10_helper
+ SOURCES
+ "internal/pow10_helper.cc"
+ "internal/pow10_helper.h"
+)
#
## TESTS
@@ -158,7 +216,7 @@ absl_test(
# test string_view_test
set(STRING_VIEW_TEST_SRC "string_view_test.cc")
-set(STRING_VIEW_TEST_PUBLIC_LIBRARIES absl::strings absl_throw_delegate absl::base)
+set(STRING_VIEW_TEST_PUBLIC_LIBRARIES absl::strings absl_internal_throw_delegate absl::base)
absl_test(
TARGET
@@ -186,7 +244,7 @@ absl_test(
# test str_replace_test
set(STR_REPLACE_TEST_SRC "str_replace_test.cc")
-set(STR_REPLACE_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_throw_delegate)
+set(STR_REPLACE_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_internal_throw_delegate)
absl_test(
TARGET
@@ -200,7 +258,7 @@ absl_test(
# test str_split_test
set(STR_SPLIT_TEST_SRC "str_split_test.cc")
-set(STR_SPLIT_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_throw_delegate)
+set(STR_SPLIT_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_internal_throw_delegate)
absl_test(
TARGET
@@ -267,7 +325,7 @@ absl_test(
# test numbers_test
set(NUMBERS_TEST_SRC "numbers_test.cc")
-set(NUMBERS_TEST_PUBLIC_LIBRARIES absl::strings)
+set(NUMBERS_TEST_PUBLIC_LIBRARIES absl::strings pow10_helper)
absl_test(
TARGET
@@ -309,7 +367,7 @@ absl_test(
# test charconv_test
set(CHARCONV_TEST_SRC "charconv_test.cc")
-set(CHARCONV_TEST_PUBLIC_LIBRARIES absl::strings)
+set(CHARCONV_TEST_PUBLIC_LIBRARIES absl::strings absl::str_format pow10_helper)
absl_test(
TARGET
@@ -347,3 +405,77 @@ absl_test(
PUBLIC_LIBRARIES
${CHARCONV_BIGINT_TEST_PUBLIC_LIBRARIES}
)
+# test str_format_test
+absl_test(
+ TARGET
+ str_format_test
+ SOURCES
+ "str_format_test.cc"
+ PUBLIC_LIBRARIES
+ absl::base
+ absl::str_format
+ absl::strings
+)
+
+# test str_format_bind_test
+absl_test(
+ TARGET
+ str_format_bind_test
+ SOURCES
+ "internal/str_format/bind_test.cc"
+ PUBLIC_LIBRARIES
+ str_format_internal
+)
+
+# test str_format_checker_test
+absl_test(
+ TARGET
+ str_format_checker_test
+ SOURCES
+ "internal/str_format/checker_test.cc"
+ PUBLIC_LIBRARIES
+ absl::str_format
+)
+
+# test str_format_convert_test
+absl_test(
+ TARGET
+ str_format_convert_test
+ SOURCES
+ "internal/str_format/convert_test.cc"
+ PUBLIC_LIBRARIES
+ str_format_internal
+ absl::numeric
+)
+
+# test str_format_output_test
+absl_test(
+ TARGET
+ str_format_output_test
+ SOURCES
+ "internal/str_format/output_test.cc"
+ PUBLIC_LIBRARIES
+ str_format_extension_internal
+)
+
+# test str_format_parser_test
+absl_test(
+ TARGET
+ str_format_parser_test
+ SOURCES
+ "internal/str_format/parser_test.cc"
+ PUBLIC_LIBRARIES
+ str_format_internal
+ absl::base
+)
+
+# test pow10_helper_test
+absl_test(
+ TARGET
+ pow10_helper_test
+ SOURCES
+ "internal/pow10_helper_test.cc"
+ PUBLIC_LIBRARIES
+ pow10_helper
+ absl::str_format
+)
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc
index 5c94acc7..5d08e816 100644
--- a/absl/strings/ascii.cc
+++ b/absl/strings/ascii.cc
@@ -15,7 +15,7 @@
#include "absl/strings/ascii.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace ascii_internal {
// # Table generated by this Python code (bit 0x02 is currently unused):
@@ -196,5 +196,5 @@ void RemoveExtraAsciiWhitespace(std::string* str) {
str->erase(output_it - &(*str)[0]);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h
index 507fad4b..98418fd2 100644
--- a/absl/strings/ascii.h
+++ b/absl/strings/ascii.h
@@ -59,7 +59,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace ascii_internal {
// Declaration for an array of bitfields holding character information.
@@ -166,7 +166,7 @@ inline char ascii_tolower(unsigned char c) {
// Converts the characters in `s` to lowercase, changing the contents of `s`.
void AsciiStrToLower(std::string* s);
-// Creates a lowercase std::string from a given absl::string_view.
+// Creates a lowercase string from a given absl::string_view.
ABSL_MUST_USE_RESULT inline std::string AsciiStrToLower(absl::string_view s) {
std::string result(s);
absl::AsciiStrToLower(&result);
@@ -184,7 +184,7 @@ inline char ascii_toupper(unsigned char c) {
// Converts the characters in `s` to uppercase, changing the contents of `s`.
void AsciiStrToUpper(std::string* s);
-// Creates an uppercase std::string from a given absl::string_view.
+// Creates an uppercase string from a given absl::string_view.
ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) {
std::string result(s);
absl::AsciiStrToUpper(&result);
@@ -196,10 +196,10 @@ ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) {
ABSL_MUST_USE_RESULT inline absl::string_view StripLeadingAsciiWhitespace(
absl::string_view str) {
auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace);
- return absl::string_view(it, str.end() - it);
+ return str.substr(it - str.begin());
}
-// Strips in place whitespace from the beginning of the given std::string.
+// Strips in place whitespace from the beginning of the given string.
inline void StripLeadingAsciiWhitespace(std::string* str) {
auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
str->erase(str->begin(), it);
@@ -210,10 +210,10 @@ inline void StripLeadingAsciiWhitespace(std::string* str) {
ABSL_MUST_USE_RESULT inline absl::string_view StripTrailingAsciiWhitespace(
absl::string_view str) {
auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace);
- return absl::string_view(str.begin(), str.rend() - it);
+ return str.substr(0, str.rend() - it);
}
-// Strips in place whitespace from the end of the given std::string
+// Strips in place whitespace from the end of the given string
inline void StripTrailingAsciiWhitespace(std::string* str) {
auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
str->erase(str->rend() - it);
@@ -226,7 +226,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripAsciiWhitespace(
return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str));
}
-// Strips in place whitespace from both ends of the given std::string
+// Strips in place whitespace from both ends of the given string
inline void StripAsciiWhitespace(std::string* str) {
StripTrailingAsciiWhitespace(str);
StripLeadingAsciiWhitespace(str);
@@ -235,7 +235,7 @@ inline void StripAsciiWhitespace(std::string* str) {
// Removes leading, trailing, and consecutive internal whitespace.
void RemoveExtraAsciiWhitespace(std::string*);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_ASCII_H_
diff --git a/absl/strings/ascii_benchmark.cc b/absl/strings/ascii_benchmark.cc
new file mode 100644
index 00000000..8dea4b8c
--- /dev/null
+++ b/absl/strings/ascii_benchmark.cc
@@ -0,0 +1,120 @@
+// 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
+//
+// 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.
+
+#include "absl/strings/ascii.h"
+
+#include <cctype>
+#include <string>
+#include <array>
+#include <random>
+
+#include "benchmark/benchmark.h"
+
+namespace {
+
+std::array<unsigned char, 256> MakeShuffledBytes() {
+ std::array<unsigned char, 256> bytes;
+ for (size_t i = 0; i < 256; ++i) bytes[i] = static_cast<unsigned char>(i);
+ std::random_device rd;
+ std::seed_seq seed({rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()});
+ std::mt19937 g(seed);
+ std::shuffle(bytes.begin(), bytes.end(), g);
+ return bytes;
+}
+
+template <typename Function>
+void AsciiBenchmark(benchmark::State& state, Function f) {
+ std::array<unsigned char, 256> bytes = MakeShuffledBytes();
+ size_t sum = 0;
+ for (auto _ : state) {
+ for (unsigned char b : bytes) sum += f(b) ? 1 : 0;
+ }
+ // Make a copy of `sum` before calling `DoNotOptimize` to make sure that `sum`
+ // can be put in a CPU register and not degrade performance in the loop above.
+ size_t sum2 = sum;
+ benchmark::DoNotOptimize(sum2);
+ state.SetBytesProcessed(state.iterations() * bytes.size());
+}
+
+using StdAsciiFunction = int (*)(int);
+template <StdAsciiFunction f>
+void BM_Ascii(benchmark::State& state) {
+ AsciiBenchmark(state, f);
+}
+
+using AbslAsciiIsFunction = bool (*)(unsigned char);
+template <AbslAsciiIsFunction f>
+void BM_Ascii(benchmark::State& state) {
+ AsciiBenchmark(state, f);
+}
+
+using AbslAsciiToFunction = char (*)(unsigned char);
+template <AbslAsciiToFunction f>
+void BM_Ascii(benchmark::State& state) {
+ AsciiBenchmark(state, f);
+}
+
+inline char Noop(unsigned char b) { return static_cast<char>(b); }
+
+BENCHMARK_TEMPLATE(BM_Ascii, Noop);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isalpha);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isalpha);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isdigit);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isdigit);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isalnum);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isalnum);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isspace);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isspace);
+BENCHMARK_TEMPLATE(BM_Ascii, std::ispunct);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_ispunct);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isblank);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isblank);
+BENCHMARK_TEMPLATE(BM_Ascii, std::iscntrl);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_iscntrl);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isxdigit);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isxdigit);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isprint);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isprint);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isgraph);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isgraph);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isupper);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isupper);
+BENCHMARK_TEMPLATE(BM_Ascii, std::islower);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_islower);
+BENCHMARK_TEMPLATE(BM_Ascii, isascii);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isascii);
+BENCHMARK_TEMPLATE(BM_Ascii, std::tolower);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_tolower);
+BENCHMARK_TEMPLATE(BM_Ascii, std::toupper);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_toupper);
+
+static void BM_StrToLower(benchmark::State& state) {
+ const int size = state.range(0);
+ std::string s(size, 'X');
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(absl::AsciiStrToLower(s));
+ }
+}
+BENCHMARK(BM_StrToLower)->Range(1, 1 << 20);
+
+static void BM_StrToUpper(benchmark::State& state) {
+ const int size = state.range(0);
+ std::string s(size, 'x');
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(absl::AsciiStrToUpper(s));
+ }
+}
+BENCHMARK(BM_StrToUpper)->Range(1, 1 << 20);
+
+} // namespace
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index ab75ab49..21ea17b1 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -20,8 +20,8 @@
#include <cstring>
#include "absl/base/casts.h"
+#include "absl/base/internal/bits.h"
#include "absl/numeric/int128.h"
-#include "absl/strings/internal/bits.h"
#include "absl/strings/internal/charconv_bigint.h"
#include "absl/strings/internal/charconv_parse.h"
@@ -57,7 +57,7 @@
// narrower mantissas.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
template <typename FloatType>
@@ -244,9 +244,9 @@ struct CalculatedFloat {
// minus the number of leading zero bits.)
int BitWidth(uint128 value) {
if (Uint128High64(value) == 0) {
- return 64 - strings_internal::CountLeadingZeros64(Uint128Low64(value));
+ return 64 - base_internal::CountLeadingZeros64(Uint128Low64(value));
}
- return 128 - strings_internal::CountLeadingZeros64(Uint128High64(value));
+ return 128 - base_internal::CountLeadingZeros64(Uint128High64(value));
}
// Calculates how far to the right a mantissa needs to be shifted to create a
@@ -519,7 +519,7 @@ CalculatedFloat CalculateFromParsedHexadecimal(
const strings_internal::ParsedFloat& parsed_hex) {
uint64_t mantissa = parsed_hex.mantissa;
int exponent = parsed_hex.exponent;
- int mantissa_width = 64 - strings_internal::CountLeadingZeros64(mantissa);
+ int mantissa_width = 64 - base_internal::CountLeadingZeros64(mantissa);
const int shift = NormalizedShiftSize<FloatType>(mantissa_width, exponent);
bool result_exact;
exponent += shift;
@@ -980,5 +980,5 @@ const int16_t kPower10ExponentTable[] = {
};
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h
index fe2dee37..160306e6 100644
--- a/absl/strings/charconv.h
+++ b/absl/strings/charconv.h
@@ -18,12 +18,12 @@
#include <system_error> // NOLINT(build/c++11)
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// Workalike compatibilty version of std::chars_format from C++17.
//
// This is an bitfield enumerator which can be passed to absl::from_chars to
-// configure the std::string-to-float conversion.
+// configure the string-to-float conversion.
enum class chars_format {
scientific = 1,
fixed = 2,
@@ -31,7 +31,7 @@ enum class chars_format {
general = fixed | scientific,
};
-// The return result of a std::string-to-number conversion.
+// The return result of a string-to-number conversion.
//
// `ec` will be set to `invalid_argument` if a well-formed number was not found
// at the start of the input range, `result_out_of_range` if a well-formed
@@ -68,7 +68,7 @@ struct from_chars_result {
// If `fmt` is set, it must be one of the enumerator values of the chars_format.
// (This is despite the fact that chars_format is a bitmask type.) If set to
// `scientific`, a matching number must contain an exponent. If set to `fixed`,
-// then an exponent will never match. (For example, the std::string "1e5" will be
+// then an exponent will never match. (For example, the string "1e5" will be
// parsed as "1".) If set to `hex`, then a hexadecimal float is parsed in the
// format that strtod() accepts, except that a "0x" prefix is NOT matched.
// (In particular, in `hex` mode, the input "0xff" results in the largest
@@ -111,7 +111,7 @@ inline chars_format& operator^=(chars_format& lhs, chars_format rhs) {
return lhs;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_CHARCONV_H_
diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc
index f8d71cc6..d07537eb 100644
--- a/absl/strings/charconv_test.cc
+++ b/absl/strings/charconv_test.cc
@@ -19,7 +19,9 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/strings/internal/pow10_helper.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
#ifdef _MSC_FULL_VER
#define ABSL_COMPILER_DOES_EXACT_ROUNDING 0
@@ -31,9 +33,11 @@
namespace {
+using absl::strings_internal::Pow10;
+
#if ABSL_COMPILER_DOES_EXACT_ROUNDING
-// Tests that the given std::string is accepted by absl::from_chars, and that it
+// Tests that the given string is accepted by absl::from_chars, and that it
// converts exactly equal to the given number.
void TestDoubleParse(absl::string_view str, double expected_number) {
SCOPED_TRACE(str);
@@ -250,7 +254,7 @@ TEST(FromChars, NearRoundingCasesExplicit) {
EXPECT_EQ(ToFloat("459926601011.e15"), ldexpf(12466336, 65));
}
-// Common test logic for converting a std::string which lies exactly halfway between
+// Common test logic for converting a string which lies exactly halfway between
// two target floats.
//
// mantissa and exponent represent the precise value between two floating point
@@ -655,7 +659,7 @@ int NextStep(int step) {
// is correct for in-bounds values, and that overflow and underflow are done
// correctly for out-of-bounds values.
//
-// input_generator maps from an integer index to a std::string to test.
+// input_generator maps from an integer index to a string to test.
// expected_generator maps from an integer index to an expected Float value.
// from_chars conversion of input_generator(i) should result in
// expected_generator(i).
@@ -678,7 +682,8 @@ void TestOverflowAndUnderflow(
auto result =
absl::from_chars(input.data(), input.data() + input.size(), actual);
EXPECT_EQ(result.ec, std::errc());
- EXPECT_EQ(expected, actual);
+ EXPECT_EQ(expected, actual)
+ << absl::StrFormat("%a vs %a", expected, actual);
}
// test legal values near upper_bound
for (index = upper_bound, step = 1; index > lower_bound;
@@ -690,7 +695,8 @@ void TestOverflowAndUnderflow(
auto result =
absl::from_chars(input.data(), input.data() + input.size(), actual);
EXPECT_EQ(result.ec, std::errc());
- EXPECT_EQ(expected, actual);
+ EXPECT_EQ(expected, actual)
+ << absl::StrFormat("%a vs %a", expected, actual);
}
// Test underflow values below lower_bound
for (index = lower_bound - 1, step = 1; index > -1000000;
@@ -747,7 +753,7 @@ TEST(FromChars, HexdecimalFloatLimits) {
// acceptable exponents in this test.
TEST(FromChars, DecimalDoubleLimits) {
auto input_gen = [](int index) { return absl::StrCat("1.0e", index); };
- auto expected_gen = [](int index) { return std::pow(10.0, index); };
+ auto expected_gen = [](int index) { return Pow10(index); };
TestOverflowAndUnderflow<double>(input_gen, expected_gen, -323, 308);
}
@@ -759,7 +765,7 @@ TEST(FromChars, DecimalDoubleLimits) {
// acceptable exponents in this test.
TEST(FromChars, DecimalFloatLimits) {
auto input_gen = [](int index) { return absl::StrCat("1.0e", index); };
- auto expected_gen = [](int index) { return std::pow(10.0, index); };
+ auto expected_gen = [](int index) { return Pow10(index); };
TestOverflowAndUnderflow<float>(input_gen, expected_gen, -45, 38);
}
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index 9874f492..69053c19 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -33,7 +33,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
// Digit conversion.
@@ -90,7 +90,7 @@ inline bool IsSurrogate(char32_t c, absl::string_view src, std::string* error) {
//
// Unescapes C escape sequences and is the reverse of CEscape().
//
-// If 'source' is valid, stores the unescaped std::string and its size in
+// If 'source' is valid, stores the unescaped string and its size in
// 'dest' and 'dest_len' respectively, and returns true. Otherwise
// returns false and optionally stores the error description in
// 'error'. Set 'error' to nullptr to disable error reporting.
@@ -105,7 +105,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
char* dest, ptrdiff_t* dest_len, std::string* error) {
char* d = dest;
const char* p = source.data();
- const char* end = source.end();
+ const char* end = p + source.size();
const char* last_byte = end - 1;
// Small optimization for case where source = dest and there's no escaping
@@ -295,7 +295,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
// ----------------------------------------------------------------------
// CUnescapeInternal()
//
-// Same as above but uses a C++ std::string for output. 'source' and 'dest'
+// Same as above but uses a C++ string for output. 'source' and 'dest'
// may be the same.
// ----------------------------------------------------------------------
bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
@@ -305,7 +305,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
ptrdiff_t dest_size;
if (!CUnescapeInternal(source,
leave_nulls_escaped,
- const_cast<char*>(dest->data()),
+ &(*dest)[0],
&dest_size,
error)) {
return false;
@@ -685,7 +685,7 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest,
// The arrays below were generated by the following code
// #include <sys/time.h>
// #include <stdlib.h>
-// #include <std::string.h>
+// #include <string.h>
// main()
// {
// static const char Base64[] =
@@ -940,7 +940,8 @@ constexpr char kBase64Chars[] =
constexpr char kWebSafeBase64Chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-void Base64EscapeInternal(const unsigned char* src, size_t szsrc, std::string* dest,
+template <typename String>
+void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest,
bool do_padding, const char* base64_chars) {
const size_t calc_escaped_size =
CalculateBase64EscapedLenInternal(szsrc, do_padding);
@@ -952,7 +953,8 @@ void Base64EscapeInternal(const unsigned char* src, size_t szsrc, std::string* d
dest->erase(escaped_len);
}
-bool Base64UnescapeInternal(const char* src, size_t slen, std::string* dest,
+template <typename String>
+bool Base64UnescapeInternal(const char* src, size_t slen, String* dest,
const signed char* unbase64) {
// Determine the size of the output std::string. Base64 encodes every 3 bytes into
// 4 characters. any leftover chars are added directly for good measure.
@@ -1000,7 +1002,7 @@ constexpr char kHexValue[256] = {
/* clang-format on */
// This is a templated function so that T can be either a char*
-// or a std::string. This works because we use the [] operator to access
+// or a string. This works because we use the [] operator to access
// individual characters at a time.
template <typename T>
void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) {
@@ -1010,7 +1012,7 @@ void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) {
}
}
-// This is a templated function so that T can be either a char* or a std::string.
+// This is a templated function so that T can be either a char* or a string.
template <typename T>
void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) {
auto dest_ptr = &dest[0];
@@ -1107,5 +1109,5 @@ std::string BytesToHexString(absl::string_view from) {
return result;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h
index 16326a9c..a31fb374 100644
--- a/absl/strings/escaping.h
+++ b/absl/strings/escaping.h
@@ -17,7 +17,7 @@
// File: escaping.h
// -----------------------------------------------------------------------------
//
-// This header file contains std::string utilities involved in escaping and
+// This header file contains string utilities involved in escaping and
// unescaping strings in various ways.
//
@@ -34,11 +34,11 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// CUnescape()
//
-// Unescapes a `source` std::string and copies it into `dest`, rewriting C-style
+// Unescapes a `source` string and copies it into `dest`, rewriting C-style
// escape sequences (http://en.cppreference.com/w/cpp/language/escape) into
// their proper code point equivalents, returning `true` if successful.
//
@@ -58,9 +58,10 @@ inline namespace lts_2018_06_20 {
// 0x99).
//
//
-// If any errors are encountered, this function returns `false` and stores the
-// first encountered error in `error`. To disable error reporting, set `error`
-// to `nullptr` or use the overload with no error reporting below.
+// If any errors are encountered, this function returns `false`, leaving the
+// `dest` output parameter in an unspecified state, and stores the first
+// encountered error in `error`. To disable error reporting, set `error` to
+// `nullptr` or use the overload with no error reporting below.
//
// Example:
//
@@ -79,7 +80,7 @@ inline bool CUnescape(absl::string_view source, std::string* dest) {
// CEscape()
//
-// Escapes a 'src' std::string using C-style escapes sequences
+// Escapes a 'src' string using C-style escapes sequences
// (http://en.cppreference.com/w/cpp/language/escape), escaping other
// non-printable/non-whitespace bytes as octal sequences (e.g. "\377").
//
@@ -92,7 +93,7 @@ std::string CEscape(absl::string_view src);
// CHexEscape()
//
-// Escapes a 'src' std::string using C-style escape sequences, escaping
+// Escapes a 'src' string using C-style escape sequences, escaping
// other non-printable/non-whitespace bytes as hexadecimal sequences (e.g.
// "\xFF").
//
@@ -105,7 +106,7 @@ std::string CHexEscape(absl::string_view src);
// Utf8SafeCEscape()
//
-// Escapes a 'src' std::string using C-style escape sequences, escaping bytes as
+// Escapes a 'src' string using C-style escape sequences, escaping bytes as
// octal sequences, and passing through UTF-8 characters without conversion.
// I.e., when encountering any bytes with their high bit set, this function
// will not escape those values, whether or not they are valid UTF-8.
@@ -113,51 +114,51 @@ std::string Utf8SafeCEscape(absl::string_view src);
// Utf8SafeCHexEscape()
//
-// Escapes a 'src' std::string using C-style escape sequences, escaping bytes as
+// Escapes a 'src' string using C-style escape sequences, escaping bytes as
// hexadecimal sequences, and passing through UTF-8 characters without
// conversion.
std::string Utf8SafeCHexEscape(absl::string_view src);
// Base64Unescape()
//
-// Converts a `src` std::string encoded in Base64 to its binary equivalent, writing
+// Converts a `src` string encoded in Base64 to its binary equivalent, writing
// it to a `dest` buffer, returning `true` on success. If `src` contains invalid
// characters, `dest` is cleared and returns `false`.
bool Base64Unescape(absl::string_view src, std::string* dest);
-// WebSafeBase64Unescape(absl::string_view, std::string*)
+// WebSafeBase64Unescape()
//
-// Converts a `src` std::string encoded in Base64 to its binary equivalent, writing
+// Converts a `src` string encoded in Base64 to its binary equivalent, writing
// it to a `dest` buffer, but using '-' instead of '+', and '_' instead of '/'.
// If `src` contains invalid characters, `dest` is cleared and returns `false`.
bool WebSafeBase64Unescape(absl::string_view src, std::string* dest);
// Base64Escape()
//
-// Encodes a `src` std::string into a `dest` buffer using base64 encoding, with
+// Encodes a `src` string into a `dest` buffer using base64 encoding, with
// padding characters. This function conforms with RFC 4648 section 4 (base64).
void Base64Escape(absl::string_view src, std::string* dest);
// WebSafeBase64Escape()
//
-// Encodes a `src` std::string into a `dest` buffer using uses '-' instead of '+' and
+// Encodes a `src` string into a `dest` buffer using '-' instead of '+' and
// '_' instead of '/', and without padding. This function conforms with RFC 4648
// section 5 (base64url).
void WebSafeBase64Escape(absl::string_view src, std::string* dest);
// HexStringToBytes()
//
-// Converts an ASCII hex std::string into bytes, returning binary data of length
+// Converts an ASCII hex string into bytes, returning binary data of length
// `from.size()/2`.
std::string HexStringToBytes(absl::string_view from);
// BytesToHexString()
//
-// Converts binary data into an ASCII text std::string, returning a std::string of size
+// Converts binary data into an ASCII text string, returning a string of size
// `2*from.size()`.
std::string BytesToHexString(absl::string_view from);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_ESCAPING_H_
diff --git a/absl/strings/escaping_test.cc b/absl/strings/escaping_test.cc
index 3f65ec10..9dc27f3f 100644
--- a/absl/strings/escaping_test.cc
+++ b/absl/strings/escaping_test.cc
@@ -543,18 +543,19 @@ static struct {
{"abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="},
};
-TEST(Base64, EscapeAndUnescape) {
+template <typename StringType>
+void TestEscapeAndUnescape() {
// Check the short strings; this tests the math (and boundaries)
for (const auto& tc : base64_tests) {
- std::string encoded("this junk should be ignored");
+ StringType encoded("this junk should be ignored");
absl::Base64Escape(tc.plaintext, &encoded);
EXPECT_EQ(encoded, tc.cyphertext);
- std::string decoded("this junk should be ignored");
+ StringType decoded("this junk should be ignored");
EXPECT_TRUE(absl::Base64Unescape(encoded, &decoded));
EXPECT_EQ(decoded, tc.plaintext);
- std::string websafe(tc.cyphertext);
+ StringType websafe(tc.cyphertext);
for (int c = 0; c < websafe.size(); ++c) {
if ('+' == websafe[c]) websafe[c] = '-';
if ('/' == websafe[c]) websafe[c] = '_';
@@ -576,7 +577,7 @@ TEST(Base64, EscapeAndUnescape) {
// Now try the long strings, this tests the streaming
for (const auto& tc : absl::strings_internal::base64_strings()) {
- std::string buffer;
+ StringType buffer;
absl::WebSafeBase64Escape(tc.plaintext, &buffer);
EXPECT_EQ(tc.cyphertext, buffer);
}
@@ -586,7 +587,7 @@ TEST(Base64, EscapeAndUnescape) {
absl::string_view data_set[] = {"ab-/", absl::string_view("\0bcd", 4),
absl::string_view("abc.\0", 5)};
for (absl::string_view bad_data : data_set) {
- std::string buf;
+ StringType buf;
EXPECT_FALSE(absl::Base64Unescape(bad_data, &buf));
EXPECT_FALSE(absl::WebSafeBase64Unescape(bad_data, &buf));
EXPECT_TRUE(buf.empty());
@@ -594,6 +595,10 @@ TEST(Base64, EscapeAndUnescape) {
}
}
+TEST(Base64, EscapeAndUnescape) {
+ TestEscapeAndUnescape<std::string>();
+}
+
TEST(Base64, DISABLED_HugeData) {
const size_t kSize = size_t(3) * 1000 * 1000 * 1000;
static_assert(kSize % 3 == 0, "kSize must be divisible by 3");
diff --git a/absl/strings/internal/bits.h b/absl/strings/internal/bits.h
deleted file mode 100644
index a7d0ee5b..00000000
--- a/absl/strings/internal/bits.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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
-//
-// 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.
-
-#ifndef ABSL_STRINGS_INTERNAL_BITS_H_
-#define ABSL_STRINGS_INTERNAL_BITS_H_
-
-#include <cstdint>
-
-#if defined(_MSC_VER) && defined(_M_X64)
-#include <intrin.h>
-#pragma intrinsic(_BitScanReverse64)
-#endif
-
-namespace absl {
-inline namespace lts_2018_06_20 {
-namespace strings_internal {
-
-// Returns the number of leading 0 bits in a 64-bit value.
-inline int CountLeadingZeros64(uint64_t n) {
-#if defined(__GNUC__)
- static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
- "__builtin_clzll does not take 64bit arg");
- return n == 0 ? 64 : __builtin_clzll(n);
-#elif defined(_MSC_VER) && defined(_M_X64)
- unsigned long result; // NOLINT(runtime/int)
- if (_BitScanReverse64(&result, n)) {
- return 63 - result;
- }
- return 64;
-#else
- int zeroes = 60;
- if (n >> 32) zeroes -= 32, n >>= 32;
- if (n >> 16) zeroes -= 16, n >>= 16;
- if (n >> 8) zeroes -= 8, n >>= 8;
- if (n >> 4) zeroes -= 4, n >>= 4;
- return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0\0"[n] + zeroes;
-#endif
-}
-
-} // namespace strings_internal
-} // inline namespace lts_2018_06_20
-} // namespace absl
-
-#endif // ABSL_STRINGS_INTERNAL_BITS_H_
diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h
index e1280b03..10b7d007 100644
--- a/absl/strings/internal/char_map.h
+++ b/absl/strings/internal/char_map.h
@@ -28,7 +28,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
class Charmap {
@@ -150,7 +150,7 @@ constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); }
constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); }
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
diff --git a/absl/strings/internal/charconv_bigint.cc b/absl/strings/internal/charconv_bigint.cc
index eb2e2976..dac907e2 100644
--- a/absl/strings/internal/charconv_bigint.cc
+++ b/absl/strings/internal/charconv_bigint.cc
@@ -19,7 +19,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
namespace {
@@ -355,5 +355,5 @@ template class BigUnsigned<4>;
template class BigUnsigned<84>;
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h
index 9827b56e..ffafc11c 100644
--- a/absl/strings/internal/charconv_bigint.h
+++ b/absl/strings/internal/charconv_bigint.h
@@ -25,7 +25,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// The largest power that 5 that can be raised to, and still fit in a uint32_t.
@@ -58,17 +58,10 @@ class BigUnsigned {
"unsupported max_words value");
BigUnsigned() : size_(0), words_{} {}
- explicit BigUnsigned(uint32_t v) : size_(v > 0 ? 1 : 0), words_{v} {}
- explicit BigUnsigned(uint64_t v)
- : size_(0),
- words_{static_cast<uint32_t>(v & 0xffffffff),
- static_cast<uint32_t>(v >> 32)} {
- if (words_[1]) {
- size_ = 2;
- } else if (words_[0]) {
- size_ = 1;
- }
- }
+ explicit constexpr BigUnsigned(uint64_t v)
+ : size_((v >> 32) ? 2 : v ? 1 : 0),
+ words_{static_cast<uint32_t>(v & 0xffffffffu),
+ static_cast<uint32_t>(v >> 32)} {}
// Constructs a BigUnsigned from the given string_view containing a decimal
// value. If the input std::string is not a decimal integer, constructs a 0
@@ -422,7 +415,7 @@ extern template class BigUnsigned<4>;
extern template class BigUnsigned<84>;
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_
diff --git a/absl/strings/internal/charconv_bigint_test.cc b/absl/strings/internal/charconv_bigint_test.cc
index 118b0dcb..dbab3208 100644
--- a/absl/strings/internal/charconv_bigint_test.cc
+++ b/absl/strings/internal/charconv_bigint_test.cc
@@ -19,7 +19,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
TEST(BigUnsigned, ShiftLeft) {
@@ -201,5 +201,5 @@ TEST(BigUnsigned, TenToTheNth) {
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc
index 37d75635..68d65a8a 100644
--- a/absl/strings/internal/charconv_parse.cc
+++ b/absl/strings/internal/charconv_parse.cc
@@ -22,7 +22,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
// ParseFloat<10> will read the first 19 significant digits of the mantissa.
@@ -92,7 +92,7 @@ static_assert(std::numeric_limits<int>::digits10 >= kDecimalExponentDigitsMax,
// To avoid incredibly large inputs causing integer overflow for our exponent,
// we impose an arbitrary but very large limit on the number of significant
-// digits we will accept. The implementation refuses to match a std::string with
+// digits we will accept. The implementation refuses to match a string with
// more consecutive significant mantissa digits than this.
constexpr int kDecimalDigitLimit = 50000000;
@@ -494,5 +494,5 @@ template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
chars_format format_flags);
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/charconv_parse.h b/absl/strings/internal/charconv_parse.h
index 41f4f40d..17d5a8f8 100644
--- a/absl/strings/internal/charconv_parse.h
+++ b/absl/strings/internal/charconv_parse.h
@@ -20,7 +20,7 @@
#include "absl/strings/charconv.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// Enum indicating whether a parsed float is a number or special value.
@@ -93,6 +93,6 @@ extern template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
absl::chars_format format_flags);
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_
diff --git a/absl/strings/internal/charconv_parse_test.cc b/absl/strings/internal/charconv_parse_test.cc
index 1ff86004..f48b9aee 100644
--- a/absl/strings/internal/charconv_parse_test.cc
+++ b/absl/strings/internal/charconv_parse_test.cc
@@ -29,16 +29,16 @@ using absl::strings_internal::ParseFloat;
namespace {
-// Check that a given std::string input is parsed to the expected mantissa and
+// Check that a given string input is parsed to the expected mantissa and
// exponent.
//
-// Input std::string `s` must contain a '$' character. It marks the end of the
+// Input string `s` must contain a '$' character. It marks the end of the
// characters that should be consumed by the match. It is stripped from the
// input to ParseFloat.
//
-// If input std::string `s` contains '[' and ']' characters, these mark the region
+// If input string `s` contains '[' and ']' characters, these mark the region
// of characters that should be marked as the "subrange". For NaNs, this is
-// the location of the extended NaN std::string. For numbers, this is the location
+// the location of the extended NaN string. For numbers, this is the location
// of the full, over-large mantissa.
template <int base>
void ExpectParsedFloat(std::string s, absl::chars_format format_flags,
@@ -92,10 +92,10 @@ void ExpectParsedFloat(std::string s, absl::chars_format format_flags,
EXPECT_EQ(characters_matched, expected_characters_matched);
}
-// Check that a given std::string input is parsed to the expected mantissa and
+// Check that a given string input is parsed to the expected mantissa and
// exponent.
//
-// Input std::string `s` must contain a '$' character. It marks the end of the
+// Input string `s` must contain a '$' character. It marks the end of the
// characters that were consumed by the match.
template <int base>
void ExpectNumber(std::string s, absl::chars_format format_flags,
@@ -106,7 +106,7 @@ void ExpectNumber(std::string s, absl::chars_format format_flags,
expected_literal_exponent);
}
-// Check that a given std::string input is parsed to the given special value.
+// Check that a given string input is parsed to the given special value.
//
// This tests against both number bases, since infinities and NaNs have
// identical representations in both modes.
@@ -116,7 +116,7 @@ void ExpectSpecial(const std::string& s, absl::chars_format format_flags,
ExpectParsedFloat<16>(s, format_flags, type, 0, 0);
}
-// Check that a given input std::string is not matched by Float.
+// Check that a given input string is not matched by Float.
template <int base>
void ExpectFailedParse(absl::string_view s, absl::chars_format format_flags) {
ParsedFloat parsed =
diff --git a/absl/strings/internal/escaping_test_common.h b/absl/strings/internal/escaping_test_common.h
index 478e0582..50ef595f 100644
--- a/absl/strings/internal/escaping_test_common.h
+++ b/absl/strings/internal/escaping_test_common.h
@@ -22,7 +22,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
struct base64_testcase {
@@ -127,7 +127,7 @@ inline const std::array<base64_testcase, 5>& base64_strings() {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_ESCAPING_TEST_COMMON_H_
diff --git a/absl/strings/internal/memutil.cc b/absl/strings/internal/memutil.cc
index 13ad686c..1d6cfa36 100644
--- a/absl/strings/internal/memutil.cc
+++ b/absl/strings/internal/memutil.cc
@@ -17,7 +17,7 @@
#include <cstdlib>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
int memcasecmp(const char* s1, const char* s2, size_t len) {
@@ -108,5 +108,5 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/memutil.h b/absl/strings/internal/memutil.h
index 828b01d5..dcc5c9a3 100644
--- a/absl/strings/internal/memutil.h
+++ b/absl/strings/internal/memutil.h
@@ -14,7 +14,7 @@
// limitations under the License.
//
-// These routines provide mem versions of standard C std::string routines,
+// These routines provide mem versions of standard C string routines,
// such as strpbrk. They function exactly the same as the str versions,
// so if you wonder what they are, replace the word "mem" by
// "str" and check out the man page. I could return void*, as the
@@ -22,14 +22,14 @@
// since this is by far the most common way these functions are called.
//
// The difference between the mem and str versions is the mem version
-// takes a pointer and a length, rather than a '\0'-terminated std::string.
+// takes a pointer and a length, rather than a '\0'-terminated string.
// The memcase* routines defined here assume the locale is "C"
// (they use absl::ascii_tolower instead of tolower).
//
// These routines are based on the BSD library.
//
-// Here's a list of routines from std::string.h, and their mem analogues.
-// Functions in lowercase are defined in std::string.h; those in UPPERCASE
+// Here's a list of routines from string.h, and their mem analogues.
+// Functions in lowercase are defined in string.h; those in UPPERCASE
// are defined here:
//
// strlen --
@@ -69,7 +69,7 @@
#include "absl/strings/ascii.h" // for absl::ascii_tolower
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
inline char* memcat(char* dest, size_t destlen, const char* src,
@@ -142,7 +142,7 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
size_t neelen);
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_MEMUTIL_H_
diff --git a/absl/strings/internal/numbers_test_common.h b/absl/strings/internal/numbers_test_common.h
index a511dcf7..32aa0bfa 100644
--- a/absl/strings/internal/numbers_test_common.h
+++ b/absl/strings/internal/numbers_test_common.h
@@ -24,7 +24,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
template <typename IntType>
@@ -65,11 +65,11 @@ struct uint32_test_case {
inline const std::array<uint32_test_case, 27>& strtouint32_test_cases() {
static const std::array<uint32_test_case, 27> test_cases{{
- {"0xffffffff", true, 16, std::numeric_limits<uint32_t>::max()},
+ {"0xffffffff", true, 16, (std::numeric_limits<uint32_t>::max)()},
{"0x34234324", true, 16, 0x34234324},
{"34234324", true, 16, 0x34234324},
{"0", true, 16, 0},
- {" \t\n 0xffffffff", true, 16, std::numeric_limits<uint32_t>::max()},
+ {" \t\n 0xffffffff", true, 16, (std::numeric_limits<uint32_t>::max)()},
{" \f\v 46", true, 10, 46}, // must accept weird whitespace
{" \t\n 72717222", true, 8, 072717222},
{" \t\n 072717222", true, 8, 072717222},
@@ -78,7 +78,7 @@ inline const std::array<uint32_test_case, 27>& strtouint32_test_cases() {
// Base-10 version.
{"34234324", true, 0, 34234324},
- {"4294967295", true, 0, std::numeric_limits<uint32_t>::max()},
+ {"4294967295", true, 0, (std::numeric_limits<uint32_t>::max)()},
{"34234324 \n\t", true, 10, 34234324},
// Unusual base
@@ -97,8 +97,8 @@ inline const std::array<uint32_test_case, 27>& strtouint32_test_cases() {
{" \t\n -123", false, 0, 0},
// Out of bounds.
- {"4294967296", false, 0, std::numeric_limits<uint32_t>::max()},
- {"0x100000000", false, 0, std::numeric_limits<uint32_t>::max()},
+ {"4294967296", false, 0, (std::numeric_limits<uint32_t>::max)()},
+ {"0x100000000", false, 0, (std::numeric_limits<uint32_t>::max)()},
{nullptr, false, 0, 0},
}};
return test_cases;
@@ -120,7 +120,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
{"000", true, 0, 0},
{"0", true, 0, 0},
{" \t\n 0xffffffffffffffff", true, 16,
- std::numeric_limits<uint64_t>::max()},
+ (std::numeric_limits<uint64_t>::max)()},
{"012345670123456701234", true, 8, int64_t{012345670123456701234}},
{"12345670123456701234", true, 8, int64_t{012345670123456701234}},
@@ -131,7 +131,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
{"34234324487834466", true, 0, int64_t{34234324487834466}},
{" \t\n 18446744073709551615", true, 0,
- std::numeric_limits<uint64_t>::max()},
+ (std::numeric_limits<uint64_t>::max)()},
{"34234324487834466 \n\t ", true, 0, int64_t{34234324487834466}},
@@ -157,12 +157,13 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
// Out of bounds.
{"18446744073709551616", false, 10, 0},
{"18446744073709551616", false, 0, 0},
- {"0x10000000000000000", false, 16, std::numeric_limits<uint64_t>::max()},
+ {"0x10000000000000000", false, 16,
+ (std::numeric_limits<uint64_t>::max)()},
{"0X10000000000000000", false, 16,
- std::numeric_limits<uint64_t>::max()}, // 0X versus 0x.
- {"0x10000000000000000", false, 0, std::numeric_limits<uint64_t>::max()},
+ (std::numeric_limits<uint64_t>::max)()}, // 0X versus 0x.
+ {"0x10000000000000000", false, 0, (std::numeric_limits<uint64_t>::max)()},
{"0X10000000000000000", false, 0,
- std::numeric_limits<uint64_t>::max()}, // 0X versus 0x.
+ (std::numeric_limits<uint64_t>::max)()}, // 0X versus 0x.
{"0x1234", true, 16, 0x1234},
@@ -174,7 +175,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_NUMBERS_TEST_COMMON_H_
diff --git a/absl/strings/internal/ostringstream.cc b/absl/strings/internal/ostringstream.cc
index 9fe5b3c5..77f4b0b3 100644
--- a/absl/strings/internal/ostringstream.cc
+++ b/absl/strings/internal/ostringstream.cc
@@ -15,7 +15,7 @@
#include "absl/strings/internal/ostringstream.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
OStringStream::Buf::int_type OStringStream::overflow(int c) {
@@ -32,5 +32,5 @@ std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/ostringstream.h b/absl/strings/internal/ostringstream.h
index 66ae3c29..908e170c 100644
--- a/absl/strings/internal/ostringstream.h
+++ b/absl/strings/internal/ostringstream.h
@@ -23,21 +23,21 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
-// The same as std::ostringstream but appends to a user-specified std::string,
+// The same as std::ostringstream but appends to a user-specified string,
// and is faster. It is ~70% faster to create, ~50% faster to write to, and
-// completely free to extract the result std::string.
+// completely free to extract the result string.
//
-// std::string s;
+// string s;
// OStringStream strm(&s);
// strm << 42 << ' ' << 3.14; // appends to `s`
//
// The stream object doesn't have to be named. Starting from C++11 operator<<
// works with rvalues of std::ostream.
//
-// std::string s;
+// string s;
// OStringStream(&s) << 42 << ' ' << 3.14; // appends to `s`
//
// OStringStream is faster to create than std::ostringstream but it's still
@@ -46,14 +46,14 @@ namespace strings_internal {
//
// Creates unnecessary instances of OStringStream: slow.
//
-// std::string s;
+// string s;
// OStringStream(&s) << 42;
// OStringStream(&s) << ' ';
// OStringStream(&s) << 3.14;
//
// Creates a single instance of OStringStream and reuses it: fast.
//
-// std::string s;
+// string s;
// OStringStream strm(&s);
// strm << 42;
// strm << ' ';
@@ -83,7 +83,7 @@ class OStringStream : private std::basic_streambuf<char>, public std::ostream {
};
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_
diff --git a/absl/strings/internal/pow10_helper.cc b/absl/strings/internal/pow10_helper.cc
new file mode 100644
index 00000000..c7f4875a
--- /dev/null
+++ b/absl/strings/internal/pow10_helper.cc
@@ -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
+//
+// 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.
+
+#include "absl/strings/internal/pow10_helper.h"
+
+#include <cmath>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace strings_internal {
+
+namespace {
+
+// The exact value of 1e23 falls precisely halfway between two representable
+// doubles. Furthermore, the rounding rules we prefer (break ties by rounding
+// to the nearest even) dictate in this case that the number should be rounded
+// down, but this is not completely specified for floating-point literals in
+// C++. (It just says to use the default rounding mode of the standard
+// library.) We ensure the result we want by using a number that has an
+// unambiguous correctly rounded answer.
+constexpr double k1e23 = 9999999999999999e7;
+
+constexpr double kPowersOfTen[] = {
+ 0.0, 1e-323, 1e-322, 1e-321, 1e-320, 1e-319, 1e-318, 1e-317, 1e-316,
+ 1e-315, 1e-314, 1e-313, 1e-312, 1e-311, 1e-310, 1e-309, 1e-308, 1e-307,
+ 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, 1e-298,
+ 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 1e-289,
+ 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, 1e-280,
+ 1e-279, 1e-278, 1e-277, 1e-276, 1e-275, 1e-274, 1e-273, 1e-272, 1e-271,
+ 1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264, 1e-263, 1e-262,
+ 1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254, 1e-253,
+ 1e-252, 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245, 1e-244,
+ 1e-243, 1e-242, 1e-241, 1e-240, 1e-239, 1e-238, 1e-237, 1e-236, 1e-235,
+ 1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228, 1e-227, 1e-226,
+ 1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218, 1e-217,
+ 1e-216, 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209, 1e-208,
+ 1e-207, 1e-206, 1e-205, 1e-204, 1e-203, 1e-202, 1e-201, 1e-200, 1e-199,
+ 1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192, 1e-191, 1e-190,
+ 1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182, 1e-181,
+ 1e-180, 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173, 1e-172,
+ 1e-171, 1e-170, 1e-169, 1e-168, 1e-167, 1e-166, 1e-165, 1e-164, 1e-163,
+ 1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156, 1e-155, 1e-154,
+ 1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146, 1e-145,
+ 1e-144, 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137, 1e-136,
+ 1e-135, 1e-134, 1e-133, 1e-132, 1e-131, 1e-130, 1e-129, 1e-128, 1e-127,
+ 1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120, 1e-119, 1e-118,
+ 1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110, 1e-109,
+ 1e-108, 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101, 1e-100,
+ 1e-99, 1e-98, 1e-97, 1e-96, 1e-95, 1e-94, 1e-93, 1e-92, 1e-91,
+ 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, 1e-83, 1e-82,
+ 1e-81, 1e-80, 1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, 1e-73,
+ 1e-72, 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, 1e-64,
+ 1e-63, 1e-62, 1e-61, 1e-60, 1e-59, 1e-58, 1e-57, 1e-56, 1e-55,
+ 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47, 1e-46,
+ 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40, 1e-39, 1e-38, 1e-37,
+ 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, 1e-28,
+ 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20, 1e-19,
+ 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10,
+ 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
+ 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8,
+ 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17,
+ 1e+18, 1e+19, 1e+20, 1e+21, 1e+22, k1e23, 1e+24, 1e+25, 1e+26,
+ 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35,
+ 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, 1e+41, 1e+42, 1e+43, 1e+44,
+ 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53,
+ 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, 1e+61, 1e+62,
+ 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71,
+ 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
+ 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89,
+ 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98,
+ 1e+99, 1e+100, 1e+101, 1e+102, 1e+103, 1e+104, 1e+105, 1e+106, 1e+107,
+ 1e+108, 1e+109, 1e+110, 1e+111, 1e+112, 1e+113, 1e+114, 1e+115, 1e+116,
+ 1e+117, 1e+118, 1e+119, 1e+120, 1e+121, 1e+122, 1e+123, 1e+124, 1e+125,
+ 1e+126, 1e+127, 1e+128, 1e+129, 1e+130, 1e+131, 1e+132, 1e+133, 1e+134,
+ 1e+135, 1e+136, 1e+137, 1e+138, 1e+139, 1e+140, 1e+141, 1e+142, 1e+143,
+ 1e+144, 1e+145, 1e+146, 1e+147, 1e+148, 1e+149, 1e+150, 1e+151, 1e+152,
+ 1e+153, 1e+154, 1e+155, 1e+156, 1e+157, 1e+158, 1e+159, 1e+160, 1e+161,
+ 1e+162, 1e+163, 1e+164, 1e+165, 1e+166, 1e+167, 1e+168, 1e+169, 1e+170,
+ 1e+171, 1e+172, 1e+173, 1e+174, 1e+175, 1e+176, 1e+177, 1e+178, 1e+179,
+ 1e+180, 1e+181, 1e+182, 1e+183, 1e+184, 1e+185, 1e+186, 1e+187, 1e+188,
+ 1e+189, 1e+190, 1e+191, 1e+192, 1e+193, 1e+194, 1e+195, 1e+196, 1e+197,
+ 1e+198, 1e+199, 1e+200, 1e+201, 1e+202, 1e+203, 1e+204, 1e+205, 1e+206,
+ 1e+207, 1e+208, 1e+209, 1e+210, 1e+211, 1e+212, 1e+213, 1e+214, 1e+215,
+ 1e+216, 1e+217, 1e+218, 1e+219, 1e+220, 1e+221, 1e+222, 1e+223, 1e+224,
+ 1e+225, 1e+226, 1e+227, 1e+228, 1e+229, 1e+230, 1e+231, 1e+232, 1e+233,
+ 1e+234, 1e+235, 1e+236, 1e+237, 1e+238, 1e+239, 1e+240, 1e+241, 1e+242,
+ 1e+243, 1e+244, 1e+245, 1e+246, 1e+247, 1e+248, 1e+249, 1e+250, 1e+251,
+ 1e+252, 1e+253, 1e+254, 1e+255, 1e+256, 1e+257, 1e+258, 1e+259, 1e+260,
+ 1e+261, 1e+262, 1e+263, 1e+264, 1e+265, 1e+266, 1e+267, 1e+268, 1e+269,
+ 1e+270, 1e+271, 1e+272, 1e+273, 1e+274, 1e+275, 1e+276, 1e+277, 1e+278,
+ 1e+279, 1e+280, 1e+281, 1e+282, 1e+283, 1e+284, 1e+285, 1e+286, 1e+287,
+ 1e+288, 1e+289, 1e+290, 1e+291, 1e+292, 1e+293, 1e+294, 1e+295, 1e+296,
+ 1e+297, 1e+298, 1e+299, 1e+300, 1e+301, 1e+302, 1e+303, 1e+304, 1e+305,
+ 1e+306, 1e+307, 1e+308,
+};
+
+} // namespace
+
+double Pow10(int exp) {
+ if (exp < -324) {
+ return 0.0;
+ } else if (exp > 308) {
+ return INFINITY;
+ } else {
+ return kPowersOfTen[exp + 324];
+ }
+}
+
+} // namespace strings_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/pow10_helper.h b/absl/strings/internal/pow10_helper.h
new file mode 100644
index 00000000..750051bd
--- /dev/null
+++ b/absl/strings/internal/pow10_helper.h
@@ -0,0 +1,38 @@
+//
+// 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
+//
+// 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.
+//
+// This test helper library contains a table of powers of 10, to guarantee
+// precise values are computed across the full range of doubles. We can't rely
+// on the pow() function, because not all standard libraries ship a version
+// that is precise.
+#ifndef ABSL_STRINGS_POW10_HELPER_H_
+#define ABSL_STRINGS_POW10_HELPER_H_
+
+#include <vector>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace strings_internal {
+
+// Computes the precise value of 10^exp. (I.e. the nearest representable
+// double to the exact value, rounding to nearest-even in the (single) case of
+// being exactly halfway between.)
+double Pow10(int exp);
+
+} // namespace strings_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_POW10_HELPER_H_
diff --git a/absl/strings/internal/pow10_helper_test.cc b/absl/strings/internal/pow10_helper_test.cc
new file mode 100644
index 00000000..371fe122
--- /dev/null
+++ b/absl/strings/internal/pow10_helper_test.cc
@@ -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
+//
+// 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.
+
+#include "absl/strings/internal/pow10_helper.h"
+
+#include <cmath>
+
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace strings_internal {
+
+namespace {
+
+struct TestCase {
+ int power; // Testing Pow10(power)
+ uint64_t significand; // Raw bits of the expected value
+ int radix; // significand is adjusted by 2^radix
+};
+
+TEST(Pow10HelperTest, Works) {
+ // The logic in pow10_helper.cc is so simple that theoretically we don't even
+ // need a test. However, we're paranoid and believe that there may be
+ // compilers that don't round floating-point literals correctly, even though
+ // it is specified by the standard. We check various edge cases, just to be
+ // sure.
+ constexpr TestCase kTestCases[] = {
+ // Subnormals
+ {-323, 0x2, -1074},
+ {-322, 0x14, -1074},
+ {-321, 0xca, -1074},
+ {-320, 0x7e8, -1074},
+ {-319, 0x4f10, -1074},
+ {-318, 0x316a2, -1074},
+ {-317, 0x1ee257, -1074},
+ {-316, 0x134d761, -1074},
+ {-315, 0xc1069cd, -1074},
+ {-314, 0x78a42205, -1074},
+ {-313, 0x4b6695433, -1074},
+ {-312, 0x2f201d49fb, -1074},
+ {-311, 0x1d74124e3d1, -1074},
+ {-310, 0x12688b70e62b, -1074},
+ {-309, 0xb8157268fdaf, -1074},
+ {-308, 0x730d67819e8d2, -1074},
+ // Values that are very close to rounding the other way.
+ // Comment shows difference of significand from the true value.
+ {-307, 0x11fa182c40c60d, -1072}, // -.4588
+ {-290, 0x18f2b061aea072, -1016}, // .4854
+ {-276, 0x11BA03F5B21000, -969}, // .4709
+ {-259, 0x1899C2F6732210, -913}, // .4830
+ {-252, 0x1D53844EE47DD1, -890}, // -.4743
+ {-227, 0x1E5297287C2F45, -807}, // -.4708
+ {-198, 0x1322E220A5B17E, -710}, // -.4714
+ {-195, 0x12B010D3E1CF56, -700}, // .4928
+ {-192, 0x123FF06EEA847A, -690}, // .4968
+ {-163, 0x1708D0F84D3DE7, -594}, // -.4977
+ {-145, 0x13FAAC3E3FA1F3, -534}, // -.4785
+ {-111, 0x133D4032C2C7F5, -421}, // .4774
+ {-106, 0x1D5B561574765B, -405}, // -.4869
+ {-104, 0x16EF5B40C2FC77, -398}, // -.4741
+ {-88, 0x197683DF2F268D, -345}, // -.4738
+ {-86, 0x13E497065CD61F, -338}, // .4736
+ {-76, 0x17288E1271F513, -305}, // -.4761
+ {-63, 0x1A53FC9631D10D, -262}, // .4929
+ {-30, 0x14484BFEEBC2A0, -152}, // .4758
+ {-21, 0x12E3B40A0E9B4F, -122}, // -.4916
+ {-5, 0x14F8B588E368F1, -69}, // .4829
+ {23, 0x152D02C7E14AF6, 24}, // -.5000 (exactly, round-to-even)
+ {29, 0x1431E0FAE6D721, 44}, // -.4870
+ {34, 0x1ED09BEAD87C03, 60}, // -.4721
+ {70, 0x172EBAD6DDC73D, 180}, // .4733
+ {105, 0x1BE7ABD3781ECA, 296}, // -.4850
+ {126, 0x17A2ECC414A03F, 366}, // -.4999
+ {130, 0x1CDA62055B2D9E, 379}, // .4855
+ {165, 0x115D847AD00087, 496}, // -.4913
+ {172, 0x14B378469B6732, 519}, // .4818
+ {187, 0x1262DFEEBBB0F9, 569}, // -.4805
+ {210, 0x18557F31326BBB, 645}, // -.4992
+ {212, 0x1302CB5E6F642A, 652}, // -.4838
+ {215, 0x1290BA9A38C7D1, 662}, // -.4881
+ {236, 0x1F736F9B3494E9, 731}, // .4707
+ {244, 0x176EC98994F489, 758}, // .4924
+ {250, 0x1658E3AB795204, 778}, // -.4963
+ {252, 0x117571DDF6C814, 785}, // .4873
+ {254, 0x1B4781EAD1989E, 791}, // -.4887
+ {260, 0x1A03FDE214CAF1, 811}, // .4784
+ {284, 0x1585041B2C477F, 891}, // .4798
+ {304, 0x1D2A1BE4048F90, 957}, // -.4987
+ // Out-of-range values
+ {-324, 0x0, 0},
+ {-325, 0x0, 0},
+ {-326, 0x0, 0},
+ {309, 1, 2000},
+ {310, 1, 2000},
+ {311, 1, 2000},
+ };
+ for (const TestCase& test_case : kTestCases) {
+ EXPECT_EQ(Pow10(test_case.power),
+ std::ldexp(test_case.significand, test_case.radix))
+ << absl::StrFormat("Failure for Pow10(%d): %a vs %a", test_case.power,
+ Pow10(test_case.power),
+ std::ldexp(test_case.significand, test_case.radix));
+ }
+}
+
+} // namespace
+} // namespace strings_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h
index b3690332..2951bf84 100644
--- a/absl/strings/internal/resize_uninitialized.h
+++ b/absl/strings/internal/resize_uninitialized.h
@@ -24,7 +24,7 @@
#include "absl/meta/type_traits.h" // for void_t
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// Is a subclass of true_type or false_type, depending on whether or not
@@ -45,8 +45,8 @@ void ResizeUninit(string_type* s, size_t new_size, std::false_type) {
s->resize(new_size);
}
-// Returns true if the std::string implementation supports a resize where
-// the new characters added to the std::string are left untouched.
+// Returns true if the string implementation supports a resize where
+// the new characters added to the string are left untouched.
//
// (A better name might be "STLStringSupportsUninitializedResize", alluding to
// the previous function.)
@@ -58,14 +58,14 @@ inline constexpr bool STLStringSupportsNontrashingResize(string_type*) {
// Like str->resize(new_size), except any new characters added to "*str" as a
// result of resizing may be left uninitialized, rather than being filled with
// '0' bytes. Typically used when code is then going to overwrite the backing
-// store of the std::string with known data. Uses a Google extension to std::string.
+// store of the string with known data. Uses a Google extension to ::string.
template <typename string_type, typename = void>
inline void STLStringResizeUninitialized(string_type* s, size_t new_size) {
ResizeUninit(s, new_size, HasResizeUninitialized<string_type>());
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h
index 7fc56a3c..fed7bf7c 100644
--- a/absl/strings/internal/stl_type_traits.h
+++ b/absl/strings/internal/stl_type_traits.h
@@ -40,7 +40,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
template <typename C, template <typename...> class T>
@@ -243,6 +243,6 @@ struct IsStrictlyBaseOfAndConvertibleToSTLContainer
IsConvertibleToSTLContainer<C>> {};
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
new file mode 100644
index 00000000..e5e1eee5
--- /dev/null
+++ b/absl/strings/internal/str_format/arg.cc
@@ -0,0 +1,379 @@
+//
+// POSIX spec:
+// http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html
+//
+#include "absl/strings/internal/str_format/arg.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/port.h"
+#include "absl/strings/internal/str_format/float_conversion.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+namespace {
+
+const char kDigit[2][32] = { "0123456789abcdef", "0123456789ABCDEF" };
+
+// Reduce *capacity by s.size(), clipped to a 0 minimum.
+void ReducePadding(string_view s, size_t *capacity) {
+ *capacity = Excess(s.size(), *capacity);
+}
+
+// Reduce *capacity by n, clipped to a 0 minimum.
+void ReducePadding(size_t n, size_t *capacity) {
+ *capacity = Excess(n, *capacity);
+}
+
+template <typename T>
+struct MakeUnsigned : std::make_unsigned<T> {};
+template <>
+struct MakeUnsigned<absl::uint128> {
+ using type = absl::uint128;
+};
+
+template <typename T>
+struct IsSigned : std::is_signed<T> {};
+template <>
+struct IsSigned<absl::uint128> : std::false_type {};
+
+class ConvertedIntInfo {
+ public:
+ template <typename T>
+ ConvertedIntInfo(T v, ConversionChar conv) {
+ using Unsigned = typename MakeUnsigned<T>::type;
+ auto u = static_cast<Unsigned>(v);
+ if (IsNeg(v)) {
+ is_neg_ = true;
+ u = Unsigned{} - u;
+ } else {
+ is_neg_ = false;
+ }
+ UnsignedToStringRight(u, conv);
+ }
+
+ string_view digits() const {
+ return {end() - size_, static_cast<size_t>(size_)};
+ }
+ bool is_neg() const { return is_neg_; }
+
+ private:
+ template <typename T, bool IsSigned>
+ struct IsNegImpl {
+ static bool Eval(T v) { return v < 0; }
+ };
+ template <typename T>
+ struct IsNegImpl<T, false> {
+ static bool Eval(T) {
+ return false;
+ }
+ };
+
+ template <typename T>
+ bool IsNeg(T v) {
+ return IsNegImpl<T, IsSigned<T>::value>::Eval(v);
+ }
+
+ template <typename T>
+ void UnsignedToStringRight(T u, ConversionChar conv) {
+ char *p = end();
+ switch (conv.radix()) {
+ default:
+ case 10:
+ for (; u; u /= 10)
+ *--p = static_cast<char>('0' + static_cast<size_t>(u % 10));
+ break;
+ case 8:
+ for (; u; u /= 8)
+ *--p = static_cast<char>('0' + static_cast<size_t>(u % 8));
+ break;
+ case 16: {
+ const char *digits = kDigit[conv.upper() ? 1 : 0];
+ for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)];
+ break;
+ }
+ }
+ size_ = static_cast<int>(end() - p);
+ }
+
+ const char *end() const { return storage_ + sizeof(storage_); }
+ char *end() { return storage_ + sizeof(storage_); }
+
+ bool is_neg_;
+ int size_;
+ // Max size: 128 bit value as octal -> 43 digits
+ char storage_[128 / 3 + 1];
+};
+
+// Note: 'o' conversions do not have a base indicator, it's just that
+// the '#' flag is specified to modify the precision for 'o' conversions.
+string_view BaseIndicator(const ConvertedIntInfo &info,
+ const ConversionSpec conv) {
+ bool alt = conv.flags().alt;
+ int radix = conv.conv().radix();
+ if (conv.conv().id() == ConversionChar::p)
+ alt = true; // always show 0x for %p.
+ // From the POSIX description of '#' flag:
+ // "For x or X conversion specifiers, a non-zero result shall have
+ // 0x (or 0X) prefixed to it."
+ if (alt && radix == 16 && !info.digits().empty()) {
+ if (conv.conv().upper()) return "0X";
+ return "0x";
+ }
+ return {};
+}
+
+string_view SignColumn(bool neg, const ConversionSpec conv) {
+ if (conv.conv().is_signed()) {
+ if (neg) return "-";
+ if (conv.flags().show_pos) return "+";
+ if (conv.flags().sign_col) return " ";
+ }
+ return {};
+}
+
+bool ConvertCharImpl(unsigned char v, const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ size_t fill = 0;
+ if (conv.width() >= 0) fill = conv.width();
+ ReducePadding(1, &fill);
+ if (!conv.flags().left) sink->Append(fill, ' ');
+ sink->Append(1, v);
+ if (conv.flags().left) sink->Append(fill, ' ');
+ return true;
+}
+
+bool ConvertIntImplInner(const ConvertedIntInfo &info,
+ const ConversionSpec conv, FormatSinkImpl *sink) {
+ // Print as a sequence of Substrings:
+ // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
+ size_t fill = 0;
+ if (conv.width() >= 0) fill = conv.width();
+
+ string_view formatted = info.digits();
+ ReducePadding(formatted, &fill);
+
+ string_view sign = SignColumn(info.is_neg(), conv);
+ ReducePadding(sign, &fill);
+
+ string_view base_indicator = BaseIndicator(info, conv);
+ ReducePadding(base_indicator, &fill);
+
+ int precision = conv.precision();
+ bool precision_specified = precision >= 0;
+ if (!precision_specified)
+ precision = 1;
+
+ if (conv.flags().alt && conv.conv().id() == ConversionChar::o) {
+ // From POSIX description of the '#' (alt) flag:
+ // "For o conversion, it increases the precision (if necessary) to
+ // force the first digit of the result to be zero."
+ if (formatted.empty() || *formatted.begin() != '0') {
+ int needed = static_cast<int>(formatted.size()) + 1;
+ precision = std::max(precision, needed);
+ }
+ }
+
+ size_t num_zeroes = Excess(formatted.size(), precision);
+ ReducePadding(num_zeroes, &fill);
+
+ size_t num_left_spaces = !conv.flags().left ? fill : 0;
+ size_t num_right_spaces = conv.flags().left ? fill : 0;
+
+ // From POSIX description of the '0' (zero) flag:
+ // "For d, i, o, u, x, and X conversion specifiers, if a precision
+ // is specified, the '0' flag is ignored."
+ if (!precision_specified && conv.flags().zero) {
+ num_zeroes += num_left_spaces;
+ num_left_spaces = 0;
+ }
+
+ sink->Append(num_left_spaces, ' ');
+ sink->Append(sign);
+ sink->Append(base_indicator);
+ sink->Append(num_zeroes, '0');
+ sink->Append(formatted);
+ sink->Append(num_right_spaces, ' ');
+ return true;
+}
+
+template <typename T>
+bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
+ ConvertedIntInfo info(v, conv.conv());
+ if (conv.flags().basic && conv.conv().id() != ConversionChar::p) {
+ if (info.is_neg()) sink->Append(1, '-');
+ if (info.digits().empty()) {
+ sink->Append(1, '0');
+ } else {
+ sink->Append(info.digits());
+ }
+ return true;
+ }
+ return ConvertIntImplInner(info, conv, sink);
+}
+
+template <typename T>
+bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
+ if (conv.conv().is_float()) {
+ return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
+ }
+ if (conv.conv().id() == ConversionChar::c)
+ return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
+ if (!conv.conv().is_integral())
+ return false;
+ if (!conv.conv().is_signed() && IsSigned<T>::value) {
+ using U = typename MakeUnsigned<T>::type;
+ return FormatConvertImpl(static_cast<U>(v), conv, sink).value;
+ }
+ return ConvertIntImplInner(v, conv, sink);
+}
+
+template <typename T>
+bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
+ return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink);
+}
+
+inline bool ConvertStringArg(string_view v, const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ if (conv.conv().id() != ConversionChar::s)
+ return false;
+ if (conv.flags().basic) {
+ sink->Append(v);
+ return true;
+ }
+ return sink->PutPaddedString(v, conv.width(), conv.precision(),
+ conv.flags().left);
+}
+
+} // namespace
+
+// ==================== Strings ====================
+ConvertResult<Conv::s> FormatConvertImpl(const std::string &v,
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertStringArg(v, conv, sink)};
+}
+
+ConvertResult<Conv::s> FormatConvertImpl(string_view v,
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertStringArg(v, conv, sink)};
+}
+
+ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ if (conv.conv().id() == ConversionChar::p)
+ return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
+ size_t len;
+ if (v == nullptr) {
+ len = 0;
+ } else if (conv.precision() < 0) {
+ len = std::strlen(v);
+ } else {
+ // If precision is set, we look for the null terminator on the valid range.
+ len = std::find(v, v + conv.precision(), '\0') - v;
+ }
+ return {ConvertStringArg(string_view(v, len), conv, sink)};
+}
+
+// ==================== Raw pointers ====================
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ if (conv.conv().id() != ConversionChar::p)
+ return {false};
+ if (!v.value) {
+ sink->Append("(nil)");
+ return {true};
+ }
+ return {ConvertIntImplInner(v.value, conv, sink)};
+}
+
+// ==================== Floats ====================
+FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertFloatArg(v, conv, sink)};
+}
+FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertFloatArg(v, conv, sink)};
+}
+FloatingConvertResult FormatConvertImpl(long double v,
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertFloatArg(v, conv, sink)};
+}
+
+// ==================== Chars ====================
+IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(signed char v,
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned char v,
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+
+// ==================== Ints ====================
+IntegralConvertResult FormatConvertImpl(short v, // NOLINT
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(long v, // NOLINT
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(long long v, // NOLINT
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(absl::uint128 v,
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
+
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
+
+
+} // namespace str_format_internal
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
new file mode 100644
index 00000000..0af4c839
--- /dev/null
+++ b/absl/strings/internal/str_format/arg.h
@@ -0,0 +1,422 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
+
+#include <string.h>
+#include <wchar.h>
+
+#include <cstdio>
+#include <iomanip>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/port.h"
+#include "absl/meta/type_traits.h"
+#include "absl/numeric/int128.h"
+#include "absl/strings/internal/str_format/extension.h"
+#include "absl/strings/string_view.h"
+
+class Cord;
+class CordReader;
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+class FormatCountCapture;
+class FormatSink;
+
+namespace str_format_internal {
+
+template <typename T, typename = void>
+struct HasUserDefinedConvert : std::false_type {};
+
+template <typename T>
+struct HasUserDefinedConvert<
+ T, void_t<decltype(AbslFormatConvert(
+ std::declval<const T&>(), std::declval<ConversionSpec>(),
+ std::declval<FormatSink*>()))>> : std::true_type {};
+template <typename T>
+class StreamedWrapper;
+
+// If 'v' can be converted (in the printf sense) according to 'conv',
+// then convert it, appending to `sink` and return `true`.
+// Otherwise fail and return `false`.
+// Raw pointers.
+struct VoidPtr {
+ VoidPtr() = default;
+ template <typename T,
+ decltype(reinterpret_cast<uintptr_t>(std::declval<T*>())) = 0>
+ VoidPtr(T* ptr) // NOLINT
+ : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {}
+ uintptr_t value;
+};
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+
+// Strings.
+ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v,
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+template <class AbslCord,
+ typename std::enable_if<
+ std::is_same<AbslCord, ::Cord>::value>::type* = nullptr,
+ class AbslCordReader = ::CordReader>
+ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value,
+ ConversionSpec conv,
+ FormatSinkImpl* sink) {
+ if (conv.conv().id() != ConversionChar::s) return {false};
+
+ bool is_left = conv.flags().left;
+ size_t space_remaining = 0;
+
+ int width = conv.width();
+ if (width >= 0) space_remaining = width;
+
+ size_t to_write = value.size();
+
+ int precision = conv.precision();
+ if (precision >= 0)
+ to_write = std::min(to_write, static_cast<size_t>(precision));
+
+ space_remaining = Excess(to_write, space_remaining);
+
+ if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' ');
+
+ string_view piece;
+ for (AbslCordReader reader(value);
+ to_write > 0 && reader.ReadFragment(&piece); to_write -= piece.size()) {
+ if (piece.size() > to_write) piece.remove_suffix(piece.size() - to_write);
+ sink->Append(piece);
+ }
+
+ if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' ');
+ return {true};
+}
+
+using IntegralConvertResult =
+ ConvertResult<Conv::c | Conv::numeric | Conv::star>;
+using FloatingConvertResult = ConvertResult<Conv::floating>;
+
+// Floats.
+FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+FloatingConvertResult FormatConvertImpl(double v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+FloatingConvertResult FormatConvertImpl(long double v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+
+// Chars.
+IntegralConvertResult FormatConvertImpl(char v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(signed char v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned char v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+
+// Ints.
+IntegralConvertResult FormatConvertImpl(short v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(int v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(long v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(long long v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0>
+IntegralConvertResult FormatConvertImpl(T v, ConversionSpec conv,
+ FormatSinkImpl* sink) {
+ return FormatConvertImpl(static_cast<int>(v), conv, sink);
+}
+
+// We provide this function to help the checker, but it is never defined.
+// FormatArgImpl will use the underlying Convert functions instead.
+template <typename T>
+typename std::enable_if<std::is_enum<T>::value &&
+ !HasUserDefinedConvert<T>::value,
+ IntegralConvertResult>::type
+FormatConvertImpl(T v, ConversionSpec conv, FormatSinkImpl* sink);
+
+template <typename T>
+ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
+ ConversionSpec conv,
+ FormatSinkImpl* out) {
+ std::ostringstream oss;
+ oss << v.v_;
+ if (!oss) return {false};
+ return str_format_internal::FormatConvertImpl(oss.str(), conv, out);
+}
+
+// Use templates and dependent types to delay evaluation of the function
+// until after FormatCountCapture is fully defined.
+struct FormatCountCaptureHelper {
+ template <class T = int>
+ static ConvertResult<Conv::n> ConvertHelper(const FormatCountCapture& v,
+ ConversionSpec conv,
+ FormatSinkImpl* sink) {
+ const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v;
+
+ if (conv.conv().id() != str_format_internal::ConversionChar::n)
+ return {false};
+ *v2.p_ = static_cast<int>(sink->size());
+ return {true};
+ }
+};
+
+template <class T = int>
+ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v,
+ ConversionSpec conv,
+ FormatSinkImpl* sink) {
+ return FormatCountCaptureHelper::ConvertHelper(v, conv, sink);
+}
+
+// Helper friend struct to hide implementation details from the public API of
+// FormatArgImpl.
+struct FormatArgImplFriend {
+ template <typename Arg>
+ static bool ToInt(Arg arg, int* out) {
+ // A value initialized ConversionSpec has a `none` conv, which tells the
+ // dispatcher to run the `int` conversion.
+ return arg.dispatcher_(arg.data_, {}, out);
+ }
+
+ template <typename Arg>
+ static bool Convert(Arg arg, str_format_internal::ConversionSpec conv,
+ FormatSinkImpl* out) {
+ return arg.dispatcher_(arg.data_, conv, out);
+ }
+
+ template <typename Arg>
+ static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) {
+ return arg.dispatcher_;
+ }
+};
+
+// A type-erased handle to a format argument.
+class FormatArgImpl {
+ private:
+ enum { kInlinedSpace = 8 };
+
+ using VoidPtr = str_format_internal::VoidPtr;
+
+ union Data {
+ const void* ptr;
+ const volatile void* volatile_ptr;
+ char buf[kInlinedSpace];
+ };
+
+ using Dispatcher = bool (*)(Data, ConversionSpec, void* out);
+
+ template <typename T>
+ struct store_by_value
+ : std::integral_constant<bool, (sizeof(T) <= kInlinedSpace) &&
+ (std::is_integral<T>::value ||
+ std::is_floating_point<T>::value ||
+ std::is_pointer<T>::value ||
+ std::is_same<VoidPtr, T>::value)> {};
+
+ enum StoragePolicy { ByPointer, ByVolatilePointer, ByValue };
+ template <typename T>
+ struct storage_policy
+ : std::integral_constant<StoragePolicy,
+ (std::is_volatile<T>::value
+ ? ByVolatilePointer
+ : (store_by_value<T>::value ? ByValue
+ : ByPointer))> {
+ };
+
+ // To reduce the number of vtables we will decay values before hand.
+ // Anything with a user-defined Convert will get its own vtable.
+ // For everything else:
+ // - Decay char* and char arrays into `const char*`
+ // - Decay any other pointer to `const void*`
+ // - Decay all enums to their underlying type.
+ // - Decay function pointers to void*.
+ template <typename T, typename = void>
+ struct DecayType {
+ static constexpr bool kHasUserDefined =
+ str_format_internal::HasUserDefinedConvert<T>::value;
+ using type = typename std::conditional<
+ !kHasUserDefined && std::is_convertible<T, const char*>::value,
+ const char*,
+ typename std::conditional<!kHasUserDefined &&
+ std::is_convertible<T, VoidPtr>::value,
+ VoidPtr, const T&>::type>::type;
+ };
+ template <typename T>
+ struct DecayType<T,
+ typename std::enable_if<
+ !str_format_internal::HasUserDefinedConvert<T>::value &&
+ std::is_enum<T>::value>::type> {
+ using type = typename std::underlying_type<T>::type;
+ };
+
+ public:
+ template <typename T>
+ explicit FormatArgImpl(const T& value) {
+ using D = typename DecayType<T>::type;
+ static_assert(
+ std::is_same<D, const T&>::value || storage_policy<D>::value == ByValue,
+ "Decayed types must be stored by value");
+ Init(static_cast<D>(value));
+ }
+
+ private:
+ friend struct str_format_internal::FormatArgImplFriend;
+ template <typename T, StoragePolicy = storage_policy<T>::value>
+ struct Manager;
+
+ template <typename T>
+ struct Manager<T, ByPointer> {
+ static Data SetValue(const T& value) {
+ Data data;
+ data.ptr = &value;
+ return data;
+ }
+
+ static const T& Value(Data arg) { return *static_cast<const T*>(arg.ptr); }
+ };
+
+ template <typename T>
+ struct Manager<T, ByVolatilePointer> {
+ static Data SetValue(const T& value) {
+ Data data;
+ data.volatile_ptr = &value;
+ return data;
+ }
+
+ static const T& Value(Data arg) {
+ return *static_cast<const T*>(arg.volatile_ptr);
+ }
+ };
+
+ template <typename T>
+ struct Manager<T, ByValue> {
+ static Data SetValue(const T& value) {
+ Data data;
+ memcpy(data.buf, &value, sizeof(value));
+ return data;
+ }
+
+ static T Value(Data arg) {
+ T value;
+ memcpy(&value, arg.buf, sizeof(T));
+ return value;
+ }
+ };
+
+ template <typename T>
+ void Init(const T& value) {
+ data_ = Manager<T>::SetValue(value);
+ dispatcher_ = &Dispatch<T>;
+ }
+
+ template <typename T>
+ static int ToIntVal(const T& val) {
+ using CommonType = typename std::conditional<std::is_signed<T>::value,
+ int64_t, uint64_t>::type;
+ if (static_cast<CommonType>(val) >
+ static_cast<CommonType>((std::numeric_limits<int>::max)())) {
+ return (std::numeric_limits<int>::max)();
+ } else if (std::is_signed<T>::value &&
+ static_cast<CommonType>(val) <
+ static_cast<CommonType>((std::numeric_limits<int>::min)())) {
+ return (std::numeric_limits<int>::min)();
+ }
+ return static_cast<int>(val);
+ }
+
+ template <typename T>
+ static bool ToInt(Data arg, int* out, std::true_type /* is_integral */,
+ std::false_type) {
+ *out = ToIntVal(Manager<T>::Value(arg));
+ return true;
+ }
+
+ template <typename T>
+ static bool ToInt(Data arg, int* out, std::false_type,
+ std::true_type /* is_enum */) {
+ *out = ToIntVal(static_cast<typename std::underlying_type<T>::type>(
+ Manager<T>::Value(arg)));
+ return true;
+ }
+
+ template <typename T>
+ static bool ToInt(Data, int*, std::false_type, std::false_type) {
+ return false;
+ }
+
+ template <typename T>
+ static bool Dispatch(Data arg, ConversionSpec spec, void* out) {
+ // A `none` conv indicates that we want the `int` conversion.
+ if (ABSL_PREDICT_FALSE(spec.conv().id() == ConversionChar::none)) {
+ return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(),
+ std::is_enum<T>());
+ }
+
+ return str_format_internal::FormatConvertImpl(
+ Manager<T>::Value(arg), spec, static_cast<FormatSinkImpl*>(out))
+ .value;
+ }
+
+ Data data_;
+ Dispatcher dispatcher_;
+};
+
+#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \
+ E template bool FormatArgImpl::Dispatch<T>(Data, ConversionSpec, void*)
+
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(char, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(signed char, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned char, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(short, __VA_ARGS__); /* NOLINT */ \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned short, /* NOLINT */ \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned int, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long, __VA_ARGS__); /* NOLINT */ \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long, /* NOLINT */ \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long long, /* NOLINT */ \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */ \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__)
+
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc
new file mode 100644
index 00000000..9cb9559c
--- /dev/null
+++ b/absl/strings/internal/str_format/arg_test.cc
@@ -0,0 +1,113 @@
+// 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
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+#include "absl/strings/internal/str_format/arg.h"
+
+#include <ostream>
+#include <string>
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+namespace {
+
+class FormatArgImplTest : public ::testing::Test {
+ public:
+ enum Color { kRed, kGreen, kBlue };
+
+ static const char *hi() { return "hi"; }
+};
+
+TEST_F(FormatArgImplTest, ToInt) {
+ int out = 0;
+ EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out));
+ EXPECT_EQ(1, out);
+ EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(-1), &out));
+ EXPECT_EQ(-1, out);
+ EXPECT_TRUE(
+ FormatArgImplFriend::ToInt(FormatArgImpl(static_cast<char>(64)), &out));
+ EXPECT_EQ(64, out);
+ EXPECT_TRUE(FormatArgImplFriend::ToInt(
+ FormatArgImpl(static_cast<unsigned long long>(123456)), &out)); // NOLINT
+ EXPECT_EQ(123456, out);
+ EXPECT_TRUE(FormatArgImplFriend::ToInt(
+ FormatArgImpl(static_cast<unsigned long long>( // NOLINT
+ std::numeric_limits<int>::max()) +
+ 1),
+ &out));
+ EXPECT_EQ(std::numeric_limits<int>::max(), out);
+ EXPECT_TRUE(FormatArgImplFriend::ToInt(
+ FormatArgImpl(static_cast<long long>( // NOLINT
+ std::numeric_limits<int>::min()) -
+ 10),
+ &out));
+ EXPECT_EQ(std::numeric_limits<int>::min(), out);
+ EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(false), &out));
+ EXPECT_EQ(0, out);
+ EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(true), &out));
+ EXPECT_EQ(1, out);
+ EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(2.2), &out));
+ EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(3.2f), &out));
+ EXPECT_FALSE(FormatArgImplFriend::ToInt(
+ FormatArgImpl(static_cast<int *>(nullptr)), &out));
+ EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out));
+ EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out));
+ EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out));
+ EXPECT_EQ(2, out);
+}
+
+extern const char kMyArray[];
+
+TEST_F(FormatArgImplTest, CharArraysDecayToCharPtr) {
+ const char* a = "";
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("")));
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("A")));
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("ABC")));
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyArray)));
+}
+
+TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) {
+ auto expected = FormatArgImplFriend::GetVTablePtrForTest(
+ FormatArgImpl(static_cast<void *>(nullptr)));
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(
+ FormatArgImpl(static_cast<int *>(nullptr))),
+ expected);
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(
+ FormatArgImpl(static_cast<volatile int *>(nullptr))),
+ expected);
+
+ auto p = static_cast<void (*)()>([] {});
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(p)),
+ expected);
+}
+
+TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) {
+ std::string s;
+ FormatSinkImpl sink(&s);
+ ConversionSpec conv;
+ conv.set_conv(ConversionChar::FromChar('s'));
+ conv.set_flags(Flags());
+ conv.set_width(-1);
+ conv.set_precision(-1);
+ EXPECT_TRUE(
+ FormatArgImplFriend::Convert(FormatArgImpl(kMyArray), conv, &sink));
+ sink.Flush();
+ EXPECT_EQ("ABCDE", s);
+}
+const char kMyArray[] = "ABCDE";
+
+} // namespace
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc
new file mode 100644
index 00000000..5cf026b6
--- /dev/null
+++ b/absl/strings/internal/str_format/bind.cc
@@ -0,0 +1,231 @@
+#include "absl/strings/internal/str_format/bind.h"
+
+#include <cerrno>
+#include <limits>
+#include <sstream>
+#include <string>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+namespace {
+
+inline bool BindFromPosition(int position, int* value,
+ absl::Span<const FormatArgImpl> pack) {
+ assert(position > 0);
+ if (static_cast<size_t>(position) > pack.size()) {
+ return false;
+ }
+ // -1 because positions are 1-based
+ return FormatArgImplFriend::ToInt(pack[position - 1], value);
+}
+
+class ArgContext {
+ public:
+ explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {}
+
+ // Fill 'bound' with the results of applying the context's argument pack
+ // to the specified 'props'. We synthesize a BoundConversion by
+ // lining up a UnboundConversion with a user argument. We also
+ // resolve any '*' specifiers for width and precision, so after
+ // this call, 'bound' has all the information it needs to be formatted.
+ // Returns false on failure.
+ bool Bind(const UnboundConversion *props, BoundConversion *bound);
+
+ private:
+ absl::Span<const FormatArgImpl> pack_;
+};
+
+inline bool ArgContext::Bind(const UnboundConversion* unbound,
+ BoundConversion* bound) {
+ const FormatArgImpl* arg = nullptr;
+ int arg_position = unbound->arg_position;
+ if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
+ arg = &pack_[arg_position - 1]; // 1-based
+
+ if (!unbound->flags.basic) {
+ int width = unbound->width.value();
+ bool force_left = false;
+ if (unbound->width.is_from_arg()) {
+ if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_))
+ return false;
+ if (width < 0) {
+ // "A negative field width is taken as a '-' flag followed by a
+ // positive field width."
+ force_left = true;
+ width = -width;
+ }
+ }
+
+ int precision = unbound->precision.value();
+ if (unbound->precision.is_from_arg()) {
+ if (!BindFromPosition(unbound->precision.get_from_arg(), &precision,
+ pack_))
+ return false;
+ }
+
+ bound->set_width(width);
+ bound->set_precision(precision);
+ bound->set_flags(unbound->flags);
+ if (force_left)
+ bound->set_left(true);
+ } else {
+ bound->set_flags(unbound->flags);
+ bound->set_width(-1);
+ bound->set_precision(-1);
+ }
+
+ bound->set_length_mod(unbound->length_mod);
+ bound->set_conv(unbound->conv);
+ bound->set_arg(arg);
+ return true;
+}
+
+template <typename Converter>
+class ConverterConsumer {
+ public:
+ ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack)
+ : converter_(converter), arg_context_(pack) {}
+
+ bool Append(string_view s) {
+ converter_.Append(s);
+ return true;
+ }
+ bool ConvertOne(const UnboundConversion& conv, string_view conv_string) {
+ BoundConversion bound;
+ if (!arg_context_.Bind(&conv, &bound)) return false;
+ return converter_.ConvertOne(bound, conv_string);
+ }
+
+ private:
+ Converter converter_;
+ ArgContext arg_context_;
+};
+
+template <typename Converter>
+bool ConvertAll(const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args, Converter converter) {
+ if (format.has_parsed_conversion()) {
+ return format.parsed_conversion()->ProcessFormat(
+ ConverterConsumer<Converter>(converter, args));
+ } else {
+ return ParseFormatString(format.str(),
+ ConverterConsumer<Converter>(converter, args));
+ }
+}
+
+class DefaultConverter {
+ public:
+ explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {}
+
+ void Append(string_view s) const { sink_->Append(s); }
+
+ bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
+ return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_);
+ }
+
+ private:
+ FormatSinkImpl* sink_;
+};
+
+class SummarizingConverter {
+ public:
+ explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {}
+
+ void Append(string_view s) const { sink_->Append(s); }
+
+ bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
+ UntypedFormatSpecImpl spec("%d");
+
+ std::ostringstream ss;
+ ss << "{" << Streamable(spec, {*bound.arg()}) << ":" << bound.flags();
+ if (bound.width() >= 0) ss << bound.width();
+ if (bound.precision() >= 0) ss << "." << bound.precision();
+ ss << bound.length_mod() << bound.conv() << "}";
+ Append(ss.str());
+ return true;
+ }
+
+ private:
+ FormatSinkImpl* sink_;
+};
+
+} // namespace
+
+bool BindWithPack(const UnboundConversion* props,
+ absl::Span<const FormatArgImpl> pack,
+ BoundConversion* bound) {
+ return ArgContext(pack).Bind(props, bound);
+}
+
+std::string Summarize(const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args) {
+ typedef SummarizingConverter Converter;
+ std::string out;
+ {
+ // inner block to destroy sink before returning out. It ensures a last
+ // flush.
+ FormatSinkImpl sink(&out);
+ if (!ConvertAll(format, args, Converter(&sink))) {
+ return "";
+ }
+ }
+ return out;
+}
+
+bool FormatUntyped(FormatRawSinkImpl raw_sink,
+ const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args) {
+ FormatSinkImpl sink(raw_sink);
+ using Converter = DefaultConverter;
+ return ConvertAll(format, args, Converter(&sink));
+}
+
+std::ostream& Streamable::Print(std::ostream& os) const {
+ if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit);
+ return os;
+}
+
+std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args) {
+ size_t orig = out->size();
+ if (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) {
+ out->erase(orig);
+ }
+ return *out;
+}
+
+int FprintF(std::FILE* output, const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args) {
+ FILERawSink sink(output);
+ if (!FormatUntyped(&sink, format, args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sink.error()) {
+ errno = sink.error();
+ return -1;
+ }
+ if (sink.count() > std::numeric_limits<int>::max()) {
+ errno = EFBIG;
+ return -1;
+ }
+ return static_cast<int>(sink.count());
+}
+
+int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args) {
+ BufferRawSink sink(output, size ? size - 1 : 0);
+ if (!FormatUntyped(&sink, format, args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t total = sink.total_written();
+ if (size) output[std::min(total, size - 1)] = 0;
+ return static_cast<int>(total);
+}
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
new file mode 100644
index 00000000..df5562f6
--- /dev/null
+++ b/absl/strings/internal/str_format/bind.h
@@ -0,0 +1,199 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
+
+#include <array>
+#include <cstdio>
+#include <sstream>
+#include <string>
+
+#include "absl/base/port.h"
+#include "absl/container/inlined_vector.h"
+#include "absl/strings/internal/str_format/arg.h"
+#include "absl/strings/internal/str_format/checker.h"
+#include "absl/strings/internal/str_format/parser.h"
+#include "absl/types/span.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+class UntypedFormatSpec;
+
+namespace str_format_internal {
+
+class BoundConversion : public ConversionSpec {
+ public:
+ const FormatArgImpl* arg() const { return arg_; }
+ void set_arg(const FormatArgImpl* a) { arg_ = a; }
+
+ private:
+ const FormatArgImpl* arg_;
+};
+
+// This is the type-erased class that the implementation uses.
+class UntypedFormatSpecImpl {
+ public:
+ UntypedFormatSpecImpl() = delete;
+
+ explicit UntypedFormatSpecImpl(string_view s)
+ : data_(s.data()), size_(s.size()) {}
+ explicit UntypedFormatSpecImpl(
+ const str_format_internal::ParsedFormatBase* pc)
+ : data_(pc), size_(~size_t{}) {}
+
+ bool has_parsed_conversion() const { return size_ == ~size_t{}; }
+
+ string_view str() const {
+ assert(!has_parsed_conversion());
+ return string_view(static_cast<const char*>(data_), size_);
+ }
+ const str_format_internal::ParsedFormatBase* parsed_conversion() const {
+ assert(has_parsed_conversion());
+ return static_cast<const str_format_internal::ParsedFormatBase*>(data_);
+ }
+
+ template <typename T>
+ static const UntypedFormatSpecImpl& Extract(const T& s) {
+ return s.spec_;
+ }
+
+ private:
+ const void* data_;
+ size_t size_;
+};
+
+template <typename T, typename...>
+struct MakeDependent {
+ using type = T;
+};
+
+// Implicitly convertible from `const char*`, `string_view`, and the
+// `ExtendedParsedFormat` type. This abstraction allows all format functions to
+// operate on any without providing too many overloads.
+template <typename... Args>
+class FormatSpecTemplate
+ : public MakeDependent<UntypedFormatSpec, Args...>::type {
+ using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type;
+
+ public:
+#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+ // Honeypot overload for when the std::string is not constexpr.
+ // We use the 'unavailable' attribute to give a better compiler error than
+ // just 'method is deleted'.
+ FormatSpecTemplate(...) // NOLINT
+ __attribute__((unavailable("Format std::string is not constexpr.")));
+
+ // Honeypot overload for when the format is constexpr and invalid.
+ // We use the 'unavailable' attribute to give a better compiler error than
+ // just 'method is deleted'.
+ // To avoid checking the format twice, we just check that the format is
+ // constexpr. If is it valid, then the overload below will kick in.
+ // We add the template here to make this overload have lower priority.
+ template <typename = void>
+ FormatSpecTemplate(const char* s) // NOLINT
+ __attribute__((
+ enable_if(str_format_internal::EnsureConstexpr(s), "constexpr trap"),
+ unavailable(
+ "Format specified does not match the arguments passed.")));
+
+ template <typename T = void>
+ FormatSpecTemplate(string_view s) // NOLINT
+ __attribute__((enable_if(str_format_internal::EnsureConstexpr(s),
+ "constexpr trap"))) {
+ static_assert(sizeof(T*) == 0,
+ "Format specified does not match the arguments passed.");
+ }
+
+ // Good format overload.
+ FormatSpecTemplate(const char* s) // NOLINT
+ __attribute__((enable_if(ValidFormatImpl<ArgumentToConv<Args>()...>(s),
+ "bad format trap")))
+ : Base(s) {}
+
+ FormatSpecTemplate(string_view s) // NOLINT
+ __attribute__((enable_if(ValidFormatImpl<ArgumentToConv<Args>()...>(s),
+ "bad format trap")))
+ : Base(s) {}
+
+#else // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+ FormatSpecTemplate(const char* s) : Base(s) {} // NOLINT
+ FormatSpecTemplate(string_view s) : Base(s) {} // NOLINT
+
+#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+ template <Conv... C, typename = typename std::enable_if<
+ sizeof...(C) == sizeof...(Args) &&
+ AllOf(Contains(ArgumentToConv<Args>(),
+ C)...)>::type>
+ FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT
+ : Base(&pc) {}
+};
+
+template <typename... Args>
+struct FormatSpecDeductionBarrier {
+ using type = FormatSpecTemplate<Args...>;
+};
+
+class Streamable {
+ public:
+ Streamable(const UntypedFormatSpecImpl& format,
+ absl::Span<const FormatArgImpl> args)
+ : format_(format), args_(args.begin(), args.end()) {}
+
+ std::ostream& Print(std::ostream& os) const;
+
+ friend std::ostream& operator<<(std::ostream& os, const Streamable& l) {
+ return l.Print(os);
+ }
+
+ private:
+ const UntypedFormatSpecImpl& format_;
+ absl::InlinedVector<FormatArgImpl, 4> args_;
+};
+
+// for testing
+std::string Summarize(UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+bool BindWithPack(const UnboundConversion* props,
+ absl::Span<const FormatArgImpl> pack, BoundConversion* bound);
+
+bool FormatUntyped(FormatRawSinkImpl raw_sink,
+ UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+
+std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+
+inline std::string FormatPack(const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args) {
+ std::string out;
+ AppendPack(&out, format, args);
+ return out;
+}
+
+int FprintF(std::FILE* output, UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+int SnprintF(char* output, size_t size, UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+
+// Returned by Streamed(v). Converts via '%s' to the string created
+// by std::ostream << v.
+template <typename T>
+class StreamedWrapper {
+ public:
+ explicit StreamedWrapper(const T& v) : v_(v) { }
+
+ private:
+ template <typename S>
+ friend ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<S>& v,
+ ConversionSpec conv,
+ FormatSinkImpl* out);
+ const T& v_;
+};
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
diff --git a/absl/strings/internal/str_format/bind_test.cc b/absl/strings/internal/str_format/bind_test.cc
new file mode 100644
index 00000000..58d9e072
--- /dev/null
+++ b/absl/strings/internal/str_format/bind_test.cc
@@ -0,0 +1,133 @@
+#include "absl/strings/internal/str_format/bind.h"
+
+#include <string.h>
+
+#include "gtest/gtest.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+namespace {
+
+template <typename T, size_t N>
+size_t ArraySize(T (&)[N]) {
+ return N;
+}
+
+class FormatBindTest : public ::testing::Test {
+ public:
+ bool Extract(const char *s, UnboundConversion *props, int *next) const {
+ absl::string_view src = s;
+ return ConsumeUnboundConversion(&src, props, next) && src.empty();
+ }
+};
+
+TEST_F(FormatBindTest, BindSingle) {
+ struct Expectation {
+ int line;
+ const char *fmt;
+ int ok_phases;
+ const FormatArgImpl *arg;
+ int width;
+ int precision;
+ int next_arg;
+ };
+ const int no = -1;
+ const int ia[] = { 10, 20, 30, 40};
+ const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]),
+ FormatArgImpl(ia[2]), FormatArgImpl(ia[3])};
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+ const Expectation kExpect[] = {
+ {__LINE__, "d", 2, &args[0], no, no, 2},
+ {__LINE__, "4d", 2, &args[0], 4, no, 2},
+ {__LINE__, ".5d", 2, &args[0], no, 5, 2},
+ {__LINE__, "4.5d", 2, &args[0], 4, 5, 2},
+ {__LINE__, "*d", 2, &args[1], 10, no, 3},
+ {__LINE__, ".*d", 2, &args[1], no, 10, 3},
+ {__LINE__, "*.*d", 2, &args[2], 10, 20, 4},
+ {__LINE__, "1$d", 2, &args[0], no, no, 0},
+ {__LINE__, "2$d", 2, &args[1], no, no, 0},
+ {__LINE__, "3$d", 2, &args[2], no, no, 0},
+ {__LINE__, "4$d", 2, &args[3], no, no, 0},
+ {__LINE__, "2$*1$d", 2, &args[1], 10, no, 0},
+ {__LINE__, "2$*2$d", 2, &args[1], 20, no, 0},
+ {__LINE__, "2$*3$d", 2, &args[1], 30, no, 0},
+ {__LINE__, "2$.*1$d", 2, &args[1], no, 10, 0},
+ {__LINE__, "2$.*2$d", 2, &args[1], no, 20, 0},
+ {__LINE__, "2$.*3$d", 2, &args[1], no, 30, 0},
+ {__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0},
+ {__LINE__, "2$*2$.*2$d", 2, &args[1], 20, 20, 0},
+ {__LINE__, "2$*1$.*3$d", 2, &args[1], 10, 30, 0},
+ {__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0},
+ {__LINE__, "1$*d", 0}, // indexed, then positional
+ {__LINE__, "*2$d", 0}, // positional, then indexed
+ {__LINE__, "6$d", 1}, // arg position out of bounds
+ {__LINE__, "1$6$d", 0}, // width position incorrectly specified
+ {__LINE__, "1$.6$d", 0}, // precision position incorrectly specified
+ {__LINE__, "1$*6$d", 1}, // width position out of bounds
+ {__LINE__, "1$.*6$d", 1}, // precision position out of bounds
+ };
+#pragma GCC diagnostic pop
+ for (const Expectation &e : kExpect) {
+ SCOPED_TRACE(e.line);
+ SCOPED_TRACE(e.fmt);
+ UnboundConversion props;
+ BoundConversion bound;
+ int ok_phases = 0;
+ int next = 0;
+ if (Extract(e.fmt, &props, &next)) {
+ ++ok_phases;
+ if (BindWithPack(&props, args, &bound)) {
+ ++ok_phases;
+ }
+ }
+ EXPECT_EQ(e.ok_phases, ok_phases);
+ if (e.ok_phases < 2) continue;
+ if (e.arg != nullptr) {
+ EXPECT_EQ(e.arg, bound.arg());
+ }
+ EXPECT_EQ(e.width, bound.width());
+ EXPECT_EQ(e.precision, bound.precision());
+ }
+}
+
+TEST_F(FormatBindTest, FormatPack) {
+ struct Expectation {
+ int line;
+ const char *fmt;
+ const char *summary;
+ };
+ const int ia[] = { 10, 20, 30, 40, -10 };
+ const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]),
+ FormatArgImpl(ia[2]), FormatArgImpl(ia[3]),
+ FormatArgImpl(ia[4])};
+ const Expectation kExpect[] = {
+ {__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"},
+ {__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"},
+ {__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"},
+ {__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"},
+ {__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"},
+ {__LINE__, "a%.*fb", "a{20:.10f}b"},
+ {__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"},
+ {__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"},
+ {__LINE__, "a%04ldb", "a{10:04ld}b"},
+ {__LINE__, "a%-#04lldb", "a{10:-#04lld}b"},
+ {__LINE__, "a%1$*5$db", "a{10:-10d}b"},
+ {__LINE__, "a%1$.*5$db", "a{10:d}b"},
+ };
+ for (const Expectation &e : kExpect) {
+ absl::string_view fmt = e.fmt;
+ SCOPED_TRACE(e.line);
+ SCOPED_TRACE(e.fmt);
+ UntypedFormatSpecImpl format(fmt);
+ EXPECT_EQ(e.summary,
+ str_format_internal::Summarize(format, absl::MakeSpan(args)))
+ << "line:" << e.line;
+ }
+}
+
+} // namespace
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h
new file mode 100644
index 00000000..d0191968
--- /dev/null
+++ b/absl/strings/internal/str_format/checker.h
@@ -0,0 +1,327 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
+
+#include "absl/strings/internal/str_format/arg.h"
+#include "absl/strings/internal/str_format/extension.h"
+
+// Compile time check support for entry points.
+
+#ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+#if defined(__clang__) && !defined(__native_client__)
+#if __has_attribute(enable_if)
+#define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
+#endif // __has_attribute(enable_if)
+#endif // defined(__clang__) && !defined(__native_client__)
+#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+constexpr bool AllOf() { return true; }
+
+template <typename... T>
+constexpr bool AllOf(bool b, T... t) {
+ return b && AllOf(t...);
+}
+
+template <typename Arg>
+constexpr Conv ArgumentToConv() {
+ return decltype(str_format_internal::FormatConvertImpl(
+ std::declval<const Arg&>(), std::declval<const ConversionSpec&>(),
+ std::declval<FormatSinkImpl*>()))::kConv;
+}
+
+#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+constexpr bool ContainsChar(const char* chars, char c) {
+ return *chars == c || (*chars && ContainsChar(chars + 1, c));
+}
+
+// A constexpr compatible list of Convs.
+struct ConvList {
+ const Conv* array;
+ int count;
+
+ // We do the bound check here to avoid having to do it on the callers.
+ // Returning an empty Conv has the same effect as short circuiting because it
+ // will never match any conversion.
+ constexpr Conv operator[](int i) const {
+ return i < count ? array[i] : Conv{};
+ }
+
+ constexpr ConvList without_front() const {
+ return count != 0 ? ConvList{array + 1, count - 1} : *this;
+ }
+};
+
+template <size_t count>
+struct ConvListT {
+ // Make sure the array has size > 0.
+ Conv list[count ? count : 1];
+};
+
+constexpr char GetChar(string_view str, size_t index) {
+ return index < str.size() ? str[index] : char{};
+}
+
+constexpr string_view ConsumeFront(string_view str, size_t len = 1) {
+ return len <= str.size() ? string_view(str.data() + len, str.size() - len)
+ : string_view();
+}
+
+constexpr string_view ConsumeAnyOf(string_view format, const char* chars) {
+ return ContainsChar(chars, GetChar(format, 0))
+ ? ConsumeAnyOf(ConsumeFront(format), chars)
+ : format;
+}
+
+constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
+
+// Helper class for the ParseDigits function.
+// It encapsulates the two return values we need there.
+struct Integer {
+ string_view format;
+ int value;
+
+ // If the next character is a '$', consume it.
+ // Otherwise, make `this` an invalid positional argument.
+ constexpr Integer ConsumePositionalDollar() const {
+ return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value}
+ : Integer{format, 0};
+ }
+};
+
+constexpr Integer ParseDigits(string_view format, int value = 0) {
+ return IsDigit(GetChar(format, 0))
+ ? ParseDigits(ConsumeFront(format),
+ 10 * value + GetChar(format, 0) - '0')
+ : Integer{format, value};
+}
+
+// Parse digits for a positional argument.
+// The parsing also consumes the '$'.
+constexpr Integer ParsePositional(string_view format) {
+ return ParseDigits(format).ConsumePositionalDollar();
+}
+
+// Parses a single conversion specifier.
+// See ConvParser::Run() for post conditions.
+class ConvParser {
+ constexpr ConvParser SetFormat(string_view format) const {
+ return ConvParser(format, args_, error_, arg_position_, is_positional_);
+ }
+
+ constexpr ConvParser SetArgs(ConvList args) const {
+ return ConvParser(format_, args, error_, arg_position_, is_positional_);
+ }
+
+ constexpr ConvParser SetError(bool error) const {
+ return ConvParser(format_, args_, error_ || error, arg_position_,
+ is_positional_);
+ }
+
+ constexpr ConvParser SetArgPosition(int arg_position) const {
+ return ConvParser(format_, args_, error_, arg_position, is_positional_);
+ }
+
+ // Consumes the next arg and verifies that it matches `conv`.
+ // `error_` is set if there is no next arg or if it doesn't match `conv`.
+ constexpr ConvParser ConsumeNextArg(char conv) const {
+ return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
+ }
+
+ // Verify that positional argument `i.value` matches `conv`.
+ // `error_` is set if `i.value` is not a valid argument or if it doesn't
+ // match.
+ constexpr ConvParser VerifyPositional(Integer i, char conv) const {
+ return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
+ }
+
+ // Parse the position of the arg and store it in `arg_position_`.
+ constexpr ConvParser ParseArgPosition(Integer arg) const {
+ return SetFormat(arg.format).SetArgPosition(arg.value);
+ }
+
+ // Consume the flags.
+ constexpr ConvParser ParseFlags() const {
+ return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
+ }
+
+ // Consume the width.
+ // If it is '*', we verify that it matches `args_`. `error_` is set if it
+ // doesn't match.
+ constexpr ConvParser ParseWidth() const {
+ return IsDigit(GetChar(format_, 0))
+ ? SetFormat(ParseDigits(format_).format)
+ : GetChar(format_, 0) == '*'
+ ? is_positional_
+ ? VerifyPositional(
+ ParsePositional(ConsumeFront(format_)), '*')
+ : SetFormat(ConsumeFront(format_))
+ .ConsumeNextArg('*')
+ : *this;
+ }
+
+ // Consume the precision.
+ // If it is '*', we verify that it matches `args_`. `error_` is set if it
+ // doesn't match.
+ constexpr ConvParser ParsePrecision() const {
+ return GetChar(format_, 0) != '.'
+ ? *this
+ : GetChar(format_, 1) == '*'
+ ? is_positional_
+ ? VerifyPositional(
+ ParsePositional(ConsumeFront(format_, 2)), '*')
+ : SetFormat(ConsumeFront(format_, 2))
+ .ConsumeNextArg('*')
+ : SetFormat(ParseDigits(ConsumeFront(format_)).format);
+ }
+
+ // Consume the length characters.
+ constexpr ConvParser ParseLength() const {
+ return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
+ }
+
+ // Consume the conversion character and verify that it matches `args_`.
+ // `error_` is set if it doesn't match.
+ constexpr ConvParser ParseConversion() const {
+ return is_positional_
+ ? VerifyPositional({ConsumeFront(format_), arg_position_},
+ GetChar(format_, 0))
+ : ConsumeNextArg(GetChar(format_, 0))
+ .SetFormat(ConsumeFront(format_));
+ }
+
+ constexpr ConvParser(string_view format, ConvList args, bool error,
+ int arg_position, bool is_positional)
+ : format_(format),
+ args_(args),
+ error_(error),
+ arg_position_(arg_position),
+ is_positional_(is_positional) {}
+
+ public:
+ constexpr ConvParser(string_view format, ConvList args, bool is_positional)
+ : format_(format),
+ args_(args),
+ error_(false),
+ arg_position_(0),
+ is_positional_(is_positional) {}
+
+ // Consume the whole conversion specifier.
+ // `format()` will be set to the character after the conversion character.
+ // `error()` will be set if any of the arguments do not match.
+ constexpr ConvParser Run() const {
+ return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this)
+ .ParseFlags()
+ .ParseWidth()
+ .ParsePrecision()
+ .ParseLength()
+ .ParseConversion();
+ }
+
+ constexpr string_view format() const { return format_; }
+ constexpr ConvList args() const { return args_; }
+ constexpr bool error() const { return error_; }
+ constexpr bool is_positional() const { return is_positional_; }
+
+ private:
+ string_view format_;
+ // Current list of arguments. If we are not in positional mode we will consume
+ // from the front.
+ ConvList args_;
+ bool error_;
+ // Holds the argument position of the conversion character, if we are in
+ // positional mode. Otherwise, it is unspecified.
+ int arg_position_;
+ // Whether we are in positional mode.
+ // It changes the behavior of '*' and where to find the converted argument.
+ bool is_positional_;
+};
+
+// Parses a whole format expression.
+// See FormatParser::Run().
+class FormatParser {
+ static constexpr bool FoundPercent(string_view format) {
+ return format.empty() ||
+ (GetChar(format, 0) == '%' && GetChar(format, 1) != '%');
+ }
+
+ // We use an inner function to increase the recursion limit.
+ // The inner function consumes up to `limit` characters on every run.
+ // This increases the limit from 512 to ~512*limit.
+ static constexpr string_view ConsumeNonPercentInner(string_view format,
+ int limit = 20) {
+ return FoundPercent(format) || !limit
+ ? format
+ : ConsumeNonPercentInner(
+ ConsumeFront(format, GetChar(format, 0) == '%' &&
+ GetChar(format, 1) == '%'
+ ? 2
+ : 1),
+ limit - 1);
+ }
+
+ // Consume characters until the next conversion spec %.
+ // It skips %%.
+ static constexpr string_view ConsumeNonPercent(string_view format) {
+ return FoundPercent(format)
+ ? format
+ : ConsumeNonPercent(ConsumeNonPercentInner(format));
+ }
+
+ static constexpr bool IsPositional(string_view format) {
+ return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format))
+ : GetChar(format, 0) == '$';
+ }
+
+ constexpr bool RunImpl(bool is_positional) const {
+ // In non-positional mode we require all arguments to be consumed.
+ // In positional mode just reaching the end of the format without errors is
+ // enough.
+ return (format_.empty() && (is_positional || args_.count == 0)) ||
+ (!format_.empty() &&
+ ValidateArg(
+ ConvParser(ConsumeFront(format_), args_, is_positional).Run()));
+ }
+
+ constexpr bool ValidateArg(ConvParser conv) const {
+ return !conv.error() && FormatParser(conv.format(), conv.args())
+ .RunImpl(conv.is_positional());
+ }
+
+ public:
+ constexpr FormatParser(string_view format, ConvList args)
+ : format_(ConsumeNonPercent(format)), args_(args) {}
+
+ // Runs the parser for `format` and `args`.
+ // It verifies that the format is valid and that all conversion specifiers
+ // match the arguments passed.
+ // In non-positional mode it also verfies that all arguments are consumed.
+ constexpr bool Run() const {
+ return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
+ }
+
+ private:
+ string_view format_;
+ // Current list of arguments.
+ // If we are not in positional mode we will consume from the front and will
+ // have to be empty in the end.
+ ConvList args_;
+};
+
+template <Conv... C>
+constexpr bool ValidFormatImpl(string_view format) {
+ return FormatParser(format,
+ {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
+ .Run();
+}
+
+#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc
new file mode 100644
index 00000000..b4f38979
--- /dev/null
+++ b/absl/strings/internal/str_format/checker_test.cc
@@ -0,0 +1,152 @@
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+namespace {
+
+std::string ConvToString(Conv conv) {
+ std::string out;
+#define CONV_SET_CASE(c) \
+ if (Contains(conv, Conv::c)) out += #c;
+ ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
+#undef CONV_SET_CASE
+ if (Contains(conv, Conv::star)) out += "*";
+ return out;
+}
+
+TEST(StrFormatChecker, ArgumentToConv) {
+ Conv conv = ArgumentToConv<std::string>();
+ EXPECT_EQ(ConvToString(conv), "s");
+
+ conv = ArgumentToConv<const char*>();
+ EXPECT_EQ(ConvToString(conv), "sp");
+
+ conv = ArgumentToConv<double>();
+ EXPECT_EQ(ConvToString(conv), "fFeEgGaA");
+
+ conv = ArgumentToConv<int>();
+ EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaA*");
+
+ conv = ArgumentToConv<std::string*>();
+ EXPECT_EQ(ConvToString(conv), "p");
+}
+
+#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+struct Case {
+ bool result;
+ const char* format;
+};
+
+template <typename... Args>
+constexpr Case ValidFormat(const char* format) {
+ return {ValidFormatImpl<ArgumentToConv<Args>()...>(format), format};
+}
+
+TEST(StrFormatChecker, ValidFormat) {
+ // We want to make sure these expressions are constexpr and they have the
+ // expected value.
+ // If they are not constexpr the attribute will just ignore them and not give
+ // a compile time error.
+ enum e {};
+ enum class e2 {};
+ constexpr Case trues[] = {
+ ValidFormat<>("abc"), //
+
+ ValidFormat<e>("%d"), //
+ ValidFormat<e2>("%d"), //
+ ValidFormat<int>("%% %d"), //
+ ValidFormat<int>("%ld"), //
+ ValidFormat<int>("%lld"), //
+ ValidFormat<std::string>("%s"), //
+ ValidFormat<std::string>("%10s"), //
+ ValidFormat<int>("%.10x"), //
+ ValidFormat<int, int>("%*.3x"), //
+ ValidFormat<int>("%1.d"), //
+ ValidFormat<int>("%.d"), //
+ ValidFormat<int, double>("%d %g"), //
+ ValidFormat<int, std::string>("%*s"), //
+ ValidFormat<int, double>("%.*f"), //
+ ValidFormat<void (*)(), volatile int*>("%p %p"), //
+ ValidFormat<string_view, const char*, double, void*>(
+ "string_view=%s const char*=%s double=%f void*=%p)"),
+
+ ValidFormat<int>("%% %1$d"), //
+ ValidFormat<int>("%1$ld"), //
+ ValidFormat<int>("%1$lld"), //
+ ValidFormat<std::string>("%1$s"), //
+ ValidFormat<std::string>("%1$10s"), //
+ ValidFormat<int>("%1$.10x"), //
+ ValidFormat<int>("%1$*1$.*1$d"), //
+ ValidFormat<int, int>("%1$*2$.3x"), //
+ ValidFormat<int>("%1$1.d"), //
+ ValidFormat<int>("%1$.d"), //
+ ValidFormat<double, int>("%2$d %1$g"), //
+ ValidFormat<int, std::string>("%2$*1$s"), //
+ ValidFormat<int, double>("%2$.*1$f"), //
+ ValidFormat<void*, string_view, const char*, double>(
+ "string_view=%2$s const char*=%3$s double=%4$f void*=%1$p "
+ "repeat=%3$s)")};
+
+ for (Case c : trues) {
+ EXPECT_TRUE(c.result) << c.format;
+ }
+
+ constexpr Case falses[] = {
+ ValidFormat<int>(""), //
+
+ ValidFormat<e>("%s"), //
+ ValidFormat<e2>("%s"), //
+ ValidFormat<>("%s"), //
+ ValidFormat<>("%r"), //
+ ValidFormat<int>("%s"), //
+ ValidFormat<int>("%.1.d"), //
+ ValidFormat<int>("%*1d"), //
+ ValidFormat<int>("%1-d"), //
+ ValidFormat<std::string, int>("%*s"), //
+ ValidFormat<int>("%*d"), //
+ ValidFormat<std::string>("%p"), //
+ ValidFormat<int (*)(int)>("%d"), //
+
+ ValidFormat<>("%3$d"), //
+ ValidFormat<>("%1$r"), //
+ ValidFormat<int>("%1$s"), //
+ ValidFormat<int>("%1$.1.d"), //
+ ValidFormat<int>("%1$*2$1d"), //
+ ValidFormat<int>("%1$1-d"), //
+ ValidFormat<std::string, int>("%2$*1$s"), //
+ ValidFormat<std::string>("%1$p"),
+
+ ValidFormat<int, int>("%d %2$d"), //
+ };
+
+ for (Case c : falses) {
+ EXPECT_FALSE(c.result) << c.format;
+ }
+}
+
+TEST(StrFormatChecker, LongFormat) {
+#define CHARS_X_40 "1234567890123456789012345678901234567890"
+#define CHARS_X_400 \
+ CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 \
+ CHARS_X_40 CHARS_X_40 CHARS_X_40
+#define CHARS_X_4000 \
+ CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 \
+ CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400
+ constexpr char long_format[] =
+ CHARS_X_4000 "%d" CHARS_X_4000 "%s" CHARS_X_4000;
+ constexpr bool is_valid = ValidFormat<int, std::string>(long_format).result;
+ EXPECT_TRUE(is_valid);
+}
+
+#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+} // namespace
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
new file mode 100644
index 00000000..95d57b67
--- /dev/null
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -0,0 +1,577 @@
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <cmath>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/strings/internal/str_format/bind.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+namespace {
+
+template <typename T, size_t N>
+size_t ArraySize(T (&)[N]) {
+ return N;
+}
+
+std::string LengthModFor(float) { return ""; }
+std::string LengthModFor(double) { return ""; }
+std::string LengthModFor(long double) { return "L"; }
+std::string LengthModFor(char) { return "hh"; }
+std::string LengthModFor(signed char) { return "hh"; }
+std::string LengthModFor(unsigned char) { return "hh"; }
+std::string LengthModFor(short) { return "h"; } // NOLINT
+std::string LengthModFor(unsigned short) { return "h"; } // NOLINT
+std::string LengthModFor(int) { return ""; }
+std::string LengthModFor(unsigned) { return ""; }
+std::string LengthModFor(long) { return "l"; } // NOLINT
+std::string LengthModFor(unsigned long) { return "l"; } // NOLINT
+std::string LengthModFor(long long) { return "ll"; } // NOLINT
+std::string LengthModFor(unsigned long long) { return "ll"; } // NOLINT
+
+std::string EscCharImpl(int v) {
+ if (isprint(v)) return std::string(1, static_cast<char>(v));
+ char buf[64];
+ int n = snprintf(buf, sizeof(buf), "\\%#.2x",
+ static_cast<unsigned>(v & 0xff));
+ assert(n > 0 && n < sizeof(buf));
+ return std::string(buf, n);
+}
+
+std::string Esc(char v) { return EscCharImpl(v); }
+std::string Esc(signed char v) { return EscCharImpl(v); }
+std::string Esc(unsigned char v) { return EscCharImpl(v); }
+
+template <typename T>
+std::string Esc(const T &v) {
+ std::ostringstream oss;
+ oss << v;
+ return oss.str();
+}
+
+void StrAppend(std::string *dst, const char *format, va_list ap) {
+ // First try with a small fixed size buffer
+ static const int kSpaceLength = 1024;
+ char space[kSpaceLength];
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int result = vsnprintf(space, kSpaceLength, format, backup_ap);
+ va_end(backup_ap);
+ if (result < kSpaceLength) {
+ if (result >= 0) {
+ // Normal case -- everything fit.
+ dst->append(space, result);
+ return;
+ }
+ if (result < 0) {
+ // Just an error.
+ return;
+ }
+ }
+
+ // Increase the buffer size to the size requested by vsnprintf,
+ // plus one for the closing \0.
+ int length = result + 1;
+ char *buf = new char[length];
+
+ // Restore the va_list before we use it again
+ va_copy(backup_ap, ap);
+ result = vsnprintf(buf, length, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result >= 0 && result < length) {
+ // It fit
+ dst->append(buf, result);
+ }
+ delete[] buf;
+}
+
+std::string StrPrint(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::string result;
+ StrAppend(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+class FormatConvertTest : public ::testing::Test { };
+
+template <typename T>
+void TestStringConvert(const T& str) {
+ const FormatArgImpl args[] = {FormatArgImpl(str)};
+ struct Expectation {
+ const char *out;
+ const char *fmt;
+ };
+ const Expectation kExpect[] = {
+ {"hello", "%1$s" },
+ {"", "%1$.s" },
+ {"", "%1$.0s" },
+ {"h", "%1$.1s" },
+ {"he", "%1$.2s" },
+ {"hello", "%1$.10s" },
+ {" hello", "%1$6s" },
+ {" he", "%1$5.2s" },
+ {"he ", "%1$-5.2s" },
+ {"hello ", "%1$-6.10s" },
+ };
+ for (const Expectation &e : kExpect) {
+ UntypedFormatSpecImpl format(e.fmt);
+ EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
+ }
+}
+
+TEST_F(FormatConvertTest, BasicString) {
+ TestStringConvert("hello"); // As char array.
+ TestStringConvert(static_cast<const char*>("hello"));
+ TestStringConvert(std::string("hello"));
+ TestStringConvert(string_view("hello"));
+}
+
+TEST_F(FormatConvertTest, NullString) {
+ const char* p = nullptr;
+ UntypedFormatSpecImpl format("%s");
+ EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)}));
+}
+
+TEST_F(FormatConvertTest, StringPrecision) {
+ // We cap at the precision.
+ char c = 'a';
+ const char* p = &c;
+ UntypedFormatSpecImpl format("%.1s");
+ EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)}));
+
+ // We cap at the nul terminator.
+ p = "ABC";
+ UntypedFormatSpecImpl format2("%.10s");
+ EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
+}
+
+TEST_F(FormatConvertTest, Pointer) {
+#if _MSC_VER
+ // MSVC's printf implementation prints pointers differently. We can't easily
+ // compare our implementation to theirs.
+ return;
+#endif
+ static int x = 0;
+ const int *xp = &x;
+ char c = 'h';
+ char *mcp = &c;
+ const char *cp = "hi";
+ const char *cnil = nullptr;
+ const int *inil = nullptr;
+ using VoidF = void (*)();
+ VoidF fp = [] {}, fnil = nullptr;
+ volatile char vc;
+ volatile char* vcp = &vc;
+ volatile char* vcnil = nullptr;
+ const FormatArgImpl args[] = {
+ FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil),
+ FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
+ FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
+ };
+ struct Expectation {
+ std::string out;
+ const char *fmt;
+ };
+ const Expectation kExpect[] = {
+ {StrPrint("%p", &x), "%p"},
+ {StrPrint("%20p", &x), "%20p"},
+ {StrPrint("%.1p", &x), "%.1p"},
+ {StrPrint("%.20p", &x), "%.20p"},
+ {StrPrint("%30.20p", &x), "%30.20p"},
+
+ {StrPrint("%-p", &x), "%-p"},
+ {StrPrint("%-20p", &x), "%-20p"},
+ {StrPrint("%-.1p", &x), "%-.1p"},
+ {StrPrint("%.20p", &x), "%.20p"},
+ {StrPrint("%-30.20p", &x), "%-30.20p"},
+
+ {StrPrint("%p", cp), "%2$p"}, // const char*
+ {"(nil)", "%3$p"}, // null const char *
+ {"(nil)", "%4$p"}, // null const int *
+ {StrPrint("%p", mcp), "%5$p"}, // nonconst char*
+
+ {StrPrint("%p", fp), "%6$p"}, // function pointer
+ {StrPrint("%p", vcp), "%8$p"}, // function pointer
+
+#ifndef __APPLE__
+ // Apple's printf differs here (0x0 vs. nil)
+ {StrPrint("%p", fnil), "%7$p"}, // null function pointer
+ {StrPrint("%p", vcnil), "%9$p"}, // null function pointer
+#endif
+ };
+ for (const Expectation &e : kExpect) {
+ UntypedFormatSpecImpl format(e.fmt);
+ EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))) << e.fmt;
+ }
+}
+
+struct Cardinal {
+ enum Pos { k1 = 1, k2 = 2, k3 = 3 };
+ enum Neg { kM1 = -1, kM2 = -2, kM3 = -3 };
+};
+
+TEST_F(FormatConvertTest, Enum) {
+ const Cardinal::Pos k3 = Cardinal::k3;
+ const Cardinal::Neg km3 = Cardinal::kM3;
+ const FormatArgImpl args[] = {FormatArgImpl(k3), FormatArgImpl(km3)};
+ UntypedFormatSpecImpl format("%1$d");
+ UntypedFormatSpecImpl format2("%2$d");
+ EXPECT_EQ("3", FormatPack(format, absl::MakeSpan(args)));
+ EXPECT_EQ("-3", FormatPack(format2, absl::MakeSpan(args)));
+}
+
+template <typename T>
+class TypedFormatConvertTest : public FormatConvertTest { };
+
+TYPED_TEST_CASE_P(TypedFormatConvertTest);
+
+std::vector<std::string> AllFlagCombinations() {
+ const char kFlags[] = {'-', '#', '0', '+', ' '};
+ std::vector<std::string> result;
+ for (size_t fsi = 0; fsi < (1ull << ArraySize(kFlags)); ++fsi) {
+ std::string flag_set;
+ for (size_t fi = 0; fi < ArraySize(kFlags); ++fi)
+ if (fsi & (1ull << fi))
+ flag_set += kFlags[fi];
+ result.push_back(flag_set);
+ }
+ return result;
+}
+
+TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) {
+ typedef TypeParam T;
+ typedef typename std::make_unsigned<T>::type UnsignedT;
+ using remove_volatile_t = typename std::remove_volatile<T>::type;
+ const T kMin = std::numeric_limits<remove_volatile_t>::min();
+ const T kMax = std::numeric_limits<remove_volatile_t>::max();
+ const T kVals[] = {
+ remove_volatile_t(1),
+ remove_volatile_t(2),
+ remove_volatile_t(3),
+ remove_volatile_t(123),
+ remove_volatile_t(-1),
+ remove_volatile_t(-2),
+ remove_volatile_t(-3),
+ remove_volatile_t(-123),
+ remove_volatile_t(0),
+ kMax - remove_volatile_t(1),
+ kMax,
+ kMin + remove_volatile_t(1),
+ kMin,
+ };
+ const char kConvChars[] = {'d', 'i', 'u', 'o', 'x', 'X'};
+ const std::string kWid[] = {"", "4", "10"};
+ const std::string kPrec[] = {"", ".", ".0", ".4", ".10"};
+
+ const std::vector<std::string> flag_sets = AllFlagCombinations();
+
+ for (size_t vi = 0; vi < ArraySize(kVals); ++vi) {
+ const T val = kVals[vi];
+ SCOPED_TRACE(Esc(val));
+ const FormatArgImpl args[] = {FormatArgImpl(val)};
+ for (size_t ci = 0; ci < ArraySize(kConvChars); ++ci) {
+ const char conv_char = kConvChars[ci];
+ for (size_t fsi = 0; fsi < flag_sets.size(); ++fsi) {
+ const std::string &flag_set = flag_sets[fsi];
+ for (size_t wi = 0; wi < ArraySize(kWid); ++wi) {
+ const std::string &wid = kWid[wi];
+ for (size_t pi = 0; pi < ArraySize(kPrec); ++pi) {
+ const std::string &prec = kPrec[pi];
+
+ const bool is_signed_conv = (conv_char == 'd' || conv_char == 'i');
+ const bool is_unsigned_to_signed =
+ !std::is_signed<T>::value && is_signed_conv;
+ // Don't consider sign-related flags '+' and ' ' when doing
+ // unsigned to signed conversions.
+ if (is_unsigned_to_signed &&
+ flag_set.find_first_of("+ ") != std::string::npos) {
+ continue;
+ }
+
+ std::string new_fmt("%");
+ new_fmt += flag_set;
+ new_fmt += wid;
+ new_fmt += prec;
+ // old and new always agree up to here.
+ std::string old_fmt = new_fmt;
+ new_fmt += conv_char;
+ std::string old_result;
+ if (is_unsigned_to_signed) {
+ // don't expect agreement on unsigned formatted as signed,
+ // as printf can't do that conversion properly. For those
+ // cases, we do expect agreement with printf with a "%u"
+ // and the unsigned equivalent of 'val'.
+ UnsignedT uval = val;
+ old_fmt += LengthModFor(uval);
+ old_fmt += "u";
+ old_result = StrPrint(old_fmt.c_str(), uval);
+ } else {
+ old_fmt += LengthModFor(val);
+ old_fmt += conv_char;
+ old_result = StrPrint(old_fmt.c_str(), val);
+ }
+
+ SCOPED_TRACE(std::string() + " old_fmt: \"" + old_fmt +
+ "\"'"
+ " new_fmt: \"" +
+ new_fmt + "\"");
+ UntypedFormatSpecImpl format(new_fmt);
+ EXPECT_EQ(old_result, FormatPack(format, absl::MakeSpan(args)));
+ }
+ }
+ }
+ }
+ }
+}
+
+TYPED_TEST_P(TypedFormatConvertTest, Char) {
+ typedef TypeParam T;
+ using remove_volatile_t = typename std::remove_volatile<T>::type;
+ static const T kMin = std::numeric_limits<remove_volatile_t>::min();
+ static const T kMax = std::numeric_limits<remove_volatile_t>::max();
+ T kVals[] = {
+ remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10),
+ remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10),
+ remove_volatile_t(0),
+ kMin + remove_volatile_t(1), kMin,
+ kMax - remove_volatile_t(1), kMax
+ };
+ for (const T &c : kVals) {
+ const FormatArgImpl args[] = {FormatArgImpl(c)};
+ UntypedFormatSpecImpl format("%c");
+ EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args)));
+ }
+}
+
+REGISTER_TYPED_TEST_CASE_P(TypedFormatConvertTest, AllIntsWithFlags, Char);
+
+typedef ::testing::Types<
+ int, unsigned, volatile int,
+ short, unsigned short,
+ long, unsigned long,
+ long long, unsigned long long,
+ signed char, unsigned char, char>
+ AllIntTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes,
+ TypedFormatConvertTest, AllIntTypes);
+TEST_F(FormatConvertTest, Uint128) {
+ absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979;
+ absl::uint128 max = absl::Uint128Max();
+ const FormatArgImpl args[] = {FormatArgImpl(v), FormatArgImpl(max)};
+
+ struct Case {
+ const char* format;
+ const char* expected;
+ } cases[] = {
+ {"%1$d", "2595989796776606496405"},
+ {"%1$30d", " 2595989796776606496405"},
+ {"%1$-30d", "2595989796776606496405 "},
+ {"%1$u", "2595989796776606496405"},
+ {"%1$x", "8cba9876066020f695"},
+ {"%2$d", "340282366920938463463374607431768211455"},
+ {"%2$u", "340282366920938463463374607431768211455"},
+ {"%2$x", "ffffffffffffffffffffffffffffffff"},
+ };
+
+ for (auto c : cases) {
+ UntypedFormatSpecImpl format(c.format);
+ EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
+ }
+}
+
+TEST_F(FormatConvertTest, Float) {
+#if _MSC_VER
+ // MSVC has a different rounding policy than us so we can't test our
+ // implementation against the native one there.
+ return;
+#endif // _MSC_VER
+
+ const char *const kFormats[] = {
+ "%", "%.3", "%8.5", "%9", "%.60", "%.30", "%03", "%+",
+ "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"};
+
+ std::vector<double> doubles = {0.0,
+ -0.0,
+ .99999999999999,
+ 99999999999999.,
+ std::numeric_limits<double>::max(),
+ -std::numeric_limits<double>::max(),
+ std::numeric_limits<double>::min(),
+ -std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::lowest(),
+ -std::numeric_limits<double>::lowest(),
+ std::numeric_limits<double>::epsilon(),
+ std::numeric_limits<double>::epsilon() + 1,
+ std::numeric_limits<double>::infinity(),
+ -std::numeric_limits<double>::infinity()};
+
+#ifndef __APPLE__
+ // Apple formats NaN differently (+nan) vs. (nan)
+ doubles.push_back(std::nan(""));
+#endif
+
+ // Some regression tests.
+ doubles.push_back(0.99999999999999989);
+
+ if (std::numeric_limits<double>::has_denorm != std::denorm_absent) {
+ doubles.push_back(std::numeric_limits<double>::denorm_min());
+ doubles.push_back(-std::numeric_limits<double>::denorm_min());
+ }
+
+ for (double base :
+ {1., 12., 123., 1234., 12345., 123456., 1234567., 12345678., 123456789.,
+ 1234567890., 12345678901., 123456789012., 1234567890123.}) {
+ for (int exp = -123; exp <= 123; ++exp) {
+ for (int sign : {1, -1}) {
+ doubles.push_back(sign * std::ldexp(base, exp));
+ }
+ }
+ }
+
+ for (const char *fmt : kFormats) {
+ for (char f : {'f', 'F', //
+ 'g', 'G', //
+ 'a', 'A', //
+ 'e', 'E'}) {
+ std::string fmt_str = std::string(fmt) + f;
+ for (double d : doubles) {
+ int i = -10;
+ FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
+ UntypedFormatSpecImpl format(fmt_str);
+ // We use ASSERT_EQ here because failures are usually correlated and a
+ // bug would print way too many failed expectations causing the test to
+ // time out.
+ ASSERT_EQ(StrPrint(fmt_str.c_str(), d, i),
+ FormatPack(format, absl::MakeSpan(args)))
+ << fmt_str << " " << StrPrint("%.18g", d) << " "
+ << StrPrint("%.999f", d);
+ }
+ }
+ }
+}
+
+TEST_F(FormatConvertTest, LongDouble) {
+ const char *const kFormats[] = {"%", "%.3", "%8.5", "%9",
+ "%.60", "%+", "% ", "%-10"};
+
+ // This value is not representable in double, but it is in long double that
+ // uses the extended format.
+ // This is to verify that we are not truncating the value mistakenly through a
+ // double.
+ long double very_precise = 10000000000000000.25L;
+
+ std::vector<long double> doubles = {
+ 0.0,
+ -0.0,
+ very_precise,
+ 1 / very_precise,
+ std::numeric_limits<long double>::max(),
+ -std::numeric_limits<long double>::max(),
+ std::numeric_limits<long double>::min(),
+ -std::numeric_limits<long double>::min(),
+ std::numeric_limits<long double>::infinity(),
+ -std::numeric_limits<long double>::infinity()};
+
+ for (const char *fmt : kFormats) {
+ for (char f : {'f', 'F', //
+ 'g', 'G', //
+ 'a', 'A', //
+ 'e', 'E'}) {
+ std::string fmt_str = std::string(fmt) + 'L' + f;
+ for (auto d : doubles) {
+ FormatArgImpl arg(d);
+ UntypedFormatSpecImpl format(fmt_str);
+ // We use ASSERT_EQ here because failures are usually correlated and a
+ // bug would print way too many failed expectations causing the test to
+ // time out.
+ ASSERT_EQ(StrPrint(fmt_str.c_str(), d),
+ FormatPack(format, {&arg, 1}))
+ << fmt_str << " " << StrPrint("%.18Lg", d) << " "
+ << StrPrint("%.999Lf", d);
+ }
+ }
+ }
+}
+
+TEST_F(FormatConvertTest, IntAsFloat) {
+ const int kMin = std::numeric_limits<int>::min();
+ const int kMax = std::numeric_limits<int>::max();
+ const int ia[] = {
+ 1, 2, 3, 123,
+ -1, -2, -3, -123,
+ 0, kMax - 1, kMax, kMin + 1, kMin };
+ for (const int fx : ia) {
+ SCOPED_TRACE(fx);
+ const FormatArgImpl args[] = {FormatArgImpl(fx)};
+ struct Expectation {
+ int line;
+ std::string out;
+ const char *fmt;
+ };
+ const double dx = static_cast<double>(fx);
+ const Expectation kExpect[] = {
+ { __LINE__, StrPrint("%f", dx), "%f" },
+ { __LINE__, StrPrint("%12f", dx), "%12f" },
+ { __LINE__, StrPrint("%.12f", dx), "%.12f" },
+ { __LINE__, StrPrint("%12a", dx), "%12a" },
+ { __LINE__, StrPrint("%.12a", dx), "%.12a" },
+ };
+ for (const Expectation &e : kExpect) {
+ SCOPED_TRACE(e.line);
+ SCOPED_TRACE(e.fmt);
+ UntypedFormatSpecImpl format(e.fmt);
+ EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
+ }
+ }
+}
+
+template <typename T>
+bool FormatFails(const char* test_format, T value) {
+ std::string format_string = std::string("<<") + test_format + ">>";
+ UntypedFormatSpecImpl format(format_string);
+
+ int one = 1;
+ const FormatArgImpl args[] = {FormatArgImpl(value), FormatArgImpl(one)};
+ EXPECT_EQ(FormatPack(format, absl::MakeSpan(args)), "")
+ << "format=" << test_format << " value=" << value;
+ return FormatPack(format, absl::MakeSpan(args)).empty();
+}
+
+TEST_F(FormatConvertTest, ExpectedFailures) {
+ // Int input
+ EXPECT_TRUE(FormatFails("%p", 1));
+ EXPECT_TRUE(FormatFails("%s", 1));
+ EXPECT_TRUE(FormatFails("%n", 1));
+
+ // Double input
+ EXPECT_TRUE(FormatFails("%p", 1.));
+ EXPECT_TRUE(FormatFails("%s", 1.));
+ EXPECT_TRUE(FormatFails("%n", 1.));
+ EXPECT_TRUE(FormatFails("%c", 1.));
+ EXPECT_TRUE(FormatFails("%d", 1.));
+ EXPECT_TRUE(FormatFails("%x", 1.));
+ EXPECT_TRUE(FormatFails("%*d", 1.));
+
+ // String input
+ EXPECT_TRUE(FormatFails("%n", ""));
+ EXPECT_TRUE(FormatFails("%c", ""));
+ EXPECT_TRUE(FormatFails("%d", ""));
+ EXPECT_TRUE(FormatFails("%x", ""));
+ EXPECT_TRUE(FormatFails("%f", ""));
+ EXPECT_TRUE(FormatFails("%*d", ""));
+}
+
+} // namespace
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc
new file mode 100644
index 00000000..e3b41c82
--- /dev/null
+++ b/absl/strings/internal/str_format/extension.cc
@@ -0,0 +1,86 @@
+//
+// 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
+//
+// 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.
+
+#include "absl/strings/internal/str_format/extension.h"
+
+#include <errno.h>
+#include <algorithm>
+#include <string>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+namespace {
+// clang-format off
+#define ABSL_LENGTH_MODS_EXPAND_ \
+ X_VAL(h) X_SEP \
+ X_VAL(hh) X_SEP \
+ X_VAL(l) X_SEP \
+ X_VAL(ll) X_SEP \
+ X_VAL(L) X_SEP \
+ X_VAL(j) X_SEP \
+ X_VAL(z) X_SEP \
+ X_VAL(t) X_SEP \
+ X_VAL(q)
+// clang-format on
+} // namespace
+
+const LengthMod::Spec LengthMod::kSpecs[] = {
+#define X_VAL(id) { LengthMod::id, #id, strlen(#id) }
+#define X_SEP ,
+ ABSL_LENGTH_MODS_EXPAND_, {LengthMod::none, "", 0}
+#undef X_VAL
+#undef X_SEP
+};
+
+const ConversionChar::Spec ConversionChar::kSpecs[] = {
+#define X_VAL(id) { ConversionChar::id, #id[0] }
+#define X_SEP ,
+ ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP),
+ {ConversionChar::none, '\0'},
+#undef X_VAL
+#undef X_SEP
+};
+
+std::string Flags::ToString() const {
+ std::string s;
+ s.append(left ? "-" : "");
+ s.append(show_pos ? "+" : "");
+ s.append(sign_col ? " " : "");
+ s.append(alt ? "#" : "");
+ s.append(zero ? "0" : "");
+ return s;
+}
+
+const size_t LengthMod::kNumValues;
+
+const size_t ConversionChar::kNumValues;
+
+bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) {
+ size_t space_remaining = 0;
+ if (w >= 0) space_remaining = w;
+ size_t n = v.size();
+ if (p >= 0) n = std::min(n, static_cast<size_t>(p));
+ string_view shown(v.data(), n);
+ space_remaining = Excess(shown.size(), space_remaining);
+ if (!l) Append(space_remaining, ' ');
+ Append(shown);
+ if (l) Append(space_remaining, ' ');
+ return true;
+}
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
new file mode 100644
index 00000000..d401b4ed
--- /dev/null
+++ b/absl/strings/internal/str_format/extension.h
@@ -0,0 +1,414 @@
+//
+// 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
+//
+// 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.
+//
+//
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
+
+#include <limits.h>
+#include <cstddef>
+#include <cstring>
+#include <ostream>
+
+#include "absl/base/port.h"
+#include "absl/strings/internal/str_format/output.h"
+#include "absl/strings/string_view.h"
+
+class Cord;
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+namespace str_format_internal {
+
+class FormatRawSinkImpl {
+ public:
+ // Implicitly convert from any type that provides the hook function as
+ // described above.
+ template <typename T, decltype(str_format_internal::InvokeFlush(
+ std::declval<T*>(), string_view()))* = nullptr>
+ FormatRawSinkImpl(T* raw) // NOLINT
+ : sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {}
+
+ void Write(string_view s) { write_(sink_, s); }
+
+ template <typename T>
+ static FormatRawSinkImpl Extract(T s) {
+ return s.sink_;
+ }
+
+ private:
+ template <typename T>
+ static void Flush(void* r, string_view s) {
+ str_format_internal::InvokeFlush(static_cast<T*>(r), s);
+ }
+
+ void* sink_;
+ void (*write_)(void*, string_view);
+};
+
+// An abstraction to which conversions write their string data.
+class FormatSinkImpl {
+ public:
+ explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {}
+
+ ~FormatSinkImpl() { Flush(); }
+
+ void Flush() {
+ raw_.Write(string_view(buf_, pos_ - buf_));
+ pos_ = buf_;
+ }
+
+ void Append(size_t n, char c) {
+ if (n == 0) return;
+ size_ += n;
+ auto raw_append = [&](size_t count) {
+ memset(pos_, c, count);
+ pos_ += count;
+ };
+ while (n > Avail()) {
+ n -= Avail();
+ if (Avail() > 0) {
+ raw_append(Avail());
+ }
+ Flush();
+ }
+ raw_append(n);
+ }
+
+ void Append(string_view v) {
+ size_t n = v.size();
+ if (n == 0) return;
+ size_ += n;
+ if (n >= Avail()) {
+ Flush();
+ raw_.Write(v);
+ return;
+ }
+ memcpy(pos_, v.data(), n);
+ pos_ += n;
+ }
+
+ size_t size() const { return size_; }
+
+ // Put 'v' to 'sink' with specified width, precision, and left flag.
+ bool PutPaddedString(string_view v, int w, int p, bool l);
+
+ template <typename T>
+ T Wrap() {
+ return T(this);
+ }
+
+ template <typename T>
+ static FormatSinkImpl* Extract(T* s) {
+ return s->sink_;
+ }
+
+ private:
+ size_t Avail() const { return buf_ + sizeof(buf_) - pos_; }
+
+ FormatRawSinkImpl raw_;
+ size_t size_ = 0;
+ char* pos_ = buf_;
+ char buf_[1024];
+};
+
+struct Flags {
+ bool basic : 1; // fastest conversion: no flags, width, or precision
+ bool left : 1; // "-"
+ bool show_pos : 1; // "+"
+ bool sign_col : 1; // " "
+ bool alt : 1; // "#"
+ bool zero : 1; // "0"
+ std::string ToString() const;
+ friend std::ostream& operator<<(std::ostream& os, const Flags& v) {
+ return os << v.ToString();
+ }
+};
+
+struct LengthMod {
+ public:
+ enum Id : uint8_t {
+ h, hh, l, ll, L, j, z, t, q, none
+ };
+ static const size_t kNumValues = none + 1;
+
+ LengthMod() : id_(none) {}
+
+ // Index into the opaque array of LengthMod enums.
+ // Requires: i < kNumValues
+ static LengthMod FromIndex(size_t i) {
+ return LengthMod(kSpecs[i].value);
+ }
+
+ static LengthMod FromId(Id id) { return LengthMod(id); }
+
+ // The length modifier std::string associated with a specified LengthMod.
+ string_view name() const {
+ const Spec& spec = kSpecs[id_];
+ return {spec.name, spec.name_length};
+ }
+
+ Id id() const { return id_; }
+
+ friend bool operator==(const LengthMod& a, const LengthMod& b) {
+ return a.id() == b.id();
+ }
+ friend bool operator!=(const LengthMod& a, const LengthMod& b) {
+ return !(a == b);
+ }
+ friend std::ostream& operator<<(std::ostream& os, const LengthMod& v) {
+ return os << v.name();
+ }
+
+ private:
+ struct Spec {
+ Id value;
+ const char *name;
+ size_t name_length;
+ };
+ static const Spec kSpecs[];
+
+ explicit LengthMod(Id id) : id_(id) {}
+
+ Id id_;
+};
+
+// clang-format off
+#define ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
+ /* text */ \
+ X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \
+ /* ints */ \
+ X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \
+ X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \
+ /* floats */ \
+ X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \
+ X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \
+ /* misc */ \
+ X_VAL(n) X_SEP X_VAL(p)
+// clang-format on
+
+struct ConversionChar {
+ public:
+ enum Id : uint8_t {
+ c, C, s, S, // text
+ d, i, o, u, x, X, // int
+ f, F, e, E, g, G, a, A, // float
+ n, p, // misc
+ none
+ };
+ static const size_t kNumValues = none + 1;
+
+ ConversionChar() : id_(none) {}
+
+ public:
+ // Index into the opaque array of ConversionChar enums.
+ // Requires: i < kNumValues
+ static ConversionChar FromIndex(size_t i) {
+ return ConversionChar(kSpecs[i].value);
+ }
+
+ static ConversionChar FromChar(char c) {
+ ConversionChar::Id out_id = ConversionChar::none;
+ switch (c) {
+#define X_VAL(id) \
+ case #id[0]: \
+ out_id = ConversionChar::id; \
+ break;
+ ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, )
+#undef X_VAL
+ default:
+ break;
+ }
+ return ConversionChar(out_id);
+ }
+
+ static ConversionChar FromId(Id id) { return ConversionChar(id); }
+ Id id() const { return id_; }
+
+ int radix() const {
+ switch (id()) {
+ case x: case X: case a: case A: case p: return 16;
+ case o: return 8;
+ default: return 10;
+ }
+ }
+
+ bool upper() const {
+ switch (id()) {
+ case X: case F: case E: case G: case A: return true;
+ default: return false;
+ }
+ }
+
+ bool is_signed() const {
+ switch (id()) {
+ case d: case i: return true;
+ default: return false;
+ }
+ }
+
+ bool is_integral() const {
+ switch (id()) {
+ case d: case i: case u: case o: case x: case X:
+ return true;
+ default: return false;
+ }
+ }
+
+ bool is_float() const {
+ switch (id()) {
+ case a: case e: case f: case g: case A: case E: case F: case G:
+ return true;
+ default: return false;
+ }
+ }
+
+ bool IsValid() const { return id() != none; }
+
+ // The associated char.
+ char Char() const { return kSpecs[id_].name; }
+
+ friend bool operator==(const ConversionChar& a, const ConversionChar& b) {
+ return a.id() == b.id();
+ }
+ friend bool operator!=(const ConversionChar& a, const ConversionChar& b) {
+ return !(a == b);
+ }
+ friend std::ostream& operator<<(std::ostream& os, const ConversionChar& v) {
+ char c = v.Char();
+ if (!c) c = '?';
+ return os << c;
+ }
+
+ private:
+ struct Spec {
+ Id value;
+ char name;
+ };
+ static const Spec kSpecs[];
+
+ explicit ConversionChar(Id id) : id_(id) {}
+
+ Id id_;
+};
+
+class ConversionSpec {
+ public:
+ Flags flags() const { return flags_; }
+ LengthMod length_mod() const { return length_mod_; }
+ ConversionChar conv() const {
+ // Keep this field first in the struct . It generates better code when
+ // accessing it when ConversionSpec is passed by value in registers.
+ static_assert(offsetof(ConversionSpec, conv_) == 0, "");
+ return conv_;
+ }
+
+ // Returns the specified width. If width is unspecfied, it returns a negative
+ // value.
+ int width() const { return width_; }
+ // Returns the specified precision. If precision is unspecfied, it returns a
+ // negative value.
+ int precision() const { return precision_; }
+
+ void set_flags(Flags f) { flags_ = f; }
+ void set_length_mod(LengthMod lm) { length_mod_ = lm; }
+ void set_conv(ConversionChar c) { conv_ = c; }
+ void set_width(int w) { width_ = w; }
+ void set_precision(int p) { precision_ = p; }
+ void set_left(bool b) { flags_.left = b; }
+
+ private:
+ ConversionChar conv_;
+ Flags flags_;
+ LengthMod length_mod_;
+ int width_;
+ int precision_;
+};
+
+constexpr uint64_t ConversionCharToConvValue(char conv) {
+ return
+#define CONV_SET_CASE(c) \
+ conv == #c[0] ? (uint64_t{1} << (1 + ConversionChar::Id::c)):
+ ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
+#undef CONV_SET_CASE
+ conv == '*'
+ ? 1
+ : 0;
+}
+
+enum class Conv : uint64_t {
+#define CONV_SET_CASE(c) c = ConversionCharToConvValue(#c[0]),
+ ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
+#undef CONV_SET_CASE
+
+ // Used for width/precision '*' specification.
+ star = ConversionCharToConvValue('*'),
+
+ // Some predefined values:
+ integral = d | i | u | o | x | X,
+ floating = a | e | f | g | A | E | F | G,
+ numeric = integral | floating,
+ string = s, // absl:ignore(std::string)
+ pointer = p
+};
+
+// Type safe OR operator.
+// We need this for two reasons:
+// 1. operator| on enums makes them decay to integers and the result is an
+// integer. We need the result to stay as an enum.
+// 2. We use "enum class" which would not work even if we accepted the decay.
+constexpr Conv operator|(Conv a, Conv b) {
+ return Conv(static_cast<uint64_t>(a) | static_cast<uint64_t>(b));
+}
+
+// Get a conversion with a single character in it.
+constexpr Conv ConversionCharToConv(char c) {
+ return Conv(ConversionCharToConvValue(c));
+}
+
+// Checks whether `c` exists in `set`.
+constexpr bool Contains(Conv set, char c) {
+ return (static_cast<uint64_t>(set) & ConversionCharToConvValue(c)) != 0;
+}
+
+// Checks whether all the characters in `c` are contained in `set`
+constexpr bool Contains(Conv set, Conv c) {
+ return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) ==
+ static_cast<uint64_t>(c);
+}
+
+// Return type of the AbslFormatConvert() functions.
+// The Conv template parameter is used to inform the framework of what
+// conversion characters are supported by that AbslFormatConvert routine.
+template <Conv C>
+struct ConvertResult {
+ static constexpr Conv kConv = C;
+ bool value;
+};
+template <Conv C>
+constexpr Conv ConvertResult<C>::kConv;
+
+// Return capacity - used, clipped to a minimum of 0.
+inline size_t Excess(size_t used, size_t capacity) {
+ return used < capacity ? capacity - used : 0;
+}
+
+} // namespace str_format_internal
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
diff --git a/absl/strings/internal/str_format/extension_test.cc b/absl/strings/internal/str_format/extension_test.cc
new file mode 100644
index 00000000..224fc923
--- /dev/null
+++ b/absl/strings/internal/str_format/extension_test.cc
@@ -0,0 +1,65 @@
+//
+// 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
+//
+// 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.
+//
+
+#include "absl/strings/internal/str_format/extension.h"
+
+#include <random>
+#include <string>
+#include "absl/strings/str_format.h"
+
+#include "gtest/gtest.h"
+
+namespace {
+
+std::string MakeRandomString(size_t len) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<> dis('a', 'z');
+ std::string s(len, '0');
+ for (char& c : s) {
+ c = dis(gen);
+ }
+ return s;
+}
+
+TEST(FormatExtensionTest, SinkAppendSubstring) {
+ for (size_t chunk_size : {1, 10, 100, 1000, 10000}) {
+ std::string expected, actual;
+ absl::str_format_internal::FormatSinkImpl sink(&actual);
+ for (size_t chunks = 0; chunks < 10; ++chunks) {
+ std::string rand = MakeRandomString(chunk_size);
+ expected += rand;
+ sink.Append(rand);
+ }
+ sink.Flush();
+ EXPECT_EQ(actual, expected);
+ }
+}
+
+TEST(FormatExtensionTest, SinkAppendChars) {
+ for (size_t chunk_size : {1, 10, 100, 1000, 10000}) {
+ std::string expected, actual;
+ absl::str_format_internal::FormatSinkImpl sink(&actual);
+ for (size_t chunks = 0; chunks < 10; ++chunks) {
+ std::string rand = MakeRandomString(1);
+ expected.append(chunk_size, rand[0]);
+ sink.Append(chunk_size, rand[0]);
+ }
+ sink.Flush();
+ EXPECT_EQ(actual, expected);
+ }
+}
+} // namespace
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc
new file mode 100644
index 00000000..7b617689
--- /dev/null
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -0,0 +1,485 @@
+#include "absl/strings/internal/str_format/float_conversion.h"
+
+#include <string.h>
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <string>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+namespace {
+
+char *CopyStringTo(string_view v, char *out) {
+ std::memcpy(out, v.data(), v.size());
+ return out + v.size();
+}
+
+template <typename Float>
+bool FallbackToSnprintf(const Float v, const ConversionSpec &conv,
+ FormatSinkImpl *sink) {
+ int w = conv.width() >= 0 ? conv.width() : 0;
+ int p = conv.precision() >= 0 ? conv.precision() : -1;
+ char fmt[32];
+ {
+ char *fp = fmt;
+ *fp++ = '%';
+ fp = CopyStringTo(conv.flags().ToString(), fp);
+ fp = CopyStringTo("*.*", fp);
+ if (std::is_same<long double, Float>()) {
+ *fp++ = 'L';
+ }
+ *fp++ = conv.conv().Char();
+ *fp = 0;
+ assert(fp < fmt + sizeof(fmt));
+ }
+ std::string space(512, '\0');
+ string_view result;
+ while (true) {
+ int n = snprintf(&space[0], space.size(), fmt, w, p, v);
+ if (n < 0) return false;
+ if (static_cast<size_t>(n) < space.size()) {
+ result = string_view(space.data(), n);
+ break;
+ }
+ space.resize(n + 1);
+ }
+ sink->Append(result);
+ return true;
+}
+
+// 128-bits in decimal: ceil(128*log(2)/log(10))
+// or std::numeric_limits<__uint128_t>::digits10
+constexpr int kMaxFixedPrecision = 39;
+
+constexpr int kBufferLength = /*sign*/ 1 +
+ /*integer*/ kMaxFixedPrecision +
+ /*point*/ 1 +
+ /*fraction*/ kMaxFixedPrecision +
+ /*exponent e+123*/ 5;
+
+struct Buffer {
+ void push_front(char c) {
+ assert(begin > data);
+ *--begin = c;
+ }
+ void push_back(char c) {
+ assert(end < data + sizeof(data));
+ *end++ = c;
+ }
+ void pop_back() {
+ assert(begin < end);
+ --end;
+ }
+
+ char &back() {
+ assert(begin < end);
+ return end[-1];
+ }
+
+ char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; }
+
+ int size() const { return static_cast<int>(end - begin); }
+
+ char data[kBufferLength];
+ char *begin;
+ char *end;
+};
+
+enum class FormatStyle { Fixed, Precision };
+
+// If the value is Inf or Nan, print it and return true.
+// Otherwise, return false.
+template <typename Float>
+bool ConvertNonNumericFloats(char sign_char, Float v,
+ const ConversionSpec &conv, FormatSinkImpl *sink) {
+ char text[4], *ptr = text;
+ if (sign_char) *ptr++ = sign_char;
+ if (std::isnan(v)) {
+ ptr = std::copy_n(conv.conv().upper() ? "NAN" : "nan", 3, ptr);
+ } else if (std::isinf(v)) {
+ ptr = std::copy_n(conv.conv().upper() ? "INF" : "inf", 3, ptr);
+ } else {
+ return false;
+ }
+
+ return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1,
+ conv.flags().left);
+}
+
+// Round up the last digit of the value.
+// It will carry over and potentially overflow. 'exp' will be adjusted in that
+// case.
+template <FormatStyle mode>
+void RoundUp(Buffer *buffer, int *exp) {
+ char *p = &buffer->back();
+ while (p >= buffer->begin && (*p == '9' || *p == '.')) {
+ if (*p == '9') *p = '0';
+ --p;
+ }
+
+ if (p < buffer->begin) {
+ *p = '1';
+ buffer->begin = p;
+ if (mode == FormatStyle::Precision) {
+ std::swap(p[1], p[2]); // move the .
+ ++*exp;
+ buffer->pop_back();
+ }
+ } else {
+ ++*p;
+ }
+}
+
+void PrintExponent(int exp, char e, Buffer *out) {
+ out->push_back(e);
+ if (exp < 0) {
+ out->push_back('-');
+ exp = -exp;
+ } else {
+ out->push_back('+');
+ }
+ // Exponent digits.
+ if (exp > 99) {
+ out->push_back(exp / 100 + '0');
+ out->push_back(exp / 10 % 10 + '0');
+ out->push_back(exp % 10 + '0');
+ } else {
+ out->push_back(exp / 10 + '0');
+ out->push_back(exp % 10 + '0');
+ }
+}
+
+template <typename Float, typename Int>
+constexpr bool CanFitMantissa() {
+ return
+#if defined(__clang__) && !defined(__SSE3__)
+ // Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289
+ // Casting from long double to uint64_t is miscompiled and drops bits.
+ (!std::is_same<Float, long double>::value ||
+ !std::is_same<Int, uint64_t>::value) &&
+#endif
+ std::numeric_limits<Float>::digits <= std::numeric_limits<Int>::digits;
+}
+
+template <typename Float>
+struct Decomposed {
+ Float mantissa;
+ int exponent;
+};
+
+// Decompose the double into an integer mantissa and an exponent.
+template <typename Float>
+Decomposed<Float> Decompose(Float v) {
+ int exp;
+ Float m = std::frexp(v, &exp);
+ m = std::ldexp(m, std::numeric_limits<Float>::digits);
+ exp -= std::numeric_limits<Float>::digits;
+ return {m, exp};
+}
+
+// Print 'digits' as decimal.
+// In Fixed mode, we add a '.' at the end.
+// In Precision mode, we add a '.' after the first digit.
+template <FormatStyle mode, typename Int>
+int PrintIntegralDigits(Int digits, Buffer *out) {
+ int printed = 0;
+ if (digits) {
+ for (; digits; digits /= 10) out->push_front(digits % 10 + '0');
+ printed = out->size();
+ if (mode == FormatStyle::Precision) {
+ out->push_front(*out->begin);
+ out->begin[1] = '.';
+ } else {
+ out->push_back('.');
+ }
+ } else if (mode == FormatStyle::Fixed) {
+ out->push_front('0');
+ out->push_back('.');
+ printed = 1;
+ }
+ return printed;
+}
+
+// Back out 'extra_digits' digits and round up if necessary.
+bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value,
+ Buffer *out, int *exp_out) {
+ if (extra_digits <= 0) return false;
+
+ // Back out the extra digits
+ out->end -= extra_digits;
+
+ bool needs_to_round_up = [&] {
+ // We look at the digit just past the end.
+ // There must be 'extra_digits' extra valid digits after end.
+ if (*out->end > '5') return true;
+ if (*out->end < '5') return false;
+ if (has_leftover_value || std::any_of(out->end + 1, out->end + extra_digits,
+ [](char c) { return c != '0'; }))
+ return true;
+
+ // Ends in ...50*, round to even.
+ return out->last_digit() % 2 == 1;
+ }();
+
+ if (needs_to_round_up) {
+ RoundUp<FormatStyle::Precision>(out, exp_out);
+ }
+ return true;
+}
+
+// Print the value into the buffer.
+// This will not include the exponent, which will be returned in 'exp_out' for
+// Precision mode.
+template <typename Int, typename Float, FormatStyle mode>
+bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
+ int *exp_out) {
+ assert((CanFitMantissa<Float, Int>()));
+
+ const int int_bits = std::numeric_limits<Int>::digits;
+
+ // In precision mode, we start printing one char to the right because it will
+ // also include the '.'
+ // In fixed mode we put the dot afterwards on the right.
+ out->begin = out->end =
+ out->data + 1 + kMaxFixedPrecision + (mode == FormatStyle::Precision);
+
+ if (exp >= 0) {
+ if (std::numeric_limits<Float>::digits + exp > int_bits) {
+ // The value will overflow the Int
+ return false;
+ }
+ int digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out);
+ int digits_to_zero_pad = precision;
+ if (mode == FormatStyle::Precision) {
+ *exp_out = digits_printed - 1;
+ digits_to_zero_pad -= digits_printed - 1;
+ if (RemoveExtraPrecision(-digits_to_zero_pad, false, out, exp_out)) {
+ return true;
+ }
+ }
+ for (; digits_to_zero_pad-- > 0;) out->push_back('0');
+ return true;
+ }
+
+ exp = -exp;
+ // We need at least 4 empty bits for the next decimal digit.
+ // We will multiply by 10.
+ if (exp > int_bits - 4) return false;
+
+ const Int mask = (Int{1} << exp) - 1;
+
+ // Print the integral part first.
+ int digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out);
+ int_mantissa &= mask;
+
+ int fractional_count = precision;
+ if (mode == FormatStyle::Precision) {
+ if (digits_printed == 0) {
+ // Find the first non-zero digit, when in Precision mode.
+ *exp_out = 0;
+ if (int_mantissa) {
+ while (int_mantissa <= mask) {
+ int_mantissa *= 10;
+ --*exp_out;
+ }
+ }
+ out->push_front(static_cast<char>(int_mantissa >> exp) + '0');
+ out->push_back('.');
+ int_mantissa &= mask;
+ } else {
+ // We already have a digit, and a '.'
+ *exp_out = digits_printed - 1;
+ fractional_count -= *exp_out;
+ if (RemoveExtraPrecision(-fractional_count, int_mantissa != 0, out,
+ exp_out)) {
+ // If we had enough digits, return right away.
+ // The code below will try to round again otherwise.
+ return true;
+ }
+ }
+ }
+
+ auto get_next_digit = [&] {
+ int_mantissa *= 10;
+ int digit = static_cast<int>(int_mantissa >> exp);
+ int_mantissa &= mask;
+ return digit;
+ };
+
+ // Print fractional_count more digits, if available.
+ for (; fractional_count > 0; --fractional_count) {
+ out->push_back(get_next_digit() + '0');
+ }
+
+ int next_digit = get_next_digit();
+ if (next_digit > 5 ||
+ (next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) {
+ RoundUp<mode>(out, exp_out);
+ }
+
+ return true;
+}
+
+template <FormatStyle mode, typename Float>
+bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out,
+ int *exp) {
+ if (precision > kMaxFixedPrecision) return false;
+
+ // Try with uint64_t.
+ if (CanFitMantissa<Float, std::uint64_t>() &&
+ FloatToBufferImpl<std::uint64_t, Float, mode>(
+ static_cast<std::uint64_t>(decomposed.mantissa),
+ static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp))
+ return true;
+
+#if defined(__SIZEOF_INT128__)
+ // If that is not enough, try with __uint128_t.
+ return CanFitMantissa<Float, __uint128_t>() &&
+ FloatToBufferImpl<__uint128_t, Float, mode>(
+ static_cast<__uint128_t>(decomposed.mantissa),
+ static_cast<__uint128_t>(decomposed.exponent), precision, out,
+ exp);
+#endif
+ return false;
+}
+
+void WriteBufferToSink(char sign_char, string_view str,
+ const ConversionSpec &conv, FormatSinkImpl *sink) {
+ int left_spaces = 0, zeros = 0, right_spaces = 0;
+ int missing_chars =
+ conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) -
+ static_cast<int>(sign_char != 0),
+ 0)
+ : 0;
+ if (conv.flags().left) {
+ right_spaces = missing_chars;
+ } else if (conv.flags().zero) {
+ zeros = missing_chars;
+ } else {
+ left_spaces = missing_chars;
+ }
+
+ sink->Append(left_spaces, ' ');
+ if (sign_char) sink->Append(1, sign_char);
+ sink->Append(zeros, '0');
+ sink->Append(str);
+ sink->Append(right_spaces, ' ');
+}
+
+template <typename Float>
+bool FloatToSink(const Float v, const ConversionSpec &conv,
+ FormatSinkImpl *sink) {
+ // Print the sign or the sign column.
+ Float abs_v = v;
+ char sign_char = 0;
+ if (std::signbit(abs_v)) {
+ sign_char = '-';
+ abs_v = -abs_v;
+ } else if (conv.flags().show_pos) {
+ sign_char = '+';
+ } else if (conv.flags().sign_col) {
+ sign_char = ' ';
+ }
+
+ // Print nan/inf.
+ if (ConvertNonNumericFloats(sign_char, abs_v, conv, sink)) {
+ return true;
+ }
+
+ int precision = conv.precision() < 0 ? 6 : conv.precision();
+
+ int exp = 0;
+
+ auto decomposed = Decompose(abs_v);
+
+ Buffer buffer;
+
+ switch (conv.conv().id()) {
+ case ConversionChar::f:
+ case ConversionChar::F:
+ if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer,
+ nullptr)) {
+ return FallbackToSnprintf(v, conv, sink);
+ }
+ if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back();
+ break;
+
+ case ConversionChar::e:
+ case ConversionChar::E:
+ if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
+ &exp)) {
+ return FallbackToSnprintf(v, conv, sink);
+ }
+ if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back();
+ PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer);
+ break;
+
+ case ConversionChar::g:
+ case ConversionChar::G:
+ precision = std::max(0, precision - 1);
+ if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
+ &exp)) {
+ return FallbackToSnprintf(v, conv, sink);
+ }
+ if (precision + 1 > exp && exp >= -4) {
+ if (exp < 0) {
+ // Have 1.23456, needs 0.00123456
+ // Move the first digit
+ buffer.begin[1] = *buffer.begin;
+ // Add some zeros
+ for (; exp < -1; ++exp) *buffer.begin-- = '0';
+ *buffer.begin-- = '.';
+ *buffer.begin = '0';
+ } else if (exp > 0) {
+ // Have 1.23456, needs 1234.56
+ // Move the '.' exp positions to the right.
+ std::rotate(buffer.begin + 1, buffer.begin + 2,
+ buffer.begin + exp + 2);
+ }
+ exp = 0;
+ }
+ if (!conv.flags().alt) {
+ while (buffer.back() == '0') buffer.pop_back();
+ if (buffer.back() == '.') buffer.pop_back();
+ }
+ if (exp) PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer);
+ break;
+
+ case ConversionChar::a:
+ case ConversionChar::A:
+ return FallbackToSnprintf(v, conv, sink);
+
+ default:
+ return false;
+ }
+
+ WriteBufferToSink(sign_char,
+ string_view(buffer.begin, buffer.end - buffer.begin), conv,
+ sink);
+
+ return true;
+}
+
+} // namespace
+
+bool ConvertFloatImpl(long double v, const ConversionSpec &conv,
+ FormatSinkImpl *sink) {
+ return FloatToSink(v, conv, sink);
+}
+
+bool ConvertFloatImpl(float v, const ConversionSpec &conv,
+ FormatSinkImpl *sink) {
+ return FloatToSink(v, conv, sink);
+}
+
+bool ConvertFloatImpl(double v, const ConversionSpec &conv,
+ FormatSinkImpl *sink) {
+ return FloatToSink(v, conv, sink);
+}
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/float_conversion.h b/absl/strings/internal/str_format/float_conversion.h
new file mode 100644
index 00000000..280c471a
--- /dev/null
+++ b/absl/strings/internal/str_format/float_conversion.h
@@ -0,0 +1,23 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
+
+#include "absl/strings/internal/str_format/extension.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+bool ConvertFloatImpl(float v, const ConversionSpec &conv,
+ FormatSinkImpl *sink);
+
+bool ConvertFloatImpl(double v, const ConversionSpec &conv,
+ FormatSinkImpl *sink);
+
+bool ConvertFloatImpl(long double v, const ConversionSpec &conv,
+ FormatSinkImpl *sink);
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
diff --git a/absl/strings/internal/str_format/output.cc b/absl/strings/internal/str_format/output.cc
new file mode 100644
index 00000000..010bf341
--- /dev/null
+++ b/absl/strings/internal/str_format/output.cc
@@ -0,0 +1,72 @@
+// 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
+//
+// 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.
+
+#include "absl/strings/internal/str_format/output.h"
+
+#include <errno.h>
+#include <cstring>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+namespace {
+struct ClearErrnoGuard {
+ ClearErrnoGuard() : old_value(errno) { errno = 0; }
+ ~ClearErrnoGuard() {
+ if (!errno) errno = old_value;
+ }
+ int old_value;
+};
+} // namespace
+
+void BufferRawSink::Write(string_view v) {
+ size_t to_write = std::min(v.size(), size_);
+ std::memcpy(buffer_, v.data(), to_write);
+ buffer_ += to_write;
+ size_ -= to_write;
+ total_written_ += v.size();
+}
+
+void FILERawSink::Write(string_view v) {
+ while (!v.empty() && !error_) {
+ // Reset errno to zero in case the libc implementation doesn't set errno
+ // when a failure occurs.
+ ClearErrnoGuard guard;
+
+ if (size_t result = std::fwrite(v.data(), 1, v.size(), output_)) {
+ // Some progress was made.
+ count_ += result;
+ v.remove_prefix(result);
+ } else {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno) {
+ error_ = errno;
+ } else if (std::ferror(output_)) {
+ // Non-POSIX compliant libc implementations may not set errno, so we
+ // have check the streams error indicator.
+ error_ = EBADF;
+ } else {
+ // We're likely on a non-POSIX system that encountered EINTR but had no
+ // way of reporting it.
+ continue;
+ }
+ }
+ }
+}
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/output.h b/absl/strings/internal/str_format/output.h
new file mode 100644
index 00000000..0f3ab349
--- /dev/null
+++ b/absl/strings/internal/str_format/output.h
@@ -0,0 +1,103 @@
+// 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
+//
+// 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.
+//
+// Output extension hooks for the Format library.
+// `internal::InvokeFlush` calls the appropriate flush function for the
+// specified output argument.
+// `BufferRawSink` is a simple output sink for a char buffer. Used by SnprintF.
+// `FILERawSink` is a std::FILE* based sink. Used by PrintF and FprintF.
+
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
+
+#include <cstdio>
+#include <ostream>
+#include <string>
+
+#include "absl/base/port.h"
+#include "absl/strings/string_view.h"
+
+class Cord;
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+// RawSink implementation that writes into a char* buffer.
+// It will not overflow the buffer, but will keep the total count of chars
+// that would have been written.
+class BufferRawSink {
+ public:
+ BufferRawSink(char* buffer, size_t size) : buffer_(buffer), size_(size) {}
+
+ size_t total_written() const { return total_written_; }
+ void Write(string_view v);
+
+ private:
+ char* buffer_;
+ size_t size_;
+ size_t total_written_ = 0;
+};
+
+// RawSink implementation that writes into a FILE*.
+// It keeps track of the total number of bytes written and any error encountered
+// during the writes.
+class FILERawSink {
+ public:
+ explicit FILERawSink(std::FILE* output) : output_(output) {}
+
+ void Write(string_view v);
+
+ size_t count() const { return count_; }
+ int error() const { return error_; }
+
+ private:
+ std::FILE* output_;
+ int error_ = 0;
+ size_t count_ = 0;
+};
+
+// Provide RawSink integration with common types from the STL.
+inline void AbslFormatFlush(std::string* out, string_view s) {
+ out->append(s.data(), s.size());
+}
+inline void AbslFormatFlush(std::ostream* out, string_view s) {
+ out->write(s.data(), s.size());
+}
+
+template <class AbslCord, typename = typename std::enable_if<
+ std::is_same<AbslCord, ::Cord>::value>::type>
+inline void AbslFormatFlush(AbslCord* out, string_view s) {
+ out->Append(s);
+}
+
+inline void AbslFormatFlush(FILERawSink* sink, string_view v) {
+ sink->Write(v);
+}
+
+inline void AbslFormatFlush(BufferRawSink* sink, string_view v) {
+ sink->Write(v);
+}
+
+template <typename T>
+auto InvokeFlush(T* out, string_view s)
+ -> decltype(str_format_internal::AbslFormatFlush(out, s)) {
+ str_format_internal::AbslFormatFlush(out, s);
+}
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
diff --git a/absl/strings/internal/str_format/output_test.cc b/absl/strings/internal/str_format/output_test.cc
new file mode 100644
index 00000000..0a014cac
--- /dev/null
+++ b/absl/strings/internal/str_format/output_test.cc
@@ -0,0 +1,80 @@
+// 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
+//
+// 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.
+
+#include "absl/strings/internal/str_format/output.h"
+
+#include <sstream>
+#include <string>
+
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace {
+
+TEST(InvokeFlush, String) {
+ std::string str = "ABC";
+ str_format_internal::InvokeFlush(&str, "DEF");
+ EXPECT_EQ(str, "ABCDEF");
+
+#if UTIL_FORMAT_HAS_GLOBAL_STRING
+ std::string str2 = "ABC";
+ str_format_internal::InvokeFlush(&str2, "DEF");
+ EXPECT_EQ(str2, "ABCDEF");
+#endif // UTIL_FORMAT_HAS_GLOBAL_STRING
+}
+
+TEST(InvokeFlush, Stream) {
+ std::stringstream str;
+ str << "ABC";
+ str_format_internal::InvokeFlush(&str, "DEF");
+ EXPECT_EQ(str.str(), "ABCDEF");
+}
+
+TEST(BufferRawSink, Limits) {
+ char buf[16];
+ {
+ std::fill(std::begin(buf), std::end(buf), 'x');
+ str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
+ str_format_internal::InvokeFlush(&bufsink, "Hello World237");
+ EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx");
+ }
+ {
+ std::fill(std::begin(buf), std::end(buf), 'x');
+ str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
+ str_format_internal::InvokeFlush(&bufsink, "Hello World237237");
+ EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x");
+ }
+ {
+ std::fill(std::begin(buf), std::end(buf), 'x');
+ str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
+ str_format_internal::InvokeFlush(&bufsink, "Hello World");
+ str_format_internal::InvokeFlush(&bufsink, "237");
+ EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx");
+ }
+ {
+ std::fill(std::begin(buf), std::end(buf), 'x');
+ str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
+ str_format_internal::InvokeFlush(&bufsink, "Hello World");
+ str_format_internal::InvokeFlush(&bufsink, "237237");
+ EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x");
+ }
+}
+
+} // namespace
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc
new file mode 100644
index 00000000..119a711e
--- /dev/null
+++ b/absl/strings/internal/str_format/parser.cc
@@ -0,0 +1,311 @@
+#include "absl/strings/internal/str_format/parser.h"
+
+#include <assert.h>
+#include <string.h>
+#include <wchar.h>
+#include <cctype>
+#include <cstdint>
+
+#include <algorithm>
+#include <initializer_list>
+#include <limits>
+#include <ostream>
+#include <string>
+#include <unordered_set>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+namespace {
+
+bool CheckFastPathSetting(const UnboundConversion& conv) {
+ bool should_be_basic = !conv.flags.left && //
+ !conv.flags.show_pos && //
+ !conv.flags.sign_col && //
+ !conv.flags.alt && //
+ !conv.flags.zero && //
+ (conv.width.value() == -1) &&
+ (conv.precision.value() == -1);
+ if (should_be_basic != conv.flags.basic) {
+ fprintf(stderr,
+ "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d "
+ "width=%d precision=%d\n",
+ conv.flags.basic, conv.flags.left, conv.flags.show_pos,
+ conv.flags.sign_col, conv.flags.alt, conv.flags.zero,
+ conv.width.value(), conv.precision.value());
+ }
+ return should_be_basic == conv.flags.basic;
+}
+
+// Keep a single table for all the conversion chars and length modifiers.
+// We invert the length modifiers to make them negative so that we can easily
+// test for them.
+// Everything else is `none`, which is a negative constant.
+using CC = ConversionChar::Id;
+using LM = LengthMod::Id;
+static constexpr std::int8_t none = -128;
+static constexpr std::int8_t kIds[] = {
+ none, none, none, none, none, none, none, none, // 00-07
+ none, none, none, none, none, none, none, none, // 08-0f
+ none, none, none, none, none, none, none, none, // 10-17
+ none, none, none, none, none, none, none, none, // 18-1f
+ none, none, none, none, none, none, none, none, // 20-27
+ none, none, none, none, none, none, none, none, // 28-2f
+ none, none, none, none, none, none, none, none, // 30-37
+ none, none, none, none, none, none, none, none, // 38-3f
+ none, CC::A, none, CC::C, none, CC::E, CC::F, CC::G, // @ABCDEFG
+ none, none, none, none, ~LM::L, none, none, none, // HIJKLMNO
+ none, none, none, CC::S, none, none, none, none, // PQRSTUVW
+ CC::X, none, none, none, none, none, none, none, // XYZ[\]^_
+ none, CC::a, none, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg
+ ~LM::h, CC::i, ~LM::j, none, ~LM::l, none, CC::n, CC::o, // hijklmno
+ CC::p, ~LM::q, none, CC::s, ~LM::t, CC::u, none, none, // pqrstuvw
+ CC::x, none, ~LM::z, none, none, none, none, none, // xyz{|}~!
+ none, none, none, none, none, none, none, none, // 80-87
+ none, none, none, none, none, none, none, none, // 88-8f
+ none, none, none, none, none, none, none, none, // 90-97
+ none, none, none, none, none, none, none, none, // 98-9f
+ none, none, none, none, none, none, none, none, // a0-a7
+ none, none, none, none, none, none, none, none, // a8-af
+ none, none, none, none, none, none, none, none, // b0-b7
+ none, none, none, none, none, none, none, none, // b8-bf
+ none, none, none, none, none, none, none, none, // c0-c7
+ none, none, none, none, none, none, none, none, // c8-cf
+ none, none, none, none, none, none, none, none, // d0-d7
+ none, none, none, none, none, none, none, none, // d8-df
+ none, none, none, none, none, none, none, none, // e0-e7
+ none, none, none, none, none, none, none, none, // e8-ef
+ none, none, none, none, none, none, none, none, // f0-f7
+ none, none, none, none, none, none, none, none, // f8-ff
+};
+
+template <bool is_positional>
+bool ConsumeConversion(string_view *src, UnboundConversion *conv,
+ int *next_arg) {
+ const char *pos = src->data();
+ const char *const end = pos + src->size();
+ char c;
+ // Read the next char into `c` and update `pos`. Returns false if there are
+ // no more chars to read.
+#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR() \
+ do { \
+ if (ABSL_PREDICT_FALSE(pos == end)) return false; \
+ c = *pos++; \
+ } while (0)
+
+ const auto parse_digits = [&] {
+ int digits = c - '0';
+ // We do not want to overflow `digits` so we consume at most digits10
+ // digits. If there are more digits the parsing will fail later on when the
+ // digit doesn't match the expected characters.
+ int num_digits = std::numeric_limits<int>::digits10;
+ for (;;) {
+ if (ABSL_PREDICT_FALSE(pos == end || !num_digits)) break;
+ c = *pos++;
+ if (!std::isdigit(c)) break;
+ --num_digits;
+ digits = 10 * digits + c - '0';
+ }
+ return digits;
+ };
+
+ if (is_positional) {
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false;
+ conv->arg_position = parse_digits();
+ assert(conv->arg_position > 0);
+ if (ABSL_PREDICT_FALSE(c != '$')) return false;
+ }
+
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+
+ // We should start with the basic flag on.
+ assert(conv->flags.basic);
+
+ // Any non alpha character makes this conversion not basic.
+ // This includes flags (-+ #0), width (1-9, *) or precision (.).
+ // All conversion characters and length modifiers are alpha characters.
+ if (c < 'A') {
+ conv->flags.basic = false;
+
+ for (; c <= '0';) {
+ // FIXME: We might be able to speed this up reusing the kIds lookup table
+ // from above.
+ // It might require changing Flags to be a plain integer where we can |= a
+ // value.
+ switch (c) {
+ case '-':
+ conv->flags.left = true;
+ break;
+ case '+':
+ conv->flags.show_pos = true;
+ break;
+ case ' ':
+ conv->flags.sign_col = true;
+ break;
+ case '#':
+ conv->flags.alt = true;
+ break;
+ case '0':
+ conv->flags.zero = true;
+ break;
+ default:
+ goto flags_done;
+ }
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ }
+flags_done:
+
+ if (c <= '9') {
+ if (c >= '0') {
+ int maybe_width = parse_digits();
+ if (!is_positional && c == '$') {
+ if (ABSL_PREDICT_FALSE(*next_arg != 0)) return false;
+ // Positional conversion.
+ *next_arg = -1;
+ conv->flags = Flags();
+ conv->flags.basic = true;
+ return ConsumeConversion<true>(src, conv, next_arg);
+ }
+ conv->width.set_value(maybe_width);
+ } else if (c == '*') {
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (is_positional) {
+ if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false;
+ conv->width.set_from_arg(parse_digits());
+ if (ABSL_PREDICT_FALSE(c != '$')) return false;
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ } else {
+ conv->width.set_from_arg(++*next_arg);
+ }
+ }
+ }
+
+ if (c == '.') {
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (std::isdigit(c)) {
+ conv->precision.set_value(parse_digits());
+ } else if (c == '*') {
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (is_positional) {
+ if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false;
+ conv->precision.set_from_arg(parse_digits());
+ if (c != '$') return false;
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ } else {
+ conv->precision.set_from_arg(++*next_arg);
+ }
+ } else {
+ conv->precision.set_value(0);
+ }
+ }
+ }
+
+ std::int8_t id = kIds[static_cast<unsigned char>(c)];
+
+ if (id < 0) {
+ if (ABSL_PREDICT_FALSE(id == none)) return false;
+
+ // It is a length modifier.
+ using str_format_internal::LengthMod;
+ LengthMod length_mod = LengthMod::FromId(static_cast<LM>(~id));
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (c == 'h' && length_mod.id() == LengthMod::h) {
+ conv->length_mod = LengthMod::FromId(LengthMod::hh);
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ } else if (c == 'l' && length_mod.id() == LengthMod::l) {
+ conv->length_mod = LengthMod::FromId(LengthMod::ll);
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ } else {
+ conv->length_mod = length_mod;
+ }
+ id = kIds[static_cast<unsigned char>(c)];
+ if (ABSL_PREDICT_FALSE(id < 0)) return false;
+ }
+
+ assert(CheckFastPathSetting(*conv));
+ (void)(&CheckFastPathSetting);
+
+ conv->conv = ConversionChar::FromId(static_cast<CC>(id));
+ if (!is_positional) conv->arg_position = ++*next_arg;
+ *src = string_view(pos, end - pos);
+ return true;
+}
+
+} // namespace
+
+bool ConsumeUnboundConversion(string_view *src, UnboundConversion *conv,
+ int *next_arg) {
+ if (*next_arg < 0) return ConsumeConversion<true>(src, conv, next_arg);
+ return ConsumeConversion<false>(src, conv, next_arg);
+}
+
+struct ParsedFormatBase::ParsedFormatConsumer {
+ explicit ParsedFormatConsumer(ParsedFormatBase *parsedformat)
+ : parsed(parsedformat), data_pos(parsedformat->data_.get()) {}
+
+ bool Append(string_view s) {
+ if (s.empty()) return true;
+
+ size_t text_end = AppendText(s);
+
+ if (!parsed->items_.empty() && !parsed->items_.back().is_conversion) {
+ // Let's extend the existing text run.
+ parsed->items_.back().text_end = text_end;
+ } else {
+ // Let's make a new text run.
+ parsed->items_.push_back({false, text_end, {}});
+ }
+ return true;
+ }
+
+ bool ConvertOne(const UnboundConversion &conv, string_view s) {
+ size_t text_end = AppendText(s);
+ parsed->items_.push_back({true, text_end, conv});
+ return true;
+ }
+
+ size_t AppendText(string_view s) {
+ memcpy(data_pos, s.data(), s.size());
+ data_pos += s.size();
+ return static_cast<size_t>(data_pos - parsed->data_.get());
+ }
+
+ ParsedFormatBase *parsed;
+ char* data_pos;
+};
+
+ParsedFormatBase::ParsedFormatBase(string_view format, bool allow_ignored,
+ std::initializer_list<Conv> convs)
+ : data_(format.empty() ? nullptr : new char[format.size()]) {
+ has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) ||
+ !MatchesConversions(allow_ignored, convs);
+}
+
+bool ParsedFormatBase::MatchesConversions(
+ bool allow_ignored, std::initializer_list<Conv> convs) const {
+ std::unordered_set<int> used;
+ auto add_if_valid_conv = [&](int pos, char c) {
+ if (static_cast<size_t>(pos) > convs.size() ||
+ !Contains(convs.begin()[pos - 1], c))
+ return false;
+ used.insert(pos);
+ return true;
+ };
+ for (const ConversionItem &item : items_) {
+ if (!item.is_conversion) continue;
+ auto &conv = item.conv;
+ if (conv.precision.is_from_arg() &&
+ !add_if_valid_conv(conv.precision.get_from_arg(), '*'))
+ return false;
+ if (conv.width.is_from_arg() &&
+ !add_if_valid_conv(conv.width.get_from_arg(), '*'))
+ return false;
+ if (!add_if_valid_conv(conv.arg_position, conv.conv.Char())) return false;
+ }
+ return used.size() == convs.size() || allow_ignored;
+}
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h
new file mode 100644
index 00000000..9842c117
--- /dev/null
+++ b/absl/strings/internal/str_format/parser.h
@@ -0,0 +1,294 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <cassert>
+#include <initializer_list>
+#include <iosfwd>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "absl/strings/internal/str_format/checker.h"
+#include "absl/strings/internal/str_format/extension.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+// The analyzed properties of a single specified conversion.
+struct UnboundConversion {
+ UnboundConversion()
+ : flags() /* This is required to zero all the fields of flags. */ {
+ flags.basic = true;
+ }
+
+ class InputValue {
+ public:
+ void set_value(int value) {
+ assert(value >= 0);
+ value_ = value;
+ }
+ int value() const { return value_; }
+
+ // Marks the value as "from arg". aka the '*' format.
+ // Requires `value >= 1`.
+ // When set, is_from_arg() return true and get_from_arg() returns the
+ // original value.
+ // `value()`'s return value is unspecfied in this state.
+ void set_from_arg(int value) {
+ assert(value > 0);
+ value_ = -value - 1;
+ }
+ bool is_from_arg() const { return value_ < -1; }
+ int get_from_arg() const {
+ assert(is_from_arg());
+ return -value_ - 1;
+ }
+
+ private:
+ int value_ = -1;
+ };
+
+ // No need to initialize. It will always be set in the parser.
+ int arg_position;
+
+ InputValue width;
+ InputValue precision;
+
+ Flags flags;
+ LengthMod length_mod;
+ ConversionChar conv;
+};
+
+// Consume conversion spec prefix (not including '%') of '*src' if valid.
+// Examples of valid specs would be e.g.: "s", "d", "-12.6f".
+// If valid, the front of src is advanced such that src becomes the
+// part following the conversion spec, and the spec part is broken down and
+// returned in 'conv'.
+// If invalid, returns false and leaves 'src' unmodified.
+// For example:
+// Given "d9", returns "d", and leaves src="9",
+// Given "!", returns "" and leaves src="!".
+bool ConsumeUnboundConversion(string_view* src, UnboundConversion* conv,
+ int* next_arg);
+
+// Parse the format string provided in 'src' and pass the identified items into
+// 'consumer'.
+// Text runs will be passed by calling
+// Consumer::Append(string_view);
+// ConversionItems will be passed by calling
+// Consumer::ConvertOne(UnboundConversion, string_view);
+// In the case of ConvertOne, the string_view that is passed is the
+// portion of the format string corresponding to the conversion, not including
+// the leading %. On success, it returns true. On failure, it stops and returns
+// false.
+template <typename Consumer>
+bool ParseFormatString(string_view src, Consumer consumer) {
+ int next_arg = 0;
+ while (!src.empty()) {
+ const char* percent =
+ static_cast<const char*>(memchr(src.data(), '%', src.size()));
+ if (!percent) {
+ // We found the last substring.
+ return consumer.Append(src);
+ }
+ // We found a percent, so push the text run then process the percent.
+ size_t percent_loc = percent - src.data();
+ if (!consumer.Append(string_view(src.data(), percent_loc))) return false;
+ if (percent + 1 >= src.data() + src.size()) return false;
+
+ UnboundConversion conv;
+
+ switch (percent[1]) {
+ case '%':
+ if (!consumer.Append("%")) return false;
+ src.remove_prefix(percent_loc + 2);
+ continue;
+
+#define PARSER_CASE(ch) \
+ case #ch[0]: \
+ src.remove_prefix(percent_loc + 2); \
+ conv.conv = ConversionChar::FromId(ConversionChar::ch); \
+ conv.arg_position = ++next_arg; \
+ break;
+ ABSL_CONVERSION_CHARS_EXPAND_(PARSER_CASE, );
+#undef PARSER_CASE
+
+ default:
+ src.remove_prefix(percent_loc + 1);
+ if (!ConsumeUnboundConversion(&src, &conv, &next_arg)) return false;
+ break;
+ }
+ if (next_arg == 0) {
+ // This indicates an error in the format std::string.
+ // The only way to get next_arg == 0 is to have a positional argument
+ // first which sets next_arg to -1 and then a non-positional argument
+ // which does ++next_arg.
+ // Checking here seems to be the cheapeast place to do it.
+ return false;
+ }
+ if (!consumer.ConvertOne(
+ conv, string_view(percent + 1, src.data() - (percent + 1)))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Always returns true, or fails to compile in a constexpr context if s does not
+// point to a constexpr char array.
+constexpr bool EnsureConstexpr(string_view s) {
+ return s.empty() || s[0] == s[0];
+}
+
+class ParsedFormatBase {
+ public:
+ explicit ParsedFormatBase(string_view format, bool allow_ignored,
+ std::initializer_list<Conv> convs);
+
+ ParsedFormatBase(const ParsedFormatBase& other) { *this = other; }
+
+ ParsedFormatBase(ParsedFormatBase&& other) { *this = std::move(other); }
+
+ ParsedFormatBase& operator=(const ParsedFormatBase& other) {
+ if (this == &other) return *this;
+ has_error_ = other.has_error_;
+ items_ = other.items_;
+ size_t text_size = items_.empty() ? 0 : items_.back().text_end;
+ data_.reset(new char[text_size]);
+ memcpy(data_.get(), other.data_.get(), text_size);
+ return *this;
+ }
+
+ ParsedFormatBase& operator=(ParsedFormatBase&& other) {
+ if (this == &other) return *this;
+ has_error_ = other.has_error_;
+ data_ = std::move(other.data_);
+ items_ = std::move(other.items_);
+ // Reset the vector to make sure the invariants hold.
+ other.items_.clear();
+ return *this;
+ }
+
+ template <typename Consumer>
+ bool ProcessFormat(Consumer consumer) const {
+ const char* const base = data_.get();
+ string_view text(base, 0);
+ for (const auto& item : items_) {
+ const char* const end = text.data() + text.size();
+ text = string_view(end, (base + item.text_end) - end);
+ if (item.is_conversion) {
+ if (!consumer.ConvertOne(item.conv, text)) return false;
+ } else {
+ if (!consumer.Append(text)) return false;
+ }
+ }
+ return !has_error_;
+ }
+
+ bool has_error() const { return has_error_; }
+
+ private:
+ // Returns whether the conversions match and if !allow_ignored it verifies
+ // that all conversions are used by the format.
+ bool MatchesConversions(bool allow_ignored,
+ std::initializer_list<Conv> convs) const;
+
+ struct ParsedFormatConsumer;
+
+ struct ConversionItem {
+ bool is_conversion;
+ // Points to the past-the-end location of this element in the data_ array.
+ size_t text_end;
+ UnboundConversion conv;
+ };
+
+ bool has_error_;
+ std::unique_ptr<char[]> data_;
+ std::vector<ConversionItem> items_;
+};
+
+
+// A value type representing a preparsed format. These can be created, copied
+// around, and reused to speed up formatting loops.
+// The user must specify through the template arguments the conversion
+// characters used in the format. This will be checked at compile time.
+//
+// This class uses Conv enum values to specify each argument.
+// This allows for more flexibility as you can specify multiple possible
+// conversion characters for each argument.
+// ParsedFormat<char...> is a simplified alias for when the user only
+// needs to specify a single conversion character for each argument.
+//
+// Example:
+// // Extended format supports multiple characters per argument:
+// using MyFormat = ExtendedParsedFormat<Conv::d | Conv::x>;
+// MyFormat GetFormat(bool use_hex) {
+// if (use_hex) return MyFormat("foo %x bar");
+// return MyFormat("foo %d bar");
+// }
+// // 'format' can be used with any value that supports 'd' and 'x',
+// // like `int`.
+// auto format = GetFormat(use_hex);
+// value = StringF(format, i);
+//
+// This class also supports runtime format checking with the ::New() and
+// ::NewAllowIgnored() factory functions.
+// This is the only API that allows the user to pass a runtime specified format
+// string. These factory functions will return NULL if the format does not match
+// the conversions requested by the user.
+template <str_format_internal::Conv... C>
+class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase {
+ public:
+ explicit ExtendedParsedFormat(string_view format)
+#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+ __attribute__((
+ enable_if(str_format_internal::EnsureConstexpr(format),
+ "Format std::string is not constexpr."),
+ enable_if(str_format_internal::ValidFormatImpl<C...>(format),
+ "Format specified does not match the template arguments.")))
+#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+ : ExtendedParsedFormat(format, false) {
+ }
+
+ // ExtendedParsedFormat factory function.
+ // The user still has to specify the conversion characters, but they will not
+ // be checked at compile time. Instead, it will be checked at runtime.
+ // This delays the checking to runtime, but allows the user to pass
+ // dynamically sourced formats.
+ // It returns NULL if the format does not match the conversion characters.
+ // The user is responsible for checking the return value before using it.
+ //
+ // The 'New' variant will check that all the specified arguments are being
+ // consumed by the format and return NULL if any argument is being ignored.
+ // The 'NewAllowIgnored' variant will not verify this and will allow formats
+ // that ignore arguments.
+ static std::unique_ptr<ExtendedParsedFormat> New(string_view format) {
+ return New(format, false);
+ }
+ static std::unique_ptr<ExtendedParsedFormat> NewAllowIgnored(
+ string_view format) {
+ return New(format, true);
+ }
+
+ private:
+ static std::unique_ptr<ExtendedParsedFormat> New(string_view format,
+ bool allow_ignored) {
+ std::unique_ptr<ExtendedParsedFormat> conv(
+ new ExtendedParsedFormat(format, allow_ignored));
+ if (conv->has_error()) return nullptr;
+ return conv;
+ }
+
+ ExtendedParsedFormat(string_view s, bool allow_ignored)
+ : ParsedFormatBase(s, allow_ignored, {C...}) {}
+};
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc
new file mode 100644
index 00000000..14d90344
--- /dev/null
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -0,0 +1,391 @@
+#include "absl/strings/internal/str_format/parser.h"
+
+#include <string.h>
+#include "gtest/gtest.h"
+#include "absl/base/macros.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+namespace {
+
+TEST(LengthModTest, Names) {
+ struct Expectation {
+ int line;
+ LengthMod::Id id;
+ const char *name;
+ };
+ const Expectation kExpect[] = {
+ {__LINE__, LengthMod::none, "" },
+ {__LINE__, LengthMod::h, "h" },
+ {__LINE__, LengthMod::hh, "hh"},
+ {__LINE__, LengthMod::l, "l" },
+ {__LINE__, LengthMod::ll, "ll"},
+ {__LINE__, LengthMod::L, "L" },
+ {__LINE__, LengthMod::j, "j" },
+ {__LINE__, LengthMod::z, "z" },
+ {__LINE__, LengthMod::t, "t" },
+ {__LINE__, LengthMod::q, "q" },
+ };
+ EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), LengthMod::kNumValues);
+ for (auto e : kExpect) {
+ SCOPED_TRACE(e.line);
+ LengthMod mod = LengthMod::FromId(e.id);
+ EXPECT_EQ(e.id, mod.id());
+ EXPECT_EQ(e.name, mod.name());
+ }
+}
+
+TEST(ConversionCharTest, Names) {
+ struct Expectation {
+ ConversionChar::Id id;
+ char name;
+ };
+ // clang-format off
+ const Expectation kExpect[] = {
+#define X(c) {ConversionChar::c, #c[0]}
+ X(c), X(C), X(s), X(S), // text
+ X(d), X(i), X(o), X(u), X(x), X(X), // int
+ X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A), // float
+ X(n), X(p), // misc
+#undef X
+ {ConversionChar::none, '\0'},
+ };
+ // clang-format on
+ EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), ConversionChar::kNumValues);
+ for (auto e : kExpect) {
+ SCOPED_TRACE(e.name);
+ ConversionChar v = ConversionChar::FromId(e.id);
+ EXPECT_EQ(e.id, v.id());
+ EXPECT_EQ(e.name, v.Char());
+ }
+}
+
+class ConsumeUnboundConversionTest : public ::testing::Test {
+ public:
+ typedef UnboundConversion Props;
+ string_view Consume(string_view* src) {
+ int next = 0;
+ const char* prev_begin = src->data();
+ o = UnboundConversion(); // refresh
+ ConsumeUnboundConversion(src, &o, &next);
+ return {prev_begin, static_cast<size_t>(src->data() - prev_begin)};
+ }
+
+ bool Run(const char *fmt, bool force_positional = false) {
+ string_view src = fmt;
+ int next = force_positional ? -1 : 0;
+ o = UnboundConversion(); // refresh
+ return ConsumeUnboundConversion(&src, &o, &next) && src.empty();
+ }
+ UnboundConversion o;
+};
+
+TEST_F(ConsumeUnboundConversionTest, ConsumeSpecification) {
+ struct Expectation {
+ int line;
+ string_view src;
+ string_view out;
+ string_view src_post;
+ };
+ const Expectation kExpect[] = {
+ {__LINE__, "", "", "" },
+ {__LINE__, "b", "", "b" }, // 'b' is invalid
+ {__LINE__, "ba", "", "ba"}, // 'b' is invalid
+ {__LINE__, "l", "", "l" }, // just length mod isn't okay
+ {__LINE__, "d", "d", "" }, // basic
+ {__LINE__, "d ", "d", " " }, // leave suffix
+ {__LINE__, "dd", "d", "d" }, // don't be greedy
+ {__LINE__, "d9", "d", "9" }, // leave non-space suffix
+ {__LINE__, "dzz", "d", "zz"}, // length mod as suffix
+ {__LINE__, "1$*2$d", "1$*2$d", "" }, // arg indexing and * allowed.
+ {__LINE__, "0-14.3hhd", "0-14.3hhd", ""}, // precision, width
+ {__LINE__, " 0-+#14.3hhd", " 0-+#14.3hhd", ""}, // flags
+ };
+ for (const auto& e : kExpect) {
+ SCOPED_TRACE(e.line);
+ string_view src = e.src;
+ EXPECT_EQ(e.src, src);
+ string_view out = Consume(&src);
+ EXPECT_EQ(e.out, out);
+ EXPECT_EQ(e.src_post, src);
+ }
+}
+
+TEST_F(ConsumeUnboundConversionTest, BasicConversion) {
+ EXPECT_FALSE(Run(""));
+ EXPECT_FALSE(Run("z"));
+
+ EXPECT_FALSE(Run("dd")); // no excess allowed
+
+ EXPECT_TRUE(Run("d"));
+ EXPECT_EQ('d', o.conv.Char());
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_LT(o.width.value(), 0);
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_LT(o.precision.value(), 0);
+ EXPECT_EQ(1, o.arg_position);
+ EXPECT_EQ(LengthMod::none, o.length_mod.id());
+}
+
+TEST_F(ConsumeUnboundConversionTest, ArgPosition) {
+ EXPECT_TRUE(Run("d"));
+ EXPECT_EQ(1, o.arg_position);
+ EXPECT_TRUE(Run("3$d"));
+ EXPECT_EQ(3, o.arg_position);
+ EXPECT_TRUE(Run("1$d"));
+ EXPECT_EQ(1, o.arg_position);
+ EXPECT_TRUE(Run("1$d", true));
+ EXPECT_EQ(1, o.arg_position);
+ EXPECT_TRUE(Run("123$d"));
+ EXPECT_EQ(123, o.arg_position);
+ EXPECT_TRUE(Run("123$d", true));
+ EXPECT_EQ(123, o.arg_position);
+ EXPECT_TRUE(Run("10$d"));
+ EXPECT_EQ(10, o.arg_position);
+ EXPECT_TRUE(Run("10$d", true));
+ EXPECT_EQ(10, o.arg_position);
+
+ // Position can't be zero.
+ EXPECT_FALSE(Run("0$d"));
+ EXPECT_FALSE(Run("0$d", true));
+ EXPECT_FALSE(Run("1$*0$d"));
+ EXPECT_FALSE(Run("1$.*0$d"));
+
+ // Position can't start with a zero digit at all. That is not a 'decimal'.
+ EXPECT_FALSE(Run("01$p"));
+ EXPECT_FALSE(Run("01$p", true));
+ EXPECT_FALSE(Run("1$*01$p"));
+ EXPECT_FALSE(Run("1$.*01$p"));
+}
+
+TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) {
+ EXPECT_TRUE(Run("14d"));
+ EXPECT_EQ('d', o.conv.Char());
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_EQ(14, o.width.value());
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_LT(o.precision.value(), 0);
+
+ EXPECT_TRUE(Run("14.d"));
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_EQ(14, o.width.value());
+ EXPECT_EQ(0, o.precision.value());
+
+ EXPECT_TRUE(Run(".d"));
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_LT(o.width.value(), 0);
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_EQ(0, o.precision.value());
+
+ EXPECT_TRUE(Run(".5d"));
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_LT(o.width.value(), 0);
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_EQ(5, o.precision.value());
+
+ EXPECT_TRUE(Run(".0d"));
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_LT(o.width.value(), 0);
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_EQ(0, o.precision.value());
+
+ EXPECT_TRUE(Run("14.5d"));
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_EQ(14, o.width.value());
+ EXPECT_EQ(5, o.precision.value());
+
+ EXPECT_TRUE(Run("*.*d"));
+ EXPECT_TRUE(o.width.is_from_arg());
+ EXPECT_EQ(1, o.width.get_from_arg());
+ EXPECT_TRUE(o.precision.is_from_arg());
+ EXPECT_EQ(2, o.precision.get_from_arg());
+ EXPECT_EQ(3, o.arg_position);
+
+ EXPECT_TRUE(Run("*d"));
+ EXPECT_TRUE(o.width.is_from_arg());
+ EXPECT_EQ(1, o.width.get_from_arg());
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_LT(o.precision.value(), 0);
+ EXPECT_EQ(2, o.arg_position);
+
+ EXPECT_TRUE(Run(".*d"));
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_LT(o.width.value(), 0);
+ EXPECT_TRUE(o.precision.is_from_arg());
+ EXPECT_EQ(1, o.precision.get_from_arg());
+ EXPECT_EQ(2, o.arg_position);
+
+ // mixed implicit and explicit: didn't specify arg position.
+ EXPECT_FALSE(Run("*23$.*34$d"));
+
+ EXPECT_TRUE(Run("12$*23$.*34$d"));
+ EXPECT_EQ(12, o.arg_position);
+ EXPECT_TRUE(o.width.is_from_arg());
+ EXPECT_EQ(23, o.width.get_from_arg());
+ EXPECT_TRUE(o.precision.is_from_arg());
+ EXPECT_EQ(34, o.precision.get_from_arg());
+
+ EXPECT_TRUE(Run("2$*5$.*9$d"));
+ EXPECT_EQ(2, o.arg_position);
+ EXPECT_TRUE(o.width.is_from_arg());
+ EXPECT_EQ(5, o.width.get_from_arg());
+ EXPECT_TRUE(o.precision.is_from_arg());
+ EXPECT_EQ(9, o.precision.get_from_arg());
+
+ EXPECT_FALSE(Run(".*0$d")) << "no arg 0";
+
+ // Large values
+ EXPECT_TRUE(Run("999999999.999999999d"));
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_EQ(999999999, o.width.value());
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_EQ(999999999, o.precision.value());
+
+ EXPECT_FALSE(Run("1000000000.999999999d"));
+ EXPECT_FALSE(Run("999999999.1000000000d"));
+}
+
+TEST_F(ConsumeUnboundConversionTest, Flags) {
+ static const char kAllFlags[] = "-+ #0";
+ static const int kNumFlags = ABSL_ARRAYSIZE(kAllFlags) - 1;
+ for (int rev = 0; rev < 2; ++rev) {
+ for (int i = 0; i < 1 << kNumFlags; ++i) {
+ std::string fmt;
+ for (int k = 0; k < kNumFlags; ++k)
+ if ((i >> k) & 1) fmt += kAllFlags[k];
+ // flag order shouldn't matter
+ if (rev == 1) { std::reverse(fmt.begin(), fmt.end()); }
+ fmt += 'd';
+ SCOPED_TRACE(fmt);
+ EXPECT_TRUE(Run(fmt.c_str()));
+ EXPECT_EQ(fmt.find('-') == std::string::npos, !o.flags.left);
+ EXPECT_EQ(fmt.find('+') == std::string::npos, !o.flags.show_pos);
+ EXPECT_EQ(fmt.find(' ') == std::string::npos, !o.flags.sign_col);
+ EXPECT_EQ(fmt.find('#') == std::string::npos, !o.flags.alt);
+ EXPECT_EQ(fmt.find('0') == std::string::npos, !o.flags.zero);
+ }
+ }
+}
+
+TEST_F(ConsumeUnboundConversionTest, BasicFlag) {
+ // Flag is on
+ for (const char* fmt : {"d", "llx", "G", "1$X"}) {
+ SCOPED_TRACE(fmt);
+ EXPECT_TRUE(Run(fmt));
+ EXPECT_TRUE(o.flags.basic);
+ }
+
+ // Flag is off
+ for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) {
+ SCOPED_TRACE(fmt);
+ EXPECT_TRUE(Run(fmt));
+ EXPECT_FALSE(o.flags.basic);
+ }
+}
+
+struct SummarizeConsumer {
+ std::string* out;
+ explicit SummarizeConsumer(std::string* out) : out(out) {}
+
+ bool Append(string_view s) {
+ *out += "[" + std::string(s) + "]";
+ return true;
+ }
+
+ bool ConvertOne(const UnboundConversion& conv, string_view s) {
+ *out += "{";
+ *out += std::string(s);
+ *out += ":";
+ *out += std::to_string(conv.arg_position) + "$";
+ if (conv.width.is_from_arg()) {
+ *out += std::to_string(conv.width.get_from_arg()) + "$*";
+ }
+ if (conv.precision.is_from_arg()) {
+ *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
+ }
+ *out += conv.conv.Char();
+ *out += "}";
+ return true;
+ }
+};
+
+std::string SummarizeParsedFormat(const ParsedFormatBase& pc) {
+ std::string out;
+ if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!";
+ return out;
+}
+
+class ParsedFormatTest : public testing::Test {};
+
+TEST_F(ParsedFormatTest, ValueSemantics) {
+ ParsedFormatBase p1({}, true, {}); // empty format
+ EXPECT_EQ("", SummarizeParsedFormat(p1));
+
+ ParsedFormatBase p2 = p1; // copy construct (empty)
+ EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
+
+ p1 = ParsedFormatBase("hello%s", true, {Conv::s}); // move assign
+ EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p1));
+
+ ParsedFormatBase p3 = p1; // copy construct (nonempty)
+ EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p3));
+
+ using std::swap;
+ swap(p1, p2);
+ EXPECT_EQ("", SummarizeParsedFormat(p1));
+ EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p2));
+ swap(p1, p2); // undo
+
+ p2 = p1; // copy assign
+ EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
+}
+
+struct ExpectParse {
+ const char* in;
+ std::initializer_list<Conv> conv_set;
+ const char* out;
+};
+
+TEST_F(ParsedFormatTest, Parsing) {
+ // Parse should be equivalent to that obtained by ConversionParseIterator.
+ // No need to retest the parsing edge cases here.
+ const ExpectParse kExpect[] = {
+ {"", {}, ""},
+ {"ab", {}, "[ab]"},
+ {"a%d", {Conv::d}, "[a]{d:1$d}"},
+ {"a%+d", {Conv::d}, "[a]{+d:1$d}"},
+ {"a% d", {Conv::d}, "[a]{ d:1$d}"},
+ {"a%b %d", {}, "[a]!"}, // stop after error
+ };
+ for (const auto& e : kExpect) {
+ SCOPED_TRACE(e.in);
+ EXPECT_EQ(e.out,
+ SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
+ }
+}
+
+TEST_F(ParsedFormatTest, ParsingFlagOrder) {
+ const ExpectParse kExpect[] = {
+ {"a%+ 0d", {Conv::d}, "[a]{+ 0d:1$d}"},
+ {"a%+0 d", {Conv::d}, "[a]{+0 d:1$d}"},
+ {"a%0+ d", {Conv::d}, "[a]{0+ d:1$d}"},
+ {"a% +0d", {Conv::d}, "[a]{ +0d:1$d}"},
+ {"a%0 +d", {Conv::d}, "[a]{0 +d:1$d}"},
+ {"a% 0+d", {Conv::d}, "[a]{ 0+d:1$d}"},
+ {"a%+ 0+d", {Conv::d}, "[a]{+ 0+d:1$d}"},
+ };
+ for (const auto& e : kExpect) {
+ SCOPED_TRACE(e.in);
+ EXPECT_EQ(e.out,
+ SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
+ }
+}
+
+} // namespace
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h
index 5834403e..90b06d6f 100644
--- a/absl/strings/internal/str_join_internal.h
+++ b/absl/strings/internal/str_join_internal.h
@@ -43,7 +43,7 @@
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
//
@@ -190,7 +190,7 @@ struct DefaultFormatter<std::unique_ptr<ValueType>>
//
// The main joining algorithm. This simply joins the elements in the given
-// iterator range, each separated by the given separator, into an output std::string,
+// iterator range, each separated by the given separator, into an output string,
// and formats each element using the provided Formatter object.
template <typename Iterator, typename Formatter>
std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
@@ -206,20 +206,20 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
}
// A joining algorithm that's optimized for a forward iterator range of
-// std::string-like objects that do not need any additional formatting. This is to
-// optimize the common case of joining, say, a std::vector<std::string> or a
+// string-like objects that do not need any additional formatting. This is to
+// optimize the common case of joining, say, a std::vector<string> or a
// std::vector<absl::string_view>.
//
// This is an overload of the previous JoinAlgorithm() function. Here the
// Formatter argument is of type NoFormatter. Since NoFormatter is an internal
// type, this overload is only invoked when strings::Join() is called with a
-// range of std::string-like objects (e.g., std::string, absl::string_view), and an
+// range of string-like objects (e.g., string, absl::string_view), and an
// explicit Formatter argument was NOT specified.
//
// The optimization is that the needed space will be reserved in the output
-// std::string to avoid the need to resize while appending. To do this, the iterator
+// string to avoid the need to resize while appending. To do this, the iterator
// range will be traversed twice: once to calculate the total needed size, and
-// then again to copy the elements and delimiters to the output std::string.
+// then again to copy the elements and delimiters to the output string.
template <typename Iterator,
typename = typename std::enable_if<std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
@@ -307,7 +307,7 @@ std::string JoinRange(const Range& range, absl::string_view separator) {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index e2d2c6b0..2300193a 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -47,12 +47,12 @@
#endif // _GLIBCXX_DEBUG
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// This class is implicitly constructible from everything that absl::string_view
// is implicitly constructible from. If it's constructed from a temporary
-// std::string, the data is moved into a data member so its lifetime matches that of
+// string, the data is moved into a data member so its lifetime matches that of
// the ConvertibleToStringView instance.
class ConvertibleToStringView {
public:
@@ -103,7 +103,7 @@ ConvertibleToStringView(std::string&& s) // NOLINT(runtime/explicit)
absl::string_view value_;
};
-// An iterator that enumerates the parts of a std::string from a Splitter. The text
+// An iterator that enumerates the parts of a string from a Splitter. The text
// to be split, the Delimiter, and the Predicate are all taken from the given
// Splitter object. Iterators may only be compared if they refer to the same
// Splitter instance.
@@ -160,7 +160,7 @@ class SplitIterator {
}
const absl::string_view text = splitter_->text();
const absl::string_view d = delimiter_.Find(text, pos_);
- if (d.data() == text.end()) state_ = kLastState;
+ if (d.data() == text.data() + text.size()) state_ = kLastState;
curr_ = text.substr(pos_, d.data() - (text.data() + pos_));
pos_ += curr_.size() + d.size();
} while (!predicate_(curr_));
@@ -229,14 +229,31 @@ struct IsInitializerList
// compiled in C++11 will get an error due to ambiguous conversion paths (in
// C++11 std::vector<T>::operator= is overloaded to take either a std::vector<T>
// or an std::initializer_list<T>).
+
+template <typename C, bool has_value_type, bool has_mapped_type>
+struct SplitterIsConvertibleToImpl : std::false_type {};
+
+template <typename C>
+struct SplitterIsConvertibleToImpl<C, true, false>
+ : std::is_constructible<typename C::value_type, absl::string_view> {};
+
+template <typename C>
+struct SplitterIsConvertibleToImpl<C, true, true>
+ : absl::conjunction<
+ std::is_constructible<typename C::key_type, absl::string_view>,
+ std::is_constructible<typename C::mapped_type, absl::string_view>> {};
+
template <typename C>
struct SplitterIsConvertibleTo
- : std::enable_if<
+ : SplitterIsConvertibleToImpl<
+ C,
#ifdef _GLIBCXX_DEBUG
!IsStrictlyBaseOfAndConvertibleToSTLContainer<C>::value &&
#endif // _GLIBCXX_DEBUG
- !IsInitializerList<C>::value && HasValueType<C>::value &&
- HasConstIterator<C>::value> {
+ !IsInitializerList<
+ typename std::remove_reference<C>::type>::value &&
+ HasValueType<C>::value && HasConstIterator<C>::value,
+ HasMappedType<C>::value> {
};
// This class implements the range that is returned by absl::StrSplit(). This
@@ -282,7 +299,8 @@ class Splitter {
// An implicit conversion operator that is restricted to only those containers
// that the splitter is convertible to.
template <typename Container,
- typename OnlyIf = typename SplitterIsConvertibleTo<Container>::type>
+ typename = typename std::enable_if<
+ SplitterIsConvertibleTo<Container>::value>::type>
operator Container() const { // NOLINT(runtime/explicit)
return ConvertToContainer<Container, typename Container::value_type,
HasMappedType<Container>::value>()(*this);
@@ -431,7 +449,7 @@ class Splitter {
};
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
diff --git a/absl/strings/internal/utf8.cc b/absl/strings/internal/utf8.cc
index 81b6241b..c6ab0d52 100644
--- a/absl/strings/internal/utf8.cc
+++ b/absl/strings/internal/utf8.cc
@@ -17,7 +17,7 @@
#include "absl/strings/internal/utf8.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) {
@@ -49,5 +49,5 @@ size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/utf8.h b/absl/strings/internal/utf8.h
index 5a94ae6f..59c4f5b4 100644
--- a/absl/strings/internal/utf8.h
+++ b/absl/strings/internal/utf8.h
@@ -22,7 +22,7 @@
#include <cstdint>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// For Unicode code points 0 through 0x10FFFF, EncodeUTF8Char writes
@@ -43,7 +43,7 @@ enum { kMaxEncodedUTF8Size = 4 };
size_t EncodeUTF8Char(char *buffer, char32_t utf8_char);
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_UTF8_H_
diff --git a/absl/strings/match.cc b/absl/strings/match.cc
index 8f33c72c..12ba8edf 100644
--- a/absl/strings/match.cc
+++ b/absl/strings/match.cc
@@ -17,7 +17,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
bool CaseEqual(absl::string_view piece1, absl::string_view piece2) {
@@ -28,6 +28,13 @@ bool CaseEqual(absl::string_view piece1, absl::string_view piece2) {
}
} // namespace
+bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) {
+ return (piece1.size() == piece2.size() &&
+ 0 == absl::strings_internal::memcasecmp(piece1.data(), piece2.data(),
+ piece1.size()));
+ // memcasecmp uses absl::ascii_tolower().
+}
+
bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix) {
return (text.size() >= prefix.size()) &&
CaseEqual(text.substr(0, prefix.size()), prefix);
@@ -38,5 +45,5 @@ bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) {
CaseEqual(text.substr(text.size() - suffix.size()), suffix);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/match.h b/absl/strings/match.h
index 908f051f..5805207c 100644
--- a/absl/strings/match.h
+++ b/absl/strings/match.h
@@ -17,7 +17,7 @@
// File: match.h
// -----------------------------------------------------------------------------
//
-// This file contains simple utilities for performing std::string matching checks.
+// This file contains simple utilities for performing string matching checks.
// All of these function parameters are specified as `absl::string_view`,
// meaning that these functions can accept `std::string`, `absl::string_view` or
// nul-terminated C-style strings.
@@ -38,18 +38,18 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// StrContains()
//
-// Returns whether a given std::string `haystack` contains the substring `needle`.
+// Returns whether a given string `haystack` contains the substring `needle`.
inline bool StrContains(absl::string_view haystack, absl::string_view needle) {
return haystack.find(needle, 0) != haystack.npos;
}
// StartsWith()
//
-// Returns whether a given std::string `text` begins with `prefix`.
+// Returns whether a given string `text` begins with `prefix`.
inline bool StartsWith(absl::string_view text, absl::string_view prefix) {
return prefix.empty() ||
(text.size() >= prefix.size() &&
@@ -58,7 +58,7 @@ inline bool StartsWith(absl::string_view text, absl::string_view prefix) {
// EndsWith()
//
-// Returns whether a given std::string `text` ends with `suffix`.
+// Returns whether a given string `text` ends with `suffix`.
inline bool EndsWith(absl::string_view text, absl::string_view suffix) {
return suffix.empty() ||
(text.size() >= suffix.size() &&
@@ -67,19 +67,25 @@ inline bool EndsWith(absl::string_view text, absl::string_view suffix) {
);
}
-// StartsWithIgnoreCase()
+// EqualsIgnoreCase()
//
-// Returns whether a given std::string `text` starts with `starts_with`, ignoring
+// Returns whether given ASCII strings `piece1` and `piece2` are equal, ignoring
// case in the comparison.
+bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2);
+
+// StartsWithIgnoreCase()
+//
+// Returns whether a given ASCII string `text` starts with `starts_with`,
+// ignoring case in the comparison.
bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix);
// EndsWithIgnoreCase()
//
-// Returns whether a given std::string `text` ends with `ends_with`, ignoring case
-// in the comparison.
+// Returns whether a given ASCII string `text` ends with `ends_with`, ignoring
+// case in the comparison.
bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_MATCH_H_
diff --git a/absl/strings/match_test.cc b/absl/strings/match_test.cc
index d194f0e6..c21e00bf 100644
--- a/absl/strings/match_test.cc
+++ b/absl/strings/match_test.cc
@@ -80,6 +80,17 @@ TEST(MatchTest, ContainsNull) {
EXPECT_FALSE(absl::StrContains(cs, sv2));
}
+TEST(MatchTest, EqualsIgnoreCase) {
+ std::string text = "the";
+ absl::string_view data(text);
+
+ EXPECT_TRUE(absl::EqualsIgnoreCase(data, "The"));
+ EXPECT_TRUE(absl::EqualsIgnoreCase(data, "THE"));
+ EXPECT_TRUE(absl::EqualsIgnoreCase(data, "the"));
+ EXPECT_FALSE(absl::EqualsIgnoreCase(data, "Quick"));
+ EXPECT_FALSE(absl::EqualsIgnoreCase(data, "then"));
+}
+
TEST(MatchTest, StartsWithIgnoreCase) {
EXPECT_TRUE(absl::StartsWithIgnoreCase("foo", "foo"));
EXPECT_TRUE(absl::StartsWithIgnoreCase("foo", "Fo"));
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index 48dca919..4c3ddb34 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file contains std::string processing functions related to
+// This file contains string processing functions related to
// numeric values.
#include "absl/strings/numbers.h"
@@ -30,15 +30,15 @@
#include <memory>
#include <utility>
+#include "absl/base/internal/bits.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/strings/ascii.h"
#include "absl/strings/charconv.h"
-#include "absl/strings/internal/bits.h"
#include "absl/strings/internal/memutil.h"
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
bool SimpleAtof(absl::string_view str, float* value) {
*value = 0.0;
@@ -162,8 +162,8 @@ bool SimpleAtob(absl::string_view str, bool* value) {
// their output to the beginning of the buffer. The caller is responsible
// for ensuring that the buffer has enough space to hold the output.
//
-// Returns a pointer to the end of the std::string (i.e. the null character
-// terminating the std::string).
+// Returns a pointer to the end of the string (i.e. the null character
+// terminating the string).
// ----------------------------------------------------------------------
namespace {
@@ -345,7 +345,7 @@ static std::pair<uint64_t, uint64_t> Mul32(std::pair<uint64_t, uint64_t> num,
uint64_t bits128_up = (bits96_127 >> 32) + (bits64_127 < bits64_95);
if (bits128_up == 0) return {bits64_127, bits0_63};
- int shift = 64 - strings_internal::CountLeadingZeros64(bits128_up);
+ int shift = 64 - base_internal::CountLeadingZeros64(bits128_up);
uint64_t lo = (bits0_63 >> shift) + (bits64_127 << (64 - shift));
uint64_t hi = (bits64_127 >> shift) + (bits128_up << (64 - shift));
return {hi, lo};
@@ -376,7 +376,7 @@ static std::pair<uint64_t, uint64_t> PowFive(uint64_t num, int expfive) {
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5};
result = Mul32(result, powers_of_five[expfive & 15]);
- int shift = strings_internal::CountLeadingZeros64(result.first);
+ int shift = base_internal::CountLeadingZeros64(result.first);
if (shift != 0) {
result.first = (result.first << shift) + (result.second >> (64 - shift));
result.second = (result.second << shift);
@@ -910,5 +910,5 @@ bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) {
}
} // namespace numbers_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index 8efdb200..250d2603 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -38,48 +38,48 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// SimpleAtoi()
//
-// Converts the given std::string into an integer value, returning `true` if
-// successful. The std::string must reflect a base-10 integer (optionally followed or
+// Converts the given string into an integer value, returning `true` if
+// successful. The string must reflect a base-10 integer (optionally followed or
// preceded by ASCII whitespace) whose value falls within the range of the
-// integer type,
+// integer type.
template <typename int_type>
ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out);
// SimpleAtof()
//
-// Converts the given std::string (optionally followed or preceded by ASCII
+// Converts the given string (optionally followed or preceded by ASCII
// whitespace) into a float, which may be rounded on overflow or underflow.
-// See http://en.cppreference.com/w/c/std::string/byte/strtof for details about the
+// See http://en.cppreference.com/w/c/string/byte/strtof for details about the
// allowed formats for `str`.
ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* value);
// SimpleAtod()
//
-// Converts the given std::string (optionally followed or preceded by ASCII
+// Converts the given string (optionally followed or preceded by ASCII
// whitespace) into a double, which may be rounded on overflow or underflow.
-// See http://en.cppreference.com/w/c/std::string/byte/strtof for details about the
+// See http://en.cppreference.com/w/c/string/byte/strtof for details about the
// allowed formats for `str`.
ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* value);
// SimpleAtob()
//
-// Converts the given std::string into a boolean, returning `true` if successful.
+// Converts the given string into a boolean, returning `true` if successful.
// The following case-insensitive strings are interpreted as boolean `true`:
// "true", "t", "yes", "y", "1". The following case-insensitive strings
// are interpreted as boolean `false`: "false", "f", "no", "n", "0".
ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* value);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// End of public API. Implementation details follow.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace numbers_internal {
// safe_strto?() functions for implementing SimpleAtoi()
@@ -172,9 +172,9 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out,
// SimpleAtoi()
//
-// Converts a std::string to an integer, using `safe_strto?()` functions for actual
+// Converts a string to an integer, using `safe_strto?()` functions for actual
// parsing, returning `true` if successful. The `safe_strto?()` functions apply
-// strict checking; the std::string must be a base-10 integer, optionally followed or
+// strict checking; the string must be a base-10 integer, optionally followed or
// preceded by ASCII whitespace, with a value in the range of the corresponding
// integer type.
template <typename int_type>
@@ -182,7 +182,7 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out) {
return numbers_internal::safe_strtoi_base(s, out, 10);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_NUMBERS_H_
diff --git a/absl/strings/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc
new file mode 100644
index 00000000..0570b758
--- /dev/null
+++ b/absl/strings/numbers_benchmark.cc
@@ -0,0 +1,263 @@
+// 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
+//
+// 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.
+
+#include <cstdint>
+#include <random>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/strings/numbers.h"
+
+namespace {
+
+template <typename T>
+void BM_FastIntToBuffer(benchmark::State& state) {
+ const int inc = state.range(0);
+ char buf[absl::numbers_internal::kFastToBufferSize];
+ // Use the unsigned type to increment to take advantage of well-defined
+ // modular arithmetic.
+ typename std::make_unsigned<T>::type x = 0;
+ for (auto _ : state) {
+ absl::numbers_internal::FastIntToBuffer(static_cast<T>(x), buf);
+ x += inc;
+ }
+}
+BENCHMARK_TEMPLATE(BM_FastIntToBuffer, int32_t)->Range(0, 1 << 15);
+BENCHMARK_TEMPLATE(BM_FastIntToBuffer, int64_t)->Range(0, 1 << 30);
+
+// Creates an integer that would be printed as `num_digits` repeated 7s in the
+// given `base`. `base` must be greater than or equal to 8.
+int64_t RepeatedSevens(int num_digits, int base) {
+ ABSL_RAW_CHECK(base >= 8, "");
+ int64_t num = 7;
+ while (--num_digits) num = base * num + 7;
+ return num;
+}
+
+void BM_safe_strto32_string(benchmark::State& state) {
+ const int digits = state.range(0);
+ const int base = state.range(1);
+ std::string str(digits, '7'); // valid in octal, decimal and hex
+ int32_t value = 0;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(
+ absl::numbers_internal::safe_strto32_base(str, &value, base));
+ }
+ ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
+}
+BENCHMARK(BM_safe_strto32_string)
+ ->ArgPair(1, 8)
+ ->ArgPair(1, 10)
+ ->ArgPair(1, 16)
+ ->ArgPair(2, 8)
+ ->ArgPair(2, 10)
+ ->ArgPair(2, 16)
+ ->ArgPair(4, 8)
+ ->ArgPair(4, 10)
+ ->ArgPair(4, 16)
+ ->ArgPair(8, 8)
+ ->ArgPair(8, 10)
+ ->ArgPair(8, 16)
+ ->ArgPair(10, 8)
+ ->ArgPair(9, 10);
+
+void BM_safe_strto64_string(benchmark::State& state) {
+ const int digits = state.range(0);
+ const int base = state.range(1);
+ std::string str(digits, '7'); // valid in octal, decimal and hex
+ int64_t value = 0;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(
+ absl::numbers_internal::safe_strto64_base(str, &value, base));
+ }
+ ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
+}
+BENCHMARK(BM_safe_strto64_string)
+ ->ArgPair(1, 8)
+ ->ArgPair(1, 10)
+ ->ArgPair(1, 16)
+ ->ArgPair(2, 8)
+ ->ArgPair(2, 10)
+ ->ArgPair(2, 16)
+ ->ArgPair(4, 8)
+ ->ArgPair(4, 10)
+ ->ArgPair(4, 16)
+ ->ArgPair(8, 8)
+ ->ArgPair(8, 10)
+ ->ArgPair(8, 16)
+ ->ArgPair(16, 8)
+ ->ArgPair(16, 10)
+ ->ArgPair(16, 16);
+
+void BM_safe_strtou32_string(benchmark::State& state) {
+ const int digits = state.range(0);
+ const int base = state.range(1);
+ std::string str(digits, '7'); // valid in octal, decimal and hex
+ uint32_t value = 0;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(
+ absl::numbers_internal::safe_strtou32_base(str, &value, base));
+ }
+ ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
+}
+BENCHMARK(BM_safe_strtou32_string)
+ ->ArgPair(1, 8)
+ ->ArgPair(1, 10)
+ ->ArgPair(1, 16)
+ ->ArgPair(2, 8)
+ ->ArgPair(2, 10)
+ ->ArgPair(2, 16)
+ ->ArgPair(4, 8)
+ ->ArgPair(4, 10)
+ ->ArgPair(4, 16)
+ ->ArgPair(8, 8)
+ ->ArgPair(8, 10)
+ ->ArgPair(8, 16)
+ ->ArgPair(10, 8)
+ ->ArgPair(9, 10);
+
+void BM_safe_strtou64_string(benchmark::State& state) {
+ const int digits = state.range(0);
+ const int base = state.range(1);
+ std::string str(digits, '7'); // valid in octal, decimal and hex
+ uint64_t value = 0;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(
+ absl::numbers_internal::safe_strtou64_base(str, &value, base));
+ }
+ ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
+}
+BENCHMARK(BM_safe_strtou64_string)
+ ->ArgPair(1, 8)
+ ->ArgPair(1, 10)
+ ->ArgPair(1, 16)
+ ->ArgPair(2, 8)
+ ->ArgPair(2, 10)
+ ->ArgPair(2, 16)
+ ->ArgPair(4, 8)
+ ->ArgPair(4, 10)
+ ->ArgPair(4, 16)
+ ->ArgPair(8, 8)
+ ->ArgPair(8, 10)
+ ->ArgPair(8, 16)
+ ->ArgPair(16, 8)
+ ->ArgPair(16, 10)
+ ->ArgPair(16, 16);
+
+// Returns a vector of `num_strings` strings. Each string represents a
+// floating point number with `num_digits` digits before the decimal point and
+// another `num_digits` digits after.
+std::vector<std::string> MakeFloatStrings(int num_strings, int num_digits) {
+ // For convenience, use a random number generator to generate the test data.
+ // We don't actually need random properties, so use a fixed seed.
+ std::minstd_rand0 rng(1);
+ std::uniform_int_distribution<int> random_digit('0', '9');
+
+ std::vector<std::string> float_strings(num_strings);
+ for (std::string& s : float_strings) {
+ s.reserve(2 * num_digits + 1);
+ for (int i = 0; i < num_digits; ++i) {
+ s.push_back(static_cast<char>(random_digit(rng)));
+ }
+ s.push_back('.');
+ for (int i = 0; i < num_digits; ++i) {
+ s.push_back(static_cast<char>(random_digit(rng)));
+ }
+ }
+ return float_strings;
+}
+
+template <typename StringType>
+StringType GetStringAs(const std::string& s) {
+ return static_cast<StringType>(s);
+}
+template <>
+const char* GetStringAs<const char*>(const std::string& s) {
+ return s.c_str();
+}
+
+template <typename StringType>
+std::vector<StringType> GetStringsAs(const std::vector<std::string>& strings) {
+ std::vector<StringType> result;
+ result.reserve(strings.size());
+ for (const std::string& s : strings) {
+ result.push_back(GetStringAs<StringType>(s));
+ }
+ return result;
+}
+
+template <typename T>
+void BM_SimpleAtof(benchmark::State& state) {
+ const int num_strings = state.range(0);
+ const int num_digits = state.range(1);
+ std::vector<std::string> backing_strings =
+ MakeFloatStrings(num_strings, num_digits);
+ std::vector<T> inputs = GetStringsAs<T>(backing_strings);
+ float value;
+ for (auto _ : state) {
+ for (const T& input : inputs) {
+ benchmark::DoNotOptimize(absl::SimpleAtof(input, &value));
+ }
+ }
+}
+BENCHMARK_TEMPLATE(BM_SimpleAtof, absl::string_view)
+ ->ArgPair(10, 1)
+ ->ArgPair(10, 2)
+ ->ArgPair(10, 4)
+ ->ArgPair(10, 8);
+BENCHMARK_TEMPLATE(BM_SimpleAtof, const char*)
+ ->ArgPair(10, 1)
+ ->ArgPair(10, 2)
+ ->ArgPair(10, 4)
+ ->ArgPair(10, 8);
+BENCHMARK_TEMPLATE(BM_SimpleAtof, std::string)
+ ->ArgPair(10, 1)
+ ->ArgPair(10, 2)
+ ->ArgPair(10, 4)
+ ->ArgPair(10, 8);
+
+template <typename T>
+void BM_SimpleAtod(benchmark::State& state) {
+ const int num_strings = state.range(0);
+ const int num_digits = state.range(1);
+ std::vector<std::string> backing_strings =
+ MakeFloatStrings(num_strings, num_digits);
+ std::vector<T> inputs = GetStringsAs<T>(backing_strings);
+ double value;
+ for (auto _ : state) {
+ for (const T& input : inputs) {
+ benchmark::DoNotOptimize(absl::SimpleAtod(input, &value));
+ }
+ }
+}
+BENCHMARK_TEMPLATE(BM_SimpleAtod, absl::string_view)
+ ->ArgPair(10, 1)
+ ->ArgPair(10, 2)
+ ->ArgPair(10, 4)
+ ->ArgPair(10, 8);
+BENCHMARK_TEMPLATE(BM_SimpleAtod, const char*)
+ ->ArgPair(10, 1)
+ ->ArgPair(10, 2)
+ ->ArgPair(10, 4)
+ ->ArgPair(10, 8);
+BENCHMARK_TEMPLATE(BM_SimpleAtod, std::string)
+ ->ArgPair(10, 1)
+ ->ArgPair(10, 2)
+ ->ArgPair(10, 4)
+ ->ArgPair(10, 8);
+
+} // namespace
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index 24e7138c..099326c2 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file tests std::string processing functions related to numeric values.
+// This file tests string processing functions related to numeric values.
#include "absl/strings/numbers.h"
@@ -39,6 +39,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/internal/numbers_test_common.h"
+#include "absl/strings/internal/pow10_helper.h"
namespace {
@@ -56,16 +57,11 @@ using testing::Eq;
using testing::MatchesRegex;
// Number of floats to test with.
-// 10,000,000 is a reasonable default for a test that only takes a few seconds.
+// 5,000,000 is a reasonable default for a test that only takes a few seconds.
// 1,000,000,000+ triggers checking for all possible mantissa values for
// double-precision tests. 2,000,000,000+ triggers checking for every possible
// single-precision float.
-#ifdef _MSC_VER
-// Use a smaller number on MSVC to avoid test time out (1 min)
const int kFloatNumCases = 5000000;
-#else
-const int kFloatNumCases = 10000000;
-#endif
// This is a slow, brute-force routine to compute the exact base-10
// representation of a double-precision floating-point number. It
@@ -716,8 +712,9 @@ TEST(stringtest, safe_strtou64_base_length_delimited) {
}
}
-// feenableexcept() and fedisableexcept() are missing on Mac OS X, MSVC.
-#if defined(_MSC_VER) || defined(__APPLE__)
+// feenableexcept() and fedisableexcept() are missing on Mac OS X, MSVC,
+// and WebAssembly.
+#if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
#define ABSL_MISSING_FEENABLEEXCEPT 1
#define ABSL_MISSING_FEDISABLEEXCEPT 1
#endif
@@ -875,7 +872,7 @@ TEST_F(SimpleDtoaTest, ExhaustiveDoubleToSixDigits) {
}
for (int exponent = -324; exponent <= 308; ++exponent) {
- double powten = pow(10.0, exponent);
+ double powten = absl::strings_internal::Pow10(exponent);
if (powten == 0) powten = 5e-324;
if (kFloatNumCases >= 1e9) {
// The exhaustive test takes a very long time, so log progress.
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 499f3f41..2f2e5315 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -23,7 +23,7 @@
#include "absl/strings/internal/resize_uninitialized.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
AlphaNum::AlphaNum(Hex hex) {
char* const end = &digits_[numbers_internal::kFastToBufferSize];
@@ -80,8 +80,8 @@ AlphaNum::AlphaNum(Dec dec) {
// ----------------------------------------------------------------------
// StrCat()
// This merges the given strings or integers, with no delimiter. This
-// is designed to be the fastest possible way to construct a std::string out
-// of a mix of raw C strings, StringPieces, strings, and integer values.
+// is designed to be the fastest possible way to construct a string out
+// of a mix of raw C strings, string_views, strings, and integer values.
// ----------------------------------------------------------------------
// Append is merely a version of memcpy that returns the address of the byte
@@ -155,10 +155,10 @@ std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
}
// It's possible to call StrAppend with an absl::string_view that is itself a
-// fragment of the std::string we're appending to. However the results of this are
+// fragment of the string we're appending to. However the results of this are
// random. Therefore, check for this in debug mode. Use unsigned math so we
// only have to do one comparison. Note, there's an exception case: appending an
-// empty std::string is always allowed.
+// empty string is always allowed.
#define ASSERT_NO_OVERLAP(dest, src) \
assert(((src).size() == 0) || \
(uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
@@ -237,5 +237,5 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
assert(out == begin + dest->size());
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index c694cbc1..edda40ad 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -23,7 +23,7 @@
// designed to be used as a parameter type that efficiently manages conversion
// to strings and avoids copies in the above operations.
//
-// Any routine accepting either a std::string or a number may accept `AlphaNum`.
+// Any routine accepting either a string or a number may accept `AlphaNum`.
// The basic idea is that by accepting a `const AlphaNum &` as an argument
// to your function, your callers will automagically convert bools, integers,
// and floating point values to strings for you.
@@ -63,10 +63,10 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
-// AlphaNumBuffer allows a way to pass a std::string to StrCat without having to do
+// AlphaNumBuffer allows a way to pass a string to StrCat without having to do
// memory allocation. It is simply a pair of a fixed-size character array, and
// a size. Please don't use outside of absl, yet.
template <size_t max_size>
@@ -98,6 +98,10 @@ enum PadSpec : uint8_t {
kZeroPad14,
kZeroPad15,
kZeroPad16,
+ kZeroPad17,
+ kZeroPad18,
+ kZeroPad19,
+ kZeroPad20,
kSpacePad2 = kZeroPad2 + 64,
kSpacePad3,
@@ -114,14 +118,18 @@ enum PadSpec : uint8_t {
kSpacePad14,
kSpacePad15,
kSpacePad16,
+ kSpacePad17,
+ kSpacePad18,
+ kSpacePad19,
+ kSpacePad20,
};
// -----------------------------------------------------------------------------
// Hex
// -----------------------------------------------------------------------------
//
-// `Hex` stores a set of hexadecimal std::string conversion parameters for use
-// within `AlphaNum` std::string conversions.
+// `Hex` stores a set of hexadecimal string conversion parameters for use
+// within `AlphaNum` string conversions.
struct Hex {
uint64_t value;
uint8_t width;
@@ -169,8 +177,8 @@ struct Hex {
// Dec
// -----------------------------------------------------------------------------
//
-// `Dec` stores a set of decimal std::string conversion parameters for use
-// within `AlphaNum` std::string conversions. Dec is slower than the default
+// `Dec` stores a set of decimal string conversion parameters for use
+// within `AlphaNum` string conversions. Dec is slower than the default
// integer conversion, so use it only if you need padding.
struct Dec {
uint64_t value;
@@ -272,7 +280,7 @@ class AlphaNum {
//
// Merges given strings or numbers, using no delimiter(s).
//
-// `StrCat()` is designed to be the fastest possible way to construct a std::string
+// `StrCat()` is designed to be the fastest possible way to construct a string
// out of a mix of raw C strings, string_views, strings, bool values,
// and numeric values.
//
@@ -280,7 +288,7 @@ class AlphaNum {
// works poorly on strings built up out of fragments.
//
// For clarity and performance, don't use `StrCat()` when appending to a
-// std::string. Use `StrAppend()` instead. In particular, avoid using any of these
+// string. Use `StrAppend()` instead. In particular, avoid using any of these
// (anti-)patterns:
//
// str.append(StrCat(...))
@@ -329,26 +337,26 @@ ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a, const AlphaNum
// StrAppend()
// -----------------------------------------------------------------------------
//
-// Appends a std::string or set of strings to an existing std::string, in a similar
+// Appends a string or set of strings to an existing string, in a similar
// fashion to `StrCat()`.
//
// WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
// a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
// not try to check each of its input arguments to be sure that they are not
-// a subset of the std::string being appended to. That is, while this will work:
+// a subset of the string being appended to. That is, while this will work:
//
-// std::string s = "foo";
+// string s = "foo";
// s += s;
//
// This output is undefined:
//
-// std::string s = "foo";
+// string s = "foo";
// StrAppend(&s, s);
//
// This output is undefined as well, since `absl::string_view` does not own its
// data:
//
-// std::string s = "foobar";
+// string s = "foobar";
// absl::string_view p = s;
// StrAppend(&s, p);
@@ -381,7 +389,7 @@ SixDigits(double d) {
return result;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STR_CAT_H_
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index e9d67690..07141072 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -28,7 +28,8 @@
#define ABSL_EXPECT_DEBUG_DEATH(statement, regex) \
EXPECT_DEBUG_DEATH(statement, ".*")
#else
-#define ABSL_EXPECT_DEBUG_DEATH EXPECT_DEBUG_DEATH
+#define ABSL_EXPECT_DEBUG_DEATH(statement, regex) \
+ EXPECT_DEBUG_DEATH(statement, regex)
#endif
namespace {
@@ -426,7 +427,7 @@ void CheckHex(IntType v, const char* nopad_format, const char* zeropad_format,
snprintf(expected, sizeof(expected), nopad_format, v);
EXPECT_EQ(expected, actual) << " decimal value " << v;
- for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad16; ++spec) {
+ for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad20; ++spec) {
std::string actual =
absl::StrCat(absl::Hex(v, static_cast<absl::PadSpec>(spec)));
snprintf(expected, sizeof(expected), zeropad_format,
@@ -434,7 +435,7 @@ void CheckHex(IntType v, const char* nopad_format, const char* zeropad_format,
EXPECT_EQ(expected, actual) << " decimal value " << v;
}
- for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad16; ++spec) {
+ for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad20; ++spec) {
std::string actual =
absl::StrCat(absl::Hex(v, static_cast<absl::PadSpec>(spec)));
snprintf(expected, sizeof(expected), spacepad_format,
@@ -452,7 +453,7 @@ void CheckDec(IntType v, const char* nopad_format, const char* zeropad_format,
snprintf(expected, sizeof(expected), nopad_format, v);
EXPECT_EQ(expected, actual) << " decimal value " << v;
- for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad16; ++spec) {
+ for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad20; ++spec) {
std::string actual =
absl::StrCat(absl::Dec(v, static_cast<absl::PadSpec>(spec)));
snprintf(expected, sizeof(expected), zeropad_format,
@@ -462,7 +463,7 @@ void CheckDec(IntType v, const char* nopad_format, const char* zeropad_format,
<< "' digits " << (spec - absl::kZeroPad2 + 2);
}
- for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad16; ++spec) {
+ for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad20; ++spec) {
std::string actual =
absl::StrCat(absl::Dec(v, static_cast<absl::PadSpec>(spec)));
snprintf(expected, sizeof(expected), spacepad_format,
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
new file mode 100644
index 00000000..7b19d411
--- /dev/null
+++ b/absl/strings/str_format.h
@@ -0,0 +1,514 @@
+//
+// 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
+//
+// 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.
+//
+// -----------------------------------------------------------------------------
+// File: str_format.h
+// -----------------------------------------------------------------------------
+//
+// The `str_format` library is a typesafe replacement for the family of
+// `printf()` string formatting routines within the `<cstdio>` standard library
+// header. Like the `printf` family, the `str_format` uses a "format string" to
+// perform argument substitutions based on types.
+//
+// Example:
+//
+// string s = absl::StrFormat("%s %s You have $%d!", "Hello", name, dollars);
+//
+// The library consists of the following basic utilities:
+//
+// * `absl::StrFormat()`, a type-safe replacement for `std::sprintf()`, to
+// write a format string to a `string` value.
+// * `absl::StrAppendFormat()` to append a format string to a `string`
+// * `absl::StreamFormat()` to more efficiently write a format string to a
+// stream, such as`std::cout`.
+// * `absl::PrintF()`, `absl::FPrintF()` and `absl::SNPrintF()` as
+// replacements for `std::printf()`, `std::fprintf()` and `std::snprintf()`.
+//
+// Note: a version of `std::sprintf()` is not supported as it is
+// generally unsafe due to buffer overflows.
+//
+// Additionally, you can provide a format string (and its associated arguments)
+// using one of the following abstractions:
+//
+// * A `FormatSpec` class template fully encapsulates a format string and its
+// type arguments and is usually provided to `str_format` functions as a
+// variadic argument of type `FormatSpec<Arg...>`. The `FormatSpec<Args...>`
+// template is evaluated at compile-time, providing type safety.
+// * A `ParsedFormat` instance, which encapsulates a specific, pre-compiled
+// format string for a specific set of type(s), and which can be passed
+// between API boundaries. (The `FormatSpec` type should not be used
+// directly.)
+//
+// The `str_format` library provides the ability to output its format strings to
+// arbitrary sink types:
+//
+// * A generic `Format()` function to write outputs to arbitrary sink types,
+// which must implement a `RawSinkFormat` interface. (See
+// `str_format_sink.h` for more information.)
+//
+// * A `FormatUntyped()` function that is similar to `Format()` except it is
+// loosely typed. `FormatUntyped()` is not a template and does not perform
+// any compile-time checking of the format string; instead, it returns a
+// boolean from a runtime check.
+//
+// In addition, the `str_format` library provides extension points for
+// augmenting formatting to new types. These extensions are fully documented
+// within the `str_format_extension.h` header file.
+#ifndef ABSL_STRINGS_STR_FORMAT_H_
+#define ABSL_STRINGS_STR_FORMAT_H_
+
+#include <cstdio>
+#include <string>
+
+#include "absl/strings/internal/str_format/arg.h" // IWYU pragma: export
+#include "absl/strings/internal/str_format/bind.h" // IWYU pragma: export
+#include "absl/strings/internal/str_format/checker.h" // IWYU pragma: export
+#include "absl/strings/internal/str_format/extension.h" // IWYU pragma: export
+#include "absl/strings/internal/str_format/parser.h" // IWYU pragma: export
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+// UntypedFormatSpec
+//
+// A type-erased class that can be used directly within untyped API entry
+// points. An `UntypedFormatSpec` is specifically used as an argument to
+// `FormatUntyped()`.
+//
+// Example:
+//
+// absl::UntypedFormatSpec format("%d");
+// string out;
+// CHECK(absl::FormatUntyped(&out, format, {absl::FormatArg(1)}));
+class UntypedFormatSpec {
+ public:
+ UntypedFormatSpec() = delete;
+ UntypedFormatSpec(const UntypedFormatSpec&) = delete;
+ UntypedFormatSpec& operator=(const UntypedFormatSpec&) = delete;
+
+ explicit UntypedFormatSpec(string_view s) : spec_(s) {}
+
+ protected:
+ explicit UntypedFormatSpec(const str_format_internal::ParsedFormatBase* pc)
+ : spec_(pc) {}
+
+ private:
+ friend str_format_internal::UntypedFormatSpecImpl;
+ str_format_internal::UntypedFormatSpecImpl spec_;
+};
+
+// FormatStreamed()
+//
+// Takes a streamable argument and returns an object that can print it
+// with '%s'. Allows printing of types that have an `operator<<` but no
+// intrinsic type support within `StrFormat()` itself.
+//
+// Example:
+//
+// absl::StrFormat("%s", absl::FormatStreamed(obj));
+template <typename T>
+str_format_internal::StreamedWrapper<T> FormatStreamed(const T& v) {
+ return str_format_internal::StreamedWrapper<T>(v);
+}
+
+// FormatCountCapture
+//
+// This class provides a way to safely wrap `StrFormat()` captures of `%n`
+// conversions, which denote the number of characters written by a formatting
+// operation to this point, into an integer value.
+//
+// This wrapper is designed to allow safe usage of `%n` within `StrFormat(); in
+// the `printf()` family of functions, `%n` is not safe to use, as the `int *`
+// buffer can be used to capture arbitrary data.
+//
+// Example:
+//
+// int n = 0;
+// string s = absl::StrFormat("%s%d%n", "hello", 123,
+// absl::FormatCountCapture(&n));
+// EXPECT_EQ(8, n);
+class FormatCountCapture {
+ public:
+ explicit FormatCountCapture(int* p) : p_(p) {}
+
+ private:
+ // FormatCountCaptureHelper is used to define FormatConvertImpl() for this
+ // class.
+ friend struct str_format_internal::FormatCountCaptureHelper;
+ // Unused() is here because of the false positive from -Wunused-private-field
+ // p_ is used in the templated function of the friend FormatCountCaptureHelper
+ // class.
+ int* Unused() { return p_; }
+ int* p_;
+};
+
+// FormatSpec
+//
+// The `FormatSpec` type defines the makeup of a format string within the
+// `str_format` library. You should not need to use or manipulate this type
+// directly. A `FormatSpec` is a variadic class template that is evaluated at
+// compile-time, according to the format string and arguments that are passed
+// to it.
+//
+// For a `FormatSpec` to be valid at compile-time, it must be provided as
+// either:
+//
+// * A `constexpr` literal or `absl::string_view`, which is how it most often
+// used.
+// * A `ParsedFormat` instantiation, which ensures the format string is
+// valid before use. (See below.)
+//
+// Example:
+//
+// // Provided as a string literal.
+// absl::StrFormat("Welcome to %s, Number %d!", "The Village", 6);
+//
+// // Provided as a constexpr absl::string_view.
+// constexpr absl::string_view formatString = "Welcome to %s, Number %d!";
+// absl::StrFormat(formatString, "The Village", 6);
+//
+// // Provided as a pre-compiled ParsedFormat object.
+// // Note that this example is useful only for illustration purposes.
+// absl::ParsedFormat<'s', 'd'> formatString("Welcome to %s, Number %d!");
+// absl::StrFormat(formatString, "TheVillage", 6);
+//
+// A format string generally follows the POSIX syntax as used within the POSIX
+// `printf` specification.
+//
+// (See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html.)
+//
+// In specific, the `FormatSpec` supports the following type specifiers:
+// * `c` for characters
+// * `s` for strings
+// * `d` or `i` for integers
+// * `o` for unsigned integer conversions into octal
+// * `x` or `X` for unsigned integer conversions into hex
+// * `u` for unsigned integers
+// * `f` or `F` for floating point values into decimal notation
+// * `e` or `E` for floating point values into exponential notation
+// * `a` or `A` for floating point values into hex exponential notation
+// * `g` or `G` for floating point values into decimal or exponential
+// notation based on their precision
+// * `p` for pointer address values
+// * `n` for the special case of writing out the number of characters
+// written to this point. The resulting value must be captured within an
+// `absl::FormatCountCapture` type.
+//
+// NOTE: `o`, `x\X` and `u` will convert signed values to their unsigned
+// counterpart before formatting.
+//
+// Examples:
+// "%c", 'a' -> "a"
+// "%c", 32 -> " "
+// "%s", "C" -> "C"
+// "%s", std::string("C++") -> "C++"
+// "%d", -10 -> "-10"
+// "%o", 10 -> "12"
+// "%x", 16 -> "10"
+// "%f", 123456789 -> "123456789.000000"
+// "%e", .01 -> "1.00000e-2"
+// "%a", -3.0 -> "-0x1.8p+1"
+// "%g", .01 -> "1e-2"
+// "%p", *int -> "0x7ffdeb6ad2a4"
+//
+// int n = 0;
+// string s = absl::StrFormat(
+// "%s%d%n", "hello", 123, absl::FormatCountCapture(&n));
+// EXPECT_EQ(8, n);
+//
+// The `FormatSpec` intrinsically supports all of these fundamental C++ types:
+//
+// * Characters: `char`, `signed char`, `unsigned char`
+// * Integers: `int`, `short`, `unsigned short`, `unsigned`, `long`,
+// `unsigned long`, `long long`, `unsigned long long`
+// * Floating-point: `float`, `double`, `long double`
+//
+// However, in the `str_format` library, a format conversion specifies a broader
+// C++ conceptual category instead of an exact type. For example, `%s` binds to
+// any string-like argument, so `std::string`, `absl::string_view`, and
+// `const char*` are all accepted. Likewise, `%d` accepts any integer-like
+// argument, etc.
+
+template <typename... Args>
+using FormatSpec =
+ typename str_format_internal::FormatSpecDeductionBarrier<Args...>::type;
+
+// ParsedFormat
+//
+// A `ParsedFormat` is a class template representing a preparsed `FormatSpec`,
+// with template arguments specifying the conversion characters used within the
+// format string. Such characters must be valid format type specifiers, and
+// these type specifiers are checked at compile-time.
+//
+// Instances of `ParsedFormat` can be created, copied, and reused to speed up
+// formatting loops. A `ParsedFormat` may either be constructed statically, or
+// dynamically through its `New()` factory function, which only constructs a
+// runtime object if the format is valid at that time.
+//
+// Example:
+//
+// // Verified at compile time.
+// absl::ParsedFormat<'s', 'd'> formatString("Welcome to %s, Number %d!");
+// absl::StrFormat(formatString, "TheVillage", 6);
+//
+// // Verified at runtime.
+// auto format_runtime = absl::ParsedFormat<'d'>::New(format_string);
+// if (format_runtime) {
+// value = absl::StrFormat(*format_runtime, i);
+// } else {
+// ... error case ...
+// }
+template <char... Conv>
+using ParsedFormat = str_format_internal::ExtendedParsedFormat<
+ str_format_internal::ConversionCharToConv(Conv)...>;
+
+// StrFormat()
+//
+// Returns a `string` given a `printf()`-style format string and zero or more
+// additional arguments. Use it as you would `sprintf()`. `StrFormat()` is the
+// primary formatting function within the `str_format` library, and should be
+// used in most cases where you need type-safe conversion of types into
+// formatted strings.
+//
+// The format string generally consists of ordinary character data along with
+// one or more format conversion specifiers (denoted by the `%` character).
+// Ordinary character data is returned unchanged into the result string, while
+// each conversion specification performs a type substitution from
+// `StrFormat()`'s other arguments. See the comments for `FormatSpec` for full
+// information on the makeup of this format string.
+//
+// Example:
+//
+// string s = absl::StrFormat(
+// "Welcome to %s, Number %d!", "The Village", 6);
+// EXPECT_EQ("Welcome to The Village, Number 6!", s);
+//
+// Returns an empty string in case of error.
+template <typename... Args>
+ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec<Args...>& format,
+ const Args&... args) {
+ return str_format_internal::FormatPack(
+ str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ {str_format_internal::FormatArgImpl(args)...});
+}
+
+// StrAppendFormat()
+//
+// Appends to a `dst` string given a format string, and zero or more additional
+// arguments, returning `*dst` as a convenience for chaining purposes. Appends
+// nothing in case of error (but possibly alters its capacity).
+//
+// Example:
+//
+// string orig("For example PI is approximately ");
+// std::cout << StrAppendFormat(&orig, "%12.6f", 3.14);
+template <typename... Args>
+std::string& StrAppendFormat(std::string* dst, const FormatSpec<Args...>& format,
+ const Args&... args) {
+ return str_format_internal::AppendPack(
+ dst, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ {str_format_internal::FormatArgImpl(args)...});
+}
+
+// StreamFormat()
+//
+// Writes to an output stream given a format string and zero or more arguments,
+// generally in a manner that is more efficient than streaming the result of
+// `absl:: StrFormat()`. The returned object must be streamed before the full
+// expression ends.
+//
+// Example:
+//
+// std::cout << StreamFormat("%12.6f", 3.14);
+template <typename... Args>
+ABSL_MUST_USE_RESULT str_format_internal::Streamable StreamFormat(
+ const FormatSpec<Args...>& format, const Args&... args) {
+ return str_format_internal::Streamable(
+ str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ {str_format_internal::FormatArgImpl(args)...});
+}
+
+// PrintF()
+//
+// Writes to stdout given a format string and zero or more arguments. This
+// function is functionally equivalent to `std::printf()` (and type-safe);
+// prefer `absl::PrintF()` over `std::printf()`.
+//
+// Example:
+//
+// std::string_view s = "Ulaanbaatar";
+// absl::PrintF("The capital of Mongolia is %s", s);
+//
+// Outputs: "The capital of Mongolia is Ulaanbaatar"
+//
+template <typename... Args>
+int PrintF(const FormatSpec<Args...>& format, const Args&... args) {
+ return str_format_internal::FprintF(
+ stdout, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ {str_format_internal::FormatArgImpl(args)...});
+}
+
+// FPrintF()
+//
+// Writes to a file given a format string and zero or more arguments. This
+// function is functionally equivalent to `std::fprintf()` (and type-safe);
+// prefer `absl::FPrintF()` over `std::fprintf()`.
+//
+// Example:
+//
+// std::string_view s = "Ulaanbaatar";
+// absl::FPrintF(stdout, "The capital of Mongolia is %s", s);
+//
+// Outputs: "The capital of Mongolia is Ulaanbaatar"
+//
+template <typename... Args>
+int FPrintF(std::FILE* output, const FormatSpec<Args...>& format,
+ const Args&... args) {
+ return str_format_internal::FprintF(
+ output, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ {str_format_internal::FormatArgImpl(args)...});
+}
+
+// SNPrintF()
+//
+// Writes to a sized buffer given a format string and zero or more arguments.
+// This function is functionally equivalent to `std::snprintf()` (and
+// type-safe); prefer `absl::SNPrintF()` over `std::snprintf()`.
+//
+// Example:
+//
+// std::string_view s = "Ulaanbaatar";
+// char output[128];
+// absl::SNPrintF(output, sizeof(output),
+// "The capital of Mongolia is %s", s);
+//
+// Post-condition: output == "The capital of Mongolia is Ulaanbaatar"
+//
+template <typename... Args>
+int SNPrintF(char* output, std::size_t size, const FormatSpec<Args...>& format,
+ const Args&... args) {
+ return str_format_internal::SnprintF(
+ output, size, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ {str_format_internal::FormatArgImpl(args)...});
+}
+
+// -----------------------------------------------------------------------------
+// Custom Output Formatting Functions
+// -----------------------------------------------------------------------------
+
+// FormatRawSink
+//
+// FormatRawSink is a type erased wrapper around arbitrary sink objects
+// specifically used as an argument to `Format()`.
+// FormatRawSink does not own the passed sink object. The passed object must
+// outlive the FormatRawSink.
+class FormatRawSink {
+ public:
+ // Implicitly convert from any type that provides the hook function as
+ // described above.
+ template <typename T,
+ typename = typename std::enable_if<std::is_constructible<
+ str_format_internal::FormatRawSinkImpl, T*>::value>::type>
+ FormatRawSink(T* raw) // NOLINT
+ : sink_(raw) {}
+
+ private:
+ friend str_format_internal::FormatRawSinkImpl;
+ str_format_internal::FormatRawSinkImpl sink_;
+};
+
+// Format()
+//
+// Writes a formatted string to an arbitrary sink object (implementing the
+// `absl::FormatRawSink` interface), using a format string and zero or more
+// additional arguments.
+//
+// By default, `string` and `std::ostream` are supported as destination objects.
+//
+// `absl::Format()` is a generic version of `absl::StrFormat(), for custom
+// sinks. The format string, like format strings for `StrFormat()`, is checked
+// at compile-time.
+//
+// On failure, this function returns `false` and the state of the sink is
+// unspecified.
+template <typename... Args>
+bool Format(FormatRawSink raw_sink, const FormatSpec<Args...>& format,
+ const Args&... args) {
+ return str_format_internal::FormatUntyped(
+ str_format_internal::FormatRawSinkImpl::Extract(raw_sink),
+ str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ {str_format_internal::FormatArgImpl(args)...});
+}
+
+// FormatArg
+//
+// A type-erased handle to a format argument specifically used as an argument to
+// `FormatUntyped()`. You may construct `FormatArg` by passing
+// reference-to-const of any printable type. `FormatArg` is both copyable and
+// assignable. The source data must outlive the `FormatArg` instance. See
+// example below.
+//
+using FormatArg = str_format_internal::FormatArgImpl;
+
+// FormatUntyped()
+//
+// Writes a formatted string to an arbitrary sink object (implementing the
+// `absl::FormatRawSink` interface), using an `UntypedFormatSpec` and zero or
+// more additional arguments.
+//
+// This function acts as the most generic formatting function in the
+// `str_format` library. The caller provides a raw sink, an unchecked format
+// string, and (usually) a runtime specified list of arguments; no compile-time
+// checking of formatting is performed within this function. As a result, a
+// caller should check the return value to verify that no error occurred.
+// On failure, this function returns `false` and the state of the sink is
+// unspecified.
+//
+// The arguments are provided in an `absl::Span<const absl::FormatArg>`.
+// Each `absl::FormatArg` object binds to a single argument and keeps a
+// reference to it. The values used to create the `FormatArg` objects must
+// outlive this function call. (See `str_format_arg.h` for information on
+// the `FormatArg` class.)_
+//
+// Example:
+//
+// std::optional<string> FormatDynamic(const string& in_format,
+// const vector<string>& in_args) {
+// string out;
+// std::vector<absl::FormatArg> args;
+// for (const auto& v : in_args) {
+// // It is important that 'v' is a reference to the objects in in_args.
+// // The values we pass to FormatArg must outlive the call to
+// // FormatUntyped.
+// args.emplace_back(v);
+// }
+// absl::UntypedFormatSpec format(in_format);
+// if (!absl::FormatUntyped(&out, format, args)) {
+// return std::nullopt;
+// }
+// return std::move(out);
+// }
+//
+ABSL_MUST_USE_RESULT inline bool FormatUntyped(
+ FormatRawSink raw_sink, const UntypedFormatSpec& format,
+ absl::Span<const FormatArg> args) {
+ return str_format_internal::FormatUntyped(
+ str_format_internal::FormatRawSinkImpl::Extract(raw_sink),
+ str_format_internal::UntypedFormatSpecImpl::Extract(format), args);
+}
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+#endif // ABSL_STRINGS_STR_FORMAT_H_
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
new file mode 100644
index 00000000..77b8647f
--- /dev/null
+++ b/absl/strings/str_format_test.cc
@@ -0,0 +1,628 @@
+
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace {
+using str_format_internal::FormatArgImpl;
+
+class FormatEntryPointTest : public ::testing::Test { };
+
+TEST_F(FormatEntryPointTest, Format) {
+ std::string sink;
+ EXPECT_TRUE(Format(&sink, "A format %d", 123));
+ EXPECT_EQ("A format 123", sink);
+ sink.clear();
+
+ ParsedFormat<'d'> pc("A format %d");
+ EXPECT_TRUE(Format(&sink, pc, 123));
+ EXPECT_EQ("A format 123", sink);
+}
+TEST_F(FormatEntryPointTest, UntypedFormat) {
+ constexpr const char* formats[] = {
+ "",
+ "a",
+ "%80d",
+#if !defined(_MSC_VER) && !defined(__ANDROID__)
+ // MSVC and Android don't support positional syntax.
+ "complicated multipart %% %1$d format %1$0999d",
+#endif // _MSC_VER
+ };
+ for (const char* fmt : formats) {
+ std::string actual;
+ int i = 123;
+ FormatArgImpl arg_123(i);
+ absl::Span<const FormatArgImpl> args(&arg_123, 1);
+ UntypedFormatSpec format(fmt);
+
+ EXPECT_TRUE(FormatUntyped(&actual, format, args));
+ char buf[4096]{};
+ snprintf(buf, sizeof(buf), fmt, 123);
+ EXPECT_EQ(
+ str_format_internal::FormatPack(
+ str_format_internal::UntypedFormatSpecImpl::Extract(format), args),
+ buf);
+ EXPECT_EQ(actual, buf);
+ }
+ // The internal version works with a preparsed format.
+ ParsedFormat<'d'> pc("A format %d");
+ int i = 345;
+ FormatArg arg(i);
+ std::string out;
+ EXPECT_TRUE(str_format_internal::FormatUntyped(
+ &out, str_format_internal::UntypedFormatSpecImpl(&pc), {&arg, 1}));
+ EXPECT_EQ("A format 345", out);
+}
+
+TEST_F(FormatEntryPointTest, StringFormat) {
+ EXPECT_EQ("123", StrFormat("%d", 123));
+ constexpr absl::string_view view("=%d=", 4);
+ EXPECT_EQ("=123=", StrFormat(view, 123));
+}
+
+TEST_F(FormatEntryPointTest, AppendFormat) {
+ std::string s;
+ std::string& r = StrAppendFormat(&s, "%d", 123);
+ EXPECT_EQ(&s, &r); // should be same object
+ EXPECT_EQ("123", r);
+}
+
+TEST_F(FormatEntryPointTest, AppendFormatFail) {
+ std::string s = "orig";
+
+ UntypedFormatSpec format(" more %d");
+ FormatArgImpl arg("not an int");
+
+ EXPECT_EQ("orig",
+ str_format_internal::AppendPack(
+ &s, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ {&arg, 1}));
+}
+
+
+TEST_F(FormatEntryPointTest, ManyArgs) {
+ EXPECT_EQ("24", StrFormat("%24$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24));
+ EXPECT_EQ("60", StrFormat("%60$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60));
+}
+
+TEST_F(FormatEntryPointTest, Preparsed) {
+ ParsedFormat<'d'> pc("%d");
+ EXPECT_EQ("123", StrFormat(pc, 123));
+ // rvalue ok?
+ EXPECT_EQ("123", StrFormat(ParsedFormat<'d'>("%d"), 123));
+ constexpr absl::string_view view("=%d=", 4);
+ EXPECT_EQ("=123=", StrFormat(ParsedFormat<'d'>(view), 123));
+}
+
+TEST_F(FormatEntryPointTest, FormatCountCapture) {
+ int n = 0;
+ EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n)));
+ EXPECT_EQ(0, n);
+ EXPECT_EQ("123", StrFormat("%d%n", 123, FormatCountCapture(&n)));
+ EXPECT_EQ(3, n);
+}
+
+TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) {
+ // Should reject int*.
+ int n = 0;
+ UntypedFormatSpec format("%d%n");
+ int i = 123, *ip = &n;
+ FormatArgImpl args[2] = {FormatArgImpl(i), FormatArgImpl(ip)};
+
+ EXPECT_EQ("", str_format_internal::FormatPack(
+ str_format_internal::UntypedFormatSpecImpl::Extract(format),
+ absl::MakeSpan(args)));
+}
+
+TEST_F(FormatEntryPointTest, FormatCountCaptureMultiple) {
+ int n1 = 0;
+ int n2 = 0;
+ EXPECT_EQ(" 1 2",
+ StrFormat("%5d%n%10d%n", 1, FormatCountCapture(&n1), 2,
+ FormatCountCapture(&n2)));
+ EXPECT_EQ(5, n1);
+ EXPECT_EQ(15, n2);
+}
+
+TEST_F(FormatEntryPointTest, FormatCountCaptureExample) {
+ int n;
+ std::string s;
+ StrAppendFormat(&s, "%s: %n%s\n", "(1,1)", FormatCountCapture(&n), "(1,2)");
+ StrAppendFormat(&s, "%*s%s\n", n, "", "(2,2)");
+ EXPECT_EQ(7, n);
+ EXPECT_EQ(
+ "(1,1): (1,2)\n"
+ " (2,2)\n",
+ s);
+}
+
+TEST_F(FormatEntryPointTest, Stream) {
+ const std::string formats[] = {
+ "",
+ "a",
+ "%80d",
+#if !defined(_MSC_VER) && !defined(__ANDROID__)
+ // MSVC doesn't support positional syntax.
+ "complicated multipart %% %1$d format %1$080d",
+#endif // _MSC_VER
+ };
+ std::string buf(4096, '\0');
+ for (const auto& fmt : formats) {
+ const auto parsed = ParsedFormat<'d'>::NewAllowIgnored(fmt);
+ std::ostringstream oss;
+ oss << StreamFormat(*parsed, 123);
+ int fmt_result = snprintf(&*buf.begin(), buf.size(), fmt.c_str(), 123);
+ ASSERT_TRUE(oss) << fmt;
+ ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size())
+ << fmt_result;
+ EXPECT_EQ(buf.c_str(), oss.str());
+ }
+}
+
+TEST_F(FormatEntryPointTest, StreamOk) {
+ std::ostringstream oss;
+ oss << StreamFormat("hello %d", 123);
+ EXPECT_EQ("hello 123", oss.str());
+ EXPECT_TRUE(oss.good());
+}
+
+TEST_F(FormatEntryPointTest, StreamFail) {
+ std::ostringstream oss;
+ UntypedFormatSpec format("hello %d");
+ FormatArgImpl arg("non-numeric");
+ oss << str_format_internal::Streamable(
+ str_format_internal::UntypedFormatSpecImpl::Extract(format), {&arg, 1});
+ EXPECT_EQ("hello ", oss.str()); // partial write
+ EXPECT_TRUE(oss.fail());
+}
+
+std::string WithSnprintf(const char* fmt, ...) {
+ std::string buf;
+ buf.resize(128);
+ va_list va;
+ va_start(va, fmt);
+ int r = vsnprintf(&*buf.begin(), buf.size(), fmt, va);
+ va_end(va);
+ EXPECT_GE(r, 0);
+ EXPECT_LT(r, buf.size());
+ buf.resize(r);
+ return buf;
+}
+
+TEST_F(FormatEntryPointTest, FloatPrecisionArg) {
+ // Test that positional parameters for width and precision
+ // are indexed to precede the value.
+ // Also sanity check the same formats against snprintf.
+ EXPECT_EQ("0.1", StrFormat("%.1f", 0.1));
+ EXPECT_EQ("0.1", WithSnprintf("%.1f", 0.1));
+ EXPECT_EQ(" 0.1", StrFormat("%*.1f", 5, 0.1));
+ EXPECT_EQ(" 0.1", WithSnprintf("%*.1f", 5, 0.1));
+ EXPECT_EQ("0.1", StrFormat("%.*f", 1, 0.1));
+ EXPECT_EQ("0.1", WithSnprintf("%.*f", 1, 0.1));
+ EXPECT_EQ(" 0.1", StrFormat("%*.*f", 5, 1, 0.1));
+ EXPECT_EQ(" 0.1", WithSnprintf("%*.*f", 5, 1, 0.1));
+}
+namespace streamed_test {
+struct X {};
+std::ostream& operator<<(std::ostream& os, const X&) {
+ return os << "X";
+}
+} // streamed_test
+
+TEST_F(FormatEntryPointTest, FormatStreamed) {
+ EXPECT_EQ("123", StrFormat("%s", FormatStreamed(123)));
+ EXPECT_EQ(" 123", StrFormat("%5s", FormatStreamed(123)));
+ EXPECT_EQ("123 ", StrFormat("%-5s", FormatStreamed(123)));
+ EXPECT_EQ("X", StrFormat("%s", FormatStreamed(streamed_test::X())));
+ EXPECT_EQ("123", StrFormat("%s", FormatStreamed(StreamFormat("%d", 123))));
+}
+
+// Helper class that creates a temporary file and exposes a FILE* to it.
+// It will close the file on destruction.
+class TempFile {
+ public:
+ TempFile() : file_(std::tmpfile()) {}
+ ~TempFile() { std::fclose(file_); }
+
+ std::FILE* file() const { return file_; }
+
+ // Read the file into a std::string.
+ std::string ReadFile() {
+ std::fseek(file_, 0, SEEK_END);
+ int size = std::ftell(file_);
+ EXPECT_GT(size, 0);
+ std::rewind(file_);
+ std::string str(2 * size, ' ');
+ int read_bytes = std::fread(&str[0], 1, str.size(), file_);
+ EXPECT_EQ(read_bytes, size);
+ str.resize(read_bytes);
+ EXPECT_TRUE(std::feof(file_));
+ return str;
+ }
+
+ private:
+ std::FILE* file_;
+};
+
+TEST_F(FormatEntryPointTest, FPrintF) {
+ TempFile tmp;
+ int result =
+ FPrintF(tmp.file(), "STRING: %s NUMBER: %010d", std::string("ABC"), -19);
+ EXPECT_EQ(result, 30);
+ EXPECT_EQ(tmp.ReadFile(), "STRING: ABC NUMBER: -000000019");
+}
+
+TEST_F(FormatEntryPointTest, FPrintFError) {
+ errno = 0;
+ int result = FPrintF(stdin, "ABC");
+ EXPECT_LT(result, 0);
+ EXPECT_EQ(errno, EBADF);
+}
+
+#if __GLIBC__
+TEST_F(FormatEntryPointTest, FprintfTooLarge) {
+ std::FILE* f = std::fopen("/dev/null", "w");
+ int width = 2000000000;
+ errno = 0;
+ int result = FPrintF(f, "%*d %*d", width, 0, width, 0);
+ EXPECT_LT(result, 0);
+ EXPECT_EQ(errno, EFBIG);
+ std::fclose(f);
+}
+
+TEST_F(FormatEntryPointTest, PrintF) {
+ int stdout_tmp = dup(STDOUT_FILENO);
+
+ TempFile tmp;
+ std::fflush(stdout);
+ dup2(fileno(tmp.file()), STDOUT_FILENO);
+
+ int result = PrintF("STRING: %s NUMBER: %010d", std::string("ABC"), -19);
+
+ std::fflush(stdout);
+ dup2(stdout_tmp, STDOUT_FILENO);
+ close(stdout_tmp);
+
+ EXPECT_EQ(result, 30);
+ EXPECT_EQ(tmp.ReadFile(), "STRING: ABC NUMBER: -000000019");
+}
+#endif // __GLIBC__
+
+TEST_F(FormatEntryPointTest, SNPrintF) {
+ char buffer[16];
+ int result =
+ SNPrintF(buffer, sizeof(buffer), "STRING: %s", std::string("ABC"));
+ EXPECT_EQ(result, 11);
+ EXPECT_EQ(std::string(buffer), "STRING: ABC");
+
+ result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456);
+ EXPECT_EQ(result, 14);
+ EXPECT_EQ(std::string(buffer), "NUMBER: 123456");
+
+ result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 1234567);
+ EXPECT_EQ(result, 15);
+ EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
+
+ result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 12345678);
+ EXPECT_EQ(result, 16);
+ EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
+
+ result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456789);
+ EXPECT_EQ(result, 17);
+ EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
+
+ result = SNPrintF(nullptr, 0, "Just checking the %s of the output.", "size");
+ EXPECT_EQ(result, 37);
+}
+
+TEST(StrFormat, BehavesAsDocumented) {
+ std::string s = absl::StrFormat("%s, %d!", "Hello", 123);
+ EXPECT_EQ("Hello, 123!", s);
+ // The format of a replacement is
+ // '%'[position][flags][width['.'precision]][length_modifier][format]
+ EXPECT_EQ(absl::StrFormat("%1$+3.2Lf", 1.1), "+1.10");
+ // Text conversion:
+ // "c" - Character. Eg: 'a' -> "A", 20 -> " "
+ EXPECT_EQ(StrFormat("%c", 'a'), "a");
+ EXPECT_EQ(StrFormat("%c", 0x20), " ");
+ // Formats char and integral types: int, long, uint64_t, etc.
+ EXPECT_EQ(StrFormat("%c", int{'a'}), "a");
+ EXPECT_EQ(StrFormat("%c", long{'a'}), "a"); // NOLINT
+ EXPECT_EQ(StrFormat("%c", uint64_t{'a'}), "a");
+ // "s" - std::string Eg: "C" -> "C", std::string("C++") -> "C++"
+ // Formats std::string, char*, string_view, and Cord.
+ EXPECT_EQ(StrFormat("%s", "C"), "C");
+ EXPECT_EQ(StrFormat("%s", std::string("C++")), "C++");
+ EXPECT_EQ(StrFormat("%s", string_view("view")), "view");
+ // Integral Conversion
+ // These format integral types: char, int, long, uint64_t, etc.
+ EXPECT_EQ(StrFormat("%d", char{10}), "10");
+ EXPECT_EQ(StrFormat("%d", int{10}), "10");
+ EXPECT_EQ(StrFormat("%d", long{10}), "10"); // NOLINT
+ EXPECT_EQ(StrFormat("%d", uint64_t{10}), "10");
+ // d,i - signed decimal Eg: -10 -> "-10"
+ EXPECT_EQ(StrFormat("%d", -10), "-10");
+ EXPECT_EQ(StrFormat("%i", -10), "-10");
+ // o - octal Eg: 10 -> "12"
+ EXPECT_EQ(StrFormat("%o", 10), "12");
+ // u - unsigned decimal Eg: 10 -> "10"
+ EXPECT_EQ(StrFormat("%u", 10), "10");
+ // x/X - lower,upper case hex Eg: 10 -> "a"/"A"
+ EXPECT_EQ(StrFormat("%x", 10), "a");
+ EXPECT_EQ(StrFormat("%X", 10), "A");
+ // Floating-point, with upper/lower-case output.
+ // These format floating points types: float, double, long double, etc.
+ EXPECT_EQ(StrFormat("%.1f", float{1}), "1.0");
+ EXPECT_EQ(StrFormat("%.1f", double{1}), "1.0");
+ const long double long_double = 1.0;
+ EXPECT_EQ(StrFormat("%.1f", long_double), "1.0");
+ // These also format integral types: char, int, long, uint64_t, etc.:
+ EXPECT_EQ(StrFormat("%.1f", char{1}), "1.0");
+ EXPECT_EQ(StrFormat("%.1f", int{1}), "1.0");
+ EXPECT_EQ(StrFormat("%.1f", long{1}), "1.0"); // NOLINT
+ EXPECT_EQ(StrFormat("%.1f", uint64_t{1}), "1.0");
+ // f/F - decimal. Eg: 123456789 -> "123456789.000000"
+ EXPECT_EQ(StrFormat("%f", 123456789), "123456789.000000");
+ EXPECT_EQ(StrFormat("%F", 123456789), "123456789.000000");
+ // e/E - exponentiated Eg: .01 -> "1.00000e-2"/"1.00000E-2"
+ EXPECT_EQ(StrFormat("%e", .01), "1.000000e-02");
+ EXPECT_EQ(StrFormat("%E", .01), "1.000000E-02");
+ // g/G - exponentiate to fit Eg: .01 -> "0.01", 1e10 ->"1e+10"/"1E+10"
+ EXPECT_EQ(StrFormat("%g", .01), "0.01");
+ EXPECT_EQ(StrFormat("%g", 1e10), "1e+10");
+ EXPECT_EQ(StrFormat("%G", 1e10), "1E+10");
+ // a/A - lower,upper case hex Eg: -3.0 -> "-0x1.8p+1"/"-0X1.8P+1"
+
+// On Android platform <=21, there is a regression in hexfloat formatting.
+#if !defined(__ANDROID_API__) || __ANDROID_API__ > 21
+ EXPECT_EQ(StrFormat("%.1a", -3.0), "-0x1.8p+1"); // .1 to fix MSVC output
+ EXPECT_EQ(StrFormat("%.1A", -3.0), "-0X1.8P+1"); // .1 to fix MSVC output
+#endif
+
+ // Other conversion
+ int64_t value = 0x7ffdeb4;
+ auto ptr_value = static_cast<uintptr_t>(value);
+ const int& something = *reinterpret_cast<const int*>(ptr_value);
+ EXPECT_EQ(StrFormat("%p", &something), StrFormat("0x%x", ptr_value));
+
+ // Output widths are supported, with optional flags.
+ EXPECT_EQ(StrFormat("%3d", 1), " 1");
+ EXPECT_EQ(StrFormat("%3d", 123456), "123456");
+ EXPECT_EQ(StrFormat("%06.2f", 1.234), "001.23");
+ EXPECT_EQ(StrFormat("%+d", 1), "+1");
+ EXPECT_EQ(StrFormat("% d", 1), " 1");
+ EXPECT_EQ(StrFormat("%-4d", -1), "-1 ");
+ EXPECT_EQ(StrFormat("%#o", 10), "012");
+ EXPECT_EQ(StrFormat("%#x", 15), "0xf");
+ EXPECT_EQ(StrFormat("%04d", 8), "0008");
+ // Posix positional substitution.
+ EXPECT_EQ(absl::StrFormat("%2$s, %3$s, %1$s!", "vici", "veni", "vidi"),
+ "veni, vidi, vici!");
+ // Length modifiers are ignored.
+ EXPECT_EQ(StrFormat("%hhd", int{1}), "1");
+ EXPECT_EQ(StrFormat("%hd", int{1}), "1");
+ EXPECT_EQ(StrFormat("%ld", int{1}), "1");
+ EXPECT_EQ(StrFormat("%lld", int{1}), "1");
+ EXPECT_EQ(StrFormat("%Ld", int{1}), "1");
+ EXPECT_EQ(StrFormat("%jd", int{1}), "1");
+ EXPECT_EQ(StrFormat("%zd", int{1}), "1");
+ EXPECT_EQ(StrFormat("%td", int{1}), "1");
+ EXPECT_EQ(StrFormat("%qd", int{1}), "1");
+}
+
+using str_format_internal::ExtendedParsedFormat;
+using str_format_internal::ParsedFormatBase;
+
+struct SummarizeConsumer {
+ std::string* out;
+ explicit SummarizeConsumer(std::string* out) : out(out) {}
+
+ bool Append(string_view s) {
+ *out += "[" + std::string(s) + "]";
+ return true;
+ }
+
+ bool ConvertOne(const str_format_internal::UnboundConversion& conv,
+ string_view s) {
+ *out += "{";
+ *out += std::string(s);
+ *out += ":";
+ *out += std::to_string(conv.arg_position) + "$";
+ if (conv.width.is_from_arg()) {
+ *out += std::to_string(conv.width.get_from_arg()) + "$*";
+ }
+ if (conv.precision.is_from_arg()) {
+ *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
+ }
+ *out += conv.conv.Char();
+ *out += "}";
+ return true;
+ }
+};
+
+std::string SummarizeParsedFormat(const ParsedFormatBase& pc) {
+ std::string out;
+ if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!";
+ return out;
+}
+
+class ParsedFormatTest : public testing::Test {};
+
+TEST_F(ParsedFormatTest, SimpleChecked) {
+ EXPECT_EQ("[ABC]{d:1$d}[DEF]",
+ SummarizeParsedFormat(ParsedFormat<'d'>("ABC%dDEF")));
+ EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}",
+ SummarizeParsedFormat(ParsedFormat<'s', 'd', 'f'>("%sFFF%dZZZ%f")));
+ EXPECT_EQ("{s:1$s}[ ]{.*d:3$.2$*d}",
+ SummarizeParsedFormat(ParsedFormat<'s', '*', 'd'>("%s %.*d")));
+}
+
+TEST_F(ParsedFormatTest, SimpleUncheckedCorrect) {
+ auto f = ParsedFormat<'d'>::New("ABC%dDEF");
+ ASSERT_TRUE(f);
+ EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
+
+ std::string format = "%sFFF%dZZZ%f";
+ auto f2 = ParsedFormat<'s', 'd', 'f'>::New(format);
+
+ ASSERT_TRUE(f2);
+ EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
+
+ f2 = ParsedFormat<'s', 'd', 'f'>::New("%s %d %f");
+
+ ASSERT_TRUE(f2);
+ EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
+
+ auto star = ParsedFormat<'*', 'd'>::New("%*d");
+ ASSERT_TRUE(star);
+ EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star));
+
+ auto dollar = ParsedFormat<'d', 's'>::New("%2$s %1$d");
+ ASSERT_TRUE(dollar);
+ EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar));
+ // with reuse
+ dollar = ParsedFormat<'d', 's'>::New("%2$s %1$d %1$d");
+ ASSERT_TRUE(dollar);
+ EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}",
+ SummarizeParsedFormat(*dollar));
+}
+
+TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) {
+ EXPECT_FALSE((ParsedFormat<'d', 's'>::New("ABC")));
+ EXPECT_FALSE((ParsedFormat<'d', 's'>::New("%dABC")));
+ EXPECT_FALSE((ParsedFormat<'d', 's'>::New("ABC%2$s")));
+ auto f = ParsedFormat<'d', 's'>::NewAllowIgnored("ABC");
+ ASSERT_TRUE(f);
+ EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
+ f = ParsedFormat<'d', 's'>::NewAllowIgnored("%dABC");
+ ASSERT_TRUE(f);
+ EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f));
+ f = ParsedFormat<'d', 's'>::NewAllowIgnored("ABC%2$s");
+ ASSERT_TRUE(f);
+ EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
+}
+
+TEST_F(ParsedFormatTest, SimpleUncheckedUnsupported) {
+ EXPECT_FALSE(ParsedFormat<'d'>::New("%1$d %1$x"));
+ EXPECT_FALSE(ParsedFormat<'x'>::New("%1$d %1$x"));
+}
+
+TEST_F(ParsedFormatTest, SimpleUncheckedIncorrect) {
+ EXPECT_FALSE(ParsedFormat<'d'>::New(""));
+
+ EXPECT_FALSE(ParsedFormat<'d'>::New("ABC%dDEF%d"));
+
+ std::string format = "%sFFF%dZZZ%f";
+ EXPECT_FALSE((ParsedFormat<'s', 'd', 'g'>::New(format)));
+}
+
+using str_format_internal::Conv;
+
+TEST_F(ParsedFormatTest, UncheckedCorrect) {
+ auto f = ExtendedParsedFormat<Conv::d>::New("ABC%dDEF");
+ ASSERT_TRUE(f);
+ EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
+
+ std::string format = "%sFFF%dZZZ%f";
+ auto f2 =
+ ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New(format);
+
+ ASSERT_TRUE(f2);
+ EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
+
+ f2 = ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New(
+ "%s %d %f");
+
+ ASSERT_TRUE(f2);
+ EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
+
+ auto star = ExtendedParsedFormat<Conv::star, Conv::d>::New("%*d");
+ ASSERT_TRUE(star);
+ EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star));
+
+ auto dollar = ExtendedParsedFormat<Conv::d, Conv::s>::New("%2$s %1$d");
+ ASSERT_TRUE(dollar);
+ EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar));
+ // with reuse
+ dollar = ExtendedParsedFormat<Conv::d, Conv::s>::New("%2$s %1$d %1$d");
+ ASSERT_TRUE(dollar);
+ EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}",
+ SummarizeParsedFormat(*dollar));
+}
+
+TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
+ EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("ABC")));
+ EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("%dABC")));
+ EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("ABC%2$s")));
+ auto f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("ABC");
+ ASSERT_TRUE(f);
+ EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
+ f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("%dABC");
+ ASSERT_TRUE(f);
+ EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f));
+ f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("ABC%2$s");
+ ASSERT_TRUE(f);
+ EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
+}
+
+TEST_F(ParsedFormatTest, UncheckedMultipleTypes) {
+ auto dx = ExtendedParsedFormat<Conv::d | Conv::x>::New("%1$d %1$x");
+ EXPECT_TRUE(dx);
+ EXPECT_EQ("{1$d:1$d}[ ]{1$x:1$x}", SummarizeParsedFormat(*dx));
+
+ dx = ExtendedParsedFormat<Conv::d | Conv::x>::New("%1$d");
+ EXPECT_TRUE(dx);
+ EXPECT_EQ("{1$d:1$d}", SummarizeParsedFormat(*dx));
+}
+
+TEST_F(ParsedFormatTest, UncheckedIncorrect) {
+ EXPECT_FALSE(ExtendedParsedFormat<Conv::d>::New(""));
+
+ EXPECT_FALSE(ExtendedParsedFormat<Conv::d>::New("ABC%dDEF%d"));
+
+ std::string format = "%sFFF%dZZZ%f";
+ EXPECT_FALSE((ExtendedParsedFormat<Conv::s, Conv::d, Conv::g>::New(format)));
+}
+
+TEST_F(ParsedFormatTest, RegressionMixPositional) {
+ EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::o>::New("%1$d %o")));
+}
+
+} // namespace
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+// Some codegen thunks that we can use to easily dump the generated assembly for
+// different StrFormat calls.
+
+std::string CodegenAbslStrFormatInt(int i) { // NOLINT
+ return absl::StrFormat("%d", i);
+}
+
+std::string CodegenAbslStrFormatIntStringInt64(int i, const std::string& s,
+ int64_t i64) { // NOLINT
+ return absl::StrFormat("%d %s %d", i, s, i64);
+}
+
+void CodegenAbslStrAppendFormatInt(std::string* out, int i) { // NOLINT
+ absl::StrAppendFormat(out, "%d", i);
+}
+
+void CodegenAbslStrAppendFormatIntStringInt64(std::string* out, int i,
+ const std::string& s,
+ int64_t i64) { // NOLINT
+ absl::StrAppendFormat(out, "%d %s %d", i, s, i64);
+}
diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h
index bce2cd66..dc476a22 100644
--- a/absl/strings/str_join.h
+++ b/absl/strings/str_join.h
@@ -18,10 +18,10 @@
// -----------------------------------------------------------------------------
//
// This header file contains functions for joining a range of elements and
-// returning the result as a std::string. StrJoin operations are specified by passing
-// a range, a separator std::string to use between the elements joined, and an
+// returning the result as a string. StrJoin operations are specified by passing
+// a range, a separator string to use between the elements joined, and an
// optional Formatter responsible for converting each argument in the range to a
-// std::string. If omitted, a default `AlphaNumFormatter()` is called on the elements
+// string. If omitted, a default `AlphaNumFormatter()` is called on the elements
// to be joined, using the same formatting that `absl::StrCat()` uses. This
// package defines a number of default formatters, and you can define your own
// implementations.
@@ -29,7 +29,7 @@
// Ranges are specified by passing a container with `std::begin()` and
// `std::end()` iterators, container-specific `begin()` and `end()` iterators, a
// brace-initialized `std::initializer_list`, or a `std::tuple` of heterogeneous
-// objects. The separator std::string is specified as an `absl::string_view`.
+// objects. The separator string is specified as an `absl::string_view`.
//
// Because the default formatter uses the `absl::AlphaNum` class,
// `absl::StrJoin()`, like `absl::StrCat()`, will work out-of-the-box on
@@ -37,8 +37,8 @@
//
// Example:
//
-// std::vector<std::string> v = {"foo", "bar", "baz"};
-// std::string s = absl::StrJoin(v, "-");
+// std::vector<string> v = {"foo", "bar", "baz"};
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("foo-bar-baz", s);
//
// See comments on the `absl::StrJoin()` function for more examples.
@@ -52,6 +52,7 @@
#include <iterator>
#include <string>
#include <tuple>
+#include <type_traits>
#include <utility>
#include "absl/base/macros.h"
@@ -59,23 +60,23 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// Concept: Formatter
// -----------------------------------------------------------------------------
//
// A Formatter is a function object that is responsible for formatting its
-// argument as a std::string and appending it to a given output std::string. Formatters
+// argument as a string and appending it to a given output string. Formatters
// may be implemented as function objects, lambdas, or normal functions. You may
// provide your own Formatter to enable `absl::StrJoin()` to work with arbitrary
// types.
//
// The following is an example of a custom Formatter that simply uses
-// `std::to_string()` to format an integer as a std::string.
+// `std::to_string()` to format an integer as a string.
//
// struct MyFormatter {
-// void operator()(std::string* out, int i) const {
+// void operator()(string* out, int i) const {
// out->append(std::to_string(i));
// }
// };
@@ -84,7 +85,7 @@ inline namespace lts_2018_06_20 {
// argument to `absl::StrJoin()`:
//
// std::vector<int> v = {1, 2, 3, 4};
-// std::string s = absl::StrJoin(v, "-", MyFormatter());
+// string s = absl::StrJoin(v, "-", MyFormatter());
// EXPECT_EQ("1-2-3-4", s);
//
// The following standard formatters are provided within this file:
@@ -156,10 +157,10 @@ DereferenceFormatter() {
// StrJoin()
// -----------------------------------------------------------------------------
//
-// Joins a range of elements and returns the result as a std::string.
-// `absl::StrJoin()` takes a range, a separator std::string to use between the
+// Joins a range of elements and returns the result as a string.
+// `absl::StrJoin()` takes a range, a separator string to use between the
// elements joined, and an optional Formatter responsible for converting each
-// argument in the range to a std::string.
+// argument in the range to a string.
//
// If omitted, the default `AlphaNumFormatter()` is called on the elements to be
// joined.
@@ -167,22 +168,22 @@ DereferenceFormatter() {
// Example 1:
// // Joins a collection of strings. This pattern also works with a collection
// // of `absl::string_view` or even `const char*`.
-// std::vector<std::string> v = {"foo", "bar", "baz"};
-// std::string s = absl::StrJoin(v, "-");
+// std::vector<string> v = {"foo", "bar", "baz"};
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("foo-bar-baz", s);
//
// Example 2:
// // Joins the values in the given `std::initializer_list<>` specified using
// // brace initialization. This pattern also works with an initializer_list
// // of ints or `absl::string_view` -- any `AlphaNum`-compatible type.
-// std::string s = absl::StrJoin({"foo", "bar", "baz"}, "-");
+// string s = absl::StrJoin({"foo", "bar", "baz"}, "-");
// EXPECT_EQ("foo-bar-baz", s);
//
// Example 3:
// // Joins a collection of ints. This pattern also works with floats,
// // doubles, int64s -- any `StrCat()`-compatible type.
// std::vector<int> v = {1, 2, 3, -4};
-// std::string s = absl::StrJoin(v, "-");
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("1-2-3--4", s);
//
// Example 4:
@@ -193,7 +194,7 @@ DereferenceFormatter() {
// // `std::vector<int*>`.
// int x = 1, y = 2, z = 3;
// std::vector<int*> v = {&x, &y, &z};
-// std::string s = absl::StrJoin(v, "-");
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("1-2-3", s);
//
// Example 5:
@@ -202,42 +203,42 @@ DereferenceFormatter() {
// v.emplace_back(new int(1));
// v.emplace_back(new int(2));
// v.emplace_back(new int(3));
-// std::string s = absl::StrJoin(v, "-");
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("1-2-3", s);
//
// Example 6:
// // Joins a `std::map`, with each key-value pair separated by an equals
// // sign. This pattern would also work with, say, a
// // `std::vector<std::pair<>>`.
-// std::map<std::string, int> m = {
+// std::map<string, int> m = {
// std::make_pair("a", 1),
// std::make_pair("b", 2),
// std::make_pair("c", 3)};
-// std::string s = absl::StrJoin(m, ",", absl::PairFormatter("="));
+// string s = absl::StrJoin(m, ",", absl::PairFormatter("="));
// EXPECT_EQ("a=1,b=2,c=3", s);
//
// Example 7:
// // These examples show how `absl::StrJoin()` handles a few common edge
// // cases:
-// std::vector<std::string> v_empty;
+// std::vector<string> v_empty;
// EXPECT_EQ("", absl::StrJoin(v_empty, "-"));
//
-// std::vector<std::string> v_one_item = {"foo"};
+// std::vector<string> v_one_item = {"foo"};
// EXPECT_EQ("foo", absl::StrJoin(v_one_item, "-"));
//
-// std::vector<std::string> v_empty_string = {""};
+// std::vector<string> v_empty_string = {""};
// EXPECT_EQ("", absl::StrJoin(v_empty_string, "-"));
//
-// std::vector<std::string> v_one_item_empty_string = {"a", ""};
+// std::vector<string> v_one_item_empty_string = {"a", ""};
// EXPECT_EQ("a-", absl::StrJoin(v_one_item_empty_string, "-"));
//
-// std::vector<std::string> v_two_empty_string = {"", ""};
+// std::vector<string> v_two_empty_string = {"", ""};
// EXPECT_EQ("-", absl::StrJoin(v_two_empty_string, "-"));
//
// Example 8:
// // Joins a `std::tuple<T...>` of heterogeneous types, converting each to
-// // a std::string using the `absl::AlphaNum` class.
-// std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
+// // a string using the `absl::AlphaNum` class.
+// string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
// EXPECT_EQ("123-abc-0.456", s);
template <typename Iterator, typename Formatter>
@@ -284,7 +285,7 @@ std::string StrJoin(const std::tuple<T...>& value, absl::string_view separator)
return strings_internal::JoinAlgorithm(value, separator, AlphaNumFormatter());
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STR_JOIN_H_
diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc
index c50d314a..72a0b584 100644
--- a/absl/strings/str_replace.cc
+++ b/absl/strings/str_replace.cc
@@ -17,7 +17,7 @@
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
using FixedMapping =
@@ -77,5 +77,5 @@ int StrReplaceAll(strings_internal::FixedMapping replacements, std::string* targ
return StrReplaceAll<strings_internal::FixedMapping>(replacements, target);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h
index 7ba43946..a963f91e 100644
--- a/absl/strings/str_replace.h
+++ b/absl/strings/str_replace.h
@@ -17,19 +17,19 @@
// File: str_replace.h
// -----------------------------------------------------------------------------
//
-// This file defines `absl::StrReplaceAll()`, a general-purpose std::string
+// This file defines `absl::StrReplaceAll()`, a general-purpose string
// replacement function designed for large, arbitrary text substitutions,
// especially on strings which you are receiving from some other system for
// further processing (e.g. processing regular expressions, escaping HTML
-// entities, etc. `StrReplaceAll` is designed to be efficient even when only
+// entities, etc.). `StrReplaceAll` is designed to be efficient even when only
// one substitution is being performed, or when substitution is rare.
//
-// If the std::string being modified is known at compile-time, and the substitutions
+// If the string being modified is known at compile-time, and the substitutions
// vary, `absl::Substitute()` may be a better choice.
//
// Example:
//
-// std::string html_escaped = absl::StrReplaceAll(user_input, {
+// string html_escaped = absl::StrReplaceAll(user_input, {
// {"&", "&amp;"},
// {"<", "&lt;"},
// {">", "&gt;"},
@@ -46,20 +46,20 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// StrReplaceAll()
//
-// Replaces character sequences within a given std::string with replacements provided
+// Replaces character sequences within a given string with replacements provided
// within an initializer list of key/value pairs. Candidate replacements are
-// considered in order as they occur within the std::string, with earlier matches
+// considered in order as they occur within the string, with earlier matches
// taking precedence, and longer matches taking precedence for candidates
-// starting at the same position in the std::string. Once a substitution is made, the
+// starting at the same position in the string. Once a substitution is made, the
// replaced text is not considered for any further substitutions.
//
// Example:
//
-// std::string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
+// string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
// {{"$count", absl::StrCat(5)},
// {"$who", "Bob"},
// {"#Noun", "Apples"}});
@@ -79,28 +79,28 @@ ABSL_MUST_USE_RESULT std::string StrReplaceAll(
// replacements["$who"] = "Bob";
// replacements["$count"] = "5";
// replacements["#Noun"] = "Apples";
-// std::string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
+// string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
// replacements);
// EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
//
// // A std::vector of std::pair elements can be more efficient.
-// std::vector<std::pair<const absl::string_view, std::string>> replacements;
+// std::vector<std::pair<const absl::string_view, string>> replacements;
// replacements.push_back({"&", "&amp;"});
// replacements.push_back({"<", "&lt;"});
// replacements.push_back({">", "&gt;"});
-// std::string s = absl::StrReplaceAll("if (ptr < &foo)",
+// string s = absl::StrReplaceAll("if (ptr < &foo)",
// replacements);
// EXPECT_EQ("if (ptr &lt; &amp;foo)", s);
template <typename StrToStrMapping>
std::string StrReplaceAll(absl::string_view s, const StrToStrMapping& replacements);
// Overload of `StrReplaceAll()` to replace character sequences within a given
-// output std::string *in place* with replacements provided within an initializer
+// output string *in place* with replacements provided within an initializer
// list of key/value pairs, returning the number of substitutions that occurred.
//
// Example:
//
-// std::string s = std::string("$who bought $count #Noun. Thanks $who!");
+// string s = std::string("$who bought $count #Noun. Thanks $who!");
// int count;
// count = absl::StrReplaceAll({{"$count", absl::StrCat(5)},
// {"$who", "Bob"},
@@ -113,12 +113,12 @@ int StrReplaceAll(
std::string* target);
// Overload of `StrReplaceAll()` to replace patterns within a given output
-// std::string *in place* with replacements provided within a container of key/value
+// string *in place* with replacements provided within a container of key/value
// pairs.
//
// Example:
//
-// std::string s = std::string("if (ptr < &foo)");
+// string s = std::string("if (ptr < &foo)");
// int count = absl::StrReplaceAll({{"&", "&amp;"},
// {"<", "&lt;"},
// {">", "&gt;"}}, &s);
@@ -209,7 +209,7 @@ int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) {
return substitutions;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STR_REPLACE_H_
diff --git a/absl/strings/str_replace_benchmark.cc b/absl/strings/str_replace_benchmark.cc
index e608de8d..8386f2e6 100644
--- a/absl/strings/str_replace_benchmark.cc
+++ b/absl/strings/str_replace_benchmark.cc
@@ -38,16 +38,16 @@ struct Replacement {
{"liquor", "shakes"}, //
};
-// Here, we set up a std::string for use in global-replace benchmarks.
+// Here, we set up a string for use in global-replace benchmarks.
// We started with a million blanks, and then deterministically insert
-// 10,000 copies each of two pangrams. The result is a std::string that is
+// 10,000 copies each of two pangrams. The result is a string that is
// 40% blank space and 60% these words. 'the' occurs 18,247 times and
// all the substitutions together occur 49,004 times.
//
-// We then create "after_replacing_the" to be a std::string that is a result of
+// We then create "after_replacing_the" to be a string that is a result of
// replacing "the" with "box" in big_string.
//
-// And then we create "after_replacing_many" to be a std::string that is result
+// And then we create "after_replacing_many" to be a string that is result
// of preferring several substitutions.
void SetUpStrings() {
if (big_string == nullptr) {
diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc
index 2a31e09e..cd90425f 100644
--- a/absl/strings/str_split.cc
+++ b/absl/strings/str_split.cc
@@ -27,7 +27,7 @@
#include "absl/strings/ascii.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
@@ -44,10 +44,11 @@ absl::string_view GenericFind(absl::string_view text,
if (delimiter.empty() && text.length() > 0) {
// Special case for empty std::string delimiters: always return a zero-length
// absl::string_view referring to the item at position 1 past pos.
- return absl::string_view(text.begin() + pos + 1, 0);
+ return absl::string_view(text.data() + pos + 1, 0);
}
size_t found_pos = absl::string_view::npos;
- absl::string_view found(text.end(), 0); // By default, not found
+ absl::string_view found(text.data() + text.size(),
+ 0); // By default, not found
found_pos = find_policy.Find(text, delimiter, pos);
if (found_pos != absl::string_view::npos) {
found = absl::string_view(text.data() + found_pos,
@@ -88,7 +89,7 @@ absl::string_view ByString::Find(absl::string_view text, size_t pos) const {
// absl::string_view.
size_t found_pos = text.find(delimiter_[0], pos);
if (found_pos == absl::string_view::npos)
- return absl::string_view(text.end(), 0);
+ return absl::string_view(text.data() + text.size(), 0);
return text.substr(found_pos, 1);
}
return GenericFind(text, delimiter_, pos, LiteralPolicy());
@@ -101,7 +102,7 @@ absl::string_view ByString::Find(absl::string_view text, size_t pos) const {
absl::string_view ByChar::Find(absl::string_view text, size_t pos) const {
size_t found_pos = text.find(c_, pos);
if (found_pos == absl::string_view::npos)
- return absl::string_view(text.end(), 0);
+ return absl::string_view(text.data() + text.size(), 0);
return text.substr(found_pos, 1);
}
@@ -129,10 +130,10 @@ absl::string_view ByLength::Find(absl::string_view text,
// If the std::string is shorter than the chunk size we say we
// "can't find the delimiter" so this will be the last chunk.
if (substr.length() <= static_cast<size_t>(length_))
- return absl::string_view(text.end(), 0);
+ return absl::string_view(text.data() + text.size(), 0);
- return absl::string_view(substr.begin() + length_, 0);
+ return absl::string_view(substr.data() + length_, 0);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h
index b9f586b9..9483b30e 100644
--- a/absl/strings/str_split.h
+++ b/absl/strings/str_split.h
@@ -19,13 +19,13 @@
//
// This file contains functions for splitting strings. It defines the main
// `StrSplit()` function, several delimiters for determining the boundaries on
-// which to split the std::string, and predicates for filtering delimited results.
+// which to split the string, and predicates for filtering delimited results.
// `StrSplit()` adapts the returned collection to the type specified by the
// caller.
//
// Example:
//
-// // Splits the given std::string on commas. Returns the results in a
+// // Splits the given string on commas. Returns the results in a
// // vector of strings.
// std::vector<std::string> v = absl::StrSplit("a,b,c", ',');
// // Can also use ","
@@ -49,14 +49,14 @@
#include "absl/strings/strip.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
//------------------------------------------------------------------------------
// Delimiters
//------------------------------------------------------------------------------
//
// `StrSplit()` uses delimiters to define the boundaries between elements in the
-// provided input. Several `Delimiter` types are defined below. If a std::string
+// provided input. Several `Delimiter` types are defined below. If a string
// (`const char*`, `std::string`, or `absl::string_view`) is passed in place of
// an explicit `Delimiter` object, `StrSplit()` treats it the same way as if it
// were passed a `ByString` delimiter.
@@ -66,7 +66,7 @@ inline namespace lts_2018_06_20 {
//
// The following `Delimiter` types are available for use within `StrSplit()`:
//
-// - `ByString` (default for std::string arguments)
+// - `ByString` (default for string arguments)
// - `ByChar` (default for a char argument)
// - `ByAnyChar`
// - `ByLength`
@@ -77,15 +77,15 @@ inline namespace lts_2018_06_20 {
// be split and the position to begin searching for the next delimiter in the
// input text. The returned absl::string_view should refer to the next
// occurrence (after pos) of the represented delimiter; this returned
-// absl::string_view represents the next location where the input std::string should
+// absl::string_view represents the next location where the input string should
// be broken. The returned absl::string_view may be zero-length if the Delimiter
-// does not represent a part of the std::string (e.g., a fixed-length delimiter). If
+// does not represent a part of the string (e.g., a fixed-length delimiter). If
// no delimiter is found in the given text, a zero-length absl::string_view
// referring to text.end() should be returned (e.g.,
// absl::string_view(text.end(), 0)). It is important that the returned
// absl::string_view always be within the bounds of input text given as an
-// argument--it must not refer to a std::string that is physically located outside of
-// the given std::string.
+// argument--it must not refer to a string that is physically located outside of
+// the given string.
//
// The following example is a simple Delimiter object that is created with a
// single char and will look for that char in the text passed to the Find()
@@ -105,13 +105,13 @@ inline namespace lts_2018_06_20 {
// ByString
//
-// A sub-std::string delimiter. If `StrSplit()` is passed a std::string in place of a
-// `Delimiter` object, the std::string will be implicitly converted into a
+// A sub-string delimiter. If `StrSplit()` is passed a string in place of a
+// `Delimiter` object, the string will be implicitly converted into a
// `ByString` delimiter.
//
// Example:
//
-// // Because a std::string literal is converted to an `absl::ByString`,
+// // Because a string literal is converted to an `absl::ByString`,
// // the following two splits are equivalent.
//
// std::vector<std::string> v1 = absl::StrSplit("a, b, c", ", ");
@@ -132,7 +132,7 @@ class ByString {
// ByChar
//
// A single character delimiter. `ByChar` is functionally equivalent to a
-// 1-char std::string within a `ByString` delimiter, but slightly more
+// 1-char string within a `ByString` delimiter, but slightly more
// efficient.
//
// Example:
@@ -165,9 +165,9 @@ class ByChar {
// ByAnyChar
//
// A delimiter that will match any of the given byte-sized characters within
-// its provided std::string.
+// its provided string.
//
-// Note: this delimiter works with single-byte std::string data, but does not work
+// Note: this delimiter works with single-byte string data, but does not work
// with variable-width encodings, such as UTF-8.
//
// Example:
@@ -176,8 +176,8 @@ class ByChar {
// std::vector<std::string> v = absl::StrSplit("a,b=c", ByAnyChar(",="));
// // v[0] == "a", v[1] == "b", v[2] == "c"
//
-// If `ByAnyChar` is given the empty std::string, it behaves exactly like
-// `ByString` and matches each individual character in the input std::string.
+// If `ByAnyChar` is given the empty string, it behaves exactly like
+// `ByString` and matches each individual character in the input string.
//
class ByAnyChar {
public:
@@ -193,7 +193,7 @@ class ByAnyChar {
// A delimiter for splitting into equal-length strings. The length argument to
// the constructor must be greater than 0.
//
-// Note: this delimiter works with single-byte std::string data, but does not work
+// Note: this delimiter works with single-byte string data, but does not work
// with variable-width encodings, such as UTF-8.
//
// Example:
@@ -203,7 +203,7 @@ class ByAnyChar {
// // v[0] == "123", v[1] == "456", v[2] == "789"
//
-// Note that the std::string does not have to be a multiple of the fixed split
+// Note that the string does not have to be a multiple of the fixed split
// length. In such a case, the last substring will be shorter.
//
// using absl::ByLength;
@@ -224,9 +224,9 @@ namespace strings_internal {
// A traits-like metafunction for selecting the default Delimiter object type
// for a particular Delimiter type. The base case simply exposes type Delimiter
// itself as the delimiter's Type. However, there are specializations for
-// std::string-like objects that map them to the ByString delimiter object.
+// string-like objects that map them to the ByString delimiter object.
// This allows functions like absl::StrSplit() and absl::MaxSplits() to accept
-// std::string-like objects (e.g., ',') as delimiter arguments but they will be
+// string-like objects (e.g., ',') as delimiter arguments but they will be
// treated as if a ByString delimiter was given.
template <typename Delimiter>
struct SelectDelimiter {
@@ -262,7 +262,8 @@ class MaxSplitsImpl {
: delimiter_(delimiter), limit_(limit), count_(0) {}
absl::string_view Find(absl::string_view text, size_t pos) {
if (count_++ == limit_) {
- return absl::string_view(text.end(), 0); // No more matches.
+ return absl::string_view(text.data() + text.size(),
+ 0); // No more matches.
}
return delimiter_.Find(text, pos);
}
@@ -332,7 +333,7 @@ struct AllowEmpty {
// SkipEmpty()
//
// Returns `false` if the given `absl::string_view` is empty, indicating that
-// `StrSplit()` should omit the empty std::string.
+// `StrSplit()` should omit the empty string.
//
// Example:
//
@@ -340,7 +341,7 @@ struct AllowEmpty {
//
// // v[0] == "a", v[1] == "b"
//
-// Note: `SkipEmpty()` does not consider a std::string containing only whitespace
+// Note: `SkipEmpty()` does not consider a string containing only whitespace
// to be empty. To skip such whitespace as well, use the `SkipWhitespace()`
// predicate.
struct SkipEmpty {
@@ -350,7 +351,7 @@ struct SkipEmpty {
// SkipWhitespace()
//
// Returns `false` if the given `absl::string_view` is empty *or* contains only
-// whitespace, indicating that `StrSplit()` should omit the std::string.
+// whitespace, indicating that `StrSplit()` should omit the string.
//
// Example:
//
@@ -374,11 +375,11 @@ struct SkipWhitespace {
// StrSplit()
//
-// Splits a given `std::string` based on the provided `Delimiter` object,
-// returning the elements within the type specified by the caller. Optionally,
-// you may also pass a `Predicate` to `StrSplit()` indicating whether to include
-// or exclude the resulting element within the final result set. (See the
-// overviews for Delimiters and Predicates above.)
+// Splits a given string based on the provided `Delimiter` object, returning the
+// elements within the type specified by the caller. Optionally, you may pass a
+// `Predicate` to `StrSplit()` indicating whether to include or exclude the
+// resulting element within the final result set. (See the overviews for
+// Delimiters and Predicates above.)
//
// Example:
//
@@ -413,7 +414,7 @@ struct SkipWhitespace {
//
// The `StrSplit()` function adapts the returned collection to the collection
// specified by the caller (e.g. `std::vector` above). The returned collections
-// may contain `string`, `absl::string_view` (in which case the original std::string
+// may contain `string`, `absl::string_view` (in which case the original string
// being split must ensure that it outlives the collection), or any object that
// can be explicitly created from an `absl::string_view`. This behavior works
// for:
@@ -425,7 +426,7 @@ struct SkipWhitespace {
// Example:
//
// // The results are returned as `absl::string_view` objects. Note that we
-// // have to ensure that the input std::string outlives any results.
+// // have to ensure that the input string outlives any results.
// std::vector<absl::string_view> v = absl::StrSplit("a,b,c", ',');
//
// // Stores results in a std::set<std::string>, which also performs
@@ -445,7 +446,7 @@ struct SkipWhitespace {
// // is provided as a series of key/value pairs. For example, the 0th element
// // resulting from the split will be stored as a key to the 1st element. If
// // an odd number of elements are resolved, the last element is paired with
-// // a default-constructed value (e.g., empty std::string).
+// // a default-constructed value (e.g., empty string).
// std::map<std::string, std::string> m = absl::StrSplit("a,b,c", ',');
// // m["a"] == "b", m["c"] == "" // last component value equals ""
//
@@ -453,14 +454,14 @@ struct SkipWhitespace {
// elements and is not a collection type. When splitting to a `std::pair` the
// first two split strings become the `std::pair` `.first` and `.second`
// members, respectively. The remaining split substrings are discarded. If there
-// are less than two split substrings, the empty std::string is used for the
+// are less than two split substrings, the empty string is used for the
// corresponding
// `std::pair` member.
//
// Example:
//
// // Stores first two split strings as the members in a std::pair.
-// std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
+// std::pair<string, string> p = absl::StrSplit("a,b,c", ',');
// // p.first == "a", p.second == "b" // "c" is omitted.
//
// The `StrSplit()` function can be used multiple times to perform more
@@ -468,9 +469,9 @@ struct SkipWhitespace {
//
// Example:
//
-// // The input std::string "a=b=c,d=e,f=,g" becomes
+// // The input string "a=b=c,d=e,f=,g" becomes
// // { "a" => "b=c", "d" => "e", "f" => "", "g" => "" }
-// std::map<std::string, std::string> m;
+// std::map<string, string> m;
// for (absl::string_view sp : absl::StrSplit("a=b=c,d=e,f=,g", ',')) {
// m.insert(absl::StrSplit(sp, absl::MaxSplits('=', 1)));
// }
@@ -507,7 +508,7 @@ StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d,
std::move(text), DelimiterType(d), std::move(p));
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STR_SPLIT_H_
diff --git a/absl/strings/str_split_benchmark.cc b/absl/strings/str_split_benchmark.cc
index 326ff744..0ac297c8 100644
--- a/absl/strings/str_split_benchmark.cc
+++ b/absl/strings/str_split_benchmark.cc
@@ -35,16 +35,16 @@ std::string MakeTestString(int desired_length) {
return test;
}
-void BM_Split2StringPiece(benchmark::State& state) {
+void BM_Split2StringView(benchmark::State& state) {
std::string test = MakeTestString(state.range(0));
for (auto _ : state) {
std::vector<absl::string_view> result = absl::StrSplit(test, ';');
benchmark::DoNotOptimize(result);
}
}
-BENCHMARK_RANGE(BM_Split2StringPiece, 0, 1 << 20);
+BENCHMARK_RANGE(BM_Split2StringView, 0, 1 << 20);
-void BM_Split2StringPieceLifted(benchmark::State& state) {
+void BM_Split2StringViewLifted(benchmark::State& state) {
std::string test = MakeTestString(state.range(0));
std::vector<absl::string_view> result;
for (auto _ : state) {
@@ -52,7 +52,7 @@ void BM_Split2StringPieceLifted(benchmark::State& state) {
}
benchmark::DoNotOptimize(result);
}
-BENCHMARK_RANGE(BM_Split2StringPieceLifted, 0, 1 << 20);
+BENCHMARK_RANGE(BM_Split2StringViewLifted, 0, 1 << 20);
void BM_Split2String(benchmark::State& state) {
std::string test = MakeTestString(state.range(0));
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index c172a762..caa88277 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -37,6 +37,34 @@ using ::testing::ElementsAre;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
+TEST(Split, TraitsTest) {
+ static_assert(!absl::strings_internal::SplitterIsConvertibleTo<int>::value,
+ "");
+ static_assert(!absl::strings_internal::SplitterIsConvertibleTo<std::string>::value,
+ "");
+ static_assert(absl::strings_internal::SplitterIsConvertibleTo<
+ std::vector<std::string>>::value,
+ "");
+ static_assert(
+ !absl::strings_internal::SplitterIsConvertibleTo<std::vector<int>>::value,
+ "");
+ static_assert(absl::strings_internal::SplitterIsConvertibleTo<
+ std::vector<absl::string_view>>::value,
+ "");
+ static_assert(absl::strings_internal::SplitterIsConvertibleTo<
+ std::map<std::string, std::string>>::value,
+ "");
+ static_assert(absl::strings_internal::SplitterIsConvertibleTo<
+ std::map<absl::string_view, absl::string_view>>::value,
+ "");
+ static_assert(!absl::strings_internal::SplitterIsConvertibleTo<
+ std::map<int, std::string>>::value,
+ "");
+ static_assert(!absl::strings_internal::SplitterIsConvertibleTo<
+ std::map<std::string, int>>::value,
+ "");
+}
+
// This tests the overall split API, which is made up of the absl::StrSplit()
// function and the Delimiter objects in the absl:: namespace.
// This TEST macro is outside of any namespace to require full specification of
@@ -248,7 +276,7 @@ TEST(SplitIterator, Basics) {
EXPECT_EQ(it, end);
}
-// Simple Predicate to skip a particular std::string.
+// Simple Predicate to skip a particular string.
class Skip {
public:
explicit Skip(const std::string& s) : s_(s) {}
@@ -735,12 +763,12 @@ template <typename Delimiter>
static bool IsFoundAtStartingPos(absl::string_view text, Delimiter d,
size_t starting_pos, int expected_pos) {
absl::string_view found = d.Find(text, starting_pos);
- return found.data() != text.end() &&
+ return found.data() != text.data() + text.size() &&
expected_pos == found.data() - text.data();
}
// Helper function for testing Delimiter objects. Returns true if the given
-// Delimiter is found in the given std::string at the given position. This function
+// Delimiter is found in the given string at the given position. This function
// tests two cases:
// 1. The actual text given, staring at position 0
// 2. The text given with leading padding that should be ignored
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc
index 92197a75..3620ff44 100644
--- a/absl/strings/string_view.cc
+++ b/absl/strings/string_view.cc
@@ -24,7 +24,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
void WritePadding(std::ostream& o, size_t pad) {
@@ -241,7 +241,7 @@ constexpr string_view::size_type string_view::npos;
ABSL_STRING_VIEW_SELECTANY
constexpr string_view::size_type string_view::kMaxSize;
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_STD_STRING_VIEW
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index 4a5e4d8a..df6f1ae4 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -19,7 +19,7 @@
//
// This file contains the definition of the `absl::string_view` class. A
// `string_view` points to a contiguous span of characters, often part or all of
-// another `std::string`, double-quoted std::string literal, character array, or even
+// another `std::string`, double-quoted string literal, character array, or even
// another `string_view`.
//
// This `absl::string_view` abstraction is designed to be a drop-in
@@ -35,9 +35,9 @@
#include <string_view>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
using std::string_view;
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else // ABSL_HAVE_STD_STRING_VIEW
@@ -55,19 +55,19 @@ using std::string_view;
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// absl::string_view
//
-// A `string_view` provides a lightweight view into the std::string data provided by
-// a `std::string`, double-quoted std::string literal, character array, or even
-// another `string_view`. A `string_view` does *not* own the std::string to which it
+// A `string_view` provides a lightweight view into the string data provided by
+// a `std::string`, double-quoted string literal, character array, or even
+// another `string_view`. A `string_view` does *not* own the string to which it
// points, and that data cannot be modified through the view.
//
// You can use `string_view` as a function or method parameter anywhere a
-// parameter can receive a double-quoted std::string literal, `const char*`,
+// parameter can receive a double-quoted string literal, `const char*`,
// `std::string`, or another `absl::string_view` argument with no need to copy
-// the std::string data. Systematic use of `string_view` within function arguments
+// the string data. Systematic use of `string_view` within function arguments
// reduces data copies and `strlen()` calls.
//
// Because of its small size, prefer passing `string_view` by value:
@@ -100,8 +100,8 @@ inline namespace lts_2018_06_20 {
// `string_view` this way, it is your responsibility to ensure that the object
// pointed to by the `string_view` outlives the `string_view`.
//
-// A `string_view` may represent a whole std::string or just part of a std::string. For
-// example, when splitting a std::string, `std::vector<absl::string_view>` is a
+// A `string_view` may represent a whole string or just part of a string. For
+// example, when splitting a string, `std::vector<absl::string_view>` is a
// natural data type for the output.
//
//
@@ -144,7 +144,7 @@ inline namespace lts_2018_06_20 {
// All empty `string_view` objects whether null or not, are equal:
//
// absl::string_view() == absl::string_view("", 0)
-// absl::string_view(nullptr, 0) == absl:: string_view("abcdef"+6, 0)
+// absl::string_view(nullptr, 0) == absl::string_view("abcdef"+6, 0)
class string_view {
public:
using traits_type = std::char_traits<char>;
@@ -176,8 +176,19 @@ class string_view {
// Implicit constructor of a `string_view` from nul-terminated `str`. When
// accepting possibly null strings, use `absl::NullSafeStringView(str)`
// instead (see below).
+#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \
+ (defined(__GNUC__) && !defined(__clang__))
+ // GCC has __builtin_strlen according to
+ // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html, but
+ // ABSL_HAVE_BUILTIN doesn't detect that, so we use the extra checks above.
+ // __builtin_strlen is constexpr.
+ constexpr string_view(const char* str) // NOLINT(runtime/explicit)
+ : ptr_(str),
+ length_(CheckLengthInternal(str ? __builtin_strlen(str) : 0)) {}
+#else
constexpr string_view(const char* str) // NOLINT(runtime/explicit)
- : ptr_(str), length_(CheckLengthInternal(StrLenInternal(str))) {}
+ : ptr_(str), length_(CheckLengthInternal(str ? strlen(str) : 0)) {}
+#endif
// Implicit constructor of a `string_view` from a `const char*` and length.
constexpr string_view(const char* data, size_type len)
@@ -343,7 +354,7 @@ class string_view {
//
// Returns a "substring" of the `string_view` (at offset `pos` and length
// `n`) as another string_view. This function throws `std::out_of_bounds` if
- // `pos > size'.
+ // `pos > size`.
string_view substr(size_type pos, size_type n = npos) const {
if (ABSL_PREDICT_FALSE(pos > length_))
base_internal::ThrowStdOutOfRange("absl::string_view::substr");
@@ -354,7 +365,7 @@ class string_view {
// string_view::compare()
//
// Performs a lexicographical comparison between the `string_view` and
- // another `absl::string_view), returning -1 if `this` is less than, 0 if
+ // another `absl::string_view`, returning -1 if `this` is less than, 0 if
// `this` is equal to, and 1 if `this` is greater than the passed std::string
// view. Note that in the case of data equality, a further comparison is made
// on the respective sizes of the two `string_view`s to determine which is
@@ -482,23 +493,7 @@ class string_view {
private:
static constexpr size_type kMaxSize =
- std::numeric_limits<difference_type>::max();
-
- // check whether __builtin_strlen is provided by the compiler.
- // GCC doesn't have __has_builtin()
- // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970),
- // but has __builtin_strlen according to
- // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html.
-#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \
- (defined(__GNUC__) && !defined(__clang__))
- static constexpr size_type StrLenInternal(const char* str) {
- return str ? __builtin_strlen(str) : 0;
- }
-#else
- static constexpr size_type StrLenInternal(const char* str) {
- return str ? strlen(str) : 0;
- }
-#endif
+ (std::numeric_limits<difference_type>::max)();
static constexpr size_type CheckLengthInternal(size_type len) {
return ABSL_ASSERT(len <= kMaxSize), len;
@@ -543,13 +538,13 @@ inline bool operator>=(string_view x, string_view y) noexcept {
// IO Insertion Operator
std::ostream& operator<<(std::ostream& o, string_view piece);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_STD_STRING_VIEW
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// ClippedSubstr()
//
@@ -570,7 +565,7 @@ inline string_view NullSafeStringView(const char* p) {
return p ? string_view(p) : string_view();
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STRING_VIEW_H_
diff --git a/absl/strings/string_view_benchmark.cc b/absl/strings/string_view_benchmark.cc
index fb46db18..f4420320 100644
--- a/absl/strings/string_view_benchmark.cc
+++ b/absl/strings/string_view_benchmark.cc
@@ -151,7 +151,7 @@ void BM_find_string_view_len_one(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
- s.find("x"); // not present; length 1
+ benchmark::DoNotOptimize(s.find("x")); // not present; length 1
}
}
BENCHMARK(BM_find_string_view_len_one)->Range(1, 1 << 20);
@@ -160,7 +160,7 @@ void BM_find_string_view_len_two(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
- s.find("xx"); // not present; length 2
+ benchmark::DoNotOptimize(s.find("xx")); // not present; length 2
}
}
BENCHMARK(BM_find_string_view_len_two)->Range(1, 1 << 20);
@@ -169,7 +169,7 @@ void BM_find_one_char(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
- s.find('x'); // not present
+ benchmark::DoNotOptimize(s.find('x')); // not present
}
}
BENCHMARK(BM_find_one_char)->Range(1, 1 << 20);
@@ -178,7 +178,7 @@ void BM_rfind_one_char(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
- s.rfind('x'); // not present
+ benchmark::DoNotOptimize(s.rfind('x')); // not present
}
}
BENCHMARK(BM_rfind_one_char)->Range(1, 1 << 20);
@@ -193,7 +193,7 @@ void BM_worst_case_find_first_of(benchmark::State& state, int haystack_len) {
absl::string_view s(haystack);
for (auto _ : state) {
- s.find_first_of(needle);
+ benchmark::DoNotOptimize(s.find_first_of(needle));
}
}
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index fffa7b99..ed34ed83 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -35,7 +35,8 @@
#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEATH_IF_SUPPORTED(statement, ".*")
#else
-#define ABSL_EXPECT_DEATH_IF_SUPPORTED EXPECT_DEATH_IF_SUPPORTED
+#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+ EXPECT_DEATH_IF_SUPPORTED(statement, regex)
#endif
namespace {
@@ -283,7 +284,7 @@ TEST(StringViewTest, ComparisonOperatorsByCharacterPosition) {
}
#undef COMPARE
-// Sadly, our users often confuse std::string::npos with absl::string_view::npos;
+// Sadly, our users often confuse string::npos with absl::string_view::npos;
// So much so that we test here that they are the same. They need to
// both be unsigned, and both be the maximum-valued integer of their type.
@@ -677,9 +678,9 @@ TEST(StringViewTest, STL2Substr) {
EXPECT_EQ(a.substr(23, absl::string_view::npos), c);
// throw exception
#ifdef ABSL_HAVE_EXCEPTIONS
- EXPECT_THROW(a.substr(99, 2), std::out_of_range);
+ EXPECT_THROW((void)a.substr(99, 2), std::out_of_range);
#else
- EXPECT_DEATH(a.substr(99, 2), "absl::string_view::substr");
+ EXPECT_DEATH((void)a.substr(99, 2), "absl::string_view::substr");
#endif
}
@@ -811,15 +812,18 @@ TEST(StringViewTest, FrontBackSingleChar) {
}
// `std::string_view::string_view(const char*)` calls
-// `std::char_traits<char>::length(const char*)` to get the std::string length. In
+// `std::char_traits<char>::length(const char*)` to get the string length. In
// libc++, it doesn't allow `nullptr` in the constexpr context, with the error
// "read of dereferenced null pointer is not allowed in a constant expression".
// At run time, the behavior of `std::char_traits::length()` on `nullptr` is
-// undefined by the standard and usually results in crash with libc++. This
-// conforms to the standard, but `absl::string_view` implements a different
+// undefined by the standard and usually results in crash with libc++.
+// In MSVC, creating a constexpr string_view from nullptr also triggers an
+// "unevaluable pointer value" error. This compiler implementation conforms
+// to the standard, but `absl::string_view` implements a different
// behavior for historical reasons. We work around tests that construct
// `string_view` from `nullptr` when using libc++.
-#if !defined(ABSL_HAVE_STD_STRING_VIEW) || !defined(_LIBCPP_VERSION)
+#if !defined(ABSL_HAVE_STD_STRING_VIEW) || \
+ (!defined(_LIBCPP_VERSION) && !defined(_MSC_VER))
#define ABSL_HAVE_STRING_VIEW_FROM_NULLPTR 1
#endif // !defined(ABSL_HAVE_STD_STRING_VIEW) || !defined(_LIBCPP_VERSION)
diff --git a/absl/strings/strip.h b/absl/strings/strip.h
index ea64c334..059f57b7 100644
--- a/absl/strings/strip.h
+++ b/absl/strings/strip.h
@@ -17,7 +17,7 @@
// File: strip.h
// -----------------------------------------------------------------------------
//
-// This file contains various functions for stripping substrings from a std::string.
+// This file contains various functions for stripping substrings from a string.
#ifndef ABSL_STRINGS_STRIP_H_
#define ABSL_STRINGS_STRIP_H_
@@ -30,11 +30,11 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// ConsumePrefix()
//
-// Strips the `expected` prefix from the start of the given std::string, returning
+// Strips the `expected` prefix from the start of the given string, returning
// `true` if the strip operation succeeded or false otherwise.
//
// Example:
@@ -49,7 +49,7 @@ inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) {
}
// ConsumeSuffix()
//
-// Strips the `expected` suffix from the end of the given std::string, returning
+// Strips the `expected` suffix from the end of the given string, returning
// `true` if the strip operation succeeded or false otherwise.
//
// Example:
@@ -65,9 +65,9 @@ inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) {
// StripPrefix()
//
-// Returns a view into the input std::string 'str' with the given 'prefix' removed,
-// but leaving the original std::string intact. If the prefix does not match at the
-// start of the std::string, returns the original std::string instead.
+// Returns a view into the input string 'str' with the given 'prefix' removed,
+// but leaving the original string intact. If the prefix does not match at the
+// start of the string, returns the original string instead.
ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix(
absl::string_view str, absl::string_view prefix) {
if (absl::StartsWith(str, prefix)) str.remove_prefix(prefix.size());
@@ -76,16 +76,16 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix(
// StripSuffix()
//
-// Returns a view into the input std::string 'str' with the given 'suffix' removed,
-// but leaving the original std::string intact. If the suffix does not match at the
-// end of the std::string, returns the original std::string instead.
+// Returns a view into the input string 'str' with the given 'suffix' removed,
+// but leaving the original string intact. If the suffix does not match at the
+// end of the string, returns the original string instead.
ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix(
absl::string_view str, absl::string_view suffix) {
if (absl::EndsWith(str, suffix)) str.remove_suffix(suffix.size());
return str;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STRIP_H_
diff --git a/absl/strings/strip_test.cc b/absl/strings/strip_test.cc
index 205c160c..40c4c607 100644
--- a/absl/strings/strip_test.cc
+++ b/absl/strings/strip_test.cc
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file contains functions that remove a defined part from the std::string,
-// i.e., strip the std::string.
+// This file contains functions that remove a defined part from the string,
+// i.e., strip the string.
#include "absl/strings/strip.h"
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index 849443b9..b70e70e8 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -23,7 +23,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace substitute_internal {
void SubstituteAndAppendArray(std::string* output, absl::string_view format,
@@ -168,5 +168,5 @@ Arg::Arg(Dec dec) {
}
} // namespace substitute_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index def79be2..43d73ad7 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -17,46 +17,46 @@
// File: substitute.h
// -----------------------------------------------------------------------------
//
-// This package contains functions for efficiently performing std::string
-// substitutions using a format std::string with positional notation:
+// This package contains functions for efficiently performing string
+// substitutions using a format string with positional notation:
// `Substitute()` and `SubstituteAndAppend()`.
//
// Unlike printf-style format specifiers, `Substitute()` functions do not need
// to specify the type of the substitution arguments. Supported arguments
-// following the format std::string, such as strings, string_views, ints,
+// following the format string, such as strings, string_views, ints,
// floats, and bools, are automatically converted to strings during the
// substitution process. (See below for a full list of supported types.)
//
// `Substitute()` does not allow you to specify *how* to format a value, beyond
-// the default conversion to std::string. For example, you cannot format an integer
+// the default conversion to string. For example, you cannot format an integer
// in hex.
//
-// The format std::string uses positional identifiers indicated by a dollar sign ($)
+// The format string uses positional identifiers indicated by a dollar sign ($)
// and single digit positional ids to indicate which substitution arguments to
-// use at that location within the format std::string.
+// use at that location within the format string.
//
// Example 1:
-// std::string s = Substitute("$1 purchased $0 $2. Thanks $1!",
+// string s = Substitute("$1 purchased $0 $2. Thanks $1!",
// 5, "Bob", "Apples");
// EXPECT_EQ("Bob purchased 5 Apples. Thanks Bob!", s);
//
// Example 2:
-// std::string s = "Hi. ";
+// string s = "Hi. ";
// SubstituteAndAppend(&s, "My name is $0 and I am $1 years old.", "Bob", 5);
// EXPECT_EQ("Hi. My name is Bob and I am 5 years old.", s);
//
//
// Supported types:
-// * absl::string_view, std::string, const char* (null is equivalent to "")
+// * absl::string_view, string, const char* (null is equivalent to "")
// * int32_t, int64_t, uint32_t, uint64
// * float, double
// * bool (Printed as "true" or "false")
-// * pointer types other than char* (Printed as "0x<lower case hex std::string>",
+// * pointer types other than char* (Printed as "0x<lower case hex string>",
// except that null is printed as "NULL")
//
-// If an invalid format std::string is provided, Substitute returns an empty std::string
-// and SubstituteAndAppend does not change the provided output std::string.
-// A format std::string is invalid if it:
+// If an invalid format string is provided, Substitute returns an empty string
+// and SubstituteAndAppend does not change the provided output string.
+// A format string is invalid if it:
// * ends in an unescaped $ character,
// e.g. "Hello $", or
// * calls for a position argument which is not provided,
@@ -82,14 +82,14 @@
#include "absl/strings/strip.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace substitute_internal {
// Arg
//
// This class provides an argument type for `absl::Substitute()` and
// `absl::SubstituteAndAppend()`. `Arg` handles implicit conversion of various
-// types to a std::string. (`Arg` is very similar to the `AlphaNum` class in
+// types to a string. (`Arg` is very similar to the `AlphaNum` class in
// `StrCat()`.)
//
// This class has implicit constructors.
@@ -198,8 +198,8 @@ constexpr int PlaceholderBitmask(const char* format) {
// SubstituteAndAppend()
//
-// Substitutes variables into a given format std::string and appends to a given
-// output std::string. See file comments above for usage.
+// Substitutes variables into a given format string and appends to a given
+// output string. See file comments above for usage.
//
// The declarations of `SubstituteAndAppend()` below consist of overloads
// for passing 0 to 10 arguments, respectively.
@@ -445,7 +445,7 @@ void SubstituteAndAppend(
// Substitute()
//
-// Substitutes variables into a given format std::string. See file comments above
+// Substitutes variables into a given format string. See file comments above
// for usage.
//
// The declarations of `Substitute()` below consist of overloads for passing 0
@@ -457,7 +457,7 @@ void SubstituteAndAppend(
// Example:
// template <typename... Args>
// void VarMsg(absl::string_view format, const Args&... args) {
-// std::string s = absl::Substitute(format, args...);
+// string s = absl::Substitute(format, args...);
ABSL_MUST_USE_RESULT inline std::string Substitute(absl::string_view format) {
std::string result;
@@ -667,7 +667,7 @@ std::string Substitute(
"format std::string doesn't contain all of $0 through $9");
#endif // ABSL_BAD_CALL_IF
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_SUBSTITUTE_H_
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index 372874e1..e63b1d16 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -88,6 +88,9 @@ cc_test(
size = "small",
srcs = ["barrier_test.cc"],
copts = ABSL_TEST_COPTS,
+ tags = [
+ "no_test_wasm",
+ ],
deps = [
":synchronization",
"//absl/time",
@@ -100,6 +103,9 @@ cc_test(
size = "small",
srcs = ["blocking_counter_test.cc"],
copts = ABSL_TEST_COPTS,
+ tags = [
+ "no_test_wasm",
+ ],
deps = [
":synchronization",
"//absl/time",
@@ -138,6 +144,9 @@ cc_library(
name = "thread_pool",
testonly = 1,
hdrs = ["internal/thread_pool.h"],
+ visibility = [
+ "//absl:__subpackages__",
+ ],
deps = [
":synchronization",
"//absl/base:core_headers",
@@ -149,6 +158,7 @@ cc_test(
size = "large",
srcs = ["mutex_test.cc"],
copts = ABSL_TEST_COPTS,
+ shard_count = 25,
deps = [
":synchronization",
":thread_pool",
@@ -160,18 +170,32 @@ cc_test(
],
)
-cc_test(
- name = "mutex_benchmark",
+cc_library(
+ name = "mutex_benchmark_common",
+ testonly = 1,
srcs = ["mutex_benchmark.cc"],
- copts = ABSL_TEST_COPTS,
- tags = ["benchmark"],
- visibility = ["//visibility:private"],
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = [
+ "//absl/synchronization:__pkg__",
+ ],
deps = [
":synchronization",
":thread_pool",
"//absl/base",
+ "//absl/base:base_internal",
"@com_github_google_benchmark//:benchmark_main",
],
+ alwayslink = 1,
+)
+
+cc_binary(
+ name = "mutex_benchmark",
+ testonly = 1,
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":mutex_benchmark_common",
+ ],
)
cc_test(
@@ -205,6 +229,7 @@ cc_test(
name = "per_thread_sem_test",
size = "medium",
copts = ABSL_TEST_COPTS,
+ tags = ["no_test_wasm"],
deps = [
":per_thread_sem_test_common",
":synchronization",
@@ -225,6 +250,7 @@ cc_test(
"//absl:windows": [],
"//conditions:default": ["-pthread"],
}),
+ tags = ["no_test_ios_x86_64"],
deps = [
":synchronization",
"//absl/base",
diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt
index c19f5725..de0d7b7d 100644
--- a/absl/synchronization/CMakeLists.txt
+++ b/absl/synchronization/CMakeLists.txt
@@ -34,7 +34,7 @@ list(APPEND SYNCHRONIZATION_INTERNAL_HEADERS
# synchronization library
-list(APPEND SYNCHRONIZATION_SRC
+list(APPEND SYNCHRONIZATION_SRC
"barrier.cc"
"blocking_counter.cc"
"internal/create_thread_identity.cc"
diff --git a/absl/synchronization/barrier.cc b/absl/synchronization/barrier.cc
index 545bb891..ee66c240 100644
--- a/absl/synchronization/barrier.cc
+++ b/absl/synchronization/barrier.cc
@@ -18,7 +18,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// Return whether int *arg is zero.
static bool IsZero(void *arg) {
@@ -48,5 +48,5 @@ bool Barrier::Block() {
return this->num_to_exit_ == 0;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/barrier.h b/absl/synchronization/barrier.h
index ccae0a4c..77ac3602 100644
--- a/absl/synchronization/barrier.h
+++ b/absl/synchronization/barrier.h
@@ -23,7 +23,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// Barrier
//
@@ -74,6 +74,6 @@ class Barrier {
int num_to_exit_ GUARDED_BY(lock_);
};
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_BARRIER_H_
diff --git a/absl/synchronization/blocking_counter.cc b/absl/synchronization/blocking_counter.cc
index f998099a..82d889a9 100644
--- a/absl/synchronization/blocking_counter.cc
+++ b/absl/synchronization/blocking_counter.cc
@@ -17,7 +17,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// Return whether int *arg is zero.
static bool IsZero(void *arg) {
@@ -53,5 +53,5 @@ void BlockingCounter::Wait() {
// after we return from this method.
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/blocking_counter.h b/absl/synchronization/blocking_counter.h
index 08b6f58c..554e396c 100644
--- a/absl/synchronization/blocking_counter.h
+++ b/absl/synchronization/blocking_counter.h
@@ -24,7 +24,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// BlockingCounter
//
@@ -93,7 +93,7 @@ class BlockingCounter {
int num_waiting_ GUARDED_BY(lock_);
};
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_
diff --git a/absl/synchronization/blocking_counter_test.cc b/absl/synchronization/blocking_counter_test.cc
index 486aa9b1..b3b55dd7 100644
--- a/absl/synchronization/blocking_counter_test.cc
+++ b/absl/synchronization/blocking_counter_test.cc
@@ -22,7 +22,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
void PauseAndDecreaseCounter(BlockingCounter* counter, int* done) {
@@ -64,5 +64,5 @@ TEST(BlockingCounterTest, BasicFunctionality) {
}
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc
index 38cc7e2f..f27f16da 100644
--- a/absl/synchronization/internal/create_thread_identity.cc
+++ b/absl/synchronization/internal/create_thread_identity.cc
@@ -27,7 +27,7 @@
#include "absl/synchronization/internal/per_thread_sem.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
// ThreadIdentity storage is persistent, we maintain a free-list of previously
@@ -108,7 +108,7 @@ base_internal::ThreadIdentity* CreateThreadIdentity() {
}
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/absl/synchronization/internal/create_thread_identity.h b/absl/synchronization/internal/create_thread_identity.h
index 05b5f7e1..1132d516 100644
--- a/absl/synchronization/internal/create_thread_identity.h
+++ b/absl/synchronization/internal/create_thread_identity.h
@@ -29,7 +29,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
// Allocates and attaches a ThreadIdentity object for the calling thread.
@@ -50,6 +50,6 @@ inline base_internal::ThreadIdentity* GetOrCreateCurrentThreadIdentity() {
}
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_INTERNAL_CREATE_THREAD_IDENTITY_H_
diff --git a/absl/synchronization/internal/graphcycles.cc b/absl/synchronization/internal/graphcycles.cc
index 5a015844..139be0f5 100644
--- a/absl/synchronization/internal/graphcycles.cc
+++ b/absl/synchronization/internal/graphcycles.cc
@@ -44,7 +44,7 @@
// Do not use STL. This module does not use standard memory allocation.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
namespace {
@@ -205,8 +205,7 @@ class NodeSet {
}
private:
- static const int32_t kEmpty;
- static const int32_t kDel;
+ enum : int32_t { kEmpty = -1, kDel = -2 };
Vec<int32_t> table_;
uint32_t occupied_; // Count of non-empty slots (includes deleted slots)
@@ -256,9 +255,6 @@ class NodeSet {
NodeSet& operator=(const NodeSet&) = delete;
};
-const int32_t NodeSet::kEmpty = -1;
-const int32_t NodeSet::kDel = -2;
-
// We encode a node index and a node version in GraphId. The version
// number is incremented when the GraphId is freed which automatically
// invalidates all copies of the GraphId.
@@ -695,7 +691,7 @@ int GraphCycles::GetStackTrace(GraphId id, void*** ptr) {
}
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/absl/synchronization/internal/graphcycles.h b/absl/synchronization/internal/graphcycles.h
index e43ae26b..6609ea06 100644
--- a/absl/synchronization/internal/graphcycles.h
+++ b/absl/synchronization/internal/graphcycles.h
@@ -41,7 +41,7 @@
#include <cstdint>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
// Opaque identifier for a graph node.
@@ -133,7 +133,7 @@ class GraphCycles {
};
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif
diff --git a/absl/synchronization/internal/graphcycles_test.cc b/absl/synchronization/internal/graphcycles_test.cc
index 09332bad..4dc2bdc5 100644
--- a/absl/synchronization/internal/graphcycles_test.cc
+++ b/absl/synchronization/internal/graphcycles_test.cc
@@ -25,7 +25,7 @@
#include "absl/base/macros.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
// We emulate a GraphCycles object with a node vector and an edge vector.
@@ -460,5 +460,5 @@ TEST_F(GraphCyclesTest, ManyEdges) {
}
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h
index 3d3dc0cb..34ae94ec 100644
--- a/absl/synchronization/internal/kernel_timeout.h
+++ b/absl/synchronization/internal/kernel_timeout.h
@@ -25,9 +25,6 @@
#ifndef ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
#define ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
-#ifdef _WIN32
-#include <intsafe.h>
-#endif
#include <time.h>
#include <algorithm>
#include <limits>
@@ -37,7 +34,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
class Futex;
@@ -80,7 +77,7 @@ class KernelTimeout {
if (x <= 0) x = 1;
// A time larger than what can be represented to the kernel is treated
// as no timeout.
- if (x == std::numeric_limits<int64_t>::max()) x = 0;
+ if (x == (std::numeric_limits<int64_t>::max)()) x = 0;
return x;
}
@@ -94,7 +91,7 @@ class KernelTimeout {
ERROR,
"Tried to create a timespec from a non-timeout; never do this.");
// But we'll try to continue sanely. no-timeout ~= saturated timeout.
- n = std::numeric_limits<int64_t>::max();
+ n = (std::numeric_limits<int64_t>::max)();
}
// Kernel APIs validate timespecs as being at or after the epoch,
@@ -105,7 +102,7 @@ class KernelTimeout {
struct timespec abstime;
int64_t seconds = std::min(n / kNanosPerSecond,
- int64_t{std::numeric_limits<time_t>::max()});
+ int64_t{(std::numeric_limits<time_t>::max)()});
abstime.tv_sec = static_cast<time_t>(seconds);
abstime.tv_nsec =
static_cast<decltype(abstime.tv_nsec)>(n % kNanosPerSecond);
@@ -118,9 +115,14 @@ class KernelTimeout {
// Windows. Callers should recognize that the return value is a
// relative duration (it should be recomputed by calling this method
// in the case of a spurious wakeup).
- DWORD InMillisecondsFromNow() const {
+ // This header file may be included transitively by public header files,
+ // so we define our own DWORD and INFINITE instead of getting them from
+ // <intsafe.h> and <WinBase.h>.
+ typedef unsigned long DWord; // NOLINT
+ DWord InMillisecondsFromNow() const {
+ constexpr DWord kInfinite = (std::numeric_limits<DWord>::max)();
if (!has_timeout()) {
- return INFINITE;
+ return kInfinite;
}
// The use of absl::Now() to convert from absolute time to
// relative time means that absl::Now() cannot use anything that
@@ -129,13 +131,13 @@ class KernelTimeout {
if (ns_ >= now) {
// Round up so that Now() + ms_from_now >= ns_.
constexpr uint64_t max_nanos =
- std::numeric_limits<int64_t>::max() - 999999u;
+ (std::numeric_limits<int64_t>::max)() - 999999u;
uint64_t ms_from_now =
(std::min<uint64_t>(max_nanos, ns_ - now) + 999999u) / 1000000u;
- if (ms_from_now > std::numeric_limits<DWORD>::max()) {
- return INFINITE;
+ if (ms_from_now > kInfinite) {
+ return kInfinite;
}
- return static_cast<DWORD>(ms_from_now);
+ return static_cast<DWord>(ms_from_now);
}
return 0;
}
@@ -146,6 +148,6 @@ class KernelTimeout {
};
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
diff --git a/absl/synchronization/internal/mutex_nonprod.cc b/absl/synchronization/internal/mutex_nonprod.cc
index a8071a96..4b0b8bcd 100644
--- a/absl/synchronization/internal/mutex_nonprod.cc
+++ b/absl/synchronization/internal/mutex_nonprod.cc
@@ -31,7 +31,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
namespace {
@@ -316,5 +316,5 @@ bool Condition::Eval() const {
void RegisterSymbolizer(bool (*)(const void*, char*, int)) {}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/internal/mutex_nonprod.inc b/absl/synchronization/internal/mutex_nonprod.inc
index 2d06285f..0ae4c0ea 100644
--- a/absl/synchronization/internal/mutex_nonprod.inc
+++ b/absl/synchronization/internal/mutex_nonprod.inc
@@ -36,7 +36,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
class Condition;
namespace synchronization_internal {
@@ -254,5 +254,5 @@ class SynchronizationStorage {
};
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc
index 53b789d6..9de2d136 100644
--- a/absl/synchronization/internal/per_thread_sem.cc
+++ b/absl/synchronization/internal/per_thread_sem.cc
@@ -25,7 +25,7 @@
#include "absl/synchronization/internal/waiter.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
void PerThreadSem::SetThreadBlockedCounter(std::atomic<int> *counter) {
@@ -59,7 +59,7 @@ void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) {
}
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
extern "C" {
diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h
index fc64f768..6efd5951 100644
--- a/absl/synchronization/internal/per_thread_sem.h
+++ b/absl/synchronization/internal/per_thread_sem.h
@@ -32,7 +32,7 @@
#include "absl/synchronization/internal/kernel_timeout.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
class Mutex;
@@ -81,7 +81,7 @@ class PerThreadSem {
};
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// In some build configurations we pass --detect-odr-violations to the
diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc
index 63c8e56a..18b2458b 100644
--- a/absl/synchronization/internal/per_thread_sem_test.cc
+++ b/absl/synchronization/internal/per_thread_sem_test.cc
@@ -33,7 +33,7 @@
// primitives which might use PerThreadSem, most notably absl::Mutex.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
class SimpleSemaphore {
@@ -154,12 +154,15 @@ TEST_F(PerThreadSemTest, WithTimeout) {
TEST_F(PerThreadSemTest, Timeouts) {
absl::Time timeout = absl::Now() + absl::Milliseconds(50);
+ // Allow for a slight early return, to account for quality of implementation
+ // issues on various platforms.
+ const absl::Duration slop = absl::Microseconds(200);
EXPECT_FALSE(Wait(timeout));
- EXPECT_LE(timeout, absl::Now());
+ EXPECT_LE(timeout, absl::Now() + slop);
absl::Time negative_timeout = absl::UnixEpoch() - absl::Milliseconds(100);
EXPECT_FALSE(Wait(negative_timeout));
- EXPECT_LE(negative_timeout, absl::Now()); // trivially true :)
+ EXPECT_LE(negative_timeout, absl::Now() + slop); // trivially true :)
Post(GetOrCreateCurrentThreadIdentity());
// The wait here has an expired timeout, but we have a wake to consume,
@@ -170,5 +173,5 @@ TEST_F(PerThreadSemTest, Timeouts) {
} // namespace
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/internal/thread_pool.h b/absl/synchronization/internal/thread_pool.h
index 82dedbf5..66c7546b 100644
--- a/absl/synchronization/internal/thread_pool.h
+++ b/absl/synchronization/internal/thread_pool.h
@@ -25,7 +25,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
// A simple ThreadPool implementation for tests.
@@ -86,7 +86,7 @@ class ThreadPool {
};
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_
diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc
index ad86acce..76fdd861 100644
--- a/absl/synchronization/internal/waiter.cc
+++ b/absl/synchronization/internal/waiter.cc
@@ -46,7 +46,7 @@
#include "absl/synchronization/internal/kernel_timeout.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
static void MaybeBecomeIdle() {
@@ -410,5 +410,5 @@ void Waiter::Poke() {
#endif
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h
index 1c284c0a..2b737260 100644
--- a/absl/synchronization/internal/waiter.h
+++ b/absl/synchronization/internal/waiter.h
@@ -53,7 +53,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace synchronization_internal {
// Waiter is an OS-specific semaphore.
@@ -135,7 +135,7 @@ class Waiter {
};
} // namespace synchronization_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_
diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc
index 90c9009b..b7360c29 100644
--- a/absl/synchronization/lifetime_test.cc
+++ b/absl/synchronization/lifetime_test.cc
@@ -72,23 +72,19 @@ void ThreadTwo(absl::Mutex* mutex, absl::CondVar* condvar,
// Launch thread 1 and thread 2, and block on their completion.
// If any of 'mutex', 'condvar', or 'notification' is nullptr, use a locally
// constructed instance instead.
-void RunTests(absl::Mutex* mutex, absl::CondVar* condvar,
- absl::Notification* notification) {
+void RunTests(absl::Mutex* mutex, absl::CondVar* condvar) {
absl::Mutex default_mutex;
absl::CondVar default_condvar;
- absl::Notification default_notification;
+ absl::Notification notification;
if (!mutex) {
mutex = &default_mutex;
}
if (!condvar) {
condvar = &default_condvar;
}
- if (!notification) {
- notification = &default_notification;
- }
bool state = false;
- std::thread thread_one(ThreadOne, mutex, condvar, notification, &state);
- std::thread thread_two(ThreadTwo, mutex, condvar, notification, &state);
+ std::thread thread_one(ThreadOne, mutex, condvar, &notification, &state);
+ std::thread thread_two(ThreadTwo, mutex, condvar, &notification, &state);
thread_one.join();
thread_two.join();
}
@@ -96,8 +92,7 @@ void RunTests(absl::Mutex* mutex, absl::CondVar* condvar,
void TestLocals() {
absl::Mutex mutex;
absl::CondVar condvar;
- absl::Notification notification;
- RunTests(&mutex, &condvar, &notification);
+ RunTests(&mutex, &condvar);
}
// Global variables during start and termination
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc
index 14e24a76..9f8d6cd7 100644
--- a/absl/synchronization/mutex.cc
+++ b/absl/synchronization/mutex.cc
@@ -71,7 +71,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalMutexYield() { std::this_thread::yield(); }
} // extern "C"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
@@ -299,7 +299,7 @@ static struct SynchEvent { // this is a trivial hash table for the events
// set "bits" in the word there (waiting until lockbit is clear before doing
// so), and return a refcounted reference that will remain valid until
// UnrefSynchEvent() is called. If a new SynchEvent is allocated,
-// the std::string name is copied into it.
+// the string name is copied into it.
// When used with a mutex, the caller should also ensure that kMuEvent
// is set in the mutex word, and similarly for condition variables and kCVEvent.
static SynchEvent *EnsureSynchEvent(std::atomic<intptr_t> *addr,
@@ -1828,8 +1828,8 @@ bool Mutex::LockSlowWithDeadline(MuHow how, const Condition *cond,
cond == nullptr || EvalConditionAnnotated(cond, this, true, how);
}
-// RAW_CHECK_FMT() takes a condition, a printf-style format std::string, and
-// the printf-style argument list. The format std::string must be a literal.
+// RAW_CHECK_FMT() takes a condition, a printf-style format string, and
+// the printf-style argument list. The format string must be a literal.
// Arguments after the first are not evaluated unless the condition is true.
#define RAW_CHECK_FMT(cond, ...) \
do { \
@@ -1976,7 +1976,7 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) {
// Unlock this mutex, which is held by the current thread.
// If waitp is non-zero, it must be the wait parameters for the current thread
// which holds the lock but is not runnable because its condition is false
-// or it n the process of blocking on a condition variable; it must requeue
+// or it is in the process of blocking on a condition variable; it must requeue
// itself on the mutex/condvar to wait for its condition to become true.
void Mutex::UnlockSlow(SynchWaitParams *waitp) {
intptr_t v = mu_.load(std::memory_order_relaxed);
@@ -2685,5 +2685,5 @@ bool Condition::GuaranteedEqual(const Condition *a, const Condition *b) {
a->arg_ == b->arg_ && a->method_ == b->method_;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index b5276037..ce97707f 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -24,7 +24,7 @@
// Unlike a `std::mutex`, the Abseil `Mutex` provides the following additional
// features:
// * Conditional predicates intrinsic to the `Mutex` object
-// * Reader/writer locks, in addition to standard exclusive/writer locks
+// * Shared/reader locks, in addition to standard exclusive/writer locks
// * Deadlock detection and debug support.
//
// The following helper classes are also defined within this file:
@@ -81,7 +81,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
class Condition;
struct SynchWaitParams;
@@ -291,7 +291,7 @@ class LOCKABLE Mutex {
// Mutex::ReaderLockWhen()
// Mutex::WriterLockWhen()
//
- // Blocks until simultaneously both `cond` is `true` and this` Mutex` can
+ // Blocks until simultaneously both `cond` is `true` and this `Mutex` can
// be acquired, then atomically acquires this `Mutex`. `LockWhen()` is
// logically equivalent to `*Lock(); Await();` though they may have different
// performance characteristics.
@@ -559,7 +559,7 @@ class SCOPED_LOCKABLE ReaderMutexLock {
// WriterMutexLock
//
// The `WriterMutexLock` is a helper class, like `MutexLock`, which acquires and
-// releases a write (exclusive) lock on a `Mutex` va RAII.
+// releases a write (exclusive) lock on a `Mutex` via RAII.
class SCOPED_LOCKABLE WriterMutexLock {
public:
explicit WriterMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu)
@@ -585,7 +585,7 @@ class SCOPED_LOCKABLE WriterMutexLock {
// -----------------------------------------------------------------------------
//
// As noted above, `Mutex` contains a number of member functions which take a
-// `Condition` as a argument; clients can wait for conditions to become `true`
+// `Condition` as an argument; clients can wait for conditions to become `true`
// before attempting to acquire the mutex. These sections are known as
// "condition critical" sections. To use a `Condition`, you simply need to
// construct it, and use within an appropriate `Mutex` member function;
@@ -963,7 +963,7 @@ void RegisterMutexTracer(void (*fn)(const char *msg, const void *obj,
//
// The function pointer registered here will be called here on various CondVar
// events. The callback is given an opaque handle to the CondVar object and
-// a std::string identifying the event. This is thread-safe, but only a single
+// a string identifying the event. This is thread-safe, but only a single
// tracer can be registered.
//
// Events that can be sent are "Wait", "Unwait", "Signal wakeup", and
@@ -1015,7 +1015,7 @@ enum class OnDeadlockCycle {
// the manner chosen here.
void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// In some build configurations we pass --detect-odr-violations to the
diff --git a/absl/synchronization/mutex_benchmark.cc b/absl/synchronization/mutex_benchmark.cc
index 30a52355..2652bb97 100644
--- a/absl/synchronization/mutex_benchmark.cc
+++ b/absl/synchronization/mutex_benchmark.cc
@@ -12,16 +12,154 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <cstdint>
+#include <mutex> // NOLINT(build/c++11)
#include <vector>
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/sysinfo.h"
+#include "absl/base/internal/cycleclock.h"
+#include "absl/base/internal/spinlock.h"
#include "absl/synchronization/blocking_counter.h"
#include "absl/synchronization/internal/thread_pool.h"
#include "absl/synchronization/mutex.h"
+#include "benchmark/benchmark.h"
namespace {
+void BM_Mutex(benchmark::State& state) {
+ static absl::Mutex* mu = new absl::Mutex;
+ for (auto _ : state) {
+ absl::MutexLock lock(mu);
+ }
+}
+BENCHMARK(BM_Mutex)->UseRealTime()->Threads(1)->ThreadPerCpu();
+
+static void DelayNs(int64_t ns, int* data) {
+ int64_t end = absl::base_internal::CycleClock::Now() +
+ ns * absl::base_internal::CycleClock::Frequency() / 1e9;
+ while (absl::base_internal::CycleClock::Now() < end) {
+ ++(*data);
+ benchmark::DoNotOptimize(*data);
+ }
+}
+
+template <typename MutexType>
+class RaiiLocker {
+ public:
+ explicit RaiiLocker(MutexType* mu) : mu_(mu) { mu_->Lock(); }
+ ~RaiiLocker() { mu_->Unlock(); }
+ private:
+ MutexType* mu_;
+};
+
+template <>
+class RaiiLocker<std::mutex> {
+ public:
+ explicit RaiiLocker(std::mutex* mu) : mu_(mu) { mu_->lock(); }
+ ~RaiiLocker() { mu_->unlock(); }
+ private:
+ std::mutex* mu_;
+};
+
+template <typename MutexType>
+void BM_Contended(benchmark::State& state) {
+ struct Shared {
+ MutexType mu;
+ int data = 0;
+ };
+ static auto* shared = new Shared;
+ int local = 0;
+ for (auto _ : state) {
+ // Here we model both local work outside of the critical section as well as
+ // some work inside of the critical section. The idea is to capture some
+ // more or less realisitic contention levels.
+ // If contention is too low, the benchmark won't measure anything useful.
+ // If contention is unrealistically high, the benchmark will favor
+ // bad mutex implementations that block and otherwise distract threads
+ // from the mutex and shared state for as much as possible.
+ // To achieve this amount of local work is multiplied by number of threads
+ // to keep ratio between local work and critical section approximately
+ // equal regardless of number of threads.
+ DelayNs(100 * state.threads, &local);
+ RaiiLocker<MutexType> locker(&shared->mu);
+ DelayNs(state.range(0), &shared->data);
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_Contended, absl::Mutex)
+ ->UseRealTime()
+ // ThreadPerCpu poorly handles non-power-of-two CPU counts.
+ ->Threads(1)
+ ->Threads(2)
+ ->Threads(4)
+ ->Threads(6)
+ ->Threads(8)
+ ->Threads(12)
+ ->Threads(16)
+ ->Threads(24)
+ ->Threads(32)
+ ->Threads(48)
+ ->Threads(64)
+ ->Threads(96)
+ ->Threads(128)
+ ->Threads(192)
+ ->Threads(256)
+ // Some empirically chosen amounts of work in critical section.
+ // 1 is low contention, 200 is high contention and few values in between.
+ ->Arg(1)
+ ->Arg(20)
+ ->Arg(50)
+ ->Arg(200);
+
+BENCHMARK_TEMPLATE(BM_Contended, absl::base_internal::SpinLock)
+ ->UseRealTime()
+ // ThreadPerCpu poorly handles non-power-of-two CPU counts.
+ ->Threads(1)
+ ->Threads(2)
+ ->Threads(4)
+ ->Threads(6)
+ ->Threads(8)
+ ->Threads(12)
+ ->Threads(16)
+ ->Threads(24)
+ ->Threads(32)
+ ->Threads(48)
+ ->Threads(64)
+ ->Threads(96)
+ ->Threads(128)
+ ->Threads(192)
+ ->Threads(256)
+ // Some empirically chosen amounts of work in critical section.
+ // 1 is low contention, 200 is high contention and few values in between.
+ ->Arg(1)
+ ->Arg(20)
+ ->Arg(50)
+ ->Arg(200);
+
+BENCHMARK_TEMPLATE(BM_Contended, std::mutex)
+ ->UseRealTime()
+ // ThreadPerCpu poorly handles non-power-of-two CPU counts.
+ ->Threads(1)
+ ->Threads(2)
+ ->Threads(4)
+ ->Threads(6)
+ ->Threads(8)
+ ->Threads(12)
+ ->Threads(16)
+ ->Threads(24)
+ ->Threads(32)
+ ->Threads(48)
+ ->Threads(64)
+ ->Threads(96)
+ ->Threads(128)
+ ->Threads(192)
+ ->Threads(256)
+ // Some empirically chosen amounts of work in critical section.
+ // 1 is low contention, 200 is high contention and few values in between.
+ ->Arg(1)
+ ->Arg(20)
+ ->Arg(50)
+ ->Arg(200);
+
// Measure the overhead of conditions on mutex release (when they must be
// evaluated). Mutex has (some) support for equivalence classes allowing
// Conditions with the same function/argument to potentially not be multiply
@@ -74,21 +212,12 @@ void BM_ConditionWaiters(benchmark::State& state) {
mu.Unlock();
}
-#ifdef THREAD_SANITIZER
-// ThreadSanitizer can't handle 8192 threads.
-constexpr int kMaxConditionWaiters = 2048;
-#else
+// Some configurations have higher thread limits than others.
+#if defined(__linux__) && !defined(THREAD_SANITIZER)
constexpr int kMaxConditionWaiters = 8192;
+#else
+constexpr int kMaxConditionWaiters = 1024;
#endif
BENCHMARK(BM_ConditionWaiters)->RangePair(0, 2, 1, kMaxConditionWaiters);
-void BM_ContendedMutex(benchmark::State& state) {
- static absl::Mutex* mu = new absl::Mutex;
- for (auto _ : state) {
- absl::MutexLock lock(mu);
- }
-}
-BENCHMARK(BM_ContendedMutex)->Threads(1);
-BENCHMARK(BM_ContendedMutex)->ThreadPerCpu();
-
} // namespace
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index 53b93784..b2820e20 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -29,6 +29,7 @@
#include <vector>
#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/sysinfo.h"
#include "absl/memory/memory.h"
@@ -54,8 +55,8 @@ CreateDefaultPool() {
// Hack to schedule a function to run on a thread pool thread after a
// duration has elapsed.
static void ScheduleAfter(absl::synchronization_internal::ThreadPool *tp,
- const std::function<void()> &func,
- absl::Duration after) {
+ absl::Duration after,
+ const std::function<void()> &func) {
tp->Schedule([func, after] {
absl::SleepFor(after);
func();
@@ -1150,249 +1151,369 @@ TEST(Mutex, DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS {
// and so never expires/passes, and one that will expire/pass in the near
// future.
-// Encapsulate a Mutex-protected bool with its associated Condition/CondVar.
-class Cond {
- public:
- explicit Cond(bool use_deadline) : use_deadline_(use_deadline), c_(&b_) {}
-
- void Set(bool v) {
- absl::MutexLock lock(&mu_);
- b_ = v;
+static absl::Duration TimeoutTestAllowedSchedulingDelay() {
+ // Note: we use a function here because Microsoft Visual Studio fails to
+ // properly initialize constexpr static absl::Duration variables.
+ return absl::Milliseconds(150);
+}
+
+// Returns true if `actual_delay` is close enough to `expected_delay` to pass
+// the timeouts/deadlines test. Otherwise, logs warnings and returns false.
+ABSL_MUST_USE_RESULT
+static bool DelayIsWithinBounds(absl::Duration expected_delay,
+ absl::Duration actual_delay) {
+ bool pass = true;
+ // Do not allow the observed delay to be less than expected. This may occur
+ // in practice due to clock skew or when the synchronization primitives use a
+ // different clock than absl::Now(), but these cases should be handled by the
+ // the retry mechanism in each TimeoutTest.
+ if (actual_delay < expected_delay) {
+ ABSL_RAW_LOG(WARNING,
+ "Actual delay %s was too short, expected %s (difference %s)",
+ absl::FormatDuration(actual_delay).c_str(),
+ absl::FormatDuration(expected_delay).c_str(),
+ absl::FormatDuration(actual_delay - expected_delay).c_str());
+ pass = false;
}
-
- bool AwaitWithTimeout(absl::Duration timeout) {
- absl::MutexLock lock(&mu_);
- return use_deadline_ ? mu_.AwaitWithDeadline(c_, absl::Now() + timeout)
- : mu_.AwaitWithTimeout(c_, timeout);
+ // If the expected delay is <= zero then allow a small error tolerance, since
+ // we do not expect context switches to occur during test execution.
+ // Otherwise, thread scheduling delays may be substantial in rare cases, so
+ // tolerate up to kTimeoutTestAllowedSchedulingDelay of error.
+ absl::Duration tolerance = expected_delay <= absl::ZeroDuration()
+ ? absl::Milliseconds(10)
+ : TimeoutTestAllowedSchedulingDelay();
+ if (actual_delay > expected_delay + tolerance) {
+ ABSL_RAW_LOG(WARNING,
+ "Actual delay %s was too long, expected %s (difference %s)",
+ absl::FormatDuration(actual_delay).c_str(),
+ absl::FormatDuration(expected_delay).c_str(),
+ absl::FormatDuration(actual_delay - expected_delay).c_str());
+ pass = false;
}
+ return pass;
+}
+
+// Parameters for TimeoutTest, below.
+struct TimeoutTestParam {
+ // The file and line number (used for logging purposes only).
+ const char *from_file;
+ int from_line;
+
+ // Should the absolute deadline API based on absl::Time be tested? If false,
+ // the relative deadline API based on absl::Duration is tested.
+ bool use_absolute_deadline;
+
+ // The deadline/timeout used when calling the API being tested
+ // (e.g. Mutex::LockWhenWithDeadline).
+ absl::Duration wait_timeout;
+
+ // The delay before the condition will be set true by the test code. If zero
+ // or negative, the condition is set true immediately (before calling the API
+ // being tested). Otherwise, if infinite, the condition is never set true.
+ // Otherwise a closure is scheduled for the future that sets the condition
+ // true.
+ absl::Duration satisfy_condition_delay;
+
+ // The expected result of the condition after the call to the API being
+ // tested. Generally `true` means the condition was true when the API returns,
+ // `false` indicates an expected timeout.
+ bool expected_result;
+
+ // The expected delay before the API under test returns. This is inherently
+ // flaky, so some slop is allowed (see `DelayIsWithinBounds` above), and the
+ // test keeps trying indefinitely until this constraint passes.
+ absl::Duration expected_delay;
+};
- bool LockWhenWithTimeout(absl::Duration timeout) {
- bool b = use_deadline_ ? mu_.LockWhenWithDeadline(c_, absl::Now() + timeout)
- : mu_.LockWhenWithTimeout(c_, timeout);
- mu_.Unlock();
- return b;
+// Print a `TimeoutTestParam` to a debug log.
+std::ostream &operator<<(std::ostream &os, const TimeoutTestParam &param) {
+ return os << "from: " << param.from_file << ":" << param.from_line
+ << " use_absolute_deadline: "
+ << (param.use_absolute_deadline ? "true" : "false")
+ << " wait_timeout: " << param.wait_timeout
+ << " satisfy_condition_delay: " << param.satisfy_condition_delay
+ << " expected_result: "
+ << (param.expected_result ? "true" : "false")
+ << " expected_delay: " << param.expected_delay;
+}
+
+std::string FormatString(const TimeoutTestParam &param) {
+ std::ostringstream os;
+ os << param;
+ return os.str();
+}
+
+// Like `thread::Executor::ScheduleAt` except:
+// a) Delays zero or negative are executed immediately in the current thread.
+// b) Infinite delays are never scheduled.
+// c) Calls this test's `ScheduleAt` helper instead of using `pool` directly.
+static void RunAfterDelay(absl::Duration delay,
+ absl::synchronization_internal::ThreadPool *pool,
+ const std::function<void()> &callback) {
+ if (delay <= absl::ZeroDuration()) {
+ callback(); // immediate
+ } else if (delay != absl::InfiniteDuration()) {
+ ScheduleAfter(pool, delay, callback);
}
+}
- bool ReaderLockWhenWithTimeout(absl::Duration timeout) {
- bool b = use_deadline_
- ? mu_.ReaderLockWhenWithDeadline(c_, absl::Now() + timeout)
- : mu_.ReaderLockWhenWithTimeout(c_, timeout);
- mu_.ReaderUnlock();
- return b;
- }
+class TimeoutTest : public ::testing::Test,
+ public ::testing::WithParamInterface<TimeoutTestParam> {};
- void Await() {
- absl::MutexLock lock(&mu_);
- mu_.Await(c_);
- }
+std::vector<TimeoutTestParam> MakeTimeoutTestParamValues() {
+ // The `finite` delay is a finite, relatively short, delay. We make it larger
+ // than our allowed scheduling delay (slop factor) to avoid confusion when
+ // diagnosing test failures. The other constants here have clear meanings.
+ const absl::Duration finite = 3 * TimeoutTestAllowedSchedulingDelay();
+ const absl::Duration never = absl::InfiniteDuration();
+ const absl::Duration negative = -absl::InfiniteDuration();
+ const absl::Duration immediate = absl::ZeroDuration();
- void Signal(bool v) {
- absl::MutexLock lock(&mu_);
- b_ = v;
- cv_.Signal();
+ // Every test case is run twice; once using the absolute deadline API and once
+ // using the relative timeout API.
+ std::vector<TimeoutTestParam> values;
+ for (bool use_absolute_deadline : {false, true}) {
+ // Tests with a negative timeout (deadline in the past), which should
+ // immediately return current state of the condition.
+
+ // The condition is already true:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ negative, // wait_timeout
+ immediate, // satisfy_condition_delay
+ true, // expected_result
+ immediate, // expected_delay
+ });
+
+ // The condition becomes true, but the timeout has already expired:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ negative, // wait_timeout
+ finite, // satisfy_condition_delay
+ false, // expected_result
+ immediate // expected_delay
+ });
+
+ // The condition never becomes true:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ negative, // wait_timeout
+ never, // satisfy_condition_delay
+ false, // expected_result
+ immediate // expected_delay
+ });
+
+ // Tests with an infinite timeout (deadline in the infinite future), which
+ // should only return when the condition becomes true.
+
+ // The condition is already true:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ never, // wait_timeout
+ immediate, // satisfy_condition_delay
+ true, // expected_result
+ immediate // expected_delay
+ });
+
+ // The condition becomes true before the (infinite) expiry:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ never, // wait_timeout
+ finite, // satisfy_condition_delay
+ true, // expected_result
+ finite, // expected_delay
+ });
+
+ // Tests with a (small) finite timeout (deadline soon), with the condition
+ // becoming true both before and after its expiry.
+
+ // The condition is already true:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ never, // wait_timeout
+ immediate, // satisfy_condition_delay
+ true, // expected_result
+ immediate // expected_delay
+ });
+
+ // The condition becomes true before the expiry:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ finite * 2, // wait_timeout
+ finite, // satisfy_condition_delay
+ true, // expected_result
+ finite // expected_delay
+ });
+
+ // The condition becomes true, but the timeout has already expired:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ finite, // wait_timeout
+ finite * 2, // satisfy_condition_delay
+ false, // expected_result
+ finite // expected_delay
+ });
+
+ // The condition never becomes true:
+ values.push_back(TimeoutTestParam{
+ __FILE__, __LINE__, use_absolute_deadline,
+ finite, // wait_timeout
+ never, // satisfy_condition_delay
+ false, // expected_result
+ finite // expected_delay
+ });
}
-
- bool WaitWithTimeout(absl::Duration timeout) {
- absl::MutexLock lock(&mu_);
- absl::Time deadline = absl::Now() + timeout;
- if (use_deadline_) {
- while (!b_ && !cv_.WaitWithDeadline(&mu_, deadline)) {
- }
- } else {
- while (!b_ && !cv_.WaitWithTimeout(&mu_, timeout)) {
- timeout = deadline - absl::Now(); // recompute timeout
- }
+ return values;
+}
+
+// Instantiate `TimeoutTest` with `MakeTimeoutTestParamValues()`.
+INSTANTIATE_TEST_CASE_P(All, TimeoutTest,
+ testing::ValuesIn(MakeTimeoutTestParamValues()));
+
+TEST_P(TimeoutTest, Await) {
+ const TimeoutTestParam params = GetParam();
+ ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+ // Because this test asserts bounds on scheduling delays it is flaky. To
+ // compensate it loops forever until it passes. Failures express as test
+ // timeouts, in which case the test log can be used to diagnose the issue.
+ for (int attempt = 1;; ++attempt) {
+ ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+ absl::Mutex mu;
+ bool value = false; // condition value (under mu)
+
+ std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+ CreateDefaultPool();
+ RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+ absl::MutexLock l(&mu);
+ value = true;
+ });
+
+ absl::MutexLock lock(&mu);
+ absl::Time start_time = absl::Now();
+ absl::Condition cond(&value);
+ bool result =
+ params.use_absolute_deadline
+ ? mu.AwaitWithDeadline(cond, start_time + params.wait_timeout)
+ : mu.AwaitWithTimeout(cond, params.wait_timeout);
+ if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+ EXPECT_EQ(params.expected_result, result);
+ break;
}
- return b_;
- }
-
- void Wait() {
- absl::MutexLock lock(&mu_);
- while (!b_) cv_.Wait(&mu_);
- }
-
- private:
- const bool use_deadline_;
-
- bool b_;
- absl::Condition c_;
- absl::CondVar cv_;
- absl::Mutex mu_;
-};
-
-class OperationTimer {
- public:
- OperationTimer() : start_(absl::Now()) {}
- absl::Duration Get() const { return absl::Now() - start_; }
-
- private:
- const absl::Time start_;
-};
-
-static void CheckResults(bool exp_result, bool act_result,
- absl::Duration exp_duration,
- absl::Duration act_duration) {
- ABSL_RAW_CHECK(exp_result == act_result, "CheckResults failed");
- // Allow for some worse-case scheduling delay and clock skew.
- if ((exp_duration - absl::Milliseconds(40) > act_duration) ||
- (exp_duration + absl::Milliseconds(150) < act_duration)) {
- ABSL_RAW_LOG(FATAL, "CheckResults failed: operation took %s, expected %s",
- absl::FormatDuration(act_duration).c_str(),
- absl::FormatDuration(exp_duration).c_str());
}
}
-static void TestAwaitTimeout(Cond *cp, absl::Duration timeout, bool exp_result,
- absl::Duration exp_duration) {
- OperationTimer t;
- bool act_result = cp->AwaitWithTimeout(timeout);
- CheckResults(exp_result, act_result, exp_duration, t.Get());
-}
-
-static void TestLockWhenTimeout(Cond *cp, absl::Duration timeout,
- bool exp_result, absl::Duration exp_duration) {
- OperationTimer t;
- bool act_result = cp->LockWhenWithTimeout(timeout);
- CheckResults(exp_result, act_result, exp_duration, t.Get());
-}
+TEST_P(TimeoutTest, LockWhen) {
+ const TimeoutTestParam params = GetParam();
+ ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+ // Because this test asserts bounds on scheduling delays it is flaky. To
+ // compensate it loops forever until it passes. Failures express as test
+ // timeouts, in which case the test log can be used to diagnose the issue.
+ for (int attempt = 1;; ++attempt) {
+ ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+ absl::Mutex mu;
+ bool value = false; // condition value (under mu)
+
+ std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+ CreateDefaultPool();
+ RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+ absl::MutexLock l(&mu);
+ value = true;
+ });
+
+ absl::Time start_time = absl::Now();
+ absl::Condition cond(&value);
+ bool result =
+ params.use_absolute_deadline
+ ? mu.LockWhenWithDeadline(cond, start_time + params.wait_timeout)
+ : mu.LockWhenWithTimeout(cond, params.wait_timeout);
+ mu.Unlock();
-static void TestReaderLockWhenTimeout(Cond *cp, absl::Duration timeout,
- bool exp_result,
- absl::Duration exp_duration) {
- OperationTimer t;
- bool act_result = cp->ReaderLockWhenWithTimeout(timeout);
- CheckResults(exp_result, act_result, exp_duration, t.Get());
+ if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+ EXPECT_EQ(params.expected_result, result);
+ break;
+ }
+ }
}
-static void TestWaitTimeout(Cond *cp, absl::Duration timeout, bool exp_result,
- absl::Duration exp_duration) {
- OperationTimer t;
- bool act_result = cp->WaitWithTimeout(timeout);
- CheckResults(exp_result, act_result, exp_duration, t.Get());
+TEST_P(TimeoutTest, ReaderLockWhen) {
+ const TimeoutTestParam params = GetParam();
+ ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+ // Because this test asserts bounds on scheduling delays it is flaky. To
+ // compensate it loops forever until it passes. Failures express as test
+ // timeouts, in which case the test log can be used to diagnose the issue.
+ for (int attempt = 0;; ++attempt) {
+ ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+ absl::Mutex mu;
+ bool value = false; // condition value (under mu)
+
+ std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+ CreateDefaultPool();
+ RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+ absl::MutexLock l(&mu);
+ value = true;
+ });
+
+ absl::Time start_time = absl::Now();
+ bool result =
+ params.use_absolute_deadline
+ ? mu.ReaderLockWhenWithDeadline(absl::Condition(&value),
+ start_time + params.wait_timeout)
+ : mu.ReaderLockWhenWithTimeout(absl::Condition(&value),
+ params.wait_timeout);
+ mu.ReaderUnlock();
+
+ if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+ EXPECT_EQ(params.expected_result, result);
+ break;
+ }
+ }
}
-// Tests with a negative timeout (deadline in the past), which should
-// immediately return the current state of the condition.
-static void TestNegativeTimeouts(absl::synchronization_internal::ThreadPool *tp,
- Cond *cp) {
- const absl::Duration negative = -absl::InfiniteDuration();
- const absl::Duration immediate = absl::ZeroDuration();
-
- // The condition is already true:
- cp->Set(true);
- TestAwaitTimeout(cp, negative, true, immediate);
- TestLockWhenTimeout(cp, negative, true, immediate);
- TestReaderLockWhenTimeout(cp, negative, true, immediate);
- TestWaitTimeout(cp, negative, true, immediate);
-
- // The condition becomes true, but the timeout has already expired:
- const absl::Duration delay = absl::Milliseconds(200);
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), 3 * delay);
- TestAwaitTimeout(cp, negative, false, immediate);
- TestLockWhenTimeout(cp, negative, false, immediate);
- TestReaderLockWhenTimeout(cp, negative, false, immediate);
- cp->Await(); // wait for the scheduled Set() to complete
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay);
- TestWaitTimeout(cp, negative, false, immediate);
- cp->Wait(); // wait for the scheduled Signal() to complete
-
- // The condition never becomes true:
- cp->Set(false);
- TestAwaitTimeout(cp, negative, false, immediate);
- TestLockWhenTimeout(cp, negative, false, immediate);
- TestReaderLockWhenTimeout(cp, negative, false, immediate);
- TestWaitTimeout(cp, negative, false, immediate);
-}
-
-// Tests with an infinite timeout (deadline in the infinite future), which
-// should only return when the condition becomes true.
-static void TestInfiniteTimeouts(absl::synchronization_internal::ThreadPool *tp,
- Cond *cp) {
- const absl::Duration infinite = absl::InfiniteDuration();
- const absl::Duration immediate = absl::ZeroDuration();
-
- // The condition is already true:
- cp->Set(true);
- TestAwaitTimeout(cp, infinite, true, immediate);
- TestLockWhenTimeout(cp, infinite, true, immediate);
- TestReaderLockWhenTimeout(cp, infinite, true, immediate);
- TestWaitTimeout(cp, infinite, true, immediate);
-
- // The condition becomes true before the (infinite) expiry:
- const absl::Duration delay = absl::Milliseconds(200);
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
- TestAwaitTimeout(cp, infinite, true, delay);
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
- TestLockWhenTimeout(cp, infinite, true, delay);
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
- TestReaderLockWhenTimeout(cp, infinite, true, delay);
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay);
- TestWaitTimeout(cp, infinite, true, delay);
-}
-
-// Tests with a (small) finite timeout (deadline soon), with the condition
-// becoming true both before and after its expiry.
-static void TestFiniteTimeouts(absl::synchronization_internal::ThreadPool *tp,
- Cond *cp) {
- const absl::Duration finite = absl::Milliseconds(400);
- const absl::Duration immediate = absl::ZeroDuration();
+TEST_P(TimeoutTest, Wait) {
+ const TimeoutTestParam params = GetParam();
+ ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+ // Because this test asserts bounds on scheduling delays it is flaky. To
+ // compensate it loops forever until it passes. Failures express as test
+ // timeouts, in which case the test log can be used to diagnose the issue.
+ for (int attempt = 0;; ++attempt) {
+ ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+ absl::Mutex mu;
+ bool value = false; // condition value (under mu)
+ absl::CondVar cv; // signals a change of `value`
+
+ std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+ CreateDefaultPool();
+ RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+ absl::MutexLock l(&mu);
+ value = true;
+ cv.Signal();
+ });
+
+ absl::MutexLock lock(&mu);
+ absl::Time start_time = absl::Now();
+ absl::Duration timeout = params.wait_timeout;
+ absl::Time deadline = start_time + timeout;
+ while (!value) {
+ if (params.use_absolute_deadline ? cv.WaitWithDeadline(&mu, deadline)
+ : cv.WaitWithTimeout(&mu, timeout)) {
+ break; // deadline/timeout exceeded
+ }
+ timeout = deadline - absl::Now(); // recompute
+ }
+ bool result = value; // note: `mu` is still held
- // The condition is already true:
- cp->Set(true);
- TestAwaitTimeout(cp, finite, true, immediate);
- TestLockWhenTimeout(cp, finite, true, immediate);
- TestReaderLockWhenTimeout(cp, finite, true, immediate);
- TestWaitTimeout(cp, finite, true, immediate);
-
- // The condition becomes true before the expiry:
- const absl::Duration delay1 = finite / 2;
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
- TestAwaitTimeout(cp, finite, true, delay1);
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
- TestLockWhenTimeout(cp, finite, true, delay1);
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
- TestReaderLockWhenTimeout(cp, finite, true, delay1);
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay1);
- TestWaitTimeout(cp, finite, true, delay1);
-
- // The condition becomes true, but the timeout has already expired:
- const absl::Duration delay2 = finite * 2;
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), 3 * delay2);
- TestAwaitTimeout(cp, finite, false, finite);
- TestLockWhenTimeout(cp, finite, false, finite);
- TestReaderLockWhenTimeout(cp, finite, false, finite);
- cp->Await(); // wait for the scheduled Set() to complete
- cp->Set(false);
- ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay2);
- TestWaitTimeout(cp, finite, false, finite);
- cp->Wait(); // wait for the scheduled Signal() to complete
-
- // The condition never becomes true:
- cp->Set(false);
- TestAwaitTimeout(cp, finite, false, finite);
- TestLockWhenTimeout(cp, finite, false, finite);
- TestReaderLockWhenTimeout(cp, finite, false, finite);
- TestWaitTimeout(cp, finite, false, finite);
-}
-
-TEST(Mutex, Timeouts) {
- auto tp = CreateDefaultPool();
- for (bool use_deadline : {false, true}) {
- Cond cond(use_deadline);
- TestNegativeTimeouts(tp.get(), &cond);
- TestInfiniteTimeouts(tp.get(), &cond);
- TestFiniteTimeouts(tp.get(), &cond);
+ if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+ EXPECT_EQ(params.expected_result, result);
+ break;
+ }
}
}
diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc
index a38acc5f..472b7a3e 100644
--- a/absl/synchronization/notification.cc
+++ b/absl/synchronization/notification.cc
@@ -22,7 +22,8 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
+
void Notification::Notify() {
MutexLock l(&this->mutex_);
@@ -82,5 +83,5 @@ bool Notification::WaitForNotificationWithDeadline(absl::Time deadline) const {
return notified;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h
index 99f8ee63..25821b18 100644
--- a/absl/synchronization/notification.h
+++ b/absl/synchronization/notification.h
@@ -52,11 +52,12 @@
#include <atomic>
+#include "absl/base/macros.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// Notification
@@ -109,6 +110,6 @@ class Notification {
std::atomic<bool> notified_yet_; // written under mutex_
};
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_NOTIFICATION_H_
diff --git a/absl/synchronization/notification_test.cc b/absl/synchronization/notification_test.cc
index 83e17e34..d1b66743 100644
--- a/absl/synchronization/notification_test.cc
+++ b/absl/synchronization/notification_test.cc
@@ -21,7 +21,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// A thread-safe class that holds a counter.
class ThreadSafeCounter {
@@ -72,10 +72,13 @@ static void BasicTests(bool notify_before_waiting, Notification* notification) {
notification->WaitForNotificationWithTimeout(absl::Milliseconds(0)));
EXPECT_FALSE(notification->WaitForNotificationWithDeadline(absl::Now()));
+ const absl::Duration delay = absl::Milliseconds(50);
+ // Allow for a slight early return, to account for quality of implementation
+ // issues on various platforms.
+ const absl::Duration slop = absl::Microseconds(200);
absl::Time start = absl::Now();
- EXPECT_FALSE(
- notification->WaitForNotificationWithTimeout(absl::Milliseconds(50)));
- EXPECT_LE(start + absl::Milliseconds(50), absl::Now());
+ EXPECT_FALSE(notification->WaitForNotificationWithTimeout(delay));
+ EXPECT_LE(start + delay, absl::Now() + slop);
ThreadSafeCounter ready_counter;
ThreadSafeCounter done_counter;
@@ -122,5 +125,5 @@ TEST(NotificationTest, SanityTest) {
BasicTests(true, &local_notification2);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index e793da87..4d9c01c4 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -27,15 +27,16 @@ licenses(["notice"]) # Apache 2.0
cc_library(
name = "time",
srcs = [
+ "civil_time.cc",
"clock.cc",
"duration.cc",
"format.cc",
- "internal/get_current_time_ios.inc",
+ "internal/get_current_time_chrono.inc",
"internal/get_current_time_posix.inc",
- "internal/get_current_time_windows.inc",
"time.cc",
],
hdrs = [
+ "civil_time.h",
"clock.h",
"time.h",
],
@@ -73,10 +74,10 @@ cc_library(
cc_test(
name = "time_test",
srcs = [
+ "civil_time_test.cc",
"clock_test.cc",
"duration_test.cc",
"format_test.cc",
- "time_norm_test.cc",
"time_test.cc",
"time_zone_test.cc",
],
@@ -95,6 +96,7 @@ cc_test(
cc_test(
name = "time_benchmark",
srcs = [
+ "civil_time_benchmark.cc",
"clock_benchmark.cc",
"duration_benchmark.cc",
"format_benchmark.cc",
@@ -108,6 +110,7 @@ cc_test(
":test_util",
":time",
"//absl/base",
+ "//absl/hash",
"@com_github_google_benchmark//:benchmark_main",
],
)
diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt
index 06272364..53216cda 100644
--- a/absl/time/CMakeLists.txt
+++ b/absl/time/CMakeLists.txt
@@ -15,6 +15,7 @@
#
list(APPEND TIME_PUBLIC_HEADERS
+ "civil_time.h"
"clock.h"
"time.h"
)
@@ -29,6 +30,7 @@ list(APPEND TIME_INTERNAL_HEADERS
)
list(APPEND TIME_SRC
+ "civil_time.cc"
"time.cc"
"clock.cc"
"duration.cc"
@@ -74,11 +76,11 @@ absl_library(
# test time_test
list(APPEND TIME_TEST_SRC
+ "civil_time_test.cc"
"time_test.cc"
"clock_test.cc"
"duration_test.cc"
"format_test.cc"
- "time_norm_test.cc"
"time_test.cc"
"time_zone_test.cc"
"internal/test_util.cc"
diff --git a/absl/time/civil_time.cc b/absl/time/civil_time.cc
new file mode 100644
index 00000000..c6fa5469
--- /dev/null
+++ b/absl/time/civil_time.cc
@@ -0,0 +1,90 @@
+// 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
+//
+// 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.
+
+#include "absl/time/civil_time.h"
+
+#include <cstdlib>
+#include <string>
+
+#include "absl/strings/str_cat.h"
+#include "absl/time/time.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+namespace {
+
+// Since a civil time has a larger year range than absl::Time (64-bit years vs
+// 64-bit seconds, respectively) we normalize years to roughly +/- 400 years
+// around the year 2400, which will produce an equivalent year in a range that
+// absl::Time can handle.
+inline civil_year_t NormalizeYear(civil_year_t year) {
+ return 2400 + year % 400;
+}
+
+// Formats the given CivilSecond according to the given format.
+std::string FormatYearAnd(string_view fmt, CivilSecond cs) {
+ const CivilSecond ncs(NormalizeYear(cs.year()), cs.month(), cs.day(),
+ cs.hour(), cs.minute(), cs.second());
+ const TimeZone utc = UTCTimeZone();
+ // TODO(absl-team): Avoid conversion of fmt std::string.
+ return StrCat(cs.year(), FormatTime(std::string(fmt), FromCivil(ncs, utc), utc));
+}
+
+} // namespace
+
+std::string FormatCivilTime(CivilSecond c) {
+ return FormatYearAnd("-%m-%dT%H:%M:%S", c);
+}
+std::string FormatCivilTime(CivilMinute c) {
+ return FormatYearAnd("-%m-%dT%H:%M", c);
+}
+std::string FormatCivilTime(CivilHour c) {
+ return FormatYearAnd("-%m-%dT%H", c);
+}
+std::string FormatCivilTime(CivilDay c) {
+ return FormatYearAnd("-%m-%d", c);
+}
+std::string FormatCivilTime(CivilMonth c) {
+ return FormatYearAnd("-%m", c);
+}
+std::string FormatCivilTime(CivilYear c) {
+ return FormatYearAnd("", c);
+}
+
+namespace time_internal {
+
+std::ostream& operator<<(std::ostream& os, CivilYear y) {
+ return os << FormatCivilTime(y);
+}
+std::ostream& operator<<(std::ostream& os, CivilMonth m) {
+ return os << FormatCivilTime(m);
+}
+std::ostream& operator<<(std::ostream& os, CivilDay d) {
+ return os << FormatCivilTime(d);
+}
+std::ostream& operator<<(std::ostream& os, CivilHour h) {
+ return os << FormatCivilTime(h);
+}
+std::ostream& operator<<(std::ostream& os, CivilMinute m) {
+ return os << FormatCivilTime(m);
+}
+std::ostream& operator<<(std::ostream& os, CivilSecond s) {
+ return os << FormatCivilTime(s);
+}
+
+} // namespace time_internal
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/time/civil_time.h b/absl/time/civil_time.h
new file mode 100644
index 00000000..f6cc3ff8
--- /dev/null
+++ b/absl/time/civil_time.h
@@ -0,0 +1,488 @@
+// 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
+//
+// 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.
+//
+// -----------------------------------------------------------------------------
+// File: civil_time.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines abstractions for computing with "civil time".
+// The term "civil time" refers to the legally recognized human-scale time
+// that is represented by the six fields `YYYY-MM-DD hh:mm:ss`. A "date"
+// is perhaps the most common example of a civil time (represented here as
+// an `absl::CivilDay`).
+//
+// Modern-day civil time follows the Gregorian Calendar and is a
+// time-zone-independent concept: a civil time of "2015-06-01 12:00:00", for
+// example, is not tied to a time zone. Put another way, a civil time does not
+// map to a unique point in time; a civil time must be mapped to an absolute
+// time *through* a time zone.
+//
+// Because a civil time is what most people think of as "time," it is common to
+// map absolute times to civil times to present to users.
+//
+// Time zones define the relationship between absolute and civil times. Given an
+// absolute or civil time and a time zone, you can compute the other time:
+//
+// Civil Time = F(Absolute Time, Time Zone)
+// Absolute Time = G(Civil Time, Time Zone)
+//
+// The Abseil time library allows you to construct such civil times from
+// absolute times; consult time.h for such functionality.
+//
+// This library provides six classes for constructing civil-time objects, and
+// provides several helper functions for rounding, iterating, and performing
+// arithmetic on civil-time objects, while avoiding complications like
+// daylight-saving time (DST):
+//
+// * `absl::CivilSecond`
+// * `absl::CivilMinute`
+// * `absl::CivilHour`
+// * `absl::CivilDay`
+// * `absl::CivilMonth`
+// * `absl::CivilYear`
+//
+// Example:
+//
+// // Construct a civil-time object for a specific day
+// const absl::CivilDay cd(1969, 07, 20);
+//
+// // Construct a civil-time object for a specific second
+// const absl::CivilSecond cd(2018, 8, 1, 12, 0, 1);
+//
+// Note: In C++14 and later, this library is usable in a constexpr context.
+//
+// Example:
+//
+// // Valid in C++14
+// constexpr absl::CivilDay cd(1969, 07, 20);
+//
+
+#ifndef ABSL_TIME_CIVIL_TIME_H_
+#define ABSL_TIME_CIVIL_TIME_H_
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "absl/time/internal/cctz/include/cctz/civil_time.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+
+namespace time_internal {
+struct second_tag : cctz::detail::second_tag {};
+struct minute_tag : second_tag, cctz::detail::minute_tag {};
+struct hour_tag : minute_tag, cctz::detail::hour_tag {};
+struct day_tag : hour_tag, cctz::detail::day_tag {};
+struct month_tag : day_tag, cctz::detail::month_tag {};
+struct year_tag : month_tag, cctz::detail::year_tag {};
+} // namespace time_internal
+
+// -----------------------------------------------------------------------------
+// CivilSecond, CivilMinute, CivilHour, CivilDay, CivilMonth, CivilYear
+// -----------------------------------------------------------------------------
+//
+// Each of these civil-time types is a simple value type with the same
+// interface for construction and the same six accessors for each of the civil
+// time fields (year, month, day, hour, minute, and second, aka YMDHMS). These
+// classes differ only in their alignment, which is indicated by the type name
+// and specifies the field on which arithmetic operates.
+//
+// CONSTRUCTION
+//
+// Each of the civil-time types can be constructed in two ways: by directly
+// passing to the constructor up to six integers representing the YMDHMS fields,
+// or by copying the YMDHMS fields from a differently aligned civil-time type.
+// Omitted fields are assigned their minimum valid value. Hours, minutes, and
+// seconds will be set to 0, month and day will be set to 1. Since there is no
+// minimum year, the default is 1970.
+//
+// Examples:
+//
+// absl::CivilDay default_value; // 1970-01-01 00:00:00
+//
+// absl::CivilDay a(2015, 2, 3); // 2015-02-03 00:00:00
+// absl::CivilDay b(2015, 2, 3, 4, 5, 6); // 2015-02-03 00:00:00
+// absl::CivilDay c(2015); // 2015-01-01 00:00:00
+//
+// absl::CivilSecond ss(2015, 2, 3, 4, 5, 6); // 2015-02-03 04:05:06
+// absl::CivilMinute mm(ss); // 2015-02-03 04:05:00
+// absl::CivilHour hh(mm); // 2015-02-03 04:00:00
+// absl::CivilDay d(hh); // 2015-02-03 00:00:00
+// absl::CivilMonth m(d); // 2015-02-01 00:00:00
+// absl::CivilYear y(m); // 2015-01-01 00:00:00
+//
+// m = absl::CivilMonth(y); // 2015-01-01 00:00:00
+// d = absl::CivilDay(m); // 2015-01-01 00:00:00
+// hh = absl::CivilHour(d); // 2015-01-01 00:00:00
+// mm = absl::CivilMinute(hh); // 2015-01-01 00:00:00
+// ss = absl::CivilSecond(mm); // 2015-01-01 00:00:00
+//
+// Each civil-time class is aligned to the civil-time field indicated in the
+// class's name after normalization. Alignment is performed by setting all the
+// inferior fields to their minimum valid value (as described above). The
+// following are examples of how each of the six types would align the fields
+// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the
+// string format used here is not important; it's just a shorthand way of
+// showing the six YMDHMS fields.)
+//
+// absl::CivilSecond : 2015-11-22 12:34:56
+// absl::CivilMinute : 2015-11-22 12:34:00
+// absl::CivilHour : 2015-11-22 12:00:00
+// absl::CivilDay : 2015-11-22 00:00:00
+// absl::CivilMonth : 2015-11-01 00:00:00
+// absl::CivilYear : 2015-01-01 00:00:00
+//
+// Each civil-time type performs arithmetic on the field to which it is
+// aligned. This means that adding 1 to an absl::CivilDay increments the day
+// field (normalizing as necessary), and subtracting 7 from an absl::CivilMonth
+// operates on the month field (normalizing as necessary). All arithmetic
+// produces a valid civil time. Difference requires two similarly aligned
+// civil-time objects and returns the scalar answer in units of the objects'
+// alignment. For example, the difference between two absl::CivilHour objects
+// will give an answer in units of civil hours.
+//
+// ALIGNMENT CONVERSION
+//
+// The alignment of a civil-time object cannot change, but the object may be
+// used to construct a new object with a different alignment. This is referred
+// to as "realigning". When realigning to a type with the same or more
+// precision (e.g., absl::CivilDay -> absl::CivilSecond), the conversion may be
+// performed implicitly since no information is lost. However, if information
+// could be discarded (e.g., CivilSecond -> CivilDay), the conversion must
+// be explicit at the call site.
+//
+// Examples:
+//
+// void UseDay(absl::CivilDay day);
+//
+// absl::CivilSecond cs;
+// UseDay(cs); // Won't compile because data may be discarded
+// UseDay(absl::CivilDay(cs)); // OK: explicit conversion
+//
+// absl::CivilDay cd;
+// UseDay(cd); // OK: no conversion needed
+//
+// absl::CivilMonth cm;
+// UseDay(cm); // OK: implicit conversion to absl::CivilDay
+//
+// NORMALIZATION
+//
+// Normalization takes invalid values and adjusts them to produce valid values.
+// Within the civil-time library, integer arguments passed to the Civil*
+// constructors may be out-of-range, in which case they are normalized by
+// carrying overflow into a field of courser granularity to produce valid
+// civil-time objects. This normalization enables natural arithmetic on
+// constructor arguments without worrying about the field's range.
+//
+// Examples:
+//
+// // Out-of-range; normalized to 2016-11-01
+// absl::CivilDay d(2016, 10, 32);
+// // Out-of-range, negative: normalized to 2016-10-30T23
+// absl::CivilHour h1(2016, 10, 31, -1);
+// // Normalization is cumulative: normalized to 2016-10-30T23
+// absl::CivilHour h2(2016, 10, 32, -25);
+//
+// Note: If normalization is undesired, you can signal an error by comparing
+// the constructor arguments to the normalized values returned by the YMDHMS
+// properties.
+//
+// COMPARISON
+//
+// Comparison between civil-time objects considers all six YMDHMS fields,
+// regardless of the type's alignment. Comparison between differently aligned
+// civil-time types is allowed.
+//
+// Examples:
+//
+// absl::CivilDay feb_3(2015, 2, 3); // 2015-02-03 00:00:00
+// absl::CivilDay mar_4(2015, 3, 4); // 2015-03-04 00:00:00
+// // feb_3 < mar_4
+// // absl::CivilYear(feb_3) == absl::CivilYear(mar_4)
+//
+// absl::CivilSecond feb_3_noon(2015, 2, 3, 12, 0, 0); // 2015-02-03 12:00:00
+// // feb_3 < feb_3_noon
+// // feb_3 == absl::CivilDay(feb_3_noon)
+//
+// // Iterates all the days of February 2015.
+// for (absl::CivilDay d(2015, 2, 1); d < absl::CivilMonth(2015, 3); ++d) {
+// // ...
+// }
+//
+// ARITHMETIC
+//
+// Civil-time types support natural arithmetic operators such as addition,
+// subtraction, and difference. Arithmetic operates on the civil-time field
+// indicated in the type's name. Difference operators require arguments with
+// the same alignment and return the answer in units of the alignment.
+//
+// Example:
+//
+// absl::CivilDay a(2015, 2, 3);
+// ++a; // 2015-02-04 00:00:00
+// --a; // 2015-02-03 00:00:00
+// absl::CivilDay b = a + 1; // 2015-02-04 00:00:00
+// absl::CivilDay c = 1 + b; // 2015-02-05 00:00:00
+// int n = c - a; // n = 2 (civil days)
+// int m = c - absl::CivilMonth(c); // Won't compile: different types.
+//
+// ACCESSORS
+//
+// Each civil-time type has accessors for all six of the civil-time fields:
+// year, month, day, hour, minute, and second.
+//
+// civil_year_t year()
+// int month()
+// int day()
+// int hour()
+// int minute()
+// int second()
+//
+// Recall that fields inferior to the type's aligment will be set to their
+// minimum valid value.
+//
+// Example:
+//
+// absl::CivilDay d(2015, 6, 28);
+// // d.year() == 2015
+// // d.month() == 6
+// // d.day() == 28
+// // d.hour() == 0
+// // d.minute() == 0
+// // d.second() == 0
+//
+// CASE STUDY: Adding a month to January 31.
+//
+// One of the classic questions that arises when considering a civil time
+// library (or a date library or a date/time library) is this:
+// "What is the result of adding a month to January 31?"
+// This is an interesting question because it is unclear what is meant by a
+// "month", and several different answers are possible, depending on context:
+//
+// 1. March 3 (or 2 if a leap year), if "add a month" means to add a month to
+// the current month, and adjust the date to overflow the extra days into
+// March. In this case the result of "February 31" would be normalized as
+// within the civil-time library.
+// 2. February 28 (or 29 if a leap year), if "add a month" means to add a
+// month, and adjust the date while holding the resulting month constant.
+// In this case, the result of "February 31" would be truncated to the last
+// day in February.
+// 3. An error. The caller may get some error, an exception, an invalid date
+// object, or perhaps return `false`. This may make sense because there is
+// no single unambiguously correct answer to the question.
+//
+// Practically speaking, any answer that is not what the programmer intended
+// is the wrong answer.
+//
+// The Abseil time library avoids this problem by making it impossible to
+// ask ambiguous questions. All civil-time objects are aligned to a particular
+// civil-field boundary (such as aligned to a year, month, day, hour, minute,
+// or second), and arithmetic operates on the field to which the object is
+// aligned. This means that in order to "add a month" the object must first be
+// aligned to a month boundary, which is equivalent to the first day of that
+// month.
+//
+// Of course, there are ways to compute an answer the question at hand using
+// this Abseil time library, but they require the programmer to be explicit
+// about the answer they expect. To illustrate, let's see how to compute all
+// three of the above possible answers to the question of "Jan 31 plus 1
+// month":
+//
+// Example:
+//
+// const absl::CivilDay d(2015, 1, 31);
+//
+// // Answer 1:
+// // Add 1 to the month field in the constructor, and rely on normalization.
+// const auto normalized = absl::CivilDay(d.year(), d.month() + 1, d.day());
+// // normalized == 2015-03-03 (aka Feb 31)
+//
+// // Answer 2:
+// // Add 1 to month field, capping to the end of next month.
+// const auto next_month = absl::CivilMonth(d) + 1;
+// const auto last_day_of_next_month = absl::CivilDay(next_month + 1) - 1;
+// const auto capped = std::min(normalized, last_day_of_next_month);
+// // capped == 2015-02-28
+//
+// // Answer 3:
+// // Signal an error if the normalized answer is not in next month.
+// if (absl::CivilMonth(normalized) != next_month) {
+// // error, month overflow
+// }
+//
+using CivilSecond =
+ time_internal::cctz::detail::civil_time<time_internal::second_tag>;
+using CivilMinute =
+ time_internal::cctz::detail::civil_time<time_internal::minute_tag>;
+using CivilHour =
+ time_internal::cctz::detail::civil_time<time_internal::hour_tag>;
+using CivilDay =
+ time_internal::cctz::detail::civil_time<time_internal::day_tag>;
+using CivilMonth =
+ time_internal::cctz::detail::civil_time<time_internal::month_tag>;
+using CivilYear =
+ time_internal::cctz::detail::civil_time<time_internal::year_tag>;
+
+// civil_year_t
+//
+// Type alias of a civil-time year value. This type is guaranteed to (at least)
+// support any year value supported by `time_t`.
+//
+// Example:
+//
+// absl::CivilSecond cs = ...;
+// absl::civil_year_t y = cs.year();
+// cs = absl::CivilSecond(y, 1, 1, 0, 0, 0); // CivilSecond(CivilYear(cs))
+//
+using civil_year_t = time_internal::cctz::year_t;
+
+// civil_diff_t
+//
+// Type alias of the difference between two civil-time values.
+// This type is used to indicate arguments that are not
+// normalized (such as parameters to the civil-time constructors), the results
+// of civil-time subtraction, or the operand to civil-time addition.
+//
+// Example:
+//
+// absl::civil_diff_t n_sec = cs1 - cs2; // cs1 == cs2 + n_sec;
+//
+using civil_diff_t = time_internal::cctz::diff_t;
+
+// Weekday::monday, Weekday::tuesday, Weekday::wednesday, Weekday::thursday,
+// Weekday::friday, Weekday::saturday, Weekday::sunday
+//
+// The Weekday enum class represents the civil-time concept of a "weekday" with
+// members for all days of the week.
+//
+// absl::Weekday wd = absl::Weekday::thursday;
+//
+using Weekday = time_internal::cctz::weekday;
+
+// GetWeekday()
+//
+// Returns the absl::Weekday for the given absl::CivilDay.
+//
+// Example:
+//
+// absl::CivilDay a(2015, 8, 13);
+// absl::Weekday wd = absl::GetWeekday(a); // wd == absl::Weekday::thursday
+//
+inline Weekday GetWeekday(CivilDay cd) {
+ return time_internal::cctz::get_weekday(cd);
+}
+
+// NextWeekday()
+// PrevWeekday()
+//
+// Returns the absl::CivilDay that strictly follows or precedes a given
+// absl::CivilDay, and that falls on the given absl::Weekday.
+//
+// Example, given the following month:
+//
+// August 2015
+// Su Mo Tu We Th Fr Sa
+// 1
+// 2 3 4 5 6 7 8
+// 9 10 11 12 13 14 15
+// 16 17 18 19 20 21 22
+// 23 24 25 26 27 28 29
+// 30 31
+//
+// absl::CivilDay a(2015, 8, 13);
+// // absl::GetWeekday(a) == absl::Weekday::thursday
+// absl::CivilDay b = absl::NextWeekday(a, absl::Weekday::thursday);
+// // b = 2015-08-20
+// absl::CivilDay c = absl::PrevWeekday(a, absl::Weekday::thursday);
+// // c = 2015-08-06
+//
+// absl::CivilDay d = ...
+// // Gets the following Thursday if d is not already Thursday
+// absl::CivilDay thurs1 = absl::PrevWeekday(d, absl::Weekday::thursday) + 7;
+// // Gets the previous Thursday if d is not already Thursday
+// absl::CivilDay thurs2 = absl::NextWeekday(d, absl::Weekday::thursday) - 7;
+//
+inline CivilDay NextWeekday(CivilDay cd, Weekday wd) {
+ return CivilDay(time_internal::cctz::next_weekday(cd, wd));
+}
+inline CivilDay PrevWeekday(CivilDay cd, Weekday wd) {
+ return CivilDay(time_internal::cctz::prev_weekday(cd, wd));
+}
+
+// GetYearDay()
+//
+// Returns the day-of-year for the given absl::CivilDay.
+//
+// Example:
+//
+// absl::CivilDay a(2015, 1, 1);
+// int yd_jan_1 = absl::GetYearDay(a); // yd_jan_1 = 1
+// absl::CivilDay b(2015, 12, 31);
+// int yd_dec_31 = absl::GetYearDay(b); // yd_dec_31 = 365
+//
+inline int GetYearDay(CivilDay cd) {
+ return time_internal::cctz::get_yearday(cd);
+}
+
+// FormatCivilTime()
+//
+// Formats the given civil-time value into a string value of the following
+// format:
+//
+// Type | Format
+// ---------------------------------
+// CivilSecond | YYYY-MM-DDTHH:MM:SS
+// CivilMinute | YYYY-MM-DDTHH:MM
+// CivilHour | YYYY-MM-DDTHH
+// CivilDay | YYYY-MM-DD
+// CivilMonth | YYYY-MM
+// CivilYear | YYYY
+//
+// Example:
+//
+// absl::CivilDay d = absl::CivilDay(1969, 7, 20);
+// string day_string = absl::FormatCivilTime(d); // "1969-07-20"
+//
+std::string FormatCivilTime(CivilSecond c);
+std::string FormatCivilTime(CivilMinute c);
+std::string FormatCivilTime(CivilHour c);
+std::string FormatCivilTime(CivilDay c);
+std::string FormatCivilTime(CivilMonth c);
+std::string FormatCivilTime(CivilYear c);
+
+namespace time_internal { // For functions found via ADL on civil-time tags.
+
+// Streaming Operators
+//
+// Each civil-time type may be sent to an output stream using operator<<().
+// The result matches the string produced by `FormatCivilTime()`.
+//
+// Example:
+//
+// absl::CivilDay d = absl::CivilDay("1969-07-20");
+// std::cout << "Date is: " << d << "\n";
+//
+std::ostream& operator<<(std::ostream& os, CivilYear y);
+std::ostream& operator<<(std::ostream& os, CivilMonth m);
+std::ostream& operator<<(std::ostream& os, CivilDay d);
+std::ostream& operator<<(std::ostream& os, CivilHour h);
+std::ostream& operator<<(std::ostream& os, CivilMinute m);
+std::ostream& operator<<(std::ostream& os, CivilSecond s);
+
+} // namespace time_internal
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_TIME_CIVIL_TIME_H_
diff --git a/absl/time/civil_time_benchmark.cc b/absl/time/civil_time_benchmark.cc
new file mode 100644
index 00000000..f30f636d
--- /dev/null
+++ b/absl/time/civil_time_benchmark.cc
@@ -0,0 +1,107 @@
+// 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
+//
+// 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.
+
+#include "absl/time/civil_time.h"
+
+#include <numeric>
+#include <vector>
+
+#include "absl/hash/hash.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+// Run on (12 X 3492 MHz CPUs); 2018-11-05T13:44:29.814239103-08:00
+// CPU: Intel Haswell with HyperThreading (6 cores) dL1:32KB dL2:256KB dL3:15MB
+// Benchmark Time(ns) CPU(ns) Iterations
+// ----------------------------------------------------------------
+// BM_Difference_Days 14.5 14.5 48531105
+// BM_Step_Days 12.6 12.6 54876006
+// BM_Format 587 587 1000000
+// BM_Parse 692 692 1000000
+// BM_RoundTripFormatParse 1309 1309 532075
+// BM_CivilYearAbslHash 0.710 0.710 976400000
+// BM_CivilMonthAbslHash 1.13 1.13 619500000
+// BM_CivilDayAbslHash 1.70 1.70 426000000
+// BM_CivilHourAbslHash 2.45 2.45 287600000
+// BM_CivilMinuteAbslHash 3.21 3.21 226200000
+// BM_CivilSecondAbslHash 4.10 4.10 171800000
+
+void BM_Difference_Days(benchmark::State& state) {
+ const absl::CivilDay c(2014, 8, 22);
+ const absl::CivilDay epoch(1970, 1, 1);
+ while (state.KeepRunning()) {
+ const absl::civil_diff_t n = c - epoch;
+ benchmark::DoNotOptimize(n);
+ }
+}
+BENCHMARK(BM_Difference_Days);
+
+void BM_Step_Days(benchmark::State& state) {
+ const absl::CivilDay kStart(2014, 8, 22);
+ absl::CivilDay c = kStart;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(++c);
+ }
+}
+BENCHMARK(BM_Step_Days);
+
+void BM_Format(benchmark::State& state) {
+ const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
+ while (state.KeepRunning()) {
+ const std::string s = absl::FormatCivilTime(c);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_Format);
+
+template <typename T>
+void BM_CivilTimeAbslHash(benchmark::State& state) {
+ const int kSize = 100000;
+ std::vector<T> civil_times(kSize);
+ std::iota(civil_times.begin(), civil_times.end(), T(2018));
+
+ absl::Hash<T> absl_hasher;
+ while (state.KeepRunningBatch(kSize)) {
+ for (const T civil_time : civil_times) {
+ benchmark::DoNotOptimize(absl_hasher(civil_time));
+ }
+ }
+}
+void BM_CivilYearAbslHash(benchmark::State& state) {
+ BM_CivilTimeAbslHash<absl::CivilYear>(state);
+}
+void BM_CivilMonthAbslHash(benchmark::State& state) {
+ BM_CivilTimeAbslHash<absl::CivilMonth>(state);
+}
+void BM_CivilDayAbslHash(benchmark::State& state) {
+ BM_CivilTimeAbslHash<absl::CivilDay>(state);
+}
+void BM_CivilHourAbslHash(benchmark::State& state) {
+ BM_CivilTimeAbslHash<absl::CivilHour>(state);
+}
+void BM_CivilMinuteAbslHash(benchmark::State& state) {
+ BM_CivilTimeAbslHash<absl::CivilMinute>(state);
+}
+void BM_CivilSecondAbslHash(benchmark::State& state) {
+ BM_CivilTimeAbslHash<absl::CivilSecond>(state);
+}
+BENCHMARK(BM_CivilYearAbslHash);
+BENCHMARK(BM_CivilMonthAbslHash);
+BENCHMARK(BM_CivilDayAbslHash);
+BENCHMARK(BM_CivilHourAbslHash);
+BENCHMARK(BM_CivilMinuteAbslHash);
+BENCHMARK(BM_CivilSecondAbslHash);
+
+} // namespace
diff --git a/absl/time/civil_time_test.cc b/absl/time/civil_time_test.cc
new file mode 100644
index 00000000..dc83d7a9
--- /dev/null
+++ b/absl/time/civil_time_test.cc
@@ -0,0 +1,1073 @@
+// 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
+//
+// 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.
+
+#include "absl/time/civil_time.h"
+
+#include <limits>
+#include <sstream>
+#include <type_traits>
+
+#include "absl/base/macros.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+TEST(CivilTime, DefaultConstruction) {
+ absl::CivilSecond ss;
+ EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(ss));
+
+ absl::CivilMinute mm;
+ EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(mm));
+
+ absl::CivilHour hh;
+ EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hh));
+
+ absl::CivilDay d;
+ EXPECT_EQ("1970-01-01", absl::FormatCivilTime(d));
+
+ absl::CivilMonth m;
+ EXPECT_EQ("1970-01", absl::FormatCivilTime(m));
+
+ absl::CivilYear y;
+ EXPECT_EQ("1970", absl::FormatCivilTime(y));
+}
+
+TEST(CivilTime, StructMember) {
+ struct S {
+ absl::CivilDay day;
+ };
+ S s = {};
+ EXPECT_EQ(absl::CivilDay{}, s.day);
+}
+
+TEST(CivilTime, FieldsConstruction) {
+ EXPECT_EQ("2015-01-02T03:04:05",
+ absl::FormatCivilTime(absl::CivilSecond(2015, 1, 2, 3, 4, 5)));
+ EXPECT_EQ("2015-01-02T03:04:00",
+ absl::FormatCivilTime(absl::CivilSecond(2015, 1, 2, 3, 4)));
+ EXPECT_EQ("2015-01-02T03:00:00",
+ absl::FormatCivilTime(absl::CivilSecond(2015, 1, 2, 3)));
+ EXPECT_EQ("2015-01-02T00:00:00",
+ absl::FormatCivilTime(absl::CivilSecond(2015, 1, 2)));
+ EXPECT_EQ("2015-01-01T00:00:00",
+ absl::FormatCivilTime(absl::CivilSecond(2015, 1)));
+ EXPECT_EQ("2015-01-01T00:00:00",
+ absl::FormatCivilTime(absl::CivilSecond(2015)));
+
+ EXPECT_EQ("2015-01-02T03:04",
+ absl::FormatCivilTime(absl::CivilMinute(2015, 1, 2, 3, 4, 5)));
+ EXPECT_EQ("2015-01-02T03:04",
+ absl::FormatCivilTime(absl::CivilMinute(2015, 1, 2, 3, 4)));
+ EXPECT_EQ("2015-01-02T03:00",
+ absl::FormatCivilTime(absl::CivilMinute(2015, 1, 2, 3)));
+ EXPECT_EQ("2015-01-02T00:00",
+ absl::FormatCivilTime(absl::CivilMinute(2015, 1, 2)));
+ EXPECT_EQ("2015-01-01T00:00",
+ absl::FormatCivilTime(absl::CivilMinute(2015, 1)));
+ EXPECT_EQ("2015-01-01T00:00",
+ absl::FormatCivilTime(absl::CivilMinute(2015)));
+
+ EXPECT_EQ("2015-01-02T03",
+ absl::FormatCivilTime(absl::CivilHour(2015, 1, 2, 3, 4, 5)));
+ EXPECT_EQ("2015-01-02T03",
+ absl::FormatCivilTime(absl::CivilHour(2015, 1, 2, 3, 4)));
+ EXPECT_EQ("2015-01-02T03",
+ absl::FormatCivilTime(absl::CivilHour(2015, 1, 2, 3)));
+ EXPECT_EQ("2015-01-02T00",
+ absl::FormatCivilTime(absl::CivilHour(2015, 1, 2)));
+ EXPECT_EQ("2015-01-01T00",
+ absl::FormatCivilTime(absl::CivilHour(2015, 1)));
+ EXPECT_EQ("2015-01-01T00",
+ absl::FormatCivilTime(absl::CivilHour(2015)));
+
+ EXPECT_EQ("2015-01-02",
+ absl::FormatCivilTime(absl::CivilDay(2015, 1, 2, 3, 4, 5)));
+ EXPECT_EQ("2015-01-02",
+ absl::FormatCivilTime(absl::CivilDay(2015, 1, 2, 3, 4)));
+ EXPECT_EQ("2015-01-02",
+ absl::FormatCivilTime(absl::CivilDay(2015, 1, 2, 3)));
+ EXPECT_EQ("2015-01-02",
+ absl::FormatCivilTime(absl::CivilDay(2015, 1, 2)));
+ EXPECT_EQ("2015-01-01",
+ absl::FormatCivilTime(absl::CivilDay(2015, 1)));
+ EXPECT_EQ("2015-01-01",
+ absl::FormatCivilTime(absl::CivilDay(2015)));
+
+ EXPECT_EQ("2015-01",
+ absl::FormatCivilTime(absl::CivilMonth(2015, 1, 2, 3, 4, 5)));
+ EXPECT_EQ("2015-01",
+ absl::FormatCivilTime(absl::CivilMonth(2015, 1, 2, 3, 4)));
+ EXPECT_EQ("2015-01",
+ absl::FormatCivilTime(absl::CivilMonth(2015, 1, 2, 3)));
+ EXPECT_EQ("2015-01",
+ absl::FormatCivilTime(absl::CivilMonth(2015, 1, 2)));
+ EXPECT_EQ("2015-01",
+ absl::FormatCivilTime(absl::CivilMonth(2015, 1)));
+ EXPECT_EQ("2015-01",
+ absl::FormatCivilTime(absl::CivilMonth(2015)));
+
+ EXPECT_EQ("2015",
+ absl::FormatCivilTime(absl::CivilYear(2015, 1, 2, 3, 4, 5)));
+ EXPECT_EQ("2015",
+ absl::FormatCivilTime(absl::CivilYear(2015, 1, 2, 3, 4)));
+ EXPECT_EQ("2015",
+ absl::FormatCivilTime(absl::CivilYear(2015, 1, 2, 3)));
+ EXPECT_EQ("2015",
+ absl::FormatCivilTime(absl::CivilYear(2015, 1, 2)));
+ EXPECT_EQ("2015",
+ absl::FormatCivilTime(absl::CivilYear(2015, 1)));
+ EXPECT_EQ("2015",
+ absl::FormatCivilTime(absl::CivilYear(2015)));
+}
+
+TEST(CivilTime, FieldsConstructionLimits) {
+ const int kIntMax = std::numeric_limits<int>::max();
+ EXPECT_EQ("2038-01-19T03:14:07",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, 1, 1, 0, 0, kIntMax)));
+ EXPECT_EQ("6121-02-11T05:21:07",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, 1, 1, 0, kIntMax, kIntMax)));
+ EXPECT_EQ("251104-11-20T12:21:07",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, 1, 1, kIntMax, kIntMax, kIntMax)));
+ EXPECT_EQ("6130715-05-30T12:21:07",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, 1, kIntMax, kIntMax, kIntMax, kIntMax)));
+ EXPECT_EQ("185087685-11-26T12:21:07",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, kIntMax, kIntMax, kIntMax, kIntMax, kIntMax)));
+
+ const int kIntMin = std::numeric_limits<int>::min();
+ EXPECT_EQ("1901-12-13T20:45:52",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, 1, 1, 0, 0, kIntMin)));
+ EXPECT_EQ("-2182-11-20T18:37:52",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, 1, 1, 0, kIntMin, kIntMin)));
+ EXPECT_EQ("-247165-02-11T10:37:52",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, 1, 1, kIntMin, kIntMin, kIntMin)));
+ EXPECT_EQ("-6126776-08-01T10:37:52",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, 1, kIntMin, kIntMin, kIntMin, kIntMin)));
+ EXPECT_EQ("-185083747-10-31T10:37:52",
+ absl::FormatCivilTime(absl::CivilSecond(
+ 1970, kIntMin, kIntMin, kIntMin, kIntMin, kIntMin)));
+}
+
+TEST(CivilTime, RangeLimits) {
+ const absl::civil_year_t kYearMax =
+ std::numeric_limits<absl::civil_year_t>::max();
+ EXPECT_EQ(absl::CivilYear(kYearMax),
+ absl::CivilYear::max());
+ EXPECT_EQ(absl::CivilMonth(kYearMax, 12),
+ absl::CivilMonth::max());
+ EXPECT_EQ(absl::CivilDay(kYearMax, 12, 31),
+ absl::CivilDay::max());
+ EXPECT_EQ(absl::CivilHour(kYearMax, 12, 31, 23),
+ absl::CivilHour::max());
+ EXPECT_EQ(absl::CivilMinute(kYearMax, 12, 31, 23, 59),
+ absl::CivilMinute::max());
+ EXPECT_EQ(absl::CivilSecond(kYearMax, 12, 31, 23, 59, 59),
+ absl::CivilSecond::max());
+
+ const absl::civil_year_t kYearMin =
+ std::numeric_limits<absl::civil_year_t>::min();
+ EXPECT_EQ(absl::CivilYear(kYearMin),
+ absl::CivilYear::min());
+ EXPECT_EQ(absl::CivilMonth(kYearMin, 1),
+ absl::CivilMonth::min());
+ EXPECT_EQ(absl::CivilDay(kYearMin, 1, 1),
+ absl::CivilDay::min());
+ EXPECT_EQ(absl::CivilHour(kYearMin, 1, 1, 0),
+ absl::CivilHour::min());
+ EXPECT_EQ(absl::CivilMinute(kYearMin, 1, 1, 0, 0),
+ absl::CivilMinute::min());
+ EXPECT_EQ(absl::CivilSecond(kYearMin, 1, 1, 0, 0, 0),
+ absl::CivilSecond::min());
+}
+
+TEST(CivilTime, ImplicitCrossAlignment) {
+ absl::CivilYear year(2015);
+ absl::CivilMonth month = year;
+ absl::CivilDay day = month;
+ absl::CivilHour hour = day;
+ absl::CivilMinute minute = hour;
+ absl::CivilSecond second = minute;
+
+ second = year;
+ EXPECT_EQ(second, year);
+ second = month;
+ EXPECT_EQ(second, month);
+ second = day;
+ EXPECT_EQ(second, day);
+ second = hour;
+ EXPECT_EQ(second, hour);
+ second = minute;
+ EXPECT_EQ(second, minute);
+
+ minute = year;
+ EXPECT_EQ(minute, year);
+ minute = month;
+ EXPECT_EQ(minute, month);
+ minute = day;
+ EXPECT_EQ(minute, day);
+ minute = hour;
+ EXPECT_EQ(minute, hour);
+
+ hour = year;
+ EXPECT_EQ(hour, year);
+ hour = month;
+ EXPECT_EQ(hour, month);
+ hour = day;
+ EXPECT_EQ(hour, day);
+
+ day = year;
+ EXPECT_EQ(day, year);
+ day = month;
+ EXPECT_EQ(day, month);
+
+ month = year;
+ EXPECT_EQ(month, year);
+
+ // Ensures unsafe conversions are not allowed.
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilSecond, absl::CivilMinute>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilSecond, absl::CivilHour>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilSecond, absl::CivilDay>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilSecond, absl::CivilMonth>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilSecond, absl::CivilYear>::value));
+
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilMinute, absl::CivilHour>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilMinute, absl::CivilDay>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilMinute, absl::CivilMonth>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilMinute, absl::CivilYear>::value));
+
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilHour, absl::CivilDay>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilHour, absl::CivilMonth>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilHour, absl::CivilYear>::value));
+
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilDay, absl::CivilMonth>::value));
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilDay, absl::CivilYear>::value));
+
+ EXPECT_FALSE(
+ (std::is_convertible<absl::CivilMonth, absl::CivilYear>::value));
+}
+
+TEST(CivilTime, ExplicitCrossAlignment) {
+ //
+ // Assign from smaller units -> larger units
+ //
+
+ absl::CivilSecond second(2015, 1, 2, 3, 4, 5);
+ EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(second));
+
+ absl::CivilMinute minute(second);
+ EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(minute));
+
+ absl::CivilHour hour(minute);
+ EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hour));
+
+ absl::CivilDay day(hour);
+ EXPECT_EQ("2015-01-02", absl::FormatCivilTime(day));
+
+ absl::CivilMonth month(day);
+ EXPECT_EQ("2015-01", absl::FormatCivilTime(month));
+
+ absl::CivilYear year(month);
+ EXPECT_EQ("2015", absl::FormatCivilTime(year));
+
+ //
+ // Now assign from larger units -> smaller units
+ //
+
+ month = absl::CivilMonth(year);
+ EXPECT_EQ("2015-01", absl::FormatCivilTime(month));
+
+ day = absl::CivilDay(month);
+ EXPECT_EQ("2015-01-01", absl::FormatCivilTime(day));
+
+ hour = absl::CivilHour(day);
+ EXPECT_EQ("2015-01-01T00", absl::FormatCivilTime(hour));
+
+ minute = absl::CivilMinute(hour);
+ EXPECT_EQ("2015-01-01T00:00", absl::FormatCivilTime(minute));
+
+ second = absl::CivilSecond(minute);
+ EXPECT_EQ("2015-01-01T00:00:00", absl::FormatCivilTime(second));
+}
+
+// Metafunction to test whether difference is allowed between two types.
+template <typename T1, typename T2>
+struct HasDiff {
+ template <typename U1, typename U2>
+ static std::false_type test(...);
+ template <typename U1, typename U2>
+ static std::true_type test(decltype(std::declval<U1>() - std::declval<U2>()));
+ static constexpr bool value = decltype(test<T1, T2>(0))::value;
+};
+
+TEST(CivilTime, DisallowCrossAlignedDifference) {
+ // Difference is allowed between types with the same alignment.
+ static_assert(HasDiff<absl::CivilSecond, absl::CivilSecond>::value, "");
+ static_assert(HasDiff<absl::CivilMinute, absl::CivilMinute>::value, "");
+ static_assert(HasDiff<absl::CivilHour, absl::CivilHour>::value, "");
+ static_assert(HasDiff<absl::CivilDay, absl::CivilDay>::value, "");
+ static_assert(HasDiff<absl::CivilMonth, absl::CivilMonth>::value, "");
+ static_assert(HasDiff<absl::CivilYear, absl::CivilYear>::value, "");
+
+ // Difference is disallowed between types with different alignments.
+ static_assert(!HasDiff<absl::CivilSecond, absl::CivilMinute>::value, "");
+ static_assert(!HasDiff<absl::CivilSecond, absl::CivilHour>::value, "");
+ static_assert(!HasDiff<absl::CivilSecond, absl::CivilDay>::value, "");
+ static_assert(!HasDiff<absl::CivilSecond, absl::CivilMonth>::value, "");
+ static_assert(!HasDiff<absl::CivilSecond, absl::CivilYear>::value, "");
+
+ static_assert(!HasDiff<absl::CivilMinute, absl::CivilHour>::value, "");
+ static_assert(!HasDiff<absl::CivilMinute, absl::CivilDay>::value, "");
+ static_assert(!HasDiff<absl::CivilMinute, absl::CivilMonth>::value, "");
+ static_assert(!HasDiff<absl::CivilMinute, absl::CivilYear>::value, "");
+
+ static_assert(!HasDiff<absl::CivilHour, absl::CivilDay>::value, "");
+ static_assert(!HasDiff<absl::CivilHour, absl::CivilMonth>::value, "");
+ static_assert(!HasDiff<absl::CivilHour, absl::CivilYear>::value, "");
+
+ static_assert(!HasDiff<absl::CivilDay, absl::CivilMonth>::value, "");
+ static_assert(!HasDiff<absl::CivilDay, absl::CivilYear>::value, "");
+
+ static_assert(!HasDiff<absl::CivilMonth, absl::CivilYear>::value, "");
+}
+
+TEST(CivilTime, ValueSemantics) {
+ const absl::CivilHour a(2015, 1, 2, 3);
+ const absl::CivilHour b = a;
+ const absl::CivilHour c(b);
+ absl::CivilHour d;
+ d = c;
+ EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(d));
+}
+
+TEST(CivilTime, Relational) {
+ // Tests that the alignment unit is ignored in comparison.
+ const absl::CivilYear year(2014);
+ const absl::CivilMonth month(year);
+ EXPECT_EQ(year, month);
+
+#define TEST_RELATIONAL(OLDER, YOUNGER) \
+ do { \
+ EXPECT_FALSE(OLDER < OLDER); \
+ EXPECT_FALSE(OLDER > OLDER); \
+ EXPECT_TRUE(OLDER >= OLDER); \
+ EXPECT_TRUE(OLDER <= OLDER); \
+ EXPECT_FALSE(YOUNGER < YOUNGER); \
+ EXPECT_FALSE(YOUNGER > YOUNGER); \
+ EXPECT_TRUE(YOUNGER >= YOUNGER); \
+ EXPECT_TRUE(YOUNGER <= YOUNGER); \
+ EXPECT_EQ(OLDER, OLDER); \
+ EXPECT_NE(OLDER, YOUNGER); \
+ EXPECT_LT(OLDER, YOUNGER); \
+ EXPECT_LE(OLDER, YOUNGER); \
+ EXPECT_GT(YOUNGER, OLDER); \
+ EXPECT_GE(YOUNGER, OLDER); \
+ } while (0)
+
+ // Alignment is ignored in comparison (verified above), so CivilSecond is
+ // used to test comparison in all field positions.
+ TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 0, 0, 0),
+ absl::CivilSecond(2015, 1, 1, 0, 0, 0));
+ TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 0, 0, 0),
+ absl::CivilSecond(2014, 2, 1, 0, 0, 0));
+ TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 0, 0, 0),
+ absl::CivilSecond(2014, 1, 2, 0, 0, 0));
+ TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 0, 0, 0),
+ absl::CivilSecond(2014, 1, 1, 1, 0, 0));
+ TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 1, 0, 0),
+ absl::CivilSecond(2014, 1, 1, 1, 1, 0));
+ TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 1, 1, 0),
+ absl::CivilSecond(2014, 1, 1, 1, 1, 1));
+
+ // Tests the relational operators of two different civil-time types.
+ TEST_RELATIONAL(absl::CivilDay(2014, 1, 1),
+ absl::CivilMinute(2014, 1, 1, 1, 1));
+ TEST_RELATIONAL(absl::CivilDay(2014, 1, 1),
+ absl::CivilMonth(2014, 2));
+
+#undef TEST_RELATIONAL
+}
+
+TEST(CivilTime, Arithmetic) {
+ absl::CivilSecond second(2015, 1, 2, 3, 4, 5);
+ EXPECT_EQ("2015-01-02T03:04:06", absl::FormatCivilTime(second += 1));
+ EXPECT_EQ("2015-01-02T03:04:07", absl::FormatCivilTime(second + 1));
+ EXPECT_EQ("2015-01-02T03:04:08", absl::FormatCivilTime(2 + second));
+ EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(second - 1));
+ EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(second -= 1));
+ EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(second++));
+ EXPECT_EQ("2015-01-02T03:04:07", absl::FormatCivilTime(++second));
+ EXPECT_EQ("2015-01-02T03:04:07", absl::FormatCivilTime(second--));
+ EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(--second));
+
+ absl::CivilMinute minute(2015, 1, 2, 3, 4);
+ EXPECT_EQ("2015-01-02T03:05", absl::FormatCivilTime(minute += 1));
+ EXPECT_EQ("2015-01-02T03:06", absl::FormatCivilTime(minute + 1));
+ EXPECT_EQ("2015-01-02T03:07", absl::FormatCivilTime(2 + minute));
+ EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(minute - 1));
+ EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(minute -= 1));
+ EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(minute++));
+ EXPECT_EQ("2015-01-02T03:06", absl::FormatCivilTime(++minute));
+ EXPECT_EQ("2015-01-02T03:06", absl::FormatCivilTime(minute--));
+ EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(--minute));
+
+ absl::CivilHour hour(2015, 1, 2, 3);
+ EXPECT_EQ("2015-01-02T04", absl::FormatCivilTime(hour += 1));
+ EXPECT_EQ("2015-01-02T05", absl::FormatCivilTime(hour + 1));
+ EXPECT_EQ("2015-01-02T06", absl::FormatCivilTime(2 + hour));
+ EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hour - 1));
+ EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hour -= 1));
+ EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hour++));
+ EXPECT_EQ("2015-01-02T05", absl::FormatCivilTime(++hour));
+ EXPECT_EQ("2015-01-02T05", absl::FormatCivilTime(hour--));
+ EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(--hour));
+
+ absl::CivilDay day(2015, 1, 2);
+ EXPECT_EQ("2015-01-03", absl::FormatCivilTime(day += 1));
+ EXPECT_EQ("2015-01-04", absl::FormatCivilTime(day + 1));
+ EXPECT_EQ("2015-01-05", absl::FormatCivilTime(2 + day));
+ EXPECT_EQ("2015-01-02", absl::FormatCivilTime(day - 1));
+ EXPECT_EQ("2015-01-02", absl::FormatCivilTime(day -= 1));
+ EXPECT_EQ("2015-01-02", absl::FormatCivilTime(day++));
+ EXPECT_EQ("2015-01-04", absl::FormatCivilTime(++day));
+ EXPECT_EQ("2015-01-04", absl::FormatCivilTime(day--));
+ EXPECT_EQ("2015-01-02", absl::FormatCivilTime(--day));
+
+ absl::CivilMonth month(2015, 1);
+ EXPECT_EQ("2015-02", absl::FormatCivilTime(month += 1));
+ EXPECT_EQ("2015-03", absl::FormatCivilTime(month + 1));
+ EXPECT_EQ("2015-04", absl::FormatCivilTime(2 + month));
+ EXPECT_EQ("2015-01", absl::FormatCivilTime(month - 1));
+ EXPECT_EQ("2015-01", absl::FormatCivilTime(month -= 1));
+ EXPECT_EQ("2015-01", absl::FormatCivilTime(month++));
+ EXPECT_EQ("2015-03", absl::FormatCivilTime(++month));
+ EXPECT_EQ("2015-03", absl::FormatCivilTime(month--));
+ EXPECT_EQ("2015-01", absl::FormatCivilTime(--month));
+
+ absl::CivilYear year(2015);
+ EXPECT_EQ("2016", absl::FormatCivilTime(year += 1));
+ EXPECT_EQ("2017", absl::FormatCivilTime(year + 1));
+ EXPECT_EQ("2018", absl::FormatCivilTime(2 + year));
+ EXPECT_EQ("2015", absl::FormatCivilTime(year - 1));
+ EXPECT_EQ("2015", absl::FormatCivilTime(year -= 1));
+ EXPECT_EQ("2015", absl::FormatCivilTime(year++));
+ EXPECT_EQ("2017", absl::FormatCivilTime(++year));
+ EXPECT_EQ("2017", absl::FormatCivilTime(year--));
+ EXPECT_EQ("2015", absl::FormatCivilTime(--year));
+}
+
+TEST(CivilTime, ArithmeticLimits) {
+ const int kIntMax = std::numeric_limits<int>::max();
+ const int kIntMin = std::numeric_limits<int>::min();
+
+ absl::CivilSecond second(1970, 1, 1, 0, 0, 0);
+ second += kIntMax;
+ EXPECT_EQ("2038-01-19T03:14:07", absl::FormatCivilTime(second));
+ second -= kIntMax;
+ EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(second));
+ second += kIntMin;
+ EXPECT_EQ("1901-12-13T20:45:52", absl::FormatCivilTime(second));
+ second -= kIntMin;
+ EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(second));
+
+ absl::CivilMinute minute(1970, 1, 1, 0, 0);
+ minute += kIntMax;
+ EXPECT_EQ("6053-01-23T02:07", absl::FormatCivilTime(minute));
+ minute -= kIntMax;
+ EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(minute));
+ minute += kIntMin;
+ EXPECT_EQ("-2114-12-08T21:52", absl::FormatCivilTime(minute));
+ minute -= kIntMin;
+ EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(minute));
+
+ absl::CivilHour hour(1970, 1, 1, 0);
+ hour += kIntMax;
+ EXPECT_EQ("246953-10-09T07", absl::FormatCivilTime(hour));
+ hour -= kIntMax;
+ EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hour));
+ hour += kIntMin;
+ EXPECT_EQ("-243014-03-24T16", absl::FormatCivilTime(hour));
+ hour -= kIntMin;
+ EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hour));
+
+ absl::CivilDay day(1970, 1, 1);
+ day += kIntMax;
+ EXPECT_EQ("5881580-07-11", absl::FormatCivilTime(day));
+ day -= kIntMax;
+ EXPECT_EQ("1970-01-01", absl::FormatCivilTime(day));
+ day += kIntMin;
+ EXPECT_EQ("-5877641-06-23", absl::FormatCivilTime(day));
+ day -= kIntMin;
+ EXPECT_EQ("1970-01-01", absl::FormatCivilTime(day));
+
+ absl::CivilMonth month(1970, 1);
+ month += kIntMax;
+ EXPECT_EQ("178958940-08", absl::FormatCivilTime(month));
+ month -= kIntMax;
+ EXPECT_EQ("1970-01", absl::FormatCivilTime(month));
+ month += kIntMin;
+ EXPECT_EQ("-178955001-05", absl::FormatCivilTime(month));
+ month -= kIntMin;
+ EXPECT_EQ("1970-01", absl::FormatCivilTime(month));
+
+ absl::CivilYear year(0);
+ year += kIntMax;
+ EXPECT_EQ("2147483647", absl::FormatCivilTime(year));
+ year -= kIntMax;
+ EXPECT_EQ("0", absl::FormatCivilTime(year));
+ year += kIntMin;
+ EXPECT_EQ("-2147483648", absl::FormatCivilTime(year));
+ year -= kIntMin;
+ EXPECT_EQ("0", absl::FormatCivilTime(year));
+}
+
+TEST(CivilTime, Difference) {
+ absl::CivilSecond second(2015, 1, 2, 3, 4, 5);
+ EXPECT_EQ(0, second - second);
+ EXPECT_EQ(10, (second + 10) - second);
+ EXPECT_EQ(-10, (second - 10) - second);
+
+ absl::CivilMinute minute(2015, 1, 2, 3, 4);
+ EXPECT_EQ(0, minute - minute);
+ EXPECT_EQ(10, (minute + 10) - minute);
+ EXPECT_EQ(-10, (minute - 10) - minute);
+
+ absl::CivilHour hour(2015, 1, 2, 3);
+ EXPECT_EQ(0, hour - hour);
+ EXPECT_EQ(10, (hour + 10) - hour);
+ EXPECT_EQ(-10, (hour - 10) - hour);
+
+ absl::CivilDay day(2015, 1, 2);
+ EXPECT_EQ(0, day - day);
+ EXPECT_EQ(10, (day + 10) - day);
+ EXPECT_EQ(-10, (day - 10) - day);
+
+ absl::CivilMonth month(2015, 1);
+ EXPECT_EQ(0, month - month);
+ EXPECT_EQ(10, (month + 10) - month);
+ EXPECT_EQ(-10, (month - 10) - month);
+
+ absl::CivilYear year(2015);
+ EXPECT_EQ(0, year - year);
+ EXPECT_EQ(10, (year + 10) - year);
+ EXPECT_EQ(-10, (year - 10) - year);
+}
+
+TEST(CivilTime, DifferenceLimits) {
+ const absl::civil_diff_t kDiffMax =
+ std::numeric_limits<absl::civil_diff_t>::max();
+ const absl::civil_diff_t kDiffMin =
+ std::numeric_limits<absl::civil_diff_t>::min();
+
+ // Check day arithmetic at the end of the year range.
+ const absl::CivilDay max_day(kDiffMax, 12, 31);
+ EXPECT_EQ(1, max_day - (max_day - 1));
+ EXPECT_EQ(-1, (max_day - 1) - max_day);
+
+ // Check day arithmetic at the start of the year range.
+ const absl::CivilDay min_day(kDiffMin, 1, 1);
+ EXPECT_EQ(1, (min_day + 1) - min_day);
+ EXPECT_EQ(-1, min_day - (min_day + 1));
+
+ // Check the limits of the return value.
+ const absl::CivilDay d1(1970, 1, 1);
+ const absl::CivilDay d2(25252734927768524, 7, 27);
+ EXPECT_EQ(kDiffMax, d2 - d1);
+ EXPECT_EQ(kDiffMin, d1 - (d2 + 1));
+}
+
+TEST(CivilTime, Properties) {
+ absl::CivilSecond ss(2015, 2, 3, 4, 5, 6);
+ EXPECT_EQ(2015, ss.year());
+ EXPECT_EQ(2, ss.month());
+ EXPECT_EQ(3, ss.day());
+ EXPECT_EQ(4, ss.hour());
+ EXPECT_EQ(5, ss.minute());
+ EXPECT_EQ(6, ss.second());
+
+ absl::CivilMinute mm(2015, 2, 3, 4, 5, 6);
+ EXPECT_EQ(2015, mm.year());
+ EXPECT_EQ(2, mm.month());
+ EXPECT_EQ(3, mm.day());
+ EXPECT_EQ(4, mm.hour());
+ EXPECT_EQ(5, mm.minute());
+ EXPECT_EQ(0, mm.second());
+
+ absl::CivilHour hh(2015, 2, 3, 4, 5, 6);
+ EXPECT_EQ(2015, hh.year());
+ EXPECT_EQ(2, hh.month());
+ EXPECT_EQ(3, hh.day());
+ EXPECT_EQ(4, hh.hour());
+ EXPECT_EQ(0, hh.minute());
+ EXPECT_EQ(0, hh.second());
+
+ absl::CivilDay d(2015, 2, 3, 4, 5, 6);
+ EXPECT_EQ(2015, d.year());
+ EXPECT_EQ(2, d.month());
+ EXPECT_EQ(3, d.day());
+ EXPECT_EQ(0, d.hour());
+ EXPECT_EQ(0, d.minute());
+ EXPECT_EQ(0, d.second());
+
+ absl::CivilMonth m(2015, 2, 3, 4, 5, 6);
+ EXPECT_EQ(2015, m.year());
+ EXPECT_EQ(2, m.month());
+ EXPECT_EQ(1, m.day());
+ EXPECT_EQ(0, m.hour());
+ EXPECT_EQ(0, m.minute());
+ EXPECT_EQ(0, m.second());
+
+ absl::CivilYear y(2015, 2, 3, 4, 5, 6);
+ EXPECT_EQ(2015, y.year());
+ EXPECT_EQ(1, y.month());
+ EXPECT_EQ(1, y.day());
+ EXPECT_EQ(0, y.hour());
+ EXPECT_EQ(0, y.minute());
+ EXPECT_EQ(0, y.second());
+}
+
+TEST(CivilTime, Format) {
+ absl::CivilSecond ss;
+ EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(ss));
+
+ absl::CivilMinute mm;
+ EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(mm));
+
+ absl::CivilHour hh;
+ EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hh));
+
+ absl::CivilDay d;
+ EXPECT_EQ("1970-01-01", absl::FormatCivilTime(d));
+
+ absl::CivilMonth m;
+ EXPECT_EQ("1970-01", absl::FormatCivilTime(m));
+
+ absl::CivilYear y;
+ EXPECT_EQ("1970", absl::FormatCivilTime(y));
+}
+
+TEST(CivilTime, FormatAndParseLenient) {
+ absl::CivilSecond ss;
+ EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(ss));
+
+ absl::CivilMinute mm;
+ EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(mm));
+
+ absl::CivilHour hh;
+ EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hh));
+
+ absl::CivilDay d;
+ EXPECT_EQ("1970-01-01", absl::FormatCivilTime(d));
+
+ absl::CivilMonth m;
+ EXPECT_EQ("1970-01", absl::FormatCivilTime(m));
+
+ absl::CivilYear y;
+ EXPECT_EQ("1970", absl::FormatCivilTime(y));
+}
+
+TEST(CivilTime, OutputStream) {
+ absl::CivilSecond cs(2016, 2, 3, 4, 5, 6);
+ {
+ std::stringstream ss;
+ ss << std::left << std::setfill('.');
+ ss << std::setw(3) << 'X';
+ ss << std::setw(21) << absl::CivilYear(cs);
+ ss << std::setw(3) << 'X';
+ EXPECT_EQ("X..2016.................X..", ss.str());
+ }
+ {
+ std::stringstream ss;
+ ss << std::left << std::setfill('.');
+ ss << std::setw(3) << 'X';
+ ss << std::setw(21) << absl::CivilMonth(cs);
+ ss << std::setw(3) << 'X';
+ EXPECT_EQ("X..2016-02..............X..", ss.str());
+ }
+ {
+ std::stringstream ss;
+ ss << std::left << std::setfill('.');
+ ss << std::setw(3) << 'X';
+ ss << std::setw(21) << absl::CivilDay(cs);
+ ss << std::setw(3) << 'X';
+ EXPECT_EQ("X..2016-02-03...........X..", ss.str());
+ }
+ {
+ std::stringstream ss;
+ ss << std::left << std::setfill('.');
+ ss << std::setw(3) << 'X';
+ ss << std::setw(21) << absl::CivilHour(cs);
+ ss << std::setw(3) << 'X';
+ EXPECT_EQ("X..2016-02-03T04........X..", ss.str());
+ }
+ {
+ std::stringstream ss;
+ ss << std::left << std::setfill('.');
+ ss << std::setw(3) << 'X';
+ ss << std::setw(21) << absl::CivilMinute(cs);
+ ss << std::setw(3) << 'X';
+ EXPECT_EQ("X..2016-02-03T04:05.....X..", ss.str());
+ }
+ {
+ std::stringstream ss;
+ ss << std::left << std::setfill('.');
+ ss << std::setw(3) << 'X';
+ ss << std::setw(21) << absl::CivilSecond(cs);
+ ss << std::setw(3) << 'X';
+ EXPECT_EQ("X..2016-02-03T04:05:06..X..", ss.str());
+ }
+ {
+ std::stringstream ss;
+ ss << std::left << std::setfill('.');
+ ss << std::setw(3) << 'X';
+ ss << std::setw(21) << absl::Weekday::wednesday;
+ ss << std::setw(3) << 'X';
+ EXPECT_EQ("X..Wednesday............X..", ss.str());
+ }
+}
+
+TEST(CivilTime, Weekday) {
+ absl::CivilDay d(1970, 1, 1);
+ EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(d)) << d;
+
+ // We used to get this wrong for years < -30.
+ d = absl::CivilDay(-31, 12, 24);
+ EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(d)) << d;
+}
+
+TEST(CivilTime, NextPrevWeekday) {
+ // Jan 1, 1970 was a Thursday.
+ const absl::CivilDay thursday(1970, 1, 1);
+
+ // Thursday -> Thursday
+ absl::CivilDay d = absl::NextWeekday(thursday, absl::Weekday::thursday);
+ EXPECT_EQ(7, d - thursday) << d;
+ EXPECT_EQ(d - 14, absl::PrevWeekday(thursday, absl::Weekday::thursday));
+
+ // Thursday -> Friday
+ d = absl::NextWeekday(thursday, absl::Weekday::friday);
+ EXPECT_EQ(1, d - thursday) << d;
+ EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::friday));
+
+ // Thursday -> Saturday
+ d = absl::NextWeekday(thursday, absl::Weekday::saturday);
+ EXPECT_EQ(2, d - thursday) << d;
+ EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::saturday));
+
+ // Thursday -> Sunday
+ d = absl::NextWeekday(thursday, absl::Weekday::sunday);
+ EXPECT_EQ(3, d - thursday) << d;
+ EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::sunday));
+
+ // Thursday -> Monday
+ d = absl::NextWeekday(thursday, absl::Weekday::monday);
+ EXPECT_EQ(4, d - thursday) << d;
+ EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::monday));
+
+ // Thursday -> Tuesday
+ d = absl::NextWeekday(thursday, absl::Weekday::tuesday);
+ EXPECT_EQ(5, d - thursday) << d;
+ EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::tuesday));
+
+ // Thursday -> Wednesday
+ d = absl::NextWeekday(thursday, absl::Weekday::wednesday);
+ EXPECT_EQ(6, d - thursday) << d;
+ EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::wednesday));
+}
+
+// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
+TEST(CivilTime, DifferenceWithHugeYear) {
+ absl::CivilDay d1(9223372036854775807, 1, 1);
+ absl::CivilDay d2(9223372036854775807, 12, 31);
+ EXPECT_EQ(364, d2 - d1);
+
+ d1 = absl::CivilDay(-9223372036854775807 - 1, 1, 1);
+ d2 = absl::CivilDay(-9223372036854775807 - 1, 12, 31);
+ EXPECT_EQ(365, d2 - d1);
+
+ // Check the limits of the return value at the end of the year range.
+ d1 = absl::CivilDay(9223372036854775807, 1, 1);
+ d2 = absl::CivilDay(9198119301927009252, 6, 6);
+ EXPECT_EQ(9223372036854775807, d1 - d2);
+ d2 = d2 - 1;
+ EXPECT_EQ(-9223372036854775807 - 1, d2 - d1);
+
+ // Check the limits of the return value at the start of the year range.
+ d1 = absl::CivilDay(-9223372036854775807 - 1, 1, 1);
+ d2 = absl::CivilDay(-9198119301927009254, 7, 28);
+ EXPECT_EQ(9223372036854775807, d2 - d1);
+ d2 = d2 + 1;
+ EXPECT_EQ(-9223372036854775807 - 1, d1 - d2);
+
+ // Check the limits of the return value from either side of year 0.
+ d1 = absl::CivilDay(-12626367463883278, 9, 3);
+ d2 = absl::CivilDay(12626367463883277, 3, 28);
+ EXPECT_EQ(9223372036854775807, d2 - d1);
+ d2 = d2 + 1;
+ EXPECT_EQ(-9223372036854775807 - 1, d1 - d2);
+}
+
+// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
+TEST(CivilTime, DifferenceNoIntermediateOverflow) {
+ // The difference up to the minute field would be below the minimum
+ // int64_t, but the 52 extra seconds brings us back to the minimum.
+ absl::CivilSecond s1(-292277022657, 1, 27, 8, 29 - 1, 52);
+ absl::CivilSecond s2(1970, 1, 1, 0, 0 - 1, 0);
+ EXPECT_EQ(-9223372036854775807 - 1, s1 - s2);
+
+ // The difference up to the minute field would be above the maximum
+ // int64_t, but the -53 extra seconds brings us back to the maximum.
+ s1 = absl::CivilSecond(292277026596, 12, 4, 15, 30, 7 - 7);
+ s2 = absl::CivilSecond(1970, 1, 1, 0, 0, 0 - 7);
+ EXPECT_EQ(9223372036854775807, s1 - s2);
+}
+
+TEST(CivilTime, NormalizeSimpleOverflow) {
+ absl::CivilSecond cs;
+ cs = absl::CivilSecond(2013, 11, 15, 16, 32, 59 + 1);
+ EXPECT_EQ("2013-11-15T16:33:00", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 16, 59 + 1, 14);
+ EXPECT_EQ("2013-11-15T17:00:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 23 + 1, 32, 14);
+ EXPECT_EQ("2013-11-16T00:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 30 + 1, 16, 32, 14);
+ EXPECT_EQ("2013-12-01T16:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 12 + 1, 15, 16, 32, 14);
+ EXPECT_EQ("2014-01-15T16:32:14", absl::FormatCivilTime(cs));
+}
+
+TEST(CivilTime, NormalizeSimpleUnderflow) {
+ absl::CivilSecond cs;
+ cs = absl::CivilSecond(2013, 11, 15, 16, 32, 0 - 1);
+ EXPECT_EQ("2013-11-15T16:31:59", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 16, 0 - 1, 14);
+ EXPECT_EQ("2013-11-15T15:59:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 0 - 1, 32, 14);
+ EXPECT_EQ("2013-11-14T23:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 1 - 1, 16, 32, 14);
+ EXPECT_EQ("2013-10-31T16:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 1 - 1, 15, 16, 32, 14);
+ EXPECT_EQ("2012-12-15T16:32:14", absl::FormatCivilTime(cs));
+}
+
+TEST(CivilTime, NormalizeMultipleOverflow) {
+ absl::CivilSecond cs(2013, 12, 31, 23, 59, 59 + 1);
+ EXPECT_EQ("2014-01-01T00:00:00", absl::FormatCivilTime(cs));
+}
+
+TEST(CivilTime, NormalizeMultipleUnderflow) {
+ absl::CivilSecond cs(2014, 1, 1, 0, 0, 0 - 1);
+ EXPECT_EQ("2013-12-31T23:59:59", absl::FormatCivilTime(cs));
+}
+
+TEST(CivilTime, NormalizeOverflowLimits) {
+ absl::CivilSecond cs;
+
+ const int kintmax = std::numeric_limits<int>::max();
+ cs = absl::CivilSecond(0, kintmax, kintmax, kintmax, kintmax, kintmax);
+ EXPECT_EQ("185085715-11-27T12:21:07", absl::FormatCivilTime(cs));
+
+ const int kintmin = std::numeric_limits<int>::min();
+ cs = absl::CivilSecond(0, kintmin, kintmin, kintmin, kintmin, kintmin);
+ EXPECT_EQ("-185085717-10-31T10:37:52", absl::FormatCivilTime(cs));
+}
+
+TEST(CivilTime, NormalizeComplexOverflow) {
+ absl::CivilSecond cs;
+ cs = absl::CivilSecond(2013, 11, 15, 16, 32, 14 + 123456789);
+ EXPECT_EQ("2017-10-14T14:05:23", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 16, 32 + 1234567, 14);
+ EXPECT_EQ("2016-03-22T00:39:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 16 + 123456, 32, 14);
+ EXPECT_EQ("2027-12-16T16:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15 + 1234, 16, 32, 14);
+ EXPECT_EQ("2017-04-02T16:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11 + 123, 15, 16, 32, 14);
+ EXPECT_EQ("2024-02-15T16:32:14", absl::FormatCivilTime(cs));
+}
+
+TEST(CivilTime, NormalizeComplexUnderflow) {
+ absl::CivilSecond cs;
+ cs = absl::CivilSecond(1999, 3, 0, 0, 0, 0); // year 400
+ EXPECT_EQ("1999-02-28T00:00:00", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 16, 32, 14 - 123456789);
+ EXPECT_EQ("2009-12-17T18:59:05", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 16, 32 - 1234567, 14);
+ EXPECT_EQ("2011-07-12T08:25:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15, 16 - 123456, 32, 14);
+ EXPECT_EQ("1999-10-16T16:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11, 15 - 1234, 16, 32, 14);
+ EXPECT_EQ("2010-06-30T16:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11 - 123, 15, 16, 32, 14);
+ EXPECT_EQ("2003-08-15T16:32:14", absl::FormatCivilTime(cs));
+}
+
+TEST(CivilTime, NormalizeMishmash) {
+ absl::CivilSecond cs;
+ cs = absl::CivilSecond(2013, 11 - 123, 15 + 1234, 16 - 123456, 32 + 1234567,
+ 14 - 123456789);
+ EXPECT_EQ("1991-05-09T03:06:05", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11 + 123, 15 - 1234, 16 + 123456, 32 - 1234567,
+ 14 + 123456789);
+ EXPECT_EQ("2036-05-24T05:58:23", absl::FormatCivilTime(cs));
+
+ cs = absl::CivilSecond(2013, 11, -146097 + 1, 16, 32, 14);
+ EXPECT_EQ("1613-11-01T16:32:14", absl::FormatCivilTime(cs));
+ cs = absl::CivilSecond(2013, 11 + 400 * 12, -146097 + 1, 16, 32, 14);
+ EXPECT_EQ("2013-11-01T16:32:14", absl::FormatCivilTime(cs));
+}
+
+// Convert all the days from 1970-1-1 to 1970-1-146097 (aka 2369-12-31)
+// and check that they normalize to the expected time. 146097 days span
+// the 400-year Gregorian cycle used during normalization.
+TEST(CivilTime, NormalizeAllTheDays) {
+ absl::CivilDay expected(1970, 1, 1);
+ for (int day = 1; day <= 146097; ++day) {
+ absl::CivilSecond cs(1970, 1, day, 0, 0, 0);
+ EXPECT_EQ(expected, cs);
+ ++expected;
+ }
+}
+
+TEST(CivilTime, NormalizeWithHugeYear) {
+ absl::CivilMonth c(9223372036854775807, 1);
+ EXPECT_EQ("9223372036854775807-01", absl::FormatCivilTime(c));
+ c = c - 1; // Causes normalization
+ EXPECT_EQ("9223372036854775806-12", absl::FormatCivilTime(c));
+
+ c = absl::CivilMonth(-9223372036854775807 - 1, 1);
+ EXPECT_EQ("-9223372036854775808-01", absl::FormatCivilTime(c));
+ c = c + 12; // Causes normalization
+ EXPECT_EQ("-9223372036854775807-01", absl::FormatCivilTime(c));
+}
+
+TEST(CivilTime, LeapYears) {
+ const absl::CivilSecond s1(2013, 2, 28 + 1, 0, 0, 0);
+ EXPECT_EQ("2013-03-01T00:00:00", absl::FormatCivilTime(s1));
+
+ const absl::CivilSecond s2(2012, 2, 28 + 1, 0, 0, 0);
+ EXPECT_EQ("2012-02-29T00:00:00", absl::FormatCivilTime(s2));
+
+ const absl::CivilSecond s3(1900, 2, 28 + 1, 0, 0, 0);
+ EXPECT_EQ("1900-03-01T00:00:00", absl::FormatCivilTime(s3));
+
+ const struct {
+ int year;
+ int days;
+ struct {
+ int month;
+ int day;
+ } leap_day; // The date of the day after Feb 28.
+ } kLeapYearTable[]{
+ {1900, 365, {3, 1}},
+ {1999, 365, {3, 1}},
+ {2000, 366, {2, 29}}, // leap year
+ {2001, 365, {3, 1}},
+ {2002, 365, {3, 1}},
+ {2003, 365, {3, 1}},
+ {2004, 366, {2, 29}}, // leap year
+ {2005, 365, {3, 1}},
+ {2006, 365, {3, 1}},
+ {2007, 365, {3, 1}},
+ {2008, 366, {2, 29}}, // leap year
+ {2009, 365, {3, 1}},
+ {2100, 365, {3, 1}},
+ };
+
+ for (int i = 0; i < ABSL_ARRAYSIZE(kLeapYearTable); ++i) {
+ const int y = kLeapYearTable[i].year;
+ const int m = kLeapYearTable[i].leap_day.month;
+ const int d = kLeapYearTable[i].leap_day.day;
+ const int n = kLeapYearTable[i].days;
+
+ // Tests incrementing through the leap day.
+ const absl::CivilDay feb28(y, 2, 28);
+ const absl::CivilDay next_day = feb28 + 1;
+ EXPECT_EQ(m, next_day.month());
+ EXPECT_EQ(d, next_day.day());
+
+ // Tests difference in days of leap years.
+ const absl::CivilYear year(feb28);
+ const absl::CivilYear next_year = year + 1;
+ EXPECT_EQ(n, absl::CivilDay(next_year) - absl::CivilDay(year));
+ }
+}
+
+TEST(CivilTime, FirstThursdayInMonth) {
+ const absl::CivilDay nov1(2014, 11, 1);
+ const absl::CivilDay thursday =
+ absl::PrevWeekday(nov1, absl::Weekday::thursday) + 7;
+ EXPECT_EQ("2014-11-06", absl::FormatCivilTime(thursday));
+
+ // Bonus: Date of Thanksgiving in the United States
+ // Rule: Fourth Thursday of November
+ const absl::CivilDay thanksgiving = thursday + 7 * 3;
+ EXPECT_EQ("2014-11-27", absl::FormatCivilTime(thanksgiving));
+}
+
+TEST(CivilTime, DocumentationExample) {
+ absl::CivilSecond second(2015, 6, 28, 1, 2, 3); // 2015-06-28 01:02:03
+ absl::CivilMinute minute(second); // 2015-06-28 01:02:00
+ absl::CivilDay day(minute); // 2015-06-28 00:00:00
+
+ second -= 1; // 2015-06-28 01:02:02
+ --second; // 2015-06-28 01:02:01
+ EXPECT_EQ(minute, second - 1); // Comparison between types
+ EXPECT_LT(minute, second);
+
+ // int diff = second - minute; // ERROR: Mixed types, won't compile
+
+ absl::CivilDay june_1(2015, 6, 1); // Pass fields to c'tor.
+ int diff = day - june_1; // Num days between 'day' and June 1
+ EXPECT_EQ(27, diff);
+
+ // Fields smaller than alignment are floored to their minimum value.
+ absl::CivilDay day_floor(2015, 1, 2, 9, 9, 9);
+ EXPECT_EQ(0, day_floor.hour()); // 09:09:09 is floored
+ EXPECT_EQ(absl::CivilDay(2015, 1, 2), day_floor);
+
+ // Unspecified fields default to their minium value
+ absl::CivilDay day_default(2015); // Defaults to Jan 1
+ EXPECT_EQ(absl::CivilDay(2015, 1, 1), day_default);
+
+ // Iterates all the days of June.
+ absl::CivilMonth june(day); // CivilDay -> CivilMonth
+ absl::CivilMonth july = june + 1;
+ for (absl::CivilDay day = june_1; day < july; ++day) {
+ // ...
+ }
+}
+
+} // namespace
diff --git a/absl/time/clock.cc b/absl/time/clock.cc
index 3b1e8739..2915d78b 100644
--- a/absl/time/clock.cc
+++ b/absl/time/clock.cc
@@ -34,7 +34,7 @@
#include "absl/base/thread_annotations.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
Time Now() {
// TODO(bww): Get a timespec instead so we don't have to divide.
int64_t n = absl::GetCurrentTimeNanos();
@@ -44,7 +44,7 @@ Time Now() {
}
return time_internal::FromUnixDuration(absl::Nanoseconds(n));
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// Decide if we should use the fast GetCurrentTimeNanos() algorithm
@@ -59,10 +59,8 @@ Time Now() {
#endif
#endif
-#if defined(__APPLE__)
-#include "absl/time/internal/get_current_time_ios.inc"
-#elif defined(_WIN32)
-#include "absl/time/internal/get_current_time_windows.inc"
+#if defined(__APPLE__) || defined(_WIN32)
+#include "absl/time/internal/get_current_time_chrono.inc"
#else
#include "absl/time/internal/get_current_time_posix.inc"
#endif
@@ -75,11 +73,11 @@ Time Now() {
#if !ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
int64_t GetCurrentTimeNanos() {
return GET_CURRENT_TIME_NANOS_FROM_SYSTEM();
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else // Use the cyclecounter-based implementation below.
@@ -97,7 +95,7 @@ static int64_t stats_slow_paths;
static int64_t stats_fast_slow_paths;
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
// This is a friend wrapper around UnscaledCycleClock::Now()
// (needed to access UnscaledCycleClock).
@@ -522,12 +520,12 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns,
return estimated_base_ns;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
// Returns the maximum duration that SleepOnce() can sleep for.
@@ -555,7 +553,7 @@ void SleepOnce(absl::Duration to_sleep) {
}
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
extern "C" {
diff --git a/absl/time/clock.h b/absl/time/clock.h
index 15f587c5..b2941126 100644
--- a/absl/time/clock.h
+++ b/absl/time/clock.h
@@ -26,7 +26,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// Now()
//
@@ -50,7 +50,7 @@ int64_t GetCurrentTimeNanos();
// * Returns immediately when passed a nonpositive duration.
void SleepFor(absl::Duration duration);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// -----------------------------------------------------------------------------
diff --git a/absl/time/clock_test.cc b/absl/time/clock_test.cc
index f143c036..707166d0 100644
--- a/absl/time/clock_test.cc
+++ b/absl/time/clock_test.cc
@@ -35,36 +35,84 @@ TEST(Time, Now) {
EXPECT_GE(after, now);
}
-TEST(SleepForTest, BasicSanity) {
- absl::Duration sleep_time = absl::Milliseconds(2500);
- absl::Time start = absl::Now();
- absl::SleepFor(sleep_time);
- absl::Time end = absl::Now();
- EXPECT_LE(sleep_time - absl::Milliseconds(100), end - start);
- EXPECT_GE(sleep_time + absl::Milliseconds(200), end - start);
-}
+enum class AlarmPolicy { kWithoutAlarm, kWithAlarm };
-#ifdef ABSL_HAVE_ALARM
-// Helper for test SleepFor.
+#if defined(ABSL_HAVE_ALARM)
bool alarm_handler_invoked = false;
+
void AlarmHandler(int signo) {
ASSERT_EQ(signo, SIGALRM);
alarm_handler_invoked = true;
}
+#endif
+
+// Does SleepFor(d) take between lower_bound and upper_bound at least
+// once between now and (now + timeout)? If requested (and supported),
+// add an alarm for the middle of the sleep period and expect it to fire.
+bool SleepForBounded(absl::Duration d, absl::Duration lower_bound,
+ absl::Duration upper_bound, absl::Duration timeout,
+ AlarmPolicy alarm_policy, int* attempts) {
+ const absl::Time deadline = absl::Now() + timeout;
+ while (absl::Now() < deadline) {
+#if defined(ABSL_HAVE_ALARM)
+ sig_t old_alarm = SIG_DFL;
+ if (alarm_policy == AlarmPolicy::kWithAlarm) {
+ alarm_handler_invoked = false;
+ old_alarm = signal(SIGALRM, AlarmHandler);
+ alarm(absl::ToInt64Seconds(d / 2));
+ }
+#else
+ EXPECT_EQ(alarm_policy, AlarmPolicy::kWithoutAlarm);
+#endif
+ ++*attempts;
+ absl::Time start = absl::Now();
+ absl::SleepFor(d);
+ absl::Duration actual = absl::Now() - start;
+#if defined(ABSL_HAVE_ALARM)
+ if (alarm_policy == AlarmPolicy::kWithAlarm) {
+ signal(SIGALRM, old_alarm);
+ if (!alarm_handler_invoked) continue;
+ }
+#endif
+ if (lower_bound <= actual && actual <= upper_bound) {
+ return true; // yes, the SleepFor() was correctly bounded
+ }
+ }
+ return false;
+}
-TEST(SleepForTest, AlarmSupport) {
- alarm_handler_invoked = false;
- sig_t old_alarm = signal(SIGALRM, AlarmHandler);
- alarm(2);
- absl::Duration sleep_time = absl::Milliseconds(3500);
- absl::Time start = absl::Now();
- absl::SleepFor(sleep_time);
- absl::Time end = absl::Now();
- EXPECT_TRUE(alarm_handler_invoked);
- EXPECT_LE(sleep_time - absl::Milliseconds(100), end - start);
- EXPECT_GE(sleep_time + absl::Milliseconds(200), end - start);
- signal(SIGALRM, old_alarm);
+testing::AssertionResult AssertSleepForBounded(absl::Duration d,
+ absl::Duration early,
+ absl::Duration late,
+ absl::Duration timeout,
+ AlarmPolicy alarm_policy) {
+ const absl::Duration lower_bound = d - early;
+ const absl::Duration upper_bound = d + late;
+ int attempts = 0;
+ if (SleepForBounded(d, lower_bound, upper_bound, timeout, alarm_policy,
+ &attempts)) {
+ return testing::AssertionSuccess();
+ }
+ return testing::AssertionFailure()
+ << "SleepFor(" << d << ") did not return within [" << lower_bound
+ << ":" << upper_bound << "] in " << attempts << " attempt"
+ << (attempts == 1 ? "" : "s") << " over " << timeout
+ << (alarm_policy == AlarmPolicy::kWithAlarm ? " with" : " without")
+ << " an alarm";
+}
+
+// Tests that SleepFor() returns neither too early nor too late.
+TEST(SleepFor, Bounded) {
+ const absl::Duration d = absl::Milliseconds(2500);
+ const absl::Duration early = absl::Milliseconds(100);
+ const absl::Duration late = absl::Milliseconds(300);
+ const absl::Duration timeout = 48 * d;
+ EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
+ AlarmPolicy::kWithoutAlarm));
+#if defined(ABSL_HAVE_ALARM)
+ EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
+ AlarmPolicy::kWithAlarm));
+#endif
}
-#endif // ABSL_HAVE_ALARM
} // namespace
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index 2271f7da..04669709 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -67,7 +67,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
@@ -79,10 +79,16 @@ constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
// Can't use std::isinfinite() because it doesn't exist on windows.
inline bool IsFinite(double d) {
+ if (std::isnan(d)) return false;
return d != std::numeric_limits<double>::infinity() &&
d != -std::numeric_limits<double>::infinity();
}
+inline bool IsValidDivisor(double d) {
+ if (std::isnan(d)) return false;
+ return d != 0.0;
+}
+
// Can't use std::round() because it is only available in C++11.
// Note that we ignore the possibility of floating-point over/underflow.
template <typename Double>
@@ -456,7 +462,7 @@ Duration& Duration::operator/=(int64_t r) {
}
Duration& Duration::operator/=(double r) {
- if (time_internal::IsInfiniteDuration(*this) || r == 0.0) {
+ if (time_internal::IsInfiniteDuration(*this) || !IsValidDivisor(r)) {
const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0);
return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
}
@@ -667,7 +673,7 @@ std::chrono::hours ToChronoHours(Duration d) {
}
//
-// To/From std::string formatting.
+// To/From string formatting.
//
namespace {
@@ -745,7 +751,7 @@ void AppendNumberUnit(std::string* out, double n, DisplayUnit unit) {
} // namespace
// From Go's doc at http://golang.org/pkg/time/#Duration.String
-// [FormatDuration] returns a std::string representing the duration in the
+// [FormatDuration] returns a string representing the duration in the
// form "72h3m0.5s". Leading zero units are omitted. As a special
// case, durations less than one second format use a smaller unit
// (milli-, micro-, or nanoseconds) to ensure that the leading digit
@@ -788,8 +794,8 @@ std::string FormatDuration(Duration d) {
namespace {
// A helper for ParseDuration() that parses a leading number from the given
-// std::string and stores the result in *int_part/*frac_part/*frac_scale. The
-// given std::string pointer is modified to point to the first unconsumed char.
+// string and stores the result in *int_part/*frac_part/*frac_scale. The
+// given string pointer is modified to point to the first unconsumed char.
bool ConsumeDurationNumber(const char** dpp, int64_t* int_part,
int64_t* frac_part, int64_t* frac_scale) {
*int_part = 0;
@@ -817,8 +823,8 @@ bool ConsumeDurationNumber(const char** dpp, int64_t* int_part,
}
// A helper for ParseDuration() that parses a leading unit designator (e.g.,
-// ns, us, ms, s, m, h) from the given std::string and stores the resulting unit
-// in "*unit". The given std::string pointer is modified to point to the first
+// ns, us, ms, s, m, h) from the given string and stores the resulting unit
+// in "*unit". The given string pointer is modified to point to the first
// unconsumed char.
bool ConsumeDurationUnit(const char** start, Duration* unit) {
const char *s = *start;
@@ -851,7 +857,7 @@ bool ConsumeDurationUnit(const char** start, Duration* unit) {
} // namespace
// From Go's doc at http://golang.org/pkg/time/#ParseDuration
-// [ParseDuration] parses a duration std::string. A duration std::string is
+// [ParseDuration] parses a duration string. A duration string is
// a possibly signed sequence of decimal numbers, each with optional
// fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
// Valid time units are "ns", "us" "ms", "s", "m", "h".
@@ -896,14 +902,11 @@ bool ParseDuration(const std::string& dur_string, Duration* d) {
*d = dur;
return true;
}
-
bool ParseFlag(const std::string& text, Duration* dst, std::string* ) {
return ParseDuration(text, dst);
}
-std::string UnparseFlag(Duration d) {
- return FormatDuration(d);
-}
+std::string UnparseFlag(Duration d) { return FormatDuration(d); }
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/duration_benchmark.cc b/absl/time/duration_benchmark.cc
index 54f89a1f..d5657bd5 100644
--- a/absl/time/duration_benchmark.cc
+++ b/absl/time/duration_benchmark.cc
@@ -27,47 +27,113 @@ namespace {
//
void BM_Duration_Factory_Nanoseconds(benchmark::State& state) {
+ int64_t i = 0;
while (state.KeepRunning()) {
- benchmark::DoNotOptimize(absl::Nanoseconds(1));
+ benchmark::DoNotOptimize(absl::Nanoseconds(i));
+ i += 314159;
}
}
BENCHMARK(BM_Duration_Factory_Nanoseconds);
void BM_Duration_Factory_Microseconds(benchmark::State& state) {
+ int64_t i = 0;
while (state.KeepRunning()) {
- benchmark::DoNotOptimize(absl::Microseconds(1));
+ benchmark::DoNotOptimize(absl::Microseconds(i));
+ i += 314;
}
}
BENCHMARK(BM_Duration_Factory_Microseconds);
void BM_Duration_Factory_Milliseconds(benchmark::State& state) {
+ int64_t i = 0;
while (state.KeepRunning()) {
- benchmark::DoNotOptimize(absl::Milliseconds(1));
+ benchmark::DoNotOptimize(absl::Milliseconds(i));
+ i += 1;
}
}
BENCHMARK(BM_Duration_Factory_Milliseconds);
void BM_Duration_Factory_Seconds(benchmark::State& state) {
+ int64_t i = 0;
while (state.KeepRunning()) {
- benchmark::DoNotOptimize(absl::Seconds(1));
+ benchmark::DoNotOptimize(absl::Seconds(i));
+ i += 1;
}
}
BENCHMARK(BM_Duration_Factory_Seconds);
void BM_Duration_Factory_Minutes(benchmark::State& state) {
+ int64_t i = 0;
while (state.KeepRunning()) {
- benchmark::DoNotOptimize(absl::Minutes(1));
+ benchmark::DoNotOptimize(absl::Minutes(i));
+ i += 1;
}
}
BENCHMARK(BM_Duration_Factory_Minutes);
void BM_Duration_Factory_Hours(benchmark::State& state) {
+ int64_t i = 0;
while (state.KeepRunning()) {
- benchmark::DoNotOptimize(absl::Hours(1));
+ benchmark::DoNotOptimize(absl::Hours(i));
+ i += 1;
}
}
BENCHMARK(BM_Duration_Factory_Hours);
+void BM_Duration_Factory_DoubleNanoseconds(benchmark::State& state) {
+ double d = 1;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(absl::Nanoseconds(d));
+ d = d * 1.00000001 + 1;
+ }
+}
+BENCHMARK(BM_Duration_Factory_DoubleNanoseconds);
+
+void BM_Duration_Factory_DoubleMicroseconds(benchmark::State& state) {
+ double d = 1e-3;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(absl::Microseconds(d));
+ d = d * 1.00000001 + 1e-3;
+ }
+}
+BENCHMARK(BM_Duration_Factory_DoubleMicroseconds);
+
+void BM_Duration_Factory_DoubleMilliseconds(benchmark::State& state) {
+ double d = 1e-6;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(absl::Milliseconds(d));
+ d = d * 1.00000001 + 1e-6;
+ }
+}
+BENCHMARK(BM_Duration_Factory_DoubleMilliseconds);
+
+void BM_Duration_Factory_DoubleSeconds(benchmark::State& state) {
+ double d = 1e-9;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(absl::Seconds(d));
+ d = d * 1.00000001 + 1e-9;
+ }
+}
+BENCHMARK(BM_Duration_Factory_DoubleSeconds);
+
+void BM_Duration_Factory_DoubleMinutes(benchmark::State& state) {
+ double d = 1e-9;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(absl::Minutes(d));
+ d = d * 1.00000001 + 1e-9;
+ }
+}
+BENCHMARK(BM_Duration_Factory_DoubleMinutes);
+
+void BM_Duration_Factory_DoubleHours(benchmark::State& state) {
+ double d = 1e-9;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(absl::Hours(d));
+ d = d * 1.00000001 + 1e-9;
+ }
+}
+BENCHMARK(BM_Duration_Factory_DoubleHours);
+
//
// Arithmetic
//
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index 704684ed..61f3c5c0 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -16,7 +16,9 @@
#include <cmath>
#include <cstdint>
#include <ctime>
+#include <iomanip>
#include <limits>
+#include <random>
#include <string>
#include "gmock/gmock.h"
@@ -54,6 +56,17 @@ MATCHER_P(TimevalMatcher, tv, "") {
return false;
}
+TEST(Duration, ConstExpr) {
+ constexpr absl::Duration d0 = absl::ZeroDuration();
+ static_assert(d0 == absl::ZeroDuration(), "ZeroDuration()");
+ constexpr absl::Duration d1 = absl::Seconds(1);
+ static_assert(d1 == absl::Seconds(1), "Seconds(1)");
+ static_assert(d1 != absl::ZeroDuration(), "Seconds(1)");
+ constexpr absl::Duration d2 = absl::InfiniteDuration();
+ static_assert(d2 == absl::InfiniteDuration(), "InfiniteDuration()");
+ static_assert(d2 != absl::ZeroDuration(), "InfiniteDuration()");
+}
+
TEST(Duration, ValueSemantics) {
// If this compiles, the test passes.
constexpr absl::Duration a; // Default construction
@@ -105,22 +118,22 @@ TEST(Duration, Factories) {
}
TEST(Duration, ToConversion) {
-#define TEST_DURATION_CONVERSION(UNIT) \
- do { \
- const absl::Duration d = absl::UNIT(1.5); \
- const absl::Duration z = absl::ZeroDuration(); \
- const absl::Duration inf = absl::InfiniteDuration(); \
- const double dbl_inf = std::numeric_limits<double>::infinity(); \
- EXPECT_EQ(kint64min, absl::ToInt64##UNIT(-inf)); \
- EXPECT_EQ(-1, absl::ToInt64##UNIT(-d)); \
- EXPECT_EQ(0, absl::ToInt64##UNIT(z)); \
- EXPECT_EQ(1, absl::ToInt64##UNIT(d)); \
- EXPECT_EQ(kint64max, absl::ToInt64##UNIT(inf)); \
- EXPECT_EQ(-dbl_inf, absl::ToDouble##UNIT(-inf)); \
- EXPECT_EQ(-1.5, absl::ToDouble##UNIT(-d)); \
- EXPECT_EQ(0, absl::ToDouble##UNIT(z)); \
- EXPECT_EQ(1.5, absl::ToDouble##UNIT(d)); \
- EXPECT_EQ(dbl_inf, absl::ToDouble##UNIT(inf)); \
+#define TEST_DURATION_CONVERSION(UNIT) \
+ do { \
+ const absl::Duration d = absl::UNIT(1.5); \
+ constexpr absl::Duration z = absl::ZeroDuration(); \
+ constexpr absl::Duration inf = absl::InfiniteDuration(); \
+ constexpr double dbl_inf = std::numeric_limits<double>::infinity(); \
+ EXPECT_EQ(kint64min, absl::ToInt64##UNIT(-inf)); \
+ EXPECT_EQ(-1, absl::ToInt64##UNIT(-d)); \
+ EXPECT_EQ(0, absl::ToInt64##UNIT(z)); \
+ EXPECT_EQ(1, absl::ToInt64##UNIT(d)); \
+ EXPECT_EQ(kint64max, absl::ToInt64##UNIT(inf)); \
+ EXPECT_EQ(-dbl_inf, absl::ToDouble##UNIT(-inf)); \
+ EXPECT_EQ(-1.5, absl::ToDouble##UNIT(-d)); \
+ EXPECT_EQ(0, absl::ToDouble##UNIT(z)); \
+ EXPECT_EQ(1.5, absl::ToDouble##UNIT(d)); \
+ EXPECT_EQ(dbl_inf, absl::ToDouble##UNIT(inf)); \
} while (0)
TEST_DURATION_CONVERSION(Nanoseconds);
@@ -790,6 +803,40 @@ TEST(Duration, DivisionByZero) {
EXPECT_EQ(-dbl_inf, absl::FDivDuration(-any_dur, zero));
}
+TEST(Duration, NaN) {
+ // Note that IEEE 754 does not define the behavior of a nan's sign when it is
+ // copied, so the code below allows for either + or - InfiniteDuration.
+#define TEST_NAN_HANDLING(NAME, NAN) \
+ do { \
+ const auto inf = absl::InfiniteDuration(); \
+ auto x = NAME(NAN); \
+ EXPECT_TRUE(x == inf || x == -inf); \
+ auto y = NAME(42); \
+ y *= NAN; \
+ EXPECT_TRUE(y == inf || y == -inf); \
+ auto z = NAME(42); \
+ z /= NAN; \
+ EXPECT_TRUE(z == inf || z == -inf); \
+ } while (0)
+
+ const double nan = std::numeric_limits<double>::quiet_NaN();
+ TEST_NAN_HANDLING(absl::Nanoseconds, nan);
+ TEST_NAN_HANDLING(absl::Microseconds, nan);
+ TEST_NAN_HANDLING(absl::Milliseconds, nan);
+ TEST_NAN_HANDLING(absl::Seconds, nan);
+ TEST_NAN_HANDLING(absl::Minutes, nan);
+ TEST_NAN_HANDLING(absl::Hours, nan);
+
+ TEST_NAN_HANDLING(absl::Nanoseconds, -nan);
+ TEST_NAN_HANDLING(absl::Microseconds, -nan);
+ TEST_NAN_HANDLING(absl::Milliseconds, -nan);
+ TEST_NAN_HANDLING(absl::Seconds, -nan);
+ TEST_NAN_HANDLING(absl::Minutes, -nan);
+ TEST_NAN_HANDLING(absl::Hours, -nan);
+
+#undef TEST_NAN_HANDLING
+}
+
TEST(Duration, Range) {
const absl::Duration range = ApproxYears(100 * 1e9);
const absl::Duration range_future = range;
@@ -1284,6 +1331,16 @@ TEST(Duration, SmallConversions) {
EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(0.875e-9));
EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(1.000e-9));
+ EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(-0.124999999e-9));
+ EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.125e-9));
+ EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.250e-9));
+ EXPECT_EQ(-absl::Nanoseconds(1) / 2, absl::Seconds(-0.375e-9));
+ EXPECT_EQ(-absl::Nanoseconds(1) / 2, absl::Seconds(-0.500e-9));
+ EXPECT_EQ(-absl::Nanoseconds(3) / 4, absl::Seconds(-0.625e-9));
+ EXPECT_EQ(-absl::Nanoseconds(3) / 4, absl::Seconds(-0.750e-9));
+ EXPECT_EQ(-absl::Nanoseconds(1), absl::Seconds(-0.875e-9));
+ EXPECT_EQ(-absl::Nanoseconds(1), absl::Seconds(-1.000e-9));
+
timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 0;
@@ -1313,6 +1370,86 @@ TEST(Duration, SmallConversions) {
EXPECT_THAT(ToTimeval(absl::Nanoseconds(2000)), TimevalMatcher(tv));
}
+void VerifySameAsMul(double time_as_seconds, int* const misses) {
+ auto direct_seconds = absl::Seconds(time_as_seconds);
+ auto mul_by_one_second = time_as_seconds * absl::Seconds(1);
+ if (direct_seconds != mul_by_one_second) {
+ if (*misses > 10) return;
+ ASSERT_LE(++(*misses), 10) << "Too many errors, not reporting more.";
+ EXPECT_EQ(direct_seconds, mul_by_one_second)
+ << "given double time_as_seconds = " << std::setprecision(17)
+ << time_as_seconds;
+ }
+}
+
+// For a variety of interesting durations, we find the exact point
+// where one double converts to that duration, and the very next double
+// converts to the next duration. For both of those points, verify that
+// Seconds(point) returns the same duration as point * Seconds(1.0)
+TEST(Duration, ToDoubleSecondsCheckEdgeCases) {
+ constexpr uint32_t kTicksPerSecond = absl::time_internal::kTicksPerSecond;
+ constexpr auto duration_tick = absl::time_internal::MakeDuration(0, 1u);
+ int misses = 0;
+ for (int64_t seconds = 0; seconds < 99; ++seconds) {
+ uint32_t tick_vals[] = {0, +999, +999999, +999999999, kTicksPerSecond - 1,
+ 0, 1000, 1000000, 1000000000, kTicksPerSecond,
+ 1, 1001, 1000001, 1000000001, kTicksPerSecond + 1,
+ 2, 1002, 1000002, 1000000002, kTicksPerSecond + 2,
+ 3, 1003, 1000003, 1000000003, kTicksPerSecond + 3,
+ 4, 1004, 1000004, 1000000004, kTicksPerSecond + 4,
+ 5, 6, 7, 8, 9};
+ for (uint32_t ticks : tick_vals) {
+ absl::Duration s_plus_t = absl::Seconds(seconds) + ticks * duration_tick;
+ for (absl::Duration d : {s_plus_t, -s_plus_t}) {
+ absl::Duration after_d = d + duration_tick;
+ EXPECT_NE(d, after_d);
+ EXPECT_EQ(after_d - d, duration_tick);
+
+ double low_edge = ToDoubleSeconds(d);
+ EXPECT_EQ(d, absl::Seconds(low_edge));
+
+ double high_edge = ToDoubleSeconds(after_d);
+ EXPECT_EQ(after_d, absl::Seconds(high_edge));
+
+ for (;;) {
+ double midpoint = low_edge + (high_edge - low_edge) / 2;
+ if (midpoint == low_edge || midpoint == high_edge) break;
+ absl::Duration mid_duration = absl::Seconds(midpoint);
+ if (mid_duration == d) {
+ low_edge = midpoint;
+ } else {
+ EXPECT_EQ(mid_duration, after_d);
+ high_edge = midpoint;
+ }
+ }
+ // Now low_edge is the highest double that converts to Duration d,
+ // and high_edge is the lowest double that converts to Duration after_d.
+ VerifySameAsMul(low_edge, &misses);
+ VerifySameAsMul(high_edge, &misses);
+ }
+ }
+ }
+}
+
+TEST(Duration, ToDoubleSecondsCheckRandom) {
+ std::random_device rd;
+ std::seed_seq seed({rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()});
+ std::mt19937_64 gen(seed);
+ // We want doubles distributed from 1/8ns up to 2^63, where
+ // as many values are tested from 1ns to 2ns as from 1sec to 2sec,
+ // so even distribute along a log-scale of those values, and
+ // exponentiate before using them. (9.223377e+18 is just slightly
+ // out of bounds for absl::Duration.)
+ std::uniform_real_distribution<double> uniform(std::log(0.125e-9),
+ std::log(9.223377e+18));
+ int misses = 0;
+ for (int i = 0; i < 1000000; ++i) {
+ double d = std::exp(uniform(gen));
+ VerifySameAsMul(d, &misses);
+ VerifySameAsMul(-d, &misses);
+ }
+}
+
TEST(Duration, ConversionSaturation) {
absl::Duration d;
diff --git a/absl/time/format.cc b/absl/time/format.cc
index 6bf3c20f..6aabcee9 100644
--- a/absl/time/format.cc
+++ b/absl/time/format.cc
@@ -22,7 +22,7 @@
namespace cctz = absl::time_internal::cctz;
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
extern const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
@@ -35,15 +35,13 @@ namespace {
const char kInfiniteFutureStr[] = "infinite-future";
const char kInfinitePastStr[] = "infinite-past";
-using cctz_sec = cctz::time_point<cctz::sys_seconds>;
-using cctz_fem = cctz::detail::femtoseconds;
struct cctz_parts {
- cctz_sec sec;
- cctz_fem fem;
+ cctz::time_point<cctz::seconds> sec;
+ cctz::detail::femtoseconds fem;
};
-inline cctz_sec unix_epoch() {
- return std::chrono::time_point_cast<cctz::sys_seconds>(
+inline cctz::time_point<cctz::seconds> unix_epoch() {
+ return std::chrono::time_point_cast<cctz::seconds>(
std::chrono::system_clock::from_time_t(0));
}
@@ -54,8 +52,8 @@ cctz_parts Split(absl::Time t) {
const auto d = time_internal::ToUnixDuration(t);
const int64_t rep_hi = time_internal::GetRepHi(d);
const int64_t rep_lo = time_internal::GetRepLo(d);
- const auto sec = unix_epoch() + cctz::sys_seconds(rep_hi);
- const auto fem = cctz_fem(rep_lo * (1000 * 1000 / 4));
+ const auto sec = unix_epoch() + cctz::seconds(rep_hi);
+ const auto fem = cctz::detail::femtoseconds(rep_lo * (1000 * 1000 / 4));
return {sec, fem};
}
@@ -91,7 +89,7 @@ bool ParseTime(const std::string& format, const std::string& input, absl::Time*
return absl::ParseTime(format, input, absl::UTCTimeZone(), time, err);
}
-// If the input std::string does not contain an explicit UTC offset, interpret
+// If the input string does not contain an explicit UTC offset, interpret
// the fields with respect to the given TimeZone.
bool ParseTime(const std::string& format, const std::string& input, absl::TimeZone tz,
absl::Time* time, std::string* err) {
@@ -139,5 +137,5 @@ std::string UnparseFlag(absl::Time t) {
return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/format_benchmark.cc b/absl/time/format_benchmark.cc
index ee53d71c..766f1b39 100644
--- a/absl/time/format_benchmark.cc
+++ b/absl/time/format_benchmark.cc
@@ -38,7 +38,8 @@ void BM_Format_FormatTime(benchmark::State& state) {
const absl::TimeZone lax =
absl::time_internal::LoadTimeZone("America/Los_Angeles");
const absl::Time t =
- absl::FromDateTime(1977, 6, 28, 9, 8, 7, lax) + absl::Nanoseconds(1);
+ absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax) +
+ absl::Nanoseconds(1);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(absl::FormatTime(fmt, t, lax).length());
}
@@ -50,8 +51,8 @@ void BM_Format_ParseTime(benchmark::State& state) {
state.SetLabel(fmt);
const absl::TimeZone lax =
absl::time_internal::LoadTimeZone("America/Los_Angeles");
- absl::Time t =
- absl::FromDateTime(1977, 6, 28, 9, 8, 7, lax) + absl::Nanoseconds(1);
+ absl::Time t = absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax) +
+ absl::Nanoseconds(1);
const std::string when = absl::FormatTime(fmt, t, lax);
std::string err;
while (state.KeepRunning()) {
diff --git a/absl/time/format_test.cc b/absl/time/format_test.cc
index 7c84c33f..ac8d5ea3 100644
--- a/absl/time/format_test.cc
+++ b/absl/time/format_test.cc
@@ -118,7 +118,7 @@ TEST(FormatTime, RFC1123FormatPadsYear) { // locale specific
absl::TimeZone tz = absl::UTCTimeZone();
// A year of 77 should be padded to 0077.
- absl::Time t = absl::FromDateTime(77, 6, 28, 9, 8, 7, tz);
+ absl::Time t = absl::FromCivil(absl::CivilSecond(77, 6, 28, 9, 8, 7), tz);
EXPECT_EQ("Mon, 28 Jun 0077 09:08:07 +0000",
absl::FormatTime(absl::RFC1123_full, t, tz));
EXPECT_EQ("28 Jun 0077 09:08:07 +0000",
@@ -154,9 +154,9 @@ TEST(ParseTime, Basics) {
EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
"2013-06-28 19:08:09 -0800", &t, &err))
<< err;
- absl::Time::Breakdown bd = t.In(absl::FixedTimeZone(-8 * 60 * 60));
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, -8 * 60 * 60, false);
- EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
+ const auto ci = absl::FixedTimeZone(-8 * 60 * 60).At(t);
+ EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
+ EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
}
TEST(ParseTime, NullErrorString) {
@@ -177,17 +177,17 @@ TEST(ParseTime, WithTimeZone) {
EXPECT_TRUE(
absl::ParseTime("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &t, &e))
<< e;
- absl::Time::Breakdown bd = t.In(tz);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true);
- EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
+ auto ci = tz.At(t);
+ EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
+ EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
// But the timezone is ignored when a UTC offset is present.
EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
"2013-06-28 19:08:09 +0800", tz, &t, &e))
<< e;
- bd = t.In(absl::FixedTimeZone(8 * 60 * 60));
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 6, 28, 19, 8, 9, 8 * 60 * 60, false);
- EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
+ ci = absl::FixedTimeZone(8 * 60 * 60).At(t);
+ EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
+ EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
}
TEST(ParseTime, ErrorCases) {
@@ -332,15 +332,15 @@ TEST(ParseTime, InfiniteTime) {
EXPECT_TRUE(absl::ParseTime("infinite-future %H:%M", "infinite-future 03:04",
&t, &err));
EXPECT_NE(absl::InfiniteFuture(), t);
- EXPECT_EQ(3, t.In(tz).hour);
- EXPECT_EQ(4, t.In(tz).minute);
+ EXPECT_EQ(3, tz.At(t).cs.hour());
+ EXPECT_EQ(4, tz.At(t).cs.minute());
// "infinite-past" as literal std::string
EXPECT_TRUE(
absl::ParseTime("infinite-past %H:%M", "infinite-past 03:04", &t, &err));
EXPECT_NE(absl::InfinitePast(), t);
- EXPECT_EQ(3, t.In(tz).hour);
- EXPECT_EQ(4, t.In(tz).minute);
+ EXPECT_EQ(3, tz.At(t).cs.hour());
+ EXPECT_EQ(4, tz.At(t).cs.minute());
// The input doesn't match the format.
EXPECT_FALSE(absl::ParseTime("infinite-future %H:%M", "03:04", &t, &err));
@@ -365,16 +365,17 @@ TEST(ParseTime, FailsOnUnrepresentableTime) {
//
TEST(FormatParse, RoundTrip) {
- const absl::TimeZone gst =
+ const absl::TimeZone lax =
absl::time_internal::LoadTimeZone("America/Los_Angeles");
- const absl::Time in = absl::FromDateTime(1977, 6, 28, 9, 8, 7, gst);
+ const absl::Time in =
+ absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax);
const absl::Duration subseconds = absl::Nanoseconds(654321);
std::string err;
// RFC3339, which renders subseconds.
{
absl::Time out;
- const std::string s = absl::FormatTime(absl::RFC3339_full, in + subseconds, gst);
+ const std::string s = absl::FormatTime(absl::RFC3339_full, in + subseconds, lax);
EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
<< s << ": " << err;
EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez
@@ -383,7 +384,7 @@ TEST(FormatParse, RoundTrip) {
// RFC1123, which only does whole seconds.
{
absl::Time out;
- const std::string s = absl::FormatTime(absl::RFC1123_full, in, gst);
+ const std::string s = absl::FormatTime(absl::RFC1123_full, in, lax);
EXPECT_TRUE(absl::ParseTime(absl::RFC1123_full, s, &out, &err))
<< s << ": " << err;
EXPECT_EQ(in, out); // RFC1123_full includes %z
@@ -393,7 +394,12 @@ TEST(FormatParse, RoundTrip) {
// work. On Windows, `absl::ParseTime()` falls back to std::get_time() which
// appears to fail on "%c" (or at least on the "%c" text produced by
// `strftime()`). This makes it fail the round-trip test.
-#ifndef _MSC_VER
+ //
+ // Under the emscripten compiler `absl::ParseTime() falls back to
+ // `strptime()`, but that ends up using a different definition for "%c"
+ // compared to `strftime()`, also causing the round-trip test to fail
+ // (see https://github.com/kripken/emscripten/pull/7491).
+#if !defined(_MSC_VER) && !defined(__EMSCRIPTEN__)
// Even though we don't know what %c will produce, it should roundtrip,
// but only in the 0-offset timezone.
{
@@ -402,7 +408,7 @@ TEST(FormatParse, RoundTrip) {
EXPECT_TRUE(absl::ParseTime("%c", s, &out, &err)) << s << ": " << err;
EXPECT_EQ(in, out);
}
-#endif // _MSC_VER
+#endif // !_MSC_VER && !__EMSCRIPTEN__
}
TEST(FormatParse, RoundTripDistantFuture) {
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel
index 9f1ba21c..e2cfe801 100644
--- a/absl/time/internal/cctz/BUILD.bazel
+++ b/absl/time/internal/cctz/BUILD.bazel
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+package(features = ["-parse_headers"])
+
licenses(["notice"]) # Apache License
### libraries
@@ -102,6 +104,7 @@ cc_test(
"no_test_android_arm",
"no_test_android_arm64",
"no_test_android_x86",
+ "no_test_wasm",
],
deps = [
":civil_time",
diff --git a/absl/time/internal/cctz/include/cctz/civil_time.h b/absl/time/internal/cctz/include/cctz/civil_time.h
index 31a28cb7..9fabbc3d 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time.h
@@ -18,7 +18,7 @@
#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -60,7 +60,7 @@ namespace cctz {
// inferior fields to their minimum valid value (as described above). The
// following are examples of how each of the six types would align the fields
// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the
-// std::string format used here is not important; it's just a shorthand way of
+// string format used here is not important; it's just a shorthand way of
// showing the six YMDHMS fields.)
//
// civil_second 2015-11-22 12:34:56
@@ -325,7 +325,7 @@ using detail::get_yearday;
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
index 78d1db1c..289ff499 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -32,7 +32,7 @@
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -327,6 +327,37 @@ CONSTEXPR_F fields align(year_tag, fields f) noexcept {
////////////////////////////////////////////////////////////////////////
+namespace impl {
+
+template <typename H>
+H AbslHashValueImpl(second_tag, H h, fields f) {
+ return H::combine(std::move(h), f.y, f.m, f.d, f.hh, f.mm, f.ss);
+}
+template <typename H>
+H AbslHashValueImpl(minute_tag, H h, fields f) {
+ return H::combine(std::move(h), f.y, f.m, f.d, f.hh, f.mm);
+}
+template <typename H>
+H AbslHashValueImpl(hour_tag, H h, fields f) {
+ return H::combine(std::move(h), f.y, f.m, f.d, f.hh);
+}
+template <typename H>
+H AbslHashValueImpl(day_tag, H h, fields f) {
+ return H::combine(std::move(h), f.y, f.m, f.d);
+}
+template <typename H>
+H AbslHashValueImpl(month_tag, H h, fields f) {
+ return H::combine(std::move(h), f.y, f.m);
+}
+template <typename H>
+H AbslHashValueImpl(year_tag, H h, fields f) {
+ return H::combine(std::move(h), f.y);
+}
+
+} // namespace impl
+
+////////////////////////////////////////////////////////////////////////
+
template <typename T>
class civil_time {
public:
@@ -356,11 +387,11 @@ class civil_time {
: civil_time(ct.f_) {}
// Factories for the maximum/minimum representable civil_time.
- static civil_time max() {
+ static CONSTEXPR_F civil_time max() {
const auto max_year = std::numeric_limits<std::int_least64_t>::max();
return civil_time(max_year, 12, 31, 23, 59, 59);
}
- static civil_time min() {
+ static CONSTEXPR_F civil_time min() {
const auto min_year = std::numeric_limits<std::int_least64_t>::min();
return civil_time(min_year, 1, 1, 0, 0, 0);
}
@@ -404,23 +435,24 @@ class civil_time {
}
// Binary arithmetic operators.
- inline friend CONSTEXPR_M civil_time operator+(civil_time a,
- diff_t n) noexcept {
+ friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept {
return a += n;
}
- inline friend CONSTEXPR_M civil_time operator+(diff_t n,
- civil_time a) noexcept {
+ friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept {
return a += n;
}
- inline friend CONSTEXPR_M civil_time operator-(civil_time a,
- diff_t n) noexcept {
+ friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept {
return a -= n;
}
- inline friend CONSTEXPR_M diff_t operator-(const civil_time& lhs,
- const civil_time& rhs) noexcept {
+ friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept {
return difference(T{}, lhs.f_, rhs.f_);
}
+ template <typename H>
+ friend H AbslHashValue(H h, civil_time a) {
+ return impl::AbslHashValueImpl(T{}, std::move(h), a.f_);
+ }
+
private:
// All instantiations of this template are allowed to call the following
// private constructor and access the private fields member.
@@ -435,8 +467,8 @@ class civil_time {
// Disallows difference between differently aligned types.
// auto n = civil_day(...) - civil_hour(...); // would be confusing.
-template <typename Tag1, typename Tag2>
-CONSTEXPR_F diff_t operator-(civil_time<Tag1>, civil_time<Tag2>) = delete;
+template <typename T, typename U>
+CONSTEXPR_F diff_t operator-(civil_time<T>, civil_time<U>) = delete;
using civil_year = civil_time<year_tag>;
using civil_month = civil_time<month_tag>;
@@ -505,22 +537,20 @@ enum class weekday {
};
CONSTEXPR_F weekday get_weekday(const civil_day& cd) noexcept {
- CONSTEXPR_D weekday k_weekday_by_sun_off[7] = {
- weekday::sunday, weekday::monday, weekday::tuesday,
- weekday::wednesday, weekday::thursday, weekday::friday,
+ CONSTEXPR_D weekday k_weekday_by_mon_off[13] = {
+ weekday::monday, weekday::tuesday, weekday::wednesday,
+ weekday::thursday, weekday::friday, weekday::saturday,
+ weekday::sunday, weekday::monday, weekday::tuesday,
+ weekday::wednesday, weekday::thursday, weekday::friday,
weekday::saturday,
};
CONSTEXPR_D int k_weekday_offsets[1 + 12] = {
-1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4,
};
- year_t wd = cd.year() - (cd.month() < 3);
- if (wd >= 0) {
- wd += wd / 4 - wd / 100 + wd / 400;
- } else {
- wd += (wd - 3) / 4 - (wd - 99) / 100 + (wd - 399) / 400;
- }
+ year_t wd = 2400 + (cd.year() % 400) - (cd.month() < 3);
+ wd += wd / 4 - wd / 100 + wd / 400;
wd += k_weekday_offsets[cd.month()] + cd.day();
- return k_weekday_by_sun_off[(wd % 7 + 7) % 7];
+ return k_weekday_by_mon_off[wd % 7 + 6];
}
////////////////////////////////////////////////////////////////////////
@@ -556,7 +586,7 @@ std::ostream& operator<<(std::ostream& os, weekday wd);
} // namespace detail
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#undef CONSTEXPR_M
diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h
index 52d69384..0c34393e 100644
--- a/absl/time/internal/cctz/include/cctz/time_zone.h
+++ b/absl/time/internal/cctz/include/cctz/time_zone.h
@@ -28,30 +28,31 @@
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
// Convenience aliases. Not intended as public API points.
template <typename D>
using time_point = std::chrono::time_point<std::chrono::system_clock, D>;
-using sys_seconds = std::chrono::duration<std::int_fast64_t>;
+using seconds = std::chrono::duration<std::int_fast64_t>;
+using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead.
namespace detail {
template <typename D>
-inline std::pair<time_point<sys_seconds>, D>
+inline std::pair<time_point<seconds>, D>
split_seconds(const time_point<D>& tp) {
- auto sec = std::chrono::time_point_cast<sys_seconds>(tp);
+ auto sec = std::chrono::time_point_cast<seconds>(tp);
auto sub = tp - sec;
if (sub.count() < 0) {
- sec -= sys_seconds(1);
- sub += sys_seconds(1);
+ sec -= seconds(1);
+ sub += seconds(1);
}
return {sec, std::chrono::duration_cast<D>(sub)};
}
-inline std::pair<time_point<sys_seconds>, sys_seconds>
-split_seconds(const time_point<sys_seconds>& tp) {
- return {tp, sys_seconds(0)};
+inline std::pair<time_point<seconds>, seconds>
+split_seconds(const time_point<seconds>& tp) {
+ return {tp, seconds::zero()};
}
} // namespace detail
@@ -100,7 +101,7 @@ class time_zone {
bool is_dst; // is offset non-standard?
const char* abbr; // time-zone abbreviation (e.g., "PST")
};
- absolute_lookup lookup(const time_point<sys_seconds>& tp) const;
+ absolute_lookup lookup(const time_point<seconds>& tp) const;
template <typename D>
absolute_lookup lookup(const time_point<D>& tp) const {
return lookup(detail::split_seconds(tp).first);
@@ -119,9 +120,9 @@ class time_zone {
// of the given civil-time argument, and the pre, trans, and post
// members will give the absolute time answers using the pre-transition
// offset, the transition point itself, and the post-transition offset,
- // respectively (all three times are equal if kind == UNIQUE). If any
+ // respectively (all three times are equal if kind == UNIQUE). If any
// of these three absolute times is outside the representable range of a
- // time_point<sys_seconds> the field is set to its maximum/minimum value.
+ // time_point<seconds> the field is set to its maximum/minimum value.
//
// Example:
// cctz::time_zone lax;
@@ -153,23 +154,90 @@ class time_zone {
SKIPPED, // the civil time did not exist (pre >= trans > post)
REPEATED, // the civil time was ambiguous (pre < trans <= post)
} kind;
- time_point<sys_seconds> pre; // uses the pre-transition offset
- time_point<sys_seconds> trans; // instant of civil-offset change
- time_point<sys_seconds> post; // uses the post-transition offset
+ time_point<seconds> pre; // uses the pre-transition offset
+ time_point<seconds> trans; // instant of civil-offset change
+ time_point<seconds> post; // uses the post-transition offset
};
civil_lookup lookup(const civil_second& cs) const;
+ // Finds the time of the next/previous offset change in this time zone.
+ //
+ // By definition, next_transition(tp, &trans) returns false when tp has
+ // its maximum value, and prev_transition(tp, &trans) returns false
+ // when tp has its minimum value. If the zone has no transitions, the
+ // result will also be false no matter what the argument.
+ //
+ // Otherwise, when tp has its minimum value, next_transition(tp, &trans)
+ // returns true and sets trans to the first recorded transition. Chains
+ // of calls to next_transition()/prev_transition() will eventually return
+ // false, but it is unspecified exactly when next_transition(tp, &trans)
+ // jumps to false, or what time is set by prev_transition(tp, &trans) for
+ // a very distant tp.
+ //
+ // Note: Enumeration of time-zone transitions is for informational purposes
+ // only. Modern time-related code should not care about when offset changes
+ // occur.
+ //
+ // Example:
+ // cctz::time_zone nyc;
+ // if (!cctz::load_time_zone("America/New_York", &nyc)) { ... }
+ // const auto now = std::chrono::system_clock::now();
+ // auto tp = cctz::time_point<cctz::seconds>::min();
+ // cctz::time_zone::civil_transition trans;
+ // while (tp <= now && nyc.next_transition(tp, &trans)) {
+ // // transition: trans.from -> trans.to
+ // tp = nyc.lookup(trans.to).trans;
+ // }
+ struct civil_transition {
+ civil_second from; // the civil time we jump from
+ civil_second to; // the civil time we jump to
+ };
+ bool next_transition(const time_point<seconds>& tp,
+ civil_transition* trans) const;
+ template <typename D>
+ bool next_transition(const time_point<D>& tp,
+ civil_transition* trans) const {
+ return next_transition(detail::split_seconds(tp).first, trans);
+ }
+ bool prev_transition(const time_point<seconds>& tp,
+ civil_transition* trans) const;
+ template <typename D>
+ bool prev_transition(const time_point<D>& tp,
+ civil_transition* trans) const {
+ return prev_transition(detail::split_seconds(tp).first, trans);
+ }
+
+ // version() and description() provide additional information about the
+ // time zone. The content of each of the returned strings is unspecified,
+ // however, when the IANA Time Zone Database is the underlying data source
+ // the version() std::string will be in the familar form (e.g, "2018e") or
+ // empty when unavailable.
+ //
+ // Note: These functions are for informational or testing purposes only.
+ std::string version() const; // empty when unknown
+ std::string description() const;
+
+ // Relational operators.
+ friend bool operator==(time_zone lhs, time_zone rhs) {
+ return &lhs.effective_impl() == &rhs.effective_impl();
+ }
+ friend bool operator!=(time_zone lhs, time_zone rhs) {
+ return !(lhs == rhs);
+ }
+
+ template <typename H>
+ friend H AbslHashValue(H h, time_zone tz) {
+ return H::combine(std::move(h), &tz.effective_impl());
+ }
+
class Impl;
private:
explicit time_zone(const Impl* impl) : impl_(impl) {}
+ const Impl& effective_impl() const; // handles implicit UTC
const Impl* impl_;
};
-// Relational operators.
-bool operator==(time_zone lhs, time_zone rhs);
-inline bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); }
-
// Loads the named time zone. May perform I/O on the initial load.
// If the name is invalid, or some other kind of error occurs, returns
// false and "*tz" is set to the UTC time zone.
@@ -181,9 +249,10 @@ time_zone utc_time_zone();
// Returns a time zone that is a fixed offset (seconds east) from UTC.
// Note: If the absolute value of the offset is greater than 24 hours
// you'll get UTC (i.e., zero offset) instead.
-time_zone fixed_time_zone(const sys_seconds& offset);
+time_zone fixed_time_zone(const seconds& offset);
// Returns a time zone representing the local time zone. Falls back to UTC.
+// Note: local_time_zone.name() may only be something like "localtime".
time_zone local_time_zone();
// Returns the civil time (cctz::civil_second) within the given time zone at
@@ -200,8 +269,8 @@ inline civil_second convert(const time_point<D>& tp, const time_zone& tz) {
// it was either repeated or non-existent), then the returned time_point is
// the best estimate that preserves relative order. That is, this function
// guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz).
-inline time_point<sys_seconds> convert(const civil_second& cs,
- const time_zone& tz) {
+inline time_point<seconds> convert(const civil_second& cs,
+ const time_zone& tz) {
const time_zone::civil_lookup cl = tz.lookup(cs);
if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans;
return cl.pre;
@@ -209,14 +278,14 @@ inline time_point<sys_seconds> convert(const civil_second& cs,
namespace detail {
using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>;
-std::string format(const std::string&, const time_point<sys_seconds>&,
+std::string format(const std::string&, const time_point<seconds>&,
const femtoseconds&, const time_zone&);
bool parse(const std::string&, const std::string&, const time_zone&,
- time_point<sys_seconds>*, femtoseconds*, std::string* err = nullptr);
+ time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
} // namespace detail
// Formats the given time_point in the given cctz::time_zone according to
-// the provided format std::string. Uses strftime()-like formatting options,
+// the provided format string. Uses strftime()-like formatting options,
// with the following extensions:
//
// - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
@@ -227,7 +296,7 @@ bool parse(const std::string&, const std::string&, const time_zone&,
// - %E*f - Fractional seconds with full precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
//
-// Note that %E0S behaves like %S, and %E0f produces no characters. In
+// Note that %E0S behaves like %S, and %E0f produces no characters. In
// contrast %E*f always produces at least one digit, which may be '0'.
//
// Note that %Y produces as many characters as it takes to fully render the
@@ -235,7 +304,7 @@ bool parse(const std::string&, const std::string&, const time_zone&,
// more than four characters, just like %Y.
//
// Tip: Format strings should include the UTC offset (e.g., %z, %Ez, or %E*z)
-// so that the resulting std::string uniquely identifies an absolute time.
+// so that the resulting string uniquely identifies an absolute time.
//
// Example:
// cctz::time_zone lax;
@@ -251,10 +320,10 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
return detail::format(fmt, p.first, n, tz);
}
-// Parses an input std::string according to the provided format std::string and
+// Parses an input string according to the provided format string and
// returns the corresponding time_point. Uses strftime()-like formatting
// options, with the same extensions as cctz::format(), but with the
-// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
+// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
// and %E*z also accept the same inputs.
//
// %Y consumes as many numeric characters as it can, so the matching data
@@ -265,7 +334,7 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
//
// "1970-01-01 00:00:00.0 +0000"
//
-// For example, parsing a std::string of "15:45" (%H:%M) will return a time_point
+// For example, parsing a string of "15:45" (%H:%M) will return a time_point
// that represents "1970-01-01 15:45:00.0 +0000".
//
// Note that parse() returns time instants, so it makes most sense to parse
@@ -299,7 +368,7 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
template <typename D>
inline bool parse(const std::string& fmt, const std::string& input,
const time_zone& tz, time_point<D>* tpp) {
- time_point<sys_seconds> sec;
+ time_point<seconds> sec;
detail::femtoseconds fs;
const bool b = detail::parse(fmt, input, tz, &sec, &fs);
if (b) {
@@ -312,7 +381,7 @@ inline bool parse(const std::string& fmt, const std::string& input,
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
diff --git a/absl/time/internal/cctz/include/cctz/zone_info_source.h b/absl/time/internal/cctz/include/cctz/zone_info_source.h
index b72a02cf..b3274e00 100644
--- a/absl/time/internal/cctz/include/cctz/zone_info_source.h
+++ b/absl/time/internal/cctz/include/cctz/zone_info_source.h
@@ -21,7 +21,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -32,15 +32,20 @@ class ZoneInfoSource {
virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread()
virtual int Skip(std::size_t offset) = 0; // like fseek()
+
+ // Until the zoneinfo data supports versioning information, we provide
+ // a way for a ZoneInfoSource to indicate it out-of-band. The default
+ // implementation returns an empty std::string.
+ virtual std::string Version() const;
};
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz_extension {
@@ -89,7 +94,7 @@ extern ZoneInfoSourceFactory zone_info_source_factory;
} // namespace cctz_extension
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index f13cb4ee..4498d7d0 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -754,23 +754,21 @@ void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) {
}
BENCHMARK(BM_Zone_LoadAllTimeZonesCached);
-void BM_Zone_TimeZoneImplGetImplicit(benchmark::State& state) {
+void BM_Zone_TimeZoneEqualityImplicit(benchmark::State& state) {
cctz::time_zone tz; // implicit UTC
- cctz::time_zone::Impl::get(tz);
while (state.KeepRunning()) {
- cctz::time_zone::Impl::get(tz);
+ benchmark::DoNotOptimize(tz == tz);
}
}
-BENCHMARK(BM_Zone_TimeZoneImplGetImplicit);
+BENCHMARK(BM_Zone_TimeZoneEqualityImplicit);
-void BM_Zone_TimeZoneImplGetExplicit(benchmark::State& state) {
+void BM_Zone_TimeZoneEqualityExplicit(benchmark::State& state) {
cctz::time_zone tz = cctz::utc_time_zone(); // explicit UTC
- cctz::time_zone::Impl::get(tz);
while (state.KeepRunning()) {
- cctz::time_zone::Impl::get(tz);
+ benchmark::DoNotOptimize(tz == tz);
}
}
-BENCHMARK(BM_Zone_TimeZoneImplGetExplicit);
+BENCHMARK(BM_Zone_TimeZoneEqualityExplicit);
void BM_Zone_UTCTimeZone(benchmark::State& state) {
cctz::time_zone tz;
@@ -780,13 +778,13 @@ void BM_Zone_UTCTimeZone(benchmark::State& state) {
}
BENCHMARK(BM_Zone_UTCTimeZone);
-// In each "ToDateTime" benchmark we switch between two instants
-// separated by at least one transition in order to defeat any
-// internal caching of previous results (e.g., see local_time_hint_).
+// In each "ToCivil" benchmark we switch between two instants separated
+// by at least one transition in order to defeat any internal caching of
+// previous results (e.g., see local_time_hint_).
//
// The "UTC" variants use UTC instead of the Google/local time zone.
-void BM_Time_ToDateTime_CCTZ(benchmark::State& state) {
+void BM_Time_ToCivil_CCTZ(benchmark::State& state) {
const cctz::time_zone tz = TestTimeZone();
std::chrono::system_clock::time_point tp =
std::chrono::system_clock::from_time_t(1384569027);
@@ -798,9 +796,9 @@ void BM_Time_ToDateTime_CCTZ(benchmark::State& state) {
benchmark::DoNotOptimize(cctz::convert(tp, tz));
}
}
-BENCHMARK(BM_Time_ToDateTime_CCTZ);
+BENCHMARK(BM_Time_ToCivil_CCTZ);
-void BM_Time_ToDateTime_Libc(benchmark::State& state) {
+void BM_Time_ToCivil_Libc(benchmark::State& state) {
// No timezone support, so just use localtime.
time_t t = 1384569027;
time_t t2 = 1418962578;
@@ -815,9 +813,9 @@ void BM_Time_ToDateTime_Libc(benchmark::State& state) {
#endif
}
}
-BENCHMARK(BM_Time_ToDateTime_Libc);
+BENCHMARK(BM_Time_ToCivil_Libc);
-void BM_Time_ToDateTimeUTC_CCTZ(benchmark::State& state) {
+void BM_Time_ToCivilUTC_CCTZ(benchmark::State& state) {
const cctz::time_zone tz = cctz::utc_time_zone();
std::chrono::system_clock::time_point tp =
std::chrono::system_clock::from_time_t(1384569027);
@@ -826,9 +824,9 @@ void BM_Time_ToDateTimeUTC_CCTZ(benchmark::State& state) {
benchmark::DoNotOptimize(cctz::convert(tp, tz));
}
}
-BENCHMARK(BM_Time_ToDateTimeUTC_CCTZ);
+BENCHMARK(BM_Time_ToCivilUTC_CCTZ);
-void BM_Time_ToDateTimeUTC_Libc(benchmark::State& state) {
+void BM_Time_ToCivilUTC_Libc(benchmark::State& state) {
time_t t = 1384569027;
struct tm tm;
while (state.KeepRunning()) {
@@ -840,16 +838,16 @@ void BM_Time_ToDateTimeUTC_Libc(benchmark::State& state) {
#endif
}
}
-BENCHMARK(BM_Time_ToDateTimeUTC_Libc);
+BENCHMARK(BM_Time_ToCivilUTC_Libc);
-// In each "FromDateTime" benchmark we switch between two YMDhms
-// values separated by at least one transition in order to defeat any
-// internal caching of previous results (e.g., see time_local_hint_).
+// In each "FromCivil" benchmark we switch between two YMDhms values
+// separated by at least one transition in order to defeat any internal
+// caching of previous results (e.g., see time_local_hint_).
//
// The "UTC" variants use UTC instead of the Google/local time zone.
// The "Day0" variants require normalization of the day of month.
-void BM_Time_FromDateTime_CCTZ(benchmark::State& state) {
+void BM_Time_FromCivil_CCTZ(benchmark::State& state) {
const cctz::time_zone tz = TestTimeZone();
int i = 0;
while (state.KeepRunning()) {
@@ -862,9 +860,9 @@ void BM_Time_FromDateTime_CCTZ(benchmark::State& state) {
}
}
}
-BENCHMARK(BM_Time_FromDateTime_CCTZ);
+BENCHMARK(BM_Time_FromCivil_CCTZ);
-void BM_Time_FromDateTime_Libc(benchmark::State& state) {
+void BM_Time_FromCivil_Libc(benchmark::State& state) {
// No timezone support, so just use localtime.
int i = 0;
while (state.KeepRunning()) {
@@ -888,20 +886,20 @@ void BM_Time_FromDateTime_Libc(benchmark::State& state) {
benchmark::DoNotOptimize(mktime(&tm));
}
}
-BENCHMARK(BM_Time_FromDateTime_Libc);
+BENCHMARK(BM_Time_FromCivil_Libc);
-void BM_Time_FromDateTimeUTC_CCTZ(benchmark::State& state) {
+void BM_Time_FromCivilUTC_CCTZ(benchmark::State& state) {
const cctz::time_zone tz = cctz::utc_time_zone();
while (state.KeepRunning()) {
benchmark::DoNotOptimize(
cctz::convert(cctz::civil_second(2014, 12, 18, 20, 16, 18), tz));
}
}
-BENCHMARK(BM_Time_FromDateTimeUTC_CCTZ);
+BENCHMARK(BM_Time_FromCivilUTC_CCTZ);
-// There is no BM_Time_FromDateTimeUTC_Libc.
+// There is no BM_Time_FromCivilUTC_Libc.
-void BM_Time_FromDateTimeDay0_CCTZ(benchmark::State& state) {
+void BM_Time_FromCivilDay0_CCTZ(benchmark::State& state) {
const cctz::time_zone tz = TestTimeZone();
int i = 0;
while (state.KeepRunning()) {
@@ -914,9 +912,9 @@ void BM_Time_FromDateTimeDay0_CCTZ(benchmark::State& state) {
}
}
}
-BENCHMARK(BM_Time_FromDateTimeDay0_CCTZ);
+BENCHMARK(BM_Time_FromCivilDay0_CCTZ);
-void BM_Time_FromDateTimeDay0_Libc(benchmark::State& state) {
+void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
// No timezone support, so just use localtime.
int i = 0;
while (state.KeepRunning()) {
@@ -940,7 +938,7 @@ void BM_Time_FromDateTimeDay0_Libc(benchmark::State& state) {
benchmark::DoNotOptimize(mktime(&tm));
}
}
-BENCHMARK(BM_Time_FromDateTimeDay0_Libc);
+BENCHMARK(BM_Time_FromCivilDay0_Libc);
const char* const kFormats[] = {
RFC1123_full, // 0
diff --git a/absl/time/internal/cctz/src/civil_time_detail.cc b/absl/time/internal/cctz/src/civil_time_detail.cc
index 92a2e09c..e888066d 100644
--- a/absl/time/internal/cctz/src/civil_time_detail.cc
+++ b/absl/time/internal/cctz/src/civil_time_detail.cc
@@ -19,7 +19,7 @@
#include <sstream>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
namespace detail {
@@ -88,5 +88,5 @@ std::ostream& operator<<(std::ostream& os, weekday wd) {
} // namespace detail
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/civil_time_test.cc b/absl/time/internal/cctz/src/civil_time_test.cc
index d26c6498..2417a2a9 100644
--- a/absl/time/internal/cctz/src/civil_time_test.cc
+++ b/absl/time/internal/cctz/src/civil_time_test.cc
@@ -23,7 +23,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -621,7 +621,7 @@ TEST(CivilTime, Relational) {
TEST_RELATIONAL(civil_second(2014, 1, 1, 1, 1, 0),
civil_second(2014, 1, 1, 1, 1, 1));
- // Tests the relational operators of two different CivilTime types.
+ // Tests the relational operators of two different civil-time types.
TEST_RELATIONAL(civil_day(2014, 1, 1), civil_minute(2014, 1, 1, 1, 1));
TEST_RELATIONAL(civil_day(2014, 1, 1), civil_month(2014, 2));
@@ -1047,5 +1047,5 @@ TEST(CivilTime, FirstThursdayInMonth) {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc
index 4b608f68..070abd26 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.cc
+++ b/absl/time/internal/cctz/src/time_zone_fixed.cc
@@ -15,13 +15,13 @@
#include "time_zone_fixed.h"
#include <algorithm>
+#include <cassert>
#include <chrono>
-#include <cstdio>
#include <cstring>
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -30,8 +30,15 @@ namespace {
// The prefix used for the internal names of fixed-offset zones.
const char kFixedOffsetPrefix[] = "Fixed/UTC";
+const char kDigits[] = "0123456789";
+
+char* Format02d(char* p, int v) {
+ *p++ = kDigits[(v / 10) % 10];
+ *p++ = kDigits[v % 10];
+ return p;
+}
+
int Parse02d(const char* p) {
- static const char kDigits[] = "0123456789";
if (const char* ap = std::strchr(kDigits, *p)) {
int v = static_cast<int>(ap - kDigits);
if (const char* bp = std::strchr(kDigits, *++p)) {
@@ -43,9 +50,9 @@ int Parse02d(const char* p) {
} // namespace
-bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
+bool FixedOffsetFromName(const std::string& name, seconds* offset) {
if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
- *offset = sys_seconds::zero();
+ *offset = seconds::zero();
return true;
}
@@ -70,12 +77,12 @@ bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
secs += ((hours * 60) + mins) * 60;
if (secs > 24 * 60 * 60) return false; // outside supported offset range
- *offset = sys_seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
+ *offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
return true;
}
-std::string FixedOffsetToName(const sys_seconds& offset) {
- if (offset == sys_seconds::zero()) return "UTC";
+std::string FixedOffsetToName(const seconds& offset) {
+ if (offset == seconds::zero()) return "UTC";
if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
// We don't support fixed-offset zones more than 24 hours
// away from UTC to avoid complications in rendering such
@@ -96,13 +103,21 @@ std::string FixedOffsetToName(const sys_seconds& offset) {
}
int hours = minutes / 60;
minutes %= 60;
- char buf[sizeof(kFixedOffsetPrefix) + sizeof("-24:00:00")];
- snprintf(buf, sizeof(buf), "%s%c%02d:%02d:%02d",
- kFixedOffsetPrefix, sign, hours, minutes, seconds);
+ char buf[sizeof(kFixedOffsetPrefix) - 1 + sizeof("-24:00:00")];
+ std::strcpy(buf, kFixedOffsetPrefix);
+ char* ep = buf + sizeof(kFixedOffsetPrefix) - 1;
+ *ep++ = sign;
+ ep = Format02d(ep, hours);
+ *ep++ = ':';
+ ep = Format02d(ep, minutes);
+ *ep++ = ':';
+ ep = Format02d(ep, seconds);
+ *ep++ = '\0';
+ assert(ep == buf + sizeof(buf));
return buf;
}
-std::string FixedOffsetToAbbr(const sys_seconds& offset) {
+std::string FixedOffsetToAbbr(const seconds& offset) {
std::string abbr = FixedOffsetToName(offset);
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
@@ -121,5 +136,5 @@ std::string FixedOffsetToAbbr(const sys_seconds& offset) {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.h b/absl/time/internal/cctz/src/time_zone_fixed.h
index 15e9db1e..dbb2958e 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.h
+++ b/absl/time/internal/cctz/src/time_zone_fixed.h
@@ -20,7 +20,7 @@
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -39,13 +39,13 @@ namespace cctz {
// Note: FixedOffsetFromName() fails on syntax errors or when the parsed
// offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr()
// both produce "UTC" when the argument offset exceeds 24 hours.
-bool FixedOffsetFromName(const std::string& name, sys_seconds* offset);
-std::string FixedOffsetToName(const sys_seconds& offset);
-std::string FixedOffsetToAbbr(const sys_seconds& offset);
+bool FixedOffsetFromName(const std::string& name, seconds* offset);
+std::string FixedOffsetToName(const seconds& offset);
+std::string FixedOffsetToAbbr(const seconds& offset);
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc
index 0efbbc79..02ecb2cf 100644
--- a/absl/time/internal/cctz/src/time_zone_format.cc
+++ b/absl/time/internal/cctz/src/time_zone_format.cc
@@ -38,7 +38,7 @@
#include "time_zone_if.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
namespace detail {
@@ -142,6 +142,9 @@ char* Format02d(char* ep, int v) {
// Formats a UTC offset, like +00:00.
char* FormatOffset(char* ep, int offset, const char* mode) {
+ // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
+ // generate a "negative zero" when we're formatting a zero offset
+ // as the result of a failed load_time_zone().
char sign = '+';
if (offset < 0) {
offset = -offset; // bounded by 24h so no overflow
@@ -278,7 +281,7 @@ const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
// not support the tm_gmtoff and tm_zone extensions to std::tm.
//
// Requires that zero() <= fs < seconds(1).
-std::string format(const std::string& format, const time_point<sys_seconds>& tp,
+std::string format(const std::string& format, const time_point<seconds>& tp,
const detail::femtoseconds& fs, const time_zone& tz) {
std::string result;
result.reserve(format.size()); // A reasonable guess for the result size.
@@ -531,7 +534,7 @@ const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) {
return dp;
}
-// Parses a std::string into a std::tm using strptime(3).
+// Parses a string into a std::tm using strptime(3).
const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
if (dp != nullptr) {
dp = strptime(dp, fmt, tm);
@@ -556,7 +559,7 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
// We also handle the %z specifier to accommodate platforms that do not
// support the tm_gmtoff extension to std::tm. %Z is parsed but ignored.
bool parse(const std::string& format, const std::string& input,
- const time_zone& tz, time_point<sys_seconds>* sec,
+ const time_zone& tz, time_point<seconds>* sec,
detail::femtoseconds* fs, std::string* err) {
// The unparsed input.
const char* data = input.c_str(); // NUL terminated
@@ -741,7 +744,7 @@ bool parse(const std::string& format, const std::string& input,
data = ParseTM(data, spec.c_str(), &tm);
// If we successfully parsed %p we need to remember whether the result
- // was AM or PM so that we can adjust tm_hour before ConvertDateTime().
+ // was AM or PM so that we can adjust tm_hour before time_zone::lookup().
// So reparse the input with a known AM hour, and check if it is shifted
// to a PM hour.
if (spec == "%p" && data != nullptr) {
@@ -823,15 +826,15 @@ bool parse(const std::string& format, const std::string& input,
const auto tp = ptz.lookup(cs).pre;
// Checks for overflow/underflow and returns an error as necessary.
- if (tp == time_point<sys_seconds>::max()) {
- const auto al = ptz.lookup(time_point<sys_seconds>::max());
+ if (tp == time_point<seconds>::max()) {
+ const auto al = ptz.lookup(time_point<seconds>::max());
if (cs > al.cs) {
if (err != nullptr) *err = "Out-of-range field";
return false;
}
}
- if (tp == time_point<sys_seconds>::min()) {
- const auto al = ptz.lookup(time_point<sys_seconds>::min());
+ if (tp == time_point<seconds>::min()) {
+ const auto al = ptz.lookup(time_point<seconds>::min());
if (cs < al.cs) {
if (err != nullptr) *err = "Out-of-range field";
return false;
@@ -846,5 +849,5 @@ bool parse(const std::string& format, const std::string& input,
} // namespace detail
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc
index 649e9b9a..260c56ad 100644
--- a/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_format_test.cc
@@ -23,18 +23,10 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-using std::chrono::time_point_cast;
-using std::chrono::system_clock;
-using std::chrono::nanoseconds;
-using std::chrono::microseconds;
-using std::chrono::milliseconds;
-using std::chrono::seconds;
-using std::chrono::minutes;
-using std::chrono::hours;
-using testing::HasSubstr;
+namespace chrono = std::chrono;
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -73,6 +65,17 @@ void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt,
EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz));
}
+// These tests sometimes run on platforms that have zoneinfo data so old
+// that the transition we are attempting to check does not exist, most
+// notably Android emulators. Fortunately, AndroidZoneInfoSource supports
+// time_zone::version() so, in cases where we've learned that it matters,
+// we can make the check conditionally.
+int VersionCmp(time_zone tz, const std::string& target) {
+ std::string version = tz.version();
+ if (version.empty() && !target.empty()) return 1; // unknown > known
+ return version.compare(target);
+}
+
} // namespace
//
@@ -82,33 +85,36 @@ void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt,
TEST(Format, TimePointResolution) {
const char kFmt[] = "%H:%M:%E*S";
const time_zone utc = utc_time_zone();
- const time_point<nanoseconds> t0 = system_clock::from_time_t(1420167845) +
- milliseconds(123) + microseconds(456) +
- nanoseconds(789);
- EXPECT_EQ("03:04:05.123456789",
- format(kFmt, time_point_cast<nanoseconds>(t0), utc));
- EXPECT_EQ("03:04:05.123456",
- format(kFmt, time_point_cast<microseconds>(t0), utc));
- EXPECT_EQ("03:04:05.123",
- format(kFmt, time_point_cast<milliseconds>(t0), utc));
+ const time_point<chrono::nanoseconds> t0 =
+ chrono::system_clock::from_time_t(1420167845) +
+ chrono::milliseconds(123) + chrono::microseconds(456) +
+ chrono::nanoseconds(789);
+ EXPECT_EQ(
+ "03:04:05.123456789",
+ format(kFmt, chrono::time_point_cast<chrono::nanoseconds>(t0), utc));
+ EXPECT_EQ(
+ "03:04:05.123456",
+ format(kFmt, chrono::time_point_cast<chrono::microseconds>(t0), utc));
+ EXPECT_EQ(
+ "03:04:05.123",
+ format(kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc));
EXPECT_EQ("03:04:05",
- format(kFmt, time_point_cast<seconds>(t0), utc));
+ format(kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc));
EXPECT_EQ("03:04:05",
- format(kFmt, time_point_cast<sys_seconds>(t0), utc));
+ format(kFmt, chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), utc));
EXPECT_EQ("03:04:00",
- format(kFmt, time_point_cast<minutes>(t0), utc));
+ format(kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc));
EXPECT_EQ("03:00:00",
- format(kFmt, time_point_cast<hours>(t0), utc));
+ format(kFmt, chrono::time_point_cast<chrono::hours>(t0), utc));
}
TEST(Format, TimePointExtendedResolution) {
const char kFmt[] = "%H:%M:%E*S";
const time_zone utc = utc_time_zone();
- const time_point<sys_seconds> tp =
- std::chrono::time_point_cast<sys_seconds>(
- std::chrono::system_clock::from_time_t(0)) +
- std::chrono::hours(12) + std::chrono::minutes(34) +
- std::chrono::seconds(56);
+ const time_point<absl::time_internal::cctz::seconds> tp =
+ chrono::time_point_cast<absl::time_internal::cctz::seconds>(
+ chrono::system_clock::from_time_t(0)) +
+ chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56);
EXPECT_EQ(
"12:34:56.123456789012345",
@@ -133,7 +139,7 @@ TEST(Format, TimePointExtendedResolution) {
TEST(Format, Basics) {
time_zone tz = utc_time_zone();
- time_point<nanoseconds> tp = system_clock::from_time_t(0);
+ time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
// Starts with a couple basic edge cases.
EXPECT_EQ("", format("", tp, tz));
@@ -146,8 +152,9 @@ TEST(Format, Basics) {
std::string bigger(100000, 'x');
EXPECT_EQ(bigger, format(bigger, tp, tz));
- tp += hours(13) + minutes(4) + seconds(5);
- tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
+ tp += chrono::hours(13) + chrono::minutes(4) + chrono::seconds(5);
+ tp += chrono::milliseconds(6) + chrono::microseconds(7) +
+ chrono::nanoseconds(8);
EXPECT_EQ("1970-01-01", format("%Y-%m-%d", tp, tz));
EXPECT_EQ("13:04:05", format("%H:%M:%S", tp, tz));
EXPECT_EQ("13:04:05.006", format("%H:%M:%E3S", tp, tz));
@@ -157,7 +164,7 @@ TEST(Format, Basics) {
TEST(Format, PosixConversions) {
const time_zone tz = utc_time_zone();
- auto tp = system_clock::from_time_t(0);
+ auto tp = chrono::system_clock::from_time_t(0);
TestFormatSpecifier(tp, tz, "%d", "01");
TestFormatSpecifier(tp, tz, "%e", " 1"); // extension but internal support
@@ -197,7 +204,7 @@ TEST(Format, PosixConversions) {
TEST(Format, LocaleSpecific) {
const time_zone tz = utc_time_zone();
- auto tp = system_clock::from_time_t(0);
+ auto tp = chrono::system_clock::from_time_t(0);
TestFormatSpecifier(tp, tz, "%a", "Thu");
TestFormatSpecifier(tp, tz, "%A", "Thursday");
@@ -206,8 +213,8 @@ TEST(Format, LocaleSpecific) {
// %c should at least produce the numeric year and time-of-day.
const std::string s = format("%c", tp, utc_time_zone());
- EXPECT_THAT(s, HasSubstr("1970"));
- EXPECT_THAT(s, HasSubstr("00:00:00"));
+ EXPECT_THAT(s, testing::HasSubstr("1970"));
+ EXPECT_THAT(s, testing::HasSubstr("00:00:00"));
TestFormatSpecifier(tp, tz, "%p", "AM");
TestFormatSpecifier(tp, tz, "%x", "01/01/70");
@@ -246,7 +253,7 @@ TEST(Format, LocaleSpecific) {
TEST(Format, Escaping) {
const time_zone tz = utc_time_zone();
- auto tp = system_clock::from_time_t(0);
+ auto tp = chrono::system_clock::from_time_t(0);
TestFormatSpecifier(tp, tz, "%%", "%");
TestFormatSpecifier(tp, tz, "%%a", "%a");
@@ -267,8 +274,8 @@ TEST(Format, ExtendedSeconds) {
const time_zone tz = utc_time_zone();
// No subseconds.
- time_point<nanoseconds> tp = system_clock::from_time_t(0);
- tp += seconds(5);
+ time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
+ tp += chrono::seconds(5);
EXPECT_EQ("05", format("%E*S", tp, tz));
EXPECT_EQ("05", format("%E0S", tp, tz));
EXPECT_EQ("05.0", format("%E1S", tp, tz));
@@ -288,7 +295,8 @@ TEST(Format, ExtendedSeconds) {
EXPECT_EQ("05.000000000000000", format("%E15S", tp, tz));
// With subseconds.
- tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
+ tp += chrono::milliseconds(6) + chrono::microseconds(7) +
+ chrono::nanoseconds(8);
EXPECT_EQ("05.006007008", format("%E*S", tp, tz));
EXPECT_EQ("05", format("%E0S", tp, tz));
EXPECT_EQ("05.0", format("%E1S", tp, tz));
@@ -308,17 +316,18 @@ TEST(Format, ExtendedSeconds) {
EXPECT_EQ("05.006007008000000", format("%E15S", tp, tz));
// Times before the Unix epoch.
- tp = system_clock::from_time_t(0) + microseconds(-1);
+ tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1);
EXPECT_EQ("1969-12-31 23:59:59.999999",
format("%Y-%m-%d %H:%M:%E*S", tp, tz));
// Here is a "%E*S" case we got wrong for a while. While the first
// instant below is correctly rendered as "...:07.333304", the second
// one used to appear as "...:07.33330499999999999".
- tp = system_clock::from_time_t(0) + microseconds(1395024427333304);
+ tp = chrono::system_clock::from_time_t(0) +
+ chrono::microseconds(1395024427333304);
EXPECT_EQ("2014-03-17 02:47:07.333304",
format("%Y-%m-%d %H:%M:%E*S", tp, tz));
- tp += microseconds(1);
+ tp += chrono::microseconds(1);
EXPECT_EQ("2014-03-17 02:47:07.333305",
format("%Y-%m-%d %H:%M:%E*S", tp, tz));
}
@@ -327,8 +336,8 @@ TEST(Format, ExtendedSubeconds) {
const time_zone tz = utc_time_zone();
// No subseconds.
- time_point<nanoseconds> tp = system_clock::from_time_t(0);
- tp += seconds(5);
+ time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
+ tp += chrono::seconds(5);
EXPECT_EQ("0", format("%E*f", tp, tz));
EXPECT_EQ("", format("%E0f", tp, tz));
EXPECT_EQ("0", format("%E1f", tp, tz));
@@ -348,7 +357,8 @@ TEST(Format, ExtendedSubeconds) {
EXPECT_EQ("000000000000000", format("%E15f", tp, tz));
// With subseconds.
- tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
+ tp += chrono::milliseconds(6) + chrono::microseconds(7) +
+ chrono::nanoseconds(8);
EXPECT_EQ("006007008", format("%E*f", tp, tz));
EXPECT_EQ("", format("%E0f", tp, tz));
EXPECT_EQ("0", format("%E1f", tp, tz));
@@ -368,17 +378,18 @@ TEST(Format, ExtendedSubeconds) {
EXPECT_EQ("006007008000000", format("%E15f", tp, tz));
// Times before the Unix epoch.
- tp = system_clock::from_time_t(0) + microseconds(-1);
+ tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1);
EXPECT_EQ("1969-12-31 23:59:59.999999",
format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
// Here is a "%E*S" case we got wrong for a while. While the first
// instant below is correctly rendered as "...:07.333304", the second
// one used to appear as "...:07.33330499999999999".
- tp = system_clock::from_time_t(0) + microseconds(1395024427333304);
+ tp = chrono::system_clock::from_time_t(0) +
+ chrono::microseconds(1395024427333304);
EXPECT_EQ("2014-03-17 02:47:07.333304",
format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
- tp += microseconds(1);
+ tp += chrono::microseconds(1);
EXPECT_EQ("2014-03-17 02:47:07.333305",
format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
}
@@ -393,8 +404,8 @@ TEST(Format, CompareExtendSecondsVsSubseconds) {
auto fmt_B = [](const std::string& prec) { return "%S.%E" + prec + "f"; };
// No subseconds:
- time_point<nanoseconds> tp = system_clock::from_time_t(0);
- tp += seconds(5);
+ time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
+ tp += chrono::seconds(5);
// ... %E*S and %S.%E*f are different.
EXPECT_EQ("05", format(fmt_A("*"), tp, tz));
EXPECT_EQ("05.0", format(fmt_B("*"), tp, tz));
@@ -410,7 +421,8 @@ TEST(Format, CompareExtendSecondsVsSubseconds) {
// With subseconds:
// ... %E*S and %S.%E*f are the same.
- tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
+ tp += chrono::milliseconds(6) + chrono::microseconds(7) +
+ chrono::nanoseconds(8);
EXPECT_EQ("05.006007008", format(fmt_A("*"), tp, tz));
EXPECT_EQ("05.006007008", format(fmt_B("*"), tp, tz));
// ... %E0S and %S.%E0f are different.
@@ -425,7 +437,7 @@ TEST(Format, CompareExtendSecondsVsSubseconds) {
}
TEST(Format, ExtendedOffset) {
- auto tp = system_clock::from_time_t(0);
+ auto tp = chrono::system_clock::from_time_t(0);
time_zone tz = utc_time_zone();
TestFormatSpecifier(tp, tz, "%Ez", "+00:00");
@@ -447,30 +459,28 @@ TEST(Format, ExtendedOffset) {
TEST(Format, ExtendedSecondOffset) {
const time_zone utc = utc_time_zone();
- time_point<seconds> tp;
+ time_point<chrono::seconds> tp;
time_zone tz;
EXPECT_TRUE(load_time_zone("America/New_York", &tz));
tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc);
if (tz.lookup(tp).offset == -5 * 60 * 60) {
- // We're likely dealing with zoneinfo that doesn't support really old
- // timestamps, so America/New_York never looks to be on local mean time.
+ // It looks like the tzdata is only 32 bit (probably macOS),
+ // which bottoms out at 1901-12-13T20:45:52+00:00.
} else {
TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02");
TestFormatSpecifier(tp, tz, "%Ez", "-04:56");
}
- tp += seconds(1);
+ tp += chrono::seconds(1);
TestFormatSpecifier(tp, tz, "%E*z", "-05:00:00");
EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz));
tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc);
-#if defined(__ANDROID__) && __ANDROID_API__ < 25
- // Only Android 'N'.1 and beyond have this tz2016g transition.
-#else
- TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
- TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
-#endif
- tp += seconds(1);
+ if (VersionCmp(tz, "2016g") >= 0) {
+ TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
+ TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
+ }
+ tp += chrono::seconds(1);
TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00");
}
@@ -511,44 +521,44 @@ TEST(Format, RFC3339Format) {
time_zone tz;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
- time_point<nanoseconds> tp =
+ time_point<chrono::nanoseconds> tp =
convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += milliseconds(100);
+ tp += chrono::milliseconds(100);
EXPECT_EQ("1977-06-28T09:08:07.1-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += milliseconds(20);
+ tp += chrono::milliseconds(20);
EXPECT_EQ("1977-06-28T09:08:07.12-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += milliseconds(3);
+ tp += chrono::milliseconds(3);
EXPECT_EQ("1977-06-28T09:08:07.123-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += microseconds(400);
+ tp += chrono::microseconds(400);
EXPECT_EQ("1977-06-28T09:08:07.1234-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += microseconds(50);
+ tp += chrono::microseconds(50);
EXPECT_EQ("1977-06-28T09:08:07.12345-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += microseconds(6);
+ tp += chrono::microseconds(6);
EXPECT_EQ("1977-06-28T09:08:07.123456-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += nanoseconds(700);
+ tp += chrono::nanoseconds(700);
EXPECT_EQ("1977-06-28T09:08:07.1234567-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += nanoseconds(80);
+ tp += chrono::nanoseconds(80);
EXPECT_EQ("1977-06-28T09:08:07.12345678-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
- tp += nanoseconds(9);
+ tp += chrono::nanoseconds(9);
EXPECT_EQ("1977-06-28T09:08:07.123456789-07:00",
format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
@@ -571,13 +581,13 @@ TEST(Parse, TimePointResolution) {
const char kFmt[] = "%H:%M:%E*S";
const time_zone utc = utc_time_zone();
- time_point<nanoseconds> tp_ns;
+ time_point<chrono::nanoseconds> tp_ns;
EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_ns));
EXPECT_EQ("03:04:05.123456789", format(kFmt, tp_ns, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ns));
EXPECT_EQ("03:04:05.123456", format(kFmt, tp_ns, utc));
- time_point<microseconds> tp_us;
+ time_point<chrono::microseconds> tp_us;
EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_us));
EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_us));
@@ -585,7 +595,7 @@ TEST(Parse, TimePointResolution) {
EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_us));
EXPECT_EQ("03:04:05.123", format(kFmt, tp_us, utc));
- time_point<milliseconds> tp_ms;
+ time_point<chrono::milliseconds> tp_ms;
EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ms));
EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_ms));
@@ -593,17 +603,17 @@ TEST(Parse, TimePointResolution) {
EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_ms));
EXPECT_EQ("03:04:05", format(kFmt, tp_ms, utc));
- time_point<seconds> tp_s;
+ time_point<chrono::seconds> tp_s;
EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_s));
EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_s));
EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc));
- time_point<minutes> tp_m;
+ time_point<chrono::minutes> tp_m;
EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_m));
EXPECT_EQ("03:04:00", format(kFmt, tp_m, utc));
- time_point<hours> tp_h;
+ time_point<chrono::hours> tp_h;
EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_h));
EXPECT_EQ("03:00:00", format(kFmt, tp_h, utc));
}
@@ -612,7 +622,7 @@ TEST(Parse, TimePointExtendedResolution) {
const char kFmt[] = "%H:%M:%E*S";
const time_zone utc = utc_time_zone();
- time_point<sys_seconds> tp;
+ time_point<absl::time_internal::cctz::seconds> tp;
detail::femtoseconds fs;
EXPECT_TRUE(detail::parse(kFmt, "12:34:56.123456789012345", utc, &tp, &fs));
EXPECT_EQ("12:34:56.123456789012345", detail::format(kFmt, tp, fs, utc));
@@ -630,11 +640,12 @@ TEST(Parse, TimePointExtendedResolution) {
TEST(Parse, Basics) {
time_zone tz = utc_time_zone();
- time_point<nanoseconds> tp = system_clock::from_time_t(1234567890);
+ time_point<chrono::nanoseconds> tp =
+ chrono::system_clock::from_time_t(1234567890);
// Simple edge cases.
EXPECT_TRUE(parse("", "", tz, &tp));
- EXPECT_EQ(system_clock::from_time_t(0), tp); // everything defaulted
+ EXPECT_EQ(chrono::system_clock::from_time_t(0), tp); // everything defaulted
EXPECT_TRUE(parse(" ", " ", tz, &tp));
EXPECT_TRUE(parse(" ", " ", tz, &tp));
EXPECT_TRUE(parse("x", "x", tz, &tp));
@@ -648,7 +659,7 @@ TEST(Parse, Basics) {
TEST(Parse, WithTimeZone) {
time_zone tz;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
- time_point<nanoseconds> tp;
+ time_point<chrono::nanoseconds> tp;
// We can parse a std::string without a UTC offset if we supply a timezone.
EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &tp));
@@ -659,13 +670,13 @@ TEST(Parse, WithTimeZone) {
utc_time_zone(), &tp));
ExpectTime(tp, tz, 2013, 6, 28, 19 - 8 - 7, 8, 9, -7 * 60 * 60, true, "PDT");
- // Check a skipped time (a Spring DST transition). parse() returns
- // the preferred-offset result, as defined for ConvertDateTime().
+ // Check a skipped time (a Spring DST transition). parse() uses the
+ // pre-transition offset.
EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-03-13 02:15:00", tz, &tp));
ExpectTime(tp, tz, 2011, 3, 13, 3, 15, 0, -7 * 60 * 60, true, "PDT");
- // Check a repeated time (a Fall DST transition). parse() returns
- // the preferred-offset result, as defined for ConvertDateTime().
+ // Check a repeated time (a Fall DST transition). parse() uses the
+ // pre-transition offset.
EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-11-06 01:15:00", tz, &tp));
ExpectTime(tp, tz, 2011, 11, 6, 1, 15, 0, -7 * 60 * 60, true, "PDT");
}
@@ -673,7 +684,7 @@ TEST(Parse, WithTimeZone) {
TEST(Parse, LeapSecond) {
time_zone tz;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
- time_point<nanoseconds> tp;
+ time_point<chrono::nanoseconds> tp;
// ":59" -> ":59"
EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59-08:00", tz, &tp));
@@ -697,7 +708,7 @@ TEST(Parse, LeapSecond) {
TEST(Parse, ErrorCases) {
const time_zone tz = utc_time_zone();
- auto tp = system_clock::from_time_t(0);
+ auto tp = chrono::system_clock::from_time_t(0);
// Illegal trailing data.
EXPECT_FALSE(parse("%S", "123", tz, &tp));
@@ -740,7 +751,7 @@ TEST(Parse, ErrorCases) {
TEST(Parse, PosixConversions) {
time_zone tz = utc_time_zone();
- auto tp = system_clock::from_time_t(0);
+ auto tp = chrono::system_clock::from_time_t(0);
const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
tp = reset;
@@ -829,14 +840,14 @@ TEST(Parse, PosixConversions) {
tp = reset;
EXPECT_TRUE(parse("%s", "1234567890", tz, &tp));
- EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
+ EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
// %s conversion, like %z/%Ez, pays no heed to the optional zone.
time_zone lax;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax));
tp = reset;
EXPECT_TRUE(parse("%s", "1234567890", lax, &tp));
- EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
+ EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
// This is most important when the time has the same YMDhms
// breakdown in the zone as some other time. For example, ...
@@ -844,16 +855,16 @@ TEST(Parse, PosixConversions) {
// 1414920600 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PST)
tp = reset;
EXPECT_TRUE(parse("%s", "1414917000", lax, &tp));
- EXPECT_EQ(system_clock::from_time_t(1414917000), tp);
+ EXPECT_EQ(chrono::system_clock::from_time_t(1414917000), tp);
tp = reset;
EXPECT_TRUE(parse("%s", "1414920600", lax, &tp));
- EXPECT_EQ(system_clock::from_time_t(1414920600), tp);
+ EXPECT_EQ(chrono::system_clock::from_time_t(1414920600), tp);
#endif
}
TEST(Parse, LocaleSpecific) {
time_zone tz = utc_time_zone();
- auto tp = system_clock::from_time_t(0);
+ auto tp = chrono::system_clock::from_time_t(0);
const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
// %a is parsed but ignored.
@@ -984,7 +995,8 @@ TEST(Parse, LocaleSpecific) {
TEST(Parse, ExtendedSeconds) {
const time_zone tz = utc_time_zone();
- const time_point<nanoseconds> unix_epoch = system_clock::from_time_t(0);
+ const time_point<chrono::nanoseconds> unix_epoch =
+ chrono::system_clock::from_time_t(0);
// All %E<prec>S cases are treated the same as %E*S on input.
auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7",
@@ -992,47 +1004,47 @@ TEST(Parse, ExtendedSeconds) {
for (const std::string& prec : precisions) {
const std::string fmt = "%E" + prec + "S";
SCOPED_TRACE(fmt);
- time_point<nanoseconds> tp = unix_epoch;
+ time_point<chrono::nanoseconds> tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "5", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.0", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.00", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.6", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.60", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.600", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.67", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(670), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.670", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(670), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.678", tz, &tp));
- EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(678), tp);
+ EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(678), tp);
}
// Here is a "%E*S" case we got wrong for a while. The fractional
// part of the first instant is less than 2^31 and was correctly
// parsed, while the second (and any subsecond field >=2^31) failed.
- time_point<nanoseconds> tp = unix_epoch;
+ time_point<chrono::nanoseconds> tp = unix_epoch;
EXPECT_TRUE(parse("%E*S", "0.2147483647", tz, &tp));
- EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+ EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
tp = unix_epoch;
EXPECT_TRUE(parse("%E*S", "0.2147483648", tz, &tp));
- EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+ EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
// We should also be able to specify long strings of digits far
// beyond the current resolution and have them convert the same way.
@@ -1040,18 +1052,18 @@ TEST(Parse, ExtendedSeconds) {
EXPECT_TRUE(parse(
"%E*S", "0.214748364801234567890123456789012345678901234567890123456789",
tz, &tp));
- EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+ EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
}
TEST(Parse, ExtendedSecondsScan) {
const time_zone tz = utc_time_zone();
- time_point<nanoseconds> tp;
+ time_point<chrono::nanoseconds> tp;
for (int ms = 0; ms < 1000; ms += 111) {
for (int us = 0; us < 1000; us += 27) {
const int micros = ms * 1000 + us;
for (int ns = 0; ns < 1000; ns += 9) {
- const auto expected =
- system_clock::from_time_t(0) + nanoseconds(micros * 1000 + ns);
+ const auto expected = chrono::system_clock::from_time_t(0) +
+ chrono::nanoseconds(micros * 1000 + ns);
std::ostringstream oss;
oss << "0." << std::setfill('0') << std::setw(3);
oss << ms << std::setw(3) << us << std::setw(3) << ns;
@@ -1065,7 +1077,8 @@ TEST(Parse, ExtendedSecondsScan) {
TEST(Parse, ExtendedSubeconds) {
const time_zone tz = utc_time_zone();
- const time_point<nanoseconds> unix_epoch = system_clock::from_time_t(0);
+ const time_point<chrono::nanoseconds> unix_epoch =
+ chrono::system_clock::from_time_t(0);
// All %E<prec>f cases are treated the same as %E*f on input.
auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7",
@@ -1073,41 +1086,42 @@ TEST(Parse, ExtendedSubeconds) {
for (const std::string& prec : precisions) {
const std::string fmt = "%E" + prec + "f";
SCOPED_TRACE(fmt);
- time_point<nanoseconds> tp = unix_epoch - seconds(1);
+ time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1);
EXPECT_TRUE(parse(fmt, "", tz, &tp));
EXPECT_EQ(unix_epoch, tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "6", tz, &tp));
- EXPECT_EQ(unix_epoch + milliseconds(600), tp);
+ EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "60", tz, &tp));
- EXPECT_EQ(unix_epoch + milliseconds(600), tp);
+ EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "600", tz, &tp));
- EXPECT_EQ(unix_epoch + milliseconds(600), tp);
+ EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "67", tz, &tp));
- EXPECT_EQ(unix_epoch + milliseconds(670), tp);
+ EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "670", tz, &tp));
- EXPECT_EQ(unix_epoch + milliseconds(670), tp);
+ EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "678", tz, &tp));
- EXPECT_EQ(unix_epoch + milliseconds(678), tp);
+ EXPECT_EQ(unix_epoch + chrono::milliseconds(678), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "6789", tz, &tp));
- EXPECT_EQ(unix_epoch + milliseconds(678) + microseconds(900), tp);
+ EXPECT_EQ(
+ unix_epoch + chrono::milliseconds(678) + chrono::microseconds(900), tp);
}
// Here is a "%E*f" case we got wrong for a while. The fractional
// part of the first instant is less than 2^31 and was correctly
// parsed, while the second (and any subsecond field >=2^31) failed.
- time_point<nanoseconds> tp = unix_epoch;
+ time_point<chrono::nanoseconds> tp = unix_epoch;
EXPECT_TRUE(parse("%E*f", "2147483647", tz, &tp));
- EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+ EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
tp = unix_epoch;
EXPECT_TRUE(parse("%E*f", "2147483648", tz, &tp));
- EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+ EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
// We should also be able to specify long strings of digits far
// beyond the current resolution and have them convert the same way.
@@ -1115,11 +1129,11 @@ TEST(Parse, ExtendedSubeconds) {
EXPECT_TRUE(parse(
"%E*f", "214748364801234567890123456789012345678901234567890123456789",
tz, &tp));
- EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+ EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
}
TEST(Parse, ExtendedSubecondsScan) {
- time_point<nanoseconds> tp;
+ time_point<chrono::nanoseconds> tp;
const time_zone tz = utc_time_zone();
for (int ms = 0; ms < 1000; ms += 111) {
for (int us = 0; us < 1000; us += 27) {
@@ -1129,14 +1143,14 @@ TEST(Parse, ExtendedSubecondsScan) {
oss << std::setfill('0') << std::setw(3) << ms;
oss << std::setw(3) << us << std::setw(3) << ns;
const std::string nanos = oss.str();
- const auto expected =
- system_clock::from_time_t(0) + nanoseconds(micros * 1000 + ns);
+ const auto expected = chrono::system_clock::from_time_t(0) +
+ chrono::nanoseconds(micros * 1000 + ns);
for (int ps = 0; ps < 1000; ps += 250) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(3) << ps;
const std::string input = nanos + oss.str() + "999";
EXPECT_TRUE(parse("%E*f", input, tz, &tp));
- EXPECT_EQ(expected + nanoseconds(ps) / 1000, tp) << input;
+ EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input;
}
}
}
@@ -1145,7 +1159,7 @@ TEST(Parse, ExtendedSubecondsScan) {
TEST(Parse, ExtendedOffset) {
const time_zone utc = utc_time_zone();
- time_point<sys_seconds> tp;
+ time_point<absl::time_internal::cctz::seconds> tp;
// %z against +-HHMM.
EXPECT_TRUE(parse("%z", "+0000", utc, &tp));
@@ -1195,7 +1209,7 @@ TEST(Parse, ExtendedOffset) {
TEST(Parse, ExtendedSecondOffset) {
const time_zone utc = utc_time_zone();
- time_point<sys_seconds> tp;
+ time_point<absl::time_internal::cctz::seconds> tp;
// %Ez against +-HH:MM:SS.
EXPECT_TRUE(parse("%Ez", "+00:00:00", utc, &tp));
@@ -1264,7 +1278,7 @@ TEST(Parse, ExtendedSecondOffset) {
TEST(Parse, ExtendedYears) {
const time_zone utc = utc_time_zone();
const char e4y_fmt[] = "%E4Y%m%d"; // no separators
- time_point<sys_seconds> tp;
+ time_point<absl::time_internal::cctz::seconds> tp;
// %E4Y consumes exactly four chars, including any sign.
EXPECT_TRUE(parse(e4y_fmt, "-9991127", utc, &tp));
@@ -1295,45 +1309,45 @@ TEST(Parse, ExtendedYears) {
TEST(Parse, RFC3339Format) {
const time_zone tz = utc_time_zone();
- time_point<nanoseconds> tp;
+ time_point<chrono::nanoseconds> tp;
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp));
ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC");
// Check that %Ez also accepts "Z" as a synonym for "+00:00".
- time_point<nanoseconds> tp2;
+ time_point<chrono::nanoseconds> tp2;
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp2));
EXPECT_EQ(tp, tp2);
}
TEST(Parse, MaxRange) {
const time_zone utc = utc_time_zone();
- time_point<sys_seconds> tp;
+ time_point<absl::time_internal::cctz::seconds> tp;
// tests the upper limit using +00:00 offset
EXPECT_TRUE(
parse(RFC3339_sec, "292277026596-12-04T15:30:07+00:00", utc, &tp));
- EXPECT_EQ(tp, time_point<sys_seconds>::max());
+ EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
EXPECT_FALSE(
parse(RFC3339_sec, "292277026596-12-04T15:30:08+00:00", utc, &tp));
// tests the upper limit using -01:00 offset
EXPECT_TRUE(
parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
- EXPECT_EQ(tp, time_point<sys_seconds>::max());
+ EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
EXPECT_FALSE(
parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
// tests the lower limit using +00:00 offset
EXPECT_TRUE(
parse(RFC3339_sec, "-292277022657-01-27T08:29:52+00:00", utc, &tp));
- EXPECT_EQ(tp, time_point<sys_seconds>::min());
+ EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::min());
EXPECT_FALSE(
parse(RFC3339_sec, "-292277022657-01-27T08:29:51+00:00", utc, &tp));
// tests the lower limit using +01:00 offset
EXPECT_TRUE(
parse(RFC3339_sec, "-292277022657-01-27T09:29:52+01:00", utc, &tp));
- EXPECT_EQ(tp, time_point<sys_seconds>::min());
+ EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::min());
EXPECT_FALSE(
parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp));
@@ -1356,11 +1370,11 @@ TEST(FormatParse, RoundTrip) {
time_zone lax;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax));
const auto in = convert(civil_second(1977, 6, 28, 9, 8, 7), lax);
- const auto subseconds = nanoseconds(654321);
+ const auto subseconds = chrono::nanoseconds(654321);
// RFC3339, which renders subseconds.
{
- time_point<nanoseconds> out;
+ time_point<chrono::nanoseconds> out;
const std::string s = format(RFC3339_full, in + subseconds, lax);
EXPECT_TRUE(parse(RFC3339_full, s, lax, &out)) << s;
EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez
@@ -1368,7 +1382,7 @@ TEST(FormatParse, RoundTrip) {
// RFC1123, which only does whole seconds.
{
- time_point<nanoseconds> out;
+ time_point<chrono::nanoseconds> out;
const std::string s = format(RFC1123_full, in, lax);
EXPECT_TRUE(parse(RFC1123_full, s, lax, &out)) << s;
EXPECT_EQ(in, out); // RFC1123_full includes %z
@@ -1381,7 +1395,7 @@ TEST(FormatParse, RoundTrip) {
// Even though we don't know what %c will produce, it should roundtrip,
// but only in the 0-offset timezone.
{
- time_point<nanoseconds> out;
+ time_point<chrono::nanoseconds> out;
time_zone utc = utc_time_zone();
const std::string s = format("%c", in, utc);
EXPECT_TRUE(parse("%c", s, utc, &out)) << s;
@@ -1392,23 +1406,23 @@ TEST(FormatParse, RoundTrip) {
TEST(FormatParse, RoundTripDistantFuture) {
const time_zone utc = utc_time_zone();
- const time_point<sys_seconds> in = time_point<sys_seconds>::max();
+ const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::max();
const std::string s = format(RFC3339_full, in, utc);
- time_point<sys_seconds> out;
+ time_point<absl::time_internal::cctz::seconds> out;
EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
EXPECT_EQ(in, out);
}
TEST(FormatParse, RoundTripDistantPast) {
const time_zone utc = utc_time_zone();
- const time_point<sys_seconds> in = time_point<sys_seconds>::min();
+ const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::min();
const std::string s = format(RFC3339_full, in, utc);
- time_point<sys_seconds> out;
+ time_point<absl::time_internal::cctz::seconds> out;
EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
EXPECT_EQ(in, out);
}
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_if.cc b/absl/time/internal/cctz/src/time_zone_if.cc
index d289a5c9..f7c36b2b 100644
--- a/absl/time/internal/cctz/src/time_zone_if.cc
+++ b/absl/time/internal/cctz/src/time_zone_if.cc
@@ -17,7 +17,7 @@
#include "time_zone_libc.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -39,5 +39,5 @@ TimeZoneIf::~TimeZoneIf() {}
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h
index 2d5f885d..9886f2c5 100644
--- a/absl/time/internal/cctz/src/time_zone_if.h
+++ b/absl/time/internal/cctz/src/time_zone_if.h
@@ -24,7 +24,7 @@
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -38,35 +38,37 @@ class TimeZoneIf {
virtual ~TimeZoneIf();
virtual time_zone::absolute_lookup BreakTime(
- const time_point<sys_seconds>& tp) const = 0;
+ const time_point<seconds>& tp) const = 0;
virtual time_zone::civil_lookup MakeTime(
const civil_second& cs) const = 0;
+ virtual bool NextTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const = 0;
+ virtual bool PrevTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const = 0;
+
+ virtual std::string Version() const = 0;
virtual std::string Description() const = 0;
- virtual bool NextTransition(time_point<sys_seconds>* tp) const = 0;
- virtual bool PrevTransition(time_point<sys_seconds>* tp) const = 0;
protected:
TimeZoneIf() {}
};
-// Convert between time_point<sys_seconds> and a count of seconds since
-// the Unix epoch. We assume that the std::chrono::system_clock and the
+// Convert between time_point<seconds> and a count of seconds since the
+// Unix epoch. We assume that the std::chrono::system_clock and the
// Unix clock are second aligned, but not that they share an epoch.
-inline std::int_fast64_t ToUnixSeconds(const time_point<sys_seconds>& tp) {
- return (tp - std::chrono::time_point_cast<sys_seconds>(
- std::chrono::system_clock::from_time_t(0)))
- .count();
+inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
+ return (tp - std::chrono::time_point_cast<seconds>(
+ std::chrono::system_clock::from_time_t(0))).count();
}
-inline time_point<sys_seconds> FromUnixSeconds(std::int_fast64_t t) {
- return std::chrono::time_point_cast<sys_seconds>(
- std::chrono::system_clock::from_time_t(0)) +
- sys_seconds(t);
+inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) {
+ return std::chrono::time_point_cast<seconds>(
+ std::chrono::system_clock::from_time_t(0)) + seconds(t);
}
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc
index 3ba40ac2..a4e42916 100644
--- a/absl/time/internal/cctz/src/time_zone_impl.cc
+++ b/absl/time/internal/cctz/src/time_zone_impl.cc
@@ -22,7 +22,7 @@
#include "time_zone_fixed.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -46,8 +46,8 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
const time_zone::Impl* const utc_impl = UTCImpl();
// First check for UTC (which is never a key in time_zone_map).
- auto offset = sys_seconds::zero();
- if (FixedOffsetFromName(name, &offset) && offset == sys_seconds::zero()) {
+ auto offset = seconds::zero();
+ if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
*tz = time_zone(utc_impl);
return true;
}
@@ -84,15 +84,6 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
return impl != utc_impl;
}
-const time_zone::Impl& time_zone::Impl::get(const time_zone& tz) {
- if (tz.impl_ == nullptr) {
- // Dereferencing an implicit-UTC time_zone is expected to be
- // rare, so we don't mind paying a small synchronization cost.
- return *UTCImpl();
- }
- return *tz.impl_;
-}
-
void time_zone::Impl::ClearTimeZoneMapTestOnly() {
std::lock_guard<std::mutex> lock(time_zone_mutex);
if (time_zone_map != nullptr) {
@@ -115,5 +106,5 @@ const time_zone::Impl* time_zone::Impl::UTCImpl() {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_impl.h b/absl/time/internal/cctz/src/time_zone_impl.h
index a955e40e..7da2e99d 100644
--- a/absl/time/internal/cctz/src/time_zone_impl.h
+++ b/absl/time/internal/cctz/src/time_zone_impl.h
@@ -24,7 +24,7 @@
#include "time_zone_info.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -38,19 +38,18 @@ class time_zone::Impl {
// some other kind of error occurs. Note that loading "UTC" never fails.
static bool LoadTimeZone(const std::string& name, time_zone* tz);
- // Dereferences the time_zone to obtain its Impl.
- static const time_zone::Impl& get(const time_zone& tz);
-
// Clears the map of cached time zones. Primarily for use in benchmarks
// that gauge the performance of loading/parsing the time-zone data.
static void ClearTimeZoneMapTestOnly();
// The primary key is the time-zone ID (e.g., "America/New_York").
- const std::string& name() const { return name_; }
+ const std::string& Name() const {
+ // TODO: It would nice if the zoneinfo data included the zone name.
+ return name_;
+ }
// Breaks a time_point down to civil-time components in this time zone.
- time_zone::absolute_lookup BreakTime(
- const time_point<sys_seconds>& tp) const {
+ time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const {
return zone_->BreakTime(tp);
}
@@ -61,28 +60,22 @@ class time_zone::Impl {
return zone_->MakeTime(cs);
}
- // Returns an implementation-specific description of this time zone.
- std::string Description() const { return zone_->Description(); }
-
// Finds the time of the next/previous offset change in this time zone.
- //
- // By definition, NextTransition(&tp) returns false when tp has its
- // maximum value, and PrevTransition(&tp) returns false when tp has its
- // mimimum value. If the zone has no transitions, the result will also
- // be false no matter what the argument.
- //
- // Otherwise, when tp has its mimimum value, NextTransition(&tp) returns
- // true and sets tp to the first recorded transition. Chains of calls
- // to NextTransition()/PrevTransition() will eventually return false,
- // but it is unspecified exactly when NextTransition(&tp) jumps to false,
- // or what time is set by PrevTransition(&tp) for a very distant tp.
- bool NextTransition(time_point<sys_seconds>* tp) const {
- return zone_->NextTransition(tp);
+ bool NextTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const {
+ return zone_->NextTransition(tp, trans);
}
- bool PrevTransition(time_point<sys_seconds>* tp) const {
- return zone_->PrevTransition(tp);
+ bool PrevTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const {
+ return zone_->PrevTransition(tp, trans);
}
+ // Returns an implementation-defined version std::string for this time zone.
+ std::string Version() const { return zone_->Version(); }
+
+ // Returns an implementation-defined description of this time zone.
+ std::string Description() const { return zone_->Description(); }
+
private:
explicit Impl(const std::string& name);
static const Impl* UTCImpl();
@@ -93,7 +86,7 @@ class time_zone::Impl {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index 7f5a8fad..e19d1d2d 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -50,7 +50,7 @@
#include "time_zone_posix.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -141,7 +141,7 @@ std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday,
return (days * kSecsPerDay) + pt.time.offset;
}
-inline time_zone::civil_lookup MakeUnique(const time_point<sys_seconds>& tp) {
+inline time_zone::civil_lookup MakeUnique(const time_point<seconds>& tp) {
time_zone::civil_lookup cl;
cl.kind = time_zone::civil_lookup::UNIQUE;
cl.pre = cl.trans = cl.post = tp;
@@ -180,21 +180,20 @@ inline civil_second YearShift(const civil_second& cs, year_t shift) {
} // namespace
// What (no leap-seconds) UTC+seconds zoneinfo would look like.
-bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
+bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
transition_types_.resize(1);
TransitionType& tt(transition_types_.back());
tt.utc_offset = static_cast<std::int_least32_t>(offset.count());
tt.is_dst = false;
tt.abbr_index = 0;
- // We temporarily add some redundant, contemporary (2012 through 2021)
+ // We temporarily add some redundant, contemporary (2013 through 2023)
// transitions for performance reasons. See TimeZoneInfo::LocalTime().
// TODO: Fix the performance issue and remove the extra transitions.
transitions_.clear();
transitions_.reserve(12);
for (const std::int_fast64_t unix_time : {
-(1LL << 59), // BIG_BANG
- 1325376000LL, // 2012-01-01T00:00:00+00:00
1356998400LL, // 2013-01-01T00:00:00+00:00
1388534400LL, // 2014-01-01T00:00:00+00:00
1420070400LL, // 2015-01-01T00:00:00+00:00
@@ -204,6 +203,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
1546300800LL, // 2019-01-01T00:00:00+00:00
1577836800LL, // 2020-01-01T00:00:00+00:00
1609459200LL, // 2021-01-01T00:00:00+00:00
+ 1640995200LL, // 2022-01-01T00:00:00+00:00
+ 1672531200LL, // 2023-01-01T00:00:00+00:00
2147483647LL, // 2^31 - 1
}) {
Transition& tr(*transitions_.emplace(transitions_.end()));
@@ -219,8 +220,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
future_spec_.clear(); // never needed for a fixed-offset zone
extended_ = false;
- tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs;
- tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs;
+ tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
+ tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
transitions_.shrink_to_fit();
return true;
@@ -286,7 +287,7 @@ bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index,
return true;
}
-// Use the POSIX-TZ-environment-variable-style std::string to handle times
+// Use the POSIX-TZ-environment-variable-style string to handle times
// in years after the last transition stored in the zoneinfo data.
void TimeZoneInfo::ExtendTransitions(const std::string& name,
const Header& hdr) {
@@ -520,6 +521,13 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
// We don't check for EOF so that we're forwards compatible.
+ // If we did not find version information during the standard loading
+ // process (as of tzh_version '3' that is unsupported), then ask the
+ // ZoneInfoSource for any out-of-bound version std::string it may be privy to.
+ if (version_.empty()) {
+ version_ = zip->Version();
+ }
+
// Trim redundant transitions. zic may have added these to work around
// differences between the glibc and reference implementations (see
// zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
@@ -566,10 +574,10 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
}
// Compute the maximum/minimum civil times that can be converted to a
- // time_point<sys_seconds> for each of the zone's transition types.
+ // time_point<seconds> for each of the zone's transition types.
for (auto& tt : transition_types_) {
- tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs;
- tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs;
+ tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
+ tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
}
transitions_.shrink_to_fit();
@@ -606,6 +614,10 @@ class FileZoneInfoSource : public ZoneInfoSource {
if (rc == 0) len_ -= offset;
return rc;
}
+ std::string Version() const override {
+ // TODO: It would nice if the zoneinfo data included the tzdb version.
+ return std::string();
+ }
protected:
explicit FileZoneInfoSource(
@@ -655,14 +667,15 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length));
}
-#if defined(__ANDROID__)
class AndroidZoneInfoSource : public FileZoneInfoSource {
public:
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
+ std::string Version() const override { return version_; }
private:
- explicit AndroidZoneInfoSource(FILE* fp, std::size_t len)
- : FileZoneInfoSource(fp, len) {}
+ explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
+ : FileZoneInfoSource(fp, len), version_(vers) {}
+ std::string version_;
};
std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
@@ -670,6 +683,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
// Use of the "file:" prefix is intended for testing purposes only.
if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5));
+#if defined(__ANDROID__)
// See Android's libc/tzcode/bionic.cpp for additional information.
for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
"/system/usr/share/zoneinfo/tzdata"}) {
@@ -679,6 +693,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
char hbuf[24]; // covers header.zonetab_offset too
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
if (strncmp(hbuf, "tzdata", 6) != 0) continue;
+ const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : "";
const std::int_fast32_t index_offset = Decode32(hbuf + 12);
const std::int_fast32_t data_offset = Decode32(hbuf + 16);
if (index_offset < 0 || data_offset < index_offset) continue;
@@ -699,13 +714,13 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
if (strcmp(name.c_str(), ebuf) == 0) {
if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
- fp.release(), static_cast<std::size_t>(length)));
+ fp.release(), static_cast<std::size_t>(length), vers));
}
}
}
+#endif // __ANDROID__
return nullptr;
}
-#endif
} // namespace
@@ -714,7 +729,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
// zone never fails because the simple, fixed-offset state can be
// internally generated. Note that this depends on our choice to not
// accept leap-second encoded ("right") zoneinfo.
- auto offset = sys_seconds::zero();
+ auto offset = seconds::zero();
if (FixedOffsetFromName(name, &offset)) {
return ResetToBuiltinUTC(offset);
}
@@ -723,9 +738,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
auto zip = cctz_extension::zone_info_source_factory(
name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> {
if (auto zip = FileZoneInfoSource::Open(name)) return zip;
-#if defined(__ANDROID__)
if (auto zip = AndroidZoneInfoSource::Open(name)) return zip;
-#endif
return nullptr;
});
return zip != nullptr && Load(name, zip.get());
@@ -756,14 +769,14 @@ time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
year_t c4_shift) const {
assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_);
time_zone::civil_lookup cl = MakeTime(cs);
- if (c4_shift > sys_seconds::max().count() / kSecsPer400Years) {
- cl.pre = cl.trans = cl.post = time_point<sys_seconds>::max();
+ if (c4_shift > seconds::max().count() / kSecsPer400Years) {
+ cl.pre = cl.trans = cl.post = time_point<seconds>::max();
} else {
- const auto offset = sys_seconds(c4_shift * kSecsPer400Years);
- const auto limit = time_point<sys_seconds>::max() - offset;
+ const auto offset = seconds(c4_shift * kSecsPer400Years);
+ const auto limit = time_point<seconds>::max() - offset;
for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) {
if (*tp > limit) {
- *tp = time_point<sys_seconds>::max();
+ *tp = time_point<seconds>::max();
} else {
*tp += offset;
}
@@ -773,7 +786,7 @@ time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
}
time_zone::absolute_lookup TimeZoneInfo::BreakTime(
- const time_point<sys_seconds>& tp) const {
+ const time_point<seconds>& tp) const {
std::int_fast64_t unix_time = ToUnixSeconds(tp);
const std::size_t timecnt = transitions_.size();
assert(timecnt != 0); // We always add a transition.
@@ -789,7 +802,7 @@ time_zone::absolute_lookup TimeZoneInfo::BreakTime(
const std::int_fast64_t diff =
unix_time - transitions_[timecnt - 1].unix_time;
const year_t shift = diff / kSecsPer400Years + 1;
- const auto d = sys_seconds(shift * kSecsPer400Years);
+ const auto d = seconds(shift * kSecsPer400Years);
time_zone::absolute_lookup al = BreakTime(tp - d);
al.cs = YearShift(al.cs, shift * 400);
return al;
@@ -848,7 +861,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
if (tr->prev_civil_sec >= cs) {
// Before first transition, so use the default offset.
const TransitionType& tt(transition_types_[default_transition_type_]);
- if (cs < tt.civil_min) return MakeUnique(time_point<sys_seconds>::min());
+ if (cs < tt.civil_min) return MakeUnique(time_point<seconds>::min());
return MakeUnique(cs - (civil_second() + tt.utc_offset));
}
// tr->prev_civil_sec < cs < tr->civil_sec
@@ -865,7 +878,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
return TimeLocal(YearShift(cs, shift * -400), shift);
}
const TransitionType& tt(transition_types_[tr->type_index]);
- if (cs > tt.civil_max) return MakeUnique(time_point<sys_seconds>::max());
+ if (cs > tt.civil_max) return MakeUnique(time_point<seconds>::max());
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
}
// tr->civil_sec <= cs <= tr->prev_civil_sec
@@ -886,17 +899,20 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
}
+std::string TimeZoneInfo::Version() const {
+ return version_;
+}
+
std::string TimeZoneInfo::Description() const {
std::ostringstream oss;
- // TODO: It would nice if the zoneinfo data included the zone name.
- // TODO: It would nice if the zoneinfo data included the tzdb version.
oss << "#trans=" << transitions_.size();
oss << " #types=" << transition_types_.size();
oss << " spec='" << future_spec_ << "'";
return oss.str();
}
-bool TimeZoneInfo::NextTransition(time_point<sys_seconds>* tp) const {
+bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const {
if (transitions_.empty()) return false;
const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size();
@@ -905,22 +921,24 @@ bool TimeZoneInfo::NextTransition(time_point<sys_seconds>* tp) const {
// really a sentinel, not a transition. See tz/zic.c.
++begin;
}
- std::int_fast64_t unix_time = ToUnixSeconds(*tp);
+ std::int_fast64_t unix_time = ToUnixSeconds(tp);
const Transition target = { unix_time };
const Transition* tr = std::upper_bound(begin, end, target,
Transition::ByUnixTime());
- if (tr != begin) { // skip no-op transitions
- for (; tr != end; ++tr) {
- if (!EquivTransitions(tr[-1].type_index, tr[0].type_index)) break;
- }
+ for (; tr != end; ++tr) { // skip no-op transitions
+ std::uint_fast8_t prev_type_index =
+ (tr == begin) ? default_transition_type_ : tr[-1].type_index;
+ if (!EquivTransitions(prev_type_index, tr[0].type_index)) break;
}
// When tr == end we return false, ignoring future_spec_.
if (tr == end) return false;
- *tp = FromUnixSeconds(tr->unix_time);
+ trans->from = tr->prev_civil_sec + 1;
+ trans->to = tr->civil_sec;
return true;
}
-bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const {
+bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const {
if (transitions_.empty()) return false;
const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size();
@@ -929,11 +947,12 @@ bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const {
// really a sentinel, not a transition. See tz/zic.c.
++begin;
}
- std::int_fast64_t unix_time = ToUnixSeconds(*tp);
- if (FromUnixSeconds(unix_time) != *tp) {
+ std::int_fast64_t unix_time = ToUnixSeconds(tp);
+ if (FromUnixSeconds(unix_time) != tp) {
if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) {
if (end == begin) return false; // Ignore future_spec_.
- *tp = FromUnixSeconds((--end)->unix_time);
+ trans->from = (--end)->prev_civil_sec + 1;
+ trans->to = end->civil_sec;
return true;
}
unix_time += 1; // ceils
@@ -941,18 +960,19 @@ bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const {
const Transition target = { unix_time };
const Transition* tr = std::lower_bound(begin, end, target,
Transition::ByUnixTime());
- if (tr != begin) { // skip no-op transitions
- for (; tr - 1 != begin; --tr) {
- if (!EquivTransitions(tr[-2].type_index, tr[-1].type_index)) break;
- }
+ for (; tr != begin; --tr) { // skip no-op transitions
+ std::uint_fast8_t prev_type_index =
+ (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index;
+ if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break;
}
// When tr == end we return the "last" transition, ignoring future_spec_.
if (tr == begin) return false;
- *tp = FromUnixSeconds((--tr)->unix_time);
+ trans->from = (--tr)->prev_civil_sec + 1;
+ trans->to = tr->civil_sec;
return true;
}
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_info.h b/absl/time/internal/cctz/src/time_zone_info.h
index 5abf2811..e7a7d02f 100644
--- a/absl/time/internal/cctz/src/time_zone_info.h
+++ b/absl/time/internal/cctz/src/time_zone_info.h
@@ -28,7 +28,7 @@
#include "tzfile.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -72,12 +72,15 @@ class TimeZoneInfo : public TimeZoneIf {
// TimeZoneIf implementations.
time_zone::absolute_lookup BreakTime(
- const time_point<sys_seconds>& tp) const override;
+ const time_point<seconds>& tp) const override;
time_zone::civil_lookup MakeTime(
const civil_second& cs) const override;
+ bool NextTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const override;
+ bool PrevTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const override;
+ std::string Version() const override;
std::string Description() const override;
- bool NextTransition(time_point<sys_seconds>* tp) const override;
- bool PrevTransition(time_point<sys_seconds>* tp) const override;
private:
struct Header { // counts of:
@@ -99,7 +102,7 @@ class TimeZoneInfo : public TimeZoneIf {
std::uint_fast8_t tt2_index) const;
void ExtendTransitions(const std::string& name, const Header& hdr);
- bool ResetToBuiltinUTC(const sys_seconds& offset);
+ bool ResetToBuiltinUTC(const seconds& offset);
bool Load(const std::string& name, ZoneInfoSource* zip);
// Helpers for BreakTime() and MakeTime().
@@ -115,6 +118,7 @@ class TimeZoneInfo : public TimeZoneIf {
std::uint_fast8_t default_transition_type_; // for before first transition
std::string abbreviations_; // all the NUL-terminated abbreviations
+ std::string version_; // the tzdata version if available
std::string future_spec_; // for after the last zic transition
bool extended_; // future_spec_ was used to generate transitions
year_t last_year_; // the final year of the generated transitions
@@ -128,7 +132,7 @@ class TimeZoneInfo : public TimeZoneIf {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc
index 32a7a927..3dd75b50 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -20,6 +20,7 @@
#include <chrono>
#include <ctime>
+#include <limits>
#include <tuple>
#include <utility>
@@ -27,7 +28,7 @@
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -86,73 +87,206 @@ OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr,
#endif // !defined(__tm_gmtoff) && !defined(__tm_zone)
#endif
+inline std::tm* gm_time(const std::time_t *timep, std::tm *result) {
+#if defined(_WIN32) || defined(_WIN64)
+ return gmtime_s(result, timep) ? nullptr : result;
+#else
+ return gmtime_r(timep, result);
+#endif
+}
+
+inline std::tm* local_time(const std::time_t *timep, std::tm *result) {
+#if defined(_WIN32) || defined(_WIN64)
+ return localtime_s(result, timep) ? nullptr : result;
+#else
+ return localtime_r(timep, result);
+#endif
+}
+
+// Converts a civil second and "dst" flag into a time_t and UTC offset.
+// Returns false if time_t cannot represent the requested civil second.
+// Caller must have already checked that cs.year() will fit into a tm_year.
+bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
+ std::tm tm;
+ tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
+ tm.tm_mon = cs.month() - 1;
+ tm.tm_mday = cs.day();
+ tm.tm_hour = cs.hour();
+ tm.tm_min = cs.minute();
+ tm.tm_sec = cs.second();
+ tm.tm_isdst = is_dst;
+ *t = std::mktime(&tm);
+ if (*t == std::time_t{-1}) {
+ std::tm tm2;
+ const std::tm* tmp = local_time(t, &tm2);
+ if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
+ tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
+ tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
+ tmp->tm_sec != tm.tm_sec) {
+ // A true error (not just one second before the epoch).
+ return false;
+ }
+ }
+ *off = get_offset_abbr(tm).first;
+ return true;
+}
+
+// Find the least time_t in [lo:hi] where local time matches offset, given:
+// (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
+std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
+ std::tm tm;
+ while (lo + 1 != hi) {
+ const std::time_t mid = lo + (hi - lo) / 2;
+ if (std::tm* tmp = local_time(&mid, &tm)) {
+ if (get_offset_abbr(*tmp).first == offset) {
+ hi = mid;
+ } else {
+ lo = mid;
+ }
+ } else {
+ // If std::tm cannot hold some result we resort to a linear search,
+ // ignoring all failed conversions. Slow, but never really happens.
+ while (++lo != hi) {
+ if (std::tm* tmp = local_time(&lo, &tm)) {
+ if (get_offset_abbr(*tmp).first == offset) break;
+ }
+ }
+ return lo;
+ }
+ }
+ return hi;
+}
+
} // namespace
TimeZoneLibC::TimeZoneLibC(const std::string& name)
: local_(name == "localtime") {}
time_zone::absolute_lookup TimeZoneLibC::BreakTime(
- const time_point<sys_seconds>& tp) const {
+ const time_point<seconds>& tp) const {
time_zone::absolute_lookup al;
- std::time_t t = ToUnixSeconds(tp);
+ al.offset = 0;
+ al.is_dst = false;
+ al.abbr = "-00";
+
+ const std::int_fast64_t s = ToUnixSeconds(tp);
+
+ // If std::time_t cannot hold the input we saturate the output.
+ if (s < std::numeric_limits<std::time_t>::min()) {
+ al.cs = civil_second::min();
+ return al;
+ }
+ if (s > std::numeric_limits<std::time_t>::max()) {
+ al.cs = civil_second::max();
+ return al;
+ }
+
+ const std::time_t t = static_cast<std::time_t>(s);
std::tm tm;
- if (local_) {
-#if defined(_WIN32) || defined(_WIN64)
- localtime_s(&tm, &t);
-#else
- localtime_r(&t, &tm);
-#endif
- std::tie(al.offset, al.abbr) = get_offset_abbr(tm);
- } else {
-#if defined(_WIN32) || defined(_WIN64)
- gmtime_s(&tm, &t);
-#else
- gmtime_r(&t, &tm);
-#endif
- al.offset = 0;
- al.abbr = "UTC";
+ std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
+
+ // If std::tm cannot hold the result we saturate the output.
+ if (tmp == nullptr) {
+ al.cs = (s < 0) ? civil_second::min() : civil_second::max();
+ return al;
}
- al.cs = civil_second(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- al.is_dst = tm.tm_isdst > 0;
+
+ const year_t year = tmp->tm_year + year_t{1900};
+ al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ std::tie(al.offset, al.abbr) = get_offset_abbr(*tmp);
+ if (!local_) al.abbr = "UTC"; // as expected by cctz
+ al.is_dst = tmp->tm_isdst > 0;
return al;
}
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
- time_zone::civil_lookup cl;
- std::time_t t;
- if (local_) {
- // Does not handle SKIPPED/AMBIGUOUS or huge years.
- std::tm tm;
- tm.tm_year = static_cast<int>(cs.year() - 1900);
- tm.tm_mon = cs.month() - 1;
- tm.tm_mday = cs.day();
- tm.tm_hour = cs.hour();
- tm.tm_min = cs.minute();
- tm.tm_sec = cs.second();
- tm.tm_isdst = -1;
- t = std::mktime(&tm);
+ if (!local_) {
+ // If time_point<seconds> cannot hold the result we saturate.
+ static const civil_second min_tp_cs =
+ civil_second() + ToUnixSeconds(time_point<seconds>::min());
+ static const civil_second max_tp_cs =
+ civil_second() + ToUnixSeconds(time_point<seconds>::max());
+ const time_point<seconds> tp =
+ (cs < min_tp_cs)
+ ? time_point<seconds>::min()
+ : (cs > max_tp_cs) ? time_point<seconds>::max()
+ : FromUnixSeconds(cs - civil_second());
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
+ }
+
+ // If tm_year cannot hold the requested year we saturate the result.
+ if (cs.year() < 0) {
+ if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
+ const time_point<seconds> tp = time_point<seconds>::min();
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
+ }
} else {
- t = cs - civil_second();
+ if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
+ const time_point<seconds> tp = time_point<seconds>::max();
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
+ }
}
- cl.kind = time_zone::civil_lookup::UNIQUE;
- cl.pre = cl.trans = cl.post = FromUnixSeconds(t);
- return cl;
-}
-std::string TimeZoneLibC::Description() const {
- return local_ ? "localtime" : "UTC";
+ // We probe with "is_dst" values of 0 and 1 to try to distinguish unique
+ // civil seconds from skipped or repeated ones. This is not always possible
+ // however, as the "dst" flag does not change over some offset transitions.
+ // We are also subject to the vagaries of mktime() implementations.
+ std::time_t t0, t1;
+ int offset0, offset1;
+ if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
+ if (t0 == t1) {
+ // The civil time was singular (pre == trans == post).
+ const time_point<seconds> tp = FromUnixSeconds(t0);
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
+ }
+
+ if (t0 > t1) {
+ std::swap(t0, t1);
+ std::swap(offset0, offset1);
+ }
+ const std::time_t tt = find_trans(t0, t1, offset1);
+ const time_point<seconds> trans = FromUnixSeconds(tt);
+
+ if (offset0 < offset1) {
+ // The civil time did not exist (pre >= trans > post).
+ const time_point<seconds> pre = FromUnixSeconds(t1);
+ const time_point<seconds> post = FromUnixSeconds(t0);
+ return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
+ }
+
+ // The civil time was ambiguous (pre < trans <= post).
+ const time_point<seconds> pre = FromUnixSeconds(t0);
+ const time_point<seconds> post = FromUnixSeconds(t1);
+ return {time_zone::civil_lookup::REPEATED, pre, trans, post};
+ }
+
+ // make_time() failed somehow so we saturate the result.
+ const time_point<seconds> tp = (cs < civil_second())
+ ? time_point<seconds>::min()
+ : time_point<seconds>::max();
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
}
-bool TimeZoneLibC::NextTransition(time_point<sys_seconds>* tp) const {
+bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const {
return false;
}
-bool TimeZoneLibC::PrevTransition(time_point<sys_seconds>* tp) const {
+bool TimeZoneLibC::PrevTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const {
return false;
}
+std::string TimeZoneLibC::Version() const {
+ return std::string(); // unknown
+}
+
+std::string TimeZoneLibC::Description() const {
+ return local_ ? "localtime" : "UTC";
+}
+
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_libc.h b/absl/time/internal/cctz/src/time_zone_libc.h
index 0856b200..67372d45 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.h
+++ b/absl/time/internal/cctz/src/time_zone_libc.h
@@ -20,7 +20,7 @@
#include "time_zone_if.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -33,12 +33,15 @@ class TimeZoneLibC : public TimeZoneIf {
// TimeZoneIf implementations.
time_zone::absolute_lookup BreakTime(
- const time_point<sys_seconds>& tp) const override;
+ const time_point<seconds>& tp) const override;
time_zone::civil_lookup MakeTime(
const civil_second& cs) const override;
+ bool NextTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const override;
+ bool PrevTransition(const time_point<seconds>& tp,
+ time_zone::civil_transition* trans) const override;
+ std::string Version() const override;
std::string Description() const override;
- bool NextTransition(time_point<sys_seconds>* tp) const override;
- bool PrevTransition(time_point<sys_seconds>* tp) const override;
private:
const bool local_; // localtime or UTC
@@ -46,7 +49,7 @@ class TimeZoneLibC : public TimeZoneIf {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index 18d1565f..711d705a 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -28,7 +28,7 @@
#include "time_zone_impl.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -62,20 +62,43 @@ int __system_property_get(const char* name, char* value) {
#endif
std::string time_zone::name() const {
- return time_zone::Impl::get(*this).name();
+ return effective_impl().Name();
}
time_zone::absolute_lookup time_zone::lookup(
- const time_point<sys_seconds>& tp) const {
- return time_zone::Impl::get(*this).BreakTime(tp);
+ const time_point<seconds>& tp) const {
+ return effective_impl().BreakTime(tp);
}
time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
- return time_zone::Impl::get(*this).MakeTime(cs);
+ return effective_impl().MakeTime(cs);
}
-bool operator==(time_zone lhs, time_zone rhs) {
- return &time_zone::Impl::get(lhs) == &time_zone::Impl::get(rhs);
+bool time_zone::next_transition(const time_point<seconds>& tp,
+ civil_transition* trans) const {
+ return effective_impl().NextTransition(tp, trans);
+}
+
+bool time_zone::prev_transition(const time_point<seconds>& tp,
+ civil_transition* trans) const {
+ return effective_impl().PrevTransition(tp, trans);
+}
+
+std::string time_zone::version() const {
+ return effective_impl().Version();
+}
+
+std::string time_zone::description() const {
+ return effective_impl().Description();
+}
+
+const time_zone::Impl& time_zone::effective_impl() const {
+ if (impl_ == nullptr) {
+ // Dereferencing an implicit-UTC time_zone is expected to be
+ // rare, so we don't mind paying a small synchronization cost.
+ return *time_zone::Impl::UTC().impl_;
+ }
+ return *impl_;
}
bool load_time_zone(const std::string& name, time_zone* tz) {
@@ -86,7 +109,7 @@ time_zone utc_time_zone() {
return time_zone::Impl::UTC(); // avoid name lookup
}
-time_zone fixed_time_zone(const sys_seconds& offset) {
+time_zone fixed_time_zone(const seconds& offset) {
time_zone tz;
load_time_zone(FixedOffsetToName(offset), &tz);
return tz;
@@ -143,5 +166,5 @@ time_zone local_time_zone() {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index a1b6c687..e0e355ee 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -16,7 +16,9 @@
#include <chrono>
#include <cstddef>
+#include <cstdlib>
#include <future>
+#include <limits>
#include <string>
#include <thread>
#include <vector>
@@ -24,17 +26,10 @@
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "gtest/gtest.h"
-using std::chrono::time_point_cast;
-using std::chrono::system_clock;
-using std::chrono::nanoseconds;
-using std::chrono::microseconds;
-using std::chrono::milliseconds;
-using std::chrono::seconds;
-using std::chrono::minutes;
-using std::chrono::hours;
+namespace chrono = std::chrono;
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -659,6 +654,17 @@ time_zone LoadZone(const std::string& name) {
/* EXPECT_STREQ(zone, al.abbr); */ \
} while (0)
+// These tests sometimes run on platforms that have zoneinfo data so old
+// that the transition we are attempting to check does not exist, most
+// notably Android emulators. Fortunately, AndroidZoneInfoSource supports
+// time_zone::version() so, in cases where we've learned that it matters,
+// we can make the check conditionally.
+int VersionCmp(time_zone tz, const std::string& target) {
+ std::string version = tz.version();
+ if (version.empty() && !target.empty()) return 1; // unknown > known
+ return version.compare(target);
+}
+
} // namespace
TEST(TimeZones, LoadZonesConcurrently) {
@@ -716,13 +722,13 @@ TEST(TimeZone, NamedTimeZones) {
EXPECT_EQ("America/New_York", nyc.name());
const time_zone syd = LoadZone("Australia/Sydney");
EXPECT_EQ("Australia/Sydney", syd.name());
- const time_zone fixed0 = fixed_time_zone(sys_seconds::zero());
+ const time_zone fixed0 = fixed_time_zone(absl::time_internal::cctz::seconds::zero());
EXPECT_EQ("UTC", fixed0.name());
- const time_zone fixed_pos =
- fixed_time_zone(hours(3) + minutes(25) + seconds(45));
+ const time_zone fixed_pos = fixed_time_zone(
+ chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45));
EXPECT_EQ("Fixed/UTC+03:25:45", fixed_pos.name());
- const time_zone fixed_neg =
- fixed_time_zone(-(hours(12) + minutes(34) + seconds(56)));
+ const time_zone fixed_neg = fixed_time_zone(
+ -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56)));
EXPECT_EQ("Fixed/UTC-12:34:56", fixed_neg.name());
}
@@ -732,19 +738,19 @@ TEST(TimeZone, Failures) {
tz = LoadZone("America/Los_Angeles");
EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz));
- EXPECT_EQ(system_clock::from_time_t(0),
+ EXPECT_EQ(chrono::system_clock::from_time_t(0),
convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC
// Ensures that the load still fails on a subsequent attempt.
tz = LoadZone("America/Los_Angeles");
EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz));
- EXPECT_EQ(system_clock::from_time_t(0),
+ EXPECT_EQ(chrono::system_clock::from_time_t(0),
convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC
// Loading an empty std::string timezone should fail.
tz = LoadZone("America/Los_Angeles");
EXPECT_FALSE(load_time_zone("", &tz));
- EXPECT_EQ(system_clock::from_time_t(0),
+ EXPECT_EQ(chrono::system_clock::from_time_t(0),
convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC
}
@@ -759,7 +765,7 @@ TEST(TimeZone, Equality) {
EXPECT_EQ(implicit_utc, explicit_utc);
EXPECT_EQ(implicit_utc.name(), explicit_utc.name());
- const time_zone fixed_zero = fixed_time_zone(sys_seconds::zero());
+ const time_zone fixed_zero = fixed_time_zone(absl::time_internal::cctz::seconds::zero());
EXPECT_EQ(fixed_zero, LoadZone(fixed_zero.name()));
EXPECT_EQ(fixed_zero, explicit_utc);
@@ -767,23 +773,25 @@ TEST(TimeZone, Equality) {
EXPECT_EQ(fixed_utc, LoadZone(fixed_utc.name()));
EXPECT_EQ(fixed_utc, explicit_utc);
- const time_zone fixed_pos =
- fixed_time_zone(hours(3) + minutes(25) + seconds(45));
+ const time_zone fixed_pos = fixed_time_zone(
+ chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45));
EXPECT_EQ(fixed_pos, LoadZone(fixed_pos.name()));
EXPECT_NE(fixed_pos, explicit_utc);
- const time_zone fixed_neg =
- fixed_time_zone(-(hours(12) + minutes(34) + seconds(56)));
+ const time_zone fixed_neg = fixed_time_zone(
+ -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56)));
EXPECT_EQ(fixed_neg, LoadZone(fixed_neg.name()));
EXPECT_NE(fixed_neg, explicit_utc);
- const time_zone fixed_lim = fixed_time_zone(hours(24));
+ const time_zone fixed_lim = fixed_time_zone(chrono::hours(24));
EXPECT_EQ(fixed_lim, LoadZone(fixed_lim.name()));
EXPECT_NE(fixed_lim, explicit_utc);
- const time_zone fixed_ovfl = fixed_time_zone(hours(24) + seconds(1));
+ const time_zone fixed_ovfl =
+ fixed_time_zone(chrono::hours(24) + chrono::seconds(1));
EXPECT_EQ(fixed_ovfl, LoadZone(fixed_ovfl.name()));
EXPECT_EQ(fixed_ovfl, explicit_utc);
- EXPECT_EQ(fixed_time_zone(seconds(1)), fixed_time_zone(seconds(1)));
+ EXPECT_EQ(fixed_time_zone(chrono::seconds(1)),
+ fixed_time_zone(chrono::seconds(1)));
const time_zone local = local_time_zone();
EXPECT_EQ(local, LoadZone(local.name()));
@@ -796,40 +804,43 @@ TEST(TimeZone, Equality) {
TEST(StdChronoTimePoint, TimeTAlignment) {
// Ensures that the Unix epoch and the system clock epoch are an integral
// number of seconds apart. This simplifies conversions to/from time_t.
- auto diff = system_clock::time_point() - system_clock::from_time_t(0);
- EXPECT_EQ(system_clock::time_point::duration::zero(), diff % seconds(1));
+ auto diff = chrono::system_clock::time_point() -
+ chrono::system_clock::from_time_t(0);
+ EXPECT_EQ(chrono::system_clock::time_point::duration::zero(),
+ diff % chrono::seconds(1));
}
TEST(BreakTime, TimePointResolution) {
const time_zone utc = utc_time_zone();
- const auto t0 = system_clock::from_time_t(0);
+ const auto t0 = chrono::system_clock::from_time_t(0);
- ExpectTime(time_point_cast<nanoseconds>(t0), utc,
+ ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(time_point_cast<microseconds>(t0), utc,
+ ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(time_point_cast<milliseconds>(t0), utc,
+ ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(time_point_cast<seconds>(t0), utc,
+ ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(time_point_cast<sys_seconds>(t0), utc,
+ ExpectTime(chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(time_point_cast<minutes>(t0), utc,
+ ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(time_point_cast<hours>(t0), utc,
+ ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
}
TEST(BreakTime, LocalTimeInUTC) {
const time_zone tz = utc_time_zone();
- const auto tp = system_clock::from_time_t(0);
+ const auto tp = chrono::system_clock::from_time_t(0);
ExpectTime(tp, tz, 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(BreakTime, LocalTimeInUTCUnaligned) {
const time_zone tz = utc_time_zone();
- const auto tp = system_clock::from_time_t(0) - milliseconds(500);
+ const auto tp =
+ chrono::system_clock::from_time_t(0) - chrono::milliseconds(500);
ExpectTime(tp, tz, 1969, 12, 31, 23, 59, 59, 0, false, "UTC");
EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
}
@@ -837,15 +848,16 @@ TEST(BreakTime, LocalTimeInUTCUnaligned) {
TEST(BreakTime, LocalTimePosix) {
// See IEEE Std 1003.1-1988 B.2.3 General Terms, Epoch.
const time_zone tz = utc_time_zone();
- const auto tp = system_clock::from_time_t(536457599);
+ const auto tp = chrono::system_clock::from_time_t(536457599);
ExpectTime(tp, tz, 1986, 12, 31, 23, 59, 59, 0, false, "UTC");
EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(TimeZoneImpl, LocalTimeInFixed) {
- const sys_seconds offset = -(hours(8) + minutes(33) + seconds(47));
+ const absl::time_internal::cctz::seconds offset =
+ -(chrono::hours(8) + chrono::minutes(33) + chrono::seconds(47));
const time_zone tz = fixed_time_zone(offset);
- const auto tp = system_clock::from_time_t(0);
+ const auto tp = chrono::system_clock::from_time_t(0);
ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false,
"-083347");
EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
@@ -853,52 +865,52 @@ TEST(TimeZoneImpl, LocalTimeInFixed) {
TEST(BreakTime, LocalTimeInNewYork) {
const time_zone tz = LoadZone("America/New_York");
- const auto tp = system_clock::from_time_t(45);
+ const auto tp = chrono::system_clock::from_time_t(45);
ExpectTime(tp, tz, 1969, 12, 31, 19, 0, 45, -5 * 60 * 60, false, "EST");
EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(BreakTime, LocalTimeInMTV) {
const time_zone tz = LoadZone("America/Los_Angeles");
- const auto tp = system_clock::from_time_t(1380855729);
+ const auto tp = chrono::system_clock::from_time_t(1380855729);
ExpectTime(tp, tz, 2013, 10, 3, 20, 2, 9, -7 * 60 * 60, true, "PDT");
EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(BreakTime, LocalTimeInSydney) {
const time_zone tz = LoadZone("Australia/Sydney");
- const auto tp = system_clock::from_time_t(90);
+ const auto tp = chrono::system_clock::from_time_t(90);
ExpectTime(tp, tz, 1970, 1, 1, 10, 1, 30, 10 * 60 * 60, false, "AEST");
EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(MakeTime, TimePointResolution) {
const time_zone utc = utc_time_zone();
- const time_point<nanoseconds> tp_ns =
+ const time_point<chrono::nanoseconds> tp_ns =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_ns, utc));
- const time_point<microseconds> tp_us =
+ const time_point<chrono::microseconds> tp_us =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_us, utc));
- const time_point<milliseconds> tp_ms =
+ const time_point<chrono::milliseconds> tp_ms =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_ms, utc));
- const time_point<seconds> tp_s =
+ const time_point<chrono::seconds> tp_s =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_s, utc));
- const time_point<sys_seconds> tp_s64 =
+ const time_point<absl::time_internal::cctz::seconds> tp_s64 =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_s64, utc));
- // These next two require time_point_cast because the conversion from a
- // resolution of seconds (the return value of convert()) to a coarser
- // resolution requires an explicit cast.
- const time_point<minutes> tp_m =
- time_point_cast<minutes>(
+ // These next two require chrono::time_point_cast because the conversion
+ // from a resolution of seconds (the return value of convert()) to a
+ // coarser resolution requires an explicit cast.
+ const time_point<chrono::minutes> tp_m =
+ chrono::time_point_cast<chrono::minutes>(
convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc));
- const time_point<hours> tp_h =
- time_point_cast<hours>(
+ const time_point<chrono::hours> tp_h =
+ chrono::time_point_cast<chrono::hours>(
convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc));
}
@@ -906,7 +918,7 @@ TEST(MakeTime, TimePointResolution) {
TEST(MakeTime, Normalization) {
const time_zone tz = LoadZone("America/New_York");
const auto tp = convert(civil_second(2009, 2, 13, 18, 31, 30), tz);
- EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
+ EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
// Now requests for the same time_point but with out-of-range fields.
EXPECT_EQ(tp, convert(civil_second(2008, 14, 13, 18, 31, 30), tz)); // month
@@ -916,71 +928,234 @@ TEST(MakeTime, Normalization) {
EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 18, 30, 90), tz)); // second
}
-// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
+// NOTE: Run this with -ftrapv to detect overflow problems.
TEST(MakeTime, SysSecondsLimits) {
const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez";
const time_zone utc = utc_time_zone();
- const time_zone east = fixed_time_zone(hours(14));
- const time_zone west = fixed_time_zone(-hours(14));
- time_point<sys_seconds> tp;
+ const time_zone east = fixed_time_zone(chrono::hours(14));
+ const time_zone west = fixed_time_zone(-chrono::hours(14));
+ time_point<absl::time_internal::cctz::seconds> tp;
- // Approach the maximal time_point<sys_seconds> value from below.
+ // Approach the maximal time_point<cctz::seconds> value from below.
tp = convert(civil_second(292277026596, 12, 4, 15, 30, 6), utc);
EXPECT_EQ("292277026596-12-04T15:30:06+00:00", format(RFC3339, tp, utc));
tp = convert(civil_second(292277026596, 12, 4, 15, 30, 7), utc);
EXPECT_EQ("292277026596-12-04T15:30:07+00:00", format(RFC3339, tp, utc));
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
tp = convert(civil_second(292277026596, 12, 4, 15, 30, 8), utc);
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
tp = convert(civil_second::max(), utc);
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
// Checks that we can also get the maximal value for a far-east zone.
tp = convert(civil_second(292277026596, 12, 5, 5, 30, 7), east);
EXPECT_EQ("292277026596-12-05T05:30:07+14:00", format(RFC3339, tp, east));
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
tp = convert(civil_second(292277026596, 12, 5, 5, 30, 8), east);
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
tp = convert(civil_second::max(), east);
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
// Checks that we can also get the maximal value for a far-west zone.
tp = convert(civil_second(292277026596, 12, 4, 1, 30, 7), west);
EXPECT_EQ("292277026596-12-04T01:30:07-14:00", format(RFC3339, tp, west));
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
tp = convert(civil_second(292277026596, 12, 4, 7, 30, 8), west);
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
tp = convert(civil_second::max(), west);
- EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
- // Approach the minimal time_point<sys_seconds> value from above.
+ // Approach the minimal time_point<cctz::seconds> value from above.
tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 53), utc);
EXPECT_EQ("-292277022657-01-27T08:29:53+00:00", format(RFC3339, tp, utc));
tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 52), utc);
EXPECT_EQ("-292277022657-01-27T08:29:52+00:00", format(RFC3339, tp, utc));
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 51), utc);
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
tp = convert(civil_second::min(), utc);
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
// Checks that we can also get the minimal value for a far-east zone.
tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 52), east);
EXPECT_EQ("-292277022657-01-27T22:29:52+14:00", format(RFC3339, tp, east));
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 51), east);
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
tp = convert(civil_second::min(), east);
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
// Checks that we can also get the minimal value for a far-west zone.
tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 52), west);
EXPECT_EQ("-292277022657-01-26T18:29:52-14:00", format(RFC3339, tp, west));
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 51), west);
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
tp = convert(civil_second::min(), west);
- EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+ EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
+
+ // Some similar checks for the "libc" time-zone implementation.
+ if (sizeof(std::time_t) >= 8) {
+ // Checks that "tm_year + 1900", as used by the "libc" implementation,
+ // can produce year values beyond the range on an int without overflow.
+#if defined(_WIN32) || defined(_WIN64)
+ // localtime_s() and gmtime_s() don't believe in years outside [1970:3000].
+#else
+ const time_zone utc = LoadZone("libc:UTC");
+ const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900;
+ tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), utc);
+ EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, utc));
+ const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
+ tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), utc);
+ EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, utc));
+#endif
+ }
+}
+
+TEST(MakeTime, LocalTimeLibC) {
+ // Checks that cctz and libc agree on transition points in [1970:2037].
+ //
+ // We limit this test case to environments where:
+ // 1) we know how to change the time zone used by localtime()/mktime(),
+ // 2) cctz and localtime()/mktime() will use similar-enough tzdata, and
+ // 3) we have some idea about how mktime() behaves during transitions.
+#if defined(__linux__)
+ const char* const ep = getenv("TZ");
+ std::string tz_name = (ep != nullptr) ? ep : "";
+ for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) {
+ ASSERT_EQ(0, setenv("TZ", *np, 1)); // change what "localtime" means
+ const auto zi = local_time_zone();
+ const auto lc = LoadZone("libc:localtime");
+ time_zone::civil_transition trans;
+ for (auto tp = zi.lookup(civil_second()).trans;
+ zi.next_transition(tp, &trans);
+ tp = zi.lookup(trans.to).trans) {
+ const auto fcl = zi.lookup(trans.from);
+ const auto tcl = zi.lookup(trans.to);
+ civil_second cs; // compare cs in zi and lc
+ if (fcl.kind == time_zone::civil_lookup::UNIQUE) {
+ if (tcl.kind == time_zone::civil_lookup::UNIQUE) {
+ // Both unique; must be an is_dst or abbr change.
+ ASSERT_EQ(trans.from, trans.to);
+ const auto trans = fcl.trans;
+ const auto tal = zi.lookup(trans);
+ const auto tprev = trans - absl::time_internal::cctz::seconds(1);
+ const auto pal = zi.lookup(tprev);
+ if (pal.is_dst == tal.is_dst) {
+ ASSERT_STRNE(pal.abbr, tal.abbr);
+ }
+ continue;
+ }
+ ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind);
+ cs = trans.to;
+ } else {
+ ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind);
+ ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind);
+ cs = trans.from;
+ }
+ if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t)
+ const auto cl_zi = zi.lookup(cs);
+ if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) {
+ // The "libc" implementation cannot correctly classify transitions
+ // that don't change the "tm_isdst" flag. In Europe/Volgograd, for
+ // example, there is a SKIPPED transition from +03 to +04 with dst=F
+ // on both sides ...
+ // 1540681199 = 2018-10-28 01:59:59 +03:00:00 [dst=F off=10800]
+ // 1540681200 = 2018-10-28 03:00:00 +04:00:00 [dst=F off=14400]
+ // but std::mktime(2018-10-28 02:00:00, tm_isdst=0) fails, unlike,
+ // say, the similar Europe/Chisinau transition from +02 to +03 ...
+ // 1521935999 = 2018-03-25 01:59:59 +02:00:00 [dst=F off=7200]
+ // 1521936000 = 2018-03-25 03:00:00 +03:00:00 [dst=T off=10800]
+ // where std::mktime(2018-03-25 02:00:00, tm_isdst=0) succeeds and
+ // returns 1521936000.
+ continue;
+ }
+ if (cs == civil_second(2037, 10, 4, 2, 0, 0)) {
+ const std::string tzname = *np;
+ if (tzname == "Africa/Casablanca" || tzname == "Africa/El_Aaiun") {
+ // The "libc" implementation gets this transition wrong (at least
+ // until 2018g when it was removed), returning an offset of 3600
+ // instead of 0. TODO: Revert this when 2018g is ubiquitous.
+ continue;
+ }
+ }
+ const auto cl_lc = lc.lookup(cs);
+ SCOPED_TRACE(testing::Message() << "For " << cs << " in " << *np);
+ EXPECT_EQ(cl_zi.kind, cl_lc.kind);
+ EXPECT_EQ(cl_zi.pre, cl_lc.pre);
+ EXPECT_EQ(cl_zi.trans, cl_lc.trans);
+ EXPECT_EQ(cl_zi.post, cl_lc.post);
+ }
+ }
+ if (ep == nullptr) {
+ ASSERT_EQ(0, unsetenv("TZ"));
+ } else {
+ ASSERT_EQ(0, setenv("TZ", tz_name.c_str(), 1));
+ }
+#endif
+}
+
+TEST(NextTransition, UTC) {
+ const auto tz = utc_time_zone();
+ time_zone::civil_transition trans;
+
+ auto tp = time_point<absl::time_internal::cctz::seconds>::min();
+ EXPECT_FALSE(tz.next_transition(tp, &trans));
+
+ tp = time_point<absl::time_internal::cctz::seconds>::max();
+ EXPECT_FALSE(tz.next_transition(tp, &trans));
+}
+
+TEST(PrevTransition, UTC) {
+ const auto tz = utc_time_zone();
+ time_zone::civil_transition trans;
+
+ auto tp = time_point<absl::time_internal::cctz::seconds>::max();
+ EXPECT_FALSE(tz.prev_transition(tp, &trans));
+
+ tp = time_point<absl::time_internal::cctz::seconds>::min();
+ EXPECT_FALSE(tz.prev_transition(tp, &trans));
+}
+
+TEST(NextTransition, AmericaNewYork) {
+ const auto tz = LoadZone("America/New_York");
+ time_zone::civil_transition trans;
+
+ auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz);
+ EXPECT_TRUE(tz.next_transition(tp, &trans));
+ EXPECT_EQ(civil_second(2018, 11, 4, 2, 0, 0), trans.from);
+ EXPECT_EQ(civil_second(2018, 11, 4, 1, 0, 0), trans.to);
+
+ tp = time_point<absl::time_internal::cctz::seconds>::max();
+ EXPECT_FALSE(tz.next_transition(tp, &trans));
+
+ tp = time_point<absl::time_internal::cctz::seconds>::min();
+ EXPECT_TRUE(tz.next_transition(tp, &trans));
+ if (trans.from == civil_second(1918, 3, 31, 2, 0, 0)) {
+ // It looks like the tzdata is only 32 bit (probably macOS),
+ // which bottoms out at 1901-12-13T20:45:52+00:00.
+ EXPECT_EQ(civil_second(1918, 3, 31, 3, 0, 0), trans.to);
+ } else {
+ EXPECT_EQ(civil_second(1883, 11, 18, 12, 3, 58), trans.from);
+ EXPECT_EQ(civil_second(1883, 11, 18, 12, 0, 0), trans.to);
+ }
+}
+
+TEST(PrevTransition, AmericaNewYork) {
+ const auto tz = LoadZone("America/New_York");
+ time_zone::civil_transition trans;
+
+ auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz);
+ EXPECT_TRUE(tz.prev_transition(tp, &trans));
+ EXPECT_EQ(civil_second(2018, 3, 11, 2, 0, 0), trans.from);
+ EXPECT_EQ(civil_second(2018, 3, 11, 3, 0, 0), trans.to);
+
+ tp = time_point<absl::time_internal::cctz::seconds>::min();
+ EXPECT_FALSE(tz.prev_transition(tp, &trans));
+
+ tp = time_point<absl::time_internal::cctz::seconds>::max();
+ EXPECT_TRUE(tz.prev_transition(tp, &trans));
+ // We have a transition but we don't know which one.
}
TEST(TimeZoneEdgeCase, AmericaNewYork) {
@@ -989,13 +1164,13 @@ TEST(TimeZoneEdgeCase, AmericaNewYork) {
// Spring 1:59:59 -> 3:00:00
auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -5 * 3600, false, "EST");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -4 * 3600, true, "EDT");
// Fall 1:59:59 -> 1:00:00
tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -4 * 3600, true, "EDT");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -5 * 3600, false, "EST");
}
@@ -1005,13 +1180,13 @@ TEST(TimeZoneEdgeCase, AmericaLosAngeles) {
// Spring 1:59:59 -> 3:00:00
auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -8 * 3600, false, "PST");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -7 * 3600, true, "PDT");
// Fall 1:59:59 -> 1:00:00
tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, true, "PDT");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -8 * 3600, false, "PST");
}
@@ -1021,13 +1196,13 @@ TEST(TimeZoneEdgeCase, ArizonaNoTransition) {
// No transition in Spring.
auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -7 * 3600, false, "MST");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 3, 10, 2, 0, 0, -7 * 3600, false, "MST");
// No transition in Fall.
tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, false, "MST");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 11, 3, 2, 0, 0, -7 * 3600, false, "MST");
}
@@ -1040,7 +1215,7 @@ TEST(TimeZoneEdgeCase, AsiaKathmandu) {
// 504901800 == Wed, 1 Jan 1986 00:15:00 +0545 (+0545)
auto tp = convert(civil_second(1985, 12, 31, 23, 59, 59), tz);
ExpectTime(tp, tz, 1985, 12, 31, 23, 59, 59, 5.5 * 3600, false, "+0530");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 1986, 1, 1, 0, 15, 0, 5.75 * 3600, false, "+0545");
}
@@ -1053,14 +1228,14 @@ TEST(TimeZoneEdgeCase, PacificChatham) {
// 1365256800 == Sun, 7 Apr 2013 02:45:00 +1245 (+1245)
auto tp = convert(civil_second(2013, 4, 7, 3, 44, 59), tz);
ExpectTime(tp, tz, 2013, 4, 7, 3, 44, 59, 13.75 * 3600, true, "+1345");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 4, 7, 2, 45, 0, 12.75 * 3600, false, "+1245");
// 1380376799 == Sun, 29 Sep 2013 02:44:59 +1245 (+1245)
// 1380376800 == Sun, 29 Sep 2013 03:45:00 +1345 (+1345)
tp = convert(civil_second(2013, 9, 29, 2, 44, 59), tz);
ExpectTime(tp, tz, 2013, 9, 29, 2, 44, 59, 12.75 * 3600, false, "+1245");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 9, 29, 3, 45, 0, 13.75 * 3600, true, "+1345");
}
@@ -1073,14 +1248,14 @@ TEST(TimeZoneEdgeCase, AustraliaLordHowe) {
// 1365260400 == Sun, 7 Apr 2013 01:30:00 +1030 (+1030)
auto tp = convert(civil_second(2013, 4, 7, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 4, 7, 1, 59, 59, 11 * 3600, true, "+11");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 4, 7, 1, 30, 0, 10.5 * 3600, false, "+1030");
// 1380986999 == Sun, 6 Oct 2013 01:59:59 +1030 (+1030)
// 1380987000 == Sun, 6 Oct 2013 02:30:00 +1100 (+11)
tp = convert(civil_second(2013, 10, 6, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 10, 6, 1, 59, 59, 10.5 * 3600, false, "+1030");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2013, 10, 6, 2, 30, 0, 11 * 3600, true, "+11");
}
@@ -1098,7 +1273,7 @@ TEST(TimeZoneEdgeCase, PacificApia) {
auto tp = convert(civil_second(2011, 12, 29, 23, 59, 59), tz);
ExpectTime(tp, tz, 2011, 12, 29, 23, 59, 59, -10 * 3600, true, "-10");
EXPECT_EQ(363, get_yearday(civil_day(convert(tp, tz))));
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2011, 12, 31, 0, 0, 0, 14 * 3600, true, "+14");
EXPECT_EQ(365, get_yearday(civil_day(convert(tp, tz))));
}
@@ -1106,35 +1281,31 @@ TEST(TimeZoneEdgeCase, PacificApia) {
TEST(TimeZoneEdgeCase, AfricaCairo) {
const time_zone tz = LoadZone("Africa/Cairo");
-#if defined(__ANDROID__) && __ANDROID_API__ < 21
- // Only Android 'L' and beyond have this tz2014c transition.
-#else
- // An interesting case of midnight not existing.
- //
- // 1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
- // 1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
- auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
- ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
- tp += seconds(1);
- ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
-#endif
+ if (VersionCmp(tz, "2014c") >= 0) {
+ // An interesting case of midnight not existing.
+ //
+ // 1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
+ // 1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
+ auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
+ ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
+ tp += absl::time_internal::cctz::seconds(1);
+ ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
+ }
}
TEST(TimeZoneEdgeCase, AfricaMonrovia) {
const time_zone tz = LoadZone("Africa/Monrovia");
-#if defined(__ANDROID__) && __ANDROID_API__ < 26
- // Only Android 'O' and beyond have this tz2017b transition.
-#else
- // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
- //
- // 63593069 == Thu, 6 Jan 1972 23:59:59 -0044 (MMT)
- // 63593070 == Fri, 7 Jan 1972 00:44:30 +0000 (GMT)
- auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
- ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
- tp += seconds(1);
- ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
-#endif
+ if (VersionCmp(tz, "2017b") >= 0) {
+ // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
+ //
+ // 63593069 == Thu, 6 Jan 1972 23:59:59 -0044 (MMT)
+ // 63593070 == Fri, 7 Jan 1972 00:44:30 +0000 (GMT)
+ auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
+ ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
+ tp += absl::time_internal::cctz::seconds(1);
+ ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
+ }
}
TEST(TimeZoneEdgeCase, AmericaJamaica) {
@@ -1146,30 +1317,31 @@ TEST(TimeZoneEdgeCase, AmericaJamaica) {
const time_zone tz = LoadZone("America/Jamaica");
// Before the first transition.
- auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
-#if AMERICA_JAMAICA_PRE_1913_OFFSET_FIX
- // Commit 907241e: Fix off-by-1 error for Jamaica and T&C before 1913.
- // Until that commit has made its way into a full release we avoid the
- // expectations on the -18430 offset below. TODO: Uncomment these.
- ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
- tz.lookup(tp).abbr);
-
- // Over the first (abbreviation-change only) transition.
- // -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
- // -2524503169 == Wed, 1 Jan 1890 00:00:00 -0507 (KMT)
- tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
- ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
- tz.lookup(tp).abbr);
- tp += seconds(1);
- ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
-#endif
+ if (!tz.version().empty() && VersionCmp(tz, "2018d") >= 0) {
+ // We avoid the expectations on the -18430 offset below unless we are
+ // certain we have commit 907241e (Fix off-by-1 error for Jamaica and
+ // T&C before 1913) from 2018d. TODO: Remove the "version() not empty"
+ // part when 2018d is generally available from /usr/share/zoneinfo.
+ auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
+ ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
+ tz.lookup(tp).abbr);
+
+ // Over the first (abbreviation-change only) transition.
+ // -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
+ // -2524503169 == Wed, 1 Jan 1890 00:00:00 -0507 (KMT)
+ tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
+ ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
+ tz.lookup(tp).abbr);
+ tp += absl::time_internal::cctz::seconds(1);
+ ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
+ }
// Over the last (DST) transition.
// 436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT)
// 436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST)
- tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz);
+ auto tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz);
ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST");
// After the last transition.
@@ -1190,7 +1362,7 @@ TEST(TimeZoneEdgeCase, WET) {
// 228877200 == Sun, 3 Apr 1977 02:00:00 +0100 (WEST)
tp = convert(civil_second(1977, 4, 3, 0, 59, 59), tz);
ExpectTime(tp, tz, 1977, 4, 3, 0, 59, 59, 0, false, "WET");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 1977, 4, 3, 2, 0, 0, 1 * 3600, true, "WEST");
// A non-existent time within the first transition.
@@ -1212,12 +1384,12 @@ TEST(TimeZoneEdgeCase, FixedOffsets) {
const time_zone gmtm5 = LoadZone("Etc/GMT+5"); // -0500
auto tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtm5);
ExpectTime(tp, gmtm5, 1970, 1, 1, 0, 0, 0, -5 * 3600, false, "-05");
- EXPECT_EQ(system_clock::from_time_t(5 * 3600), tp);
+ EXPECT_EQ(chrono::system_clock::from_time_t(5 * 3600), tp);
const time_zone gmtp5 = LoadZone("Etc/GMT-5"); // +0500
tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtp5);
ExpectTime(tp, gmtp5, 1970, 1, 1, 0, 0, 0, 5 * 3600, false, "+05");
- EXPECT_EQ(system_clock::from_time_t(-5 * 3600), tp);
+ EXPECT_EQ(chrono::system_clock::from_time_t(-5 * 3600), tp);
}
TEST(TimeZoneEdgeCase, NegativeYear) {
@@ -1226,7 +1398,7 @@ TEST(TimeZoneEdgeCase, NegativeYear) {
auto tp = convert(civil_second(0, 1, 1, 0, 0, 0), tz);
ExpectTime(tp, tz, 0, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC");
EXPECT_EQ(weekday::saturday, get_weekday(civil_day(convert(tp, tz))));
- tp -= seconds(1);
+ tp -= absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, -1, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC");
EXPECT_EQ(weekday::friday, get_weekday(civil_day(convert(tp, tz))));
}
@@ -1240,7 +1412,7 @@ TEST(TimeZoneEdgeCase, UTC32bitLimit) {
// 2147483648 == Tue, 19 Jan 2038 03:14:08 +0000 (UTC)
auto tp = convert(civil_second(2038, 1, 19, 3, 14, 7), tz);
ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 7, 0 * 3600, false, "UTC");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 8, 0 * 3600, false, "UTC");
}
@@ -1253,11 +1425,11 @@ TEST(TimeZoneEdgeCase, UTC5DigitYear) {
// 253402300800 == Sat, 1 Jan 1000 00:00:00 +0000 (UTC)
auto tp = convert(civil_second(9999, 12, 31, 23, 59, 59), tz);
ExpectTime(tp, tz, 9999, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC");
- tp += seconds(1);
+ tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 10000, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC");
}
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_posix.cc b/absl/time/internal/cctz/src/time_zone_posix.cc
index b6cf2875..960133d7 100644
--- a/absl/time/internal/cctz/src/time_zone_posix.cc
+++ b/absl/time/internal/cctz/src/time_zone_posix.cc
@@ -20,7 +20,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -153,5 +153,5 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_posix.h b/absl/time/internal/cctz/src/time_zone_posix.h
index 91443a21..84c39afb 100644
--- a/absl/time/internal/cctz/src/time_zone_posix.h
+++ b/absl/time/internal/cctz/src/time_zone_posix.h
@@ -56,7 +56,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
@@ -69,28 +69,38 @@ namespace cctz {
// it would take us to another day, and perhaps week, or even month.
struct PosixTransition {
enum DateFormat { J, N, M };
- struct {
+
+ struct Date {
+ struct NonLeapDay {
+ std::int_fast16_t day; // day of non-leap year [1:365]
+ };
+ struct Day {
+ std::int_fast16_t day; // day of year [0:365]
+ };
+ struct MonthWeekWeekday {
+ std::int_fast8_t month; // month of year [1:12]
+ std::int_fast8_t week; // week of month [1:5] (5==last)
+ std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat
+ };
+
DateFormat fmt;
+
union {
- struct {
- std::int_fast16_t day; // day of non-leap year [1:365]
- } j;
- struct {
- std::int_fast16_t day; // day of year [0:365]
- } n;
- struct {
- std::int_fast8_t month; // month of year [1:12]
- std::int_fast8_t week; // week of month [1:5] (5==last)
- std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat
- } m;
+ NonLeapDay j;
+ Day n;
+ MonthWeekWeekday m;
};
- } date;
- struct {
+ };
+
+ struct Time {
std::int_fast32_t offset; // seconds before/after 00:00:00
- } time;
+ };
+
+ Date date;
+ Time time;
};
-// The entirety of a POSIX-std::string specified time-zone rule. The standard
+// The entirety of a POSIX-string specified time-zone rule. The standard
// abbreviation and offset are always given. If the time zone includes
// daylight saving, then the daylight abbrevation is non-empty and the
// remaining fields are also valid. Note that the start/end transitions
@@ -114,7 +124,7 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res);
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h
index 90cfc0c4..4485ba55 100644
--- a/absl/time/internal/cctz/src/tzfile.h
+++ b/absl/time/internal/cctz/src/tzfile.h
@@ -1,3 +1,5 @@
+/* Layout and location of TZif files. */
+
#ifndef TZFILE_H
#define TZFILE_H
diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc
index a73e1c92..61669e7a 100644
--- a/absl/time/internal/cctz/src/zone_info_source.cc
+++ b/absl/time/internal/cctz/src/zone_info_source.cc
@@ -15,20 +15,21 @@
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz {
// Defined out-of-line to avoid emitting a weak vtable in all TUs.
ZoneInfoSource::~ZoneInfoSource() {}
+std::string ZoneInfoSource::Version() const { return std::string(); }
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz_extension {
@@ -55,20 +56,28 @@ ZoneInfoSourceFactory default_factory = DefaultFactory;
#if defined(_M_IX86)
#pragma comment( \
linker, \
- "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA=?default_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA")
+ "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA=?default_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA")
#elif defined(_M_IA_64) || defined(_M_AMD64)
#pragma comment( \
linker, \
- "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA=?default_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA")
+ "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA=?default_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA")
#else
#error Unsupported MSVC platform
#endif
-#else
+#else // _MSC_VER
+#if !defined(__has_attribute)
+#define __has_attribute(x) 0
+#endif
+#if __has_attribute(weak) || defined(__GNUC__)
ZoneInfoSourceFactory zone_info_source_factory
__attribute__((weak)) = DefaultFactory;
+#else
+// Make it a "strong" definition if we have no other choice.
+ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory;
+#endif
#endif // _MSC_VER
} // namespace cctz_extension
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index fe86b5cc..ac954d74 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2018e-2-g99dd695
+2018g-9-gf0d2759
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
index 8726e80d..eaaa818f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
index 2a25f3ac..a5867a67 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
index 8e32be3e..82ea5aaf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
index 5b871dba..31cfad77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
index 5b871dba..31cfad77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
index ba097504..0272fa1b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
index 65de3445..4c570548 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
index aaa657ff..dd75e3e6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
index f5f8ffbc..0ea02535 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
index 5b871dba..31cfad77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
index 5b871dba..31cfad77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
index ddf3652e..b8b9270a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
index 9fa71190..83eca03a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
index f2c9e303..549dae27 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
index 5b871dba..31cfad77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
index 5b871dba..31cfad77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
index 5b871dba..31cfad77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
index 5b871dba..31cfad77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
index ddf3652e..b8b9270a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
index ddf3652e..b8b9270a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
index b434c67f..2a154f46 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
index bbfe19d6..8779590e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
index b1c97cc5..cbdc0450 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
index a4ece7ff..d2a64bd1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
index b32e2202..bd885315 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
index 4bd3885a..0cd8ffba 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
index f5d40baf..67661856 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Adak b/absl/time/internal/cctz/testdata/zoneinfo/America/Adak
index 5696e0f8..43236498 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Adak
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Adak
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage b/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
index 6c8bdf22..9bbb2fd3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla b/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua b/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina b/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina
index 8b295a98..bc9a5228 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires
index e4866ce1..dfebfb99 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca
index 9fe9ad64..b798105e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia
index 9fe9ad64..b798105e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba
index 8c58f8c2..5df3cf6e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy
index a74ba046..7d2ba91c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja
index cb184d6a..7654aebf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza
index 5e8c44c8..10323564 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos
index 966a529b..3c849fce 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta
index b19aa222..a4b71c1f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan
index 9e5ade61..948a3901 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis
index af8aa998..acfbbe43 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman
index bbb03a0c..085fc9cc 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia
index 07e4e9f0..1fc32567 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba b/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba
index d308336b..d3b318d2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion b/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion
index 3c61ddb5..831bf843 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan b/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan
index 5708b55a..629ed423 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Atka b/absl/time/internal/cctz/testdata/zoneinfo/America/Atka
index 5696e0f8..43236498 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Atka
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Atka
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia
index 6008a574..143eafc2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas
index 21e2b719..cd531078 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados b/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados
index 63399360..7bb7ac4d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Belem b/absl/time/internal/cctz/testdata/zoneinfo/America/Belem
index b8e13b02..ab3c8a67 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Belem
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Belem
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Belize b/absl/time/internal/cctz/testdata/zoneinfo/America/Belize
index 7dcc4fc5..fd693214 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Belize
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Belize
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon b/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon
index abcde7d9..f9f13a16 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista b/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista
index f7769048..69e17a00 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota b/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota
index d8934466..b7ded8e6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Boise b/absl/time/internal/cctz/testdata/zoneinfo/America/Boise
index ada6d64b..f8d54e27 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Boise
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Boise
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires b/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires
index e4866ce1..dfebfb99 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay b/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay
index d322f01e..f8db4b6e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande b/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande
index de52bb68..495ef456 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun b/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun
index 7e69f73d..de6930cd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas b/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas
index c8cab1af..9abd028f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca b/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca
index 9fe9ad64..b798105e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne b/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne
index 6db64098..ff596578 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman b/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman
index 5c1c0637..55b08346 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago b/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago
index 3dd8f0fa..a5b1617c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua b/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua
index e3adbdbf..b2687241 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour b/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour
index 5708b55a..629ed423 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba b/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba
index 8c58f8c2..5df3cf6e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica b/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica
index c247133e..525a67ea 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Creston b/absl/time/internal/cctz/testdata/zoneinfo/America/Creston
index 798f627a..0fba7417 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Creston
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Creston
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba b/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba
index 145c89e0..8a4ee7d0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao b/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao
index d308336b..d3b318d2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn b/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn
index ad68c722..9549adcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson b/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson
index 61c96889..db9ceadd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek b/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek
index 78f90763..db9e3396 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Denver b/absl/time/internal/cctz/testdata/zoneinfo/America/Denver
index 7fc66917..5fbe26b1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Denver
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Denver
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit b/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit
index e3ea5c3e..5e022605 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica b/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton b/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton
index d02fbcd4..3fa05798 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe b/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe
index 41047f29..99b7d06e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador b/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador
index 9b8bc7a8..ac774e83 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
index 29c83e71..ada6bf78 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson b/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson
index 5923cc68..5a0b7f1c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne b/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne
index 4a92c065..09511ccd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza b/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza
index 22396bb5..e637170a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay b/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay
index f58522b6..48412a4c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
index ea293cc4..0160308b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay b/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
index b4b945e8..a3f29907 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk b/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk
index 4c8ca6f7..4597a621 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe b/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala b/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala
index abf943be..6118b5ce 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil b/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil
index 92de38be..bf087a0e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana b/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana
index 7d298767..d1dd2faf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax b/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax
index f86ece4c..756099ab 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Havana b/absl/time/internal/cctz/testdata/zoneinfo/America/Havana
index 1a58fcdc..8186060a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Havana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Havana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo b/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo
index ec435c23..26c269d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis
index 4a92c065..09511ccd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox
index cc785da9..fcd408d7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo
index a23d7b75..1abf75e7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg
index f16cb304..0133548e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City
index 0250bf90..4ce95c15 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay
index e934de61..d236b7c0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes
index adbdbeee..c818929d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
index b34f7b27..630935c1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis b/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis
index 4a92c065..09511ccd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik b/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik
index 1388e8a4..e107dc44 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit b/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit
index 0785ac57..c8138bdb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica b/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica
index 7aedd262..162306f8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy b/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy
index a74ba046..7d2ba91c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau b/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau
index d00668ad..451f3490 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville b/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville
index fdf2e88b..f4c4cf96 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello b/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello
index 60991aa3..438e3eab 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN b/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN
index cc785da9..fcd408d7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk b/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk
index d308336b..d3b318d2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz b/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz
index bc3df523..5e5fec56 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Lima b/absl/time/internal/cctz/testdata/zoneinfo/America/Lima
index 44280a5c..d9fec371 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Lima
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Lima
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles b/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles
index c0ce4402..9dad4f4c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville b/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville
index fdf2e88b..f4c4cf96 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes b/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes
index d308336b..d3b318d2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio b/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio
index 54442dc7..fec8a8bf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Managua b/absl/time/internal/cctz/testdata/zoneinfo/America/Managua
index c543ffd4..69256c63 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Managua
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Managua
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus b/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus
index 855cb02c..b10241e6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot b/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique b/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique
index f9e2399c..79716de5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
index 5671d258..5c59984d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan b/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan
index afa94c2a..43ee12d8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza b/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza
index 5e8c44c8..10323564 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee b/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee
index 55d6e326..31461386 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Merida b/absl/time/internal/cctz/testdata/zoneinfo/America/Merida
index ecc1856e..b46298e1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Merida
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Merida
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla b/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
index c0335970..26356078 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City b/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City
index f11e3d2d..1434ab08 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon b/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon
index 75bbcf2b..06ceaadf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton b/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
index 51cb1ba3..9df8d0f2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey b/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey
index dcac92ba..7dc50577 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo b/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo
index f524fd21..0d1e565c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal b/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal
index 7b4682a3..6752c5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat b/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
index e5d0289b..5091eb5d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/New_York b/absl/time/internal/cctz/testdata/zoneinfo/America/New_York
index 7553fee3..2f75480e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/New_York
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/New_York
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon b/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon
index f8a0292b..f6a856e6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nome b/absl/time/internal/cctz/testdata/zoneinfo/America/Nome
index c886c9bc..10998df3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nome
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nome
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha b/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha
index 6d91f914..95ff8a25 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah b/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah
index 8174c882..246345dd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center b/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center
index 8035b24f..1fa07037 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem b/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem
index 5b630ee6..123f2aee 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
index 190c5c86..37d78301 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Panama b/absl/time/internal/cctz/testdata/zoneinfo/America/Panama
index 5c1c0637..55b08346 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Panama
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Panama
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung b/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung
index df78b626..3e4e0db6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo b/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo
index 1b608b3e..b95c7842 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix b/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix
index adf28236..4d51271a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince b/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince
index 7306caef..d9590103 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain b/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre b/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre
index b612ac23..16b7f923 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho b/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho
index 2423fc19..10cb02b8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico b/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico
index d4525a68..a662a571 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas b/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas
index 4d84eed4..a5a8af52 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River b/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
index 70dcd2d8..ea660991 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet b/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet
index 9f50f36e..61ff6fcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Recife b/absl/time/internal/cctz/testdata/zoneinfo/America/Recife
index fe55739d..c6d99b3a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Recife
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Recife
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Regina b/absl/time/internal/cctz/testdata/zoneinfo/America/Regina
index 5fe8d6b6..20c9c84d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Regina
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Regina
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute b/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute
index 884b1f64..4365a5c8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco b/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco
index b612ac23..16b7f923 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario b/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario
index 8c58f8c2..5df3cf6e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
index 29c83e71..ada6bf78 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem b/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem
index d776a438..8080efab 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago b/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago
index ab766a41..816a0428 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo b/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo
index cc2cbf2b..4e5eba52 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo b/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo
index 308a545c..c417ba1d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund b/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
index 8e1366ca..e20e9e1c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock b/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock
index 7fc66917..5fbe26b1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka b/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka
index 662b8b67..31f70613 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
index a1d14854..65a5b0c7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current b/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current
index 4db1300a..8e9ef255 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa b/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa
index 7aea8f99..477e9395 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Thule b/absl/time/internal/cctz/testdata/zoneinfo/America/Thule
index deefcc8d..2969ebe5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Thule
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Thule
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay b/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay
index aa1d4860..e504c9ac 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
index 29c83e71..ada6bf78 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto b/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto
index 7b4682a3..6752c5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola b/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver b/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver
index 9b5d9241..0f9f8328 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin b/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin
index 447efbe2..bdedd1bd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse b/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse
index 6b62e2d3..fb3cd71a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg b/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg
index 2ffe3d8d..3718d47d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat b/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat
index 523b0a10..da209f9f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife b/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife
index d9d6eff7..e6afa390 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
index d0bbacc8..f100f474 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis
index 40a99266..916f2c25 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville
index 06863534..bd6563ec 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
index aea2be77..83c308ad 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson
index 5197dd97..e1f0b09b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo
index a5f5b6d5..60bcef68 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer
index 43a01d3e..3dd85f84 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera
index 56913f8a..7940e6ef 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole
index a5f5b6d5..60bcef68 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa
index 94a9d5a2..4bb041a2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
index 3757facc..5e565da2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
index 9fa335c4..5696abf5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen b/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen
index 239c0174..c6842af8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden
index e71bc4e8..b2f9a255 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty
index 49a4b4de..d93201cf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
index c3f0994a..281b304e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr
index 0e623cf7..6a966013 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau
index 5803a3d3..78cbcf0e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe
index 808a5026..7504052a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat
index 046c4728..8d9e03c1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad
index 046c4728..8d9e03c1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau
index 27072eb5..317466d1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad
index 3aacd78b..97fa6c73 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain
index a0c5f669..f5140926 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku
index a17d1ad8..8a090d77 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok
index 8db5e8a6..72496402 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul
index 60efb41b..82cc49c4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut
index 72f08963..efb24c27 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek
index e3f81ee3..f7a7d548 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei
index cad16b0d..8624c7ae 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta
index b57972dd..e1cfcb8d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita
index 95f56456..3baf7528 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan
index 15b358f2..79b9d3c8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing
index dbd132f2..ce9e00a5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking
index dbd132f2..ce9e00a5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo
index 28fe4307..4fc96c89 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca
index 98881f09..776f27da 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus
index ac457646..4b610b5a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka
index 98881f09..776f27da 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili
index c94fa610..f6ce91a1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai
index c12f31a1..7880d5d7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe
index 67c772b4..694f6e6a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta
index 021f8a2d..653b146a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
index 60d0de00..cf54deb8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin
index dbd132f2..ce9e00a5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
index a2e1b364..09c876a6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh
index 92642679..eab94fe8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong
index dc9058e4..8e5c5813 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd
index f367a550..8eb5f647 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk
index 84136366..e8c53c0d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul
index 9a53b3a3..833d4eba 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta
index 37b4edde..673d4801 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura
index 39ddc843..a4c08297 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem
index df511993..2d14c999 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul
index 80429ec4..a22cf592 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka
index fab27def..b9ed49ca 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi
index b7dcaab8..337e1d58 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar
index b44a1e19..0342b433 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu
index 0cbd2952..2f810b12 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu
index 0cbd2952..2f810b12 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga
index 91836953..2b2f5bfa 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata
index b57972dd..e1cfcb8d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk
index faec35d3..59efd24c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur
index 5c95ebcd..6d7d47b9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching
index 62b53892..4878622d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait
index e71bc4e8..b2f9a255 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao
index 2c20a326..d801000d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau
index 2c20a326..d801000d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan
index 2db06356..b20cc57e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar
index 3a5dcb27..ed55442e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila
index 06859a70..2c9220c9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat
index c12f31a1..7880d5d7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
index 3e663b21..f7f10ab7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk
index ed4b2482..2576a3b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk
index a5d39dff..95e3c73b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk
index 5e0d9b67..d805e4f7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral
index b8eb58d1..e36aec47 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh
index 8db5e8a6..72496402 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak
index ec98c62b..9377d038 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang
index dc24926e..dd54989f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar
index a0c5f669..f5140926 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda
index 0fc7fada..00b27844 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon
index 3cc2aafa..a00282de 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh
index e71bc4e8..b2f9a255 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon
index 92642679..eab94fe8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin
index 8d6b4dfe..9c94900c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand
index 10c7af7f..a5d1e970 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul
index 312ec40a..fa1cbd39 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai
index dbd132f2..ce9e00a5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore
index 78583666..ebc4b0d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk
index 16b1cd8f..f8b7bb21 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei
index 748873be..f9cbe672 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent
index 6f7dea4a..e75bb365 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi
index 4b2d2e29..09bb06eb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran
index 3157f806..ad9058b4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv
index df511993..2d14c999 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu
index a8bddb9f..06d3324d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu
index a8bddb9f..06d3324d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo
index 8ad44ba9..26f4d34d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk
index 919b0031..28da9c90 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang
index 3a5dcb27..ed55442e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar
index 94ddfea5..82fd4760 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator
index 94ddfea5..82fd4760 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi
index b44a1e19..0342b433 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera
index 7431eb97..c0c3767e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane
index 8db5e8a6..72496402 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok
index 80b170bc..15731abc 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk
index 220ad3db..1f86e77f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon
index 3cc2aafa..a00282de 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg
index c1abb935..fff9f3b1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan
index 4c4e045b..409c3b17 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores
index 1895e1b1..56593dbf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda
index 548d979b..3a5c6dbf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary
index 544f443a..f3192156 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde
index 6bda6db7..e2a49d24 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe
index c4865186..4dab7ef0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe
index c4865186..4dab7ef0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen
index 239c0174..c6842af8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira
index e25f8a59..5213761f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik
index dc49c324..ac6bd697 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia
index 56b383b1..b3311b63 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena
index 6fd1af32..65d19ec2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley
index 3649415b..2fd42a2c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT b/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT
index aaed12ca..4ed4467f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide
index 4f331a87..190b0e33 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane
index a327d83b..26ffd9ac 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill
index 768b1678..874c8650 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra
index aaed12ca..4ed4467f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie
index a3f6f29a..865801e5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin
index c6ae9a7b..cf42d1d8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla
index 99f07a9f..c49d499c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart
index 07784ce5..92d1215d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI b/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI
index 57597b0b..8c6c7dd0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman
index 71ca143f..8ee1a6f5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe
index 57597b0b..8c6c7dd0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne
index ec8dfe03..3f2d3d7f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW b/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW
index aaed12ca..4ed4467f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/North b/absl/time/internal/cctz/testdata/zoneinfo/Australia/North
index c6ae9a7b..cf42d1d8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/North
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/North
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth
index 85c26d50..d38b67e2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland
index a327d83b..26ffd9ac 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/South b/absl/time/internal/cctz/testdata/zoneinfo/Australia/South
index 4f331a87..190b0e33 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/South
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/South
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney
index aaed12ca..4ed4467f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania
index 07784ce5..92d1215d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria
index ec8dfe03..3f2d3d7f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/West b/absl/time/internal/cctz/testdata/zoneinfo/Australia/West
index 85c26d50..d38b67e2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/West
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/West
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna
index 768b1678..874c8650 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre
index b612ac23..16b7f923 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha
index 6d91f914..95ff8a25 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East
index 308a545c..c417ba1d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West
index 855cb02c..b10241e6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/CET b/absl/time/internal/cctz/testdata/zoneinfo/CET
index 4c4f8ef9..d585656f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/CET
+++ b/absl/time/internal/cctz/testdata/zoneinfo/CET
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT b/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT
index 5c8a1d9a..41c4136f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic
index f86ece4c..756099ab 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central
index 2ffe3d8d..3718d47d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern
index 7b4682a3..6752c5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain
index d02fbcd4..3fa05798 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
index a1d14854..65a5b0c7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific
index 9b5d9241..0f9f8328 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan
index 5fe8d6b6..20c9c84d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon
index 6b62e2d3..fb3cd71a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental b/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental
index ab766a41..816a0428 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland b/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland
index 060bef81..cae37440 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Cuba b/absl/time/internal/cctz/testdata/zoneinfo/Cuba
index 1a58fcdc..8186060a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Cuba
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Cuba
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/EET b/absl/time/internal/cctz/testdata/zoneinfo/EET
index beb273a2..d2f54c9b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/EET
+++ b/absl/time/internal/cctz/testdata/zoneinfo/EET
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/EST b/absl/time/internal/cctz/testdata/zoneinfo/EST
index ae346633..074a4fc7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/EST
+++ b/absl/time/internal/cctz/testdata/zoneinfo/EST
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT b/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT
index 54541fc2..087b641d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Egypt b/absl/time/internal/cctz/testdata/zoneinfo/Egypt
index ba097504..0272fa1b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Egypt
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Egypt
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Eire b/absl/time/internal/cctz/testdata/zoneinfo/Eire
index 655daf37..5c5a7a3b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Eire
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Eire
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1
index 082986e7..087d1f92 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10
index 23276cd1..6437c684 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11
index 28c579dc..72a912e0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12
index c7406039..6938a1af 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2
index 721cde2f..a3155777 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3
index ae06bcb6..ee776199 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4
index 5a7f878c..1ea7da29 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5
index 18cbf1fe..dda1a9e1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6
index 1aa4be88..f4a03855 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7
index cd8ed49a..2d2ccd00 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8
index e0ba6b88..826c7700 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9
index eee1bcb7..b125ad2b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1
index 4ff87014..dde682d8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10
index e12e461d..352ec08a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11
index 37f27397..dfa27fec 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12
index 09297f1b..eef949df 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13
index 97ae1e14..f9363b24 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14
index 58d6d1b2..35add05a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2
index f0dc7062..315cae4f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3
index a0790fe9..7489a153 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4
index a75a173d..560243e8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5
index 85ebf22e..b2bbe977 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6
index 95def1f9..b979dbbc 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7
index c6a776e9..365ab1f6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8
index f74a16f9..742082fc 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9
index 9b647c0f..abc0b275 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT b/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT
index 40147b9e..a88c4b66 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC b/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC
index c3b97f1a..5583f5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal
index c3b97f1a..5583f5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu
index c3b97f1a..5583f5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam
index 6dae5e47..ed064ed4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra
index b06de7a5..59625503 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan
index 90d7c2a8..5e069ea5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens
index 0001602f..9f3a0678 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
index 4527515c..a340326e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade
index 79c25d70..32a57223 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin
index b4f2a2af..7ddd510e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava
index ba82f311..85036de3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels
index d8f19a63..d0d0a08a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
index e0eac4ce..4eb7ed0d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest
index 3ddf6a52..dfdc6d24 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen
index 9c2b600b..ad6cf592 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
index 2109b52a..5bc1bfeb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen
index be87cf16..cb2ec067 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin
index 655daf37..5c5a7a3b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar
index a7105faa..117aadb8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
index 4527515c..a340326e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki
index 29b3c817..b4f8f9cb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
index 4527515c..a340326e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul
index 9a53b3a3..833d4eba 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
index 4527515c..a340326e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad
index 37280d05..982d82a3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
index b3e20a7e..9337c9ea 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov
index 40b558f8..a3b5320a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon
index a8565304..355817b5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana
index 79c25d70..32a57223 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/London b/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
index 4527515c..a340326e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg
index 6fae86c5..6c194a5c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid
index 9b51a73b..ccc9d857 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta
index c1208e2d..bf2452da 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn
index 29b3c817..b4f8f9cb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk
index 60041a41..801aead7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco
index 0b40f1ec..686ae883 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow
index 906bd05f..ddb3f4e9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
index 3e663b21..f7f10ab7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo
index 239c0174..c6842af8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris
index cf6e2e2e..ca854351 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica
index 79c25d70..32a57223 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague
index ba82f311..85036de3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
index b729ee8c..8495c506 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome
index bdd3449e..78a131b9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara
index 0539acfd..97d5dd9e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino b/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino
index bdd3449e..78a131b9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo
index 79c25d70..32a57223 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov
index e8cd6b10..8fd5f6d4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol
index f3b42b00..e82dbbc7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje
index 79c25d70..32a57223 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
index 763e0747..dcfdd082 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm
index 43c7f2e2..f3e0c7f0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
index 18f903fa..3a744cc6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane
index 52c16a42..0b86017d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
index 2109b52a..5bc1bfeb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk
index c280f430..7b61bdc5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
index 8ddba909..677f0887 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz
index 9c2b600b..ad6cf592 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican
index bdd3449e..78a131b9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna
index 9c0fac53..9e2d0c94 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
index da380af0..46ce484f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
index f4cb64f1..8f170dd9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw
index 5cbba412..d6bb1561 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb
index 79c25d70..32a57223 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
index 6f148505..e42edfc8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich
index 9c2b600b..ad6cf592 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Factory b/absl/time/internal/cctz/testdata/zoneinfo/Factory
index afeeb88d..95bc3a3b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Factory
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Factory
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GB b/absl/time/internal/cctz/testdata/zoneinfo/GB
index 4527515c..a340326e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GB
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GB
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire b/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
index 4527515c..a340326e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GMT b/absl/time/internal/cctz/testdata/zoneinfo/GMT
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GMT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GMT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 b/absl/time/internal/cctz/testdata/zoneinfo/GMT+0
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GMT+0
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GMT+0
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 b/absl/time/internal/cctz/testdata/zoneinfo/GMT-0
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GMT-0
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GMT-0
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GMT0 b/absl/time/internal/cctz/testdata/zoneinfo/GMT0
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GMT0
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GMT0
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Greenwich b/absl/time/internal/cctz/testdata/zoneinfo/Greenwich
index c05e45fd..2ee14295 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Greenwich
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Greenwich
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/HST b/absl/time/internal/cctz/testdata/zoneinfo/HST
index 03e4db07..616c31bc 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/HST
+++ b/absl/time/internal/cctz/testdata/zoneinfo/HST
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Hongkong b/absl/time/internal/cctz/testdata/zoneinfo/Hongkong
index dc9058e4..8e5c5813 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Hongkong
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Hongkong
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Iceland b/absl/time/internal/cctz/testdata/zoneinfo/Iceland
index dc49c324..ac6bd697 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Iceland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Iceland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos
index 0e5e7192..f609611c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas
index 066c1e9f..6babdeea 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos
index 34a2457b..58f80514 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen
index e7d4d3d0..2cb6f3e3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe
index db8ac687..49e23e5a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives
index 3f1a76e5..ffa33658 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius
index fd8d9111..b23e2cee 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte
index 39631f21..6e19601f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion
index d5f9aa49..11c6002e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Iran b/absl/time/internal/cctz/testdata/zoneinfo/Iran
index 3157f806..ad9058b4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Iran
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Iran
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Israel b/absl/time/internal/cctz/testdata/zoneinfo/Israel
index df511993..2d14c999 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Israel
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Israel
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Jamaica b/absl/time/internal/cctz/testdata/zoneinfo/Jamaica
index 7aedd262..162306f8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Jamaica
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Jamaica
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Japan b/absl/time/internal/cctz/testdata/zoneinfo/Japan
index 8ad44ba9..26f4d34d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Japan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Japan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein b/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein
index 1a27122e..54bd71ff 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Libya b/absl/time/internal/cctz/testdata/zoneinfo/Libya
index b32e2202..bd885315 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Libya
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Libya
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/MET b/absl/time/internal/cctz/testdata/zoneinfo/MET
index 71963d53..388dd744 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/MET
+++ b/absl/time/internal/cctz/testdata/zoneinfo/MET
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/MST b/absl/time/internal/cctz/testdata/zoneinfo/MST
index a1bee7c6..da3e926d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/MST
+++ b/absl/time/internal/cctz/testdata/zoneinfo/MST
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT b/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT
index 726a7e57..ddca8d19 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
index 29c83e71..ada6bf78 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur
index afa94c2a..43ee12d8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General
index f11e3d2d..1434ab08 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/NZ b/absl/time/internal/cctz/testdata/zoneinfo/NZ
index a5f5b6d5..60bcef68 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/NZ
+++ b/absl/time/internal/cctz/testdata/zoneinfo/NZ
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT b/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT
index 957c80b7..abe09cb9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Navajo b/absl/time/internal/cctz/testdata/zoneinfo/Navajo
index 7fc66917..5fbe26b1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Navajo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Navajo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/PRC b/absl/time/internal/cctz/testdata/zoneinfo/PRC
index dbd132f2..ce9e00a5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/PRC
+++ b/absl/time/internal/cctz/testdata/zoneinfo/PRC
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT b/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT
index 6242ac04..d773e28f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia
index 4091a85f..fd03ff76 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland
index a5f5b6d5..60bcef68 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville
index dc5a7d73..6a6c2da2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham
index 957c80b7..abe09cb9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk
index 289b795a..e79bca2d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter
index 060bef81..cae37440 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate
index 5cee55df..d650a056 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury
index a3f30e5c..80873503 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo
index 6e4b8afd..4fa169f3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
index 912db189..61a66953 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti
index 3289094a..e6a15447 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos
index 76b2b3a1..859b76d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier
index 625016d5..4e9e36c5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal
index 0c24095b..908ccc14 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam
index 4286e6ba..ffdf8c24 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu
index bd855772..c7cd0601 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston
index bd855772..c7cd0601 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati
index 762275d3..cf5b3bd3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae
index f8222e66..b6bd4b08 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein
index 1a27122e..54bd71ff 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro
index b3a8c184..53f32886 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas
index 10c5c9bc..5fad0e1b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway
index 3e38e97c..72707b5e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru
index 6092119f..7e7d920e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue
index df6110dd..1d58fe36 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
index d0b9607e..f630a65d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea
index d9c68f88..99f6bca2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago
index 3e38e97c..72707b5e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau
index e1bbea56..968f1956 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn
index 54783cf6..9092e481 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei
index 9743bc3c..d3393a20 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape
index 9743bc3c..d3393a20 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby
index 3fa1f7fa..f6fd51cb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga
index ace1ce4b..9708b870 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan
index 4286e6ba..ffdf8c24 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa
index 3e38e97c..72707b5e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti
index 7867d8bd..37e4e883 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa
index 33404138..e23c0cd2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu
index b3a5a89b..35c9e2c6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk
index 289b795a..e79bca2d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake
index 2dc630c6..837ce1f5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis
index b4f0f9bf..8be9ac4d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap
index 289b795a..e79bca2d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Poland b/absl/time/internal/cctz/testdata/zoneinfo/Poland
index 5cbba412..d6bb1561 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Poland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Poland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Portugal b/absl/time/internal/cctz/testdata/zoneinfo/Portugal
index a8565304..355817b5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Portugal
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Portugal
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/ROC b/absl/time/internal/cctz/testdata/zoneinfo/ROC
index 748873be..f9cbe672 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/ROC
+++ b/absl/time/internal/cctz/testdata/zoneinfo/ROC
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/ROK b/absl/time/internal/cctz/testdata/zoneinfo/ROK
index 312ec40a..fa1cbd39 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/ROK
+++ b/absl/time/internal/cctz/testdata/zoneinfo/ROK
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Singapore b/absl/time/internal/cctz/testdata/zoneinfo/Singapore
index 78583666..ebc4b0d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Singapore
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Singapore
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Turkey b/absl/time/internal/cctz/testdata/zoneinfo/Turkey
index 9a53b3a3..833d4eba 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Turkey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Turkey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/UCT b/absl/time/internal/cctz/testdata/zoneinfo/UCT
index 40147b9e..a88c4b66 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/UCT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/UCT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska b/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska
index 6c8bdf22..9bbb2fd3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian b/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian
index 5696e0f8..43236498 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona b/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona
index adf28236..4d51271a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Central b/absl/time/internal/cctz/testdata/zoneinfo/US/Central
index 3dd8f0fa..a5b1617c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Central
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Central
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana b/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana
index 4a92c065..09511ccd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern b/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern
index 7553fee3..2f75480e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii b/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii
index bd855772..c7cd0601 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke b/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke
index cc785da9..fcd408d7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan b/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan
index e3ea5c3e..5e022605 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain b/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain
index 7fc66917..5fbe26b1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific b/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific
index c0ce4402..9dad4f4c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa b/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa
index 3e38e97c..72707b5e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/UTC b/absl/time/internal/cctz/testdata/zoneinfo/UTC
index c3b97f1a..5583f5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/UTC
+++ b/absl/time/internal/cctz/testdata/zoneinfo/UTC
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Universal b/absl/time/internal/cctz/testdata/zoneinfo/Universal
index c3b97f1a..5583f5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Universal
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Universal
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/W-SU b/absl/time/internal/cctz/testdata/zoneinfo/W-SU
index 906bd05f..ddb3f4e9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/W-SU
+++ b/absl/time/internal/cctz/testdata/zoneinfo/W-SU
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/WET b/absl/time/internal/cctz/testdata/zoneinfo/WET
index 444a1933..9b03a17f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/WET
+++ b/absl/time/internal/cctz/testdata/zoneinfo/WET
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Zulu b/absl/time/internal/cctz/testdata/zoneinfo/Zulu
index c3b97f1a..5583f5b0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Zulu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Zulu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
index 2d90ed72..2729e6e8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -1,35 +1,35 @@
-# tz zone descriptions
+# tzdb timezone descriptions
#
# This file is in the public domain.
#
-# From Paul Eggert (2017-10-01):
-# This file contains a table where each row stands for a zone where
-# civil time stamps have agreed since 1970. Columns are separated by
+# From Paul Eggert (2018-06-27):
+# This file contains a table where each row stands for a timezone where
+# civil timestamps have agreed since 1970. Columns are separated by
# a single tab. Lines beginning with '#' are comments. All text uses
# UTF-8 encoding. The columns of the table are as follows:
#
-# 1. The countries that overlap the zone, as a comma-separated list
+# 1. The countries that overlap the timezone, as a comma-separated list
# of ISO 3166 2-character country codes. See the file 'iso3166.tab'.
-# 2. Latitude and longitude of the zone's principal location
+# 2. Latitude and longitude of the timezone's principal location
# in ISO 6709 sign-degrees-minutes-seconds format,
# either ±DDMM±DDDMM or ±DDMMSS±DDDMMSS,
# first latitude (+ is north), then longitude (+ is east).
-# 3. Zone name used in value of TZ environment variable.
-# Please see the theory.html file for how zone names are chosen.
-# If multiple zones overlap a country, each has a row in the
+# 3. Timezone name used in value of TZ environment variable.
+# Please see the theory.html file for how these names are chosen.
+# If multiple timezones overlap a country, each has a row in the
# table, with each column 1 containing the country code.
-# 4. Comments; present if and only if a country has multiple zones.
+# 4. Comments; present if and only if a country has multiple timezones.
#
-# If a zone covers multiple countries, the most-populous city is used,
+# If a timezone covers multiple countries, the most-populous city is used,
# and that country is listed first in column 1; any other countries
# are listed alphabetically by country code. The table is sorted
# first by country code, then (if possible) by an order within the
# country that (1) makes some geographical sense, and (2) puts the
-# most populous zones first, where that does not contradict (1).
+# most populous timezones first, where that does not contradict (1).
#
-# This table is intended as an aid for users, to help them select time
-# zone data entries appropriate for their practical needs. It is not
-# intended to take or endorse any position on legal or territorial claims.
+# This table is intended as an aid for users, to help them select timezones
+# appropriate for their practical needs. It is not intended to take or
+# endorse any position on legal or territorial claims.
#
#country-
#codes coordinates TZ comments
@@ -231,7 +231,7 @@ MM +1647+09610 Asia/Yangon
MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas)
MN +4801+09139 Asia/Hovd Bayan-Ölgii, Govi-Altai, Hovd, Uvs, Zavkhan
MN +4804+11430 Asia/Choibalsan Dornod, Sükhbaatar
-MO +2214+11335 Asia/Macau
+MO +221150+1133230 Asia/Macau
MQ +1436-06105 America/Martinique
MT +3554+01431 Europe/Malta
MU -2010+05730 Indian/Mauritius
@@ -289,9 +289,9 @@ RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade
RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad
RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area
RU +4457+03406 Europe/Simferopol MSK+00 - Crimea
-RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd
RU +5836+04939 Europe/Kirov MSK+00 - Kirov
RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan
+RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd
RU +5134+04602 Europe/Saratov MSK+01 - Saratov
RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk
RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia
diff --git a/absl/time/internal/get_current_time_chrono.inc b/absl/time/internal/get_current_time_chrono.inc
new file mode 100644
index 00000000..ba016e3e
--- /dev/null
+++ b/absl/time/internal/get_current_time_chrono.inc
@@ -0,0 +1,31 @@
+// 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
+//
+// 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.
+
+#include <chrono>
+#include <cstdint>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace time_internal {
+
+static int64_t GetCurrentTimeNanosFromSystem() {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::system_clock::now() -
+ std::chrono::system_clock::from_time_t(0))
+ .count();
+}
+
+} // namespace time_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/time/internal/get_current_time_ios.inc b/absl/time/internal/get_current_time_ios.inc
deleted file mode 100644
index abd43cb4..00000000
--- a/absl/time/internal/get_current_time_ios.inc
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "absl/time/clock.h"
-
-#include <sys/time.h>
-#include <ctime>
-#include <cstdint>
-
-#include "absl/base/internal/raw_logging.h"
-
-// These are not defined in the Xcode 7.3.1 SDK Headers.
-// Once we are no longer supporting Xcode 7.3.1 we can
-// remove these.
-#ifndef __WATCHOS_3_0
-#define __WATCHOS_3_0 30000
-#endif
-
-#ifndef __TVOS_10_0
-#define __TVOS_10_0 100000
-#endif
-
-#ifndef __IPHONE_10_0
-#define __IPHONE_10_0 100000
-#endif
-
-#ifndef __MAC_10_12
-#define __MAC_10_12 101200
-#endif
-
-namespace absl {
-inline namespace lts_2018_06_20 {
-namespace time_internal {
-
-static int64_t GetCurrentTimeNanosFromSystem() {
-#if (__MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_12) || \
- (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0) || \
- (__WATCH_OS_VERSION_MAX_ALLOWED >= __WATCHOS_3_0) || \
- (__TV_OS_VERSION_MAX_ALLOWED >= __TVOS_10_0)
- // clock_gettime_nsec_np is not defined on SDKs before Xcode 8.0.
- // This preprocessor logic is based upon __CLOCK_AVAILABILITY in
- // usr/include/time.h. Once we are no longer supporting Xcode 7.3.1 we can
- // remove this #if.
- // We must continue to check if it is defined until we are sure that ALL the
- // platforms we are shipping on support it.
- // clock_gettime_nsec_np is preferred because it may give higher accuracy than
- // gettimeofday in future Apple operating systems.
- // Currently (macOS 10.12/iOS 10.2) clock_gettime_nsec_np accuracy is
- // microsecond accuracy (i.e. equivalent to gettimeofday).
- if (&clock_gettime_nsec_np != nullptr) {
- return clock_gettime_nsec_np(CLOCK_REALTIME);
- }
-#endif
-#if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \
- (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12)) || \
- (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \
- (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0)) || \
- (defined(__WATCH_OS_VERSION_MIN_REQUIRED) && \
- (__WATCH_OS_VERSION_MIN_REQUIRED < __WATCHOS_3_0)) || \
- (defined(__TV_OS_VERSION_MIN_REQUIRED) && \
- (__TV_OS_VERSION_MIN_REQUIRED < __TVOS_10_0))
- // We need this block in 2 different cases:
- // a) where we are compiling with Xcode 7 in which case the block above
- // will not be compiled in, and this is the only block executed.
- // b) where we are compiling with Xcode 8+ but supporting operating systems
- // that do not define clock_gettime_nsec_np, so this is in effect
- // an else block to the block above.
- // This block will not be compiled if the min supported version is
- // guaranteed to supply clock_gettime_nsec_np.
- //
- // Once we know that clock_gettime_nsec_np is in the SDK *AND* exists on
- // all the platforms we support, we can remove both this block and alter the
- // block above to just call clock_gettime_nsec_np directly.
- const int64_t kNanosPerSecond = 1000 * 1000 * 1000;
- const int64_t kNanosPerMicrosecond = 1000;
- struct timeval tp;
- ABSL_RAW_CHECK(gettimeofday(&tp, nullptr) == 0, "Failed gettimeofday");
- return (int64_t{tp.tv_sec} * kNanosPerSecond +
- int64_t{tp.tv_usec} * kNanosPerMicrosecond);
-#endif
-}
-
-} // namespace time_internal
-} // inline namespace lts_2018_06_20
-} // namespace absl
diff --git a/absl/time/internal/get_current_time_posix.inc b/absl/time/internal/get_current_time_posix.inc
index dfbcf8a6..3ff45c4a 100644
--- a/absl/time/internal/get_current_time_posix.inc
+++ b/absl/time/internal/get_current_time_posix.inc
@@ -7,7 +7,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
static int64_t GetCurrentTimeNanosFromSystem() {
@@ -20,5 +20,5 @@ static int64_t GetCurrentTimeNanosFromSystem() {
}
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/get_current_time_windows.inc b/absl/time/internal/get_current_time_windows.inc
deleted file mode 100644
index 273d2d80..00000000
--- a/absl/time/internal/get_current_time_windows.inc
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "absl/time/clock.h"
-
-#include <chrono>
-#include <cstdint>
-
-namespace absl {
-inline namespace lts_2018_06_20 {
-namespace time_internal {
-
-static int64_t GetCurrentTimeNanosFromSystem() {
- return std::chrono::duration_cast<std::chrono::nanoseconds>(
- std::chrono::system_clock::now() -
- std::chrono::system_clock::from_time_t(0))
- .count();
-}
-
-} // namespace time_internal
-} // inline namespace lts_2018_06_20
-} // namespace absl
diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc
index 1cc89b5e..69530e64 100644
--- a/absl/time/internal/test_util.cc
+++ b/absl/time/internal/test_util.cc
@@ -24,15 +24,9 @@
namespace cctz = absl::time_internal::cctz;
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
-#if GTEST_USES_SIMPLE_RE
-extern const char kZoneAbbrRE[] = ".*"; // just punt
-#else
-extern const char kZoneAbbrRE[] = "[A-Za-z]{3,4}|[-+][0-9]{2}([0-9]{2})?";
-#endif
-
TimeZone LoadTimeZone(const std::string& name) {
TimeZone tz;
ABSL_RAW_CHECK(LoadTimeZone(name, &tz), name.c_str());
@@ -40,11 +34,11 @@ TimeZone LoadTimeZone(const std::string& name) {
}
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
namespace cctz_extension {
namespace {
@@ -129,5 +123,5 @@ ZoneInfoSourceFactory zone_info_source_factory = TestFactory;
} // namespace cctz_extension
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/internal/test_util.h b/absl/time/internal/test_util.h
index fb65f9bd..31ec18e4 100644
--- a/absl/time/internal/test_util.h
+++ b/absl/time/internal/test_util.h
@@ -17,41 +17,17 @@
#include <string>
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
#include "absl/time/time.h"
-// This helper is a macro so that failed expectations show up with the
-// correct line numbers.
-//
-// This is for internal testing of the Base Time library itself. This is not
-// part of a public API.
-#define ABSL_INTERNAL_EXPECT_TIME(bd, y, m, d, h, min, s, off, isdst) \
- do { \
- EXPECT_EQ(y, bd.year); \
- EXPECT_EQ(m, bd.month); \
- EXPECT_EQ(d, bd.day); \
- EXPECT_EQ(h, bd.hour); \
- EXPECT_EQ(min, bd.minute); \
- EXPECT_EQ(s, bd.second); \
- EXPECT_EQ(off, bd.offset); \
- EXPECT_EQ(isdst, bd.is_dst); \
- EXPECT_THAT(bd.zone_abbr, \
- testing::MatchesRegex(absl::time_internal::kZoneAbbrRE)); \
- } while (0)
-
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace time_internal {
-// A regular expression that matches all zone abbreviations (%Z).
-extern const char kZoneAbbrRE[];
-
// Loads the named timezone, but dies on any failure.
absl::TimeZone LoadTimeZone(const std::string& name);
} // namespace time_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_INTERNAL_TEST_UTIL_H_
diff --git a/absl/time/time.cc b/absl/time/time.cc
index 76ce12c9..e60857e2 100644
--- a/absl/time/time.cc
+++ b/absl/time/time.cc
@@ -22,13 +22,14 @@
// NOTE: To keep type verbosity to a minimum, the following variable naming
// conventions are used throughout this file.
//
-// cz: A cctz::time_zone
// tz: An absl::TimeZone
+// ci: An absl::TimeZone::CivilInfo
+// ti: An absl::TimeZone::TimeInfo
+// cd: An absl::CivilDay or a cctz::civil_day
+// cs: An absl::CivilSecond or a cctz::civil_second
+// bd: An absl::Time::Breakdown
// cl: A cctz::time_zone::civil_lookup
// al: A cctz::time_zone::absolute_lookup
-// cd: A cctz::civil_day
-// cs: A cctz::civil_second
-// bd: An absl::Time::Breakdown
#include "absl/time/time.h"
@@ -41,12 +42,12 @@
namespace cctz = absl::time_internal::cctz;
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
-inline cctz::time_point<cctz::sys_seconds> unix_epoch() {
- return std::chrono::time_point_cast<cctz::sys_seconds>(
+inline cctz::time_point<cctz::seconds> unix_epoch() {
+ return std::chrono::time_point_cast<cctz::seconds>(
std::chrono::system_clock::from_time_t(0));
}
@@ -76,7 +77,7 @@ inline absl::Time::Breakdown InfiniteFutureBreakdown() {
return bd;
}
-inline Time::Breakdown InfinitePastBreakdown() {
+inline absl::Time::Breakdown InfinitePastBreakdown() {
Time::Breakdown bd;
bd.year = std::numeric_limits<int64_t>::min();
bd.month = 1;
@@ -93,6 +94,26 @@ inline Time::Breakdown InfinitePastBreakdown() {
return bd;
}
+inline absl::TimeZone::CivilInfo InfiniteFutureCivilInfo() {
+ TimeZone::CivilInfo ci;
+ ci.cs = CivilSecond::max();
+ ci.subsecond = InfiniteDuration();
+ ci.offset = 0;
+ ci.is_dst = false;
+ ci.zone_abbr = "-00";
+ return ci;
+}
+
+inline absl::TimeZone::CivilInfo InfinitePastCivilInfo() {
+ TimeZone::CivilInfo ci;
+ ci.cs = CivilSecond::min();
+ ci.subsecond = -InfiniteDuration();
+ ci.offset = 0;
+ ci.is_dst = false;
+ ci.zone_abbr = "-00";
+ return ci;
+}
+
inline absl::TimeConversion InfiniteFutureTimeConversion() {
absl::TimeConversion tc;
tc.pre = tc.trans = tc.post = absl::InfiniteFuture();
@@ -111,12 +132,12 @@ inline TimeConversion InfinitePastTimeConversion() {
// Makes a Time from sec, overflowing to InfiniteFuture/InfinitePast as
// necessary. If sec is min/max, then consult cs+tz to check for overlow.
-Time MakeTimeWithOverflow(const cctz::time_point<cctz::sys_seconds>& sec,
+Time MakeTimeWithOverflow(const cctz::time_point<cctz::seconds>& sec,
const cctz::civil_second& cs,
const cctz::time_zone& tz,
bool* normalized = nullptr) {
- const auto max = cctz::time_point<cctz::sys_seconds>::max();
- const auto min = cctz::time_point<cctz::sys_seconds>::min();
+ const auto max = cctz::time_point<cctz::seconds>::max();
+ const auto min = cctz::time_point<cctz::seconds>::min();
if (sec == max) {
const auto al = tz.lookup(max);
if (cs > al.cs) {
@@ -135,19 +156,6 @@ Time MakeTimeWithOverflow(const cctz::time_point<cctz::sys_seconds>& sec,
return time_internal::FromUnixDuration(time_internal::MakeDuration(hi));
}
-inline absl::TimeConversion::Kind MapKind(
- const cctz::time_zone::civil_lookup::civil_kind& kind) {
- switch (kind) {
- case cctz::time_zone::civil_lookup::UNIQUE:
- return absl::TimeConversion::UNIQUE;
- case cctz::time_zone::civil_lookup::SKIPPED:
- return absl::TimeConversion::SKIPPED;
- case cctz::time_zone::civil_lookup::REPEATED:
- return absl::TimeConversion::REPEATED;
- }
- return absl::TimeConversion::UNIQUE;
-}
-
// Returns Mon=1..Sun=7.
inline int MapWeekday(const cctz::weekday& wd) {
switch (wd) {
@@ -169,14 +177,31 @@ inline int MapWeekday(const cctz::weekday& wd) {
return 1;
}
+bool FindTransition(const cctz::time_zone& tz,
+ bool (cctz::time_zone::*find_transition)(
+ const cctz::time_point<cctz::seconds>& tp,
+ cctz::time_zone::civil_transition* trans) const,
+ Time t, TimeZone::CivilTransition* trans) {
+ // Transitions are second-aligned, so we can discard any fractional part.
+ const auto tp = unix_epoch() + cctz::seconds(ToUnixSeconds(t));
+ cctz::time_zone::civil_transition tr;
+ if (!(tz.*find_transition)(tp, &tr)) return false;
+ trans->from = CivilSecond(tr.from);
+ trans->to = CivilSecond(tr.to);
+ return true;
+}
+
} // namespace
+//
+// Time
+//
+
absl::Time::Breakdown Time::In(absl::TimeZone tz) const {
- if (*this == absl::InfiniteFuture()) return absl::InfiniteFutureBreakdown();
- if (*this == absl::InfinitePast()) return absl::InfinitePastBreakdown();
+ if (*this == absl::InfiniteFuture()) return InfiniteFutureBreakdown();
+ if (*this == absl::InfinitePast()) return InfinitePastBreakdown();
- const auto tp =
- unix_epoch() + cctz::sys_seconds(time_internal::GetRepHi(rep_));
+ const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(rep_));
const auto al = cctz::time_zone(tz).lookup(tp);
const auto cs = al.cs;
const auto cd = cctz::civil_day(cs);
@@ -189,92 +214,18 @@ absl::Time::Breakdown Time::In(absl::TimeZone tz) const {
bd.minute = cs.minute();
bd.second = cs.second();
bd.subsecond = time_internal::MakeDuration(0, time_internal::GetRepLo(rep_));
- bd.weekday = MapWeekday(get_weekday(cd));
- bd.yearday = get_yearday(cd);
+ bd.weekday = MapWeekday(cctz::get_weekday(cd));
+ bd.yearday = cctz::get_yearday(cd);
bd.offset = al.offset;
bd.is_dst = al.is_dst;
bd.zone_abbr = al.abbr;
return bd;
}
-absl::Time FromTM(const struct tm& tm, absl::TimeZone tz) {
- const auto cz = cctz::time_zone(tz);
- const auto cs =
- cctz::civil_second(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- const auto cl = cz.lookup(cs);
- const auto tp = tm.tm_isdst == 0 ? cl.post : cl.pre;
- return MakeTimeWithOverflow(tp, cs, cz);
-}
-
-struct tm ToTM(absl::Time t, absl::TimeZone tz) {
- const absl::Time::Breakdown bd = t.In(tz);
- struct tm tm;
- std::memset(&tm, 0, sizeof(tm));
- tm.tm_sec = bd.second;
- tm.tm_min = bd.minute;
- tm.tm_hour = bd.hour;
- tm.tm_mday = bd.day;
- tm.tm_mon = bd.month - 1;
-
- // Saturates tm.tm_year in cases of over/underflow, accounting for the fact
- // that tm.tm_year is years since 1900.
- if (bd.year < std::numeric_limits<int>::min() + 1900) {
- tm.tm_year = std::numeric_limits<int>::min();
- } else if (bd.year > std::numeric_limits<int>::max()) {
- tm.tm_year = std::numeric_limits<int>::max() - 1900;
- } else {
- tm.tm_year = static_cast<int>(bd.year - 1900);
- }
-
- tm.tm_wday = bd.weekday % 7;
- tm.tm_yday = bd.yearday - 1;
- tm.tm_isdst = bd.is_dst ? 1 : 0;
-
- return tm;
-}
-
//
-// Factory functions.
+// Conversions from/to other time types.
//
-absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
- int min, int sec, TimeZone tz) {
- // Avoids years that are too extreme for civil_second to normalize.
- if (year > 300000000000) return InfiniteFutureTimeConversion();
- if (year < -300000000000) return InfinitePastTimeConversion();
- const auto cz = cctz::time_zone(tz);
- const auto cs = cctz::civil_second(year, mon, day, hour, min, sec);
- absl::TimeConversion tc;
- tc.normalized = year != cs.year() || mon != cs.month() || day != cs.day() ||
- hour != cs.hour() || min != cs.minute() || sec != cs.second();
- const auto cl = cz.lookup(cs);
- // Converts the civil_lookup struct to a TimeConversion.
- tc.pre = MakeTimeWithOverflow(cl.pre, cs, cz, &tc.normalized);
- tc.trans = MakeTimeWithOverflow(cl.trans, cs, cz, &tc.normalized);
- tc.post = MakeTimeWithOverflow(cl.post, cs, cz, &tc.normalized);
- tc.kind = MapKind(cl.kind);
- return tc;
-}
-
-absl::Time FromDateTime(int64_t year, int mon, int day, int hour, int min,
- int sec, TimeZone tz) {
- if (year > 300000000000) return InfiniteFuture();
- if (year < -300000000000) return InfinitePast();
- const auto cz = cctz::time_zone(tz);
- const auto cs = cctz::civil_second(year, mon, day, hour, min, sec);
- const auto cl = cz.lookup(cs);
- return MakeTimeWithOverflow(cl.pre, cs, cz);
-}
-
-absl::Time TimeFromTimespec(timespec ts) {
- return time_internal::FromUnixDuration(absl::DurationFromTimespec(ts));
-}
-
-absl::Time TimeFromTimeval(timeval tv) {
- return time_internal::FromUnixDuration(absl::DurationFromTimeval(tv));
-}
-
absl::Time FromUDate(double udate) {
return time_internal::FromUnixDuration(absl::Milliseconds(udate));
}
@@ -283,10 +234,6 @@ absl::Time FromUniversal(int64_t universal) {
return absl::UniversalEpoch() + 100 * absl::Nanoseconds(universal);
}
-//
-// Conversion to other time types.
-//
-
int64_t ToUnixNanos(Time t) {
if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 33 == 0) {
@@ -323,6 +270,23 @@ int64_t ToUnixSeconds(Time t) {
time_t ToTimeT(Time t) { return absl::ToTimespec(t).tv_sec; }
+double ToUDate(Time t) {
+ return absl::FDivDuration(time_internal::ToUnixDuration(t),
+ absl::Milliseconds(1));
+}
+
+int64_t ToUniversal(absl::Time t) {
+ return absl::FloorToUnit(t - absl::UniversalEpoch(), absl::Nanoseconds(100));
+}
+
+absl::Time TimeFromTimespec(timespec ts) {
+ return time_internal::FromUnixDuration(absl::DurationFromTimespec(ts));
+}
+
+absl::Time TimeFromTimeval(timeval tv) {
+ return time_internal::FromUnixDuration(absl::DurationFromTimeval(tv));
+}
+
timespec ToTimespec(Time t) {
timespec ts;
absl::Duration d = time_internal::ToUnixDuration(t);
@@ -361,15 +325,6 @@ timeval ToTimeval(Time t) {
return tv;
}
-double ToUDate(Time t) {
- return absl::FDivDuration(time_internal::ToUnixDuration(t),
- absl::Milliseconds(1));
-}
-
-int64_t ToUniversal(absl::Time t) {
- return absl::FloorToUnit(t - absl::UniversalEpoch(), absl::Nanoseconds(100));
-}
-
Time FromChrono(const std::chrono::system_clock::time_point& tp) {
return time_internal::FromUnixDuration(time_internal::FromChrono(
tp - std::chrono::system_clock::from_time_t(0)));
@@ -383,5 +338,150 @@ std::chrono::system_clock::time_point ToChronoTime(absl::Time t) {
time_internal::ToChronoDuration<D>(d);
}
-} // inline namespace lts_2018_06_20
+//
+// TimeZone
+//
+
+absl::TimeZone::CivilInfo TimeZone::At(Time t) const {
+ if (t == absl::InfiniteFuture()) return InfiniteFutureCivilInfo();
+ if (t == absl::InfinitePast()) return InfinitePastCivilInfo();
+
+ const auto ud = time_internal::ToUnixDuration(t);
+ const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(ud));
+ const auto al = cz_.lookup(tp);
+
+ TimeZone::CivilInfo ci;
+ ci.cs = CivilSecond(al.cs);
+ ci.subsecond = time_internal::MakeDuration(0, time_internal::GetRepLo(ud));
+ ci.offset = al.offset;
+ ci.is_dst = al.is_dst;
+ ci.zone_abbr = al.abbr;
+ return ci;
+}
+
+absl::TimeZone::TimeInfo TimeZone::At(CivilSecond ct) const {
+ const cctz::civil_second cs(ct);
+ const auto cl = cz_.lookup(cs);
+
+ TimeZone::TimeInfo ti;
+ switch (cl.kind) {
+ case cctz::time_zone::civil_lookup::UNIQUE:
+ ti.kind = TimeZone::TimeInfo::UNIQUE;
+ break;
+ case cctz::time_zone::civil_lookup::SKIPPED:
+ ti.kind = TimeZone::TimeInfo::SKIPPED;
+ break;
+ case cctz::time_zone::civil_lookup::REPEATED:
+ ti.kind = TimeZone::TimeInfo::REPEATED;
+ break;
+ }
+ ti.pre = MakeTimeWithOverflow(cl.pre, cs, cz_);
+ ti.trans = MakeTimeWithOverflow(cl.trans, cs, cz_);
+ ti.post = MakeTimeWithOverflow(cl.post, cs, cz_);
+ return ti;
+}
+
+bool TimeZone::NextTransition(Time t, CivilTransition* trans) const {
+ return FindTransition(cz_, &cctz::time_zone::next_transition, t, trans);
+}
+
+bool TimeZone::PrevTransition(Time t, CivilTransition* trans) const {
+ return FindTransition(cz_, &cctz::time_zone::prev_transition, t, trans);
+}
+
+//
+// Conversions involving time zones.
+//
+
+absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
+ int min, int sec, TimeZone tz) {
+ // Avoids years that are too extreme for CivilSecond to normalize.
+ if (year > 300000000000) return InfiniteFutureTimeConversion();
+ if (year < -300000000000) return InfinitePastTimeConversion();
+
+ const CivilSecond cs(year, mon, day, hour, min, sec);
+ const auto ti = tz.At(cs);
+
+ TimeConversion tc;
+ tc.pre = ti.pre;
+ tc.trans = ti.trans;
+ tc.post = ti.post;
+ switch (ti.kind) {
+ case TimeZone::TimeInfo::UNIQUE:
+ tc.kind = TimeConversion::UNIQUE;
+ break;
+ case TimeZone::TimeInfo::SKIPPED:
+ tc.kind = TimeConversion::SKIPPED;
+ break;
+ case TimeZone::TimeInfo::REPEATED:
+ tc.kind = TimeConversion::REPEATED;
+ break;
+ }
+ tc.normalized = false;
+ if (year != cs.year() || mon != cs.month() || day != cs.day() ||
+ hour != cs.hour() || min != cs.minute() || sec != cs.second()) {
+ tc.normalized = true;
+ }
+ return tc;
+}
+
+absl::Time FromTM(const struct tm& tm, absl::TimeZone tz) {
+ const CivilSecond cs(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ const auto ti = tz.At(cs);
+ return tm.tm_isdst == 0 ? ti.post : ti.pre;
+}
+
+struct tm ToTM(absl::Time t, absl::TimeZone tz) {
+ struct tm tm = {};
+
+ const auto ci = tz.At(t);
+ const auto& cs = ci.cs;
+ tm.tm_sec = cs.second();
+ tm.tm_min = cs.minute();
+ tm.tm_hour = cs.hour();
+ tm.tm_mday = cs.day();
+ tm.tm_mon = cs.month() - 1;
+
+ // Saturates tm.tm_year in cases of over/underflow, accounting for the fact
+ // that tm.tm_year is years since 1900.
+ if (cs.year() < std::numeric_limits<int>::min() + 1900) {
+ tm.tm_year = std::numeric_limits<int>::min();
+ } else if (cs.year() > std::numeric_limits<int>::max()) {
+ tm.tm_year = std::numeric_limits<int>::max() - 1900;
+ } else {
+ tm.tm_year = static_cast<int>(cs.year() - 1900);
+ }
+
+ const CivilDay cd(cs);
+ switch (GetWeekday(cd)) {
+ case Weekday::sunday:
+ tm.tm_wday = 0;
+ break;
+ case Weekday::monday:
+ tm.tm_wday = 1;
+ break;
+ case Weekday::tuesday:
+ tm.tm_wday = 2;
+ break;
+ case Weekday::wednesday:
+ tm.tm_wday = 3;
+ break;
+ case Weekday::thursday:
+ tm.tm_wday = 4;
+ break;
+ case Weekday::friday:
+ tm.tm_wday = 5;
+ break;
+ case Weekday::saturday:
+ tm.tm_wday = 6;
+ break;
+ }
+ tm.tm_yday = GetYearDay(cd) - 1;
+ tm.tm_isdst = ci.is_dst ? 1 : 0;
+
+ return tm;
+}
+
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/time/time.h b/absl/time/time.h
index 3b5739ff..3afec565 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -25,17 +25,29 @@
// * `absl::TimeZone` defines geopolitical time zone regions (as collected
// within the IANA Time Zone database (https://www.iana.org/time-zones)).
//
+// Note: Absolute times are distinct from civil times, which refer to the
+// human-scale time commonly represented by `YYYY-MM-DD hh:mm:ss`. The mapping
+// between absolute and civil times can be specified by use of time zones
+// (`absl::TimeZone` within this API). That is:
+//
+// Civil Time = F(Absolute Time, Time Zone)
+// Absolute Time = G(Civil Time, Time Zone)
+//
+// See civil_time.h for abstractions related to constructing and manipulating
+// civil time.
+//
// Example:
//
// absl::TimeZone nyc;
-//
-// // LoadTimeZone may fail so it's always better to check for success.
+// // LoadTimeZone() may fail so it's always better to check for success.
// if (!absl::LoadTimeZone("America/New_York", &nyc)) {
// // handle error case
// }
//
// // My flight leaves NYC on Jan 2, 2017 at 03:04:05
-// absl::Time takeoff = absl::FromDateTime(2017, 1, 2, 3, 4, 5, nyc);
+// absl::CivilSecond cs(2017, 1, 2, 3, 4, 5);
+// absl::Time takeoff = absl::FromCivil(cs, nyc);
+//
// absl::Duration flight_duration = absl::Hours(21) + absl::Minutes(35);
// absl::Time landing = takeoff + flight_duration;
//
@@ -47,15 +59,17 @@
// "My flight will land in Sydney on %Y-%m-%d at %H:%M:%S",
// landing, syd);
//
+
#ifndef ABSL_TIME_TIME_H_
#define ABSL_TIME_TIME_H_
-#if !defined(_WIN32)
+#if !defined(_MSC_VER)
#include <sys/time.h>
#else
#include <winsock2.h>
#endif
#include <chrono> // NOLINT(build/c++11)
+#include <cmath>
#include <cstdint>
#include <ctime>
#include <ostream>
@@ -65,10 +79,11 @@
#include "absl/base/port.h" // Needed for string vs std::string
#include "absl/strings/string_view.h"
+#include "absl/time/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
class Duration; // Defined below
class Time; // Defined below
@@ -82,6 +97,7 @@ constexpr int64_t GetRepHi(Duration d);
constexpr uint32_t GetRepLo(Duration d);
constexpr Duration MakeDuration(int64_t hi, uint32_t lo);
constexpr Duration MakeDuration(int64_t hi, int64_t lo);
+inline Duration MakePosDoubleDuration(double n);
constexpr int64_t kTicksPerNanosecond = 4;
constexpr int64_t kTicksPerSecond = 1000 * 1000 * 1000 * kTicksPerNanosecond;
template <std::intmax_t N>
@@ -139,6 +155,16 @@ class Duration {
// Value semantics.
constexpr Duration() : rep_hi_(0), rep_lo_(0) {} // zero-length duration
+ // Copyable.
+#if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1910
+ // Explicitly defining the constexpr copy constructor avoids an MSVC bug.
+ constexpr Duration(const Duration& d)
+ : rep_hi_(d.rep_hi_), rep_lo_(d.rep_lo_) {}
+#else
+ constexpr Duration(const Duration& d) = default;
+#endif
+ Duration& operator=(const Duration& d) = default;
+
// Compound assignment operators.
Duration& operator+=(Duration d);
Duration& operator-=(Duration d);
@@ -162,6 +188,11 @@ class Duration {
Duration& operator*=(float r) { return *this *= static_cast<double>(r); }
Duration& operator/=(float r) { return *this /= static_cast<double>(r); }
+ template <typename H>
+ friend H AbslHashValue(H h, Duration d) {
+ return H::combine(std::move(h), d.rep_hi_, d.rep_lo_);
+ }
+
private:
friend constexpr int64_t time_internal::GetRepHi(Duration d);
friend constexpr uint32_t time_internal::GetRepLo(Duration d);
@@ -296,6 +327,42 @@ Duration Floor(Duration d, Duration unit);
// absl::Duration c = absl::Ceil(d, absl::Microseconds(1)); // 123457us
Duration Ceil(Duration d, Duration unit);
+// InfiniteDuration()
+//
+// Returns an infinite `Duration`. To get a `Duration` representing negative
+// infinity, use `-InfiniteDuration()`.
+//
+// Duration arithmetic overflows to +/- infinity and saturates. In general,
+// arithmetic with `Duration` infinities is similar to IEEE 754 infinities
+// except where IEEE 754 NaN would be involved, in which case +/-
+// `InfiniteDuration()` is used in place of a "nan" Duration.
+//
+// Examples:
+//
+// constexpr absl::Duration inf = absl::InfiniteDuration();
+// const absl::Duration d = ... any finite duration ...
+//
+// inf == inf + inf
+// inf == inf + d
+// inf == inf - inf
+// -inf == d - inf
+//
+// inf == d * 1e100
+// inf == inf / 2
+// 0 == d / inf
+// INT64_MAX == inf / d
+//
+// d < inf
+// -inf < d
+//
+// // Division by zero returns infinity, or INT64_MIN/MAX where appropriate.
+// inf == d / 0
+// INT64_MAX == d / absl::ZeroDuration()
+//
+// The examples involving the `/` operator above also apply to `IDivDuration()`
+// and `FDivDuration()`.
+constexpr Duration InfiniteDuration();
+
// Nanoseconds()
// Microseconds()
// Milliseconds()
@@ -306,11 +373,11 @@ Duration Ceil(Duration d, Duration unit);
// Factory functions for constructing `Duration` values from an integral number
// of the unit indicated by the factory function's name.
//
-// Note: no "Days()" factory function exists because "a day" is ambiguous. Civil
-// days are not always 24 hours long, and a 24-hour duration often does not
-// correspond with a civil day. If a 24-hour duration is needed, use
-// `absl::Hours(24)`.
-//
+// Note: no "Days()" factory function exists because "a day" is ambiguous.
+// Civil days are not always 24 hours long, and a 24-hour duration often does
+// not correspond with a civil day. If a 24-hour duration is needed, use
+// `absl::Hours(24)`. (If you actually want a civil day, use absl::CivilDay
+// from civil_time.h.)
//
// Example:
//
@@ -329,6 +396,7 @@ constexpr Duration Hours(int64_t n);
// factories, which should be preferred.
//
// Example:
+//
// auto a = absl::Seconds(1.5); // OK
// auto b = absl::Milliseconds(1500); // BETTER
template <typename T, time_internal::EnableIfFloat<T> = 0>
@@ -345,7 +413,15 @@ Duration Milliseconds(T n) {
}
template <typename T, time_internal::EnableIfFloat<T> = 0>
Duration Seconds(T n) {
- return n * Seconds(1);
+ if (n >= 0) { // Note: `NaN >= 0` is false.
+ if (n >= (std::numeric_limits<int64_t>::max)()) return InfiniteDuration();
+ return time_internal::MakePosDoubleDuration(n);
+ } else {
+ if (std::isnan(n))
+ return std::signbit(n) ? -InfiniteDuration() : InfiniteDuration();
+ if (n <= (std::numeric_limits<int64_t>::min)()) return -InfiniteDuration();
+ return -time_internal::MakePosDoubleDuration(-n);
+ }
}
template <typename T, time_internal::EnableIfFloat<T> = 0>
Duration Minutes(T n) {
@@ -440,42 +516,9 @@ std::chrono::seconds ToChronoSeconds(Duration d);
std::chrono::minutes ToChronoMinutes(Duration d);
std::chrono::hours ToChronoHours(Duration d);
-// InfiniteDuration()
-//
-// Returns an infinite `Duration`. To get a `Duration` representing negative
-// infinity, use `-InfiniteDuration()`.
-//
-// Duration arithmetic overflows to +/- infinity and saturates. In general,
-// arithmetic with `Duration` infinities is similar to IEEE 754 infinities
-// except where IEEE 754 NaN would be involved, in which case +/-
-// `InfiniteDuration()` is used in place of a "nan" Duration.
-//
-// Examples:
-//
-// constexpr absl::Duration inf = absl::InfiniteDuration();
-// const absl::Duration d = ... any finite duration ...
-//
-// inf == inf + inf
-// inf == inf + d
-// inf == inf - inf
-// -inf == d - inf
-//
-// inf == d * 1e100
-// inf == inf / 2
-// 0 == d / inf
-// INT64_MAX == inf / d
-//
-// // Division by zero returns infinity, or INT64_MIN/MAX where appropriate.
-// inf == d / 0
-// INT64_MAX == d / absl::ZeroDuration()
-//
-// The examples involving the `/` operator above also apply to `IDivDuration()`
-// and `FDivDuration()`.
-constexpr Duration InfiniteDuration();
-
// FormatDuration()
//
-// Returns a std::string representing the duration in the form "72h3m0.5s".
+// Returns a string representing the duration in the form "72h3m0.5s".
// Returns "inf" or "-inf" for +/- `InfiniteDuration()`.
std::string FormatDuration(Duration d);
@@ -486,19 +529,16 @@ inline std::ostream& operator<<(std::ostream& os, Duration d) {
// ParseDuration()
//
-// Parses a duration std::string consisting of a possibly signed sequence of
+// Parses a duration string consisting of a possibly signed sequence of
// decimal numbers, each with an optional fractional part and a unit
// suffix. The valid suffixes are "ns", "us" "ms", "s", "m", and "h".
// Simple examples include "300ms", "-1.5h", and "2h45m". Parses "0" as
-// `ZeroDuration()`. Parses "inf" and "-inf" as +/- `InfiniteDuration()`.
+// `ZeroDuration()`. Parses "inf" and "-inf" as +/- `InfiniteDuration()`.
bool ParseDuration(const std::string& dur_string, Duration* d);
-// ParseFlag()
-//
+// Support for flag values of type Duration. Duration flags must be specified
+// in a format that is valid input for absl::ParseDuration().
bool ParseFlag(const std::string& text, Duration* dst, std::string* error);
-
-// UnparseFlag()
-//
std::string UnparseFlag(Duration d);
// Time
@@ -534,7 +574,7 @@ std::string UnparseFlag(Duration d);
//
// `absl::Time` uses a resolution that is high enough to avoid loss in
// precision, and a range that is wide enough to avoid overflow, when
-// converting between tick counts in most Google time scales (i.e., precision
+// converting between tick counts in most Google time scales (i.e., resolution
// of at least one nanosecond, and range +/-100 billion years). Conversions
// between the time scales are performed by truncating (towards negative
// infinity) to the nearest representable point.
@@ -544,7 +584,6 @@ std::string UnparseFlag(Duration d);
// absl::Time t1 = ...;
// absl::Time t2 = t1 + absl::Minutes(2);
// absl::Duration d = t2 - t1; // == absl::Minutes(2)
-// absl::Time::Breakdown bd = t1.In(absl::LocalTimeZone());
//
class Time {
public:
@@ -559,7 +598,11 @@ class Time {
// absl::Time t = absl::Now();
// absl::Time t = absl::TimeFromTimeval(tv);
// absl::Time t = absl::InfinitePast();
- constexpr Time() {}
+ constexpr Time() = default;
+
+ // Copyable.
+ constexpr Time(const Time& t) = default;
+ Time& operator=(const Time& t) = default;
// Assignment operators.
Time& operator+=(Duration d) {
@@ -578,7 +621,10 @@ class Time {
// intended to represent an instant in time. So, rather than passing
// a `Time::Breakdown` to a function, pass an `absl::Time` and an
// `absl::TimeZone`.
- struct Breakdown {
+ //
+ // Deprecated. Use `absl::TimeZone::CivilInfo`.
+ struct
+ Breakdown {
int64_t year; // year (e.g., 2013)
int month; // month of year [1:12]
int day; // day of month [1:31]
@@ -602,8 +648,15 @@ class Time {
// Time::In()
//
// Returns the breakdown of this instant in the given TimeZone.
+ //
+ // Deprecated. Use `absl::TimeZone::At(Time)`.
Breakdown In(TimeZone tz) const;
+ template <typename H>
+ friend H AbslHashValue(H h, Time t) {
+ return H::combine(std::move(h), t.rep_);
+ }
+
private:
friend constexpr Time time_internal::FromUnixDuration(Duration d);
friend constexpr Duration time_internal::ToUnixDuration(Time t);
@@ -651,7 +704,7 @@ constexpr Time UniversalEpoch() {
// Returns an `absl::Time` that is infinitely far in the future.
constexpr Time InfiniteFuture() {
return Time(
- time_internal::MakeDuration(std::numeric_limits<int64_t>::max(), ~0U));
+ time_internal::MakeDuration((std::numeric_limits<int64_t>::max)(), ~0U));
}
// InfinitePast()
@@ -659,125 +712,9 @@ constexpr Time InfiniteFuture() {
// Returns an `absl::Time` that is infinitely far in the past.
constexpr Time InfinitePast() {
return Time(
- time_internal::MakeDuration(std::numeric_limits<int64_t>::min(), ~0U));
+ time_internal::MakeDuration((std::numeric_limits<int64_t>::min)(), ~0U));
}
-// TimeConversion
-//
-// An `absl::TimeConversion` represents the conversion of year, month, day,
-// hour, minute, and second values (i.e., a civil time), in a particular
-// `absl::TimeZone`, to a time instant (an absolute time), as returned by
-// `absl::ConvertDateTime()`. (Subseconds must be handled separately.)
-//
-// It is possible, though, for a caller to try to convert values that
-// do not represent an actual or unique instant in time (due to a shift
-// in UTC offset in the `absl::TimeZone`, which results in a discontinuity in
-// the civil-time components). For example, a daylight-saving-time
-// transition skips or repeats civil times---in the United States, March
-// 13, 2011 02:15 never occurred, while November 6, 2011 01:15 occurred
-// twice---so requests for such times are not well-defined.
-//
-// To account for these possibilities, `absl::TimeConversion` is richer
-// than just a single `absl::Time`. When the civil time is skipped or
-// repeated, `absl::ConvertDateTime()` returns times calculated using the
-// pre-transition and post-transition UTC offsets, plus the transition
-// time itself.
-//
-// Examples:
-//
-// absl::TimeZone lax;
-// if (!absl::LoadTimeZone("America/Los_Angeles", &lax)) { ... }
-//
-// // A unique civil time
-// absl::TimeConversion jan01 =
-// absl::ConvertDateTime(2011, 1, 1, 0, 0, 0, lax);
-// // jan01.kind == TimeConversion::UNIQUE
-// // jan01.pre is 2011/01/01 00:00:00 -0800
-// // jan01.trans is 2011/01/01 00:00:00 -0800
-// // jan01.post is 2011/01/01 00:00:00 -0800
-//
-// // A Spring DST transition, when there is a gap in civil time
-// absl::TimeConversion mar13 =
-// absl::ConvertDateTime(2011, 3, 13, 2, 15, 0, lax);
-// // mar13.kind == TimeConversion::SKIPPED
-// // mar13.pre is 2011/03/13 03:15:00 -0700
-// // mar13.trans is 2011/03/13 03:00:00 -0700
-// // mar13.post is 2011/03/13 01:15:00 -0800
-//
-// // A Fall DST transition, when civil times are repeated
-// absl::TimeConversion nov06 =
-// absl::ConvertDateTime(2011, 11, 6, 1, 15, 0, lax);
-// // nov06.kind == TimeConversion::REPEATED
-// // nov06.pre is 2011/11/06 01:15:00 -0700
-// // nov06.trans is 2011/11/06 01:00:00 -0800
-// // nov06.post is 2011/11/06 01:15:00 -0800
-//
-// The input month, day, hour, minute, and second values can also be
-// outside of their valid ranges, in which case they will be "normalized"
-// during the conversion.
-//
-// Example:
-//
-// // "October 32" normalizes to "November 1".
-// absl::TimeZone tz = absl::LocalTimeZone();
-// absl::TimeConversion tc =
-// absl::ConvertDateTime(2013, 10, 32, 8, 30, 0, tz);
-// // tc.kind == TimeConversion::UNIQUE && tc.normalized == true
-// // tc.pre.In(tz).month == 11 && tc.pre.In(tz).day == 1
-struct TimeConversion {
- Time pre; // time calculated using the pre-transition offset
- Time trans; // when the civil-time discontinuity occurred
- Time post; // time calculated using the post-transition offset
-
- enum Kind {
- UNIQUE, // the civil time was singular (pre == trans == post)
- SKIPPED, // the civil time did not exist
- REPEATED, // the civil time was ambiguous
- };
- Kind kind;
-
- bool normalized; // input values were outside their valid ranges
-};
-
-// ConvertDateTime()
-//
-// The full generality of a civil time to absl::Time conversion.
-TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
- int min, int sec, TimeZone tz);
-
-// FromDateTime()
-//
-// A convenience wrapper for `absl::ConvertDateTime()` that simply returns the
-// "pre" `absl::Time`. That is, the unique result, or the instant that
-// is correct using the pre-transition offset (as if the transition
-// never happened). This is typically the answer that humans expected when
-// faced with non-unique times, such as near daylight-saving time transitions.
-//
-// Example:
-//
-// absl::TimeZone seattle;
-// if (!absl::LoadTimeZone("America/Los_Angeles", &seattle)) { ... }
-// absl::Time t = absl::FromDateTime(2017, 9, 26, 9, 30, 0, seattle);
-Time FromDateTime(int64_t year, int mon, int day, int hour, int min, int sec,
- TimeZone tz);
-
-// FromTM()
-//
-// Converts the `tm_year`, `tm_mon`, `tm_mday`, `tm_hour`, `tm_min`, and
-// `tm_sec` fields to an `absl::Time` using the given time zone. See ctime(3)
-// for a description of the expected values of the tm fields. IFF the indicated
-// time instant is not unique (see `absl::ConvertDateTime()` above), the
-// `tm_isdst` field is consulted to select the desired instant (`tm_isdst` > 0
-// means DST, `tm_isdst` == 0 means no DST, `tm_isdst` < 0 means use the default
-// like `absl::FromDateTime()`).
-Time FromTM(const struct tm& tm, TimeZone tz);
-
-// ToTM()
-//
-// Converts the given `absl::Time` to a struct tm using the given time zone.
-// See ctime(3) for a description of the values of the tm fields.
-struct tm ToTM(Time t, TimeZone tz);
-
// FromUnixNanos()
// FromUnixMicros()
// FromUnixMillis()
@@ -862,14 +799,390 @@ Time FromChrono(const std::chrono::system_clock::time_point& tp);
// // tp == std::chrono::system_clock::from_time_t(123);
std::chrono::system_clock::time_point ToChronoTime(Time);
+// Support for flag values of type Time. Time flags must be specified in a
+// format that matches absl::RFC3339_full. For example:
+//
+// --start_time=2016-01-02T03:04:05.678+08:00
+//
+// Note: A UTC offset (or 'Z' indicating a zero-offset from UTC) is required.
+//
+// Additionally, if you'd like to specify a time as a count of
+// seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag
+// and add that duration to absl::UnixEpoch() to get an absl::Time.
+bool ParseFlag(const std::string& text, Time* t, std::string* error);
+std::string UnparseFlag(Time t);
+
+// TimeZone
+//
+// The `absl::TimeZone` is an opaque, small, value-type class representing a
+// geo-political region within which particular rules are used for converting
+// between absolute and civil times (see https://git.io/v59Ly). `absl::TimeZone`
+// values are named using the TZ identifiers from the IANA Time Zone Database,
+// such as "America/Los_Angeles" or "Australia/Sydney". `absl::TimeZone` values
+// are created from factory functions such as `absl::LoadTimeZone()`. Note:
+// strings like "PST" and "EDT" are not valid TZ identifiers. Prefer to pass by
+// value rather than const reference.
+//
+// For more on the fundamental concepts of time zones, absolute times, and civil
+// times, see https://github.com/google/cctz#fundamental-concepts
+//
+// Examples:
+//
+// absl::TimeZone utc = absl::UTCTimeZone();
+// absl::TimeZone pst = absl::FixedTimeZone(-8 * 60 * 60);
+// absl::TimeZone loc = absl::LocalTimeZone();
+// absl::TimeZone lax;
+// if (!absl::LoadTimeZone("America/Los_Angeles", &lax)) {
+// // handle error case
+// }
+//
+// See also:
+// - https://github.com/google/cctz
+// - http://www.iana.org/time-zones
+// - http://en.wikipedia.org/wiki/Zoneinfo
+class TimeZone {
+ public:
+ explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {}
+ TimeZone() = default; // UTC, but prefer UTCTimeZone() to be explicit.
+
+ // Copyable.
+ TimeZone(const TimeZone&) = default;
+ TimeZone& operator=(const TimeZone&) = default;
+
+ explicit operator time_internal::cctz::time_zone() const { return cz_; }
+
+ std::string name() const { return cz_.name(); }
+
+ // TimeZone::CivilInfo
+ //
+ // Information about the civil time corresponding to an absolute time.
+ // This struct is not intended to represent an instant in time. So, rather
+ // than passing a `TimeZone::CivilInfo` to a function, pass an `absl::Time`
+ // and an `absl::TimeZone`.
+ struct CivilInfo {
+ CivilSecond cs;
+ Duration subsecond;
+
+ // Note: The following fields exist for backward compatibility
+ // with older APIs. Accessing these fields directly is a sign of
+ // imprudent logic in the calling code. Modern time-related code
+ // should only access this data indirectly by way of FormatTime().
+ // These fields are undefined for InfiniteFuture() and InfinitePast().
+ int offset; // seconds east of UTC
+ bool is_dst; // is offset non-standard?
+ const char* zone_abbr; // time-zone abbreviation (e.g., "PST")
+ };
+
+ // TimeZone::At(Time)
+ //
+ // Returns the civil time for this TimeZone at a certain `absl::Time`.
+ // If the input time is infinite, the output civil second will be set to
+ // CivilSecond::max() or min(), and the subsecond will be infinite.
+ //
+ // Example:
+ //
+ // const auto epoch = lax.At(absl::UnixEpoch());
+ // // epoch.cs == 1969-12-31 16:00:00
+ // // epoch.subsecond == absl::ZeroDuration()
+ // // epoch.offset == -28800
+ // // epoch.is_dst == false
+ // // epoch.abbr == "PST"
+ CivilInfo At(Time t) const;
+
+ // TimeZone::TimeInfo
+ //
+ // Information about the absolute times corresponding to a civil time.
+ // (Subseconds must be handled separately.)
+ //
+ // It is possible for a caller to pass a civil-time value that does
+ // not represent an actual or unique instant in time (due to a shift
+ // in UTC offset in the TimeZone, which results in a discontinuity in
+ // the civil-time components). For example, a daylight-saving-time
+ // transition skips or repeats civil times---in the United States,
+ // March 13, 2011 02:15 never occurred, while November 6, 2011 01:15
+ // occurred twice---so requests for such times are not well-defined.
+ // To account for these possibilities, `absl::TimeZone::TimeInfo` is
+ // richer than just a single `absl::Time`.
+ struct TimeInfo {
+ enum CivilKind {
+ UNIQUE, // the civil time was singular (pre == trans == post)
+ SKIPPED, // the civil time did not exist (pre >= trans > post)
+ REPEATED, // the civil time was ambiguous (pre < trans <= post)
+ } kind;
+ Time pre; // time calculated using the pre-transition offset
+ Time trans; // when the civil-time discontinuity occurred
+ Time post; // time calculated using the post-transition offset
+ };
+
+ // TimeZone::At(CivilSecond)
+ //
+ // Returns an `absl::TimeInfo` containing the absolute time(s) for this
+ // TimeZone at an `absl::CivilSecond`. When the civil time is skipped or
+ // repeated, returns times calculated using the pre-transition and post-
+ // transition UTC offsets, plus the transition time itself.
+ //
+ // Examples:
+ //
+ // // A unique civil time
+ // const auto jan01 = lax.At(absl::CivilSecond(2011, 1, 1, 0, 0, 0));
+ // // jan01.kind == TimeZone::TimeInfo::UNIQUE
+ // // jan01.pre is 2011-01-01 00:00:00 -0800
+ // // jan01.trans is 2011-01-01 00:00:00 -0800
+ // // jan01.post is 2011-01-01 00:00:00 -0800
+ //
+ // // A Spring DST transition, when there is a gap in civil time
+ // const auto mar13 = lax.At(absl::CivilSecond(2011, 3, 13, 2, 15, 0));
+ // // mar13.kind == TimeZone::TimeInfo::SKIPPED
+ // // mar13.pre is 2011-03-13 03:15:00 -0700
+ // // mar13.trans is 2011-03-13 03:00:00 -0700
+ // // mar13.post is 2011-03-13 01:15:00 -0800
+ //
+ // // A Fall DST transition, when civil times are repeated
+ // const auto nov06 = lax.At(absl::CivilSecond(2011, 11, 6, 1, 15, 0));
+ // // nov06.kind == TimeZone::TimeInfo::REPEATED
+ // // nov06.pre is 2011-11-06 01:15:00 -0700
+ // // nov06.trans is 2011-11-06 01:00:00 -0800
+ // // nov06.post is 2011-11-06 01:15:00 -0800
+ TimeInfo At(CivilSecond ct) const;
+
+ // TimeZone::NextTransition()
+ // TimeZone::PrevTransition()
+ //
+ // Finds the time of the next/previous offset change in this time zone.
+ //
+ // By definition, `NextTransition(t, &trans)` returns false when `t` is
+ // `InfiniteFuture()`, and `PrevTransition(t, &trans)` returns false
+ // when `t` is `InfinitePast()`. If the zone has no transitions, the
+ // result will also be false no matter what the argument.
+ //
+ // Otherwise, when `t` is `InfinitePast()`, `NextTransition(t, &trans)`
+ // returns true and sets `trans` to the first recorded transition. Chains
+ // of calls to `NextTransition()/PrevTransition()` will eventually return
+ // false, but it is unspecified exactly when `NextTransition(t, &trans)`
+ // jumps to false, or what time is set by `PrevTransition(t, &trans)` for
+ // a very distant `t`.
+ //
+ // Note: Enumeration of time-zone transitions is for informational purposes
+ // only. Modern time-related code should not care about when offset changes
+ // occur.
+ //
+ // Example:
+ // absl::TimeZone nyc;
+ // if (!absl::LoadTimeZone("America/New_York", &nyc)) { ... }
+ // const auto now = absl::Now();
+ // auto t = absl::InfinitePast();
+ // absl::TimeZone::CivilTransition trans;
+ // while (t <= now && nyc.NextTransition(t, &trans)) {
+ // // transition: trans.from -> trans.to
+ // t = nyc.At(trans.to).trans;
+ // }
+ struct CivilTransition {
+ CivilSecond from; // the civil time we jump from
+ CivilSecond to; // the civil time we jump to
+ };
+ bool NextTransition(Time t, CivilTransition* trans) const;
+ bool PrevTransition(Time t, CivilTransition* trans) const;
+
+ template <typename H>
+ friend H AbslHashValue(H h, TimeZone tz) {
+ return H::combine(std::move(h), tz.cz_);
+ }
+
+ private:
+ friend bool operator==(TimeZone a, TimeZone b) { return a.cz_ == b.cz_; }
+ friend bool operator!=(TimeZone a, TimeZone b) { return a.cz_ != b.cz_; }
+ friend std::ostream& operator<<(std::ostream& os, TimeZone tz) {
+ return os << tz.name();
+ }
+
+ time_internal::cctz::time_zone cz_;
+};
+
+// LoadTimeZone()
+//
+// Loads the named zone. May perform I/O on the initial load of the named
+// zone. If the name is invalid, or some other kind of error occurs, returns
+// `false` and `*tz` is set to the UTC time zone.
+inline bool LoadTimeZone(const std::string& name, TimeZone* tz) {
+ if (name == "localtime") {
+ *tz = TimeZone(time_internal::cctz::local_time_zone());
+ return true;
+ }
+ time_internal::cctz::time_zone cz;
+ const bool b = time_internal::cctz::load_time_zone(name, &cz);
+ *tz = TimeZone(cz);
+ return b;
+}
+
+// FixedTimeZone()
+//
+// Returns a TimeZone that is a fixed offset (seconds east) from UTC.
+// Note: If the absolute value of the offset is greater than 24 hours
+// you'll get UTC (i.e., no offset) instead.
+inline TimeZone FixedTimeZone(int seconds) {
+ return TimeZone(
+ time_internal::cctz::fixed_time_zone(std::chrono::seconds(seconds)));
+}
+
+// UTCTimeZone()
+//
+// Convenience method returning the UTC time zone.
+inline TimeZone UTCTimeZone() {
+ return TimeZone(time_internal::cctz::utc_time_zone());
+}
+
+// LocalTimeZone()
+//
+// Convenience method returning the local time zone, or UTC if there is
+// no configured local zone. Warning: Be wary of using LocalTimeZone(),
+// and particularly so in a server process, as the zone configured for the
+// local machine should be irrelevant. Prefer an explicit zone name.
+inline TimeZone LocalTimeZone() {
+ return TimeZone(time_internal::cctz::local_time_zone());
+}
+
+// ToCivilSecond()
+// ToCivilMinute()
+// ToCivilHour()
+// ToCivilDay()
+// ToCivilMonth()
+// ToCivilYear()
+//
+// Helpers for TimeZone::At(Time) to return particularly aligned civil times.
+//
+// Example:
+//
+// absl::Time t = ...;
+// absl::TimeZone tz = ...;
+// const auto cd = absl::ToCivilDay(t, tz);
+inline CivilSecond ToCivilSecond(Time t, TimeZone tz) {
+ return tz.At(t).cs; // already a CivilSecond
+}
+inline CivilMinute ToCivilMinute(Time t, TimeZone tz) {
+ return CivilMinute(tz.At(t).cs);
+}
+inline CivilHour ToCivilHour(Time t, TimeZone tz) {
+ return CivilHour(tz.At(t).cs);
+}
+inline CivilDay ToCivilDay(Time t, TimeZone tz) {
+ return CivilDay(tz.At(t).cs);
+}
+inline CivilMonth ToCivilMonth(Time t, TimeZone tz) {
+ return CivilMonth(tz.At(t).cs);
+}
+inline CivilYear ToCivilYear(Time t, TimeZone tz) {
+ return CivilYear(tz.At(t).cs);
+}
+
+// FromCivil()
+//
+// Helper for TimeZone::At(CivilSecond) that provides "order-preserving
+// semantics." If the civil time maps to a unique time, that time is
+// returned. If the civil time is repeated in the given time zone, the
+// time using the pre-transition offset is returned. Otherwise, the
+// civil time is skipped in the given time zone, and the transition time
+// is returned. This means that for any two civil times, ct1 and ct2,
+// (ct1 < ct2) => (FromCivil(ct1) <= FromCivil(ct2)), the equal case
+// being when two non-existent civil times map to the same transition time.
+//
+// Note: Accepts civil times of any alignment.
+inline Time FromCivil(CivilSecond ct, TimeZone tz) {
+ const auto ti = tz.At(ct);
+ if (ti.kind == TimeZone::TimeInfo::SKIPPED) return ti.trans;
+ return ti.pre;
+}
+
+// TimeConversion
+//
+// An `absl::TimeConversion` represents the conversion of year, month, day,
+// hour, minute, and second values (i.e., a civil time), in a particular
+// `absl::TimeZone`, to a time instant (an absolute time), as returned by
+// `absl::ConvertDateTime()`. Lecacy version of `absl::TimeZone::TimeInfo`.
+//
+// Deprecated. Use `absl::TimeZone::TimeInfo`.
+struct
+ TimeConversion {
+ Time pre; // time calculated using the pre-transition offset
+ Time trans; // when the civil-time discontinuity occurred
+ Time post; // time calculated using the post-transition offset
+
+ enum Kind {
+ UNIQUE, // the civil time was singular (pre == trans == post)
+ SKIPPED, // the civil time did not exist
+ REPEATED, // the civil time was ambiguous
+ };
+ Kind kind;
+
+ bool normalized; // input values were outside their valid ranges
+};
+
+// ConvertDateTime()
+//
+// Legacy version of `absl::TimeZone::At(absl::CivilSecond)` that takes
+// the civil time as six, separate values (YMDHMS).
+//
+// The input month, day, hour, minute, and second values can be outside
+// of their valid ranges, in which case they will be "normalized" during
+// the conversion.
+//
+// Example:
+//
+// // "October 32" normalizes to "November 1".
+// absl::TimeConversion tc =
+// absl::ConvertDateTime(2013, 10, 32, 8, 30, 0, lax);
+// // tc.kind == TimeConversion::UNIQUE && tc.normalized == true
+// // absl::ToCivilDay(tc.pre, tz).month() == 11
+// // absl::ToCivilDay(tc.pre, tz).day() == 1
+//
+// Deprecated. Use `absl::TimeZone::At(CivilSecond)`.
+TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
+ int min, int sec, TimeZone tz);
+
+// FromDateTime()
+//
+// A convenience wrapper for `absl::ConvertDateTime()` that simply returns
+// the "pre" `absl::Time`. That is, the unique result, or the instant that
+// is correct using the pre-transition offset (as if the transition never
+// happened).
+//
+// Example:
+//
+// absl::Time t = absl::FromDateTime(2017, 9, 26, 9, 30, 0, lax);
+// // t = 2017-09-26 09:30:00 -0700
+//
+// Deprecated. Use `absl::TimeZone::At(CivilSecond).pre`.
+inline Time FromDateTime(int64_t year, int mon, int day, int hour,
+ int min, int sec, TimeZone tz) {
+ return ConvertDateTime(year, mon, day, hour, min, sec, tz).pre;
+}
+
+// FromTM()
+//
+// Converts the `tm_year`, `tm_mon`, `tm_mday`, `tm_hour`, `tm_min`, and
+// `tm_sec` fields to an `absl::Time` using the given time zone. See ctime(3)
+// for a description of the expected values of the tm fields. If the indicated
+// time instant is not unique (see `absl::TimeZone::At(absl::CivilSecond)`
+// above), the `tm_isdst` field is consulted to select the desired instant
+// (`tm_isdst` > 0 means DST, `tm_isdst` == 0 means no DST, `tm_isdst` < 0
+// means use the post-transition offset).
+Time FromTM(const struct tm& tm, TimeZone tz);
+
+// ToTM()
+//
+// Converts the given `absl::Time` to a struct tm using the given time zone.
+// See ctime(3) for a description of the values of the tm fields.
+struct tm ToTM(Time t, TimeZone tz);
+
// RFC3339_full
// RFC3339_sec
//
// FormatTime()/ParseTime() format specifiers for RFC3339 date/time strings,
// with trailing zeros trimmed or with fractional seconds omitted altogether.
//
-// Note that RFC3339_sec[] matches an ISO 8601 extended format for date
-// and time with UTC offset.
+// Note that RFC3339_sec[] matches an ISO 8601 extended format for date and
+// time with UTC offset. Also note the use of "%Y": RFC3339 mandates that
+// years have exactly four digits, but we allow them to take their natural
+// width.
extern const char RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez
extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez
@@ -883,7 +1196,7 @@ extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
// FormatTime()
//
// Formats the given `absl::Time` in the `absl::TimeZone` according to the
-// provided format std::string. Uses strftime()-like formatting options, with
+// provided format string. Uses strftime()-like formatting options, with
// the following extensions:
//
// - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
@@ -906,17 +1219,15 @@ extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
//
// Example:
//
-// absl::TimeZone lax;
-// if (!absl::LoadTimeZone("America/Los_Angeles", &lax)) { ... }
-// absl::Time t = absl::FromDateTime(2013, 1, 2, 3, 4, 5, lax);
-//
-// std::string f = absl::FormatTime("%H:%M:%S", t, lax); // "03:04:05"
+// absl::CivilSecond cs(2013, 1, 2, 3, 4, 5);
+// absl::Time t = absl::FromCivil(cs, lax);
+// string f = absl::FormatTime("%H:%M:%S", t, lax); // "03:04:05"
// f = absl::FormatTime("%H:%M:%E3S", t, lax); // "03:04:05.000"
//
// Note: If the given `absl::Time` is `absl::InfiniteFuture()`, the returned
-// std::string will be exactly "infinite-future". If the given `absl::Time` is
-// `absl::InfinitePast()`, the returned std::string will be exactly "infinite-past".
-// In both cases the given format std::string and `absl::TimeZone` are ignored.
+// string will be exactly "infinite-future". If the given `absl::Time` is
+// `absl::InfinitePast()`, the returned string will be exactly "infinite-past".
+// In both cases the given format string and `absl::TimeZone` are ignored.
//
std::string FormatTime(const std::string& format, Time t, TimeZone tz);
@@ -933,7 +1244,7 @@ inline std::ostream& operator<<(std::ostream& os, Time t) {
// ParseTime()
//
-// Parses an input std::string according to the provided format std::string and
+// Parses an input string according to the provided format string and
// returns the corresponding `absl::Time`. Uses strftime()-like formatting
// options, with the same extensions as FormatTime(), but with the
// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
@@ -947,7 +1258,7 @@ inline std::ostream& operator<<(std::ostream& os, Time t) {
//
// "1970-01-01 00:00:00.0 +0000"
//
-// For example, parsing a std::string of "15:45" (%H:%M) will return an absl::Time
+// For example, parsing a string of "15:45" (%H:%M) will return an absl::Time
// that represents "1970-01-01 15:45:00.0 +0000".
//
// Note that since ParseTime() returns time instants, it makes the most sense
@@ -960,7 +1271,7 @@ inline std::ostream& operator<<(std::ostream& os, Time t) {
// in the conversion.
//
// Date and time fields that are out-of-range will be treated as errors
-// rather than normalizing them like `absl::FromDateTime()` does. For example,
+// rather than normalizing them like `absl::CivilSecond` does. For example,
// it is an error to parse the date "Oct 32, 2013" because 32 is out of range.
//
// A leap second of ":60" is normalized to ":00" of the following minute
@@ -974,130 +1285,24 @@ inline std::ostream& operator<<(std::ostream& os, Time t) {
// Errors are indicated by returning false and assigning an error message
// to the "err" out param if it is non-null.
//
-// Note: If the input std::string is exactly "infinite-future", the returned
+// Note: If the input string is exactly "infinite-future", the returned
// `absl::Time` will be `absl::InfiniteFuture()` and `true` will be returned.
-// If the input std::string is "infinite-past", the returned `absl::Time` will be
+// If the input string is "infinite-past", the returned `absl::Time` will be
// `absl::InfinitePast()` and `true` will be returned.
//
bool ParseTime(const std::string& format, const std::string& input, Time* time,
std::string* err);
-// Like ParseTime() above, but if the format std::string does not contain a UTC
+// Like ParseTime() above, but if the format string does not contain a UTC
// offset specification (%z/%Ez/%E*z) then the input is interpreted in the
// given TimeZone. This means that the input, by itself, does not identify a
// unique instant. Being time-zone dependent, it also admits the possibility
// of ambiguity or non-existence, in which case the "pre" time (as defined
-// for ConvertDateTime()) is returned. For these reasons we recommend that
+// by TimeZone::TimeInfo) is returned. For these reasons we recommend that
// all date/time strings include a UTC offset so they're context independent.
bool ParseTime(const std::string& format, const std::string& input, TimeZone tz,
Time* time, std::string* err);
-// ParseFlag()
-// UnparseFlag()
-//
-// Support for flag values of type Time. Time flags must be specified in a
-// format that matches absl::RFC3339_full. For example:
-//
-// --start_time=2016-01-02T03:04:05.678+08:00
-//
-// Note: A UTC offset (or 'Z' indicating a zero-offset from UTC) is required.
-//
-// Additionally, if you'd like to specify a time as a count of
-// seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag
-// and add that duration to absl::UnixEpoch() to get an absl::Time.
-bool ParseFlag(const std::string& text, Time* t, std::string* error);
-std::string UnparseFlag(Time t);
-
-// TimeZone
-//
-// The `absl::TimeZone` is an opaque, small, value-type class representing a
-// geo-political region within which particular rules are used for converting
-// between absolute and civil times (see https://git.io/v59Ly). `absl::TimeZone`
-// values are named using the TZ identifiers from the IANA Time Zone Database,
-// such as "America/Los_Angeles" or "Australia/Sydney". `absl::TimeZone` values
-// are created from factory functions such as `absl::LoadTimeZone()`. Note:
-// strings like "PST" and "EDT" are not valid TZ identifiers. Prefer to pass by
-// value rather than const reference.
-//
-// For more on the fundamental concepts of time zones, absolute times, and civil
-// times, see https://github.com/google/cctz#fundamental-concepts
-//
-// Examples:
-//
-// absl::TimeZone utc = absl::UTCTimeZone();
-// absl::TimeZone pst = absl::FixedTimeZone(-8 * 60 * 60);
-// absl::TimeZone loc = absl::LocalTimeZone();
-// absl::TimeZone lax;
-// if (!absl::LoadTimeZone("America/Los_Angeles", &lax)) { ... }
-//
-// See also:
-// - https://github.com/google/cctz
-// - http://www.iana.org/time-zones
-// - http://en.wikipedia.org/wiki/Zoneinfo
-class TimeZone {
- public:
- explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {}
- TimeZone() = default; // UTC, but prefer UTCTimeZone() to be explicit.
- TimeZone(const TimeZone&) = default;
- TimeZone& operator=(const TimeZone&) = default;
-
- explicit operator time_internal::cctz::time_zone() const { return cz_; }
-
- std::string name() const { return cz_.name(); }
-
- private:
- friend bool operator==(TimeZone a, TimeZone b) { return a.cz_ == b.cz_; }
- friend bool operator!=(TimeZone a, TimeZone b) { return a.cz_ != b.cz_; }
- friend std::ostream& operator<<(std::ostream& os, TimeZone tz) {
- return os << tz.name();
- }
-
- time_internal::cctz::time_zone cz_;
-};
-
-// LoadTimeZone()
-//
-// Loads the named zone. May perform I/O on the initial load of the named
-// zone. If the name is invalid, or some other kind of error occurs, returns
-// `false` and `*tz` is set to the UTC time zone.
-inline bool LoadTimeZone(const std::string& name, TimeZone* tz) {
- if (name == "localtime") {
- *tz = TimeZone(time_internal::cctz::local_time_zone());
- return true;
- }
- time_internal::cctz::time_zone cz;
- const bool b = time_internal::cctz::load_time_zone(name, &cz);
- *tz = TimeZone(cz);
- return b;
-}
-
-// FixedTimeZone()
-//
-// Returns a TimeZone that is a fixed offset (seconds east) from UTC.
-// Note: If the absolute value of the offset is greater than 24 hours
-// you'll get UTC (i.e., no offset) instead.
-inline TimeZone FixedTimeZone(int seconds) {
- return TimeZone(
- time_internal::cctz::fixed_time_zone(std::chrono::seconds(seconds)));
-}
-
-// UTCTimeZone()
-//
-// Convenience method returning the UTC time zone.
-inline TimeZone UTCTimeZone() {
- return TimeZone(time_internal::cctz::utc_time_zone());
-}
-
-// LocalTimeZone()
-//
-// Convenience method returning the local time zone, or UTC if there is
-// no configured local zone. Warning: Be wary of using LocalTimeZone(),
-// and particularly so in a server process, as the zone configured for the
-// local machine should be irrelevant. Prefer an explicit zone name.
-inline TimeZone LocalTimeZone() {
- return TimeZone(time_internal::cctz::local_time_zone());
-}
-
// ============================================================================
// Implementation Details Follow
// ============================================================================
@@ -1115,6 +1320,18 @@ constexpr Duration MakeDuration(int64_t hi, int64_t lo) {
return MakeDuration(hi, static_cast<uint32_t>(lo));
}
+// Make a Duration value from a floating-point number, as long as that number
+// is in the range [ 0 .. numeric_limits<int64_t>::max ), that is, as long as
+// it's positive and can be converted to int64_t without risk of UB.
+inline Duration MakePosDoubleDuration(double n) {
+ const int64_t int_secs = static_cast<int64_t>(n);
+ const uint32_t ticks =
+ static_cast<uint32_t>((n - int_secs) * kTicksPerSecond + 0.5);
+ return ticks < kTicksPerSecond
+ ? MakeDuration(int_secs, ticks)
+ : MakeDuration(int_secs + 1, ticks - kTicksPerSecond);
+}
+
// Creates a normalized Duration from an almost-normalized (sec,ticks)
// pair. sec may be positive or negative. ticks must be in the range
// -kTicksPerSecond < *ticks < kTicksPerSecond. If ticks is negative it
@@ -1123,17 +1340,20 @@ constexpr Duration MakeNormalizedDuration(int64_t sec, int64_t ticks) {
return (ticks < 0) ? MakeDuration(sec - 1, ticks + kTicksPerSecond)
: MakeDuration(sec, ticks);
}
+
// Provide access to the Duration representation.
constexpr int64_t GetRepHi(Duration d) { return d.rep_hi_; }
constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; }
+
+// Returns true iff d is positive or negative infinity.
constexpr bool IsInfiniteDuration(Duration d) { return GetRepLo(d) == ~0U; }
// Returns an infinite Duration with the opposite sign.
// REQUIRES: IsInfiniteDuration(d)
constexpr Duration OppositeInfinity(Duration d) {
return GetRepHi(d) < 0
- ? MakeDuration(std::numeric_limits<int64_t>::max(), ~0U)
- : MakeDuration(std::numeric_limits<int64_t>::min(), ~0U);
+ ? MakeDuration((std::numeric_limits<int64_t>::max)(), ~0U)
+ : MakeDuration((std::numeric_limits<int64_t>::min)(), ~0U);
}
// Returns (-n)-1 (equivalently -(n+1)) without avoidable overflow.
@@ -1158,14 +1378,14 @@ constexpr Duration FromInt64(int64_t v, std::ratio<1, N>) {
v / N, v % N * kTicksPerNanosecond * 1000 * 1000 * 1000 / N);
}
constexpr Duration FromInt64(int64_t v, std::ratio<60>) {
- return (v <= std::numeric_limits<int64_t>::max() / 60 &&
- v >= std::numeric_limits<int64_t>::min() / 60)
+ return (v <= (std::numeric_limits<int64_t>::max)() / 60 &&
+ v >= (std::numeric_limits<int64_t>::min)() / 60)
? MakeDuration(v * 60)
: v > 0 ? InfiniteDuration() : -InfiniteDuration();
}
constexpr Duration FromInt64(int64_t v, std::ratio<3600>) {
- return (v <= std::numeric_limits<int64_t>::max() / 3600 &&
- v >= std::numeric_limits<int64_t>::min() / 3600)
+ return (v <= (std::numeric_limits<int64_t>::max)() / 3600 &&
+ v >= (std::numeric_limits<int64_t>::min)() / 3600)
? MakeDuration(v * 3600)
: v > 0 ? InfiniteDuration() : -InfiniteDuration();
}
@@ -1224,8 +1444,8 @@ T ToChronoDuration(Duration d) {
if (time_internal::IsInfiniteDuration(d))
return d < ZeroDuration() ? T::min() : T::max();
const auto v = ToInt64(d, Period{});
- if (v > std::numeric_limits<Rep>::max()) return T::max();
- if (v < std::numeric_limits<Rep>::min()) return T::min();
+ if (v > (std::numeric_limits<Rep>::max)()) return T::max();
+ if (v < (std::numeric_limits<Rep>::min)()) return T::min();
return T{v};
}
@@ -1252,7 +1472,8 @@ constexpr Duration Hours(int64_t n) {
constexpr bool operator<(Duration lhs, Duration rhs) {
return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs)
? time_internal::GetRepHi(lhs) < time_internal::GetRepHi(rhs)
- : time_internal::GetRepHi(lhs) == std::numeric_limits<int64_t>::min()
+ : time_internal::GetRepHi(lhs) ==
+ (std::numeric_limits<int64_t>::min)()
? time_internal::GetRepLo(lhs) + 1 <
time_internal::GetRepLo(rhs) + 1
: time_internal::GetRepLo(lhs) <
@@ -1277,7 +1498,8 @@ constexpr Duration operator-(Duration d) {
// a second's worth of ticks and avoid overflow (as negating int64_t-min + 1
// is safe).
return time_internal::GetRepLo(d) == 0
- ? time_internal::GetRepHi(d) == std::numeric_limits<int64_t>::min()
+ ? time_internal::GetRepHi(d) ==
+ (std::numeric_limits<int64_t>::min)()
? InfiniteDuration()
: time_internal::MakeDuration(-time_internal::GetRepHi(d))
: time_internal::IsInfiniteDuration(d)
@@ -1290,7 +1512,8 @@ constexpr Duration operator-(Duration d) {
}
constexpr Duration InfiniteDuration() {
- return time_internal::MakeDuration(std::numeric_limits<int64_t>::max(), ~0U);
+ return time_internal::MakeDuration((std::numeric_limits<int64_t>::max)(),
+ ~0U);
}
constexpr Duration FromChrono(const std::chrono::nanoseconds& d) {
@@ -1332,7 +1555,7 @@ constexpr Time FromTimeT(time_t t) {
return time_internal::FromUnixDuration(Seconds(t));
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TIME_TIME_H_
diff --git a/absl/time/time_benchmark.cc b/absl/time/time_benchmark.cc
index e1009946..9bbed6f8 100644
--- a/absl/time/time_benchmark.cc
+++ b/absl/time/time_benchmark.cc
@@ -169,32 +169,32 @@ void BM_Time_ToUnixSeconds(benchmark::State& state) {
BENCHMARK(BM_Time_ToUnixSeconds);
//
-// FromDateTime
+// FromCivil
//
-// In each "FromDateTime" benchmark we switch between two YMDhms
-// values separated by at least one transition in order to defeat any
-// internal caching of previous results (e.g., see time_local_hint_).
+// In each "FromCivil" benchmark we switch between two YMDhms values
+// separated by at least one transition in order to defeat any internal
+// caching of previous results (e.g., see time_local_hint_).
//
// The "UTC" variants use UTC instead of the Google/local time zone.
// The "Day0" variants require normalization of the day of month.
//
-void BM_Time_FromDateTime_Absl(benchmark::State& state) {
+void BM_Time_FromCivil_Absl(benchmark::State& state) {
const absl::TimeZone tz =
absl::time_internal::LoadTimeZone("America/Los_Angeles");
int i = 0;
while (state.KeepRunning()) {
if ((i & 1) == 0) {
- absl::FromDateTime(2014, 12, 18, 20, 16, 18, tz);
+ absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz);
} else {
- absl::FromDateTime(2013, 11, 15, 18, 30, 27, tz);
+ absl::FromCivil(absl::CivilSecond(2013, 11, 15, 18, 30, 27), tz);
}
++i;
}
}
-BENCHMARK(BM_Time_FromDateTime_Absl);
+BENCHMARK(BM_Time_FromCivil_Absl);
-void BM_Time_FromDateTime_Libc(benchmark::State& state) {
+void BM_Time_FromCivil_Libc(benchmark::State& state) {
// No timezone support, so just use localtime.
int i = 0;
while (state.KeepRunning()) {
@@ -219,32 +219,32 @@ void BM_Time_FromDateTime_Libc(benchmark::State& state) {
++i;
}
}
-BENCHMARK(BM_Time_FromDateTime_Libc);
+BENCHMARK(BM_Time_FromCivil_Libc);
-void BM_Time_FromDateTimeUTC_Absl(benchmark::State& state) {
+void BM_Time_FromCivilUTC_Absl(benchmark::State& state) {
const absl::TimeZone tz = absl::UTCTimeZone();
while (state.KeepRunning()) {
- FromDateTime(2014, 12, 18, 20, 16, 18, tz);
+ absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz);
}
}
-BENCHMARK(BM_Time_FromDateTimeUTC_Absl);
+BENCHMARK(BM_Time_FromCivilUTC_Absl);
-void BM_Time_FromDateTimeDay0_Absl(benchmark::State& state) {
+void BM_Time_FromCivilDay0_Absl(benchmark::State& state) {
const absl::TimeZone tz =
absl::time_internal::LoadTimeZone("America/Los_Angeles");
int i = 0;
while (state.KeepRunning()) {
if ((i & 1) == 0) {
- absl::FromDateTime(2014, 12, 0, 20, 16, 18, tz);
+ absl::FromCivil(absl::CivilSecond(2014, 12, 0, 20, 16, 18), tz);
} else {
- absl::FromDateTime(2013, 11, 0, 18, 30, 27, tz);
+ absl::FromCivil(absl::CivilSecond(2013, 11, 0, 18, 30, 27), tz);
}
++i;
}
}
-BENCHMARK(BM_Time_FromDateTimeDay0_Absl);
+BENCHMARK(BM_Time_FromCivilDay0_Absl);
-void BM_Time_FromDateTimeDay0_Libc(benchmark::State& state) {
+void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
// No timezone support, so just use localtime.
int i = 0;
while (state.KeepRunning()) {
@@ -269,7 +269,7 @@ void BM_Time_FromDateTimeDay0_Libc(benchmark::State& state) {
++i;
}
}
-BENCHMARK(BM_Time_FromDateTimeDay0_Libc);
+BENCHMARK(BM_Time_FromCivilDay0_Libc);
//
// To/FromTimespec
diff --git a/absl/time/time_norm_test.cc b/absl/time/time_norm_test.cc
deleted file mode 100644
index 4436242e..00000000
--- a/absl/time/time_norm_test.cc
+++ /dev/null
@@ -1,306 +0,0 @@
-// 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
-//
-// 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.
-
-// This file contains tests for FromDateTime() normalization, which is
-// time-zone independent so we just use UTC throughout.
-
-#include <cstdint>
-#include <limits>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/time/internal/test_util.h"
-#include "absl/time/time.h"
-
-namespace {
-
-TEST(TimeNormCase, SimpleOverflow) {
- const absl::TimeZone utc = absl::UTCTimeZone();
-
- absl::TimeConversion tc =
- absl::ConvertDateTime(2013, 11, 15, 16, 32, 59 + 1, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- absl::Time::Breakdown bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 16, 33, 0, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15, 16, 59 + 1, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 17, 0, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15, 23 + 1, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 16, 0, 32, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 30 + 1, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 12, 1, 16, 32, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 12 + 1, 15, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2014, 1, 15, 16, 32, 14, 0, false);
-}
-
-TEST(TimeNormCase, SimpleUnderflow) {
- const absl::TimeZone utc = absl::UTCTimeZone();
-
- absl::TimeConversion tc = ConvertDateTime(2013, 11, 15, 16, 32, 0 - 1, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- absl::Time::Breakdown bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 16, 31, 59, 0, false);
-
- tc = ConvertDateTime(2013, 11, 15, 16, 0 - 1, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 15, 15, 59, 14, 0, false);
-
- tc = ConvertDateTime(2013, 11, 15, 0 - 1, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 14, 23, 32, 14, 0, false);
-
- tc = ConvertDateTime(2013, 11, 1 - 1, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 10, 31, 16, 32, 14, 0, false);
-
- tc = ConvertDateTime(2013, 1 - 1, 15, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2012, 12, 15, 16, 32, 14, 0, false);
-}
-
-TEST(TimeNormCase, MultipleOverflow) {
- const absl::TimeZone utc = absl::UTCTimeZone();
- absl::TimeConversion tc = ConvertDateTime(2013, 12, 31, 23, 59, 59 + 1, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- absl::Time::Breakdown bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2014, 1, 1, 0, 0, 0, 0, false);
-}
-
-TEST(TimeNormCase, MultipleUnderflow) {
- const absl::TimeZone utc = absl::UTCTimeZone();
- absl::TimeConversion tc = absl::ConvertDateTime(2014, 1, 1, 0, 0, 0 - 1, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- absl::Time::Breakdown bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 12, 31, 23, 59, 59, 0, false);
-}
-
-TEST(TimeNormCase, OverflowLimits) {
- const absl::TimeZone utc = absl::UTCTimeZone();
- absl::TimeConversion tc;
- absl::Time::Breakdown bd;
-
- const int kintmax = std::numeric_limits<int>::max();
- tc = absl::ConvertDateTime(0, kintmax, kintmax, kintmax, kintmax, kintmax,
- utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 185085715, 11, 27, 12, 21, 7, 0, false);
-
- const int kintmin = std::numeric_limits<int>::min();
- tc = absl::ConvertDateTime(0, kintmin, kintmin, kintmin, kintmin, kintmin,
- utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, -185085717, 10, 31, 10, 37, 52, 0, false);
-
- const int64_t max_year = std::numeric_limits<int64_t>::max();
- tc = absl::ConvertDateTime(max_year, 12, 31, 23, 59, 59, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- EXPECT_EQ(absl::InfiniteFuture(), tc.pre);
-
- const int64_t min_year = std::numeric_limits<int64_t>::min();
- tc = absl::ConvertDateTime(min_year, 1, 1, 0, 0, 0, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- EXPECT_EQ(absl::InfinitePast(), tc.pre);
-}
-
-TEST(TimeNormCase, ComplexOverflow) {
- const absl::TimeZone utc = absl::UTCTimeZone();
-
- absl::TimeConversion tc =
- ConvertDateTime(2013, 11, 15, 16, 32, 14 + 123456789, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- absl::Time::Breakdown bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2017, 10, 14, 14, 5, 23, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15, 16, 32 + 1234567, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2016, 3, 22, 0, 39, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15, 16 + 123456, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2027, 12, 16, 16, 32, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15 + 1234, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2017, 4, 2, 16, 32, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11 + 123, 15, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2024, 2, 15, 16, 32, 14, 0, false);
-}
-
-TEST(TimeNormCase, ComplexUnderflow) {
- const absl::TimeZone utc = absl::UTCTimeZone();
-
- absl::TimeConversion tc =
- absl::ConvertDateTime(1999, 3, 0, 0, 0, 0, utc); // year 400
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- absl::Time::Breakdown bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 1999, 2, 28, 0, 0, 0, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15, 16, 32, 14 - 123456789, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2009, 12, 17, 18, 59, 5, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15, 16, 32 - 1234567, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2011, 7, 12, 8, 25, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15, 16 - 123456, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 1999, 10, 16, 16, 32, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11, 15 - 1234, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2010, 6, 30, 16, 32, 14, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11 - 123, 15, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2003, 8, 15, 16, 32, 14, 0, false);
-}
-
-TEST(TimeNormCase, Mishmash) {
- const absl::TimeZone utc = absl::UTCTimeZone();
-
- absl::TimeConversion tc =
- absl::ConvertDateTime(2013, 11 - 123, 15 + 1234, 16 - 123456,
- 32 + 1234567, 14 - 123456789, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- absl::Time::Breakdown bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 1991, 5, 9, 3, 6, 5, 0, false);
-
- tc = absl::ConvertDateTime(2013, 11 + 123, 15 - 1234, 16 + 123456,
- 32 - 1234567, 14 + 123456789, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2036, 5, 24, 5, 58, 23, 0, false);
-
- // Here is a normalization case we got wrong for a while. Because the
- // day is converted to "1" within a 400-year (146097-day) period, we
- // didn't need to roll the month and so we didn't mark it as normalized.
- tc = absl::ConvertDateTime(2013, 11, -146097 + 1, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 1613, 11, 1, 16, 32, 14, 0, false);
-
- // Even though the month overflow compensates for the day underflow,
- // this should still be marked as normalized.
- tc = absl::ConvertDateTime(2013, 11 + 400 * 12, -146097 + 1, 16, 32, 14, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 11, 1, 16, 32, 14, 0, false);
-}
-
-TEST(TimeNormCase, LeapYears) {
- const absl::TimeZone utc = absl::UTCTimeZone();
-
- absl::TimeConversion tc =
- absl::ConvertDateTime(2013, 2, 28 + 1, 0, 0, 0, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- absl::Time::Breakdown bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2013, 3, 1, 0, 0, 0, 0, false);
-
- tc = absl::ConvertDateTime(2012, 2, 28 + 1, 0, 0, 0, utc);
- EXPECT_FALSE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2012, 2, 29, 0, 0, 0, 0, false);
-
- tc = absl::ConvertDateTime(2000, 2, 28 + 1, 0, 0, 0, utc);
- EXPECT_FALSE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 2000, 2, 29, 0, 0, 0, 0, false);
-
- tc = absl::ConvertDateTime(1900, 2, 28 + 1, 0, 0, 0, utc);
- EXPECT_TRUE(tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- bd = tc.pre.In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 1900, 3, 1, 0, 0, 0, 0, false);
-}
-
-// Convert all the days from 1970-1-1 to 1970-1-146097 (aka 2369-12-31)
-// and check that they normalize to the expected time. 146097 days span
-// the 400-year Gregorian cycle used during normalization.
-TEST(TimeNormCase, AllTheDays) {
- const absl::TimeZone utc = absl::UTCTimeZone();
- absl::Time exp_time = absl::UnixEpoch();
-
- for (int day = 1; day <= 146097; ++day) {
- absl::TimeConversion tc = absl::ConvertDateTime(1970, 1, day, 0, 0, 0, utc);
- EXPECT_EQ(day > 31, tc.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, tc.kind);
- EXPECT_EQ(exp_time, tc.pre);
- exp_time += absl::Hours(24);
- }
-}
-
-} // namespace
diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc
index 4f8f58a6..4d710709 100644
--- a/absl/time/time_test.cc
+++ b/absl/time/time_test.cc
@@ -28,6 +28,27 @@
namespace {
+#if GTEST_USES_SIMPLE_RE
+const char kZoneAbbrRE[] = ".*"; // just punt
+#else
+const char kZoneAbbrRE[] = "[A-Za-z]{3,4}|[-+][0-9]{2}([0-9]{2})?";
+#endif
+
+// This helper is a macro so that failed expectations show up with the
+// correct line numbers.
+#define EXPECT_CIVIL_INFO(ci, y, m, d, h, min, s, off, isdst) \
+ do { \
+ EXPECT_EQ(y, ci.cs.year()); \
+ EXPECT_EQ(m, ci.cs.month()); \
+ EXPECT_EQ(d, ci.cs.day()); \
+ EXPECT_EQ(h, ci.cs.hour()); \
+ EXPECT_EQ(min, ci.cs.minute()); \
+ EXPECT_EQ(s, ci.cs.second()); \
+ EXPECT_EQ(off, ci.offset); \
+ EXPECT_EQ(isdst, ci.is_dst); \
+ EXPECT_THAT(ci.zone_abbr, testing::MatchesRegex(kZoneAbbrRE)); \
+ } while (0)
+
// A gMock matcher to match timespec values. Use this matcher like:
// timespec ts1, ts2;
// EXPECT_THAT(ts1, TimespecMatcher(ts2));
@@ -84,10 +105,10 @@ TEST(Time, ValueSemantics) {
}
TEST(Time, UnixEpoch) {
- absl::Time::Breakdown bd = absl::UnixEpoch().In(absl::UTCTimeZone());
- ABSL_INTERNAL_EXPECT_TIME(bd, 1970, 1, 1, 0, 0, 0, 0, false);
- EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
- EXPECT_EQ(4, bd.weekday); // Thursday
+ const auto ci = absl::UTCTimeZone().At(absl::UnixEpoch());
+ EXPECT_EQ(absl::CivilSecond(1970, 1, 1, 0, 0, 0), ci.cs);
+ EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
+ EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(absl::CivilDay(ci.cs)));
}
TEST(Time, Breakdown) {
@@ -95,26 +116,26 @@ TEST(Time, Breakdown) {
absl::Time t = absl::UnixEpoch();
// The Unix epoch as seen in NYC.
- absl::Time::Breakdown bd = t.In(tz);
- ABSL_INTERNAL_EXPECT_TIME(bd, 1969, 12, 31, 19, 0, 0, -18000, false);
- EXPECT_EQ(absl::ZeroDuration(), bd.subsecond);
- EXPECT_EQ(3, bd.weekday); // Wednesday
+ auto ci = tz.At(t);
+ EXPECT_CIVIL_INFO(ci, 1969, 12, 31, 19, 0, 0, -18000, false);
+ EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
+ EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(absl::CivilDay(ci.cs)));
// Just before the epoch.
t -= absl::Nanoseconds(1);
- bd = t.In(tz);
- ABSL_INTERNAL_EXPECT_TIME(bd, 1969, 12, 31, 18, 59, 59, -18000, false);
- EXPECT_EQ(absl::Nanoseconds(999999999), bd.subsecond);
- EXPECT_EQ(3, bd.weekday); // Wednesday
+ ci = tz.At(t);
+ EXPECT_CIVIL_INFO(ci, 1969, 12, 31, 18, 59, 59, -18000, false);
+ EXPECT_EQ(absl::Nanoseconds(999999999), ci.subsecond);
+ EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(absl::CivilDay(ci.cs)));
// Some time later.
t += absl::Hours(24) * 2735;
t += absl::Hours(18) + absl::Minutes(30) + absl::Seconds(15) +
absl::Nanoseconds(9);
- bd = t.In(tz);
- ABSL_INTERNAL_EXPECT_TIME(bd, 1977, 6, 28, 14, 30, 15, -14400, true);
- EXPECT_EQ(8, bd.subsecond / absl::Nanoseconds(1));
- EXPECT_EQ(2, bd.weekday); // Tuesday
+ ci = tz.At(t);
+ EXPECT_CIVIL_INFO(ci, 1977, 6, 28, 14, 30, 15, -14400, true);
+ EXPECT_EQ(8, ci.subsecond / absl::Nanoseconds(1));
+ EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(absl::CivilDay(ci.cs)));
}
TEST(Time, AdditiveOperators) {
@@ -550,67 +571,63 @@ TEST(Time, ToChronoTime) {
absl::ToChronoTime(absl::UnixEpoch() - tick));
}
-TEST(Time, ConvertDateTime) {
- const absl::TimeZone utc = absl::UTCTimeZone();
- const absl::TimeZone goog =
- absl::time_internal::LoadTimeZone("America/Los_Angeles");
+TEST(Time, TimeZoneAt) {
const absl::TimeZone nyc =
absl::time_internal::LoadTimeZone("America/New_York");
const std::string fmt = "%a, %e %b %Y %H:%M:%S %z (%Z)";
- // A simple case of normalization.
- absl::TimeConversion oct32 = ConvertDateTime(2013, 10, 32, 8, 30, 0, goog);
- EXPECT_TRUE(oct32.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, oct32.kind);
- absl::TimeConversion nov01 = ConvertDateTime(2013, 11, 1, 8, 30, 0, goog);
- EXPECT_FALSE(nov01.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, nov01.kind);
- EXPECT_EQ(oct32.pre, nov01.pre);
- EXPECT_EQ("Fri, 1 Nov 2013 08:30:00 -0700 (PDT)",
- absl::FormatTime(fmt, nov01.pre, goog));
+ // A non-transition where the civil time is unique.
+ absl::CivilSecond nov01(2013, 11, 1, 8, 30, 0);
+ const auto nov01_ci = nyc.At(nov01);
+ EXPECT_EQ(absl::TimeZone::TimeInfo::UNIQUE, nov01_ci.kind);
+ EXPECT_EQ("Fri, 1 Nov 2013 08:30:00 -0400 (EDT)",
+ absl::FormatTime(fmt, nov01_ci.pre, nyc));
+ EXPECT_EQ(nov01_ci.pre, nov01_ci.trans);
+ EXPECT_EQ(nov01_ci.pre, nov01_ci.post);
+ EXPECT_EQ(nov01_ci.pre, absl::FromCivil(nov01, nyc));
// A Spring DST transition, when there is a gap in civil time
// and we prefer the later of the possible interpretations of a
// non-existent time.
- absl::TimeConversion mar13 = ConvertDateTime(2011, 3, 13, 2, 15, 0, nyc);
- EXPECT_FALSE(mar13.normalized);
- EXPECT_EQ(absl::TimeConversion::SKIPPED, mar13.kind);
+ absl::CivilSecond mar13(2011, 3, 13, 2, 15, 0);
+ const auto mar_ci = nyc.At(mar13);
+ EXPECT_EQ(absl::TimeZone::TimeInfo::SKIPPED, mar_ci.kind);
EXPECT_EQ("Sun, 13 Mar 2011 03:15:00 -0400 (EDT)",
- absl::FormatTime(fmt, mar13.pre, nyc));
+ absl::FormatTime(fmt, mar_ci.pre, nyc));
EXPECT_EQ("Sun, 13 Mar 2011 03:00:00 -0400 (EDT)",
- absl::FormatTime(fmt, mar13.trans, nyc));
+ absl::FormatTime(fmt, mar_ci.trans, nyc));
EXPECT_EQ("Sun, 13 Mar 2011 01:15:00 -0500 (EST)",
- absl::FormatTime(fmt, mar13.post, nyc));
- EXPECT_EQ(mar13.pre, absl::FromDateTime(2011, 3, 13, 2, 15, 0, nyc));
+ absl::FormatTime(fmt, mar_ci.post, nyc));
+ EXPECT_EQ(mar_ci.trans, absl::FromCivil(mar13, nyc));
// A Fall DST transition, when civil times are repeated and
// we prefer the earlier of the possible interpretations of an
// ambiguous time.
- absl::TimeConversion nov06 = ConvertDateTime(2011, 11, 6, 1, 15, 0, nyc);
- EXPECT_FALSE(nov06.normalized);
- EXPECT_EQ(absl::TimeConversion::REPEATED, nov06.kind);
+ absl::CivilSecond nov06(2011, 11, 6, 1, 15, 0);
+ const auto nov06_ci = nyc.At(nov06);
+ EXPECT_EQ(absl::TimeZone::TimeInfo::REPEATED, nov06_ci.kind);
EXPECT_EQ("Sun, 6 Nov 2011 01:15:00 -0400 (EDT)",
- absl::FormatTime(fmt, nov06.pre, nyc));
+ absl::FormatTime(fmt, nov06_ci.pre, nyc));
EXPECT_EQ("Sun, 6 Nov 2011 01:00:00 -0500 (EST)",
- absl::FormatTime(fmt, nov06.trans, nyc));
+ absl::FormatTime(fmt, nov06_ci.trans, nyc));
EXPECT_EQ("Sun, 6 Nov 2011 01:15:00 -0500 (EST)",
- absl::FormatTime(fmt, nov06.post, nyc));
- EXPECT_EQ(nov06.pre, absl::FromDateTime(2011, 11, 6, 1, 15, 0, nyc));
+ absl::FormatTime(fmt, nov06_ci.post, nyc));
+ EXPECT_EQ(nov06_ci.pre, absl::FromCivil(nov06, nyc));
// Check that (time_t) -1 is handled correctly.
- absl::TimeConversion minus1 = ConvertDateTime(1969, 12, 31, 18, 59, 59, nyc);
- EXPECT_FALSE(minus1.normalized);
- EXPECT_EQ(absl::TimeConversion::UNIQUE, minus1.kind);
- EXPECT_EQ(-1, absl::ToTimeT(minus1.pre));
+ absl::CivilSecond minus1(1969, 12, 31, 18, 59, 59);
+ const auto minus1_cl = nyc.At(minus1);
+ EXPECT_EQ(absl::TimeZone::TimeInfo::UNIQUE, minus1_cl.kind);
+ EXPECT_EQ(-1, absl::ToTimeT(minus1_cl.pre));
EXPECT_EQ("Wed, 31 Dec 1969 18:59:59 -0500 (EST)",
- absl::FormatTime(fmt, minus1.pre, nyc));
+ absl::FormatTime(fmt, minus1_cl.pre, nyc));
EXPECT_EQ("Wed, 31 Dec 1969 23:59:59 +0000 (UTC)",
- absl::FormatTime(fmt, minus1.pre, utc));
+ absl::FormatTime(fmt, minus1_cl.pre, absl::UTCTimeZone()));
}
-// FromDateTime(year, mon, day, hour, min, sec, UTCTimeZone()) has
-// a specialized fastpath implementation which we exercise here.
-TEST(Time, FromDateTimeUTC) {
+// FromCivil(CivilSecond(year, mon, day, hour, min, sec), UTCTimeZone())
+// has a specialized fastpath implementation, which we exercise here.
+TEST(Time, FromCivilUTC) {
const absl::TimeZone utc = absl::UTCTimeZone();
const std::string fmt = "%a, %e %b %Y %H:%M:%S %z (%Z)";
const int kMax = std::numeric_limits<int>::max();
@@ -618,65 +635,36 @@ TEST(Time, FromDateTimeUTC) {
absl::Time t;
// 292091940881 is the last positive year to use the fastpath.
- t = absl::FromDateTime(292091940881, kMax, kMax, kMax, kMax, kMax, utc);
+ t = absl::FromCivil(
+ absl::CivilSecond(292091940881, kMax, kMax, kMax, kMax, kMax), utc);
EXPECT_EQ("Fri, 25 Nov 292277026596 12:21:07 +0000 (UTC)",
absl::FormatTime(fmt, t, utc));
- t = absl::FromDateTime(292091940882, kMax, kMax, kMax, kMax, kMax, utc);
- EXPECT_EQ("infinite-future", absl::FormatTime(fmt, t, utc)); // no overflow
- t = absl::FromDateTime(
- std::numeric_limits<int64_t>::max(), kMax, kMax, kMax, kMax, kMax, utc);
+ t = absl::FromCivil(
+ absl::CivilSecond(292091940882, kMax, kMax, kMax, kMax, kMax), utc);
EXPECT_EQ("infinite-future", absl::FormatTime(fmt, t, utc)); // no overflow
// -292091936940 is the last negative year to use the fastpath.
- t = absl::FromDateTime(-292091936940, kMin, kMin, kMin, kMin, kMin, utc);
+ t = absl::FromCivil(
+ absl::CivilSecond(-292091936940, kMin, kMin, kMin, kMin, kMin), utc);
EXPECT_EQ("Fri, 1 Nov -292277022657 10:37:52 +0000 (UTC)",
absl::FormatTime(fmt, t, utc));
- t = absl::FromDateTime(-292091936941, kMin, kMin, kMin, kMin, kMin, utc);
+ t = absl::FromCivil(
+ absl::CivilSecond(-292091936941, kMin, kMin, kMin, kMin, kMin), utc);
EXPECT_EQ("infinite-past", absl::FormatTime(fmt, t, utc)); // no underflow
- t = absl::FromDateTime(
- std::numeric_limits<int64_t>::min(), kMin, kMin, kMin, kMin, kMin, utc);
- EXPECT_EQ("infinite-past", absl::FormatTime(fmt, t, utc)); // no overflow
// Check that we're counting leap years correctly.
- t = absl::FromDateTime(1900, 2, 28, 23, 59, 59, utc);
+ t = absl::FromCivil(absl::CivilSecond(1900, 2, 28, 23, 59, 59), utc);
EXPECT_EQ("Wed, 28 Feb 1900 23:59:59 +0000 (UTC)",
absl::FormatTime(fmt, t, utc));
- t = absl::FromDateTime(1900, 3, 1, 0, 0, 0, utc);
+ t = absl::FromCivil(absl::CivilSecond(1900, 3, 1, 0, 0, 0), utc);
EXPECT_EQ("Thu, 1 Mar 1900 00:00:00 +0000 (UTC)",
absl::FormatTime(fmt, t, utc));
- t = absl::FromDateTime(2000, 2, 29, 23, 59, 59, utc);
+ t = absl::FromCivil(absl::CivilSecond(2000, 2, 29, 23, 59, 59), utc);
EXPECT_EQ("Tue, 29 Feb 2000 23:59:59 +0000 (UTC)",
absl::FormatTime(fmt, t, utc));
- t = absl::FromDateTime(2000, 3, 1, 0, 0, 0, utc);
+ t = absl::FromCivil(absl::CivilSecond(2000, 3, 1, 0, 0, 0), utc);
EXPECT_EQ("Wed, 1 Mar 2000 00:00:00 +0000 (UTC)",
absl::FormatTime(fmt, t, utc));
-
- // Check normalization.
- const std::string ymdhms = "%Y-%m-%d %H:%M:%S";
- t = absl::FromDateTime(2015, 1, 1, 0, 0, 60, utc);
- EXPECT_EQ("2015-01-01 00:01:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 1, 1, 0, 60, 0, utc);
- EXPECT_EQ("2015-01-01 01:00:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 1, 1, 24, 0, 0, utc);
- EXPECT_EQ("2015-01-02 00:00:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 1, 32, 0, 0, 0, utc);
- EXPECT_EQ("2015-02-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 13, 1, 0, 0, 0, utc);
- EXPECT_EQ("2016-01-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 13, 32, 60, 60, 60, utc);
- EXPECT_EQ("2016-02-03 13:01:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 1, 1, 0, 0, -1, utc);
- EXPECT_EQ("2014-12-31 23:59:59", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 1, 1, 0, -1, 0, utc);
- EXPECT_EQ("2014-12-31 23:59:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 1, 1, -1, 0, 0, utc);
- EXPECT_EQ("2014-12-31 23:00:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, 1, -1, 0, 0, 0, utc);
- EXPECT_EQ("2014-12-30 00:00:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, -1, 1, 0, 0, 0, utc);
- EXPECT_EQ("2014-11-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
- t = absl::FromDateTime(2015, -1, -1, -1, -1, -1, utc);
- EXPECT_EQ("2014-10-29 22:58:59", absl::FormatTime(ymdhms, t, utc));
}
TEST(Time, ToTM) {
@@ -684,8 +672,10 @@ TEST(Time, ToTM) {
// Compares the results of ToTM() to gmtime_r() for lots of times over the
// course of a few days.
- const absl::Time start = absl::FromDateTime(2014, 1, 2, 3, 4, 5, utc);
- const absl::Time end = absl::FromDateTime(2014, 1, 5, 3, 4, 5, utc);
+ const absl::Time start =
+ absl::FromCivil(absl::CivilSecond(2014, 1, 2, 3, 4, 5), utc);
+ const absl::Time end =
+ absl::FromCivil(absl::CivilSecond(2014, 1, 5, 3, 4, 5), utc);
for (absl::Time t = start; t < end; t += absl::Seconds(30)) {
const struct tm tm_bt = ToTM(t, utc);
const time_t tt = absl::ToTimeT(t);
@@ -711,12 +701,12 @@ TEST(Time, ToTM) {
// Checks that the tm_isdst field is correct when in standard time.
const absl::TimeZone nyc =
absl::time_internal::LoadTimeZone("America/New_York");
- absl::Time t = absl::FromDateTime(2014, 3, 1, 0, 0, 0, nyc);
+ absl::Time t = absl::FromCivil(absl::CivilSecond(2014, 3, 1, 0, 0, 0), nyc);
struct tm tm = ToTM(t, nyc);
EXPECT_FALSE(tm.tm_isdst);
// Checks that the tm_isdst field is correct when in daylight time.
- t = absl::FromDateTime(2014, 4, 1, 0, 0, 0, nyc);
+ t = absl::FromCivil(absl::CivilSecond(2014, 4, 1, 0, 0, 0), nyc);
tm = ToTM(t, nyc);
EXPECT_TRUE(tm.tm_isdst);
@@ -808,8 +798,8 @@ TEST(Time, TMRoundTrip) {
absl::time_internal::LoadTimeZone("America/New_York");
// Test round-tripping across a skipped transition
- absl::Time start = absl::FromDateTime(2014, 3, 9, 0, 0, 0, nyc);
- absl::Time end = absl::FromDateTime(2014, 3, 9, 4, 0, 0, nyc);
+ absl::Time start = absl::FromCivil(absl::CivilHour(2014, 3, 9, 0), nyc);
+ absl::Time end = absl::FromCivil(absl::CivilHour(2014, 3, 9, 4), nyc);
for (absl::Time t = start; t < end; t += absl::Minutes(1)) {
struct tm tm = ToTM(t, nyc);
absl::Time rt = FromTM(tm, nyc);
@@ -817,8 +807,8 @@ TEST(Time, TMRoundTrip) {
}
// Test round-tripping across an ambiguous transition
- start = absl::FromDateTime(2014, 11, 2, 0, 0, 0, nyc);
- end = absl::FromDateTime(2014, 11, 2, 4, 0, 0, nyc);
+ start = absl::FromCivil(absl::CivilHour(2014, 11, 2, 0), nyc);
+ end = absl::FromCivil(absl::CivilHour(2014, 11, 2, 4), nyc);
for (absl::Time t = start; t < end; t += absl::Minutes(1)) {
struct tm tm = ToTM(t, nyc);
absl::Time rt = FromTM(tm, nyc);
@@ -826,8 +816,8 @@ TEST(Time, TMRoundTrip) {
}
// Test round-tripping of unique instants crossing a day boundary
- start = absl::FromDateTime(2014, 6, 27, 22, 0, 0, nyc);
- end = absl::FromDateTime(2014, 6, 28, 4, 0, 0, nyc);
+ start = absl::FromCivil(absl::CivilHour(2014, 6, 27, 22), nyc);
+ end = absl::FromCivil(absl::CivilHour(2014, 6, 28, 4), nyc);
for (absl::Time t = start; t < end; t += absl::Minutes(1)) {
struct tm tm = ToTM(t, nyc);
absl::Time rt = FromTM(tm, nyc);
@@ -980,27 +970,27 @@ TEST(Time, ConversionSaturation) {
EXPECT_EQ(min_timespec_sec, ts.tv_sec);
EXPECT_EQ(0, ts.tv_nsec);
- // Checks how Time::In() saturates on infinities.
- absl::Time::Breakdown bd = absl::InfiniteFuture().In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, std::numeric_limits<int64_t>::max(), 12, 31, 23,
+ // Checks how TimeZone::At() saturates on infinities.
+ auto ci = utc.At(absl::InfiniteFuture());
+ EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::max(), 12, 31, 23,
59, 59, 0, false);
- EXPECT_EQ(absl::InfiniteDuration(), bd.subsecond);
- EXPECT_EQ(4, bd.weekday); // Thursday
- EXPECT_EQ(365, bd.yearday);
- EXPECT_STREQ("-00", bd.zone_abbr); // artifact of absl::Time::In()
- bd = absl::InfinitePast().In(utc);
- ABSL_INTERNAL_EXPECT_TIME(bd, std::numeric_limits<int64_t>::min(), 1, 1, 0, 0,
+ EXPECT_EQ(absl::InfiniteDuration(), ci.subsecond);
+ EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(absl::CivilDay(ci.cs)));
+ EXPECT_EQ(365, absl::GetYearDay(absl::CivilDay(ci.cs)));
+ EXPECT_STREQ("-00", ci.zone_abbr); // artifact of TimeZone::At()
+ ci = utc.At(absl::InfinitePast());
+ EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::min(), 1, 1, 0, 0,
0, 0, false);
- EXPECT_EQ(-absl::InfiniteDuration(), bd.subsecond);
- EXPECT_EQ(7, bd.weekday); // Sunday
- EXPECT_EQ(1, bd.yearday);
- EXPECT_STREQ("-00", bd.zone_abbr); // artifact of absl::Time::In()
+ EXPECT_EQ(-absl::InfiniteDuration(), ci.subsecond);
+ EXPECT_EQ(absl::Weekday::sunday, absl::GetWeekday(absl::CivilDay(ci.cs)));
+ EXPECT_EQ(1, absl::GetYearDay(absl::CivilDay(ci.cs)));
+ EXPECT_STREQ("-00", ci.zone_abbr); // artifact of TimeZone::At()
// Approach the maximal Time value from below.
- t = absl::FromDateTime(292277026596, 12, 4, 15, 30, 6, utc);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 15, 30, 6), utc);
EXPECT_EQ("292277026596-12-04T15:30:06+00:00",
absl::FormatTime(absl::RFC3339_full, t, utc));
- t = absl::FromDateTime(292277026596, 12, 4, 15, 30, 7, utc);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 15, 30, 7), utc);
EXPECT_EQ("292277026596-12-04T15:30:07+00:00",
absl::FormatTime(absl::RFC3339_full, t, utc));
EXPECT_EQ(
@@ -1008,21 +998,21 @@ TEST(Time, ConversionSaturation) {
// Checks that we can also get the maximal Time value for a far-east zone.
const absl::TimeZone plus14 = absl::FixedTimeZone(14 * 60 * 60);
- t = absl::FromDateTime(292277026596, 12, 5, 5, 30, 7, plus14);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 5, 5, 30, 7), plus14);
EXPECT_EQ("292277026596-12-05T05:30:07+14:00",
absl::FormatTime(absl::RFC3339_full, t, plus14));
EXPECT_EQ(
absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::max()), t);
// One second later should push us to infinity.
- t = absl::FromDateTime(292277026596, 12, 4, 15, 30, 8, utc);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 15, 30, 8), utc);
EXPECT_EQ("infinite-future", absl::FormatTime(absl::RFC3339_full, t, utc));
// Approach the minimal Time value from above.
- t = absl::FromDateTime(-292277022657, 1, 27, 8, 29, 53, utc);
+ t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 27, 8, 29, 53), utc);
EXPECT_EQ("-292277022657-01-27T08:29:53+00:00",
absl::FormatTime(absl::RFC3339_full, t, utc));
- t = absl::FromDateTime(-292277022657, 1, 27, 8, 29, 52, utc);
+ t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 27, 8, 29, 52), utc);
EXPECT_EQ("-292277022657-01-27T08:29:52+00:00",
absl::FormatTime(absl::RFC3339_full, t, utc));
EXPECT_EQ(
@@ -1030,14 +1020,15 @@ TEST(Time, ConversionSaturation) {
// Checks that we can also get the minimal Time value for a far-west zone.
const absl::TimeZone minus12 = absl::FixedTimeZone(-12 * 60 * 60);
- t = absl::FromDateTime(-292277022657, 1, 26, 20, 29, 52, minus12);
+ t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 26, 20, 29, 52),
+ minus12);
EXPECT_EQ("-292277022657-01-26T20:29:52-12:00",
absl::FormatTime(absl::RFC3339_full, t, minus12));
EXPECT_EQ(
absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::min()), t);
// One second before should push us to -infinity.
- t = absl::FromDateTime(-292277022657, 1, 27, 8, 29, 51, utc);
+ t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 27, 8, 29, 51), utc);
EXPECT_EQ("infinite-past", absl::FormatTime(absl::RFC3339_full, t, utc));
}
@@ -1051,38 +1042,160 @@ TEST(Time, ExtendedConversionSaturation) {
absl::time_internal::LoadTimeZone("America/New_York");
const absl::Time max =
absl::FromUnixSeconds(std::numeric_limits<int64_t>::max());
- absl::Time::Breakdown bd;
+ absl::TimeZone::CivilInfo ci;
absl::Time t;
// The maximal time converted in each zone.
- bd = max.In(syd);
- ABSL_INTERNAL_EXPECT_TIME(bd, 292277026596, 12, 5, 2, 30, 7, 39600, true);
- t = absl::FromDateTime(292277026596, 12, 5, 2, 30, 7, syd);
+ ci = syd.At(max);
+ EXPECT_CIVIL_INFO(ci, 292277026596, 12, 5, 2, 30, 7, 39600, true);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 5, 2, 30, 7), syd);
EXPECT_EQ(max, t);
- bd = max.In(nyc);
- ABSL_INTERNAL_EXPECT_TIME(bd, 292277026596, 12, 4, 10, 30, 7, -18000, false);
- t = absl::FromDateTime(292277026596, 12, 4, 10, 30, 7, nyc);
+ ci = nyc.At(max);
+ EXPECT_CIVIL_INFO(ci, 292277026596, 12, 4, 10, 30, 7, -18000, false);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 10, 30, 7), nyc);
EXPECT_EQ(max, t);
// One second later should push us to infinity.
- t = absl::FromDateTime(292277026596, 12, 5, 2, 30, 8, syd);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 5, 2, 30, 8), syd);
EXPECT_EQ(absl::InfiniteFuture(), t);
- t = absl::FromDateTime(292277026596, 12, 4, 10, 30, 8, nyc);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 10, 30, 8), nyc);
EXPECT_EQ(absl::InfiniteFuture(), t);
// And we should stick there.
- t = absl::FromDateTime(292277026596, 12, 5, 2, 30, 9, syd);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 5, 2, 30, 9), syd);
EXPECT_EQ(absl::InfiniteFuture(), t);
- t = absl::FromDateTime(292277026596, 12, 4, 10, 30, 9, nyc);
+ t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 10, 30, 9), nyc);
EXPECT_EQ(absl::InfiniteFuture(), t);
// All the way up to a saturated date/time, without overflow.
- t = absl::FromDateTime(
- std::numeric_limits<int64_t>::max(), 12, 31, 23, 59, 59, syd);
+ t = absl::FromCivil(absl::CivilSecond::max(), syd);
EXPECT_EQ(absl::InfiniteFuture(), t);
- t = absl::FromDateTime(
- std::numeric_limits<int64_t>::max(), 12, 31, 23, 59, 59, nyc);
+ t = absl::FromCivil(absl::CivilSecond::max(), nyc);
EXPECT_EQ(absl::InfiniteFuture(), t);
}
+TEST(Time, FromCivilAlignment) {
+ const absl::TimeZone utc = absl::UTCTimeZone();
+ const absl::CivilSecond cs(2015, 2, 3, 4, 5, 6);
+ absl::Time t = absl::FromCivil(cs, utc);
+ EXPECT_EQ("2015-02-03T04:05:06+00:00", absl::FormatTime(t, utc));
+ t = absl::FromCivil(absl::CivilMinute(cs), utc);
+ EXPECT_EQ("2015-02-03T04:05:00+00:00", absl::FormatTime(t, utc));
+ t = absl::FromCivil(absl::CivilHour(cs), utc);
+ EXPECT_EQ("2015-02-03T04:00:00+00:00", absl::FormatTime(t, utc));
+ t = absl::FromCivil(absl::CivilDay(cs), utc);
+ EXPECT_EQ("2015-02-03T00:00:00+00:00", absl::FormatTime(t, utc));
+ t = absl::FromCivil(absl::CivilMonth(cs), utc);
+ EXPECT_EQ("2015-02-01T00:00:00+00:00", absl::FormatTime(t, utc));
+ t = absl::FromCivil(absl::CivilYear(cs), utc);
+ EXPECT_EQ("2015-01-01T00:00:00+00:00", absl::FormatTime(t, utc));
+}
+
+TEST(Time, LegacyDateTime) {
+ const absl::TimeZone utc = absl::UTCTimeZone();
+ const std::string ymdhms = "%Y-%m-%d %H:%M:%S";
+ const int kMax = std::numeric_limits<int>::max();
+ const int kMin = std::numeric_limits<int>::min();
+ absl::Time t;
+
+ t = absl::FromDateTime(std::numeric_limits<absl::civil_year_t>::max(),
+ kMax, kMax, kMax, kMax, kMax, utc);
+ EXPECT_EQ("infinite-future",
+ absl::FormatTime(ymdhms, t, utc)); // no overflow
+ t = absl::FromDateTime(std::numeric_limits<absl::civil_year_t>::min(),
+ kMin, kMin, kMin, kMin, kMin, utc);
+ EXPECT_EQ("infinite-past",
+ absl::FormatTime(ymdhms, t, utc)); // no overflow
+
+ // Check normalization.
+ EXPECT_TRUE(absl::ConvertDateTime(2013, 10, 32, 8, 30, 0, utc).normalized);
+ t = absl::FromDateTime(2015, 1, 1, 0, 0, 60, utc);
+ EXPECT_EQ("2015-01-01 00:01:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 1, 1, 0, 60, 0, utc);
+ EXPECT_EQ("2015-01-01 01:00:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 1, 1, 24, 0, 0, utc);
+ EXPECT_EQ("2015-01-02 00:00:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 1, 32, 0, 0, 0, utc);
+ EXPECT_EQ("2015-02-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 13, 1, 0, 0, 0, utc);
+ EXPECT_EQ("2016-01-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 13, 32, 60, 60, 60, utc);
+ EXPECT_EQ("2016-02-03 13:01:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 1, 1, 0, 0, -1, utc);
+ EXPECT_EQ("2014-12-31 23:59:59", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 1, 1, 0, -1, 0, utc);
+ EXPECT_EQ("2014-12-31 23:59:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 1, 1, -1, 0, 0, utc);
+ EXPECT_EQ("2014-12-31 23:00:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, 1, -1, 0, 0, 0, utc);
+ EXPECT_EQ("2014-12-30 00:00:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, -1, 1, 0, 0, 0, utc);
+ EXPECT_EQ("2014-11-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
+ t = absl::FromDateTime(2015, -1, -1, -1, -1, -1, utc);
+ EXPECT_EQ("2014-10-29 22:58:59", absl::FormatTime(ymdhms, t, utc));
+}
+
+TEST(Time, NextTransitionUTC) {
+ const auto tz = absl::UTCTimeZone();
+ absl::TimeZone::CivilTransition trans;
+
+ auto t = absl::InfinitePast();
+ EXPECT_FALSE(tz.NextTransition(t, &trans));
+
+ t = absl::InfiniteFuture();
+ EXPECT_FALSE(tz.NextTransition(t, &trans));
+}
+
+TEST(Time, PrevTransitionUTC) {
+ const auto tz = absl::UTCTimeZone();
+ absl::TimeZone::CivilTransition trans;
+
+ auto t = absl::InfiniteFuture();
+ EXPECT_FALSE(tz.PrevTransition(t, &trans));
+
+ t = absl::InfinitePast();
+ EXPECT_FALSE(tz.PrevTransition(t, &trans));
+}
+
+TEST(Time, NextTransitionNYC) {
+ const auto tz = absl::time_internal::LoadTimeZone("America/New_York");
+ absl::TimeZone::CivilTransition trans;
+
+ auto t = absl::FromCivil(absl::CivilSecond(2018, 6, 30, 0, 0, 0), tz);
+ EXPECT_TRUE(tz.NextTransition(t, &trans));
+ EXPECT_EQ(absl::CivilSecond(2018, 11, 4, 2, 0, 0), trans.from);
+ EXPECT_EQ(absl::CivilSecond(2018, 11, 4, 1, 0, 0), trans.to);
+
+ t = absl::InfiniteFuture();
+ EXPECT_FALSE(tz.NextTransition(t, &trans));
+
+ t = absl::InfinitePast();
+ EXPECT_TRUE(tz.NextTransition(t, &trans));
+ if (trans.from == absl::CivilSecond(1918, 03, 31, 2, 0, 0)) {
+ // It looks like the tzdata is only 32 bit (probably macOS),
+ // which bottoms out at 1901-12-13T20:45:52+00:00.
+ EXPECT_EQ(absl::CivilSecond(1918, 3, 31, 3, 0, 0), trans.to);
+ } else {
+ EXPECT_EQ(absl::CivilSecond(1883, 11, 18, 12, 3, 58), trans.from);
+ EXPECT_EQ(absl::CivilSecond(1883, 11, 18, 12, 0, 0), trans.to);
+ }
+}
+
+TEST(Time, PrevTransitionNYC) {
+ const auto tz = absl::time_internal::LoadTimeZone("America/New_York");
+ absl::TimeZone::CivilTransition trans;
+
+ auto t = absl::FromCivil(absl::CivilSecond(2018, 6, 30, 0, 0, 0), tz);
+ EXPECT_TRUE(tz.PrevTransition(t, &trans));
+ EXPECT_EQ(absl::CivilSecond(2018, 3, 11, 2, 0, 0), trans.from);
+ EXPECT_EQ(absl::CivilSecond(2018, 3, 11, 3, 0, 0), trans.to);
+
+ t = absl::InfinitePast();
+ EXPECT_FALSE(tz.PrevTransition(t, &trans));
+
+ t = absl::InfiniteFuture();
+ EXPECT_TRUE(tz.PrevTransition(t, &trans));
+ // We have a transition but we don't know which one.
+}
+
} // namespace
diff --git a/absl/time/time_zone_test.cc b/absl/time/time_zone_test.cc
index 7138560a..43d91904 100644
--- a/absl/time/time_zone_test.cc
+++ b/absl/time/time_zone_test.cc
@@ -59,7 +59,7 @@ TEST(TimeZone, DefaultTimeZones) {
TEST(TimeZone, FixedTimeZone) {
const absl::TimeZone tz = absl::FixedTimeZone(123);
- const cctz::time_zone cz = cctz::fixed_time_zone(cctz::sys_seconds(123));
+ const cctz::time_zone cz = cctz::fixed_time_zone(cctz::seconds(123));
EXPECT_EQ(tz, absl::TimeZone(cz));
}
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index c50ec425..d56fea6e 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -19,6 +19,7 @@ load(
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
"ABSL_EXCEPTIONS_FLAG",
+ "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
)
package(default_visibility = ["//visibility:public"])
@@ -42,7 +43,10 @@ cc_library(
name = "bad_any_cast",
hdrs = ["bad_any_cast.h"],
copts = ABSL_DEFAULT_COPTS,
- deps = [":bad_any_cast_impl"],
+ deps = [
+ ":bad_any_cast_impl",
+ "//absl/base:config",
+ ],
)
cc_library(
@@ -52,6 +56,7 @@ cc_library(
"bad_any_cast.h",
],
copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
"//absl/base",
@@ -66,6 +71,7 @@ cc_test(
"any_test.cc",
],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":any",
"//absl/base",
@@ -97,6 +103,7 @@ cc_test(
name = "any_exception_safety_test",
srcs = ["any_exception_safety_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":any",
"//absl/base:exception_safety_testing",
@@ -121,6 +128,7 @@ cc_test(
size = "small",
srcs = ["span_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":span",
"//absl/base:config",
@@ -128,6 +136,7 @@ cc_test(
"//absl/base:exception_testing",
"//absl/container:fixed_array",
"//absl/container:inlined_vector",
+ "//absl/hash:hash_testing",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
@@ -145,6 +154,7 @@ cc_test(
"//absl/base:exception_testing",
"//absl/container:fixed_array",
"//absl/container:inlined_vector",
+ "//absl/hash:hash_testing",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
@@ -158,6 +168,7 @@ cc_library(
deps = [
":bad_optional_access",
"//absl/base:config",
+ "//absl/base:core_headers",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/utility",
@@ -169,6 +180,7 @@ cc_library(
srcs = ["bad_optional_access.cc"],
hdrs = ["bad_optional_access.h"],
copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
"//absl/base",
"//absl/base:config",
@@ -180,6 +192,7 @@ cc_library(
srcs = ["bad_variant_access.cc"],
hdrs = ["bad_variant_access.h"],
copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
"//absl/base",
"//absl/base:config",
@@ -193,6 +206,7 @@ cc_test(
"optional_test.cc",
],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":optional",
"//absl/base",
@@ -209,6 +223,7 @@ cc_test(
"optional_exception_safety_test.cc",
],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":optional",
"//absl/base:exception_safety_testing",
@@ -236,6 +251,7 @@ cc_test(
size = "small",
srcs = ["variant_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":variant",
"//absl/base:config",
@@ -253,6 +269,7 @@ cc_test(
"variant_benchmark.cc",
],
copts = ABSL_TEST_COPTS,
+ tags = ["benchmark"],
deps = [
":variant",
"//absl/utility",
@@ -267,8 +284,10 @@ cc_test(
"variant_exception_safety_test.cc",
],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":variant",
+ "//absl/base:config",
"//absl/base:exception_safety_testing",
"//absl/memory",
"@com_google_googletest//:gtest_main",
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index 2f2e3a77..e8620766 100644
--- a/absl/types/CMakeLists.txt
+++ b/absl/types/CMakeLists.txt
@@ -123,7 +123,7 @@ absl_library(
# test any_test
set(ANY_TEST_SRC "any_test.cc")
-set(ANY_TEST_PUBLIC_LIBRARIES absl::base absl::throw_delegate absl::any absl::bad_any_cast test_instance_tracker_lib)
+set(ANY_TEST_PUBLIC_LIBRARIES absl::base absl_internal_throw_delegate absl::any absl::bad_any_cast absl::test_instance_tracker)
absl_test(
TARGET
@@ -152,7 +152,7 @@ set(ANY_EXCEPTION_SAFETY_TEST_SRC "any_exception_safety_test.cc")
set(ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
absl::any
absl::base
- absl_base_internal_exception_safety_testing
+ absl_internal_exception_safety_testing
)
absl_test(
@@ -169,7 +169,7 @@ absl_test(
# test span_test
set(SPAN_TEST_SRC "span_test.cc")
-set(SPAN_TEST_PUBLIC_LIBRARIES absl::base absl::strings absl::throw_delegate absl::span test_instance_tracker_lib)
+set(SPAN_TEST_PUBLIC_LIBRARIES absl::base absl::strings absl_internal_throw_delegate absl::span absl::test_instance_tracker)
absl_test(
TARGET
@@ -197,7 +197,7 @@ absl_test(
# test optional_test
set(OPTIONAL_TEST_SRC "optional_test.cc")
-set(OPTIONAL_TEST_PUBLIC_LIBRARIES absl::base absl::throw_delegate absl::optional absl_bad_optional_access)
+set(OPTIONAL_TEST_PUBLIC_LIBRARIES absl::base absl_internal_throw_delegate absl::optional absl_bad_optional_access)
absl_test(
TARGET
@@ -213,7 +213,7 @@ absl_test(
set(OPTIONAL_EXCEPTION_SAFETY_TEST_SRC "optional_exception_safety_test.cc")
set(OPTIONAL_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
absl::optional
- absl_base_internal_exception_safety_testing
+ absl_internal_exception_safety_testing
)
absl_test(
diff --git a/absl/types/any.h b/absl/types/any.h
index 9d297dc8..dc3bfcfe 100644
--- a/absl/types/any.h
+++ b/absl/types/any.h
@@ -61,12 +61,12 @@
#include <any>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
using std::any;
using std::any_cast;
using std::bad_any_cast;
using std::make_any;
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else // ABSL_HAVE_STD_ANY
@@ -93,7 +93,7 @@ using std::make_any;
#endif // !defined(__GNUC__) || defined(__GXX_RTTI)
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace any_internal {
@@ -533,7 +533,7 @@ T* any_cast(any* operand) noexcept {
: nullptr;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#undef ABSL_ANY_DETAIL_HAS_RTTI
diff --git a/absl/types/any_exception_safety_test.cc b/absl/types/any_exception_safety_test.cc
index 36955f6c..f9dd8c48 100644
--- a/absl/types/any_exception_safety_test.cc
+++ b/absl/types/any_exception_safety_test.cc
@@ -62,7 +62,7 @@ testing::AssertionResult AnyInvariants(absl::any* a) {
static_cast<void>(unused);
return AssertionFailure()
<< "A reset `any` should not be able to be any_cast";
- } catch (absl::bad_any_cast) {
+ } catch (const absl::bad_any_cast&) {
} catch (...) {
return AssertionFailure()
<< "Unexpected exception thrown from absl::any_cast";
@@ -107,7 +107,7 @@ TEST(AnyExceptionSafety, Assignment) {
};
auto any_strong_tester = testing::MakeExceptionSafetyTester()
.WithInitialValue(original)
- .WithInvariants(AnyInvariants, any_is_strong);
+ .WithContracts(AnyInvariants, any_is_strong);
Thrower val(2);
absl::any any_val(val);
@@ -129,7 +129,7 @@ TEST(AnyExceptionSafety, Assignment) {
auto strong_empty_any_tester =
testing::MakeExceptionSafetyTester()
.WithInitialValue(absl::any{})
- .WithInvariants(AnyInvariants, empty_any_is_strong);
+ .WithContracts(AnyInvariants, empty_any_is_strong);
EXPECT_TRUE(strong_empty_any_tester.Test(assign_any));
EXPECT_TRUE(strong_empty_any_tester.Test(assign_val));
@@ -142,7 +142,7 @@ TEST(AnyExceptionSafety, Emplace) {
absl::any{absl::in_place_type_t<Thrower>(), 1, testing::nothrow_ctor};
auto one_tester = testing::MakeExceptionSafetyTester()
.WithInitialValue(initial_val)
- .WithInvariants(AnyInvariants, AnyIsEmpty);
+ .WithContracts(AnyInvariants, AnyIsEmpty);
auto emp_thrower = [](absl::any* ap) { ap->emplace<Thrower>(2); };
auto emp_throwervec = [](absl::any* ap) {
diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc
index 537767cd..6244d09e 100644
--- a/absl/types/bad_any_cast.cc
+++ b/absl/types/bad_any_cast.cc
@@ -14,13 +14,15 @@
#include "absl/types/bad_any_cast.h"
+#ifndef ABSL_HAVE_STD_ANY
+
#include <cstdlib>
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
bad_any_cast::~bad_any_cast() = default;
@@ -38,5 +40,7 @@ void ThrowBadAnyCast() {
}
} // namespace any_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+
+#endif // ABSL_HAVE_STD_ANY
diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h
index 3f89e75d..7df9afbb 100644
--- a/absl/types/bad_any_cast.h
+++ b/absl/types/bad_any_cast.h
@@ -23,8 +23,22 @@
#include <typeinfo>
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_STD_ANY
+
+#include <any>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+using std::bad_any_cast;
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#else // ABSL_HAVE_STD_ANY
+
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// bad_any_cast
@@ -53,7 +67,9 @@ namespace any_internal {
[[noreturn]] void ThrowBadAnyCast();
} // namespace any_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+#endif // ABSL_HAVE_STD_ANY
+
#endif // ABSL_TYPES_BAD_ANY_CAST_H_
diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc
index c5508678..2dc74d3b 100644
--- a/absl/types/bad_optional_access.cc
+++ b/absl/types/bad_optional_access.cc
@@ -14,13 +14,15 @@
#include "absl/types/bad_optional_access.h"
+#ifndef ABSL_HAVE_STD_OPTIONAL
+
#include <cstdlib>
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
bad_optional_access::~bad_optional_access() = default;
@@ -40,5 +42,7 @@ void throw_bad_optional_access() {
}
} // namespace optional_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+
+#endif // ABSL_HAVE_STD_OPTIONAL
diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h
index d468211a..1f56ff64 100644
--- a/absl/types/bad_optional_access.h
+++ b/absl/types/bad_optional_access.h
@@ -23,8 +23,22 @@
#include <stdexcept>
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_STD_OPTIONAL
+
+#include <optional>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+using std::bad_optional_access;
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#else // ABSL_HAVE_STD_OPTIONAL
+
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// bad_optional_access
@@ -56,7 +70,9 @@ namespace optional_internal {
[[noreturn]] void throw_bad_optional_access();
} // namespace optional_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+#endif // ABSL_HAVE_STD_OPTIONAL
+
#endif // ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_
diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc
index a796cfaa..a646ff53 100644
--- a/absl/types/bad_variant_access.cc
+++ b/absl/types/bad_variant_access.cc
@@ -14,6 +14,8 @@
#include "absl/types/bad_variant_access.h"
+#ifndef ABSL_HAVE_STD_VARIANT
+
#include <cstdlib>
#include <stdexcept>
@@ -21,7 +23,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
//////////////////////////
// [variant.bad.access] //
@@ -56,5 +58,7 @@ void Rethrow() {
}
} // namespace variant_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+
+#endif // ABSL_HAVE_STD_VARIANT
diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h
index 2e007315..e0490842 100644
--- a/absl/types/bad_variant_access.h
+++ b/absl/types/bad_variant_access.h
@@ -23,8 +23,22 @@
#include <stdexcept>
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_STD_VARIANT
+
+#include <variant>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+using std::bad_variant_access;
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#else // ABSL_HAVE_STD_VARIANT
+
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// bad_variant_access
@@ -60,7 +74,9 @@ namespace variant_internal {
[[noreturn]] void Rethrow();
} // namespace variant_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+#endif // ABSL_HAVE_STD_VARIANT
+
#endif // ABSL_TYPES_BAD_VARIANT_ACCESS_H_
diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h
index 3b884647..875f88e0 100644
--- a/absl/types/internal/variant.h
+++ b/absl/types/internal/variant.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Abseil Authors.
+// 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.
@@ -15,6 +15,7 @@
// Implementation details of absl/types/variant.h, pulled into a
// separate file to avoid cluttering the top of the API header with
// implementation details.
+//
#ifndef ABSL_TYPES_variant_internal_H_
#define ABSL_TYPES_variant_internal_H_
@@ -37,8 +38,10 @@
#include "absl/types/bad_variant_access.h"
#include "absl/utility/utility.h"
+#if !defined(ABSL_HAVE_STD_VARIANT)
+
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
template <class... Types>
class variant;
@@ -580,12 +583,9 @@ struct VariantCoreAccess {
self.index_ = other.index();
}
+ // Access a variant alternative, assuming the index is correct.
template <std::size_t I, class Variant>
static VariantAccessResult<I, Variant> Access(Variant&& self) {
- if (ABSL_PREDICT_FALSE(self.index_ != I)) {
- TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
- }
-
// This cast instead of invocation of AccessUnion with an rvalue is a
// workaround for msvc. Without this there is a runtime failure when dealing
// with rvalues.
@@ -594,6 +594,16 @@ struct VariantCoreAccess {
variant_internal::AccessUnion(self.state_, SizeT<I>()));
}
+ // Access a variant alternative, throwing if the index is incorrect.
+ template <std::size_t I, class Variant>
+ static VariantAccessResult<I, Variant> CheckedAccess(Variant&& self) {
+ if (ABSL_PREDICT_FALSE(self.index_ != I)) {
+ TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
+ }
+
+ return Access<I>(absl::forward<Variant>(self));
+ }
+
// The implementation of the move-assignment operation for a variant.
template <class VType>
struct MoveAssignVisitor {
@@ -902,6 +912,11 @@ struct PerformVisitation {
template <std::size_t... TupIs, std::size_t... Is>
constexpr ReturnType Run(std::false_type /*has_valueless*/,
index_sequence<TupIs...>, SizeT<Is>...) const {
+ static_assert(
+ std::is_same<ReturnType,
+ absl::result_of_t<Op(VariantAccessResult<
+ Is, QualifiedVariants>...)>>::value,
+ "All visitation overloads must have the same return type.");
return absl::base_internal::Invoke(
absl::forward<Op>(op),
VariantCoreAccess::Access<Is>(
@@ -1056,32 +1071,6 @@ struct OverloadSet<> {
static void Overload(...);
};
-////////////////////////////////
-// Library Fundamentals V2 TS //
-////////////////////////////////
-
-// TODO(calabrese): Consider moving this to absl/meta/type_traits.h
-
-// The following is a rough implementation of parts of the detection idiom.
-// It is used for the comparison operator checks.
-
-template <class Enabler, class To, template <class...> class Op, class... Args>
-struct is_detected_convertible_impl {
- using type = std::false_type;
-};
-
-template <class To, template <class...> class Op, class... Args>
-struct is_detected_convertible_impl<
- absl::enable_if_t<std::is_convertible<Op<Args...>, To>::value>, To, Op,
- Args...> {
- using type = std::true_type;
-};
-
-// NOTE: This differs from library fundamentals by being lazy.
-template <class To, template <class...> class Op, class... Args>
-struct is_detected_convertible
- : is_detected_convertible_impl<void, To, Op, Args...>::type {};
-
template <class T>
using LessThanResult = decltype(std::declval<T>() < std::declval<T>());
@@ -1101,6 +1090,8 @@ using EqualResult = decltype(std::declval<T>() == std::declval<T>());
template <class T>
using NotEqualResult = decltype(std::declval<T>() != std::declval<T>());
+using type_traits_internal::is_detected_convertible;
+
template <class... T>
using RequireAllHaveEqualT = absl::enable_if_t<
absl::conjunction<is_detected_convertible<bool, EqualResult, T>...>::value,
@@ -1245,23 +1236,29 @@ using VariantCopyBase = absl::conditional_t<
// Base that is dependent on whether or not the move-assign can be trivial.
template <class... T>
using VariantMoveAssignBase = absl::conditional_t<
- absl::disjunction<absl::conjunction<std::is_move_assignable<Union<T...>>,
- std::is_move_constructible<Union<T...>>,
- std::is_destructible<Union<T...>>>,
- absl::negation<absl::conjunction<
- std::is_move_constructible<T>...,
- std::is_move_assignable<T>...>>>::value,
+ absl::disjunction<
+ absl::conjunction<absl::is_move_assignable<Union<T...>>,
+ std::is_move_constructible<Union<T...>>,
+ std::is_destructible<Union<T...>>>,
+ absl::negation<absl::conjunction<std::is_move_constructible<T>...,
+ // Note: We're not qualifying this with
+ // absl:: because it doesn't compile
+ // under MSVC.
+ is_move_assignable<T>...>>>::value,
VariantCopyBase<T...>, VariantMoveAssignBaseNontrivial<T...>>;
// Base that is dependent on whether or not the copy-assign can be trivial.
template <class... T>
using VariantCopyAssignBase = absl::conditional_t<
- absl::disjunction<absl::conjunction<std::is_copy_assignable<Union<T...>>,
- std::is_copy_constructible<Union<T...>>,
- std::is_destructible<Union<T...>>>,
- absl::negation<absl::conjunction<
- std::is_copy_constructible<T>...,
- std::is_copy_assignable<T>...>>>::value,
+ absl::disjunction<
+ absl::conjunction<absl::is_copy_assignable<Union<T...>>,
+ std::is_copy_constructible<Union<T...>>,
+ std::is_destructible<Union<T...>>>,
+ absl::negation<absl::conjunction<std::is_copy_constructible<T>...,
+ // Note: We're not qualifying this with
+ // absl:: because it doesn't compile
+ // under MSVC.
+ is_copy_assignable<T>...>>>::value,
VariantMoveAssignBase<T...>, VariantCopyAssignBaseNontrivial<T...>>;
template <class... T>
@@ -1628,7 +1625,8 @@ struct VariantHashBase<Variant,
};
} // namespace variant_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+#endif // !defined(ABSL_HAVE_STD_VARIANT)
#endif // ABSL_TYPES_variant_internal_H_
diff --git a/absl/types/optional.cc b/absl/types/optional.cc
index 85424ee3..5c77f154 100644
--- a/absl/types/optional.cc
+++ b/absl/types/optional.cc
@@ -16,11 +16,11 @@
#ifndef ABSL_HAVE_STD_OPTIONAL
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
nullopt_t::init_t nullopt_t::init;
extern const nullopt_t nullopt{nullopt_t::init};
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_STD_OPTIONAL
diff --git a/absl/types/optional.h b/absl/types/optional.h
index 12f0664a..1ca8dec6 100644
--- a/absl/types/optional.h
+++ b/absl/types/optional.h
@@ -43,13 +43,13 @@
#include <optional>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
using std::bad_optional_access;
using std::optional;
using std::make_optional;
using std::nullopt_t;
using std::nullopt;
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else // ABSL_HAVE_STD_OPTIONAL
@@ -61,6 +61,7 @@ using std::nullopt;
#include <type_traits>
#include <utility>
+#include "absl/base/attributes.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
#include "absl/types/bad_optional_access.h"
@@ -94,7 +95,7 @@ using std::nullopt;
#endif
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// absl::optional
@@ -165,7 +166,7 @@ struct empty_struct {};
// This class stores the data in optional<T>.
// It is specialized based on whether T is trivially destructible.
// This is the specialization for non trivially destructible type.
-template <typename T, bool = std::is_trivially_destructible<T>::value>
+template <typename T, bool unused = std::is_trivially_destructible<T>::value>
class optional_data_dtor_base {
struct dummy_type {
static_assert(sizeof(T) % sizeof(empty_struct) == 0, "");
@@ -263,10 +264,10 @@ class optional_data_base : public optional_data_dtor_base<T> {
// have trivial move but nontrivial copy.
// Also, we should be checking is_trivially_copyable here, which is not
// supported now, so we use is_trivially_* traits instead.
-template <typename T, bool = absl::is_trivially_copy_constructible<T>::value&&
- absl::is_trivially_copy_assignable<
- typename std::remove_cv<T>::type>::value&&
- std::is_trivially_destructible<T>::value>
+template <typename T,
+ bool unused = absl::is_trivially_copy_constructible<T>::value&&
+ absl::is_trivially_copy_assignable<typename std::remove_cv<
+ T>::type>::value&& std::is_trivially_destructible<T>::value>
class optional_data;
// Trivially copyable types
@@ -414,10 +415,10 @@ constexpr copy_traits get_ctor_copy_traits() {
template <typename T>
constexpr copy_traits get_assign_copy_traits() {
- return std::is_copy_assignable<T>::value &&
+ return absl::is_copy_assignable<T>::value &&
std::is_copy_constructible<T>::value
? copy_traits::copyable
- : std::is_move_assignable<T>::value &&
+ : absl::is_move_assignable<T>::value &&
std::is_move_constructible<T>::value
? copy_traits::movable
: copy_traits::non_movable;
@@ -703,7 +704,7 @@ class optional : private optional_internal::optional_data<T>,
// optional::reset()
//
// Destroys the inner `T` value of an `absl::optional` if one is present.
- void reset() noexcept { this->destruct(); }
+ ABSL_ATTRIBUTE_REINITIALIZES void reset() noexcept { this->destruct(); }
// optional::emplace()
//
@@ -1125,7 +1126,7 @@ constexpr auto operator>=(const U& v, const optional<T>& x)
return static_cast<bool>(x) ? static_cast<bool>(v >= *x) : true;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
namespace std {
diff --git a/absl/types/optional_exception_safety_test.cc b/absl/types/optional_exception_safety_test.cc
index e136d0a0..313891f7 100644
--- a/absl/types/optional_exception_safety_test.cc
+++ b/absl/types/optional_exception_safety_test.cc
@@ -18,7 +18,7 @@
#include "absl/base/internal/exception_safety_testing.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
@@ -39,12 +39,12 @@ constexpr int kUpdatedInteger = 10;
template <typename OptionalT>
bool ValueThrowsBadOptionalAccess(const OptionalT& optional) try {
return (static_cast<void>(optional.value()), false);
-} catch (absl::bad_optional_access) {
+} catch (const absl::bad_optional_access&) {
return true;
}
template <typename OptionalT>
-AssertionResult CheckInvariants(OptionalT* optional_ptr) {
+AssertionResult OptionalInvariants(OptionalT* optional_ptr) {
// Check the current state post-throw for validity
auto& optional = *optional_ptr;
@@ -124,8 +124,8 @@ TEST(OptionalExceptionSafety, NothrowConstructors) {
TEST(OptionalExceptionSafety, Emplace) {
// Test the basic guarantee plus test the result of optional::has_value()
// is false in all cases
- auto disengaged_test = MakeExceptionSafetyTester().WithInvariants(
- CheckInvariants<Optional>, CheckDisengaged<Optional>);
+ auto disengaged_test = MakeExceptionSafetyTester().WithContracts(
+ OptionalInvariants<Optional>, CheckDisengaged<Optional>);
auto disengaged_test_empty = disengaged_test.WithInitialValue(Optional());
auto disengaged_test_nonempty =
disengaged_test.WithInitialValue(Optional(kInitialInteger));
@@ -148,11 +148,11 @@ TEST(OptionalExceptionSafety, EverythingThrowsSwap) {
// Test the basic guarantee plus test the result of optional::has_value()
// remains the same
auto test =
- MakeExceptionSafetyTester().WithInvariants(CheckInvariants<Optional>);
+ MakeExceptionSafetyTester().WithContracts(OptionalInvariants<Optional>);
auto disengaged_test_empty = test.WithInitialValue(Optional())
- .WithInvariants(CheckDisengaged<Optional>);
+ .WithContracts(CheckDisengaged<Optional>);
auto engaged_test_nonempty = test.WithInitialValue(Optional(kInitialInteger))
- .WithInvariants(CheckEngaged<Optional>);
+ .WithContracts(CheckEngaged<Optional>);
auto swap_empty = [](Optional* optional_ptr) {
auto empty = Optional();
@@ -193,11 +193,11 @@ TEST(OptionalExceptionSafety, CopyAssign) {
// Test the basic guarantee plus test the result of optional::has_value()
// remains the same
auto test =
- MakeExceptionSafetyTester().WithInvariants(CheckInvariants<Optional>);
+ MakeExceptionSafetyTester().WithContracts(OptionalInvariants<Optional>);
auto disengaged_test_empty = test.WithInitialValue(Optional())
- .WithInvariants(CheckDisengaged<Optional>);
+ .WithContracts(CheckDisengaged<Optional>);
auto engaged_test_nonempty = test.WithInitialValue(Optional(kInitialInteger))
- .WithInvariants(CheckEngaged<Optional>);
+ .WithContracts(CheckEngaged<Optional>);
auto copyassign_nonempty = [](Optional* optional_ptr) {
auto nonempty =
@@ -219,11 +219,11 @@ TEST(OptionalExceptionSafety, MoveAssign) {
// Test the basic guarantee plus test the result of optional::has_value()
// remains the same
auto test =
- MakeExceptionSafetyTester().WithInvariants(CheckInvariants<Optional>);
+ MakeExceptionSafetyTester().WithContracts(OptionalInvariants<Optional>);
auto disengaged_test_empty = test.WithInitialValue(Optional())
- .WithInvariants(CheckDisengaged<Optional>);
+ .WithContracts(CheckDisengaged<Optional>);
auto engaged_test_nonempty = test.WithInitialValue(Optional(kInitialInteger))
- .WithInvariants(CheckEngaged<Optional>);
+ .WithContracts(CheckEngaged<Optional>);
auto moveassign_empty = [](Optional* optional_ptr) {
auto empty = Optional();
@@ -280,5 +280,5 @@ TEST(OptionalExceptionSafety, NothrowMoveAssign) {
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc
index 179bfd66..fc4f00a4 100644
--- a/absl/types/optional_test.cc
+++ b/absl/types/optional_test.cc
@@ -607,11 +607,12 @@ TEST(optionalTest, CopyAssignment) {
opt2_to_empty = empty;
EXPECT_FALSE(opt2_to_empty);
- EXPECT_FALSE(std::is_copy_assignable<absl::optional<const int>>::value);
- EXPECT_TRUE(std::is_copy_assignable<absl::optional<Copyable>>::value);
- EXPECT_FALSE(std::is_copy_assignable<absl::optional<MoveableThrow>>::value);
- EXPECT_FALSE(std::is_copy_assignable<absl::optional<MoveableNoThrow>>::value);
- EXPECT_FALSE(std::is_copy_assignable<absl::optional<NonMovable>>::value);
+ EXPECT_FALSE(absl::is_copy_assignable<absl::optional<const int>>::value);
+ EXPECT_TRUE(absl::is_copy_assignable<absl::optional<Copyable>>::value);
+ EXPECT_FALSE(absl::is_copy_assignable<absl::optional<MoveableThrow>>::value);
+ EXPECT_FALSE(
+ absl::is_copy_assignable<absl::optional<MoveableNoThrow>>::value);
+ EXPECT_FALSE(absl::is_copy_assignable<absl::optional<NonMovable>>::value);
EXPECT_TRUE(absl::is_trivially_copy_assignable<int>::value);
EXPECT_TRUE(absl::is_trivially_copy_assignable<volatile int>::value);
@@ -625,9 +626,9 @@ TEST(optionalTest, CopyAssignment) {
};
EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial>::value);
- EXPECT_FALSE(std::is_copy_assignable<const Trivial>::value);
- EXPECT_FALSE(std::is_copy_assignable<volatile Trivial>::value);
- EXPECT_TRUE(std::is_copy_assignable<NonTrivial>::value);
+ EXPECT_FALSE(absl::is_copy_assignable<const Trivial>::value);
+ EXPECT_FALSE(absl::is_copy_assignable<volatile Trivial>::value);
+ EXPECT_TRUE(absl::is_copy_assignable<NonTrivial>::value);
EXPECT_FALSE(absl::is_trivially_copy_assignable<NonTrivial>::value);
// std::optional doesn't support volatile nontrivial types.
@@ -695,11 +696,11 @@ TEST(optionalTest, MoveAssignment) {
EXPECT_EQ(1, listener.volatile_move_assign);
}
#endif // ABSL_HAVE_STD_OPTIONAL
- EXPECT_FALSE(std::is_move_assignable<absl::optional<const int>>::value);
- EXPECT_TRUE(std::is_move_assignable<absl::optional<Copyable>>::value);
- EXPECT_TRUE(std::is_move_assignable<absl::optional<MoveableThrow>>::value);
- EXPECT_TRUE(std::is_move_assignable<absl::optional<MoveableNoThrow>>::value);
- EXPECT_FALSE(std::is_move_assignable<absl::optional<NonMovable>>::value);
+ EXPECT_FALSE(absl::is_move_assignable<absl::optional<const int>>::value);
+ EXPECT_TRUE(absl::is_move_assignable<absl::optional<Copyable>>::value);
+ EXPECT_TRUE(absl::is_move_assignable<absl::optional<MoveableThrow>>::value);
+ EXPECT_TRUE(absl::is_move_assignable<absl::optional<MoveableNoThrow>>::value);
+ EXPECT_FALSE(absl::is_move_assignable<absl::optional<NonMovable>>::value);
EXPECT_FALSE(
std::is_nothrow_move_assignable<absl::optional<MoveableThrow>>::value);
@@ -1041,9 +1042,9 @@ TEST(optionalTest, Value) {
// test exception throw on value()
absl::optional<int> empty;
#ifdef ABSL_HAVE_EXCEPTIONS
- EXPECT_THROW(empty.value(), absl::bad_optional_access);
+ EXPECT_THROW((void)empty.value(), absl::bad_optional_access);
#else
- EXPECT_DEATH(empty.value(), "Bad optional access");
+ EXPECT_DEATH((void)empty.value(), "Bad optional access");
#endif
// test constexpr value()
@@ -1619,7 +1620,7 @@ TEST(optionalTest, AssignmentConstraints) {
EXPECT_TRUE(
(std::is_assignable<absl::optional<AnyLike>&, const AnyLike&>::value));
EXPECT_TRUE(std::is_move_assignable<absl::optional<AnyLike>>::value);
- EXPECT_TRUE(std::is_copy_assignable<absl::optional<AnyLike>>::value);
+ EXPECT_TRUE(absl::is_copy_assignable<absl::optional<AnyLike>>::value);
}
} // namespace
diff --git a/absl/types/span.h b/absl/types/span.h
index 1b5edf25..99b6765b 100644
--- a/absl/types/span.h
+++ b/absl/types/span.h
@@ -72,7 +72,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
template <typename T>
class Span;
@@ -88,7 +88,7 @@ constexpr auto GetDataImpl(C& c, char) noexcept // NOLINT(runtime/references)
return c.data();
}
-// Before C++17, std::string::data returns a const char* in all cases.
+// Before C++17, string::data returns a const char* in all cases.
inline char* GetDataImpl(std::string& s, // NOLINT(runtime/references)
int) noexcept {
return &s[0];
@@ -380,64 +380,70 @@ class Span {
//
// Returns a reference to the i'th element of this span.
constexpr reference at(size_type i) const {
- return ABSL_PREDICT_TRUE(i < size())
- ? ptr_[i]
+ return ABSL_PREDICT_TRUE(i < size()) //
+ ? *(data() + i)
: (base_internal::ThrowStdOutOfRange(
"Span::at failed bounds check"),
- ptr_[i]);
+ *(data() + i));
}
// Span::front()
//
// Returns a reference to the first element of this span.
- reference front() const noexcept { return ABSL_ASSERT(size() > 0), ptr_[0]; }
+ constexpr reference front() const noexcept {
+ return ABSL_ASSERT(size() > 0), *data();
+ }
// Span::back()
//
// Returns a reference to the last element of this span.
- reference back() const noexcept {
- return ABSL_ASSERT(size() > 0), ptr_[size() - 1];
+ constexpr reference back() const noexcept {
+ return ABSL_ASSERT(size() > 0), *(data() + size() - 1);
}
// Span::begin()
//
// Returns an iterator to the first element of this span.
- constexpr iterator begin() const noexcept { return ptr_; }
+ constexpr iterator begin() const noexcept { return data(); }
// Span::cbegin()
//
// Returns a const iterator to the first element of this span.
- constexpr const_iterator cbegin() const noexcept { return ptr_; }
+ constexpr const_iterator cbegin() const noexcept { return begin(); }
// Span::end()
//
// Returns an iterator to the last element of this span.
- iterator end() const noexcept { return ptr_ + len_; }
+ constexpr iterator end() const noexcept { return data() + size(); }
// Span::cend()
//
// Returns a const iterator to the last element of this span.
- const_iterator cend() const noexcept { return end(); }
+ constexpr const_iterator cend() const noexcept { return end(); }
// Span::rbegin()
//
// Returns a reverse iterator starting at the last element of this span.
- reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
+ constexpr reverse_iterator rbegin() const noexcept {
+ return reverse_iterator(end());
+ }
// Span::crbegin()
//
// Returns a reverse const iterator starting at the last element of this span.
- const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+ constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
// Span::rend()
//
// Returns a reverse iterator starting at the first element of this span.
- reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
+ constexpr reverse_iterator rend() const noexcept {
+ return reverse_iterator(begin());
+ }
// Span::crend()
//
// Returns a reverse iterator starting at the first element of this span.
- const_reverse_iterator crend() const noexcept { return rend(); }
+ constexpr const_reverse_iterator crend() const noexcept { return rend(); }
// Span mutations
@@ -445,7 +451,7 @@ class Span {
//
// Removes the first `n` elements from the span.
void remove_prefix(size_type n) noexcept {
- assert(len_ >= n);
+ assert(size() >= n);
ptr_ += n;
len_ -= n;
}
@@ -454,7 +460,7 @@ class Span {
//
// Removes the last `n` elements from the span.
void remove_suffix(size_type n) noexcept {
- assert(len_ >= n);
+ assert(size() >= n);
len_ -= n;
}
@@ -475,11 +481,18 @@ class Span {
// absl::MakeSpan(vec).subspan(4); // {}
// absl::MakeSpan(vec).subspan(5); // throws std::out_of_range
constexpr Span subspan(size_type pos = 0, size_type len = npos) const {
- return (pos <= len_)
- ? Span(ptr_ + pos, span_internal::Min(len_ - pos, len))
+ return (pos <= size())
+ ? Span(data() + pos, span_internal::Min(size() - pos, len))
: (base_internal::ThrowStdOutOfRange("pos > size()"), Span());
}
+ // Support for absl::Hash.
+ template <typename H>
+ friend H AbslHashValue(H h, Span v) {
+ return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()),
+ v.size());
+ }
+
private:
pointer ptr_;
size_type len_;
@@ -746,6 +759,6 @@ template <int&... ExplicitArgumentBarrier, typename T, size_t N>
constexpr Span<const T> MakeConstSpan(const T (&array)[N]) noexcept {
return Span<const T>(array, N);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TYPES_SPAN_H_
diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc
index 5a4f0014..bd739ff2 100644
--- a/absl/types/span_test.cc
+++ b/absl/types/span_test.cc
@@ -29,6 +29,7 @@
#include "absl/base/internal/exception_testing.h"
#include "absl/container/fixed_array.h"
#include "absl/container/inlined_vector.h"
+#include "absl/hash/hash_testing.h"
#include "absl/strings/str_cat.h"
namespace {
@@ -288,7 +289,7 @@ TEST(IntSpan, Subspan) {
#ifdef ABSL_HAVE_EXCEPTIONS
EXPECT_THROW(absl::MakeSpan(ramp).subspan(11, 5), std::out_of_range);
#else
- EXPECT_DEATH(absl::MakeSpan(ramp).subspan(11, 5), "");
+ EXPECT_DEATH_IF_SUPPORTED(absl::MakeSpan(ramp).subspan(11, 5), "");
#endif
}
diff --git a/absl/types/variant.h b/absl/types/variant.h
index b0898d98..4ae4e00d 100644
--- a/absl/types/variant.h
+++ b/absl/types/variant.h
@@ -50,7 +50,7 @@
#include <variant>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
using std::bad_variant_access;
using std::get;
using std::get_if;
@@ -63,7 +63,7 @@ using std::variant_npos;
using std::variant_size;
using std::variant_size_v;
using std::visit;
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else // ABSL_HAVE_STD_VARIANT
@@ -79,13 +79,13 @@ using std::visit;
#include "absl/types/internal/variant.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// absl::variant
// -----------------------------------------------------------------------------
//
-// An 'absl::variant` type is a form of type-safe union. An `absl::variant` --
+// An `absl::variant` type is a form of type-safe union. An `absl::variant` --
// except in exceptional cases -- always holds a value of one of its alternative
// types.
//
@@ -139,7 +139,7 @@ void swap(variant<Ts...>& v, variant<Ts...>& w) noexcept(noexcept(v.swap(w))) {
// variant_size
//
-// Returns the number of alterative types available for a given `absl::variant`
+// Returns the number of alternative types available for a given `absl::variant`
// type as a compile-time constant expression. As this is a class template, it
// is not generally useful for accessing the number of alternative types of
// any given `absl::variant` instance.
@@ -251,7 +251,7 @@ using variant_alternative_t = typename variant_alternative<I, T>::type;
//
// Example:
//
-// absl::variant<int, std::string> bar = 42;
+// absl::variant<int, std::string> foo = 42;
// if (absl::holds_alternative<int>(foo)) {
// std::cout << "The variant holds an integer";
// }
@@ -293,7 +293,7 @@ constexpr bool holds_alternative(const variant<Types...>& v) noexcept {
// Overload for getting a variant's lvalue by type.
template <class T, class... Types>
constexpr T& get(variant<Types...>& v) { // NOLINT
- return variant_internal::VariantCoreAccess::Access<
+ return variant_internal::VariantCoreAccess::CheckedAccess<
variant_internal::IndexOf<T, Types...>::value>(v);
}
@@ -301,14 +301,14 @@ constexpr T& get(variant<Types...>& v) { // NOLINT
// Note: `absl::move()` is required to allow use of constexpr in C++11.
template <class T, class... Types>
constexpr T&& get(variant<Types...>&& v) {
- return variant_internal::VariantCoreAccess::Access<
+ return variant_internal::VariantCoreAccess::CheckedAccess<
variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
}
// Overload for getting a variant's const lvalue by type.
template <class T, class... Types>
constexpr const T& get(const variant<Types...>& v) {
- return variant_internal::VariantCoreAccess::Access<
+ return variant_internal::VariantCoreAccess::CheckedAccess<
variant_internal::IndexOf<T, Types...>::value>(v);
}
@@ -316,7 +316,7 @@ constexpr const T& get(const variant<Types...>& v) {
// Note: `absl::move()` is required to allow use of constexpr in C++11.
template <class T, class... Types>
constexpr const T&& get(const variant<Types...>&& v) {
- return variant_internal::VariantCoreAccess::Access<
+ return variant_internal::VariantCoreAccess::CheckedAccess<
variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
}
@@ -324,7 +324,7 @@ constexpr const T&& get(const variant<Types...>&& v) {
template <std::size_t I, class... Types>
constexpr variant_alternative_t<I, variant<Types...>>& get(
variant<Types...>& v) { // NOLINT
- return variant_internal::VariantCoreAccess::Access<I>(v);
+ return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
}
// Overload for getting a variant's rvalue by index.
@@ -332,14 +332,14 @@ constexpr variant_alternative_t<I, variant<Types...>>& get(
template <std::size_t I, class... Types>
constexpr variant_alternative_t<I, variant<Types...>>&& get(
variant<Types...>&& v) {
- return variant_internal::VariantCoreAccess::Access<I>(absl::move(v));
+ return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
}
// Overload for getting a variant's const lvalue by index.
template <std::size_t I, class... Types>
constexpr const variant_alternative_t<I, variant<Types...>>& get(
const variant<Types...>& v) {
- return variant_internal::VariantCoreAccess::Access<I>(v);
+ return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
}
// Overload for getting a variant's const rvalue by index.
@@ -347,7 +347,7 @@ constexpr const variant_alternative_t<I, variant<Types...>>& get(
template <std::size_t I, class... Types>
constexpr const variant_alternative_t<I, variant<Types...>>&& get(
const variant<Types...>&& v) {
- return variant_internal::VariantCoreAccess::Access<I>(absl::move(v));
+ return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
}
// get_if()
@@ -365,8 +365,10 @@ constexpr const variant_alternative_t<I, variant<Types...>>&& get(
template <std::size_t I, class... Types>
constexpr absl::add_pointer_t<variant_alternative_t<I, variant<Types...>>>
get_if(variant<Types...>* v) noexcept {
- return (v != nullptr && v->index() == I) ? std::addressof(absl::get<I>(*v))
- : nullptr;
+ return (v != nullptr && v->index() == I)
+ ? std::addressof(
+ variant_internal::VariantCoreAccess::Access<I>(*v))
+ : nullptr;
}
// Overload for getting a pointer to the const value stored in the given
@@ -374,8 +376,10 @@ get_if(variant<Types...>* v) noexcept {
template <std::size_t I, class... Types>
constexpr absl::add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
get_if(const variant<Types...>* v) noexcept {
- return (v != nullptr && v->index() == I) ? std::addressof(absl::get<I>(*v))
- : nullptr;
+ return (v != nullptr && v->index() == I)
+ ? std::addressof(
+ variant_internal::VariantCoreAccess::Access<I>(*v))
+ : nullptr;
}
// Overload for getting a pointer to the value stored in the given variant by
@@ -398,9 +402,9 @@ constexpr absl::add_pointer_t<const T> get_if(
// Calls a provided functor on a given set of variants. `absl::visit()` is
// commonly used to conditionally inspect the state of a given variant (or set
// of variants).
-// Requires: The expression in the Effects: element shall be a valid expression
-// of the same type and value category, for all combinations of alternative
-// types of all variants. Otherwise, the program is ill-formed.
+//
+// The functor must return the same type when called with any of the variants'
+// alternatives.
//
// Example:
//
@@ -413,9 +417,10 @@ constexpr absl::add_pointer_t<const T> get_if(
// };
//
// // Declare our variant, and call `absl::visit()` on it.
-// std::variant<int, std::string> foo = std::string("foo");
+// // Note that `GetVariant()` returns void in either case.
+// absl::variant<int, std::string> foo = std::string("foo");
// GetVariant visitor;
-// std::visit(visitor, foo); // Prints `The variant's value is: foo'
+// absl::visit(visitor, foo); // Prints `The variant's value is: foo'
template <typename Visitor, typename... Variants>
variant_internal::VisitResult<Visitor, Variants...> visit(Visitor&& vis,
Variants&&... vars) {
@@ -448,14 +453,19 @@ constexpr bool operator!=(monostate, monostate) noexcept { return false; }
//------------------------------------------------------------------------------
template <typename T0, typename... Tn>
class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
- // Intentionally not qualifing `negation` with `absl::` to work around a bug
+ static_assert(absl::conjunction<std::is_object<T0>,
+ std::is_object<Tn>...>::value,
+ "Attempted to instantiate a variant containing a non-object "
+ "type.");
+ // Intentionally not qualifying `negation` with `absl::` to work around a bug
// in MSVC 2015 with inline namespace and variadic template.
- static_assert(absl::conjunction<std::is_object<T0>, std::is_object<Tn>...,
- negation<std::is_array<T0> >,
- negation<std::is_array<Tn> >...,
- std::is_nothrow_destructible<T0>,
+ static_assert(absl::conjunction<negation<std::is_array<T0> >,
+ negation<std::is_array<Tn> >...>::value,
+ "Attempted to instantiate a variant containing an array type.");
+ static_assert(absl::conjunction<std::is_nothrow_destructible<T0>,
std::is_nothrow_destructible<Tn>...>::value,
- "Attempted to instantiate a variant with an unsupported type.");
+ "Attempted to instantiate a variant containing a non-nothrow "
+ "destructible type.");
friend struct variant_internal::VariantCoreAccess;
@@ -555,7 +565,7 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
// Assignment Operators
- // Copy assignement operator
+ // Copy assignment operator
variant& operator=(const variant& other) = default;
// Move assignment operator
@@ -783,7 +793,7 @@ operator>=(const variant<Types...>& a, const variant<Types...>& b) {
a.index());
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
namespace std {
@@ -804,7 +814,7 @@ struct hash<absl::variant<T...>>
#endif // ABSL_HAVE_STD_VARIANT
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace variant_internal {
// Helper visitor for converting a variant<Ts...>` into another type (mostly
@@ -840,7 +850,7 @@ To ConvertVariantTo(Variant&& variant) {
std::forward<Variant>(variant));
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_TYPES_VARIANT_H_
diff --git a/absl/types/variant_benchmark.cc b/absl/types/variant_benchmark.cc
index 93acc2b6..854f1448 100644
--- a/absl/types/variant_benchmark.cc
+++ b/absl/types/variant_benchmark.cc
@@ -28,7 +28,7 @@
#include "absl/utility/utility.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
template <std::size_t I>
@@ -218,5 +218,5 @@ BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2)
->DenseRange(0, integral_pow(4, 2) - 1);
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc
index d7bc90d2..ff166051 100644
--- a/absl/types/variant_exception_safety_test.cc
+++ b/absl/types/variant_exception_safety_test.cc
@@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+
#include "absl/types/variant.h"
#include <iostream>
@@ -20,11 +21,14 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/config.h"
#include "absl/base/internal/exception_safety_testing.h"
#include "absl/memory/memory.h"
+// See comment in absl/base/config.h
+#if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
using ::testing::MakeExceptionSafetyTester;
@@ -54,13 +58,13 @@ void ToValuelessByException(ThrowingVariant& v) { // NOLINT
try {
v.emplace<Thrower>();
v.emplace<Thrower>(ExceptionOnConversion<Thrower>());
- } catch (ConversionException& /*e*/) {
+ } catch (const ConversionException&) {
// This space intentionally left blank.
}
}
// Check that variant is still in a usable state after an exception is thrown.
-testing::AssertionResult CheckInvariants(ThrowingVariant* v) {
+testing::AssertionResult VariantInvariants(ThrowingVariant* v) {
using testing::AssertionFailure;
using testing::AssertionSuccess;
@@ -101,7 +105,7 @@ testing::AssertionResult CheckInvariants(ThrowingVariant* v) {
auto unused = absl::get<Thrower>(*v);
static_cast<void>(unused);
return AssertionFailure() << "Variant should not contain Thrower";
- } catch (absl::bad_variant_access) {
+ } catch (const absl::bad_variant_access&) {
} catch (...) {
return AssertionFailure() << "Unexpected exception throw from absl::get";
}
@@ -214,8 +218,8 @@ TEST(VariantExceptionSafetyTest, CopyAssign) {
MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
- EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test());
- EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_TRUE(tester.WithContracts(VariantInvariants).Test());
+ EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
}
{
const ThrowingVariant rhs(ExpectedThrowerVec());
@@ -223,8 +227,8 @@ TEST(VariantExceptionSafetyTest, CopyAssign) {
MakeExceptionSafetyTester()
.WithInitialValue(WithThrowerVec())
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
- EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test());
- EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_TRUE(tester.WithContracts(VariantInvariants).Test());
+ EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
}
// libstdc++ std::variant has bugs on copy assignment regarding exception
// safety.
@@ -252,12 +256,12 @@ TEST(VariantExceptionSafetyTest, CopyAssign) {
.WithInitialValue(WithCopyNoThrow())
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
EXPECT_TRUE(tester
- .WithInvariants(CheckInvariants,
- [](ThrowingVariant* lhs) {
- return lhs->valueless_by_exception();
- })
+ .WithContracts(VariantInvariants,
+ [](ThrowingVariant* lhs) {
+ return lhs->valueless_by_exception();
+ })
.Test());
- EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
}
#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
{
@@ -269,7 +273,7 @@ TEST(VariantExceptionSafetyTest, CopyAssign) {
const ThrowingVariant rhs(MoveNothrow{});
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
- .WithInvariants(CheckInvariants, strong_guarantee)
+ .WithContracts(VariantInvariants, strong_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
}
@@ -305,11 +309,11 @@ TEST(VariantExceptionSafetyTest, MoveAssign) {
*lhs = std::move(copy);
});
EXPECT_TRUE(tester
- .WithInvariants(
- CheckInvariants,
+ .WithContracts(
+ VariantInvariants,
[&](ThrowingVariant* lhs) { return lhs->index() == j; })
.Test());
- EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
}
{
// - otherwise (index() != j), equivalent to
@@ -319,10 +323,10 @@ TEST(VariantExceptionSafetyTest, MoveAssign) {
ThrowingVariant rhs(CopyNothrow{});
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
- .WithInvariants(CheckInvariants,
- [](ThrowingVariant* lhs) {
- return lhs->valueless_by_exception();
- })
+ .WithContracts(VariantInvariants,
+ [](ThrowingVariant* lhs) {
+ return lhs->valueless_by_exception();
+ })
.Test([&](ThrowingVariant* lhs) {
auto copy = rhs;
*lhs = std::move(copy);
@@ -348,12 +352,12 @@ TEST(VariantExceptionSafetyTest, ValueAssign) {
.WithInitialValue(WithThrower())
.WithOperation([rhs](ThrowingVariant* lhs) { *lhs = rhs; });
EXPECT_TRUE(copy_tester
- .WithInvariants(CheckInvariants,
- [](ThrowingVariant* lhs) {
- return !lhs->valueless_by_exception();
- })
+ .WithContracts(VariantInvariants,
+ [](ThrowingVariant* lhs) {
+ return !lhs->valueless_by_exception();
+ })
.Test());
- EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_FALSE(copy_tester.WithContracts(strong_guarantee).Test());
// move assign
auto move_tester = MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
@@ -362,13 +366,13 @@ TEST(VariantExceptionSafetyTest, ValueAssign) {
*lhs = std::move(copy);
});
EXPECT_TRUE(move_tester
- .WithInvariants(CheckInvariants,
- [](ThrowingVariant* lhs) {
- return !lhs->valueless_by_exception();
- })
+ .WithContracts(VariantInvariants,
+ [](ThrowingVariant* lhs) {
+ return !lhs->valueless_by_exception();
+ })
.Test());
- EXPECT_FALSE(move_tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_FALSE(move_tester.WithContracts(strong_guarantee).Test());
}
// Otherwise (*this holds something else), if is_nothrow_constructible_v<Tj,
// T> || !is_nothrow_move_constructible_v<Tj> is true, equivalent to
@@ -401,12 +405,12 @@ TEST(VariantExceptionSafetyTest, ValueAssign) {
.WithInitialValue(WithCopyNoThrow())
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
EXPECT_TRUE(copy_tester
- .WithInvariants(CheckInvariants,
- [](ThrowingVariant* lhs) {
- return lhs->valueless_by_exception();
- })
+ .WithContracts(VariantInvariants,
+ [](ThrowingVariant* lhs) {
+ return lhs->valueless_by_exception();
+ })
.Test());
- EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_FALSE(copy_tester.WithContracts(strong_guarantee).Test());
// move
auto move_tester = MakeExceptionSafetyTester()
.WithInitialValue(WithCopyNoThrow())
@@ -414,12 +418,12 @@ TEST(VariantExceptionSafetyTest, ValueAssign) {
*lhs = ExpectedThrower(testing::nothrow_ctor);
});
EXPECT_TRUE(move_tester
- .WithInvariants(CheckInvariants,
- [](ThrowingVariant* lhs) {
- return lhs->valueless_by_exception();
- })
+ .WithContracts(VariantInvariants,
+ [](ThrowingVariant* lhs) {
+ return lhs->valueless_by_exception();
+ })
.Test());
- EXPECT_FALSE(move_tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_FALSE(move_tester.WithContracts(strong_guarantee).Test());
}
// Otherwise (if is_nothrow_constructible_v<Tj, T> == false &&
// is_nothrow_move_constructible<Tj> == true),
@@ -433,7 +437,7 @@ TEST(VariantExceptionSafetyTest, ValueAssign) {
MoveNothrow rhs;
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
- .WithInvariants(CheckInvariants, strong_guarantee)
+ .WithContracts(VariantInvariants, strong_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
@@ -451,12 +455,12 @@ TEST(VariantExceptionSafetyTest, Emplace) {
v->emplace<Thrower>(args);
});
EXPECT_TRUE(tester
- .WithInvariants(CheckInvariants,
- [](ThrowingVariant* v) {
- return v->valueless_by_exception();
- })
+ .WithContracts(VariantInvariants,
+ [](ThrowingVariant* v) {
+ return v->valueless_by_exception();
+ })
.Test());
- EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
}
}
@@ -473,7 +477,7 @@ TEST(VariantExceptionSafetyTest, Swap) {
ThrowingVariant rhs = ExpectedThrower();
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithThrower())
- .WithInvariants(CheckInvariants)
+ .WithContracts(VariantInvariants)
.Test([&](ThrowingVariant* lhs) {
auto copy = rhs;
lhs->swap(copy);
@@ -487,7 +491,7 @@ TEST(VariantExceptionSafetyTest, Swap) {
ThrowingVariant rhs = ExpectedThrower();
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithCopyNoThrow())
- .WithInvariants(CheckInvariants)
+ .WithContracts(VariantInvariants)
.Test([&](ThrowingVariant* lhs) {
auto copy = rhs;
lhs->swap(copy);
@@ -497,7 +501,7 @@ TEST(VariantExceptionSafetyTest, Swap) {
ThrowingVariant rhs = ExpectedThrower();
EXPECT_TRUE(MakeExceptionSafetyTester()
.WithInitialValue(WithCopyNoThrow())
- .WithInvariants(CheckInvariants)
+ .WithContracts(VariantInvariants)
.Test([&](ThrowingVariant* lhs) {
auto copy = rhs;
copy.swap(*lhs);
@@ -506,5 +510,7 @@ TEST(VariantExceptionSafetyTest, Swap) {
}
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
+
+#endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc
index 295023ae..59223ea7 100644
--- a/absl/types/variant_test.cc
+++ b/absl/types/variant_test.cc
@@ -52,7 +52,7 @@
#endif // ABSL_HAVE_EXCEPTIONS
#define ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(...) \
- ABSL_VARIANT_TEST_EXPECT_FAIL((__VA_ARGS__), absl::bad_variant_access, \
+ ABSL_VARIANT_TEST_EXPECT_FAIL((void)(__VA_ARGS__), absl::bad_variant_access, \
"Bad variant access")
struct Hashable {};
@@ -67,7 +67,7 @@ struct hash<Hashable> {
struct NonHashable {};
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
using ::testing::DoubleEq;
@@ -404,7 +404,7 @@ struct is_trivially_move_constructible
template <class T>
struct is_trivially_move_assignable
- : std::is_move_assignable<SingleUnion<T>>::type {};
+ : absl::is_move_assignable<SingleUnion<T>>::type {};
TEST(VariantTest, NothrowMoveConstructible) {
// Verify that variant is nothrow move constructible iff its template
@@ -560,9 +560,14 @@ TEST(VariantTest, TestDtor) {
}
#ifdef ABSL_HAVE_EXCEPTIONS
-
+// See comment in absl/base/config.h
+#if defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
+TEST(VariantTest, DISABLED_TestDtorValuelessByException)
+#else
// Test destruction when in the valueless_by_exception state.
-TEST(VariantTest, TestDtorValuelessByException) {
+TEST(VariantTest, TestDtorValuelessByException)
+#endif
+{
int counter = 0;
IncrementInDtor counter_adjuster(&counter);
@@ -2440,14 +2445,14 @@ TEST(VariantTest, TestMoveConversionViaConvertVariantTo) {
TEST(VariantTest, TestCopyAndMoveTypeTraits) {
EXPECT_TRUE(std::is_copy_constructible<variant<std::string>>::value);
- EXPECT_TRUE(std::is_copy_assignable<variant<std::string>>::value);
+ EXPECT_TRUE(absl::is_copy_assignable<variant<std::string>>::value);
EXPECT_TRUE(std::is_move_constructible<variant<std::string>>::value);
- EXPECT_TRUE(std::is_move_assignable<variant<std::string>>::value);
+ EXPECT_TRUE(absl::is_move_assignable<variant<std::string>>::value);
EXPECT_TRUE(std::is_move_constructible<variant<std::unique_ptr<int>>>::value);
- EXPECT_TRUE(std::is_move_assignable<variant<std::unique_ptr<int>>>::value);
+ EXPECT_TRUE(absl::is_move_assignable<variant<std::unique_ptr<int>>>::value);
EXPECT_FALSE(
std::is_copy_constructible<variant<std::unique_ptr<int>>>::value);
- EXPECT_FALSE(std::is_copy_assignable<variant<std::unique_ptr<int>>>::value);
+ EXPECT_FALSE(absl::is_copy_assignable<variant<std::unique_ptr<int>>>::value);
EXPECT_FALSE(
absl::is_trivially_copy_constructible<variant<std::string>>::value);
@@ -2610,5 +2615,5 @@ TEST(VariantTest, MoveCtorBug) {
}
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/utility/utility.h b/absl/utility/utility.h
index abb2f8a1..66e22dc2 100644
--- a/absl/utility/utility.h
+++ b/absl/utility/utility.h
@@ -51,7 +51,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// integer_sequence
//
@@ -236,13 +236,13 @@ auto apply_helper(Functor&& functor, Tuple&& t, index_sequence<Indexes...>)
// Example:
//
// class Foo{void Bar(int);};
-// void user_function(int, std::string);
+// void user_function(int, string);
// void user_function(std::unique_ptr<Foo>);
//
// int main()
// {
-// std::tuple<int, std::string> tuple1(42, "bar");
-// // Invokes the user function overload on int, std::string.
+// std::tuple<int, string> tuple1(42, "bar");
+// // Invokes the user function overload on int, string.
// absl::apply(&user_function, tuple1);
//
// auto foo = absl::make_unique<Foo>();
@@ -287,7 +287,7 @@ T exchange(T& obj, U&& new_value) {
return old_value;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_UTILITY_UTILITY_H_