From 8e934f6db561bf2e6cbb0599164059ea08083811 Mon Sep 17 00:00:00 2001 From: Andreas Tille Date: Fri, 30 Jun 2023 20:49:50 +0100 Subject: [PATCH] Import r-cran-s2_1.1.4.orig.tar.gz [dgit import orig r-cran-s2_1.1.4.orig.tar.gz] --- DESCRIPTION | 46 + MD5 | 682 ++++ NAMESPACE | 206 ++ NEWS.md | 142 + R/RcppExports.R | 447 +++ R/data.R | 79 + R/plot.R | 158 + R/s2-accessors.R | 169 + R/s2-bounds.R | 35 + R/s2-cell-union.R | 200 ++ R/s2-cell.R | 351 ++ R/s2-constructors-formatters.R | 210 ++ R/s2-earth.R | 23 + R/s2-geography.R | 215 ++ R/s2-lnglat.R | 60 + R/s2-matrix.R | 205 ++ R/s2-options.R | 147 + R/s2-package.R | 10 + R/s2-point.R | 68 + R/s2-predicates.R | 178 + R/s2-transformers.R | 298 ++ R/utils.R | 88 + R/vctrs.R | 36 + R/wk-utils.R | 129 + R/zzz.R | 74 + README.md | 184 ++ build/partial.rdb | Bin 0 -> 60 bytes cleanup | 3 + configure | 125 + configure.win | 0 data/s2_data_example_wkt.rda | Bin 0 -> 24540 bytes data/s2_data_tbl_cities.rda | Bin 0 -> 7120 bytes data/s2_data_tbl_countries.rda | Bin 0 -> 132763 bytes data/s2_data_tbl_timezones.rda | Bin 0 -> 498373 bytes inst/extdata/emptyfile | 0 man/as_s2_geography.Rd | 74 + man/figures/rc300.png | Bin 0 -> 15702 bytes man/s2-package.Rd | 37 + man/s2_boundary.Rd | 215 ++ man/s2_bounds_cap.Rd | 39 + man/s2_cell.Rd | 74 + man/s2_cell_is_valid.Rd | 82 + man/s2_cell_union.Rd | 34 + man/s2_cell_union_normalize.Rd | 66 + man/s2_closest_feature.Rd | 132 + man/s2_contains.Rd | 169 + man/s2_data_example_wkt.Rd | 17 + man/s2_data_tbl_countries.Rd | 59 + man/s2_earth_radius_meters.Rd | 25 + man/s2_geog_point.Rd | 164 + man/s2_interpolate.Rd | 53 + man/s2_is_collection.Rd | 112 + man/s2_lnglat.Rd | 40 + man/s2_options.Rd | 109 + man/s2_plot.Rd | 52 + man/s2_point.Rd | 42 + man/wk_handle.s2_geography.Rd | 85 + src/Makevars.in | 219 ++ src/Makevars.ucrt | 3 + src/Makevars.win | 224 ++ src/RcppExports.cpp | 1482 +++++++++ src/absl/algorithm/algorithm.h | 159 + src/absl/algorithm/container.h | 1774 ++++++++++ src/absl/base/attributes.h | 764 +++++ src/absl/base/call_once.h | 219 ++ src/absl/base/casts.h | 180 ++ src/absl/base/config.h | 915 ++++++ src/absl/base/const_init.h | 76 + src/absl/base/dynamic_annotations.h | 471 +++ src/absl/base/internal/atomic_hook.h | 200 ++ src/absl/base/internal/cycleclock.cc | 77 + src/absl/base/internal/cycleclock.h | 159 + src/absl/base/internal/direct_mmap.h | 169 + src/absl/base/internal/dynamic_annotations.h | 398 +++ src/absl/base/internal/endian.h | 282 ++ src/absl/base/internal/errno_saver.h | 43 + src/absl/base/internal/fast_type_id.h | 50 + src/absl/base/internal/hide_ptr.h | 51 + src/absl/base/internal/identity.h | 37 + src/absl/base/internal/inline_variable.h | 107 + src/absl/base/internal/invoke.h | 241 ++ src/absl/base/internal/low_level_alloc.cc | 620 ++++ src/absl/base/internal/low_level_alloc.h | 130 + src/absl/base/internal/low_level_scheduling.h | 134 + src/absl/base/internal/per_thread_tls.h | 52 + src/absl/base/internal/prefetch.h | 138 + src/absl/base/internal/pretty_function.h | 33 + src/absl/base/internal/raw_logging.cc | 249 ++ src/absl/base/internal/raw_logging.h | 198 ++ src/absl/base/internal/scheduling_mode.h | 58 + src/absl/base/internal/scoped_set_env.cc | 81 + src/absl/base/internal/scoped_set_env.h | 45 + src/absl/base/internal/spinlock.cc | 232 ++ src/absl/base/internal/spinlock.h | 256 ++ src/absl/base/internal/spinlock_akaros.inc | 35 + src/absl/base/internal/spinlock_linux.inc | 71 + src/absl/base/internal/spinlock_posix.inc | 46 + src/absl/base/internal/spinlock_wait.cc | 81 + src/absl/base/internal/spinlock_wait.h | 95 + src/absl/base/internal/spinlock_win32.inc | 37 + src/absl/base/internal/strerror.cc | 88 + src/absl/base/internal/strerror.h | 39 + src/absl/base/internal/sysinfo.cc | 507 +++ src/absl/base/internal/sysinfo.h | 74 + src/absl/base/internal/thread_annotations.h | 271 ++ src/absl/base/internal/thread_identity.cc | 156 + src/absl/base/internal/thread_identity.h | 265 ++ src/absl/base/internal/throw_delegate.cc | 212 ++ src/absl/base/internal/throw_delegate.h | 75 + src/absl/base/internal/tsan_mutex_interface.h | 68 + src/absl/base/internal/unaligned_access.h | 82 + src/absl/base/internal/unscaledcycleclock.cc | 153 + src/absl/base/internal/unscaledcycleclock.h | 133 + src/absl/base/log_severity.cc | 55 + src/absl/base/log_severity.h | 172 + src/absl/base/macros.h | 158 + src/absl/base/optimization.h | 252 ++ src/absl/base/options.h | 238 ++ src/absl/base/policy_checks.h | 111 + src/absl/base/port.h | 25 + src/absl/base/thread_annotations.h | 335 ++ src/absl/container/btree_map.h | 851 +++++ src/absl/container/btree_set.h | 793 +++++ src/absl/container/fixed_array.h | 529 +++ src/absl/container/flat_hash_map.h | 613 ++++ src/absl/container/flat_hash_set.h | 510 +++ src/absl/container/inlined_vector.h | 866 +++++ src/absl/container/internal/btree.h | 2854 +++++++++++++++++ src/absl/container/internal/btree_container.h | 699 ++++ src/absl/container/internal/common.h | 207 ++ .../container/internal/compressed_tuple.h | 290 ++ .../container/internal/container_memory.h | 442 +++ .../container/internal/counting_allocator.h | 122 + .../internal/hash_function_defaults.h | 163 + .../container/internal/hash_policy_traits.h | 208 ++ src/absl/container/internal/hashtable_debug.h | 110 + .../internal/hashtable_debug_hooks.h | 85 + .../container/internal/hashtablez_sampler.cc | 238 ++ .../container/internal/hashtablez_sampler.h | 299 ++ ...ashtablez_sampler_force_weak_definition.cc | 31 + src/absl/container/internal/inlined_vector.h | 953 ++++++ src/absl/container/internal/layout.h | 743 +++++ .../container/internal/node_slot_policy.h | 92 + src/absl/container/internal/raw_hash_map.h | 198 ++ src/absl/container/internal/raw_hash_set.cc | 71 + src/absl/container/internal/raw_hash_set.h | 2369 ++++++++++++++ src/absl/container/internal/tracked.h | 83 + src/absl/container/node_hash_map.h | 604 ++++ src/absl/container/node_hash_set.h | 500 +++ src/absl/debugging/failure_signal_handler.cc | 386 +++ src/absl/debugging/failure_signal_handler.h | 121 + .../debugging/internal/address_is_readable.cc | 96 + .../debugging/internal/address_is_readable.h | 32 + src/absl/debugging/internal/demangle.cc | 1959 +++++++++++ src/absl/debugging/internal/demangle.h | 71 + src/absl/debugging/internal/elf_mem_image.cc | 387 +++ src/absl/debugging/internal/elf_mem_image.h | 139 + src/absl/debugging/internal/examine_stack.cc | 315 ++ src/absl/debugging/internal/examine_stack.h | 64 + .../debugging/internal/stack_consumption.cc | 185 ++ .../debugging/internal/stack_consumption.h | 50 + .../internal/stacktrace_aarch64-inl.inc | 204 ++ .../debugging/internal/stacktrace_arm-inl.inc | 139 + .../debugging/internal/stacktrace_config.h | 88 + .../internal/stacktrace_emscripten-inl.inc | 110 + .../internal/stacktrace_generic-inl.inc | 108 + .../internal/stacktrace_powerpc-inl.inc | 258 ++ .../internal/stacktrace_riscv-inl.inc | 236 ++ .../internal/stacktrace_unimplemented-inl.inc | 24 + .../internal/stacktrace_win32-inl.inc | 93 + .../debugging/internal/stacktrace_x86-inl.inc | 369 +++ src/absl/debugging/internal/symbolize.h | 153 + src/absl/debugging/internal/vdso_support.cc | 204 ++ src/absl/debugging/internal/vdso_support.h | 158 + src/absl/debugging/leak_check.cc | 73 + src/absl/debugging/leak_check.h | 150 + src/absl/debugging/stacktrace.cc | 142 + src/absl/debugging/stacktrace.h | 231 ++ src/absl/debugging/symbolize.cc | 17 + src/absl/debugging/symbolize.h | 99 + src/absl/debugging/symbolize_darwin.inc | 101 + src/absl/debugging/symbolize_elf.inc | 1613 ++++++++++ src/absl/debugging/symbolize_emscripten.inc | 72 + .../debugging/symbolize_unimplemented.inc | 40 + src/absl/debugging/symbolize_win32.inc | 81 + src/absl/functional/any_invocable.h | 313 ++ src/absl/functional/bind_front.h | 193 ++ src/absl/functional/function_ref.h | 143 + src/absl/functional/internal/any_invocable.h | 857 +++++ src/absl/functional/internal/front_binder.h | 95 + src/absl/functional/internal/function_ref.h | 106 + src/absl/memory/memory.h | 699 ++++ src/absl/meta/type_traits.h | 797 +++++ src/absl/numeric/bits.h | 178 + src/absl/numeric/int128.cc | 385 +++ src/absl/numeric/int128.h | 1169 +++++++ src/absl/numeric/int128_have_intrinsic.inc | 296 ++ src/absl/numeric/int128_no_intrinsic.inc | 311 ++ src/absl/numeric/internal/bits.h | 358 +++ src/absl/numeric/internal/representation.h | 55 + .../profiling/internal/exponential_biased.cc | 93 + .../profiling/internal/exponential_biased.h | 130 + .../profiling/internal/periodic_sampler.cc | 53 + .../profiling/internal/periodic_sampler.h | 211 ++ src/absl/profiling/internal/sample_recorder.h | 245 ++ src/absl/strings/ascii.cc | 200 ++ src/absl/strings/ascii.h | 242 ++ src/absl/strings/charconv.cc | 984 ++++++ src/absl/strings/charconv.h | 120 + src/absl/strings/cord.cc | 1328 ++++++++ src/absl/strings/cord.h | 1649 ++++++++++ src/absl/strings/cord_analysis.cc | 188 ++ src/absl/strings/cord_analysis.h | 44 + src/absl/strings/cord_buffer.cc | 30 + src/absl/strings/cord_buffer.h | 572 ++++ src/absl/strings/escaping.cc | 949 ++++++ src/absl/strings/escaping.h | 164 + src/absl/strings/internal/char_map.h | 156 + src/absl/strings/internal/charconv_bigint.cc | 359 +++ src/absl/strings/internal/charconv_bigint.h | 423 +++ src/absl/strings/internal/charconv_parse.cc | 504 +++ src/absl/strings/internal/charconv_parse.h | 99 + src/absl/strings/internal/cord_data_edge.h | 63 + src/absl/strings/internal/cord_internal.cc | 77 + src/absl/strings/internal/cord_internal.h | 655 ++++ src/absl/strings/internal/cord_rep_btree.cc | 1228 +++++++ src/absl/strings/internal/cord_rep_btree.h | 924 ++++++ .../internal/cord_rep_btree_navigator.cc | 187 ++ .../internal/cord_rep_btree_navigator.h | 267 ++ .../strings/internal/cord_rep_btree_reader.cc | 69 + .../strings/internal/cord_rep_btree_reader.h | 212 ++ src/absl/strings/internal/cord_rep_consume.cc | 62 + src/absl/strings/internal/cord_rep_consume.h | 50 + src/absl/strings/internal/cord_rep_crc.cc | 54 + src/absl/strings/internal/cord_rep_crc.h | 102 + src/absl/strings/internal/cord_rep_flat.h | 187 ++ src/absl/strings/internal/cord_rep_ring.cc | 773 +++++ src/absl/strings/internal/cord_rep_ring.h | 607 ++++ .../strings/internal/cord_rep_ring_reader.h | 118 + src/absl/strings/internal/cordz_functions.cc | 96 + src/absl/strings/internal/cordz_functions.h | 85 + src/absl/strings/internal/cordz_handle.cc | 139 + src/absl/strings/internal/cordz_handle.h | 131 + src/absl/strings/internal/cordz_info.cc | 418 +++ src/absl/strings/internal/cordz_info.h | 298 ++ .../strings/internal/cordz_sample_token.cc | 64 + .../strings/internal/cordz_sample_token.h | 97 + src/absl/strings/internal/cordz_statistics.h | 88 + .../strings/internal/cordz_update_scope.h | 71 + .../strings/internal/cordz_update_tracker.h | 123 + src/absl/strings/internal/escaping.cc | 181 ++ src/absl/strings/internal/escaping.h | 58 + src/absl/strings/internal/memutil.cc | 112 + src/absl/strings/internal/memutil.h | 148 + src/absl/strings/internal/ostringstream.cc | 36 + src/absl/strings/internal/ostringstream.h | 89 + src/absl/strings/internal/pow10_helper.cc | 122 + src/absl/strings/internal/pow10_helper.h | 40 + .../strings/internal/resize_uninitialized.h | 119 + src/absl/strings/internal/stl_type_traits.h | 248 ++ src/absl/strings/internal/str_format/arg.cc | 488 +++ src/absl/strings/internal/str_format/arg.h | 526 +++ src/absl/strings/internal/str_format/bind.cc | 258 ++ src/absl/strings/internal/str_format/bind.h | 248 ++ .../strings/internal/str_format/checker.h | 338 ++ .../strings/internal/str_format/extension.cc | 74 + .../strings/internal/str_format/extension.h | 448 +++ .../internal/str_format/float_conversion.cc | 1423 ++++++++ .../internal/str_format/float_conversion.h | 37 + .../strings/internal/str_format/output.cc | 72 + src/absl/strings/internal/str_format/output.h | 97 + .../strings/internal/str_format/parser.cc | 339 ++ src/absl/strings/internal/str_format/parser.h | 359 +++ src/absl/strings/internal/str_join_internal.h | 317 ++ .../strings/internal/str_split_internal.h | 430 +++ src/absl/strings/internal/string_constant.h | 72 + src/absl/strings/internal/utf8.cc | 53 + src/absl/strings/internal/utf8.h | 50 + src/absl/strings/match.cc | 43 + src/absl/strings/match.h | 100 + src/absl/strings/numbers.cc | 1093 +++++++ src/absl/strings/numbers.h | 303 ++ src/absl/strings/str_cat.cc | 246 ++ src/absl/strings/str_cat.h | 415 +++ src/absl/strings/str_format.h | 812 +++++ src/absl/strings/str_join.h | 287 ++ src/absl/strings/str_replace.cc | 82 + src/absl/strings/str_replace.h | 219 ++ src/absl/strings/str_split.cc | 139 + src/absl/strings/str_split.h | 547 ++++ src/absl/strings/string_view.cc | 219 ++ src/absl/strings/string_view.h | 711 ++++ src/absl/strings/strip.h | 93 + src/absl/strings/substitute.cc | 172 + src/absl/strings/substitute.h | 729 +++++ src/absl/synchronization/barrier.cc | 52 + src/absl/synchronization/barrier.h | 79 + src/absl/synchronization/blocking_counter.cc | 67 + src/absl/synchronization/blocking_counter.h | 101 + .../internal/create_thread_identity.cc | 143 + .../internal/create_thread_identity.h | 56 + src/absl/synchronization/internal/futex.h | 154 + .../synchronization/internal/graphcycles.cc | 698 ++++ .../synchronization/internal/graphcycles.h | 141 + .../synchronization/internal/kernel_timeout.h | 156 + .../internal/per_thread_sem.cc | 102 + .../synchronization/internal/per_thread_sem.h | 110 + .../synchronization/internal/thread_pool.h | 93 + src/absl/synchronization/internal/waiter.cc | 403 +++ src/absl/synchronization/internal/waiter.h | 161 + src/absl/synchronization/mutex.cc | 2786 ++++++++++++++++ src/absl/synchronization/mutex.h | 1090 +++++++ src/absl/synchronization/notification.cc | 78 + src/absl/synchronization/notification.h | 124 + src/absl/time/civil_time.cc | 173 + src/absl/time/civil_time.h | 538 ++++ src/absl/time/clock.cc | 585 ++++ src/absl/time/clock.h | 74 + src/absl/time/duration.cc | 955 ++++++ src/absl/time/format.cc | 160 + .../internal/cctz/include/cctz/civil_time.h | 332 ++ .../cctz/include/cctz/civil_time_detail.h | 632 ++++ .../internal/cctz/include/cctz/time_zone.h | 459 +++ .../cctz/include/cctz/zone_info_source.h | 102 + .../internal/cctz/src/civil_time_detail.cc | 94 + .../time/internal/cctz/src/time_zone_fixed.cc | 140 + .../time/internal/cctz/src/time_zone_fixed.h | 52 + .../internal/cctz/src/time_zone_format.cc | 1025 ++++++ .../time/internal/cctz/src/time_zone_if.cc | 45 + .../time/internal/cctz/src/time_zone_if.h | 77 + .../time/internal/cctz/src/time_zone_impl.cc | 113 + .../time/internal/cctz/src/time_zone_impl.h | 93 + .../time/internal/cctz/src/time_zone_info.cc | 1027 ++++++ .../time/internal/cctz/src/time_zone_info.h | 137 + .../time/internal/cctz/src/time_zone_libc.cc | 315 ++ .../time/internal/cctz/src/time_zone_libc.h | 55 + .../internal/cctz/src/time_zone_lookup.cc | 236 ++ .../time/internal/cctz/src/time_zone_posix.cc | 159 + .../time/internal/cctz/src/time_zone_posix.h | 132 + src/absl/time/internal/cctz/src/tzfile.h | 122 + .../internal/cctz/src/zone_info_source.cc | 115 + .../time/internal/get_current_time_chrono.inc | 31 + .../time/internal/get_current_time_posix.inc | 24 + src/absl/time/internal/zoneinfo.inc | 724 +++++ src/absl/time/time.cc | 500 +++ src/absl/time/time.h | 1620 ++++++++++ src/absl/types/any.h | 517 +++ src/absl/types/bad_any_cast.cc | 46 + src/absl/types/bad_any_cast.h | 75 + src/absl/types/bad_optional_access.cc | 48 + src/absl/types/bad_optional_access.h | 78 + src/absl/types/bad_variant_access.cc | 64 + src/absl/types/bad_variant_access.h | 82 + src/absl/types/compare.h | 600 ++++ src/absl/types/internal/conformance_aliases.h | 447 +++ .../types/internal/conformance_archetype.h | 978 ++++++ src/absl/types/internal/conformance_profile.h | 933 ++++++ .../internal/conformance_testing_helpers.h | 391 +++ src/absl/types/internal/optional.h | 404 +++ src/absl/types/internal/parentheses.h | 34 + src/absl/types/internal/span.h | 128 + src/absl/types/internal/transform_args.h | 246 ++ src/absl/types/internal/variant.h | 1646 ++++++++++ src/absl/types/optional.h | 779 +++++ src/absl/types/span.h | 727 +++++ src/absl/types/variant.h | 866 +++++ src/absl/utility/utility.h | 350 ++ src/cpp-compat.cpp | 40 + src/cpp-compat.h | 16 + src/geography-operator.h | 108 + src/geography.h | 89 + src/init.cpp | 14 + src/s2-accessors.cpp | 211 ++ src/s2-bounds.cpp | 64 + src/s2-cell-union.cpp | 393 +++ src/s2-cell.cpp | 812 +++++ src/s2-constructors-formatters.cpp | 955 ++++++ src/s2-geography.cpp | 25 + src/s2-lnglat.cpp | 87 + src/s2-matrix.cpp | 623 ++++ src/s2-options.h | 375 +++ src/s2-predicates.cpp | 235 ++ src/s2-transformers.cpp | 409 +++ src/s2/_fp_contract_off.h | 60 + src/s2/base/casts.h | 318 ++ src/s2/base/commandlineflags.h | 51 + src/s2/base/integral_types.h | 31 + src/s2/base/log_severity.h | 40 + src/s2/base/logging.h | 177 + src/s2/base/mutex.h | 24 + src/s2/base/port.h | 1013 ++++++ src/s2/base/spinlock.h | 60 + src/s2/base/stringprintf.cc | 107 + src/s2/base/stringprintf.h | 53 + src/s2/base/strtoint.cc | 65 + src/s2/base/strtoint.h | 106 + src/s2/base/timer.h | 50 + src/s2/encoded_s2cell_id_vector.cc | 164 + src/s2/encoded_s2cell_id_vector.h | 110 + src/s2/encoded_s2point_vector.cc | 838 +++++ src/s2/encoded_s2point_vector.h | 149 + src/s2/encoded_s2shape_index.cc | 181 ++ src/s2/encoded_s2shape_index.h | 276 ++ src/s2/encoded_string_vector.cc | 66 + src/s2/encoded_string_vector.h | 164 + src/s2/encoded_uint_vector.h | 299 ++ src/s2/id_set_lexicon.cc | 81 + src/s2/id_set_lexicon.h | 199 ++ src/s2/mutable_s2shape_index.cc | 1585 +++++++++ src/s2/mutable_s2shape_index.h | 609 ++++ src/s2/r1interval.h | 220 ++ src/s2/r2.h | 26 + src/s2/r2rect.cc | 93 + src/s2/r2rect.h | 234 ++ src/s2/s1angle.cc | 54 + src/s2/s1angle.h | 336 ++ src/s2/s1chord_angle.cc | 159 + src/s2/s1chord_angle.h | 369 +++ src/s2/s1interval.cc | 296 ++ src/s2/s1interval.h | 266 ++ src/s2/s2boolean_operation.cc | 2392 ++++++++++++++ src/s2/s2boolean_operation.h | 501 +++ src/s2/s2builder.cc | 1829 +++++++++++ src/s2/s2builder.h | 1057 ++++++ src/s2/s2builder_graph.cc | 1084 +++++++ src/s2/s2builder_graph.h | 799 +++++ src/s2/s2builder_layer.h | 50 + src/s2/s2builderutil_closed_set_normalizer.cc | 313 ++ src/s2/s2builderutil_closed_set_normalizer.h | 221 ++ ...s2builderutil_find_polygon_degeneracies.cc | 392 +++ .../s2builderutil_find_polygon_degeneracies.h | 86 + src/s2/s2builderutil_graph_shape.h | 57 + src/s2/s2builderutil_lax_polygon_layer.cc | 212 ++ src/s2/s2builderutil_lax_polygon_layer.h | 218 ++ src/s2/s2builderutil_s2point_vector_layer.cc | 74 + src/s2/s2builderutil_s2point_vector_layer.h | 122 + src/s2/s2builderutil_s2polygon_layer.cc | 191 ++ src/s2/s2builderutil_s2polygon_layer.h | 211 ++ src/s2/s2builderutil_s2polyline_layer.cc | 105 + src/s2/s2builderutil_s2polyline_layer.h | 174 + .../s2builderutil_s2polyline_vector_layer.cc | 98 + .../s2builderutil_s2polyline_vector_layer.h | 292 ++ src/s2/s2builderutil_snap_functions.cc | 354 ++ src/s2/s2builderutil_snap_functions.h | 239 ++ src/s2/s2builderutil_testing.cc | 37 + src/s2/s2builderutil_testing.h | 100 + src/s2/s2cap.cc | 347 ++ src/s2/s2cap.h | 286 ++ src/s2/s2cell.cc | 552 ++++ src/s2/s2cell.h | 249 ++ src/s2/s2cell_id.cc | 619 ++++ src/s2/s2cell_id.h | 705 ++++ src/s2/s2cell_index.cc | 149 + src/s2/s2cell_index.h | 660 ++++ src/s2/s2cell_union.cc | 515 +++ src/s2/s2cell_union.h | 399 +++ src/s2/s2centroids.cc | 84 + src/s2/s2centroids.h | 87 + src/s2/s2closest_cell_query.cc | 123 + src/s2/s2closest_cell_query.h | 385 +++ src/s2/s2closest_cell_query_base.h | 842 +++++ src/s2/s2closest_edge_query.cc | 106 + src/s2/s2closest_edge_query.h | 421 +++ src/s2/s2closest_edge_query_base.h | 946 ++++++ src/s2/s2closest_edge_query_testing.h | 91 + src/s2/s2closest_point_query.cc | 66 + src/s2/s2closest_point_query.h | 465 +++ src/s2/s2closest_point_query_base.h | 767 +++++ src/s2/s2contains_point_query.h | 328 ++ src/s2/s2contains_vertex_query.cc | 39 + src/s2/s2contains_vertex_query.h | 66 + src/s2/s2convex_hull_query.cc | 198 ++ src/s2/s2convex_hull_query.h | 110 + src/s2/s2coords.cc | 146 + src/s2/s2coords.h | 459 +++ src/s2/s2coords_internal.h | 71 + src/s2/s2crossing_edge_query.cc | 380 +++ src/s2/s2crossing_edge_query.h | 220 ++ src/s2/s2debug.cc | 23 + src/s2/s2debug.h | 69 + src/s2/s2distance_target.h | 165 + src/s2/s2earth.cc | 52 + src/s2/s2earth.h | 268 ++ src/s2/s2edge_clipping.cc | 462 +++ src/s2/s2edge_clipping.h | 183 ++ src/s2/s2edge_crosser.cc | 85 + src/s2/s2edge_crosser.h | 343 ++ src/s2/s2edge_crossings.cc | 515 +++ src/s2/s2edge_crossings.h | 138 + src/s2/s2edge_crossings_internal.h | 59 + src/s2/s2edge_distances.cc | 427 +++ src/s2/s2edge_distances.h | 192 ++ src/s2/s2edge_tessellator.cc | 276 ++ src/s2/s2edge_tessellator.h | 101 + src/s2/s2edge_vector_shape.h | 85 + src/s2/s2error.cc | 29 + src/s2/s2error.h | 147 + src/s2/s2furthest_edge_query.cc | 117 + src/s2/s2furthest_edge_query.h | 439 +++ src/s2/s2latlng.cc | 90 + src/s2/s2latlng.h | 234 ++ src/s2/s2latlng_rect.cc | 727 +++++ src/s2/s2latlng_rect.h | 434 +++ src/s2/s2latlng_rect_bounder.cc | 344 ++ src/s2/s2latlng_rect_bounder.h | 89 + src/s2/s2lax_loop_shape.cc | 104 + src/s2/s2lax_loop_shape.h | 153 + src/s2/s2lax_polygon_shape.cc | 348 ++ src/s2/s2lax_polygon_shape.h | 183 ++ src/s2/s2lax_polyline_shape.cc | 118 + src/s2/s2lax_polyline_shape.h | 124 + src/s2/s2loop.cc | 1509 +++++++++ src/s2/s2loop.h | 711 ++++ src/s2/s2loop_measures.cc | 313 ++ src/s2/s2loop_measures.h | 280 ++ src/s2/s2max_distance_targets.cc | 265 ++ src/s2/s2max_distance_targets.h | 241 ++ src/s2/s2measures.cc | 128 + src/s2/s2measures.h | 78 + src/s2/s2metrics.cc | 122 + src/s2/s2metrics.h | 199 ++ src/s2/s2min_distance_targets.cc | 295 ++ src/s2/s2min_distance_targets.h | 273 ++ src/s2/s2padded_cell.cc | 162 + src/s2/s2padded_cell.h | 108 + src/s2/s2point.h | 38 + src/s2/s2point_compression.cc | 388 +++ src/s2/s2point_compression.h | 78 + src/s2/s2point_index.h | 345 ++ src/s2/s2point_region.cc | 72 + src/s2/s2point_region.h | 76 + src/s2/s2point_span.h | 57 + src/s2/s2point_vector_shape.h | 127 + src/s2/s2pointutil.cc | 131 + src/s2/s2pointutil.h | 139 + src/s2/s2polygon.cc | 1564 +++++++++ src/s2/s2polygon.h | 934 ++++++ src/s2/s2polyline.cc | 645 ++++ src/s2/s2polyline.h | 379 +++ src/s2/s2polyline_alignment.cc | 414 +++ src/s2/s2polyline_alignment.h | 245 ++ src/s2/s2polyline_alignment_internal.h | 158 + src/s2/s2polyline_measures.cc | 42 + src/s2/s2polyline_measures.h | 53 + src/s2/s2polyline_simplifier.cc | 187 ++ src/s2/s2polyline_simplifier.h | 109 + src/s2/s2predicates.cc | 1486 +++++++++ src/s2/s2predicates.h | 282 ++ src/s2/s2predicates_internal.h | 135 + src/s2/s2projections.cc | 109 + src/s2/s2projections.h | 161 + src/s2/s2r2rect.cc | 88 + src/s2/s2r2rect.h | 292 ++ src/s2/s2region.cc | 26 + src/s2/s2region.h | 142 + src/s2/s2region_coverer.cc | 514 +++ src/s2/s2region_coverer.h | 361 +++ src/s2/s2region_intersection.cc | 84 + src/s2/s2region_intersection.h | 79 + src/s2/s2region_term_indexer.cc | 270 ++ src/s2/s2region_term_indexer.h | 299 ++ src/s2/s2region_union.cc | 90 + src/s2/s2region_union.h | 83 + src/s2/s2shape.h | 283 ++ src/s2/s2shape_index.cc | 321 ++ src/s2/s2shape_index.h | 781 +++++ src/s2/s2shape_index_buffered_region.cc | 113 + src/s2/s2shape_index_buffered_region.h | 135 + src/s2/s2shape_index_measures.cc | 92 + src/s2/s2shape_index_measures.h | 100 + src/s2/s2shape_index_region.h | 350 ++ src/s2/s2shape_measures.cc | 138 + src/s2/s2shape_measures.h | 95 + .../s2shapeutil_build_polygon_boundaries.cc | 120 + src/s2/s2shapeutil_build_polygon_boundaries.h | 66 + src/s2/s2shapeutil_coding.cc | 253 ++ src/s2/s2shapeutil_coding.h | 283 ++ src/s2/s2shapeutil_contains_brute_force.cc | 40 + src/s2/s2shapeutil_contains_brute_force.h | 41 + src/s2/s2shapeutil_count_edges.h | 57 + src/s2/s2shapeutil_edge_iterator.cc | 45 + src/s2/s2shapeutil_edge_iterator.h | 72 + src/s2/s2shapeutil_get_reference_point.cc | 107 + src/s2/s2shapeutil_get_reference_point.h | 48 + src/s2/s2shapeutil_range_iterator.cc | 58 + src/s2/s2shapeutil_range_iterator.h | 65 + src/s2/s2shapeutil_shape_edge.h | 58 + src/s2/s2shapeutil_shape_edge_id.h | 97 + src/s2/s2shapeutil_testing.h | 36 + .../s2shapeutil_visit_crossing_edge_pairs.cc | 440 +++ .../s2shapeutil_visit_crossing_edge_pairs.h | 72 + src/s2/s2testing.cc | 465 +++ src/s2/s2testing.h | 386 +++ src/s2/s2text_format.cc | 506 +++ src/s2/s2text_format.h | 289 ++ src/s2/s2wedge_relations.cc | 80 + src/s2/s2wedge_relations.h | 64 + src/s2/sequence_lexicon.h | 296 ++ src/s2/strings/ostringstream.cc | 35 + src/s2/strings/ostringstream.h | 105 + src/s2/strings/serialize.cc | 46 + src/s2/strings/serialize.h | 40 + src/s2/util/bits/bit-interleave.cc | 274 ++ src/s2/util/bits/bit-interleave.h | 53 + src/s2/util/bits/bits.cc | 155 + src/s2/util/bits/bits.h | 745 +++++ src/s2/util/coding/coder.cc | 83 + src/s2/util/coding/coder.h | 562 ++++ src/s2/util/coding/nth-derivative.h | 134 + src/s2/util/coding/transforms.h | 62 + src/s2/util/coding/varint.cc | 289 ++ src/s2/util/coding/varint.h | 476 +++ src/s2/util/endian/endian.h | 861 +++++ src/s2/util/gtl/btree.h | 2471 ++++++++++++++ src/s2/util/gtl/btree_container.h | 411 +++ src/s2/util/gtl/btree_map.h | 79 + src/s2/util/gtl/btree_set.h | 73 + src/s2/util/gtl/compact_array.h | 661 ++++ src/s2/util/gtl/container_logging.h | 291 ++ src/s2/util/gtl/dense_hash_set.h | 358 +++ src/s2/util/gtl/densehashtable.h | 1493 +++++++++ src/s2/util/gtl/hashtable_common.h | 253 ++ src/s2/util/gtl/layout.h | 28 + src/s2/util/gtl/legacy_random_shuffle.h | 77 + src/s2/util/hash/mix.h | 76 + src/s2/util/math/exactfloat/exactfloat.cc | 848 +++++ src/s2/util/math/exactfloat/exactfloat.h | 646 ++++ src/s2/util/math/mathutil.cc | 75 + src/s2/util/math/mathutil.h | 189 ++ src/s2/util/math/matrix3x3.h | 574 ++++ src/s2/util/math/vector.h | 569 ++++ src/s2/util/math/vector3_hash.h | 54 + src/s2/util/units/length-units.cc | 21 + src/s2/util/units/length-units.h | 135 + src/s2/util/units/physical-units.h | 313 ++ src/s2/value_lexicon.h | 234 ++ src/s2geography.h | 13 + src/s2geography/accessors-geog.cc | 202 ++ src/s2geography/accessors-geog.h | 36 + src/s2geography/accessors.cc | 273 ++ src/s2geography/accessors.h | 19 + src/s2geography/aggregator.h | 15 + src/s2geography/build.cc | 411 +++ src/s2geography/build.h | 97 + src/s2geography/constructor.h | 410 +++ src/s2geography/coverings.cc | 82 + src/s2geography/coverings.h | 21 + src/s2geography/distance.cc | 70 + src/s2geography/distance.h | 17 + src/s2geography/geoarrow-imports.h | 64 + src/s2geography/geography.cc | 182 ++ src/s2geography/geography.h | 200 ++ src/s2geography/index.h | 92 + src/s2geography/linear-referencing.cc | 78 + src/s2geography/linear-referencing.h | 13 + src/s2geography/predicates.cc | 83 + src/s2geography/predicates.h | 31 + src/tests/main.c | 14 + src/tests/soname.h | 9 + src/wk-impl.c | 2 + tests/area.R | 23 + tests/area.Rout | 29 + tests/testthat.R | 12 + tests/testthat/test-data.R | 30 + tests/testthat/test-plot.R | 36 + tests/testthat/test-s2-accessors.R | 228 ++ tests/testthat/test-s2-bounds.R | 44 + tests/testthat/test-s2-cell-union.R | 208 ++ tests/testthat/test-s2-cell.R | 410 +++ .../test-s2-constructors-formatters.R | 200 ++ tests/testthat/test-s2-earth.R | 5 + tests/testthat/test-s2-geography.R | 277 ++ tests/testthat/test-s2-lnglat.R | 38 + tests/testthat/test-s2-matrix.R | 270 ++ tests/testthat/test-s2-options.R | 11 + tests/testthat/test-s2-point.R | 24 + tests/testthat/test-s2-predicates.R | 182 ++ tests/testthat/test-s2-transformers.R | 721 +++++ tests/testthat/test-utils.R | 42 + tests/testthat/test-vctrs.R | 24 + tests/testthat/test-wk-utils.R | 313 ++ tools/version.c | 4 + tools/winlibs.R | 10 + 683 files changed, 190352 insertions(+) create mode 100644 DESCRIPTION create mode 100644 MD5 create mode 100644 NAMESPACE create mode 100644 NEWS.md create mode 100644 R/RcppExports.R create mode 100644 R/data.R create mode 100644 R/plot.R create mode 100644 R/s2-accessors.R create mode 100644 R/s2-bounds.R create mode 100644 R/s2-cell-union.R create mode 100644 R/s2-cell.R create mode 100644 R/s2-constructors-formatters.R create mode 100644 R/s2-earth.R create mode 100644 R/s2-geography.R create mode 100644 R/s2-lnglat.R create mode 100644 R/s2-matrix.R create mode 100644 R/s2-options.R create mode 100644 R/s2-package.R create mode 100644 R/s2-point.R create mode 100644 R/s2-predicates.R create mode 100644 R/s2-transformers.R create mode 100644 R/utils.R create mode 100644 R/vctrs.R create mode 100644 R/wk-utils.R create mode 100644 R/zzz.R create mode 100644 README.md create mode 100644 build/partial.rdb create mode 100755 cleanup create mode 100755 configure create mode 100755 configure.win create mode 100644 data/s2_data_example_wkt.rda create mode 100644 data/s2_data_tbl_cities.rda create mode 100644 data/s2_data_tbl_countries.rda create mode 100644 data/s2_data_tbl_timezones.rda create mode 100644 inst/extdata/emptyfile create mode 100644 man/as_s2_geography.Rd create mode 100644 man/figures/rc300.png create mode 100644 man/s2-package.Rd create mode 100644 man/s2_boundary.Rd create mode 100644 man/s2_bounds_cap.Rd create mode 100644 man/s2_cell.Rd create mode 100644 man/s2_cell_is_valid.Rd create mode 100644 man/s2_cell_union.Rd create mode 100644 man/s2_cell_union_normalize.Rd create mode 100644 man/s2_closest_feature.Rd create mode 100644 man/s2_contains.Rd create mode 100644 man/s2_data_example_wkt.Rd create mode 100644 man/s2_data_tbl_countries.Rd create mode 100644 man/s2_earth_radius_meters.Rd create mode 100644 man/s2_geog_point.Rd create mode 100644 man/s2_interpolate.Rd create mode 100644 man/s2_is_collection.Rd create mode 100644 man/s2_lnglat.Rd create mode 100644 man/s2_options.Rd create mode 100644 man/s2_plot.Rd create mode 100644 man/s2_point.Rd create mode 100644 man/wk_handle.s2_geography.Rd create mode 100644 src/Makevars.in create mode 100644 src/Makevars.ucrt create mode 100644 src/Makevars.win create mode 100644 src/RcppExports.cpp create mode 100644 src/absl/algorithm/algorithm.h create mode 100644 src/absl/algorithm/container.h create mode 100644 src/absl/base/attributes.h create mode 100644 src/absl/base/call_once.h create mode 100644 src/absl/base/casts.h create mode 100644 src/absl/base/config.h create mode 100644 src/absl/base/const_init.h create mode 100644 src/absl/base/dynamic_annotations.h create mode 100644 src/absl/base/internal/atomic_hook.h create mode 100644 src/absl/base/internal/cycleclock.cc create mode 100644 src/absl/base/internal/cycleclock.h create mode 100644 src/absl/base/internal/direct_mmap.h create mode 100644 src/absl/base/internal/dynamic_annotations.h create mode 100644 src/absl/base/internal/endian.h create mode 100644 src/absl/base/internal/errno_saver.h create mode 100644 src/absl/base/internal/fast_type_id.h create mode 100644 src/absl/base/internal/hide_ptr.h create mode 100644 src/absl/base/internal/identity.h create mode 100644 src/absl/base/internal/inline_variable.h create mode 100644 src/absl/base/internal/invoke.h create mode 100644 src/absl/base/internal/low_level_alloc.cc create mode 100644 src/absl/base/internal/low_level_alloc.h create mode 100644 src/absl/base/internal/low_level_scheduling.h create mode 100644 src/absl/base/internal/per_thread_tls.h create mode 100644 src/absl/base/internal/prefetch.h create mode 100644 src/absl/base/internal/pretty_function.h create mode 100644 src/absl/base/internal/raw_logging.cc create mode 100644 src/absl/base/internal/raw_logging.h create mode 100644 src/absl/base/internal/scheduling_mode.h create mode 100644 src/absl/base/internal/scoped_set_env.cc create mode 100644 src/absl/base/internal/scoped_set_env.h create mode 100644 src/absl/base/internal/spinlock.cc create mode 100644 src/absl/base/internal/spinlock.h create mode 100644 src/absl/base/internal/spinlock_akaros.inc create mode 100644 src/absl/base/internal/spinlock_linux.inc create mode 100644 src/absl/base/internal/spinlock_posix.inc create mode 100644 src/absl/base/internal/spinlock_wait.cc create mode 100644 src/absl/base/internal/spinlock_wait.h create mode 100644 src/absl/base/internal/spinlock_win32.inc create mode 100644 src/absl/base/internal/strerror.cc create mode 100644 src/absl/base/internal/strerror.h create mode 100644 src/absl/base/internal/sysinfo.cc create mode 100644 src/absl/base/internal/sysinfo.h create mode 100644 src/absl/base/internal/thread_annotations.h create mode 100644 src/absl/base/internal/thread_identity.cc create mode 100644 src/absl/base/internal/thread_identity.h create mode 100644 src/absl/base/internal/throw_delegate.cc create mode 100644 src/absl/base/internal/throw_delegate.h create mode 100644 src/absl/base/internal/tsan_mutex_interface.h create mode 100644 src/absl/base/internal/unaligned_access.h create mode 100644 src/absl/base/internal/unscaledcycleclock.cc create mode 100644 src/absl/base/internal/unscaledcycleclock.h create mode 100644 src/absl/base/log_severity.cc create mode 100644 src/absl/base/log_severity.h create mode 100644 src/absl/base/macros.h create mode 100644 src/absl/base/optimization.h create mode 100644 src/absl/base/options.h create mode 100644 src/absl/base/policy_checks.h create mode 100644 src/absl/base/port.h create mode 100644 src/absl/base/thread_annotations.h create mode 100644 src/absl/container/btree_map.h create mode 100644 src/absl/container/btree_set.h create mode 100644 src/absl/container/fixed_array.h create mode 100644 src/absl/container/flat_hash_map.h create mode 100644 src/absl/container/flat_hash_set.h create mode 100644 src/absl/container/inlined_vector.h create mode 100644 src/absl/container/internal/btree.h create mode 100644 src/absl/container/internal/btree_container.h create mode 100644 src/absl/container/internal/common.h create mode 100644 src/absl/container/internal/compressed_tuple.h create mode 100644 src/absl/container/internal/container_memory.h create mode 100644 src/absl/container/internal/counting_allocator.h create mode 100644 src/absl/container/internal/hash_function_defaults.h create mode 100644 src/absl/container/internal/hash_policy_traits.h create mode 100644 src/absl/container/internal/hashtable_debug.h create mode 100644 src/absl/container/internal/hashtable_debug_hooks.h create mode 100644 src/absl/container/internal/hashtablez_sampler.cc create mode 100644 src/absl/container/internal/hashtablez_sampler.h create mode 100644 src/absl/container/internal/hashtablez_sampler_force_weak_definition.cc create mode 100644 src/absl/container/internal/inlined_vector.h create mode 100644 src/absl/container/internal/layout.h create mode 100644 src/absl/container/internal/node_slot_policy.h create mode 100644 src/absl/container/internal/raw_hash_map.h create mode 100644 src/absl/container/internal/raw_hash_set.cc create mode 100644 src/absl/container/internal/raw_hash_set.h create mode 100644 src/absl/container/internal/tracked.h create mode 100644 src/absl/container/node_hash_map.h create mode 100644 src/absl/container/node_hash_set.h create mode 100644 src/absl/debugging/failure_signal_handler.cc create mode 100644 src/absl/debugging/failure_signal_handler.h create mode 100644 src/absl/debugging/internal/address_is_readable.cc create mode 100644 src/absl/debugging/internal/address_is_readable.h create mode 100644 src/absl/debugging/internal/demangle.cc create mode 100644 src/absl/debugging/internal/demangle.h create mode 100644 src/absl/debugging/internal/elf_mem_image.cc create mode 100644 src/absl/debugging/internal/elf_mem_image.h create mode 100644 src/absl/debugging/internal/examine_stack.cc create mode 100644 src/absl/debugging/internal/examine_stack.h create mode 100644 src/absl/debugging/internal/stack_consumption.cc create mode 100644 src/absl/debugging/internal/stack_consumption.h create mode 100644 src/absl/debugging/internal/stacktrace_aarch64-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_arm-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_config.h create mode 100644 src/absl/debugging/internal/stacktrace_emscripten-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_generic-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_powerpc-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_riscv-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_unimplemented-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_win32-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_x86-inl.inc create mode 100644 src/absl/debugging/internal/symbolize.h create mode 100644 src/absl/debugging/internal/vdso_support.cc create mode 100644 src/absl/debugging/internal/vdso_support.h create mode 100644 src/absl/debugging/leak_check.cc create mode 100644 src/absl/debugging/leak_check.h create mode 100644 src/absl/debugging/stacktrace.cc create mode 100644 src/absl/debugging/stacktrace.h create mode 100644 src/absl/debugging/symbolize.cc create mode 100644 src/absl/debugging/symbolize.h create mode 100644 src/absl/debugging/symbolize_darwin.inc create mode 100644 src/absl/debugging/symbolize_elf.inc create mode 100644 src/absl/debugging/symbolize_emscripten.inc create mode 100644 src/absl/debugging/symbolize_unimplemented.inc create mode 100644 src/absl/debugging/symbolize_win32.inc create mode 100644 src/absl/functional/any_invocable.h create mode 100644 src/absl/functional/bind_front.h create mode 100644 src/absl/functional/function_ref.h create mode 100644 src/absl/functional/internal/any_invocable.h create mode 100644 src/absl/functional/internal/front_binder.h create mode 100644 src/absl/functional/internal/function_ref.h create mode 100644 src/absl/memory/memory.h create mode 100644 src/absl/meta/type_traits.h create mode 100644 src/absl/numeric/bits.h create mode 100644 src/absl/numeric/int128.cc create mode 100644 src/absl/numeric/int128.h create mode 100644 src/absl/numeric/int128_have_intrinsic.inc create mode 100644 src/absl/numeric/int128_no_intrinsic.inc create mode 100644 src/absl/numeric/internal/bits.h create mode 100644 src/absl/numeric/internal/representation.h create mode 100644 src/absl/profiling/internal/exponential_biased.cc create mode 100644 src/absl/profiling/internal/exponential_biased.h create mode 100644 src/absl/profiling/internal/periodic_sampler.cc create mode 100644 src/absl/profiling/internal/periodic_sampler.h create mode 100644 src/absl/profiling/internal/sample_recorder.h create mode 100644 src/absl/strings/ascii.cc create mode 100644 src/absl/strings/ascii.h create mode 100644 src/absl/strings/charconv.cc create mode 100644 src/absl/strings/charconv.h create mode 100644 src/absl/strings/cord.cc create mode 100644 src/absl/strings/cord.h create mode 100644 src/absl/strings/cord_analysis.cc create mode 100644 src/absl/strings/cord_analysis.h create mode 100644 src/absl/strings/cord_buffer.cc create mode 100644 src/absl/strings/cord_buffer.h create mode 100644 src/absl/strings/escaping.cc create mode 100644 src/absl/strings/escaping.h create mode 100644 src/absl/strings/internal/char_map.h create mode 100644 src/absl/strings/internal/charconv_bigint.cc create mode 100644 src/absl/strings/internal/charconv_bigint.h create mode 100644 src/absl/strings/internal/charconv_parse.cc create mode 100644 src/absl/strings/internal/charconv_parse.h create mode 100644 src/absl/strings/internal/cord_data_edge.h create mode 100644 src/absl/strings/internal/cord_internal.cc create mode 100644 src/absl/strings/internal/cord_internal.h create mode 100644 src/absl/strings/internal/cord_rep_btree.cc create mode 100644 src/absl/strings/internal/cord_rep_btree.h create mode 100644 src/absl/strings/internal/cord_rep_btree_navigator.cc create mode 100644 src/absl/strings/internal/cord_rep_btree_navigator.h create mode 100644 src/absl/strings/internal/cord_rep_btree_reader.cc create mode 100644 src/absl/strings/internal/cord_rep_btree_reader.h create mode 100644 src/absl/strings/internal/cord_rep_consume.cc create mode 100644 src/absl/strings/internal/cord_rep_consume.h create mode 100644 src/absl/strings/internal/cord_rep_crc.cc create mode 100644 src/absl/strings/internal/cord_rep_crc.h create mode 100644 src/absl/strings/internal/cord_rep_flat.h create mode 100644 src/absl/strings/internal/cord_rep_ring.cc create mode 100644 src/absl/strings/internal/cord_rep_ring.h create mode 100644 src/absl/strings/internal/cord_rep_ring_reader.h create mode 100644 src/absl/strings/internal/cordz_functions.cc create mode 100644 src/absl/strings/internal/cordz_functions.h create mode 100644 src/absl/strings/internal/cordz_handle.cc create mode 100644 src/absl/strings/internal/cordz_handle.h create mode 100644 src/absl/strings/internal/cordz_info.cc create mode 100644 src/absl/strings/internal/cordz_info.h create mode 100644 src/absl/strings/internal/cordz_sample_token.cc create mode 100644 src/absl/strings/internal/cordz_sample_token.h create mode 100644 src/absl/strings/internal/cordz_statistics.h create mode 100644 src/absl/strings/internal/cordz_update_scope.h create mode 100644 src/absl/strings/internal/cordz_update_tracker.h create mode 100644 src/absl/strings/internal/escaping.cc create mode 100644 src/absl/strings/internal/escaping.h create mode 100644 src/absl/strings/internal/memutil.cc create mode 100644 src/absl/strings/internal/memutil.h create mode 100644 src/absl/strings/internal/ostringstream.cc create mode 100644 src/absl/strings/internal/ostringstream.h create mode 100644 src/absl/strings/internal/pow10_helper.cc create mode 100644 src/absl/strings/internal/pow10_helper.h create mode 100644 src/absl/strings/internal/resize_uninitialized.h create mode 100644 src/absl/strings/internal/stl_type_traits.h create mode 100644 src/absl/strings/internal/str_format/arg.cc create mode 100644 src/absl/strings/internal/str_format/arg.h create mode 100644 src/absl/strings/internal/str_format/bind.cc create mode 100644 src/absl/strings/internal/str_format/bind.h create mode 100644 src/absl/strings/internal/str_format/checker.h create mode 100644 src/absl/strings/internal/str_format/extension.cc create mode 100644 src/absl/strings/internal/str_format/extension.h create mode 100644 src/absl/strings/internal/str_format/float_conversion.cc create mode 100644 src/absl/strings/internal/str_format/float_conversion.h create mode 100644 src/absl/strings/internal/str_format/output.cc create mode 100644 src/absl/strings/internal/str_format/output.h create mode 100644 src/absl/strings/internal/str_format/parser.cc create mode 100644 src/absl/strings/internal/str_format/parser.h create mode 100644 src/absl/strings/internal/str_join_internal.h create mode 100644 src/absl/strings/internal/str_split_internal.h create mode 100644 src/absl/strings/internal/string_constant.h create mode 100644 src/absl/strings/internal/utf8.cc create mode 100644 src/absl/strings/internal/utf8.h create mode 100644 src/absl/strings/match.cc create mode 100644 src/absl/strings/match.h create mode 100644 src/absl/strings/numbers.cc create mode 100644 src/absl/strings/numbers.h create mode 100644 src/absl/strings/str_cat.cc create mode 100644 src/absl/strings/str_cat.h create mode 100644 src/absl/strings/str_format.h create mode 100644 src/absl/strings/str_join.h create mode 100644 src/absl/strings/str_replace.cc create mode 100644 src/absl/strings/str_replace.h create mode 100644 src/absl/strings/str_split.cc create mode 100644 src/absl/strings/str_split.h create mode 100644 src/absl/strings/string_view.cc create mode 100644 src/absl/strings/string_view.h create mode 100644 src/absl/strings/strip.h create mode 100644 src/absl/strings/substitute.cc create mode 100644 src/absl/strings/substitute.h create mode 100644 src/absl/synchronization/barrier.cc create mode 100644 src/absl/synchronization/barrier.h create mode 100644 src/absl/synchronization/blocking_counter.cc create mode 100644 src/absl/synchronization/blocking_counter.h create mode 100644 src/absl/synchronization/internal/create_thread_identity.cc create mode 100644 src/absl/synchronization/internal/create_thread_identity.h create mode 100644 src/absl/synchronization/internal/futex.h create mode 100644 src/absl/synchronization/internal/graphcycles.cc create mode 100644 src/absl/synchronization/internal/graphcycles.h create mode 100644 src/absl/synchronization/internal/kernel_timeout.h create mode 100644 src/absl/synchronization/internal/per_thread_sem.cc create mode 100644 src/absl/synchronization/internal/per_thread_sem.h create mode 100644 src/absl/synchronization/internal/thread_pool.h create mode 100644 src/absl/synchronization/internal/waiter.cc create mode 100644 src/absl/synchronization/internal/waiter.h create mode 100644 src/absl/synchronization/mutex.cc create mode 100644 src/absl/synchronization/mutex.h create mode 100644 src/absl/synchronization/notification.cc create mode 100644 src/absl/synchronization/notification.h create mode 100644 src/absl/time/civil_time.cc create mode 100644 src/absl/time/civil_time.h create mode 100644 src/absl/time/clock.cc create mode 100644 src/absl/time/clock.h create mode 100644 src/absl/time/duration.cc create mode 100644 src/absl/time/format.cc create mode 100644 src/absl/time/internal/cctz/include/cctz/civil_time.h create mode 100644 src/absl/time/internal/cctz/include/cctz/civil_time_detail.h create mode 100644 src/absl/time/internal/cctz/include/cctz/time_zone.h create mode 100644 src/absl/time/internal/cctz/include/cctz/zone_info_source.h create mode 100644 src/absl/time/internal/cctz/src/civil_time_detail.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_fixed.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_fixed.h create mode 100644 src/absl/time/internal/cctz/src/time_zone_format.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_if.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_if.h create mode 100644 src/absl/time/internal/cctz/src/time_zone_impl.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_impl.h create mode 100644 src/absl/time/internal/cctz/src/time_zone_info.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_info.h create mode 100644 src/absl/time/internal/cctz/src/time_zone_libc.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_libc.h create mode 100644 src/absl/time/internal/cctz/src/time_zone_lookup.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_posix.cc create mode 100644 src/absl/time/internal/cctz/src/time_zone_posix.h create mode 100644 src/absl/time/internal/cctz/src/tzfile.h create mode 100644 src/absl/time/internal/cctz/src/zone_info_source.cc create mode 100644 src/absl/time/internal/get_current_time_chrono.inc create mode 100644 src/absl/time/internal/get_current_time_posix.inc create mode 100644 src/absl/time/internal/zoneinfo.inc create mode 100644 src/absl/time/time.cc create mode 100644 src/absl/time/time.h create mode 100644 src/absl/types/any.h create mode 100644 src/absl/types/bad_any_cast.cc create mode 100644 src/absl/types/bad_any_cast.h create mode 100644 src/absl/types/bad_optional_access.cc create mode 100644 src/absl/types/bad_optional_access.h create mode 100644 src/absl/types/bad_variant_access.cc create mode 100644 src/absl/types/bad_variant_access.h create mode 100644 src/absl/types/compare.h create mode 100644 src/absl/types/internal/conformance_aliases.h create mode 100644 src/absl/types/internal/conformance_archetype.h create mode 100644 src/absl/types/internal/conformance_profile.h create mode 100644 src/absl/types/internal/conformance_testing_helpers.h create mode 100644 src/absl/types/internal/optional.h create mode 100644 src/absl/types/internal/parentheses.h create mode 100644 src/absl/types/internal/span.h create mode 100644 src/absl/types/internal/transform_args.h create mode 100644 src/absl/types/internal/variant.h create mode 100644 src/absl/types/optional.h create mode 100644 src/absl/types/span.h create mode 100644 src/absl/types/variant.h create mode 100644 src/absl/utility/utility.h create mode 100644 src/cpp-compat.cpp create mode 100644 src/cpp-compat.h create mode 100644 src/geography-operator.h create mode 100644 src/geography.h create mode 100644 src/init.cpp create mode 100644 src/s2-accessors.cpp create mode 100644 src/s2-bounds.cpp create mode 100644 src/s2-cell-union.cpp create mode 100644 src/s2-cell.cpp create mode 100644 src/s2-constructors-formatters.cpp create mode 100644 src/s2-geography.cpp create mode 100644 src/s2-lnglat.cpp create mode 100644 src/s2-matrix.cpp create mode 100644 src/s2-options.h create mode 100644 src/s2-predicates.cpp create mode 100644 src/s2-transformers.cpp create mode 100644 src/s2/_fp_contract_off.h create mode 100644 src/s2/base/casts.h create mode 100644 src/s2/base/commandlineflags.h create mode 100644 src/s2/base/integral_types.h create mode 100644 src/s2/base/log_severity.h create mode 100644 src/s2/base/logging.h create mode 100644 src/s2/base/mutex.h create mode 100644 src/s2/base/port.h create mode 100644 src/s2/base/spinlock.h create mode 100644 src/s2/base/stringprintf.cc create mode 100644 src/s2/base/stringprintf.h create mode 100644 src/s2/base/strtoint.cc create mode 100644 src/s2/base/strtoint.h create mode 100644 src/s2/base/timer.h create mode 100644 src/s2/encoded_s2cell_id_vector.cc create mode 100644 src/s2/encoded_s2cell_id_vector.h create mode 100644 src/s2/encoded_s2point_vector.cc create mode 100644 src/s2/encoded_s2point_vector.h create mode 100644 src/s2/encoded_s2shape_index.cc create mode 100644 src/s2/encoded_s2shape_index.h create mode 100644 src/s2/encoded_string_vector.cc create mode 100644 src/s2/encoded_string_vector.h create mode 100644 src/s2/encoded_uint_vector.h create mode 100644 src/s2/id_set_lexicon.cc create mode 100644 src/s2/id_set_lexicon.h create mode 100644 src/s2/mutable_s2shape_index.cc create mode 100644 src/s2/mutable_s2shape_index.h create mode 100644 src/s2/r1interval.h create mode 100644 src/s2/r2.h create mode 100644 src/s2/r2rect.cc create mode 100644 src/s2/r2rect.h create mode 100644 src/s2/s1angle.cc create mode 100644 src/s2/s1angle.h create mode 100644 src/s2/s1chord_angle.cc create mode 100644 src/s2/s1chord_angle.h create mode 100644 src/s2/s1interval.cc create mode 100644 src/s2/s1interval.h create mode 100644 src/s2/s2boolean_operation.cc create mode 100644 src/s2/s2boolean_operation.h create mode 100644 src/s2/s2builder.cc create mode 100644 src/s2/s2builder.h create mode 100644 src/s2/s2builder_graph.cc create mode 100644 src/s2/s2builder_graph.h create mode 100644 src/s2/s2builder_layer.h create mode 100644 src/s2/s2builderutil_closed_set_normalizer.cc create mode 100644 src/s2/s2builderutil_closed_set_normalizer.h create mode 100644 src/s2/s2builderutil_find_polygon_degeneracies.cc create mode 100644 src/s2/s2builderutil_find_polygon_degeneracies.h create mode 100644 src/s2/s2builderutil_graph_shape.h create mode 100644 src/s2/s2builderutil_lax_polygon_layer.cc create mode 100644 src/s2/s2builderutil_lax_polygon_layer.h create mode 100644 src/s2/s2builderutil_s2point_vector_layer.cc create mode 100644 src/s2/s2builderutil_s2point_vector_layer.h create mode 100644 src/s2/s2builderutil_s2polygon_layer.cc create mode 100644 src/s2/s2builderutil_s2polygon_layer.h create mode 100644 src/s2/s2builderutil_s2polyline_layer.cc create mode 100644 src/s2/s2builderutil_s2polyline_layer.h create mode 100644 src/s2/s2builderutil_s2polyline_vector_layer.cc create mode 100644 src/s2/s2builderutil_s2polyline_vector_layer.h create mode 100644 src/s2/s2builderutil_snap_functions.cc create mode 100644 src/s2/s2builderutil_snap_functions.h create mode 100644 src/s2/s2builderutil_testing.cc create mode 100644 src/s2/s2builderutil_testing.h create mode 100644 src/s2/s2cap.cc create mode 100644 src/s2/s2cap.h create mode 100644 src/s2/s2cell.cc create mode 100644 src/s2/s2cell.h create mode 100644 src/s2/s2cell_id.cc create mode 100644 src/s2/s2cell_id.h create mode 100644 src/s2/s2cell_index.cc create mode 100644 src/s2/s2cell_index.h create mode 100644 src/s2/s2cell_union.cc create mode 100644 src/s2/s2cell_union.h create mode 100644 src/s2/s2centroids.cc create mode 100644 src/s2/s2centroids.h create mode 100644 src/s2/s2closest_cell_query.cc create mode 100644 src/s2/s2closest_cell_query.h create mode 100644 src/s2/s2closest_cell_query_base.h create mode 100644 src/s2/s2closest_edge_query.cc create mode 100644 src/s2/s2closest_edge_query.h create mode 100644 src/s2/s2closest_edge_query_base.h create mode 100644 src/s2/s2closest_edge_query_testing.h create mode 100644 src/s2/s2closest_point_query.cc create mode 100644 src/s2/s2closest_point_query.h create mode 100644 src/s2/s2closest_point_query_base.h create mode 100644 src/s2/s2contains_point_query.h create mode 100644 src/s2/s2contains_vertex_query.cc create mode 100644 src/s2/s2contains_vertex_query.h create mode 100644 src/s2/s2convex_hull_query.cc create mode 100644 src/s2/s2convex_hull_query.h create mode 100644 src/s2/s2coords.cc create mode 100644 src/s2/s2coords.h create mode 100644 src/s2/s2coords_internal.h create mode 100644 src/s2/s2crossing_edge_query.cc create mode 100644 src/s2/s2crossing_edge_query.h create mode 100644 src/s2/s2debug.cc create mode 100644 src/s2/s2debug.h create mode 100644 src/s2/s2distance_target.h create mode 100644 src/s2/s2earth.cc create mode 100644 src/s2/s2earth.h create mode 100644 src/s2/s2edge_clipping.cc create mode 100644 src/s2/s2edge_clipping.h create mode 100644 src/s2/s2edge_crosser.cc create mode 100644 src/s2/s2edge_crosser.h create mode 100644 src/s2/s2edge_crossings.cc create mode 100644 src/s2/s2edge_crossings.h create mode 100644 src/s2/s2edge_crossings_internal.h create mode 100644 src/s2/s2edge_distances.cc create mode 100644 src/s2/s2edge_distances.h create mode 100644 src/s2/s2edge_tessellator.cc create mode 100644 src/s2/s2edge_tessellator.h create mode 100644 src/s2/s2edge_vector_shape.h create mode 100644 src/s2/s2error.cc create mode 100644 src/s2/s2error.h create mode 100644 src/s2/s2furthest_edge_query.cc create mode 100644 src/s2/s2furthest_edge_query.h create mode 100644 src/s2/s2latlng.cc create mode 100644 src/s2/s2latlng.h create mode 100644 src/s2/s2latlng_rect.cc create mode 100644 src/s2/s2latlng_rect.h create mode 100644 src/s2/s2latlng_rect_bounder.cc create mode 100644 src/s2/s2latlng_rect_bounder.h create mode 100644 src/s2/s2lax_loop_shape.cc create mode 100644 src/s2/s2lax_loop_shape.h create mode 100644 src/s2/s2lax_polygon_shape.cc create mode 100644 src/s2/s2lax_polygon_shape.h create mode 100644 src/s2/s2lax_polyline_shape.cc create mode 100644 src/s2/s2lax_polyline_shape.h create mode 100644 src/s2/s2loop.cc create mode 100644 src/s2/s2loop.h create mode 100644 src/s2/s2loop_measures.cc create mode 100644 src/s2/s2loop_measures.h create mode 100644 src/s2/s2max_distance_targets.cc create mode 100644 src/s2/s2max_distance_targets.h create mode 100644 src/s2/s2measures.cc create mode 100644 src/s2/s2measures.h create mode 100644 src/s2/s2metrics.cc create mode 100644 src/s2/s2metrics.h create mode 100644 src/s2/s2min_distance_targets.cc create mode 100644 src/s2/s2min_distance_targets.h create mode 100644 src/s2/s2padded_cell.cc create mode 100644 src/s2/s2padded_cell.h create mode 100644 src/s2/s2point.h create mode 100644 src/s2/s2point_compression.cc create mode 100644 src/s2/s2point_compression.h create mode 100644 src/s2/s2point_index.h create mode 100644 src/s2/s2point_region.cc create mode 100644 src/s2/s2point_region.h create mode 100644 src/s2/s2point_span.h create mode 100644 src/s2/s2point_vector_shape.h create mode 100644 src/s2/s2pointutil.cc create mode 100644 src/s2/s2pointutil.h create mode 100644 src/s2/s2polygon.cc create mode 100644 src/s2/s2polygon.h create mode 100644 src/s2/s2polyline.cc create mode 100644 src/s2/s2polyline.h create mode 100644 src/s2/s2polyline_alignment.cc create mode 100644 src/s2/s2polyline_alignment.h create mode 100644 src/s2/s2polyline_alignment_internal.h create mode 100644 src/s2/s2polyline_measures.cc create mode 100644 src/s2/s2polyline_measures.h create mode 100644 src/s2/s2polyline_simplifier.cc create mode 100644 src/s2/s2polyline_simplifier.h create mode 100644 src/s2/s2predicates.cc create mode 100644 src/s2/s2predicates.h create mode 100644 src/s2/s2predicates_internal.h create mode 100644 src/s2/s2projections.cc create mode 100644 src/s2/s2projections.h create mode 100644 src/s2/s2r2rect.cc create mode 100644 src/s2/s2r2rect.h create mode 100644 src/s2/s2region.cc create mode 100644 src/s2/s2region.h create mode 100644 src/s2/s2region_coverer.cc create mode 100644 src/s2/s2region_coverer.h create mode 100644 src/s2/s2region_intersection.cc create mode 100644 src/s2/s2region_intersection.h create mode 100644 src/s2/s2region_term_indexer.cc create mode 100644 src/s2/s2region_term_indexer.h create mode 100644 src/s2/s2region_union.cc create mode 100644 src/s2/s2region_union.h create mode 100644 src/s2/s2shape.h create mode 100644 src/s2/s2shape_index.cc create mode 100644 src/s2/s2shape_index.h create mode 100644 src/s2/s2shape_index_buffered_region.cc create mode 100644 src/s2/s2shape_index_buffered_region.h create mode 100644 src/s2/s2shape_index_measures.cc create mode 100644 src/s2/s2shape_index_measures.h create mode 100644 src/s2/s2shape_index_region.h create mode 100644 src/s2/s2shape_measures.cc create mode 100644 src/s2/s2shape_measures.h create mode 100644 src/s2/s2shapeutil_build_polygon_boundaries.cc create mode 100644 src/s2/s2shapeutil_build_polygon_boundaries.h create mode 100644 src/s2/s2shapeutil_coding.cc create mode 100644 src/s2/s2shapeutil_coding.h create mode 100644 src/s2/s2shapeutil_contains_brute_force.cc create mode 100644 src/s2/s2shapeutil_contains_brute_force.h create mode 100644 src/s2/s2shapeutil_count_edges.h create mode 100644 src/s2/s2shapeutil_edge_iterator.cc create mode 100644 src/s2/s2shapeutil_edge_iterator.h create mode 100644 src/s2/s2shapeutil_get_reference_point.cc create mode 100644 src/s2/s2shapeutil_get_reference_point.h create mode 100644 src/s2/s2shapeutil_range_iterator.cc create mode 100644 src/s2/s2shapeutil_range_iterator.h create mode 100644 src/s2/s2shapeutil_shape_edge.h create mode 100644 src/s2/s2shapeutil_shape_edge_id.h create mode 100644 src/s2/s2shapeutil_testing.h create mode 100644 src/s2/s2shapeutil_visit_crossing_edge_pairs.cc create mode 100644 src/s2/s2shapeutil_visit_crossing_edge_pairs.h create mode 100644 src/s2/s2testing.cc create mode 100644 src/s2/s2testing.h create mode 100644 src/s2/s2text_format.cc create mode 100644 src/s2/s2text_format.h create mode 100644 src/s2/s2wedge_relations.cc create mode 100644 src/s2/s2wedge_relations.h create mode 100644 src/s2/sequence_lexicon.h create mode 100644 src/s2/strings/ostringstream.cc create mode 100644 src/s2/strings/ostringstream.h create mode 100644 src/s2/strings/serialize.cc create mode 100644 src/s2/strings/serialize.h create mode 100644 src/s2/util/bits/bit-interleave.cc create mode 100644 src/s2/util/bits/bit-interleave.h create mode 100644 src/s2/util/bits/bits.cc create mode 100644 src/s2/util/bits/bits.h create mode 100644 src/s2/util/coding/coder.cc create mode 100644 src/s2/util/coding/coder.h create mode 100644 src/s2/util/coding/nth-derivative.h create mode 100644 src/s2/util/coding/transforms.h create mode 100644 src/s2/util/coding/varint.cc create mode 100644 src/s2/util/coding/varint.h create mode 100644 src/s2/util/endian/endian.h create mode 100644 src/s2/util/gtl/btree.h create mode 100644 src/s2/util/gtl/btree_container.h create mode 100644 src/s2/util/gtl/btree_map.h create mode 100644 src/s2/util/gtl/btree_set.h create mode 100644 src/s2/util/gtl/compact_array.h create mode 100644 src/s2/util/gtl/container_logging.h create mode 100644 src/s2/util/gtl/dense_hash_set.h create mode 100644 src/s2/util/gtl/densehashtable.h create mode 100644 src/s2/util/gtl/hashtable_common.h create mode 100644 src/s2/util/gtl/layout.h create mode 100644 src/s2/util/gtl/legacy_random_shuffle.h create mode 100644 src/s2/util/hash/mix.h create mode 100644 src/s2/util/math/exactfloat/exactfloat.cc create mode 100644 src/s2/util/math/exactfloat/exactfloat.h create mode 100644 src/s2/util/math/mathutil.cc create mode 100644 src/s2/util/math/mathutil.h create mode 100644 src/s2/util/math/matrix3x3.h create mode 100644 src/s2/util/math/vector.h create mode 100644 src/s2/util/math/vector3_hash.h create mode 100644 src/s2/util/units/length-units.cc create mode 100644 src/s2/util/units/length-units.h create mode 100644 src/s2/util/units/physical-units.h create mode 100644 src/s2/value_lexicon.h create mode 100644 src/s2geography.h create mode 100644 src/s2geography/accessors-geog.cc create mode 100644 src/s2geography/accessors-geog.h create mode 100644 src/s2geography/accessors.cc create mode 100644 src/s2geography/accessors.h create mode 100644 src/s2geography/aggregator.h create mode 100644 src/s2geography/build.cc create mode 100644 src/s2geography/build.h create mode 100644 src/s2geography/constructor.h create mode 100644 src/s2geography/coverings.cc create mode 100644 src/s2geography/coverings.h create mode 100644 src/s2geography/distance.cc create mode 100644 src/s2geography/distance.h create mode 100644 src/s2geography/geoarrow-imports.h create mode 100644 src/s2geography/geography.cc create mode 100644 src/s2geography/geography.h create mode 100644 src/s2geography/index.h create mode 100644 src/s2geography/linear-referencing.cc create mode 100644 src/s2geography/linear-referencing.h create mode 100644 src/s2geography/predicates.cc create mode 100644 src/s2geography/predicates.h create mode 100644 src/tests/main.c create mode 100644 src/tests/soname.h create mode 100644 src/wk-impl.c create mode 100644 tests/area.R create mode 100644 tests/area.Rout create mode 100644 tests/testthat.R create mode 100644 tests/testthat/test-data.R create mode 100644 tests/testthat/test-plot.R create mode 100644 tests/testthat/test-s2-accessors.R create mode 100644 tests/testthat/test-s2-bounds.R create mode 100644 tests/testthat/test-s2-cell-union.R create mode 100644 tests/testthat/test-s2-cell.R create mode 100644 tests/testthat/test-s2-constructors-formatters.R create mode 100644 tests/testthat/test-s2-earth.R create mode 100644 tests/testthat/test-s2-geography.R create mode 100644 tests/testthat/test-s2-lnglat.R create mode 100644 tests/testthat/test-s2-matrix.R create mode 100644 tests/testthat/test-s2-options.R create mode 100644 tests/testthat/test-s2-point.R create mode 100644 tests/testthat/test-s2-predicates.R create mode 100644 tests/testthat/test-s2-transformers.R create mode 100644 tests/testthat/test-utils.R create mode 100644 tests/testthat/test-vctrs.R create mode 100644 tests/testthat/test-wk-utils.R create mode 100644 tools/version.c create mode 100644 tools/winlibs.R diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 0000000..8fcdf91 --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,46 @@ +Package: s2 +Title: Spherical Geometry Operators Using the S2 Geometry Library +Version: 1.1.4 +Authors@R: c( + person(given = "Dewey", + family = "Dunnington", + role = c("aut"), + email = "dewey@fishandwhistle.net", + comment = c(ORCID = "0000-0002-9415-4582")), + person(given = "Edzer", + family = "Pebesma", + role = c("aut", "cre"), + email = "edzer.pebesma@uni-muenster.de", + comment = c(ORCID = "0000-0001-8049-7069")), + person("Ege", "Rubak", email="rubak@math.aau.dk", role = c("aut")), + person("Jeroen", "Ooms", , "jeroen.ooms@stat.ucla.edu", role = "ctb", comment = "configure script"), + person(family = "Google, Inc.", role = "cph", comment = "Original s2geometry.io source code") + ) +Description: Provides R bindings for Google's s2 library for geometric calculations on + the sphere. High-performance constructors and exporters provide high compatibility + with existing spatial packages, transformers construct new geometries from existing + geometries, predicates provide a means to select geometries based on spatial + relationships, and accessors extract information about geometries. +License: Apache License (== 2.0) +Encoding: UTF-8 +LazyData: true +RoxygenNote: 7.2.3 +SystemRequirements: OpenSSL >= 1.0.1 +LinkingTo: Rcpp, wk +Imports: Rcpp, wk (>= 0.6.0) +Suggests: bit64, testthat (>= 3.0.0), vctrs +URL: https://r-spatial.github.io/s2/, https://github.com/r-spatial/s2, + https://s2geometry.io/ +BugReports: https://github.com/r-spatial/s2/issues +Depends: R (>= 3.0.0) +Config/testthat/edition: 3 +NeedsCompilation: yes +Packaged: 2023-05-16 15:01:01 UTC; edzer +Author: Dewey Dunnington [aut] (), + Edzer Pebesma [aut, cre] (), + Ege Rubak [aut], + Jeroen Ooms [ctb] (configure script), + Google, Inc. [cph] (Original s2geometry.io source code) +Maintainer: Edzer Pebesma +Repository: CRAN +Date/Publication: 2023-05-17 10:40:02 UTC diff --git a/MD5 b/MD5 new file mode 100644 index 0000000..3b3561d --- /dev/null +++ b/MD5 @@ -0,0 +1,682 @@ +de87e4b860be3bfdddeff916c7c2b4e4 *DESCRIPTION +976861cda1588338545dc222e269f6a0 *NAMESPACE +5a92314e9624f924dbccc136c03e2c4b *NEWS.md +6e30cf9b0b3a9e4629b7b684856b4b07 *R/RcppExports.R +26fbc9df4408ccb165ddbd0cd8577b7e *R/data.R +4ca766e1349ea7baf27d4c8638fcb62b *R/plot.R +b783a368a460bcae5d174c575d863e59 *R/s2-accessors.R +b218944d59f977684342e52d49ab881f *R/s2-bounds.R +9b93004c163961946245e2ce97d2f27f *R/s2-cell-union.R +36a32862a21527afdf59effa34ba30bb *R/s2-cell.R +ed0cadf256cc791b85b392eef085c6f6 *R/s2-constructors-formatters.R +0968515e318a2c7a7372bddad1be6c23 *R/s2-earth.R +9cda6c070b015341cf92f57b9307a770 *R/s2-geography.R +cabc1e60f8ac15a3b463f4cd11143df9 *R/s2-lnglat.R +007cb436f26e501361a48b7782062eb6 *R/s2-matrix.R +e960477e2e4f889c85831e3d438bfe0f *R/s2-options.R +1bab90d590a0e66114c86739ba0d6ce4 *R/s2-package.R +994a3c25167362ce7175a580d75cc1f6 *R/s2-point.R +1618630f02d0c71a6884c2ef5359db7a *R/s2-predicates.R +e7dff1519bc26e311ae3e3f1bb894a69 *R/s2-transformers.R +a2bcb714226ffff00165255cd261dcc5 *R/utils.R +1b0b65e06a8b04d12df322094d6a16c2 *R/vctrs.R +542c90500e67cf705cba921a201f5a57 *R/wk-utils.R +cab4cb3dc48318d6b30fea904fea6bd8 *R/zzz.R +0d29d0b0030a40d9fb9632cf82c261c5 *README.md +439bf689fa27cf9affd0335332142165 *build/partial.rdb +cc6eebf14a3f516ec74168f3ea416fb2 *cleanup +cbac38784589bac27de4468ecf03fb13 *configure +d41d8cd98f00b204e9800998ecf8427e *configure.win +885346a0f57b92fd80b41b8afac11bad *data/s2_data_example_wkt.rda +e59a2d1d70e5a35ca7a5d9bc17ff9554 *data/s2_data_tbl_cities.rda +044840e4280d77984dc42feb2c3f387c *data/s2_data_tbl_countries.rda +94d2fee6187d451e94b028677e579b00 *data/s2_data_tbl_timezones.rda +d41d8cd98f00b204e9800998ecf8427e *inst/extdata/emptyfile +743a5630af573f46d1916e4810419a77 *man/as_s2_geography.Rd +ed01c59b91c1a9c3382d1f2ab67e60cc *man/figures/rc300.png +4a651a96987fab04091987a9343eda03 *man/s2-package.Rd +52eef06fb0b570954600e8e619fe9756 *man/s2_boundary.Rd +1ec1e77f9bf12466ef8c50a23beed9ca *man/s2_bounds_cap.Rd +ebcba3b3e746c4e15de4b309d0674c37 *man/s2_cell.Rd +bb77b7b6b94be80cd6cdf8d8f074d03b *man/s2_cell_is_valid.Rd +79be4874bfb6397f7e4ae40b18df426f *man/s2_cell_union.Rd +71b26a6c7cc913b2dd369afcd744a8f1 *man/s2_cell_union_normalize.Rd +d2590d0319581da0f2b40f438374e5ea *man/s2_closest_feature.Rd +673de08c721dbf5fe295811ceb1a6bd6 *man/s2_contains.Rd +7c5dbf9f18b14b1fa00b61ecd5efeccf *man/s2_data_example_wkt.Rd +addd614791ada510339317f68202d348 *man/s2_data_tbl_countries.Rd +d7a1ee72a69458a577921e02c2f4142f *man/s2_earth_radius_meters.Rd +c56c83fe4daade0fd891802084cc54fc *man/s2_geog_point.Rd +9676c31cb07674f1d6935a5b0462f3ca *man/s2_interpolate.Rd +32c6a0bb1280c25f7da7dc9781315d92 *man/s2_is_collection.Rd +85431ef0005937d9132048784c2ac887 *man/s2_lnglat.Rd +43d061044e95b05b97b7cdb01d392708 *man/s2_options.Rd +f9c29bc19098f5fc595e260341adeacb *man/s2_plot.Rd +6c76792603f4ce1efc64879d62b59517 *man/s2_point.Rd +de7b3d7e917213319c2adcbda8c5bf98 *man/wk_handle.s2_geography.Rd +563f58c84903d824a904660615e12c2a *src/Makevars.in +08be2245599b0ea2815f132127574b84 *src/Makevars.ucrt +7f0e39e59982689e148a8c55669548e5 *src/Makevars.win +08d872fb5bb0af47a972af7330e486ef *src/RcppExports.cpp +642c7be2ce45690aea290338fec0675b *src/absl/algorithm/algorithm.h +fd93de75d5eb59a37dc897d538c9e428 *src/absl/algorithm/container.h +8dda102d5b63125edd164fe345e55f9b *src/absl/base/attributes.h +daad8899b1d374f22c26521512c3a8bb *src/absl/base/call_once.h +26a0a864fbee53bce7754262cf239314 *src/absl/base/casts.h +43ea4eaaef94e0338dc008826e43fd7a *src/absl/base/config.h +f787189f34e273f4314344b53f5ccae9 *src/absl/base/const_init.h +033cd6a3c2de9bf80923f5054e2701e2 *src/absl/base/dynamic_annotations.h +376abfbcc035fb5ddd76fb513589a1e9 *src/absl/base/internal/atomic_hook.h +429261c728bd91fe9a974d2fcf8b8db6 *src/absl/base/internal/cycleclock.cc +b921282429acd16526077295c5ac6b6e *src/absl/base/internal/cycleclock.h +f5cec946747365781d14e7f395f3e908 *src/absl/base/internal/direct_mmap.h +3c6051b3b1895802b42fd070e0739173 *src/absl/base/internal/dynamic_annotations.h +f2a2f6524ea5494e7a484254ec99ce78 *src/absl/base/internal/endian.h +1ad6b95fe99ebc367d31ff7a9a8dd1fa *src/absl/base/internal/errno_saver.h +7cb685577c52ecbefba2c3f86f4247eb *src/absl/base/internal/fast_type_id.h +bf927fee390f1b62989522868b3104ec *src/absl/base/internal/hide_ptr.h +83cc5f6d80a853694ccd21d49215b38a *src/absl/base/internal/identity.h +ca95d2f7ac2c07926c2935004bab2077 *src/absl/base/internal/inline_variable.h +4139c861a8431f96daf9068fcddcb64f *src/absl/base/internal/invoke.h +f75b6817d994658a3311ae68b3f695f4 *src/absl/base/internal/low_level_alloc.cc +d6ce2c3d0d4abc8a9187e1c0d3917ef6 *src/absl/base/internal/low_level_alloc.h +99efd333efc4cc6a44a9abf4de89b2de *src/absl/base/internal/low_level_scheduling.h +cd26ae8fafb346728a1759160d46495e *src/absl/base/internal/per_thread_tls.h +c3e7ad34eacc65b541d0aa760ba1599d *src/absl/base/internal/prefetch.h +cff9e9f5e90b5e4455317dddbda47753 *src/absl/base/internal/pretty_function.h +cd8235040ec57fdc0cf6f85f6d8c752d *src/absl/base/internal/raw_logging.cc +3b3edfd7c70fae6821a0b911f42259a4 *src/absl/base/internal/raw_logging.h +bb74d44f7e47edcd1d865adb924db224 *src/absl/base/internal/scheduling_mode.h +fb9f075189d06957e1ae00ceefa34ee5 *src/absl/base/internal/scoped_set_env.cc +80dbd6581eea81b45ba532470673e777 *src/absl/base/internal/scoped_set_env.h +4e0f135a07c004bdecd6b414a7c6c546 *src/absl/base/internal/spinlock.cc +ac92c2ee2b026885a0fa47e95aeb0988 *src/absl/base/internal/spinlock.h +5e1d7bbdbd633f5f9e26bc6000d7bdc5 *src/absl/base/internal/spinlock_akaros.inc +184a057ae2d2273a6d02f7012585b854 *src/absl/base/internal/spinlock_linux.inc +63f56ff5eab205a2ba5a852dbd6276f2 *src/absl/base/internal/spinlock_posix.inc +3917a541890f75bcb371b26c11491bec *src/absl/base/internal/spinlock_wait.cc +3bc3b1f81150afa98a5b0abc10905f3e *src/absl/base/internal/spinlock_wait.h +d3c5fb1ed8db28e9fc573aa2939af254 *src/absl/base/internal/spinlock_win32.inc +f04df5e54b2977788e030fb2c44735c3 *src/absl/base/internal/strerror.cc +8c75ee873aa3e514edd1390f3056f89a *src/absl/base/internal/strerror.h +b3c6e089ed89e018ffdb93638014292c *src/absl/base/internal/sysinfo.cc +89920866f6792bd96338cb6f9587f3a9 *src/absl/base/internal/sysinfo.h +af1f8d88b67b54f7302728488ddb98a0 *src/absl/base/internal/thread_annotations.h +a720634e6b2aad1f7627838a08311902 *src/absl/base/internal/thread_identity.cc +675c5b31b96e0ebaa84e739ed8216b32 *src/absl/base/internal/thread_identity.h +94e874964762b519d4e59b796466116b *src/absl/base/internal/throw_delegate.cc +c1f9f8e9ee944f3b8a2640096b37f61f *src/absl/base/internal/throw_delegate.h +8c803550a22ccece7660ca6ad9a89560 *src/absl/base/internal/tsan_mutex_interface.h +c2045a4b16b8b7efce39c96a88adc6da *src/absl/base/internal/unaligned_access.h +51e2fc783ce386b7754b15fc92a92c81 *src/absl/base/internal/unscaledcycleclock.cc +17285df781bac882a78c1c4baa87d4a8 *src/absl/base/internal/unscaledcycleclock.h +6e85910a100a6c90893d108d2b80a7a1 *src/absl/base/log_severity.cc +f0fc3d187005a7cf23c54a12e04ab06c *src/absl/base/log_severity.h +9a7add8647ed73b494f794f18c11270d *src/absl/base/macros.h +7cb6ea6377947be6384172e8e4292fcf *src/absl/base/optimization.h +7553dbe117696c89a6214587a7664f9c *src/absl/base/options.h +30a55e8729c05984884801d5f5fa1422 *src/absl/base/policy_checks.h +c942a5d8341ce35b666c8a2e3625aed9 *src/absl/base/port.h +990163d6b22b150b08d9bec43b61bf8b *src/absl/base/thread_annotations.h +4903f7daa7a9b816419e6389bdffbf4a *src/absl/container/btree_map.h +d248b7efb25539492e4da9760d0f71ee *src/absl/container/btree_set.h +db720952112a075bb8ed68171b681ce1 *src/absl/container/fixed_array.h +1029770c28381ec983f929a950525aed *src/absl/container/flat_hash_map.h +31b4c898d148515c41d65eff48710843 *src/absl/container/flat_hash_set.h +72d3d49a360d5d05518b245d80da2e0b *src/absl/container/inlined_vector.h +0c9fc5d35bb8ce4a2d4b2aef0a065b40 *src/absl/container/internal/btree.h +471f4b576592fa97439abe4c1f4f5af6 *src/absl/container/internal/btree_container.h +04112e121678319b6f74e2c7c4bb2877 *src/absl/container/internal/common.h +013341274845cbce0b85a94bc2984b38 *src/absl/container/internal/compressed_tuple.h +38c85e29bfe2d83b08fb2ee63e84f428 *src/absl/container/internal/container_memory.h +a47304f7692364f874a92cce7526a13d *src/absl/container/internal/counting_allocator.h +619244bd2568402acdf114998c58fc52 *src/absl/container/internal/hash_function_defaults.h +2091e3185f7126b826ce099f50f5fdc9 *src/absl/container/internal/hash_policy_traits.h +ed69e77d87d7e4c2b42f5248904cd6d6 *src/absl/container/internal/hashtable_debug.h +85a576cfb12917cd7126d9e76e13309b *src/absl/container/internal/hashtable_debug_hooks.h +a9026aae4987bd08dd8def14e33574de *src/absl/container/internal/hashtablez_sampler.cc +e6a1d3c5965aea1bb0a84e5ce8d3ad08 *src/absl/container/internal/hashtablez_sampler.h +fcc5f2abdf1fd0edb18d122a846e84f0 *src/absl/container/internal/hashtablez_sampler_force_weak_definition.cc +0f420d68cbca4e9d9ca1274179d15e8e *src/absl/container/internal/inlined_vector.h +dde28d12969cb6df933b96f90b287506 *src/absl/container/internal/layout.h +a8db9c2e305ee7ddc082d6d1bcf74103 *src/absl/container/internal/node_slot_policy.h +7256093f3d9e6d880cf9b25295e0c61e *src/absl/container/internal/raw_hash_map.h +80426b9c7f99e24919cdfce7af00df15 *src/absl/container/internal/raw_hash_set.cc +7627e3cd5ecb10ed4ab6ba09a1ad7fee *src/absl/container/internal/raw_hash_set.h +b2c91b1fd5a05ea12fd76abd5e7401bc *src/absl/container/internal/tracked.h +dede177b23ded734f450d112907a7981 *src/absl/container/node_hash_map.h +fa098d67166a0c79cfb38f955d62aa7d *src/absl/container/node_hash_set.h +83dca33b15e6052405cf7737516cc6f1 *src/absl/debugging/failure_signal_handler.cc +33d4e12e60b432bafa7920d2b5fa67c0 *src/absl/debugging/failure_signal_handler.h +88511382784375519a3ef65a5ce247b9 *src/absl/debugging/internal/address_is_readable.cc +91ad1bd868606c791022ba6aa6ef4860 *src/absl/debugging/internal/address_is_readable.h +c8e0eb65e6f8010624c5e8b584780240 *src/absl/debugging/internal/demangle.cc +de5e466a575d551be06c658446b26ca9 *src/absl/debugging/internal/demangle.h +e54662f502407a088f04a6d51708f1e5 *src/absl/debugging/internal/elf_mem_image.cc +352cfa6b3dd2465cc293e610a0e38769 *src/absl/debugging/internal/elf_mem_image.h +dc8caa62e93f72fb1c5e4cc1fd6f0c34 *src/absl/debugging/internal/examine_stack.cc +19d1e4c50158c08334756c0fcd65728a *src/absl/debugging/internal/examine_stack.h +76431729cc82622bc53935063b049ef6 *src/absl/debugging/internal/stack_consumption.cc +817879abccfc61d31c97aa15e5b5a8df *src/absl/debugging/internal/stack_consumption.h +dc3e0ee0e29049fcd25242c4b14ca920 *src/absl/debugging/internal/stacktrace_aarch64-inl.inc +921fde95b6a08aa52d9cb628bb79c15d *src/absl/debugging/internal/stacktrace_arm-inl.inc +67da7c45a5fe837feb76fcfeff37f28b *src/absl/debugging/internal/stacktrace_config.h +d412bbe566473c31ffb571b5df159589 *src/absl/debugging/internal/stacktrace_emscripten-inl.inc +31136485c4ae4719146fe310d09794b3 *src/absl/debugging/internal/stacktrace_generic-inl.inc +4dddddc30c71242db2eff066287537fa *src/absl/debugging/internal/stacktrace_powerpc-inl.inc +8827fe270ce53a56049f0ea9d7d91429 *src/absl/debugging/internal/stacktrace_riscv-inl.inc +e7ced4e67444a66063312dc27d7bb17d *src/absl/debugging/internal/stacktrace_unimplemented-inl.inc +36cbcb6faf6816b2fd3db94ed61e077d *src/absl/debugging/internal/stacktrace_win32-inl.inc +4a9fddacc38ff421181282981ad11321 *src/absl/debugging/internal/stacktrace_x86-inl.inc +7528d1aa40e6a6645a8173fcbf8150cd *src/absl/debugging/internal/symbolize.h +66aa580ffd00da806719b6e4fee4fd67 *src/absl/debugging/internal/vdso_support.cc +32da4040ebc4e635623be36e861380d4 *src/absl/debugging/internal/vdso_support.h +944240b259867fbd920a992cb1b5610a *src/absl/debugging/leak_check.cc +803e646bd74bcc61527c082b05a41573 *src/absl/debugging/leak_check.h +fdb200b5ff89b0388299a36738915d29 *src/absl/debugging/stacktrace.cc +54920d74c95e9623f986ca3bf9a49c14 *src/absl/debugging/stacktrace.h +82338e576ae2cc4fef71e50f421164cc *src/absl/debugging/symbolize.cc +2a4f1e90fdc9a56d8812afd0f0291876 *src/absl/debugging/symbolize.h +e1ff8c65c67e769e4d929d70b4c315a4 *src/absl/debugging/symbolize_darwin.inc +f91083e80a5e31074987a0b78f5de558 *src/absl/debugging/symbolize_elf.inc +505df4d563b199a1accb368724eb8dd2 *src/absl/debugging/symbolize_emscripten.inc +37d1355403219402fa4d888e9be56fca *src/absl/debugging/symbolize_unimplemented.inc +59dde77656bded8e7a9c0141349f6230 *src/absl/debugging/symbolize_win32.inc +bde9e8a109a26a709ac7f6cd32d26335 *src/absl/functional/any_invocable.h +086310a4f0676fb86df40fcdac96d2a6 *src/absl/functional/bind_front.h +f3ea4b26c77278c38b2e7c8d88f0ba2f *src/absl/functional/function_ref.h +e62331c1f6ffc3458ada2d1d3dbff3bb *src/absl/functional/internal/any_invocable.h +ca707a4761ddef188eefa1c47a49305d *src/absl/functional/internal/front_binder.h +acc1da8f94634fb69d04d64af189f7d9 *src/absl/functional/internal/function_ref.h +c88a984158d3e1eaf700a703a747d9c0 *src/absl/memory/memory.h +a2462b42675608566733fc11ab1dbd40 *src/absl/meta/type_traits.h +7caad1d859f008e728d1a783e2007745 *src/absl/numeric/bits.h +fd2f0faf81574b2c86dd29356e120fac *src/absl/numeric/int128.cc +79cc004d6d6bf6bebf584022f33bec9e *src/absl/numeric/int128.h +2128cf980b91df6c0fc6c218d0497761 *src/absl/numeric/int128_have_intrinsic.inc +437ad46e0474b28cd231540fba4a8968 *src/absl/numeric/int128_no_intrinsic.inc +cda33272dc55c7d4e85c050048ac3b68 *src/absl/numeric/internal/bits.h +b71517976c0230c0f0be9e102124708b *src/absl/numeric/internal/representation.h +97c5bf0389ead2a8d9584b5516e03f30 *src/absl/profiling/internal/exponential_biased.cc +8dd473273bce3e970afd8a2592a00780 *src/absl/profiling/internal/exponential_biased.h +3c781cfebcbab764fa4e56e941e07a74 *src/absl/profiling/internal/periodic_sampler.cc +d815a430f4c9ff6f941a741742112bff *src/absl/profiling/internal/periodic_sampler.h +93e67b064bc20858ed6fba23748b7ddf *src/absl/profiling/internal/sample_recorder.h +d8fbe682d7f971ad41cf3585695e5b8c *src/absl/strings/ascii.cc +b1212449ec5b585102abd5fef8394f88 *src/absl/strings/ascii.h +d52529dfcbd429d31cf34475d9345029 *src/absl/strings/charconv.cc +e022adbe4ace51d7a2d3a065e30fc511 *src/absl/strings/charconv.h +8eebdbf778e886f261a8990232281e37 *src/absl/strings/cord.cc +6f1377e5033f06d48b32e92c09f97957 *src/absl/strings/cord.h +38bf276145e6768ce7d55bc72c51f4a6 *src/absl/strings/cord_analysis.cc +9eb388a8f95c0a8b6c629e139f5b19a7 *src/absl/strings/cord_analysis.h +fc2b8427acd714ecfa2c87c4d653df20 *src/absl/strings/cord_buffer.cc +d87e5ed66957627950977d971f80dcba *src/absl/strings/cord_buffer.h +3d3ee945b740992e7977948f64d37abf *src/absl/strings/escaping.cc +ca2211cce011d818358126d737a1b960 *src/absl/strings/escaping.h +9d688a5ba34606869bbb97318c6e3583 *src/absl/strings/internal/char_map.h +9ba44e5ca0ab3eaf2eea1c1d1135cbd2 *src/absl/strings/internal/charconv_bigint.cc +effc6ae35cf71076cb6d243984ef02db *src/absl/strings/internal/charconv_bigint.h +cda5aec99cde64d542c231aec6eecaeb *src/absl/strings/internal/charconv_parse.cc +bbb89ffd31b13599454ac66c7fca1021 *src/absl/strings/internal/charconv_parse.h +265c664ad73036c39cd42631e3d56ec2 *src/absl/strings/internal/cord_data_edge.h +b2aeb1b3985e3dd0e321c60975686d7c *src/absl/strings/internal/cord_internal.cc +89fc71fb8fc9cb06b7001af62a550023 *src/absl/strings/internal/cord_internal.h +d2efb216cd07c01bfa472b8adc633706 *src/absl/strings/internal/cord_rep_btree.cc +9a883463d6431cdc5a0689383884474a *src/absl/strings/internal/cord_rep_btree.h +2143a8c51257e8b4e4b165f155b38a1a *src/absl/strings/internal/cord_rep_btree_navigator.cc +6f6e44ad036456711251cd952f475a00 *src/absl/strings/internal/cord_rep_btree_navigator.h +e7ad5632b2aed15ce6f88246a8e40f6a *src/absl/strings/internal/cord_rep_btree_reader.cc +d2b0c3364d0d8e4a11288617889bfd60 *src/absl/strings/internal/cord_rep_btree_reader.h +62b2b3eaa89a7fa5bf2f790b61c80437 *src/absl/strings/internal/cord_rep_consume.cc +f500a141d4fbada9fe73c9bde8e9b997 *src/absl/strings/internal/cord_rep_consume.h +3c8f9400090931ecc6aed37d19b4214b *src/absl/strings/internal/cord_rep_crc.cc +8a1a2a0db3e9d4ab31dbb86f624277a3 *src/absl/strings/internal/cord_rep_crc.h +d701cf5c641280417b0290e8ff177dcd *src/absl/strings/internal/cord_rep_flat.h +160f554c0b0acb5c287ff2fa457f8b5a *src/absl/strings/internal/cord_rep_ring.cc +70ff93c8943bb54f4ec4b7bc03d8e298 *src/absl/strings/internal/cord_rep_ring.h +1b95eb768dc0a65c2aca391ce917168b *src/absl/strings/internal/cord_rep_ring_reader.h +2bb5021298c3f89948deb15c1cc14e67 *src/absl/strings/internal/cordz_functions.cc +7797f5c82bed0f2dd95d9a491164d0cd *src/absl/strings/internal/cordz_functions.h +d897bb95f3792816dae12cc99600f0ee *src/absl/strings/internal/cordz_handle.cc +2f4d12d83ff4611f3e8278a08a1f1dbb *src/absl/strings/internal/cordz_handle.h +46b23e9b9e81676d6633ab1edcbef807 *src/absl/strings/internal/cordz_info.cc +cceee7615f63027b2da6eead19bbab18 *src/absl/strings/internal/cordz_info.h +f20b57c507b149391110f0c4ec58e0eb *src/absl/strings/internal/cordz_sample_token.cc +8804ca3803da6893811636e9fe775b5a *src/absl/strings/internal/cordz_sample_token.h +7e6d750252301ed305689311fc447a6d *src/absl/strings/internal/cordz_statistics.h +406e4a93e3c6af34fe85e6e4ca4ef693 *src/absl/strings/internal/cordz_update_scope.h +4a710c85201ef57bdc282b2003186b29 *src/absl/strings/internal/cordz_update_tracker.h +3b55edcf191a2ece5a8f5071f3b186d2 *src/absl/strings/internal/escaping.cc +dd1f4cd55816cd3df569601375e19dd6 *src/absl/strings/internal/escaping.h +bbcc2e8ef020b2ce6fd2e17e7e019dd7 *src/absl/strings/internal/memutil.cc +73102cf47e4f94aee5dd43faa699eed5 *src/absl/strings/internal/memutil.h +2fd122662d4524b7e60268ed88de2699 *src/absl/strings/internal/ostringstream.cc +f4dd448efcf57c35b09e4ecf12ba595a *src/absl/strings/internal/ostringstream.h +0f79e5539614bead9e8b2899109414f2 *src/absl/strings/internal/pow10_helper.cc +a2710593664b501447140371f8f2910b *src/absl/strings/internal/pow10_helper.h +d41ed4ac8b5c0439686eee2a437ba24a *src/absl/strings/internal/resize_uninitialized.h +0b127a362be3ed6ce297a9bd6704b6f5 *src/absl/strings/internal/stl_type_traits.h +7e645ceaf6302cb953b278e7bd170b11 *src/absl/strings/internal/str_format/arg.cc +bbf7a17d739c9e9388ed9bbb30852ca2 *src/absl/strings/internal/str_format/arg.h +541807e23166b8458e4fe668064abff2 *src/absl/strings/internal/str_format/bind.cc +642c25c2a95404dbfbad403297c4f6e4 *src/absl/strings/internal/str_format/bind.h +25e4d30547e25840e7ade5d5361e4a6c *src/absl/strings/internal/str_format/checker.h +f1a8157f63bb446070dac767129701f9 *src/absl/strings/internal/str_format/extension.cc +7b166dea6c1b8683f113e9bfa498896c *src/absl/strings/internal/str_format/extension.h +602d5b81943933d209d9347e2138e031 *src/absl/strings/internal/str_format/float_conversion.cc +1775ec23d1a0b61c9c3cbc2786e20b9d *src/absl/strings/internal/str_format/float_conversion.h +38f00d24ace7d912f9410c77298f9450 *src/absl/strings/internal/str_format/output.cc +b9bb4e0ffdab9b7dd39fd2448df38651 *src/absl/strings/internal/str_format/output.h +bc2c67e0646b6442d7e594737371a059 *src/absl/strings/internal/str_format/parser.cc +da983b71caca5afea2879a1877269726 *src/absl/strings/internal/str_format/parser.h +3982f532a08d74ed1a024162c0752374 *src/absl/strings/internal/str_join_internal.h +31be88550fefe52ab3ede699f1e0ffa7 *src/absl/strings/internal/str_split_internal.h +e011f29d010463642a59a736caabdbd1 *src/absl/strings/internal/string_constant.h +58e100deab3a1a5d102f6767a106b023 *src/absl/strings/internal/utf8.cc +da238e6d26546310d03ce815a181f9c3 *src/absl/strings/internal/utf8.h +c26d8dc3c6998d1e4adb73b7b3e7307d *src/absl/strings/match.cc +d78b85f24f13f203df715033ebbedac4 *src/absl/strings/match.h +1ffbf9839fb7e742c2b9b5fc5d2ba8bd *src/absl/strings/numbers.cc +4ce4d257979a3fb096d3eee6c8285449 *src/absl/strings/numbers.h +4ebd95dbd026cacebb04407f77a40998 *src/absl/strings/str_cat.cc +499e4504c480d651cbbb45878ddd07d0 *src/absl/strings/str_cat.h +a0e1b0af9234c7d668a9542a31df2bc0 *src/absl/strings/str_format.h +305b931d297cecef7a7a5b1cbc92d183 *src/absl/strings/str_join.h +2b56176c8350695b37d0d810fe705486 *src/absl/strings/str_replace.cc +4cfe52299b13b659b0b79e926310859c *src/absl/strings/str_replace.h +0e0684d40f7ebcfa5e3ee516d602bc1b *src/absl/strings/str_split.cc +85eb91c7dbf8f799c2f6fdc862a9de57 *src/absl/strings/str_split.h +9513d8a3c60cffbd288b9f571af3599e *src/absl/strings/string_view.cc +bc6088f45dcc4f91be39442fe9b9c223 *src/absl/strings/string_view.h +202bdeadb019318d35a2141b0bf90ed8 *src/absl/strings/strip.h +317fe715d78ae47d1660f1a600d34fd8 *src/absl/strings/substitute.cc +35662cdc3431d806eafd35cf21ab276c *src/absl/strings/substitute.h +e8a0e5ed8343c44e8d414a72596dbde9 *src/absl/synchronization/barrier.cc +104730dc624a422f9e0610e357d1d319 *src/absl/synchronization/barrier.h +7adb87ccae08cfb4f5163c38043307b0 *src/absl/synchronization/blocking_counter.cc +76ca084891876db3ba604bb86c36e714 *src/absl/synchronization/blocking_counter.h +bce6fe21f3819cc963d73dcc7b7ce45c *src/absl/synchronization/internal/create_thread_identity.cc +51ffeadf7c798c4c32cd7ad26786b7c3 *src/absl/synchronization/internal/create_thread_identity.h +edbc4b6b4c303dd6a1c8856356bbdccf *src/absl/synchronization/internal/futex.h +39446c3969b401efba5b1b875a97ba17 *src/absl/synchronization/internal/graphcycles.cc +98204bc87786a909620b798d82b44258 *src/absl/synchronization/internal/graphcycles.h +0c51cca58de692a4dbe0f95750a20512 *src/absl/synchronization/internal/kernel_timeout.h +4fbf2a922ba2d618cc960c516c6858aa *src/absl/synchronization/internal/per_thread_sem.cc +07ff0b00a159d1e49e78f4953a375749 *src/absl/synchronization/internal/per_thread_sem.h +dcd3d301b476895c2de035ae9f4a9c2f *src/absl/synchronization/internal/thread_pool.h +cbf5c2ab41f486164b71e940d4df35c3 *src/absl/synchronization/internal/waiter.cc +6b034e180a573148b7512a25340e53fd *src/absl/synchronization/internal/waiter.h +4c313ee4f352a843fecccbd6a4047927 *src/absl/synchronization/mutex.cc +76e58005875696e6a2b00f38b22a9cad *src/absl/synchronization/mutex.h +18f40d74c189e4cb3bade5bbfac62eac *src/absl/synchronization/notification.cc +9e989de0e62bd8e3b804141f78ded6e9 *src/absl/synchronization/notification.h +43151664d8fcbd888e2b320ea6c59d4f *src/absl/time/civil_time.cc +21f77d9a8b12daee08647a79fa2f1c24 *src/absl/time/civil_time.h +5bec957f3a6069a1bbf1747096d51829 *src/absl/time/clock.cc +7669a9d4fb695b4bdf63f2199778b7da *src/absl/time/clock.h +f770f6ea46e1f6d5f99d40b058f76a23 *src/absl/time/duration.cc +1029367259d746995cfcc86300cb550e *src/absl/time/format.cc +153fe848b8d395acdc4d7188b6b1f7bb *src/absl/time/internal/cctz/include/cctz/civil_time.h +6061b7a2fb993133e2d77ab90882e4b3 *src/absl/time/internal/cctz/include/cctz/civil_time_detail.h +58a6845478b0a635b7474602a86154e9 *src/absl/time/internal/cctz/include/cctz/time_zone.h +6f945a703ababb99477bff55062287ec *src/absl/time/internal/cctz/include/cctz/zone_info_source.h +65ca6d96e925b084e0d56180aa5fe84d *src/absl/time/internal/cctz/src/civil_time_detail.cc +7d6876b726bdc87d68adb5a3be7523a6 *src/absl/time/internal/cctz/src/time_zone_fixed.cc +3afdc610576df58fccc41a7029f1591b *src/absl/time/internal/cctz/src/time_zone_fixed.h +37a70a5e5c9f72abb36cda2b9dfef5dc *src/absl/time/internal/cctz/src/time_zone_format.cc +977f9a0fc17a72093bc4c8a87784f632 *src/absl/time/internal/cctz/src/time_zone_if.cc +5aaf522801582f1c4e511bd5f47143cc *src/absl/time/internal/cctz/src/time_zone_if.h +b9933f8f7ea6ef25b5ca3ff1bf3e0ac9 *src/absl/time/internal/cctz/src/time_zone_impl.cc +5b006ce94d5395cbd204c8eb42aa673a *src/absl/time/internal/cctz/src/time_zone_impl.h +b920d1e53b3556ba81f65140c60c5517 *src/absl/time/internal/cctz/src/time_zone_info.cc +39446561973c7f453d351f8dd0bfc947 *src/absl/time/internal/cctz/src/time_zone_info.h +9be74d3b67a28a954fc5f9cb9721e173 *src/absl/time/internal/cctz/src/time_zone_libc.cc +6205d25a90cd16fe6fef686eac98a13b *src/absl/time/internal/cctz/src/time_zone_libc.h +baf6b2ca44a23c2ffc57a5185eb28c42 *src/absl/time/internal/cctz/src/time_zone_lookup.cc +e54d65914d1f72e412abe32cb94b12df *src/absl/time/internal/cctz/src/time_zone_posix.cc +21a06775679a372f33ac8918b0b03dfe *src/absl/time/internal/cctz/src/time_zone_posix.h +de6d3a441d96d5b0632441a939602bd3 *src/absl/time/internal/cctz/src/tzfile.h +09c17fe44b3b7d5eb61c51d642d227c9 *src/absl/time/internal/cctz/src/zone_info_source.cc +f510c27e928680c4df82fcd584b13620 *src/absl/time/internal/get_current_time_chrono.inc +921b6cfae31d6e15730d211d301f5bed *src/absl/time/internal/get_current_time_posix.inc +012254523fe4c7e1842f31b277e9bc60 *src/absl/time/internal/zoneinfo.inc +8893c1ef798bfa8c6a3421eafd82d0aa *src/absl/time/time.cc +f2405de29587b4932366a36e0ce8c607 *src/absl/time/time.h +c5e6e75a58fb6134a7bdad5ff6ef94b4 *src/absl/types/any.h +31416a5f0241128632dde299c016fe61 *src/absl/types/bad_any_cast.cc +e57304b355027637c47fa5b4138b5ef0 *src/absl/types/bad_any_cast.h +76faa97137f7f2f4eb70152889e11227 *src/absl/types/bad_optional_access.cc +ffd0f2debe86db173721761f28b64480 *src/absl/types/bad_optional_access.h +339cb16d4f7c125ef6f9975e9c70e2fd *src/absl/types/bad_variant_access.cc +63b5f63942914ccdaf91a52ae46b5b8f *src/absl/types/bad_variant_access.h +216ae502b816cc541789db0915e4dd76 *src/absl/types/compare.h +2466c511230ee6497dc26dd3633f7702 *src/absl/types/internal/conformance_aliases.h +aa319c2077664d2c39a6bd0813c79c18 *src/absl/types/internal/conformance_archetype.h +f5863e36a75e055aee57a18f967c5489 *src/absl/types/internal/conformance_profile.h +a87cad8cf13eaebb402020f8d7fe4119 *src/absl/types/internal/conformance_testing_helpers.h +0ceabff37c1e8943c78fc2a10db32e94 *src/absl/types/internal/optional.h +9d178054161b76df535a760be34d6213 *src/absl/types/internal/parentheses.h +27f0c5818e53bdc944c73938edf49a15 *src/absl/types/internal/span.h +599feda2b9b004573e161a9550054e99 *src/absl/types/internal/transform_args.h +637f1331d00d757439fcba667b80a4f5 *src/absl/types/internal/variant.h +29994a10008e74ff8963787848912fbc *src/absl/types/optional.h +6e8045dbd0797fcac5d8b9dbe5c6a322 *src/absl/types/span.h +1d24f87c9fb98d810977a981043d8201 *src/absl/types/variant.h +9b15fdb150cb72ea63d34d08fffd39c2 *src/absl/utility/utility.h +5f46d29bc94bcdfc28e2a4df442310de *src/cpp-compat.cpp +afabc65ce7a01abdb14efb1abccd7247 *src/cpp-compat.h +c20b3dc7b63f3bbf6af6827ac6557ca9 *src/geography-operator.h +023f7dbc8c3dd00c481507be51a9ca91 *src/geography.h +0365ec85c22784d83cce5b84d71d794a *src/init.cpp +62ed6c893b0bc0b75b67e9b02706a0b5 *src/s2-accessors.cpp +4cfaeb17ebda5876ac7110d5e15b6e87 *src/s2-bounds.cpp +10bfc12436ce2945f667824653c829d1 *src/s2-cell-union.cpp +16dc89505d02a555613082143e1d79a7 *src/s2-cell.cpp +cf59fd9682672c99319ba906455e5b32 *src/s2-constructors-formatters.cpp +19657182677a20a4e14bcd89e03f6f06 *src/s2-geography.cpp +e9f1b666c7a410cadaa4dfa34feba4b2 *src/s2-lnglat.cpp +db77ba68fab15ce99c37d6d50471cbbf *src/s2-matrix.cpp +96980fe0bed80e6566fbbd84bc8dcdf8 *src/s2-options.h +70c6d7bd00bb98b98a35f9ca8d77677d *src/s2-predicates.cpp +9c30c6461c446d323ae75d0b9f2395e5 *src/s2-transformers.cpp +8428235fc1165d0c32375e2e21a686fe *src/s2/_fp_contract_off.h +28e49579436bf834cd114aa6f01f396e *src/s2/base/casts.h +4c10c37f35c3ea32da84e18e427e0a8f *src/s2/base/commandlineflags.h +95d68a4bda8ac1ed1238c06999c7fe8f *src/s2/base/integral_types.h +188fa699db99f7b32bf12c8f5d49afab *src/s2/base/log_severity.h +e6796dc6fac7834af2337a6298f6b4f2 *src/s2/base/logging.h +322d31aa00d2046a0f35c7e373748985 *src/s2/base/mutex.h +bc87e8d47e0131c7f3b2e943823c79f8 *src/s2/base/port.h +7217ae2988c45fe3c657a6fbb5a01334 *src/s2/base/spinlock.h +4c6c57a3c08c66211d519f387ea8e972 *src/s2/base/stringprintf.cc +df6c98233d53cb881136f5f52d2c8012 *src/s2/base/stringprintf.h +c10c3c7ca58239c0915bacffc70e37e1 *src/s2/base/strtoint.cc +ac9edc4a5c5139604384d611a9d5686f *src/s2/base/strtoint.h +8a23b88128feaefecac82381870b7e1b *src/s2/base/timer.h +a85315a801220cea6aedb7545bed0675 *src/s2/encoded_s2cell_id_vector.cc +ae93520447e17b7a5ee570e973b14058 *src/s2/encoded_s2cell_id_vector.h +d7cc642275acd754dd38bc1db012beb8 *src/s2/encoded_s2point_vector.cc +d7870427a21d7a4288a82ef3d9002404 *src/s2/encoded_s2point_vector.h +1c9eda503a7610170bb7b20b221a8123 *src/s2/encoded_s2shape_index.cc +8c71134bbac741ab14ad58645c587efd *src/s2/encoded_s2shape_index.h +7ded14eeab5eba2a2cca20c71b837d1c *src/s2/encoded_string_vector.cc +2b5b150cb6a9356b9360ab4bed8fb9e0 *src/s2/encoded_string_vector.h +9e444256aa6a7b5e5c3eb73ba1708065 *src/s2/encoded_uint_vector.h +788f48e5bbfca22ed1e396d4bd675ad3 *src/s2/id_set_lexicon.cc +e7cbe5a6c88b9a286b17adb5e690286f *src/s2/id_set_lexicon.h +e77194370c1950bc155ee61708af57b0 *src/s2/mutable_s2shape_index.cc +f2106aebeef507adcbc699ee5bb2ae5b *src/s2/mutable_s2shape_index.h +a5fd0e84e01c56c79c50f208ae1b08b9 *src/s2/r1interval.h +e363bde392175774fc26dcde1c3b5076 *src/s2/r2.h +da84b9bfc2aeabd4f8ae0f84343623e7 *src/s2/r2rect.cc +4b12844b6a0327fbc6f0488b4fbea0b3 *src/s2/r2rect.h +e7ca7f23a7e8fe6f707e4169f1aadb77 *src/s2/s1angle.cc +0e9a83acf9613dc68174c6575afa373d *src/s2/s1angle.h +d9387a9af7477f6b82ec73d206cfd8d9 *src/s2/s1chord_angle.cc +7b2a01d9f67a03390a0f34b03a46fef8 *src/s2/s1chord_angle.h +34874873bee5edbf9af38cd7692ab3ae *src/s2/s1interval.cc +2a46bfae65f4a24dbbc0c8a7df478c4c *src/s2/s1interval.h +ad8719b6e9e1c836e9f4c59b216739c9 *src/s2/s2boolean_operation.cc +99423d6009cb68d7879793f9311e1bb7 *src/s2/s2boolean_operation.h +aead3947dd83dd0ed2d4becb7d14d426 *src/s2/s2builder.cc +5cf1544718ecb4046621e2b0b89a0222 *src/s2/s2builder.h +bbdb9da0943cf55e60e846d2b0e7187b *src/s2/s2builder_graph.cc +cc61ae8fff8370c728bf6de7513e06d9 *src/s2/s2builder_graph.h +0558b2bb40025b583974ac1a5d2b5857 *src/s2/s2builder_layer.h +485fed02d2c102ef03502c959735152c *src/s2/s2builderutil_closed_set_normalizer.cc +4b17df092dd21d0d5b22499a7a0d8d63 *src/s2/s2builderutil_closed_set_normalizer.h +128b04413654742d9655bc2b12dfeda7 *src/s2/s2builderutil_find_polygon_degeneracies.cc +dc965c407adeb3070db3510de3b1f0bd *src/s2/s2builderutil_find_polygon_degeneracies.h +59390c82ae51ffd8dc8a2154c7f5cc6b *src/s2/s2builderutil_graph_shape.h +85e926aef77aeccd2d0384001b3e7c09 *src/s2/s2builderutil_lax_polygon_layer.cc +8d156a2b07ddd94dd53ed45a086ddabd *src/s2/s2builderutil_lax_polygon_layer.h +6c0c3e9f73a2d693d25392a6f053cca9 *src/s2/s2builderutil_s2point_vector_layer.cc +5bcdb1842aff634eb86151c41aa92942 *src/s2/s2builderutil_s2point_vector_layer.h +338a73e56e2a0406bc308dcb7ae2a1db *src/s2/s2builderutil_s2polygon_layer.cc +d0f7d3975bf12ad12e5a453b1c664879 *src/s2/s2builderutil_s2polygon_layer.h +bc24648fc1ba139430b08df68cc45004 *src/s2/s2builderutil_s2polyline_layer.cc +0c78a6937a60f24a9896a4d411b68e90 *src/s2/s2builderutil_s2polyline_layer.h +9ed299b94f9fd05d3b228cbbb77a29e9 *src/s2/s2builderutil_s2polyline_vector_layer.cc +8177eb92bdd7082985e5e2cef2cbb57e *src/s2/s2builderutil_s2polyline_vector_layer.h +41c634fe2532fd5946349947743d9b0b *src/s2/s2builderutil_snap_functions.cc +14fae38b064b536a50fd99090bf2bb80 *src/s2/s2builderutil_snap_functions.h +0e7eb31b407e02950751a990c528833d *src/s2/s2builderutil_testing.cc +e91fca8f2ddf6c1826122f601803cf6f *src/s2/s2builderutil_testing.h +15661d854ecdbd9b5adb37f4fa38e7fd *src/s2/s2cap.cc +1aaf20b1794973131d85bbd6d16fc296 *src/s2/s2cap.h +d89508276ed169153d1e80d85989bfd5 *src/s2/s2cell.cc +cc050d60fc6b31479ad2cfc444aebc2b *src/s2/s2cell.h +df582edaa44ad77d7a5d8e9fb7c34262 *src/s2/s2cell_id.cc +2b7fcd907f3904fa8e8fedf12fd79303 *src/s2/s2cell_id.h +b77c66885dd9ce9decb058448a53bf2b *src/s2/s2cell_index.cc +c967db283c31cf3b04720da4aaf951be *src/s2/s2cell_index.h +2df8fa84fabd7e103ecbcf3dddec4ab7 *src/s2/s2cell_union.cc +b1877f966f026dafb27bd156d26d8194 *src/s2/s2cell_union.h +cf1a07e72197832f2b1302f7b3630f98 *src/s2/s2centroids.cc +b0e46b1f9b0302bf28993c83b1c29952 *src/s2/s2centroids.h +d23a0e7ec3560f1191df801a3ded25e4 *src/s2/s2closest_cell_query.cc +1022d523b50d2186b166ca68dc0deb11 *src/s2/s2closest_cell_query.h +1b857efbfb27e180b3cb9a554e64048d *src/s2/s2closest_cell_query_base.h +16334d3128469be0f498a5e1a6427659 *src/s2/s2closest_edge_query.cc +a239c16e1797acb32e845a6da24ca0b0 *src/s2/s2closest_edge_query.h +a4484a9f30e74392ad90ac037b3404d3 *src/s2/s2closest_edge_query_base.h +0974a6dabc5a7541eebf0e81656e522c *src/s2/s2closest_edge_query_testing.h +09e2f693ad6e491a4d50eb8b5d966840 *src/s2/s2closest_point_query.cc +e982b798e56156c65fb5ddf919a0520c *src/s2/s2closest_point_query.h +ffdeee92985536a16b5a6212547872ba *src/s2/s2closest_point_query_base.h +c5ac9b7327a1f2eb8c9b8683973bc48c *src/s2/s2contains_point_query.h +8cea937d5d1881cdb71d1287f5474cff *src/s2/s2contains_vertex_query.cc +0a9a1857a2e79884b600de88b69cefbe *src/s2/s2contains_vertex_query.h +06d65d3cca94ccb99beb7e33f5267e10 *src/s2/s2convex_hull_query.cc +7c2da76e9c9917194d48939ee919f088 *src/s2/s2convex_hull_query.h +effdbbf64bb7f235381201657e021ea6 *src/s2/s2coords.cc +e2a5abdcf611242bdf5cf0edf0eea5ad *src/s2/s2coords.h +28c4af3631469e232aab685be3e33122 *src/s2/s2coords_internal.h +48655b45fa96f0da0b234ca68fe4b54d *src/s2/s2crossing_edge_query.cc +28b59ab0651bf933d5271393e3313deb *src/s2/s2crossing_edge_query.h +cbeeb65a7261dc8430dcf6edc24deb94 *src/s2/s2debug.cc +12e9446c9a552088c70b7b3062c1ba5b *src/s2/s2debug.h +7298228fc7e23751181ad62e211a81b8 *src/s2/s2distance_target.h +a863fc668a334819762480cc0646c898 *src/s2/s2earth.cc +b90e02812e7d1afa32d15de3a0245dd8 *src/s2/s2earth.h +d58b020152efbe99f672b8952460d2ce *src/s2/s2edge_clipping.cc +cb59ea7179d978f41ecac9ff2ab101c5 *src/s2/s2edge_clipping.h +c5fd68f5fab9c30285a9d1995be3ddde *src/s2/s2edge_crosser.cc +91b536b7351c85048ac5f4fc8f9c36f3 *src/s2/s2edge_crosser.h +69bb6ff8072fe88be08b2e693071ea65 *src/s2/s2edge_crossings.cc +6de8f9979a94b2001e3134b4b10391a5 *src/s2/s2edge_crossings.h +482bff39a41ea67b1073b9cfdc11400b *src/s2/s2edge_crossings_internal.h +3bdbadd260d873672d7821d8a0f557ed *src/s2/s2edge_distances.cc +db89cee1d7d37be1b7f889cc92df8e94 *src/s2/s2edge_distances.h +75acbcdde0e718a3a990fa63c78c3423 *src/s2/s2edge_tessellator.cc +0f223cfa5b4b1153617bd64d47f68212 *src/s2/s2edge_tessellator.h +99195e00ed62fc8127a6de6305730930 *src/s2/s2edge_vector_shape.h +483c797be9c9eb4b9a9059577e548e7b *src/s2/s2error.cc +30c2e0bb485124026064b8a6e680c60a *src/s2/s2error.h +fe0c39101c8c58f45b75fc4c36261a82 *src/s2/s2furthest_edge_query.cc +9ab743cac7f27a70e2f49f6a8d659c49 *src/s2/s2furthest_edge_query.h +fe5f7037943d797d93f4d2be3d1119b0 *src/s2/s2latlng.cc +f86006f14f82fb5fb2dc61b217d79be0 *src/s2/s2latlng.h +e42ec23a4fdf8b6aea983db2ca3cc4d9 *src/s2/s2latlng_rect.cc +bd642eda609967162d94ac500632e454 *src/s2/s2latlng_rect.h +f8689b6a3eef9053601d310ca6146f5c *src/s2/s2latlng_rect_bounder.cc +486a11f20722a978644e99ec990516ad *src/s2/s2latlng_rect_bounder.h +6ea59dae602c6e87765068c7c4357baf *src/s2/s2lax_loop_shape.cc +6aaf1e13f42ac06794d6179a45992a12 *src/s2/s2lax_loop_shape.h +fc760f9c0ff8545929b4a1e0da1e6ff5 *src/s2/s2lax_polygon_shape.cc +35b22571a5ddbe1e7b91e6497b39e111 *src/s2/s2lax_polygon_shape.h +56c7b3c6c942d0cffe353b03ee605a8f *src/s2/s2lax_polyline_shape.cc +4d4f60c49166c8657258de45884c4087 *src/s2/s2lax_polyline_shape.h +818253e9f331b59caf71a9e18f7dfd12 *src/s2/s2loop.cc +da57f0c9e0f4c2cdf2599e0de097dbd9 *src/s2/s2loop.h +2f78e2f1e0911445a0ffe5e5b0fe014a *src/s2/s2loop_measures.cc +eef153a4e7bb190f620c250f78ac7c1c *src/s2/s2loop_measures.h +2e54cb10538dee0beadcfcf774875b64 *src/s2/s2max_distance_targets.cc +ad2641cab77de9fe057639d5afdf4edd *src/s2/s2max_distance_targets.h +66d7e4a5a3700d4b5a8c7e013fcfc370 *src/s2/s2measures.cc +b230afb1b144cfe2dfa5241d0d935cc1 *src/s2/s2measures.h +b70ef8989674552ca3d0e0c9fcb754a8 *src/s2/s2metrics.cc +c974a90f9d2ed8199af139ad33147aa4 *src/s2/s2metrics.h +582857ac635543b496cf93ee1bbe133b *src/s2/s2min_distance_targets.cc +49d8a47ed9a0f58dd97b2258b1764e77 *src/s2/s2min_distance_targets.h +94bd23bc709062647f0bdc993dfc7933 *src/s2/s2padded_cell.cc +b2841070cce458abff94905776f74eae *src/s2/s2padded_cell.h +af4bfede404056bb6ecc81a6ec4ed141 *src/s2/s2point.h +caf1d4962bb43ca5e3bfdc9d17640b49 *src/s2/s2point_compression.cc +0261a1c5b0ed5a8fec6b797ec53b5f30 *src/s2/s2point_compression.h +127b57a24cfa7ea2c78bc0fb7c9f446b *src/s2/s2point_index.h +7b88d7113b91cd8d5f0c1ad5bbacc7c4 *src/s2/s2point_region.cc +1c40d79d4102f56ec2301b62d2767a0e *src/s2/s2point_region.h +291c8ec7baef072cb29bc341fb3cb5c1 *src/s2/s2point_span.h +7d75a6945e15b9c78a713a59bd85f199 *src/s2/s2point_vector_shape.h +0475a05e50a35ca03b06e084178cd31a *src/s2/s2pointutil.cc +5d2100a54f4302ee75608d48d586fe3c *src/s2/s2pointutil.h +3272d4e15adf23b8b98b7fe50ca6e43a *src/s2/s2polygon.cc +dad5e0faba176fe983c613c3d6a60d07 *src/s2/s2polygon.h +12ef96f03b504ccf5c7523d15448b882 *src/s2/s2polyline.cc +16ea03d777619f1e59a47cbf83d53071 *src/s2/s2polyline.h +2914934120ec55346427958e5f73fb5e *src/s2/s2polyline_alignment.cc +c0f9b77a9439bc0c84d9dfe2dcbcc25b *src/s2/s2polyline_alignment.h +8a2d7dedff6ee98faf1edcc694a07a19 *src/s2/s2polyline_alignment_internal.h +c206c2fa52e4a5588b1024f45c63595b *src/s2/s2polyline_measures.cc +3b78b40b711bc148a7cc8f01f044d9a7 *src/s2/s2polyline_measures.h +eab00b14b7643e320f8044521eaa16e4 *src/s2/s2polyline_simplifier.cc +a12b87a494ef6e98dd8b19091ebf8cd8 *src/s2/s2polyline_simplifier.h +ff7549a0d6195b34b24867fff4cc6d06 *src/s2/s2predicates.cc +3e580e8e5db3954a6918420df5d50630 *src/s2/s2predicates.h +7dd5ac042f3403c26f10e8bc6bfcc8ac *src/s2/s2predicates_internal.h +7478e1714d9dcaf520159458d10e383b *src/s2/s2projections.cc +fa7c158fb73fced84c86e73bc40a4215 *src/s2/s2projections.h +8baa87c45aa11148a5207f367237e753 *src/s2/s2r2rect.cc +eef1eb7446a79b5b6430af8eede7a698 *src/s2/s2r2rect.h +57778c7c84ae75e67ed68e76f9026ec0 *src/s2/s2region.cc +2234c6d0d865cc61ca6959c01e0015e4 *src/s2/s2region.h +e12692516fc6b52c237e16cf16f11e19 *src/s2/s2region_coverer.cc +dc72c2322d4aa4d89edaeac8a99d3110 *src/s2/s2region_coverer.h +f622c31665d97c607e1cb5106c295959 *src/s2/s2region_intersection.cc +d146a90111cd2d2b33b1526d5d1eab9e *src/s2/s2region_intersection.h +4bedf29698cbce5a1d755b5d9f0e5312 *src/s2/s2region_term_indexer.cc +ba4411f3cbd9ffe23482ec22e2045b31 *src/s2/s2region_term_indexer.h +f8232c70a590ed11666c93181c9d7343 *src/s2/s2region_union.cc +7acd16e242bad3bf6f8651417f6940c2 *src/s2/s2region_union.h +9e0666e7c03402dbf71fec6da5aa9244 *src/s2/s2shape.h +25ac091ec0963d3216b86e86a050eae9 *src/s2/s2shape_index.cc +1b8ddf576bde4dc6647dcec8341a18a3 *src/s2/s2shape_index.h +906e1e16bc8aa9f5d4e99651a6cdf0fe *src/s2/s2shape_index_buffered_region.cc +2f6f557e792611c362fee453ca16eb88 *src/s2/s2shape_index_buffered_region.h +c0543285953743a9605aa3ef63ad6127 *src/s2/s2shape_index_measures.cc +1caf918a11c8bafc0835861870fbd0ea *src/s2/s2shape_index_measures.h +b095a57fa9ad2adfa3cc31f6b8d6fffd *src/s2/s2shape_index_region.h +23c94c388142e68594cbf29b677d2bbc *src/s2/s2shape_measures.cc +ce9d81e0539f9c3a136e07d12c46cb05 *src/s2/s2shape_measures.h +c5794119535ecf13d27f5ae34c58f8b0 *src/s2/s2shapeutil_build_polygon_boundaries.cc +36f49316d02ac4f5c1124d7a3365419f *src/s2/s2shapeutil_build_polygon_boundaries.h +2b6d1731185a9ff771d2f150cf6c66f2 *src/s2/s2shapeutil_coding.cc +3c7dde9ddf64c467729e08fb30741bbe *src/s2/s2shapeutil_coding.h +afd2720bf07ba0aa5f94dfe9ba0c4d3a *src/s2/s2shapeutil_contains_brute_force.cc +f6d89268f04abb12ccd63236d4362060 *src/s2/s2shapeutil_contains_brute_force.h +1344b756712c76ac8c390abdb5a5886e *src/s2/s2shapeutil_count_edges.h +a699c9e30b9f8eaea6b9308c099c2285 *src/s2/s2shapeutil_edge_iterator.cc +79c3c7d45f832a4b348116e5b9ec993d *src/s2/s2shapeutil_edge_iterator.h +ae2b2addb5cb94cd24c57c195e1b0b49 *src/s2/s2shapeutil_get_reference_point.cc +53160e1333206256c06775aceb56ff6e *src/s2/s2shapeutil_get_reference_point.h +6d1353bb32206db05048ee50a073c0e7 *src/s2/s2shapeutil_range_iterator.cc +d12c9628c73beb0f42bb462060facbe7 *src/s2/s2shapeutil_range_iterator.h +a249fc71604963665d21c311c0e1c391 *src/s2/s2shapeutil_shape_edge.h +bdf516e73992d5a15fdaa074596f8d6f *src/s2/s2shapeutil_shape_edge_id.h +007fda069d8144645da7f2c7462a8359 *src/s2/s2shapeutil_testing.h +2d43581ce7e9638ee9d4390016d422df *src/s2/s2shapeutil_visit_crossing_edge_pairs.cc +f21bfbc04ce2d1d819512176b45954bb *src/s2/s2shapeutil_visit_crossing_edge_pairs.h +489cf62e6b1d3e3ff20b67aa19975130 *src/s2/s2testing.cc +fd1b7e4602b2f79e66496c81232f9cb1 *src/s2/s2testing.h +b504a72745dbeab05a8f198831eebb7d *src/s2/s2text_format.cc +294d80e1f4a307f03d84e66ae11767a1 *src/s2/s2text_format.h +518f25738bd53f6439d3708f56a85435 *src/s2/s2wedge_relations.cc +3c1f74157144820e8e5008fdc433e018 *src/s2/s2wedge_relations.h +d3ffa54eb4cab5a70d23bee5cbc17b3e *src/s2/sequence_lexicon.h +9b6952846f960ce7c8f86d425a6cfc53 *src/s2/strings/ostringstream.cc +a7553d41936a6491b5e79bad88a676b9 *src/s2/strings/ostringstream.h +33d32d088678a9117a40a020df05e8fc *src/s2/strings/serialize.cc +ae996ef21b8b910fc64050493a0595ae *src/s2/strings/serialize.h +c90e431dac905cc9e596e1b1071b7ea1 *src/s2/util/bits/bit-interleave.cc +3bfcb45aa9bfa89827d30afba984f79c *src/s2/util/bits/bit-interleave.h +ce0ab9919f6091c79c677b39c23f8b00 *src/s2/util/bits/bits.cc +37c9da7e1a7d27c20ca04a3a8ef559ec *src/s2/util/bits/bits.h +c0d70edbf2ac3f698c17d89959e326f9 *src/s2/util/coding/coder.cc +92ada6509b11d1a90ec200c2adf9e0dd *src/s2/util/coding/coder.h +c4fe67fd8a294e03bfc46ce9fb1134d1 *src/s2/util/coding/nth-derivative.h +2e711fab16d3ec015ecc12d522b8b0ae *src/s2/util/coding/transforms.h +90d94cda2cd92a02c4851ebb93d7453e *src/s2/util/coding/varint.cc +9a4e580abf363f1e0bb4bded873514cd *src/s2/util/coding/varint.h +9e59dc6aad536cbce24eedfb658d8716 *src/s2/util/endian/endian.h +4525710cda51323baa3327ac57ed3106 *src/s2/util/gtl/btree.h +118e86e589e0fae520e30c18dc1f3e08 *src/s2/util/gtl/btree_container.h +52e6fdc3fa81363e043d7e5a35a697ef *src/s2/util/gtl/btree_map.h +81e2de007193adce24cd9d5f5739bf5e *src/s2/util/gtl/btree_set.h +d3ee6aee2a2c316240faa7a33434a23e *src/s2/util/gtl/compact_array.h +09f417c582f54941a864fcbae7d93397 *src/s2/util/gtl/container_logging.h +6868f5ef7082e686952ee034c62a6cce *src/s2/util/gtl/dense_hash_set.h +e6d5083ac705a0300e77fe096a7a0589 *src/s2/util/gtl/densehashtable.h +fa56660fc40be26b3dbfc7115c56674e *src/s2/util/gtl/hashtable_common.h +8e531532de42f1c1f7cd4adb553fe486 *src/s2/util/gtl/layout.h +8c80e94bcdfb52740a0b8c0561845642 *src/s2/util/gtl/legacy_random_shuffle.h +a36fbccb7e3c064e5a16b4ac1e7842f3 *src/s2/util/hash/mix.h +a77a8f0d9d512bbec62949a0b3db20aa *src/s2/util/math/exactfloat/exactfloat.cc +828d0024dcc62675ab2be8b6f3cc3f93 *src/s2/util/math/exactfloat/exactfloat.h +2037118f434c6f41bc8014de2eb18c92 *src/s2/util/math/mathutil.cc +0c3be461d027e0cccb9c0facb12cd046 *src/s2/util/math/mathutil.h +160830f8d1d1983f1b773f953abff72d *src/s2/util/math/matrix3x3.h +c07f92fe336b33334c46895999a4c62a *src/s2/util/math/vector.h +5b091051cad1329e1e2b535e40e9c142 *src/s2/util/math/vector3_hash.h +2aba55dd12417d944cd91a2f02b1d946 *src/s2/util/units/length-units.cc +1bdf37d9bef7eba2b40d5baad2403f8d *src/s2/util/units/length-units.h +bdfd79c04de7e7893d9cadb9dd9d2f63 *src/s2/util/units/physical-units.h +400af05643ac08d897c2100a0a68f1aa *src/s2/value_lexicon.h +f5e89fcca3521af3ae1984cf3b988541 *src/s2geography.h +d8287075eb3b5a0d59d684a3bbc5b5ba *src/s2geography/accessors-geog.cc +10c803af1a5b075cb974f138658b1f41 *src/s2geography/accessors-geog.h +8e45d5fd12d368bb2989b24b6e158da4 *src/s2geography/accessors.cc +3348390f6d77133281e54b1e8a29c45b *src/s2geography/accessors.h +a3e670ac87785c083206586c8f3f4ca6 *src/s2geography/aggregator.h +3b1bf6b72b4dd96ca7c4b760554fc63c *src/s2geography/build.cc +fed04d2f5ddae951483be94e78dd8d46 *src/s2geography/build.h +241843e894b3d958be1f475991febe29 *src/s2geography/constructor.h +26d396867ab4214aaa9c811a3f5076e0 *src/s2geography/coverings.cc +9245d1de9f44acb9dc6f2ea03324761b *src/s2geography/coverings.h +93135f5c6f3755e0b4d2c2c0e40776ed *src/s2geography/distance.cc +ac0bf08dcc4f407205a4d78007006e02 *src/s2geography/distance.h +cb5e8ca81a2ad12cd88d1cc88a2ec00b *src/s2geography/geoarrow-imports.h +be903b4618b334e46916d7c56a3b4cf0 *src/s2geography/geography.cc +f705c43a8173f1b54eb7d391004654c5 *src/s2geography/geography.h +275d807d19673e87935f24d914697a7b *src/s2geography/index.h +520c06d0ec1884663e62ba6e7b183f49 *src/s2geography/linear-referencing.cc +ad9e7623296a977bc6b38f6acd1e29b2 *src/s2geography/linear-referencing.h +d8d87d7c6b89d370f45398ce0bc4b545 *src/s2geography/predicates.cc +a8a420182aad78038f2f87e8b8e0539d *src/s2geography/predicates.h +6622cfeaf8b569e1dbb71209addae76f *src/tests/main.c +4495750656420f7cd041bf0bf27ec020 *src/tests/soname.h +3ff04996080488941707e8fd75d50d02 *src/wk-impl.c +a9ffa55303a6dcba9c5c9c9d322e2009 *tests/area.R +960ec65d25d1018d2084d81ed3324fdd *tests/area.Rout +9a6a22f7e00fd5147781e07d2edd589e *tests/testthat.R +755c4ca0ad5e6f558b9eb7c7e04ded6e *tests/testthat/test-data.R +d8d89b5827614edf67567d7e15e2f213 *tests/testthat/test-plot.R +c94a63e70152c14d6ea3d8e0c947d739 *tests/testthat/test-s2-accessors.R +6d272b0f1b19c173c19ff225c361cd31 *tests/testthat/test-s2-bounds.R +5c3476c24589151b804b66580c20e9b8 *tests/testthat/test-s2-cell-union.R +c64aab49b83a1fdc400a28233aefbda7 *tests/testthat/test-s2-cell.R +2fb5fbb6a8d7d4d13c80c650380a2763 *tests/testthat/test-s2-constructors-formatters.R +532d31b6373a11733f72fda2b4254ef6 *tests/testthat/test-s2-earth.R +022f63897a9ae3916d1990c77c23c4ae *tests/testthat/test-s2-geography.R +68d9f8c6639d165549ed1e487365bb0e *tests/testthat/test-s2-lnglat.R +b874f311928c98a3056d5628c55422c8 *tests/testthat/test-s2-matrix.R +97bdafc52d85952cf4d72ef1818ba36c *tests/testthat/test-s2-options.R +b8d52c633fa3d389a46c8198804f9757 *tests/testthat/test-s2-point.R +6a0b51e1b62caf1dc1ce91d662c9a424 *tests/testthat/test-s2-predicates.R +5ef809d341a4b5203d675644845e6e94 *tests/testthat/test-s2-transformers.R +a7de1183f03382626ca2b46bf1c4e781 *tests/testthat/test-utils.R +2c9ecaae7d9fe889868e0c69bfecc42c *tests/testthat/test-vctrs.R +eb4ce45137cf0d61472a37c5f493d59b *tests/testthat/test-wk-utils.R +0ec27c181a66ce5b72bc8c3940e9e00e *tools/version.c +497dfd3cbf113981c70269ce4b31c496 *tools/winlibs.R diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 0000000..7ed3d7c --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,206 @@ +# Generated by roxygen2: do not edit by hand + +S3method("[<-",s2_cell) +S3method("[<-",s2_geography) +S3method("[[<-",s2_cell) +S3method("[[<-",s2_geography) +S3method(Math,s2_cell) +S3method(Ops,s2_cell) +S3method(Summary,s2_cell) +S3method(as.character,s2_cell) +S3method(as.character,s2_cell_union) +S3method(as.character,s2_geography) +S3method(as.list,s2_cell) +S3method(as_s2_cell,character) +S3method(as_s2_cell,integer64) +S3method(as_s2_cell,s2_cell) +S3method(as_s2_cell,s2_geography) +S3method(as_s2_cell,wk_xy) +S3method(as_s2_cell_union,character) +S3method(as_s2_cell_union,s2_cell) +S3method(as_s2_cell_union,s2_cell_union) +S3method(as_s2_geography,WKB) +S3method(as_s2_geography,blob) +S3method(as_s2_geography,character) +S3method(as_s2_geography,logical) +S3method(as_s2_geography,s2_cell_union) +S3method(as_s2_geography,s2_geography) +S3method(as_s2_geography,wk_wkb) +S3method(as_s2_geography,wk_wkt) +S3method(as_s2_geography,wk_xy) +S3method(as_s2_lnglat,character) +S3method(as_s2_lnglat,default) +S3method(as_s2_lnglat,wk_xy) +S3method(as_s2_lnglat,wk_xyz) +S3method(as_s2_point,character) +S3method(as_s2_point,default) +S3method(as_s2_point,wk_xy) +S3method(as_s2_point,wk_xyz) +S3method(as_wkb,s2_geography) +S3method(as_wkt,s2_geography) +S3method(format,s2_cell) +S3method(format,s2_cell_union) +S3method(format,s2_geography) +S3method(format,s2_point_crs) +S3method(is.na,s2_cell) +S3method(is.na,s2_cell_union) +S3method(is.na,s2_geography) +S3method(is.numeric,s2_cell) +S3method(plot,s2_cell) +S3method(plot,s2_cell_union) +S3method(plot,s2_geography) +S3method(print,s2_cell_union) +S3method(sort,s2_cell) +S3method(str,s2_cell_union) +S3method(unique,s2_cell) +S3method(unlist,s2_cell_union) +S3method(wk_crs,s2_geography) +S3method(wk_handle,s2_geography) +S3method(wk_is_geodesic,s2_geography) +S3method(wk_set_crs,s2_geography) +S3method(wk_set_geodesic,s2_geography) +S3method(wk_writer,s2_geography) +export(as_s2_cell) +export(as_s2_cell_union) +export(as_s2_geography) +export(as_s2_lnglat) +export(as_s2_point) +export(new_s2_cell) +export(s2_area) +export(s2_as_binary) +export(s2_as_text) +export(s2_boundary) +export(s2_bounds_cap) +export(s2_bounds_rect) +export(s2_buffer_cells) +export(s2_cell) +export(s2_cell_area) +export(s2_cell_area_approx) +export(s2_cell_boundary) +export(s2_cell_center) +export(s2_cell_child) +export(s2_cell_common_ancestor_level) +export(s2_cell_common_ancestor_level_agg) +export(s2_cell_contains) +export(s2_cell_debug_string) +export(s2_cell_distance) +export(s2_cell_edge_neighbour) +export(s2_cell_invalid) +export(s2_cell_is_face) +export(s2_cell_is_leaf) +export(s2_cell_is_valid) +export(s2_cell_level) +export(s2_cell_max_distance) +export(s2_cell_may_intersect) +export(s2_cell_parent) +export(s2_cell_polygon) +export(s2_cell_sentinel) +export(s2_cell_to_lnglat) +export(s2_cell_union) +export(s2_cell_union_contains) +export(s2_cell_union_difference) +export(s2_cell_union_intersection) +export(s2_cell_union_intersects) +export(s2_cell_union_normalize) +export(s2_cell_union_union) +export(s2_cell_vertex) +export(s2_centroid) +export(s2_centroid_agg) +export(s2_closest_edges) +export(s2_closest_feature) +export(s2_closest_point) +export(s2_contains) +export(s2_contains_matrix) +export(s2_convex_hull) +export(s2_convex_hull_agg) +export(s2_coverage_union_agg) +export(s2_covered_by) +export(s2_covered_by_matrix) +export(s2_covering_cell_ids) +export(s2_covering_cell_ids_agg) +export(s2_covers) +export(s2_covers_matrix) +export(s2_data_cities) +export(s2_data_countries) +export(s2_data_timezones) +export(s2_difference) +export(s2_dimension) +export(s2_disjoint) +export(s2_disjoint_matrix) +export(s2_distance) +export(s2_distance_matrix) +export(s2_dwithin) +export(s2_dwithin_matrix) +export(s2_earth_radius_meters) +export(s2_equals) +export(s2_equals_matrix) +export(s2_farthest_feature) +export(s2_geog_from_text) +export(s2_geog_from_wkb) +export(s2_geog_point) +export(s2_geography) +export(s2_geography_writer) +export(s2_hemisphere) +export(s2_interpolate) +export(s2_interpolate_normalized) +export(s2_intersection) +export(s2_intersects) +export(s2_intersects_box) +export(s2_intersects_matrix) +export(s2_is_collection) +export(s2_is_empty) +export(s2_is_valid) +export(s2_is_valid_detail) +export(s2_length) +export(s2_lnglat) +export(s2_make_line) +export(s2_make_polygon) +export(s2_max_distance) +export(s2_max_distance_matrix) +export(s2_may_intersect_matrix) +export(s2_minimum_clearance_line_between) +export(s2_num_points) +export(s2_options) +export(s2_perimeter) +export(s2_plot) +export(s2_point) +export(s2_point_crs) +export(s2_point_on_surface) +export(s2_prepared_dwithin) +export(s2_project) +export(s2_project_normalized) +export(s2_projection_mercator) +export(s2_projection_orthographic) +export(s2_projection_plate_carree) +export(s2_rebuild) +export(s2_rebuild_agg) +export(s2_simplify) +export(s2_snap_distance) +export(s2_snap_identity) +export(s2_snap_level) +export(s2_snap_precision) +export(s2_snap_to_grid) +export(s2_sym_difference) +export(s2_tessellate_tol_default) +export(s2_touches) +export(s2_touches_matrix) +export(s2_trans_lnglat) +export(s2_trans_point) +export(s2_union) +export(s2_union_agg) +export(s2_within) +export(s2_within_matrix) +export(s2_world_plate_carree) +export(s2_x) +export(s2_y) +importFrom(Rcpp,sourceCpp) +importFrom(utils,str) +importFrom(wk,as_wkb) +importFrom(wk,as_wkt) +importFrom(wk,wk_crs) +importFrom(wk,wk_handle) +importFrom(wk,wk_is_geodesic) +importFrom(wk,wk_set_crs) +importFrom(wk,wk_set_geodesic) +importFrom(wk,wk_writer) +useDynLib(s2, .registration = TRUE) diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..9dcce52 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,142 @@ +# s2 (development version) + +* Updated more tests to pass on a forthcoming waldo package update (#237). + +# s2 1.1.3 + +* Made a test less strict to pass tests on Alpine Linux (#218, #220). +* Updated tests to pass on forthcoming waldo package update (@hadley, #226). +* Updated vendored file modifications to suppress a multi-line comment + warning on gcc (#214, #227). + +# s2 1.1.2 + +- Fixed test for `as.data.frame()` for `s2_cell()` to comply with new wk + version and the latest release of R (#207). +- Fix unary union of an empty multipolygon (#208). +- Added `#include ` to an Abseil header to fix compilation with + gcc13 (#209, #210). +- Update internal Abseil to 20220623.1 LTS (#213). + +# s2 1.1.1 + +- Fix new CRAN check warnings (#202, #203). + +# s2 1.1.0 + +- Fix for s2 build on Windows with R <= 3.6.x (#142) +- Fix for s2 build on MacOS with multiple openssl versions (#142, #145, #146) +- Fix for s2 build on 32-bit openssl (#143, #147) +- Added `s2_convex_hull()` and `s2_convex_hull_agg()` (@spiry34, #150, + #151, #163). +- Added `max_distance` argument to `s2_closest_edges()`, making + distance-constrained k-nearest neighbours possible (#125, #156, #162). +- Added a spherical `s2_point_on_surface()` implementation for polygons + (@kylebutts, #152, #161) +- Added a `s2_cell_union()` vector class to represent cell coverings and + operators to generate them from an s2 geography vector (e.g., + `s2_covering_cell_ids()`). Cell unions are useful as compact representations + of spherical geometry and can be used like a bounding box to determine + a possible intersection with one or more geographies (#85, #94, #164). +- Refactored the simple features compatability layer into a standalone + code base for potential future use in a Python adaptation (#165). +- Migrate input and output to non-deprecated wk package handlers and writers + (#101, #165, #168). +- Make `s2_union_agg()` more efficient using a recursive merge strategy + (#103, #165). +- Fix package build on Raspberry Pi (#169, #171). +- Fix warning on clang14 when compiling with `-O0` (#167, #172). +- Added `s2_prepared_dwithin()` and fixed `s2_dwithin_matrix()` such that it + efficiently uses the index (#157, #174). +- Updated `s2_lnglat()` and `s2_point()` to use `wk::xy()` (a record-style + vctr) to represent point coordinates. This is much faster than the previous + representation which relied on `list()` of external pointers (#181, #159). +- Added arguments `planar` and `tessellate_tol_m` to `s2_as_text()`, + `s2_as_binary()`. Use `planar = TRUE` and set `tessellate_tol_m` to the + maximum error for your use-case to automatically subdivide edges to + preserve or "straight" lines in Plate carree projection on import (#182). +- Added arguments `planar` and `tessellate_tol_m` to `s2_geog_from_text()`, and + `s2_geog_from_wkb()`. Use `planar = TRUE` and set `tessellate_tol_m` to the + maximum error for your use-case to automatically subdivide edges to + ensure or "straight" lines in Plate carree projection on export (#182). + +# s2 1.0.7 + +- Update the internal copy of s2geometry to use updated Abseil, + fixing a compiler warning on gcc-11 (#79, #134). + +# s2 1.0.6 + +- Added support for `STRICT_R_HEADERS` (@eddelbuettel, #118). +- Fixed a bug where the result of `s2_centroid_agg()` did not + behave like a normal point in distance calculations (#119, #121). +- Fixed a Windows UCRT check failure and updated openssl linking + (@jeroen, #122). + +# s2 1.0.5 + +* Added `s2_projection_filter()` and `s2_unprojection_filter()` to + expose the S2 edge tessellator, which can be used to make Cartesian + or great circle assumptions of line segments explicit by adding + points where necessary (#115). +* Added an `s2_cell()` vector class to expose a subset of the S2 + indexing system to R users (#85, #114). +* Added `s2_closest_edges()` to make k-nearest neighbours calculation + possible on the sphere (#111, #112). +* Added `s2_interpolate()`, `s2_interpolate_normalized()`, + `s2_project()`, and `s2_project_normalized()` to provide linear + referencing support on the sphere (#96, #110). +* Fixed import of empty points from WKB (#109). +* Added argument `dimensions` to `s2_options()` to constrain the + output dimensions of a boolean or rebuild operation (#105, #104, #110). +* Added `s2_is_valid()` and `s2_is_valid_detail()` to help find invalid + spherical geometries when importing data into S2 (#100). +* Improved error messages when importing and processing data such that + errors can be debugged more readily (#100, #98). +* The unary version of `s2_union()` can now handle MULTIPOLYGON + geometries with overlapping rings in addition to other invalid + polygons. `s2_union()` can now sanitize + almost any input to be valid spherical geometry with + minimal modification (#100, #99). +* Renamed the existing implementation of `s2_union_agg()` to + `s2_coverage_union_agg()` to make clear that the function only + works when the individual geometries do not have overlapping + interiors. `s2_union_agg()` was replaced with a + true aggregate union that can handle unions of most geometries + (#100, #97). +* Added `s2_rebuild_agg()` to match `s2_union_agg()`. Like + `s2_rebuild()`, `s2_rebuild_agg()` collects the edges in the input + and builds them into a feature, optionally snapping or simplifying + vertices in the process (#100). + +# s2 1.0.4 + +* Fixed errors that resulted from compilation on clang 12.2 (#88, #89). + +# s2 1.0.3 + +* Fixed CRAN check errors (#80). + +# s2 1.0.2 + +* Fixed CRAN check errors (#71, #75, #72). + +# s2 1.0.1 + +* Added layer creation options to `s2_options()`, which now uses strings + rather than numeric codes to specify boolean operation options, geography + construction options, and builder options (#70). +* Added `s2_rebuild()` and `s2_simplify()`, which wrap the S2 C++ `S2Builder` + class to provide simplification and fixing of invalid geographies (#70). +* The s2 package now builds and passes the CMD check on Solaris (#66, #67). +* Renamed `s2_latlng()` to `s2_lnglat()` to keep axis order consistent + throughout the package (#69). +* Added `s2_bounds_cap()` and `s2_bounds_rect()` to compute bounding areas + using geographic coordinates (@edzer, #63). +* `s2_*_matrix()` predicates now efficiently use indexing to compute the + results of many predicate comparisons (#61). + +# s2 1.0.0 + +This version is a complete rewrite of the former s2 CRAN package, entirely +backwards incompatible with previous versions. diff --git a/R/RcppExports.R b/R/RcppExports.R new file mode 100644 index 0000000..bb373a3 --- /dev/null +++ b/R/RcppExports.R @@ -0,0 +1,447 @@ +# Generated by using Rcpp::compileAttributes() -> do not edit by hand +# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +cpp_s2_init <- function() { + invisible(.Call(`_s2_cpp_s2_init`)) +} + +cpp_s2_is_collection <- function(geog) { + .Call(`_s2_cpp_s2_is_collection`, geog) +} + +cpp_s2_is_valid <- function(geog) { + .Call(`_s2_cpp_s2_is_valid`, geog) +} + +cpp_s2_is_valid_reason <- function(geog) { + .Call(`_s2_cpp_s2_is_valid_reason`, geog) +} + +cpp_s2_dimension <- function(geog) { + .Call(`_s2_cpp_s2_dimension`, geog) +} + +cpp_s2_num_points <- function(geog) { + .Call(`_s2_cpp_s2_num_points`, geog) +} + +cpp_s2_is_empty <- function(geog) { + .Call(`_s2_cpp_s2_is_empty`, geog) +} + +cpp_s2_area <- function(geog) { + .Call(`_s2_cpp_s2_area`, geog) +} + +cpp_s2_length <- function(geog) { + .Call(`_s2_cpp_s2_length`, geog) +} + +cpp_s2_perimeter <- function(geog) { + .Call(`_s2_cpp_s2_perimeter`, geog) +} + +cpp_s2_x <- function(geog) { + .Call(`_s2_cpp_s2_x`, geog) +} + +cpp_s2_y <- function(geog) { + .Call(`_s2_cpp_s2_y`, geog) +} + +cpp_s2_project_normalized <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_project_normalized`, geog1, geog2) +} + +cpp_s2_distance <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_distance`, geog1, geog2) +} + +cpp_s2_max_distance <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_max_distance`, geog1, geog2) +} + +cpp_s2_bounds_cap <- function(geog) { + .Call(`_s2_cpp_s2_bounds_cap`, geog) +} + +cpp_s2_bounds_rect <- function(geog) { + .Call(`_s2_cpp_s2_bounds_rect`, geog) +} + +cpp_s2_cell_union_normalize <- function(cellUnionVector) { + .Call(`_s2_cpp_s2_cell_union_normalize`, cellUnionVector) +} + +cpp_s2_cell_union_is_na <- function(cellUnionVector) { + .Call(`_s2_cpp_s2_cell_union_is_na`, cellUnionVector) +} + +cpp_s2_cell_union_contains <- function(cellUnionVector1, cellUnionVector2) { + .Call(`_s2_cpp_s2_cell_union_contains`, cellUnionVector1, cellUnionVector2) +} + +cpp_s2_cell_union_contains_cell <- function(cellUnionVector, cellIdVector) { + .Call(`_s2_cpp_s2_cell_union_contains_cell`, cellUnionVector, cellIdVector) +} + +cpp_s2_cell_union_intersects <- function(cellUnionVector1, cellUnionVector2) { + .Call(`_s2_cpp_s2_cell_union_intersects`, cellUnionVector1, cellUnionVector2) +} + +cpp_s2_cell_union_intersection <- function(cellUnionVector1, cellUnionVector2) { + .Call(`_s2_cpp_s2_cell_union_intersection`, cellUnionVector1, cellUnionVector2) +} + +cpp_s2_cell_union_union <- function(cellUnionVector1, cellUnionVector2) { + .Call(`_s2_cpp_s2_cell_union_union`, cellUnionVector1, cellUnionVector2) +} + +cpp_s2_cell_union_difference <- function(cellUnionVector1, cellUnionVector2) { + .Call(`_s2_cpp_s2_cell_union_difference`, cellUnionVector1, cellUnionVector2) +} + +cpp_s2_geography_from_cell_union <- function(cellUnionVector) { + .Call(`_s2_cpp_s2_geography_from_cell_union`, cellUnionVector) +} + +cpp_s2_covering_cell_ids <- function(geog, min_level, max_level, max_cells, buffer, interior) { + .Call(`_s2_cpp_s2_covering_cell_ids`, geog, min_level, max_level, max_cells, buffer, interior) +} + +cpp_s2_covering_cell_ids_agg <- function(geog, min_level, max_level, max_cells, buffer, interior, naRm) { + .Call(`_s2_cpp_s2_covering_cell_ids_agg`, geog, min_level, max_level, max_cells, buffer, interior, naRm) +} + +cpp_s2_cell_sentinel <- function() { + .Call(`_s2_cpp_s2_cell_sentinel`) +} + +cpp_s2_cell_from_string <- function(cellString) { + .Call(`_s2_cpp_s2_cell_from_string`, cellString) +} + +cpp_s2_cell_from_lnglat <- function(lnglat) { + .Call(`_s2_cpp_s2_cell_from_lnglat`, lnglat) +} + +cpp_s2_cell_to_lnglat <- function(cellId) { + .Call(`_s2_cpp_s2_cell_to_lnglat`, cellId) +} + +cpp_s2_cell_to_cell_union <- function(cellId) { + .Call(`_s2_cpp_s2_cell_to_cell_union`, cellId) +} + +cpp_s2_cell_is_na <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_is_na`, cellIdVector) +} + +cpp_s2_cell_sort <- function(cellIdVector, decreasing) { + .Call(`_s2_cpp_s2_cell_sort`, cellIdVector, decreasing) +} + +cpp_s2_cell_range <- function(cellIdVector, naRm) { + .Call(`_s2_cpp_s2_cell_range`, cellIdVector, naRm) +} + +cpp_s2_cell_unique <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_unique`, cellIdVector) +} + +cpp_s2_cell_to_string <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_to_string`, cellIdVector) +} + +cpp_s2_cell_debug_string <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_debug_string`, cellIdVector) +} + +cpp_s2_cell_is_valid <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_is_valid`, cellIdVector) +} + +cpp_s2_cell_center <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_center`, cellIdVector) +} + +cpp_s2_cell_polygon <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_polygon`, cellIdVector) +} + +cpp_s2_cell_vertex <- function(cellIdVector, k) { + .Call(`_s2_cpp_s2_cell_vertex`, cellIdVector, k) +} + +cpp_s2_cell_level <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_level`, cellIdVector) +} + +cpp_s2_cell_area <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_area`, cellIdVector) +} + +cpp_s2_cell_area_approx <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_area_approx`, cellIdVector) +} + +cpp_s2_cell_parent <- function(cellIdVector, level) { + .Call(`_s2_cpp_s2_cell_parent`, cellIdVector, level) +} + +cpp_s2_cell_child <- function(cellIdVector, k) { + .Call(`_s2_cpp_s2_cell_child`, cellIdVector, k) +} + +cpp_s2_cell_edge_neighbour <- function(cellIdVector, k) { + .Call(`_s2_cpp_s2_cell_edge_neighbour`, cellIdVector, k) +} + +cpp_s2_cell_cummax <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_cummax`, cellIdVector) +} + +cpp_s2_cell_cummin <- function(cellIdVector) { + .Call(`_s2_cpp_s2_cell_cummin`, cellIdVector) +} + +cpp_s2_cell_eq <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_eq`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_neq <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_neq`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_lt <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_lt`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_lte <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_lte`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_gte <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_gte`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_gt <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_gt`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_contains <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_contains`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_may_intersect <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_may_intersect`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_distance <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_distance`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_max_distance <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_max_distance`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_common_ancestor_level <- function(cellIdVector1, cellIdVector2) { + .Call(`_s2_cpp_s2_cell_common_ancestor_level`, cellIdVector1, cellIdVector2) +} + +cpp_s2_cell_common_ancestor_level_agg <- function(cellId) { + .Call(`_s2_cpp_s2_cell_common_ancestor_level_agg`, cellId) +} + +s2_geography_full <- function(x) { + .Call(`_s2_s2_geography_full`, x) +} + +cpp_s2_geography_is_na <- function(geog) { + .Call(`_s2_cpp_s2_geography_is_na`, geog) +} + +s2_lnglat_from_s2_point <- function(s2_point) { + .Call(`_s2_s2_lnglat_from_s2_point`, s2_point) +} + +s2_point_from_s2_lnglat <- function(s2_lnglat) { + .Call(`_s2_s2_point_from_s2_lnglat`, s2_lnglat) +} + +cpp_s2_closest_feature <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_closest_feature`, geog1, geog2) +} + +cpp_s2_farthest_feature <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_farthest_feature`, geog1, geog2) +} + +cpp_s2_closest_edges <- function(geog1, geog2, n, min_distance, max_distance) { + .Call(`_s2_cpp_s2_closest_edges`, geog1, geog2, n, min_distance, max_distance) +} + +cpp_s2_may_intersect_matrix <- function(geog1, geog2, maxEdgesPerCell, maxFeatureCells, s2options) { + .Call(`_s2_cpp_s2_may_intersect_matrix`, geog1, geog2, maxEdgesPerCell, maxFeatureCells, s2options) +} + +cpp_s2_contains_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_contains_matrix`, geog1, geog2, s2options) +} + +cpp_s2_within_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_within_matrix`, geog1, geog2, s2options) +} + +cpp_s2_intersects_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_intersects_matrix`, geog1, geog2, s2options) +} + +cpp_s2_equals_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_equals_matrix`, geog1, geog2, s2options) +} + +cpp_s2_touches_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_touches_matrix`, geog1, geog2, s2options) +} + +cpp_s2_dwithin_matrix <- function(geog1, geog2, distance) { + .Call(`_s2_cpp_s2_dwithin_matrix`, geog1, geog2, distance) +} + +cpp_s2_distance_matrix <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_distance_matrix`, geog1, geog2) +} + +cpp_s2_max_distance_matrix <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_max_distance_matrix`, geog1, geog2) +} + +cpp_s2_contains_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_contains_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_within_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_within_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_intersects_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_intersects_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_disjoint_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_disjoint_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_equals_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_equals_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_dwithin_matrix_brute_force <- function(geog1, geog2, distance) { + .Call(`_s2_cpp_s2_dwithin_matrix_brute_force`, geog1, geog2, distance) +} + +cpp_s2_intersects <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_intersects`, geog1, geog2, s2options) +} + +cpp_s2_equals <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_equals`, geog1, geog2, s2options) +} + +cpp_s2_contains <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_contains`, geog1, geog2, s2options) +} + +cpp_s2_touches <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_touches`, geog1, geog2, s2options) +} + +cpp_s2_dwithin <- function(geog1, geog2, distance) { + .Call(`_s2_cpp_s2_dwithin`, geog1, geog2, distance) +} + +cpp_s2_prepared_dwithin <- function(geog1, geog2, distance) { + .Call(`_s2_cpp_s2_prepared_dwithin`, geog1, geog2, distance) +} + +cpp_s2_intersects_box <- function(geog, lng1, lat1, lng2, lat2, detail, s2options) { + .Call(`_s2_cpp_s2_intersects_box`, geog, lng1, lat1, lng2, lat2, detail, s2options) +} + +cpp_s2_intersection <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_intersection`, geog1, geog2, s2options) +} + +cpp_s2_union <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_union`, geog1, geog2, s2options) +} + +cpp_s2_difference <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_difference`, geog1, geog2, s2options) +} + +cpp_s2_sym_difference <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_sym_difference`, geog1, geog2, s2options) +} + +cpp_s2_coverage_union_agg <- function(geog, s2options, naRm) { + .Call(`_s2_cpp_s2_coverage_union_agg`, geog, s2options, naRm) +} + +cpp_s2_union_agg <- function(geog, s2options, naRm) { + .Call(`_s2_cpp_s2_union_agg`, geog, s2options, naRm) +} + +cpp_s2_centroid_agg <- function(geog, naRm) { + .Call(`_s2_cpp_s2_centroid_agg`, geog, naRm) +} + +cpp_s2_rebuild_agg <- function(geog, s2options, naRm) { + .Call(`_s2_cpp_s2_rebuild_agg`, geog, s2options, naRm) +} + +cpp_s2_closest_point <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_closest_point`, geog1, geog2) +} + +cpp_s2_minimum_clearance_line_between <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_minimum_clearance_line_between`, geog1, geog2) +} + +cpp_s2_centroid <- function(geog) { + .Call(`_s2_cpp_s2_centroid`, geog) +} + +cpp_s2_point_on_surface <- function(geog) { + .Call(`_s2_cpp_s2_point_on_surface`, geog) +} + +cpp_s2_boundary <- function(geog) { + .Call(`_s2_cpp_s2_boundary`, geog) +} + +cpp_s2_rebuild <- function(geog, s2options) { + .Call(`_s2_cpp_s2_rebuild`, geog, s2options) +} + +cpp_s2_unary_union <- function(geog, s2options) { + .Call(`_s2_cpp_s2_unary_union`, geog, s2options) +} + +cpp_s2_interpolate_normalized <- function(geog, distanceNormalized) { + .Call(`_s2_cpp_s2_interpolate_normalized`, geog, distanceNormalized) +} + +cpp_s2_buffer_cells <- function(geog, distance, maxCells, minLevel) { + .Call(`_s2_cpp_s2_buffer_cells`, geog, distance, maxCells, minLevel) +} + +cpp_s2_convex_hull <- function(geog) { + .Call(`_s2_cpp_s2_convex_hull`, geog) +} + +cpp_s2_convex_hull_agg <- function(geog, naRm) { + .Call(`_s2_cpp_s2_convex_hull_agg`, geog, naRm) +} + diff --git a/R/data.R b/R/data.R new file mode 100644 index 0000000..23ee0ea --- /dev/null +++ b/R/data.R @@ -0,0 +1,79 @@ + +#' Low-resolution world boundaries, timezones, and cities +#' +#' Well-known binary versions of the [Natural Earth](https://www.naturalearthdata.com/) +#' low-resolution world boundaries and timezone boundaries. +#' +#' @param name The name of a country, continent, city, or `NULL` +#' for all features. +#' @param utc_offset_min,utc_offset_max Minimum and/or maximum timezone +#' offsets. +#' +#' @format A data.frame with columns `name` (character), and +#' `geometry` (wk_wkb) +#' @source [Natural Earth Data](https://www.naturalearthdata.com/) +#' @examples +#' head(s2_data_countries()) +#' s2_data_countries("Germany") +#' s2_data_countries("Europe") +#' +#' head(s2_data_timezones()) +#' s2_data_timezones(-4) +#' +#' head(s2_data_cities()) +#' s2_data_cities("Cairo") +#' +"s2_data_tbl_countries" + +#' @rdname s2_data_tbl_countries +"s2_data_tbl_timezones" + +#' @rdname s2_data_tbl_countries +"s2_data_tbl_cities" + +#' @rdname s2_data_tbl_countries +#' @export +s2_data_countries <- function(name = NULL) { + df <- s2::s2_data_tbl_countries + if (is.null(name)) { + wkb <- df$geometry + } else { + wkb <- df$geometry[(df$name %in% name) | (df$continent %in% name)] + } + + as_s2_geography(wkb) +} + +#' @rdname s2_data_tbl_countries +#' @export +s2_data_timezones <- function(utc_offset_min = NULL, utc_offset_max = utc_offset_min) { + df <- s2::s2_data_tbl_timezones + if (is.null(utc_offset_min)) { + wkb <- df$geometry + } else { + matches <- (df$utc_offset >= utc_offset_min) & (df$utc_offset <= utc_offset_max) + wkb <- df$geometry[matches] + } + + as_s2_geography(wkb) +} + +#' @rdname s2_data_tbl_countries +#' @export +s2_data_cities <- function(name = NULL) { + df <- s2::s2_data_tbl_cities + if (is.null(name)) { + wkb <- df$geometry + } else { + wkb <- df$geometry[df$name %in% name] + } + + as_s2_geography(wkb) +} + +#' Example Geometries +#' +#' These geometries are toy examples useful for testing various coordinate +#' shuffling operations in the s2 package. +#' +"s2_data_example_wkt" diff --git a/R/plot.R b/R/plot.R new file mode 100644 index 0000000..0117263 --- /dev/null +++ b/R/plot.R @@ -0,0 +1,158 @@ + +#' Plot S2 Geographies +#' +#' @inheritParams wk::wk_plot +#' @param plot_hemisphere Plot the outline of the earth +#' @param centre The longitude/latitude point of the centre of the +#' orthographic projection +#' @param simplify Use `FALSE` to skip the simplification step +#' +#' @return The input, invisibly +#' @export +#' +#' @examples +#' s2_plot(s2_data_countries()) +#' s2_plot(s2_data_cities(), add = TRUE) +#' +s2_plot <- function(x, ..., asp = 1, xlab = "", ylab = "", + rule = "evenodd", add = FALSE, + plot_hemisphere = FALSE, + simplify = TRUE, + centre = NULL) { + x <- as_s2_geography(x) + + if (add) { + last <- last_plot_env$centre + centre <- if (is.null(last)) s2_lnglat(0, 0) else last + } else if (is.null(centre)) { + centre <- s2_centroid_agg(x, na.rm = TRUE) + } + + centre <- as_s2_lnglat(centre) + projection <- s2_projection_orthographic(centre) + hemisphere_bounds_poly <- cap_to_polygon(centre, (pi / 2) - 1e-5) + + if (plot_hemisphere) { + bbox_projected <- wk::rct(-1, -1, 1, 1) + } else if (!add) { + bbox_projected <- wk::wk_handle( + x, + wk::wk_bbox_handler(), + s2_projection = projection, + s2_tessellate_tol = s2_tessellate_tol_default() + ) + } + + wk::wk_plot( + wk::xy(), + bbox = bbox_projected, + asp = asp, xlab = xlab, ylab = ylab, + add = add + ) + + if (!add) { + last_plot_env$centre <- centre + } + + if (plot_hemisphere) { + wk::wk_plot(wk::crc(0, 0, 1), add = TRUE) + } + + # estimate resolution. In user coords, this can be though of in radians + # (at the centre of the plot) + usr <- graphics::par("usr") + usr_x <- usr[1:2] + usr_y <- usr[3:4] + device_x <- graphics::grconvertX(usr_x, to = "device") + device_y <- graphics::grconvertY(usr_y, to = "device") + + scale_x <- diff(device_x) / diff(usr_x) + scale_y <- diff(device_y) / diff(usr_y) + scale <- min(abs(scale_x), abs(scale_y)) + resolution_usr_rad <- 0.25 / scale + + # limit output to dimensions in input + dimensions_in_input <- setdiff(unique(s2_dimension(x)), NA_character_) + + if (simplify) { + x_hemisphere <- s2_intersection( + x, + hemisphere_bounds_poly, + options = s2_options( + snap = s2_snap_distance(resolution_usr_rad), + snap_radius = resolution_usr_rad, + simplify_edge_chains = TRUE, + dimensions = c("point", "polyline", "polygon")[dimensions_in_input + 1] + ) + ) + } else { + x_hemisphere <- s2_intersection( + x, + hemisphere_bounds_poly, + options = s2_options( + dimensions = c("point", "polyline", "polygon")[dimensions_in_input + 1] + ) + ) + } + + x_hemisphere[s2_is_empty(x_hemisphere)] <- as_s2_geography("POINT EMPTY") + + x_hemisphere_planar <- wk::wk_handle( + x_hemisphere, + wk::wkb_writer(), + s2_projection = projection, + # if this is too small we can get a stack overflow since the + # tessellation is recursive + s2_tessellate_tol = max(0.002, resolution_usr_rad * 4) + ) + + wk::wk_plot( + x_hemisphere_planar, + ..., + rule = rule, + add = TRUE + ) + + invisible(x) +} + +#' @export +plot.s2_geography <- function(x, ...) { + s2_plot(x, ...) +} + +#' @export +plot.s2_cell_union <- function(x, ...) { + s2_plot(x, ...) + invisible(x) +} + +#' @export +plot.s2_cell <- function(x, ...) { + if (all(s2_cell_is_leaf(x), na.rm = TRUE)) { + s2_plot(s2_cell_center(x), ...) + } else { + s2_plot(s2_cell_polygon(x), ...) + } + + invisible(x) +} + +cap_to_polygon <- function(centre = s2_lnglat(0, 0), radius_rad) { + centre <- as_s2_lnglat(centre) + rad_proj <- sin(radius_rad) + points <- wk::xy( + c(0, rad_proj, 0, -rad_proj, 0), + c(rad_proj, 0, -rad_proj, 0, rad_proj) + ) + points_s2 <- wk::wk_handle( + points, + s2_geography_writer( + projection = s2_projection_orthographic(centre) + ) + ) + s2_make_polygon(s2_x(points_s2), s2_y(points_s2)) +} + +last_plot_env <- new.env(parent = emptyenv()) +last_plot_env$centre <- NULL diff --git a/R/s2-accessors.R b/R/s2-accessors.R new file mode 100644 index 0000000..961de37 --- /dev/null +++ b/R/s2-accessors.R @@ -0,0 +1,169 @@ + +#' S2 Geography Accessors +#' +#' Accessors extract information about [geography vectors][as_s2_geography]. +#' +#' @param x,y [geography vectors][as_s2_geography]. These inputs +#' are passed to [as_s2_geography()], so you can pass other objects +#' (e.g., character vectors of well-known text) directly. +#' @param radius Radius of the earth. Defaults to the average radius of +#' the earth in meters as defined by [s2_earth_radius_meters()]. +#' +#' @export +#' +#' @seealso +#' BigQuery's geography function reference: +#' +#' - [ST_ISCOLLECTION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_iscollection) +#' - [ST_DIMENSION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dimension) +#' - [ST_NUMPOINTS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_numpoints) +#' - [ST_ISEMPTY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_isempty) +#' - [ST_AREA](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_area) +#' - [ST_LENGTH](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_length) +#' - [ST_PERIMETER](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_perimeter) +#' - [ST_X](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_x) +#' - [ST_Y](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_y) +#' - [ST_DISTANCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_distance) +#' - [ST_MAXDISTANCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_maxdistance) +#' +#' @examples +#' # s2_is_collection() tests for multiple geometries in one feature +#' s2_is_collection(c("POINT (-64 45)", "MULTIPOINT ((-64 45), (8 72))")) +#' +#' # s2_dimension() returns 0 for point, 1 for line, 2 for polygon +#' s2_dimension( +#' c( +#' "GEOMETRYCOLLECTION EMPTY", +#' "POINT (-64 45)", +#' "LINESTRING (-64 45, 8 72)", +#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", +#' "GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 8 72))" +#' ) +#' ) +#' +#' # s2_num_points() counts points +#' s2_num_points(c("POINT (-64 45)", "LINESTRING (-64 45, 8 72)")) +#' +#' # s2_is_empty tests for emptiness +#' s2_is_empty(c("POINT (-64 45)", "POINT EMPTY")) +#' +#' # calculate area, length, and perimeter +#' s2_area("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") +#' s2_perimeter("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") +#' s2_length(s2_boundary("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")) +#' +#' # extract x and y coordinates from points +#' s2_x(c("POINT (-64 45)", "POINT EMPTY")) +#' s2_y(c("POINT (-64 45)", "POINT EMPTY")) +#' +#' # calculate minimum and maximum distance between two geometries +#' s2_distance( +#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", +#' "POINT (-64 45)" +#' ) +#' s2_max_distance( +#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", +#' "POINT (-64 45)" +#' ) +#' +s2_is_collection <- function(x) { + cpp_s2_is_collection(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_is_valid <- function(x) { + cpp_s2_is_valid(as_s2_geography(x, check = FALSE)) +} + +#' @rdname s2_is_collection +#' @export +s2_is_valid_detail <- function(x) { + x <- as_s2_geography(x, check = FALSE) + data.frame( + is_valid = cpp_s2_is_valid(x), + reason = cpp_s2_is_valid_reason(x), + stringsAsFactors = FALSE + ) +} + +#' @rdname s2_is_collection +#' @export +s2_dimension <- function(x) { + cpp_s2_dimension(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_num_points <- function(x) { + cpp_s2_num_points(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_is_empty <- function(x) { + cpp_s2_is_empty(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_area <- function(x, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), radius) + cpp_s2_area(recycled[[1]]) * radius ^ 2 +} + +#' @rdname s2_is_collection +#' @export +s2_length <- function(x, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), radius) + cpp_s2_length(recycled[[1]]) * radius +} + +#' @rdname s2_is_collection +#' @export +s2_perimeter <- function(x, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), radius) + cpp_s2_perimeter(recycled[[1]]) * radius +} + +#' @rdname s2_is_collection +#' @export +s2_x <- function(x) { + cpp_s2_x(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_y <- function(x) { + cpp_s2_y(as_s2_geography(x)) +} + +# document these with the other linear referencers +#' @rdname s2_interpolate +#' @export +s2_project <- function(x, y, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), radius) + length <- cpp_s2_length(recycled[[1]]) * radius + cpp_s2_project_normalized(recycled[[1]], recycled[[2]]) * length +} + +#' @rdname s2_interpolate +#' @export +s2_project_normalized <- function(x, y) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_project_normalized(recycled[[1]], recycled[[2]]) +} + +#' @rdname s2_is_collection +#' @export +s2_distance <- function(x, y, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), radius) + cpp_s2_distance(recycled[[1]], recycled[[2]]) * radius +} + +#' @rdname s2_is_collection +#' @export +s2_max_distance <- function(x, y, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), radius) + cpp_s2_max_distance(recycled[[1]], recycled[[2]]) * radius +} diff --git a/R/s2-bounds.R b/R/s2-bounds.R new file mode 100644 index 0000000..56e1736 --- /dev/null +++ b/R/s2-bounds.R @@ -0,0 +1,35 @@ + +#' Compute feature-wise and aggregate bounds +#' +#' [s2_bounds_rect()] returns a bounding latitude-longitude +#' rectangle that contains the region; [s2_bounds_cap()] returns a bounding circle +#' represented by a centre point (lat, lng) and an angle. The bound may not be tight +#' for points, polylines and geometry collections. The rectangle returned may depend on +#' the order of points or polylines. `lng_lo` values larger than `lng_hi` indicate +#' regions that span the antimeridian, see the Fiji example. +#' +#' @param x An [s2_geography()] vector. +#' @export +#' @return Both functions return a `data.frame`: +#' +#' - [s2_bounds_rect()]: Columns `minlng`, `minlat`, `maxlng`, `maxlat` (degrees) +#' - [s2_bounds_cap()]: Columns `lng`, `lat`, `angle` (degrees) +#' +#' @examples +#' s2_bounds_cap(s2_data_countries("Antarctica")) +#' s2_bounds_cap(s2_data_countries("Netherlands")) +#' s2_bounds_cap(s2_data_countries("Fiji")) +#' +#' s2_bounds_rect(s2_data_countries("Antarctica")) +#' s2_bounds_rect(s2_data_countries("Netherlands")) +#' s2_bounds_rect(s2_data_countries("Fiji")) +#' +s2_bounds_cap <- function(x) { + cpp_s2_bounds_cap(as_s2_geography(x)) +} + +#' @rdname s2_bounds_cap +#' @export +s2_bounds_rect <- function(x) { + cpp_s2_bounds_rect(as_s2_geography(x)) +} diff --git a/R/s2-cell-union.R b/R/s2-cell-union.R new file mode 100644 index 0000000..3ee5af5 --- /dev/null +++ b/R/s2-cell-union.R @@ -0,0 +1,200 @@ + +#' Create S2 Cell Union vectors +#' +#' @param x A `list()` of [s2_cell()] vectors. +#' @param ... Passed to S3 methods +#' +#' @return An object of class "s2_cell_union". +#' @export +#' +s2_cell_union <- function(x = list()) { + x <- as.list(x) + input_na <- vapply(x, is.null, logical(1)) + union <- vector("list", length(x)) + union[input_na] <- list(NULL) + union[!input_na] <- lapply(x[!input_na], as_s2_cell) + new_s2_cell_union(union) +} + +#' @rdname s2_cell_union +#' @export +as_s2_geography.s2_cell_union <- function(x, ...) { + new_s2_geography(cpp_s2_geography_from_cell_union(as_s2_cell_union(x))) +} + +#' @rdname s2_cell_union +#' @export +as_s2_cell_union <- function(x, ...) { + UseMethod("as_s2_cell_union") +} + +#' @rdname s2_cell_union +#' @export +as_s2_cell_union.s2_cell_union <- function(x, ...) { + x +} + +#' @rdname s2_cell_union +#' @export +as_s2_cell_union.s2_cell <- function(x, ...) { + cpp_s2_cell_to_cell_union(x) +} + +#' @rdname s2_cell_union +#' @export +as_s2_cell_union.character <- function(x, ...) { + split <- strsplit(x, "\\s*;\\s*") + split[is.na(x)] <- list(NULL) + s2_cell_union(split) +} + +new_s2_cell_union <- function(x) { + stopifnot(typeof(x) == "list") + structure(x, class = c("s2_cell_union", "wk_vctr")) +} + +#' @export +is.na.s2_cell_union <- function(x, ...) { + cpp_s2_cell_union_is_na(x) +} + +#' @export +format.s2_cell_union <- function(x, ...) { + formatted <- vapply( + unclass(x), + function(e) paste0(as.character(e), collapse = ";"), + character(1) + ) + + formatted[is.na(x)] <- "" + formatted +} + +#' @export +as.character.s2_cell_union <- function(x, ...) { + formatted <- vapply( + unclass(x), + function(e) paste0(as.character(e), collapse = ";"), + character(1) + ) + + formatted[is.na(x)] <- NA_character_ + formatted +} + +#' @export +print.s2_cell_union <- function(x, ...) { + utils::str(x, ...) + invisible(x) +} + +#' @method unlist s2_cell_union +#' @export +unlist.s2_cell_union <- function(x, recursive = TRUE, use.names = TRUE) { + unlisted <- unlist(unclass(x), recursive = recursive, use.names = use.names) + new_s2_cell(as.double(unlisted)) +} + + +#' @importFrom utils str +#' @export +str.s2_cell_union <- function(object, ..., indent.str = "") { + cat(sprintf("%s\n%s", indent.str, length(object), indent.str)) + str(unclass(object), ..., indent.str = indent.str) + invisible(object) +} + + + +#' S2 cell union operators +#' +#' @param x,y An [s2_geography][as_s2_geography] or [s2_cell_union()]. +#' @param min_level,max_level The minimum and maximum levels to constrain the +#' covering. +#' @param max_cells The maximum number of cells in the covering. Defaults to +#' 8. +#' @param buffer A distance to buffer outside the geography +#' @param interior Use `TRUE` to force the covering inside the geography. +#' @inheritParams s2_cell_is_valid +#' +#' @export +#' +s2_cell_union_normalize <- function(x) { + cpp_s2_cell_union_normalize(as_s2_cell_union(x)) +} + +#' @rdname s2_cell_union_normalize +#' @export +s2_cell_union_contains <- function(x, y) { + if (inherits(y, "s2_cell")) { + recycled <- recycle_common(as_s2_cell_union(x), y) + cpp_s2_cell_union_contains_cell(recycled[[1]], recycled[[2]]) + } else { + cpp_s2_cell_union_contains(as_s2_cell_union(x), as_s2_cell_union(y)) + } +} + +#' @rdname s2_cell_union_normalize +#' @export +s2_cell_union_intersects <- function(x, y) { + cpp_s2_cell_union_intersects(as_s2_cell_union(x), as_s2_cell_union(y)) +} + +#' @rdname s2_cell_union_normalize +#' @export +s2_cell_union_intersection <- function(x, y) { + cpp_s2_cell_union_intersection(as_s2_cell_union(x), as_s2_cell_union(y)) +} + +#' @rdname s2_cell_union_normalize +#' @export +s2_cell_union_union <- function(x, y) { + cpp_s2_cell_union_union(as_s2_cell_union(x), as_s2_cell_union(y)) +} + +#' @rdname s2_cell_union_normalize +#' @export +s2_cell_union_difference <- function(x, y) { + cpp_s2_cell_union_difference(as_s2_cell_union(x), as_s2_cell_union(y)) +} + +#' @rdname s2_cell_union_normalize +#' @export +s2_covering_cell_ids <- function(x, min_level = 0, max_level = 30, + max_cells = 8, buffer = 0, + interior = FALSE, + radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), buffer / radius) + cpp_s2_covering_cell_ids( + recycled[[1]], + min_level, + max_level, + max_cells, + recycled[[2]], + interior + ) +} + +#' @rdname s2_cell_union_normalize +#' @export +s2_covering_cell_ids_agg <- function(x, min_level = 0, max_level = 30, + max_cells = 8, buffer = 0, + interior = FALSE, + radius = s2_earth_radius_meters(), + na.rm = FALSE) { + distance <- as.numeric(buffer / radius) + stopifnot(length(distance) == 1) + if (is.na(distance)) { + return(new_s2_cell_union(list(NULL))) + } + + cpp_s2_covering_cell_ids_agg( + as_s2_geography(x), + min_level, + max_level, + max_cells, + distance, + interior, + na.rm + ) +} diff --git a/R/s2-cell.R b/R/s2-cell.R new file mode 100644 index 0000000..3b6e269 --- /dev/null +++ b/R/s2-cell.R @@ -0,0 +1,351 @@ + +#' Create S2 Cell vectors +#' +#' The S2 cell indexing system forms the basis for spatial indexing +#' in the S2 library. On their own, S2 cells can represent points +#' or areas. As a union, a vector of S2 cells can approximate a +#' line or polygon. These functions allow direct access to the +#' S2 cell indexing system and are designed to have minimal overhead +#' such that looping and recursion have acceptable performance +#' when used within R code. +#' +#' Under the hood, S2 cell vectors are represented in R as vectors +#' of type [double()]. This works because S2 cell identifiers are +#' 64 bits wide, as are `double`s on all systems where R runs (The +#' same trick is used by the bit64 package to represent signed +#' 64-bit integers). As a happy accident, `NA_real_` is not a valid +#' or meaningful cell identifier, so missing value support in the +#' way R users might expect is preserved. It is worth noting that +#' the underlying value of `s2_cell_sentinel()` would normally be +#' considered `NA`; however, as it is meaningful and useful when +#' programming with S2 cells, custom `is.na()` and comparison methods +#' are implemented such that `s2_cell_sentinel()` is greater than +#' all valid S2 cells and not considered missing. Users can and should +#' implement compiled code that uses the underlying bytes of the +#' vector, ensuring that the class of any returned object that should +#' be interpreted in this way is constructed with `new_s2_cell()`. +#' +#' @param x The canonical S2 cell identifier as a character vector. +#' @param ... Passed to methods +#' +#' @return An object of class s2_cell +#' @export +#' +#' @examples +#' s2_cell("4b59a0cd83b5de49") +#' as_s2_cell(s2_lnglat(-64, 45)) +#' as_s2_cell(s2_data_cities("Ottawa")) +#' +s2_cell <- function(x = character()) { + new_s2_cell(cpp_s2_cell_from_string(x)) +} + +#' @rdname s2_cell +#' @export +s2_cell_sentinel <- function() { + cpp_s2_cell_sentinel() +} + +#' @rdname s2_cell +#' @export +s2_cell_invalid <- function() { + new_s2_cell(0) +} + +#' @rdname s2_cell +#' @export +s2_cell_sentinel <- function() { + cpp_s2_cell_sentinel() +} + +#' @rdname s2_cell +#' @export +as_s2_cell <- function(x, ...) { + UseMethod("as_s2_cell") +} + +#' @rdname s2_cell +#' @export +as_s2_cell.s2_cell <- function(x, ...) { + x +} + +#' @rdname s2_cell +#' @export +as_s2_cell.character <- function(x, ...) { + s2_cell(x) +} + +#' @rdname s2_cell +#' @export +as_s2_cell.s2_geography <- function(x, ...) { + cpp_s2_cell_from_lnglat(list(s2_x(x), s2_y(x))) +} + +#' @rdname s2_cell +#' @export +as_s2_cell.wk_xy <- function(x, ...) { + cpp_s2_cell_from_lnglat(as_s2_lnglat(x)) +} + +#' @rdname s2_cell +#' @export +as_s2_cell.integer64 <- function(x, ...) { + storage <- unclass(x) + storage[is.na(x)] <- NA_real_ + new_s2_cell(storage) +} + +#' @rdname s2_cell +#' @export +new_s2_cell <- function(x) { + stopifnot(is.double(x)) + structure(x, class = c("s2_cell", "wk_vctr")) +} + +# registered in zzz.R +as.integer64.s2_cell <- function(x, ...) { + # We store 64-bit integegers the same way bit64 does so we can just set the + # class attribute and propagate NA values in the way that bit64 expects them. + x_is_na <- is.na(x) + class(x) <- "integer64" + x[x_is_na] <- bit64::NA_integer64_ + x +} + +#' @export +as.character.s2_cell <- function(x, ...) { + cpp_s2_cell_to_string(x) +} + +#' @export +format.s2_cell <- function(x, ...) { + format(as.character(x), quote = FALSE, ...) +} + +#' @export +as.list.s2_cell <- function(x, ...) { + lapply(NextMethod(), new_s2_cell) +} + +#' @export +`[<-.s2_cell` <- function(x, i, value) { + replacement <- as_s2_cell(value) + x <- unclass(x) + x[i] <- replacement + new_s2_cell(x) +} + +#' @export +`[[<-.s2_cell` <- function(x, i, value) { + x[i] <- value + x +} + +#' @export +unique.s2_cell <- function(x, ...) { + cpp_s2_cell_unique(x) +} + +#' @export +sort.s2_cell <- function(x, decreasing = FALSE, ...) { + cpp_s2_cell_sort(x, decreasing) +} + +#' @export +is.na.s2_cell <- function(x) { + cpp_s2_cell_is_na(x) +} + +#' @export +is.numeric.s2_cell <- function(x, ...) { + FALSE +} + +#' @export +Ops.s2_cell <- function(e1, e2) { + switch( + .Generic, + "==" = cpp_s2_cell_eq(e1, e2), + "!=" = cpp_s2_cell_neq(e1, e2), + "<" = cpp_s2_cell_lt(e1, e2), + "<=" = cpp_s2_cell_lte(e1, e2), + ">=" = cpp_s2_cell_gte(e1, e2), + ">" = cpp_s2_cell_gt(e1, e2), + stop("Arithmetic operations are not meaningful for type 's2_cell'", call. = FALSE) + ) +} + +#' @export +Math.s2_cell <- function(x, ...) { + switch( + .Generic, + "cummax" = cpp_s2_cell_cummax(x), + "cummin" = cpp_s2_cell_cummin(x), + stop("Arithmetic operations are not meaningful for type 's2_cell'", call. = FALSE) + ) +} + +#' @export +Summary.s2_cell <- function(x, ..., na.rm = FALSE) { + switch( + .Generic, + "min" = cpp_s2_cell_range(x, na.rm)[1], + "max" = cpp_s2_cell_range(x, na.rm)[2], + "range" = cpp_s2_cell_range(x, na.rm), + stop("Arithmetic operations are not meaningful for type 's2_cell'", call. = FALSE) + ) +} + +#' S2 cell operators +#' +#' @param x,y An [s2_cell()] vector +#' @param level An integer between 0 and 30, inclusive. +#' @param k An integer between 0 and 3 +#' @param radius The radius to use (e.g., [s2_earth_radius_meters()]) +#' @param na.rm Remove NAs prior to computing aggregate? +#' @export +#' +s2_cell_is_valid <- function(x) { + cpp_s2_cell_is_valid(x) +} + +# exporters + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_debug_string <- function(x) { + cpp_s2_cell_debug_string(x) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_to_lnglat <- function(x) { + lnglat <- cpp_s2_cell_to_lnglat(x) + s2_lnglat(lnglat[[1]], lnglat[[2]]) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_center <- function(x) { + cpp_s2_cell_center(x) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_boundary <- function(x) { + s2_boundary(cpp_s2_cell_polygon(x)) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_polygon <- function(x) { + cpp_s2_cell_polygon(x) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_vertex <- function(x, k) { + recycled <- recycle_common(x, k) + cpp_s2_cell_vertex(recycled[[1]], recycled[[2]]) +} + +# accessors + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_level <- function(x) { + cpp_s2_cell_level(x) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_is_leaf <- function(x) { + s2_cell_level(x) == 30L +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_is_face <- function(x) { + s2_cell_level(x) == 0L +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_area <- function(x, radius = s2_earth_radius_meters()) { + cpp_s2_cell_area(x) * radius ^ 2 +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_area_approx <- function(x, radius = s2_earth_radius_meters()) { + cpp_s2_cell_area_approx(x) * radius ^ 2 +} + +# transversers + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_parent <- function(x, level = -1L) { + recycled <- recycle_common(x, level) + cpp_s2_cell_parent(recycled[[1]], recycled[[2]]) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_child <- function(x, k) { + recycled <- recycle_common(x, k) + cpp_s2_cell_child(recycled[[1]], recycled[[2]]) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_edge_neighbour <- function(x, k) { + recycled <- recycle_common(x, k) + cpp_s2_cell_edge_neighbour(recycled[[1]], recycled[[2]]) +} + +# binary operators + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_contains <- function(x, y) { + cpp_s2_cell_contains(x, y) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_distance <- function(x, y, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(x, y, radius) + cpp_s2_cell_distance(recycled[[1]], recycled[[2]]) * radius +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_max_distance <- function(x, y, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(x, y, radius) + cpp_s2_cell_max_distance(recycled[[1]], recycled[[2]]) * radius +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_may_intersect <- function(x, y) { + cpp_s2_cell_may_intersect(x, y) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_common_ancestor_level <- function(x, y) { + cpp_s2_cell_common_ancestor_level(x, y) +} + +#' @rdname s2_cell_is_valid +#' @export +s2_cell_common_ancestor_level_agg <- function(x, na.rm = FALSE) { + x_na <- is.na(x) + if (any(x_na) && !na.rm) { + return(NA_integer_) + } + + cpp_s2_cell_common_ancestor_level_agg(x[!x_na]) +} diff --git a/R/s2-constructors-formatters.R b/R/s2-constructors-formatters.R new file mode 100644 index 0000000..e76501d --- /dev/null +++ b/R/s2-constructors-formatters.R @@ -0,0 +1,210 @@ + +#' Create and Format Geography Vectors +#' +#' These functions create and export [geography vectors][as_s2_geography]. +#' Unlike the BigQuery geography constructors, these functions do not sanitize +#' invalid or redundant input using [s2_union()]. Note that when creating polygons +#' using [s2_make_polygon()], rings can be open or closed. +#' +#' @inheritParams s2_is_collection +#' @inheritParams as_s2_geography +#' @param precision The number of significant digits to export when +#' writing well-known text. If `trim = FALSE`, the number of +#' digits after the decimal place. +#' @param trim Should trailing zeroes be included after the decimal place? +#' @param endian The endian-ness of the well-known binary. See [wk::wkb_translate_wkb()]. +#' @param longitude,latitude Vectors of latitude and longitude +#' @param wkt_string Well-known text +#' @param wkb_bytes A `list()` of `raw()` +#' @param planar Use `TRUE` to force planar edges in import or export. +#' @param tessellate_tol_m The maximum number of meters to that a point must +#' be moved to satisfy the planar edge constraint. +#' @param feature_id,ring_id Vectors for which a change in +#' sequential values indicates a new feature or ring. Use [factor()] +#' to convert from a character vector. +#' +#' @export +#' +#' @seealso +#' See [as_s2_geography()] for other ways to construct geography vectors. +#' +#' BigQuery's geography function reference: +#' +#' - [ST_GEOGPOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogpoint) +#' - [ST_MAKELINE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makeline) +#' - [ST_MAKEPOLYGON](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makepolygon) +#' - [ST_GEOGFROMTEXT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromtext) +#' - [ST_GEOGFROMWKB](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromwkb) +#' - [ST_ASTEXT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_astext) +#' - [ST_ASBINARY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_asbinary) +#' +#' @examples +#' # create point geographies using coordinate values: +#' s2_geog_point(-64, 45) +#' +#' # create line geographies using coordinate values: +#' s2_make_line(c(-64, 8), c(45, 71)) +#' +#' # optionally, separate features using feature_id: +#' s2_make_line( +#' c(-64, 8, -27, -27), c(45, 71, 0, 45), +#' feature_id = c(1, 1, 2, 2) +#' ) +#' +#' # create polygon geographies using coordinate values: +#' # (rings can be open or closed) +#' s2_make_polygon(c(-45, 8, 0), c(64, 71, 90)) +#' +#' # optionally, separate rings and/or features using +#' # ring_id and/or feature_id +#' s2_make_polygon( +#' c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45), +#' c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30), +#' feature_id = c(rep(1, 8), rep(2, 3)), +#' ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1) +#' ) +#' +#' # import and export well-known text +#' (geog <- s2_geog_from_text("POINT (-64 45)")) +#' s2_as_text(geog) +#' +#' # import and export well-known binary +#' (geog <- s2_geog_from_wkb(wk::as_wkb("POINT (-64 45)"))) +#' s2_as_binary(geog) +#' +#' # import geometry from planar space +#' s2_geog_from_text( +#' "POLYGON ((0 0, 1 0, 0 1, 0 0))", +#' planar = TRUE, +#' tessellate_tol_m = 1 +#' ) +#' +#' # export geographies into planar space +#' geog <- s2_make_polygon(c(179, -179, 179), c(10, 10, 11)) +#' s2_as_text(geog, planar = TRUE) +#' +#' # polygons containing a pole need an extra step +#' geog <- s2_data_countries("Antarctica") +#' geom <- s2_as_text( +#' s2_intersection(geog, s2_world_plate_carree()), +#' planar = TRUE +#' ) +#' +s2_geog_point <- function(longitude, latitude) { + wk::wk_handle(wk::xy(longitude, latitude), s2_geography_writer()) +} + +#' @rdname s2_geog_point +#' @export +s2_make_line <- function(longitude, latitude, feature_id = 1L) { + wk::wk_handle( + wk::xy(longitude, latitude), + wk::wk_linestring_filter( + s2_geography_writer(), + feature_id = as.integer(feature_id) + ) + ) +} + +#' @rdname s2_geog_point +#' @export +s2_make_polygon <- function(longitude, latitude, feature_id = 1L, ring_id = 1L, + oriented = FALSE, check = TRUE) { + wk::wk_handle( + wk::xy(longitude, latitude), + wk::wk_polygon_filter( + s2_geography_writer(oriented = oriented, check = check), + feature_id = as.integer(feature_id), + ring_id = as.integer(ring_id) + ) + ) +} + +#' @rdname s2_geog_point +#' @export +s2_geog_from_text <- function(wkt_string, oriented = FALSE, check = TRUE, + planar = FALSE, + tessellate_tol_m = s2_tessellate_tol_default()) { + attributes(wkt_string) <- NULL + wkt <- wk::new_wk_wkt(wkt_string, geodesic = TRUE) + wk::validate_wk_wkt(wkt) + + wk::wk_handle( + wkt, + s2_geography_writer( + oriented = oriented, + check = check, + tessellate_tol = if (planar) { + tessellate_tol_m / s2_earth_radius_meters() + } else { + Inf + } + ) + ) +} + +#' @rdname s2_geog_point +#' @export +s2_geog_from_wkb <- function(wkb_bytes, oriented = FALSE, check = TRUE, + planar = FALSE, + tessellate_tol_m = s2_tessellate_tol_default()) { + attributes(wkb_bytes) <- NULL + wkb <- wk::new_wk_wkb(wkb_bytes) + wk::validate_wk_wkb(wkb) + wk::wk_handle( + wkb, + s2_geography_writer( + oriented = oriented, + check = check, + tessellate_tol = if (planar) { + tessellate_tol_m / s2_earth_radius_meters() + } else { + Inf + } + ) + ) +} + +#' @rdname s2_geog_point +#' @export +s2_as_text <- function(x, precision = 16, trim = TRUE, + planar = FALSE, + tessellate_tol_m = s2_tessellate_tol_default()) { + wkt <- wk::wk_handle( + as_s2_geography(x), + wk::wkt_writer(precision = precision, trim = trim), + s2_tessellate_tol = if (planar) { + tessellate_tol_m / s2_earth_radius_meters() + } else { + Inf + } + ) + + attributes(wkt) <- NULL + wkt +} + +#' @rdname s2_geog_point +#' @export +s2_as_binary <- function(x, endian = wk::wk_platform_endian(), + planar = FALSE, + tessellate_tol_m = s2_tessellate_tol_default()) { + structure( + wk::wk_handle( + as_s2_geography(x), + wk::wkb_writer(endian = endian), + s2_tessellate_tol = if (planar) { + tessellate_tol_m / s2_earth_radius_meters() + } else { + Inf + } + ), + class = "blob" + ) +} + +#' @rdname s2_geog_point +#' @export +s2_tessellate_tol_default <- function() { + 100 +} diff --git a/R/s2-earth.R b/R/s2-earth.R new file mode 100644 index 0000000..4814130 --- /dev/null +++ b/R/s2-earth.R @@ -0,0 +1,23 @@ + +#' Earth Constants +#' +#' According to Yoder (1995), the radius of the earth is +#' 6371.01 km. These functions are used to set the +#' default radis for functions that return a distance +#' or accept a distance as input +#' (e.g., [s2_distance()] and [s2_dwithin()]). +#' +#' @export +#' +#' @references +#' Yoder, C.F. 1995. "Astrometric and Geodetic Properties of Earth and the +#' Solar System" in Global Earth Physics, A Handbook of Physical Constants, +#' AGU Reference Shelf 1, American Geophysical Union, Table 2. +#' \doi{10.1029/RF001p0001} +#' +#' @examples +#' s2_earth_radius_meters() +#' +s2_earth_radius_meters <- function() { + 6371.01 * 1000 +} diff --git a/R/s2-geography.R b/R/s2-geography.R new file mode 100644 index 0000000..df1ea5b --- /dev/null +++ b/R/s2-geography.R @@ -0,0 +1,215 @@ + +#' Create an S2 Geography Vector +#' +#' Geography vectors are arrays of points, lines, polygons, and/or collections +#' of these. Geography vectors assume coordinates are longitude and latitude +#' on a perfect sphere. +#' +#' The coercion function [as_s2_geography()] is used to wrap the input +#' of most functions in the s2 package so that you can use other objects with +#' an unambiguious interpretation as a geography vector. Geography vectors +#' have a minimal [vctrs][vctrs::vctrs-package] implementation, so you can +#' use these objects in tibble, dplyr, and other packages that use the vctrs +#' framework. +#' +#' @param x An object that can be converted to an s2_geography vector +#' @param oriented TRUE if polygon ring directions are known to be correct +#' (i.e., exterior rings are defined counter clockwise and interior +#' rings are defined clockwise). +#' @param check Use `check = FALSE` to skip error on invalid geometries +#' @param ... Unused +#' +#' @return An object with class s2_geography +#' @export +#' +#' @seealso +#' [s2_geog_from_wkb()], [s2_geog_from_text()], [s2_geog_point()], +#' [s2_make_line()], [s2_make_polygon()] for other ways to +#' create geography vectors, and [s2_as_binary()] and [s2_as_text()] +#' for other ways to export them. +#' +as_s2_geography <- function(x, ...) { + UseMethod("as_s2_geography") +} + +#' @rdname as_s2_geography +#' @export +s2_geography <- function() { + new_s2_geography(list()) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.s2_geography <- function(x, ...) { + x +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.wk_xy <- function(x, ...) { + x <- as_s2_lnglat(x) + df <- unclass(x) + s2_geog_point(df[[1]], df[[2]]) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.wk_wkb <- function(x, ..., oriented = FALSE, check = TRUE) { + if (identical(wk::wk_is_geodesic(x), FALSE)) { + + # points and an empty vector are OK and shouldn't trigger an error + meta <- wk::wk_meta(x) + if (!all(meta$geometry_type %in% c(1, 4, NA), na.rm = TRUE)) { + stop( + paste0( + "Can't create s2_geography from Cartesian wkb().\n", + "Use `wk_set_geodesic(x, TRUE)` to assert that edges can be\n", + "interpolated along the sphere." + ), + call. = FALSE + ) + } + } + + wk::wk_handle( + x, + s2_geography_writer(oriented = oriented, check = check) + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.WKB <- function(x, ..., oriented = FALSE, check = TRUE) { + s2_geog_from_wkb(x, oriented = oriented, check = check) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.blob <- function(x, ..., oriented = FALSE, check = TRUE) { + s2_geog_from_wkb(x, oriented = oriented, check = check) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.wk_wkt <- function(x, ..., oriented = FALSE, check = TRUE) { + if (identical(wk::wk_is_geodesic(x), FALSE)) { + + # points and an empty vector are OK and shouldn't trigger an error + meta <- wk::wk_meta(x) + if (!all(meta$geometry_type %in% c(1, 4, NA), na.rm = TRUE)) { + stop( + paste0( + "Can't create s2_geography from Cartesian wkt().\n", + "Use `wk_set_geodesic(x, TRUE)` to assert that edges can be\n", + "interpolated along the sphere." + ), + call. = FALSE + ) + } + } + + wk::wk_handle( + x, + s2_geography_writer(oriented = oriented, check = check) + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.character <- function(x, ..., oriented = FALSE, check = TRUE) { + s2_geog_from_text(x, oriented = oriented, check = check) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.logical <- function(x, ...) { + stopifnot(isTRUE(x)) + new_s2_geography(s2_geography_full(TRUE)) +} + +#' @importFrom wk as_wkb +#' @rdname as_s2_geography +#' @export +as_wkb.s2_geography <- function(x, ...) { + wkb <- wk::wk_handle(x, wk::wkb_writer()) + wk::wk_is_geodesic(wkb) <- TRUE + wk::wk_crs(wkb) <- wk::wk_crs_longlat() + wkb +} + +#' @importFrom wk as_wkt +#' @rdname as_s2_geography +#' @export +as_wkt.s2_geography <- function(x, ...) { + wkt <- wk::wk_handle(x, wk::wkt_writer()) + wk::wk_is_geodesic(wkt) <- TRUE + wk::wk_crs(wkt) <- wk::wk_crs_longlat() + wkt +} + +#' @importFrom wk wk_crs +#' @export +wk_crs.s2_geography <- function(x) { + wk::wk_crs_longlat() +} + +#' @importFrom wk wk_set_crs +#' @export +wk_set_crs.s2_geography <- function(x, crs) { + if (!wk::wk_crs_equal(crs, wk::wk_crs(x))) { + warning("Setting the crs of s2_geography() is not supported") + } + + x +} + +#' @importFrom wk wk_is_geodesic +#' @export +wk_is_geodesic.s2_geography <- function(x) { + TRUE +} + +#' @importFrom wk wk_set_geodesic +#' @export +wk_set_geodesic.s2_geography <- function(x, geodesic) { + if (!isTRUE(geodesic)) { + stop("Can't set geodesic of s2_geography to FALSE") + } + + x +} + +new_s2_geography <- function(x) { + structure(x, class = c("s2_geography", "wk_vctr")) +} + +#' @export +is.na.s2_geography <- function(x) { + cpp_s2_geography_is_na(x) +} + +#' @export +`[<-.s2_geography` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_geography(value) + new_s2_geography(x) +} + +#' @export +`[[<-.s2_geography` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_geography(value) + new_s2_geography(x) +} + +#' @export +format.s2_geography <- function(x, ..., max_coords = 5, precision = 9, trim = TRUE) { + wk::wk_format(x, precision = precision, max_coords = max_coords, trim = trim) +} + +# this is what gets called by the RStudio viewer, for which +# format() is best suited (s2_as_text() is more explicit for WKT output) +#' @export +as.character.s2_geography <- function(x, ..., max_coords = 5, precision = 9, trim = TRUE) { + format(x, ..., max_coords = max_coords, precision = precision, trim = trim) +} diff --git a/R/s2-lnglat.R b/R/s2-lnglat.R new file mode 100644 index 0000000..6f39ce2 --- /dev/null +++ b/R/s2-lnglat.R @@ -0,0 +1,60 @@ + +#' Create an S2 LngLat Vector +#' +#' This class represents a latitude and longitude on the Earth's surface. +#' Most calculations in S2 convert this to a [as_s2_point()], which is a +#' unit vector representation of this value. +#' +#' @param lat,lng Vectors of latitude and longitude values in degrees. +#' @param x A [s2_lnglat()] vector or an object that can be coerced to one. +#' @param ... Unused +#' +#' @return An object with class s2_lnglat +#' @export +#' +#' @examples +#' s2_lnglat(45, -64) # Halifax, Nova Scotia! +#' as.data.frame(s2_lnglat(45, -64)) +#' +s2_lnglat <- function(lng, lat) { + wk::xy(lng, lat, crs = wk::wk_crs_longlat()) +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat <- function(x, ...) { + UseMethod("as_s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.default <- function(x, ...) { + as_s2_lnglat(wk::as_xy(x)) +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.wk_xy <- function(x, ...) { + wk::wk_set_crs( + wk::as_xy(x, dims = c("x", "y")), + wk::wk_crs_longlat() + ) +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.wk_xyz <- function(x, ...) { + if (wk::wk_crs_equal(wk::wk_crs(x), s2_point_crs())) { + wk::new_wk_xy( + s2_lnglat_from_s2_point(x), + crs = wk::wk_crs_longlat() + ) + } else { + NextMethod() + } +} + +#' @export +as_s2_lnglat.character <- function(x, ...) { + as_s2_lnglat(wk::new_wk_wkt(x)) +} diff --git a/R/s2-matrix.R b/R/s2-matrix.R new file mode 100644 index 0000000..48ab320 --- /dev/null +++ b/R/s2-matrix.R @@ -0,0 +1,205 @@ + +#' Matrix Functions +#' +#' These functions are similar to accessors and predicates, but instead of +#' recycling `x` and `y` to a common length and returning a vector of that +#' length, these functions return a vector of length `x` with each element +#' `i` containing information about how the entire vector `y` relates to +#' the feature at `x[i]`. +#' +#' @inheritParams s2_is_collection +#' @inheritParams s2_contains +#' @param x,y Geography vectors, coerced using [as_s2_geography()]. +#' `x` is considered the source, where as `y` is considered the target. +#' @param k The number of closest edges to consider when searching. Note +#' that in S2 a point is also considered an edge. +#' @param min_distance The minimum distance to consider when searching for +#' edges. This filter is applied after the search is complete (i.e., +#' may cause fewer than `k` values to be returned). +#' @param max_distance The maximum distance to consider when searching for +#' edges. This filter is applied before the search. +#' @param max_edges_per_cell For [s2_may_intersect_matrix()], +#' this values controls the nature of the index on `y`, with higher values +#' leading to coarser index. Values should be between 10 and 50; the default +#' of 50 is adequate for most use cases, but for specialized operations users +#' may wish to use a lower value to increase performance. +#' @param max_feature_cells For [s2_may_intersect_matrix()], this value +#' controls the approximation of `x` used to identify potential intersections +#' on `y`. The default value of 4 gives the best performance for most operations, +#' but for specialized operations users may wish to use a higher value to increase +#' performance. +#' +#' @return A vector of length `x`. +#' @export +#' +#' @seealso +#' See pairwise predicate functions (e.g., [s2_intersects()]). +#' +#' @examples +#' city_names <- c("Vatican City", "San Marino", "Luxembourg") +#' cities <- s2_data_cities(city_names) +#' country_names <- s2_data_tbl_countries$name +#' countries <- s2_data_countries() +#' +#' # closest feature returns y indices of the closest feature +#' # for each feature in x +#' country_names[s2_closest_feature(cities, countries)] +#' +#' # farthest feature returns y indices of the farthest feature +#' # for each feature in x +#' country_names[s2_farthest_feature(cities, countries)] +#' +#' # use s2_closest_edges() to find the k-nearest neighbours +#' nearest <- s2_closest_edges(cities, cities, k = 2, min_distance = 0) +#' city_names +#' city_names[unlist(nearest)] +#' +#' # predicate matrices +#' country_names[s2_intersects_matrix(cities, countries)[[1]]] +#' +#' # distance matrices +#' s2_distance_matrix(cities, cities) +#' s2_max_distance_matrix(cities, countries[1:4]) +#' +s2_closest_feature <- function(x, y) { + cpp_s2_closest_feature(as_s2_geography(x), as_s2_geography(y)) +} + +#' @rdname s2_closest_feature +#' @export +s2_closest_edges <- function(x, y, k, min_distance = -1, max_distance = Inf, + radius = s2_earth_radius_meters()) { + stopifnot(k >= 1) + cpp_s2_closest_edges( + as_s2_geography(x), + as_s2_geography(y), + k, + min_distance / radius, + max_distance / radius + ) +} + +#' @rdname s2_closest_feature +#' @export +s2_farthest_feature <- function(x, y) { + cpp_s2_farthest_feature(as_s2_geography(x), as_s2_geography(y)) +} + +#' @rdname s2_closest_feature +#' @export +s2_distance_matrix <- function(x, y, radius = s2_earth_radius_meters()) { + cpp_s2_distance_matrix(as_s2_geography(x), as_s2_geography(y)) * radius +} + +#' @rdname s2_closest_feature +#' @export +s2_max_distance_matrix <- function(x, y, radius = s2_earth_radius_meters()) { + cpp_s2_max_distance_matrix(as_s2_geography(x), as_s2_geography(y)) * radius +} + +#' @rdname s2_closest_feature +#' @export +s2_contains_matrix <- function(x, y, options = s2_options(model = "open")) { + cpp_s2_contains_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_within_matrix <- function(x, y, options = s2_options(model = "open")) { + cpp_s2_within_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_covers_matrix <- function(x, y, options = s2_options(model = "closed")) { + cpp_s2_contains_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_covered_by_matrix <- function(x, y, options = s2_options(model = "closed")) { + cpp_s2_within_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_intersects_matrix <- function(x, y, options = s2_options()) { + cpp_s2_intersects_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_disjoint_matrix <- function(x, y, options = s2_options()) { + # disjoint is the odd one out, in that it requires a negation of intersects + # this is inconvenient to do on the C++ level, and is easier to maintain + # with setdiff() here (unless somebody complains that this is slow) + intersection <- cpp_s2_intersects_matrix(as_s2_geography(x), as_s2_geography(y), options) + Map(setdiff, list(seq_along(y)), intersection) +} + +#' @rdname s2_closest_feature +#' @export +s2_equals_matrix <- function(x, y, options = s2_options()) { + cpp_s2_equals_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_touches_matrix <- function(x, y, options = s2_options()) { + cpp_s2_touches_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_dwithin_matrix <- function(x, y, distance, radius = s2_earth_radius_meters()) { + cpp_s2_dwithin_matrix(as_s2_geography(x), as_s2_geography(y), distance / radius) +} + +#' @rdname s2_closest_feature +#' @export +s2_may_intersect_matrix <- function(x, y, max_edges_per_cell = 50, max_feature_cells = 4) { + cpp_s2_may_intersect_matrix( + as_s2_geography(x), as_s2_geography(y), + max_edges_per_cell, max_feature_cells, + s2_options() + ) +} + +# ------- for testing, non-indexed versions of matrix operators ------- + +s2_contains_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_contains_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_within_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_within_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_covers_matrix_brute_force <- function(x, y, options = s2_options(model = "closed")) { + cpp_s2_contains_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_covered_by_matrix_brute_force <- function(x, y, options = s2_options(model = "closed")) { + cpp_s2_within_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_intersects_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_intersects_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_disjoint_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_disjoint_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_equals_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_equals_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_dwithin_matrix_brute_force <- function(x, y, distance, + radius = s2_earth_radius_meters()) { + cpp_s2_dwithin_matrix_brute_force( + as_s2_geography(x), + as_s2_geography(y), + distance / radius + ) +} diff --git a/R/s2-options.R b/R/s2-options.R new file mode 100644 index 0000000..f8b3672 --- /dev/null +++ b/R/s2-options.R @@ -0,0 +1,147 @@ + +#' Geography Operation Options +#' +#' These functions specify defaults for options used to perform operations +#' and construct geometries. These are used in predicates (e.g., [s2_intersects()]), +#' and boolean operations (e.g., [s2_intersection()]) to specify the model for +#' containment and how new geometries should be constructed. +#' +#' @param model One of 'open', 'semi-open' (default for polygons), +#' or 'closed' (default for polylines). See section 'Model' +#' @param snap Use `s2_snap_identity()`, `s2_snap_distance()`, `s2_snap_level()`, +#' or `s2_snap_precision()` to specify how or if coordinate rounding should +#' occur. +#' @param snap_radius As opposed to the snap function, which specifies +#' the maximum distance a vertex should move, the snap radius (in radians) sets +#' the minimum distance between vertices of the output that don't cause vertices +#' to move more than the distance specified by the snap function. This can be used +#' to simplify the result of a boolean operation. Use -1 to specify that any +#' minimum distance is acceptable. +#' @param duplicate_edges Use `TRUE` to keep duplicate edges (e.g., duplicate +#' points). +#' @param edge_type One of 'directed' (default) or 'undirected'. +#' @param polyline_type One of 'path' (default) or 'walk'. If 'walk', +#' polylines that backtrack are preserved. +#' @param polyline_sibling_pairs One of 'discard' (default) or 'keep'. +#' @param simplify_edge_chains Use `TRUE` to remove vertices that are within +#' `snap_radius` of the original vertex. +#' @param split_crossing_edges Use `TRUE` to split crossing polyline edges +#' when creating geometries. +#' @param idempotent Use `FALSE` to apply snap even if snapping is not necessary +#' to satisfy vertex constraints. +#' @param validate Use `TRUE` to validate the result from the builder. +#' @param level A value from 0 to 30 corresponding to the cell level +#' at which snapping should occur. +#' @param distance A distance (in radians) denoting the maximum +#' distance a vertex should move in the snapping process. +#' @param precision A number by which coordinates should be multiplied +#' before being rounded. Rounded to the nearest exponent of 10. +#' @param dimensions A combination of 'point', 'polyline', and/or 'polygon' +#' that can used to constrain the output of [s2_rebuild()] or a +#' boolean operation. +#' +#' @section Model: +#' The geometry model indicates whether or not a geometry includes its boundaries. +#' Boundaries of line geometries are its end points. +#' OPEN geometries do not contain their boundary (`model = "open"`); CLOSED +#' geometries (`model = "closed"`) contain their boundary; SEMI-OPEN geometries +#' (`model = "semi-open"`) contain half of their boundaries, such that when two polygons +#' do not overlap or two lines do not cross, no point exist that belong to +#' more than one of the geometries. (This latter form, half-closed, is +#' not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on +#' which that is based). The default values for [s2_contains()] (open) +#' and covers/covered_by (closed) correspond to the SFA standard specification +#' of these operators. +#' +#' @export +#' +#' @examples +#' # use s2_options() to specify containment models, snap level +#' # layer creation options, and builder options +#' s2_options(model = "closed", snap = s2_snap_level(30)) +#' +s2_options <- function(model = NULL, + snap = s2_snap_identity(), + snap_radius = -1, + duplicate_edges = FALSE, + edge_type = "directed", + validate = FALSE, + polyline_type = "path", + polyline_sibling_pairs = "keep", + simplify_edge_chains = FALSE, + split_crossing_edges = FALSE, + idempotent = FALSE, + dimensions = c("point", "polyline", "polygon")) { + # check snap radius (passing in a huge snap radius can cause problems) + if (snap_radius > 3) { + stop( + "Snap radius is too large. Did you pass in a snap radius in meters instead of radians?", + call. = FALSE + ) + } + + structure( + list( + # model needs to be "unset" by default because there are differences in polygon + # and polyline handling by default that are good defaults to preserve + model = if (is.null(model)) -1 else match_option(model[1], c("open", "semi-open", "closed"), "model"), + snap = snap, + snap_radius = snap_radius, + duplicate_edges = duplicate_edges, + edge_type = match_option(edge_type[1], c("directed", "undirected"), "edge_type"), + validate = validate, + polyline_type = match_option(polyline_type[1], c("path", "walk"), "polyline_type"), + polyline_sibling_pairs = match_option( + polyline_sibling_pairs, + c("discard", "keep"), + "polyline_sibling_pairs" + ), + simplify_edge_chains = simplify_edge_chains, + split_crossing_edges = split_crossing_edges, + idempotent = idempotent, + dimensions = match_option(dimensions, c("point", "polyline", "polygon"), "dimensions") + ), + class = "s2_options" + ) +} + +#' @rdname s2_options +#' @export +s2_snap_identity <- function() { + structure(list(), class = "snap_identity") +} + +#' @rdname s2_options +#' @export +s2_snap_level <- function(level) { + if (level > 30) { + stop("`level` must be an intger between 1 and 30", call. = FALSE) + } + + structure(list(level = level), class = "snap_level") +} + +#' @rdname s2_options +#' @export +s2_snap_precision <- function(precision) { + structure(list(exponent = round(log10(precision))), class = "snap_precision") +} + +#' @rdname s2_options +#' @export +s2_snap_distance <- function(distance) { + structure(list(distance = distance), class = "snap_distance") +} + + +match_option <- function(x, options, arg) { + result <- match(x, options) + if (any(is.na(result))) { + stop( + sprintf("`%s` must be one of %s", arg, paste0('"', options, '"', collapse = ", ")), + call. = FALSE + ) + } + + result +} diff --git a/R/s2-package.R b/R/s2-package.R new file mode 100644 index 0000000..6c757a5 --- /dev/null +++ b/R/s2-package.R @@ -0,0 +1,10 @@ +#' @keywords internal +"_PACKAGE" + +# The following block is used by usethis to automatically manage +# roxygen namespace tags. Modify with care! +## usethis namespace: start +#' @useDynLib s2, .registration = TRUE +#' @importFrom Rcpp sourceCpp +## usethis namespace: end +NULL diff --git a/R/s2-point.R b/R/s2-point.R new file mode 100644 index 0000000..26e8adf --- /dev/null +++ b/R/s2-point.R @@ -0,0 +1,68 @@ + +#' Create an S2 Point Vector +#' +#' In S2 terminology, a "point" is a 3-dimensional unit vector representation +#' of an [s2_point()]. Internally, all s2 objects are stored as +#' 3-dimensional unit vectors. +#' +#' @param x,y,z Vectors of latitude and longitude values in degrees. +#' @param ... Unused +#' +#' @return An object with class s2_point +#' @export +#' +#' @examples +#' point <- s2_lnglat(-64, 45) # Halifax, Nova Scotia! +#' as_s2_point(point) +#' as.data.frame(as_s2_point(point)) +#' +s2_point <- function(x, y, z) { + wk::xyz(x, y, z, crs = s2_point_crs()) +} + +#' @rdname s2_point +#' @export +s2_point_crs <- function() { + structure(list(), class = "s2_point_crs") +} + +#' @export +format.s2_point_crs <- function(x, ...) { + "s2_point_crs" +} + +#' @rdname s2_point +#' @export +as_s2_point <- function(x, ...) { + UseMethod("as_s2_point") +} + +#' @rdname s2_point +#' @export +as_s2_point.default <- function(x, ...) { + as_s2_point(wk::as_xy(x)) +} + +#' @rdname s2_point +#' @export +as_s2_point.wk_xy <- function(x, ...) { + stopifnot(wk::wk_crs_equal(wk::wk_crs(x), wk::wk_crs_longlat())) + wk::new_wk_xyz( + s2_point_from_s2_lnglat(x), + crs = s2_point_crs() + ) +} + +#' @rdname s2_point +#' @export +as_s2_point.wk_xyz <- function(x, ...) { + wk::wk_set_crs( + wk::as_xy(x, dims = c("x", "y", "z")), + s2_point_crs() + ) +} + +#' @export +as_s2_point.character <- function(x, ...) { + as_s2_point(wk::new_wk_wkt(x)) +} diff --git a/R/s2-predicates.R b/R/s2-predicates.R new file mode 100644 index 0000000..26a5738 --- /dev/null +++ b/R/s2-predicates.R @@ -0,0 +1,178 @@ + +#' S2 Geography Predicates +#' +#' These functions operate two geography vectors (pairwise), and return +#' a logical vector. +#' +#' @inheritParams s2_is_collection +#' @inheritParams s2_boundary +#' @param distance A distance on the surface of the earth in the same units +#' as `radius`. +#' @param lng1,lat1,lng2,lat2 A latitude/longitude range +#' @param detail The number of points with which to approximate +#' non-geodesic edges. +#' +#' @inheritSection s2_options Model +#' +#' @export +#' +#' @seealso +#' Matrix versions of these predicates (e.g., [s2_intersects_matrix()]). +#' +#' BigQuery's geography function reference: +#' +#' - [ST_CONTAINS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_contains) +#' - [ST_COVEREDBY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_coveredby) +#' - [ST_COVERS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_covers) +#' - [ST_DISJOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_disjoint) +#' - [ST_EQUALS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_equals) +#' - [ST_INTERSECTS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersects) +#' - [ST_INTERSECTSBOX](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersectsbox) +#' - [ST_TOUCHES](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_touches) +#' - [ST_WITHIN](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_within) +#' - [ST_DWITHIN](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dwithin) +#' +#' @examples +#' s2_contains( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_within( +#' c("POINT (5 5)", "POINT (-1 1)"), +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))" +#' ) +#' +#' s2_covered_by( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_covers( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_disjoint( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_intersects( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_equals( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0))", +#' "POLYGON ((-1 -1, 10 0, 10 10, 0 10, -1 -1))" +#' ) +#' ) +#' +#' s2_intersects( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_intersects_box( +#' c("POINT (5 5)", "POINT (-1 1)"), +#' 0, 0, 10, 10 +#' ) +#' +#' s2_touches( +#' "POLYGON ((0 0, 0 1, 1 1, 0 0))", +#' c("POINT (0 0)", "POINT (0.5 0.75)", "POINT (0 0.5)") +#' ) +#' +#' s2_dwithin( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)"), +#' 0 # distance in meters +#' ) +#' +#' s2_dwithin( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)"), +#' 1e6 # distance in meters +#' ) +#' +s2_contains <- function(x, y, options = s2_options(model = "open")) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_contains(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_within <- function(x, y, options = s2_options(model = "open")) { + s2_contains(y, x, options) +} + +#' @rdname s2_contains +#' @export +s2_covered_by <- function(x, y, options = s2_options(model = "closed")) { + s2_covers(y, x, options) +} + +#' @rdname s2_contains +#' @export +s2_covers <- function(x, y, options = s2_options(model = "closed")) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_contains(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_disjoint <- function(x, y, options = s2_options()) { + !s2_intersects(x, y, options) +} + +#' @rdname s2_contains +#' @export +s2_intersects <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_intersects(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_equals <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_equals(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_intersects_box <- function(x, lng1, lat1, lng2, lat2, detail = 1000, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), lng1, lat1, lng2, lat2, detail) + cpp_s2_intersects_box( + recycled[[1]], + recycled[[2]], recycled[[3]], + recycled[[4]], recycled[[5]], + detail = recycled[[6]], + s2options = options + ) +} + +#' @rdname s2_contains +#' @export +s2_touches <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_touches(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_dwithin <- function(x, y, distance, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), distance / radius) + cpp_s2_dwithin(recycled[[1]], recycled[[2]], recycled[[3]]) +} + +#' @rdname s2_contains +#' @export +s2_prepared_dwithin <- function(x, y, distance, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), distance / radius) + cpp_s2_prepared_dwithin(recycled[[1]], recycled[[2]], recycled[[3]]) +} diff --git a/R/s2-transformers.R b/R/s2-transformers.R new file mode 100644 index 0000000..d5f64e5 --- /dev/null +++ b/R/s2-transformers.R @@ -0,0 +1,298 @@ + +#' S2 Geography Transformations +#' +#' These functions operate on one or more geography vectors and +#' return a geography vector. +#' +#' @inheritParams s2_is_collection +#' @param na.rm For aggregate calculations use `na.rm = TRUE` +#' to drop missing values. +#' @param grid_size The grid size to which coordinates should be snapped; +#' will be rounded to the nearest power of 10. +#' @param options An [s2_options()] object describing the polygon/polyline +#' model to use and the snap level. +#' @param distance The distance to buffer, in units of `radius`. +#' @param max_cells The maximum number of cells to approximate a buffer. +#' @param min_level The minimum cell level used to approximate a buffer +#' (1 - 30). Setting this value too high will result in unnecessarily +#' large geographies, but may help improve buffers along long, narrow +#' regions. +#' @param tolerance The minimum distance between vertexes to use when +#' simplifying a geography. +#' +#' @inheritSection s2_options Model +#' +#' @export +#' +#' @seealso +#' BigQuery's geography function reference: +#' +#' - [ST_BOUNDARY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_boundary) +#' - [ST_CENTROID](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_centroid) +#' - [ST_CLOSESTPOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_closestpoint) +#' - [ST_DIFFERENCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_difference) +#' - [ST_INTERSECTION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersection) +#' - [ST_UNION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union) +#' - [ST_SNAPTOGRID](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_snaptogrid) +#' - [ST_SIMPLIFY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_simplify) +#' - [ST_UNION_AGG](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union_agg) +#' - [ST_CENTROID_AGG](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#s2_centroid_agg) +#' +#' @examples +#' # returns the boundary: +#' # empty for point, endpoints of a linestring, +#' # perimeter of a polygon +#' s2_boundary("POINT (-64 45)") +#' s2_boundary("LINESTRING (0 0, 10 0)") +#' s2_boundary("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") +#' +#' # returns the area-weighted centroid, element-wise +#' s2_centroid("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") +#' s2_centroid("LINESTRING (0 0, 10 0)") +#' +#' # s2_point_on_surface guarantees a point on surface +#' # Note: this is not the same as st_point_on_surface +#' s2_centroid("POLYGON ((0 0, 10 0, 1 1, 0 10, 0 0))") +#' s2_point_on_surface("POLYGON ((0 0, 10 0, 1 1, 0 10, 0 0))") +#' +#' # returns the unweighted centroid of the entire input +#' s2_centroid_agg(c("POINT (0 0)", "POINT (10 0)")) +#' +#' # returns the closest point on x to y +#' s2_closest_point( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POINT (0 90)" # north pole! +#' ) +#' +#' # returns the shortest possible line between x and y +#' s2_minimum_clearance_line_between( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POINT (0 90)" # north pole! +#' ) +#' +#' # binary operations: difference, symmetric difference, intersection and union +#' s2_difference( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' s2_sym_difference( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' s2_intersection( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' s2_union( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' # s2_convex_hull_agg builds the convex hull of a list of geometries +#' s2_convex_hull_agg( +#' c( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" +#' ) +#' ) +#' +#' # use s2_union_agg() to aggregate geographies in a vector +#' s2_coverage_union_agg( +#' c( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" +#' ), +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' # snap to grid rounds coordinates to a specified grid size +#' s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2) +#' +#' +s2_boundary <- function(x) { + new_s2_geography(cpp_s2_boundary(as_s2_geography(x))) +} + +#' @rdname s2_boundary +#' @export +s2_centroid <- function(x) { + new_s2_geography(cpp_s2_centroid(as_s2_geography(x))) +} + +#' @rdname s2_boundary +#' @export +s2_closest_point <- function(x, y) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_geography(cpp_s2_closest_point(recycled[[1]], recycled[[2]])) +} + +#' @rdname s2_boundary +#' @export +s2_minimum_clearance_line_between <- function(x, y) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_geography(cpp_s2_minimum_clearance_line_between(recycled[[1]], recycled[[2]])) +} + +#' @rdname s2_boundary +#' @export +s2_difference <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_geography(cpp_s2_difference(recycled[[1]], recycled[[2]], options)) +} + +#' @rdname s2_boundary +#' @export +s2_sym_difference <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_geography(cpp_s2_sym_difference(recycled[[1]], recycled[[2]], options)) +} + +#' @rdname s2_boundary +#' @export +s2_intersection <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_geography(cpp_s2_intersection(recycled[[1]], recycled[[2]], options)) +} + +#' @rdname s2_boundary +#' @export +s2_union <- function(x, y = NULL, options = s2_options()) { + x <- as_s2_geography(x) + + if (is.null(y)) { + new_s2_geography(cpp_s2_unary_union(x, options)) + } else { + recycled <- recycle_common(x, as_s2_geography(y)) + new_s2_geography(cpp_s2_union(recycled[[1]], recycled[[2]], options)) + } +} + +#' @rdname s2_boundary +#' @export +s2_snap_to_grid <- function(x, grid_size) { + s2_rebuild( + x, + options = s2_options( + snap = s2_snap_precision(10^(-log10(grid_size))), + duplicate_edges = TRUE + ) + ) +} + +#' @rdname s2_boundary +#' @export +s2_simplify <- function(x, tolerance, radius = s2_earth_radius_meters()) { + s2_rebuild(x, options = s2_options(snap_radius = tolerance / radius, simplify_edge_chains = TRUE)) +} + +#' @rdname s2_boundary +#' @export +s2_rebuild <- function(x, options = s2_options()) { + new_s2_geography(cpp_s2_rebuild(as_s2_geography(x), options)) +} + +#' @rdname s2_boundary +#' @export +s2_buffer_cells <- function(x, distance, max_cells = 1000, min_level = -1, + radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), distance / radius) + new_s2_geography(cpp_s2_buffer_cells(recycled[[1]], recycled[[2]], max_cells, min_level)) +} + +#' @rdname s2_boundary +#' @export +s2_convex_hull <- function(x) { + new_s2_geography(cpp_s2_convex_hull(as_s2_geography(x))) +} + +#' @rdname s2_boundary +#' @export +s2_centroid_agg <- function(x, na.rm = FALSE) { + new_s2_geography(cpp_s2_centroid_agg(as_s2_geography(x), naRm = na.rm)) +} + +#' @rdname s2_boundary +#' @export +s2_coverage_union_agg <- function(x, options = s2_options(), na.rm = FALSE) { + new_s2_geography(cpp_s2_coverage_union_agg(as_s2_geography(x), options, na.rm)) +} + +#' @rdname s2_boundary +#' @export +s2_rebuild_agg <- function(x, options = s2_options(), na.rm = FALSE) { + new_s2_geography(cpp_s2_rebuild_agg(as_s2_geography(x), options, na.rm)) +} + +#' @rdname s2_boundary +#' @export +s2_union_agg <- function(x, options = s2_options(), na.rm = FALSE) { + new_s2_geography(cpp_s2_union_agg(s2_union(x, options = options), options, na.rm)) +} + +#' @rdname s2_boundary +#' @export +s2_convex_hull_agg <- function(x, na.rm = FALSE) { + new_s2_geography(cpp_s2_convex_hull_agg(as_s2_geography(x), na.rm)) +} + +#' Linear referencing +#' +#' @param x A simple polyline geography vector +#' @param y A simple point geography vector. The point will be +#' snapped to the nearest point on `x` for the purposes of +#' interpolation. +#' @param distance A distance along `x` in `radius` units. +#' @param distance_normalized A `distance` normalized to [s2_length()] of +#' `x`. +#' @inheritParams s2_is_collection +#' +#' @return +#' - `s2_interpolate()` returns the point on `x`, `distance` meters +#' along the line. +#' - `s2_interpolate_normalized()` returns the point on `x` interpolated +#' to a fraction along the line. +#' - `s2_project()` returns the `distance` that `point` occurs along `x`. +#' - `s2_project_normalized()` returns the `distance_normalized` along `x` +#' where `point` occurs. +#' @export +#' +#' @examples +#' s2_project_normalized("LINESTRING (0 0, 0 90)", "POINT (0 22.5)") +#' s2_project("LINESTRING (0 0, 0 90)", "POINT (0 22.5)") +#' s2_interpolate_normalized("LINESTRING (0 0, 0 90)", 0.25) +#' s2_interpolate("LINESTRING (0 0, 0 90)", 2501890) +#' +s2_interpolate <- function(x, distance, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), distance / radius) + length <- cpp_s2_length(recycled[[1]]) + new_s2_geography( + cpp_s2_interpolate_normalized(recycled[[1]], distance / radius / length) + ) +} + +#' @rdname s2_interpolate +#' @export +s2_interpolate_normalized <- function(x, distance_normalized) { + recycled <- recycle_common(as_s2_geography(x), distance_normalized) + new_s2_geography( + cpp_s2_interpolate_normalized(recycled[[1]], distance_normalized) + ) +} + +#' @rdname s2_boundary +#' @export +s2_point_on_surface <- function(x, na.rm = FALSE) { + new_s2_geography(cpp_s2_point_on_surface(as_s2_geography(x))) +} diff --git a/R/utils.R b/R/utils.R new file mode 100644 index 0000000..f12af48 --- /dev/null +++ b/R/utils.R @@ -0,0 +1,88 @@ + +new_data_frame <- function(x) { + structure(x, row.names = c(NA, length(x[[1]])), class = "data.frame") +} + +recycle_common <- function(...) { + dots <- list(...) + lengths <- vapply(dots, length, integer(1)) + non_constant_lengths <- unique(lengths[lengths != 1]) + if (length(non_constant_lengths) == 0) { + final_length <- 1 + } else if(length(non_constant_lengths) == 1) { + final_length <- non_constant_lengths + } else { + lengths_label <- paste0(non_constant_lengths, collapse = ", ") + stop(sprintf("Incompatible lengths: %s", lengths_label)) + } + + lapply(dots, rep_len, final_length) +} + +# The problems object is generated when building or processing an s2_geography(): +# instead of attaching to the object as an attribute, this function is +# called from Rcpp if there were any problems to format them in a +# human-readable way. Theoretically one could change this to only warn +# instead of stop (error values are set to NA/NULL). +stop_problems_create <- function(feature_id, problem) { + n <- length(feature_id) + feature_label <- if (n != 1) "features" else "feature" + + stop_problems( + feature_id, + problem, + sprintf("Found %d %s with invalid spherical geometry.", n, feature_label) + ) +} + +stop_problems_process <- function(feature_id, problem) { + n <- length(feature_id) + error_label <- if (n != 1) "errors" else "error" + + stop_problems( + feature_id, + problem, + sprintf("Encountered %d processing %s.", n, error_label) + ) +} + +stop_problems <- function(feature_id, problem, header) { + n <- length(feature_id) + + if (n > 10) { + feature_id <- feature_id[1:10] + problem <- problem[1:10] + more <- sprintf("\n...and %s more", n - 10) + } else { + more <- "" + } + + msg <- paste0( + header, "\n", + paste0("[", feature_id + 1, "] ", problem , collapse = "\n"), + more + ) + + stop(msg, call. = FALSE) +} + +expect_wkt_equal <- function(x, y, precision = 16) { + testthat::expect_equal( + wk::wk_format( + as_s2_geography(x), + precision = precision, + trim = TRUE, + max_coords = .Machine$integer.max + ), + wk::wk_format( + as_s2_geography(y), + precision = precision, + trim = TRUE, + max_coords = .Machine$integer.max + ) + ) +} + +expect_near <- function(x, y, epsilon) { + testthat::expect_true(abs(y - x) < epsilon) +} diff --git a/R/vctrs.R b/R/vctrs.R new file mode 100644 index 0000000..fd985f5 --- /dev/null +++ b/R/vctrs.R @@ -0,0 +1,36 @@ + +vec_proxy.s2_geography <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_geography <- function(x, ...) { + new_s2_geography(x) +} + +vec_ptype_abbr.s2_geography <- function(x, ...) { + "s2_geography" +} + +vec_proxy.s2_cell <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_cell <- function(x, ...) { + new_s2_cell(x) +} + +vec_ptype_abbr.s2_cell <- function(x, ...) { + "s2cell" +} + +vec_proxy.s2_cell_union <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_cell_union <- function(x, ...) { + new_s2_cell_union(x) +} + +vec_ptype_abbr.s2_cell_union <- function(x, ...) { + "s2cellunion" +} diff --git a/R/wk-utils.R b/R/wk-utils.R new file mode 100644 index 0000000..df6a6ca --- /dev/null +++ b/R/wk-utils.R @@ -0,0 +1,129 @@ + +#' Low-level wk filters and handlers +#' +#' @inheritParams wk::wk_handle +#' @param projection,s2_projection One of [s2_projection_plate_carree()] or +#' [s2_projection_mercator()] +#' @param tessellate_tol,s2_tessellate_tol An angle in radians. +#' Points will not be added if a line segment is within this +#' distance of a point. +#' @param x_scale The maximum x value of the projection +#' @param centre The center point of the orthographic projection +#' @param epsilon_east_west,epsilon_north_south Use a positive number to +#' define the edges of a Cartesian world slightly inward from -180, -90, +#' 180, 90. This may be used to define a world outline for a projection where +#' projecting at the extreme edges of the earth results in a non-finite value. +#' @inheritParams as_s2_geography +#' +#' @return +#' - `s2_projection_plate_carree()`, `s2_projection_mercator()`: An external pointer +#' to an S2 projection. +#' @importFrom wk wk_handle +#' @export +#' +wk_handle.s2_geography <- function(handleable, handler, ..., + s2_projection = s2_projection_plate_carree(), + s2_tessellate_tol = Inf) { + stopifnot(is.null(s2_projection) || inherits(s2_projection, "s2_projection")) + attr(handleable, "s2_projection") <- s2_projection + + if (identical(s2_tessellate_tol, Inf)) { + .Call(c_s2_handle_geography, handleable, wk::as_wk_handler(handler)) + } else { + attr(handleable, "s2_tessellate_tol") <- as.double(s2_tessellate_tol)[1] + .Call(c_s2_handle_geography_tessellated, handleable, wk::as_wk_handler(handler)) + } +} + +#' @rdname wk_handle.s2_geography +#' @export +s2_geography_writer <- function(oriented = FALSE, check = TRUE, + projection = s2_projection_plate_carree(), + tessellate_tol = Inf) { + stopifnot(is.null(projection) || inherits(projection, "s2_projection")) + + wk::new_wk_handler( + .Call( + c_s2_geography_writer_new, + as.logical(oriented)[1], + as.logical(check)[1], + projection, + as.double(tessellate_tol[1]) + ), + "s2_geography_writer" + ) +} + +#' @rdname wk_handle.s2_geography +#' @importFrom wk wk_writer +#' @method wk_writer s2_geography +#' @export +wk_writer.s2_geography <- function(handleable, ...) { + s2_geography_writer() +} + +#' @rdname wk_handle.s2_geography +#' @export +s2_trans_point <- function() { + wk::new_wk_trans(.Call(c_s2_trans_s2_point_new)) +} + +#' @rdname wk_handle.s2_geography +#' @export +s2_trans_lnglat <- function() { + wk::new_wk_trans(.Call(c_s2_trans_s2_lnglat_new)) +} + +#' @rdname wk_handle.s2_geography +#' @export +s2_projection_plate_carree <- function(x_scale = 180) { + structure( + .Call(c_s2_projection_plate_carree, as.double(x_scale)[1]), + class = "s2_projection" + ) +} + +#' @rdname wk_handle.s2_geography +#' @export +s2_projection_mercator <- function(x_scale = 20037508.3427892) { + structure( + .Call(c_s2_projection_mercator, as.double(x_scale)[1]), + class = "s2_projection" + ) +} + +#' @rdname wk_handle.s2_geography +#' @export +s2_hemisphere <- function(centre) { + cap_to_polygon(centre, pi / 2) +} + +#' @rdname wk_handle.s2_geography +#' @export +s2_world_plate_carree <- function(epsilon_east_west = 0, epsilon_north_south = 0) { + s2_make_polygon( + c( + -180 + epsilon_east_west, 0, 180 - epsilon_east_west, + 180 - epsilon_east_west, 180 - epsilon_east_west, 0, + -180 + epsilon_east_west, -180 + epsilon_east_west + ), + c( + -90 + epsilon_north_south, -90 + epsilon_north_south, + -90 + epsilon_north_south, 0, 90 - epsilon_north_south, + 90 - epsilon_north_south, 90 - epsilon_north_south, 0 + ), + oriented = TRUE + ) +} + +#' @rdname wk_handle.s2_geography +#' @export +s2_projection_orthographic <- function(centre = s2_lnglat(0, 0)) { + centre <- as_s2_lnglat(centre) + centre <- as.matrix(centre) + + structure( + .Call(c_s2_projection_orthographic, centre[1:2]), + class = "s2_projection" + ) +} diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..a06930a --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,74 @@ + +# nocov start +.onLoad <- function(...) { + # call c++ init + cpp_s2_init() + + # dynamically register vctrs dependencies + for (cls in c("s2_geography", "s2_cell", "s2_cell_union")) { + s3_register("vctrs::vec_proxy", cls) + s3_register("vctrs::vec_restore", cls) + s3_register("vctrs::vec_ptype_abbr", cls) + } + + s3_register("bit64::as.integer64", "s2_cell") +} + +s3_register <- function(generic, class, method = NULL) { + stopifnot(is.character(generic), length(generic) == 1) + stopifnot(is.character(class), length(class) == 1) + + pieces <- strsplit(generic, "::")[[1]] + stopifnot(length(pieces) == 2) + package <- pieces[[1]] + generic <- pieces[[2]] + + caller <- parent.frame() + + get_method_env <- function() { + top <- topenv(caller) + if (isNamespace(top)) { + asNamespace(environmentName(top)) + } else { + caller + } + } + get_method <- function(method, env) { + if (is.null(method)) { + get(paste0(generic, ".", class), envir = get_method_env()) + } else { + method + } + } + + method_fn <- get_method(method) + stopifnot(is.function(method_fn)) + + # Always register hook in case package is later unloaded & reloaded + setHook( + packageEvent(package, "onLoad"), + function(...) { + ns <- asNamespace(package) + + # Refresh the method, it might have been updated by `devtools::load_all()` + method_fn <- get_method(method) + + registerS3method(generic, class, method_fn, envir = ns) + } + ) + + # Avoid registration failures during loading (pkgload or regular) + if (!isNamespaceLoaded(package)) { + return(invisible()) + } + + envir <- asNamespace(package) + + # Only register if generic can be accessed + if (exists(generic, envir)) { + registerS3method(generic, class, method_fn, envir = envir) + } + + invisible() +} +# nocov end diff --git a/README.md b/README.md new file mode 100644 index 0000000..b798e46 --- /dev/null +++ b/README.md @@ -0,0 +1,184 @@ + + + +# s2 + + + +![R-CMD-check](https://github.com/r-spatial/s2/workflows/R-CMD-check/badge.svg) +[![codecov](https://codecov.io/gh/r-spatial/s2/branch/master/graph/badge.svg)](https://app.codecov.io/gh/r-spatial/s2) +[![CRAN](http://www.r-pkg.org/badges/version/s2)](https://cran.r-project.org/package=s2) +[![Downloads](http://cranlogs.r-pkg.org/badges/s2?color=brightgreen)](https://www.r-pkg.org/pkg/s2) + + +The s2 R package provides bindings to Google’s +[S2Geometry](https://s2geometry.io) library. The package exposes an API +similar to Google’s [BigQuery Geography +API](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions), +whose functions also operate on spherical geometries. Package +[sf](https://cran.r-project.org/package=sf) uses this package by default +for nearly all its geometrical operations on objects with ellipsoidal +(unprojected) coordinates; in cases where it doesn’t, such as +`st_relate()`, it emits a warning. + +This package is a complete rewrite of an earlier CRAN package s2 with +versions up to 0.4-2, for which the sources are found +[here](https://github.com/spatstat/s2/). + +## Installation + +You can install the released version of s2 from +[CRAN](https://CRAN.R-project.org) with: + +``` r +install.packages("s2") +``` + +And the development version from [GitHub](https://github.com/) with: + +``` r +# install.packages("remotes") +remotes::install_github("r-spatial/s2") +``` + +## Example + +The s2 package provides geometry transformers and predicates similar to +those found in [GEOS](https://trac.osgeo.org/geos/), except instead of +assuming a planar geometry, s2’s functions work in latitude and +longitude and assume a spherical geometry: + +``` r +library(s2) + +s2_contains( + # polygon containing much of the northern hemisphere + "POLYGON ((-63.5 44.6, -149.75 61.20, 116.4 40.2, 13.5 52.51, -63.5 44.6))", + # ...should contain the north pole + "POINT (0 90)" +) +#> [1] TRUE +``` + +The [sf package](https://r-spatial.github.io/sf/) uses s2 for geographic +coordinates with `sf::sf_use_s2(TRUE)`, and will become the default +after sf version 1.0.0. The sf package also supports creating s2 vectors +using `as_s2_geography()`: + +``` r +library(dplyr) +library(sf) + +nc_s2 <- read_sf(system.file("shape/nc.shp", package = "sf")) %>% + mutate(geometry = as_s2_geography(geometry)) %>% + as_tibble() %>% + select(NAME, geometry) + +nc_s2 +#> # A tibble: 100 × 2 +#> NAME geometry +#> +#> 1 Ashe POLYGON ((-81.4528885 36.2395859, -81.4310379 36.2607193, -81.41… +#> 2 Alleghany POLYGON ((-81.1766739 36.4154434, -81.1533661 36.4247398, -81.13… +#> 3 Surry POLYGON ((-80.4530106 36.2570877, -80.4353104 36.5510445, -80.61… +#> 4 Currituck MULTIPOLYGON (((-75.9419327 36.2943382, -75.9575119 36.2594528, … +#> 5 Northampton POLYGON ((-77.1419601 36.4170647, -77.1393204 36.4564781, -77.12… +#> 6 Hertford POLYGON ((-76.7074966 36.2661324, -76.7413483 36.3151665, -76.92… +#> 7 Camden POLYGON ((-76.0173492 36.3377304, -76.0328751 36.3359756, -76.04… +#> 8 Gates POLYGON ((-76.46035 36.3738976, -76.5024643 36.4522858, -76.4983… +#> 9 Warren POLYGON ((-78.1347198 36.2365837, -78.1096268 36.2135086, -78.05… +#> 10 Stokes POLYGON ((-80.0240555 36.5450249, -80.0480957 36.5471344, -80.43… +#> # … with 90 more rows +``` + +Use accessors to extract information about geometries: + +``` r +nc_s2 %>% + mutate( + area = s2_area(geometry), + perimeter = s2_perimeter(geometry) + ) +#> # A tibble: 100 × 4 +#> NAME geometry area perimeter +#> +#> 1 Ashe POLYGON ((-81.4528885 36.2395859, -81.4310379 3… 1.14e9 141627. +#> 2 Alleghany POLYGON ((-81.1766739 36.4154434, -81.1533661 3… 6.11e8 119876. +#> 3 Surry POLYGON ((-80.4530106 36.2570877, -80.4353104 3… 1.42e9 160458. +#> 4 Currituck MULTIPOLYGON (((-75.9419327 36.2943382, -75.957… 6.94e8 301644. +#> 5 Northampton POLYGON ((-77.1419601 36.4170647, -77.1393204 3… 1.52e9 211794. +#> 6 Hertford POLYGON ((-76.7074966 36.2661324, -76.7413483 3… 9.68e8 160780. +#> 7 Camden POLYGON ((-76.0173492 36.3377304, -76.0328751 3… 6.16e8 150430. +#> 8 Gates POLYGON ((-76.46035 36.3738976, -76.5024643 36.… 9.03e8 123170. +#> 9 Warren POLYGON ((-78.1347198 36.2365837, -78.1096268 3… 1.18e9 141073. +#> 10 Stokes POLYGON ((-80.0240555 36.5450249, -80.0480957 3… 1.23e9 140583. +#> # … with 90 more rows +``` + +Use predicates to subset vectors: + +``` r +nc_s2 %>% + filter(s2_contains(geometry, "POINT (-80.9313 35.6196)")) +#> # A tibble: 1 × 2 +#> NAME geometry +#> +#> 1 Catawba POLYGON ((-80.9312744 35.6195908, -81.0035782 35.6970558, -81.0547791… +``` + +Use transformers to create new geometries: + +``` r +nc_s2 %>% + mutate(geometry = s2_boundary(geometry)) +#> # A tibble: 100 × 2 +#> NAME geometry +#> +#> 1 Ashe LINESTRING (-81.4528885 36.2395859, -81.4310379 36.2607193, -81.… +#> 2 Alleghany LINESTRING (-81.1766739 36.4154434, -81.1533661 36.4247398, -81.… +#> 3 Surry LINESTRING (-80.4530106 36.2570877, -80.4353104 36.5510445, -80.… +#> 4 Currituck MULTILINESTRING ((-75.9419327 36.2943382, -75.9575119 36.2594528… +#> 5 Northampton LINESTRING (-77.1419601 36.4170647, -77.1393204 36.4564781, -77.… +#> 6 Hertford LINESTRING (-76.7074966 36.2661324, -76.7413483 36.3151665, -76.… +#> 7 Camden LINESTRING (-76.0173492 36.3377304, -76.0328751 36.3359756, -76.… +#> 8 Gates LINESTRING (-76.46035 36.3738976, -76.5024643 36.4522858, -76.49… +#> 9 Warren LINESTRING (-78.1347198 36.2365837, -78.1096268 36.2135086, -78.… +#> 10 Stokes LINESTRING (-80.0240555 36.5450249, -80.0480957 36.5471344, -80.… +#> # … with 90 more rows +``` + +Finally, use the WKB or WKT exporters to export to sf or some other +package: + +``` r +nc_s2 %>% + mutate(geometry = st_as_sfc(s2_as_binary(geometry))) %>% + st_as_sf() +#> Simple feature collection with 100 features and 1 field +#> Geometry type: GEOMETRY +#> Dimension: XY +#> Bounding box: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965 +#> CRS: NA +#> # A tibble: 100 × 2 +#> NAME geometry +#> +#> 1 Ashe POLYGON ((-81.45289 36.23959, -81.43104 36.26072, -81.41233 36.2… +#> 2 Alleghany POLYGON ((-81.17667 36.41544, -81.15337 36.42474, -81.1384 36.41… +#> 3 Surry POLYGON ((-80.45301 36.25709, -80.43531 36.55104, -80.61105 36.5… +#> 4 Currituck MULTIPOLYGON (((-75.94193 36.29434, -75.95751 36.25945, -75.9137… +#> 5 Northampton POLYGON ((-77.14196 36.41706, -77.13932 36.45648, -77.12733 36.4… +#> 6 Hertford POLYGON ((-76.7075 36.26613, -76.74135 36.31517, -76.92408 36.39… +#> 7 Camden POLYGON ((-76.01735 36.33773, -76.03288 36.33598, -76.04395 36.3… +#> 8 Gates POLYGON ((-76.46035 36.3739, -76.50246 36.45229, -76.49834 36.50… +#> 9 Warren POLYGON ((-78.13472 36.23658, -78.10963 36.21351, -78.05835 36.2… +#> 10 Stokes POLYGON ((-80.02406 36.54502, -80.0481 36.54713, -80.43531 36.55… +#> # … with 90 more rows +``` + +## Acknowledgment + +This project gratefully acknowledges financial +[support](https://www.r-consortium.org/projects) from the + + + diff --git a/build/partial.rdb b/build/partial.rdb new file mode 100644 index 0000000000000000000000000000000000000000..c7c2cee25c8c919773403139c01254511fe0b7a6 GIT binary patch literal 60 zcmb2|=3oE==I#ec2?+^F35jfz&L}K1V3ycmz{u&wCdTHcaCX-88MC8!QaBt`B>yoo MGzx7hHwS700OyeqZ2$lO literal 0 HcmV?d00001 diff --git a/cleanup b/cleanup new file mode 100755 index 0000000..3fd9cd2 --- /dev/null +++ b/cleanup @@ -0,0 +1,3 @@ +#!/bin/sh +rm -f src/Makevars configure.log autobrew +rm `find src -name *.o` diff --git a/configure b/configure new file mode 100755 index 0000000..fbf909f --- /dev/null +++ b/configure @@ -0,0 +1,125 @@ +# Anticonf (tm) script by Jeroen Ooms (2020) +# This script will query 'pkg-config' for the required cflags and ldflags. +# If pkg-config is unavailable or does not find the library, try setting +# INCLUDE_DIR and LIB_DIR manually via e.g: +# R CMD INSTALL --configure-vars='INCLUDE_DIR=/.../include LIB_DIR=/.../lib' + +# Library settings +PKG_CONFIG_NAME="openssl" +PKG_DEB_NAME="libssl-dev" +PKG_RPM_NAME="openssl-devel" +PKG_CSW_NAME="libssl_dev" +PKG_BREW_NAME="openssl@1.1" +PKG_TEST_FILE="tools/version.c" +PKG_LIBS="-lssl -lcrypto" +PKG_CFLAGS="" + +# Use pkg-config if available +pkg-config ${PKG_CONFIG_NAME} --atleast-version=1.0 2>/dev/null +if [ $? -eq 0 ]; then + PKGCONFIG_CFLAGS=`pkg-config --cflags ${PKG_CONFIG_NAME}` + PKGCONFIG_LIBS=`pkg-config --libs ${PKG_CONFIG_NAME}` +fi + +# Note that cflags may be empty in case of success +if [ "$INCLUDE_DIR" ] || [ "$LIB_DIR" ]; then + echo "Found INCLUDE_DIR and/or LIB_DIR!" + PKG_CFLAGS="-I$INCLUDE_DIR $PKG_CFLAGS" + PKG_LIBS="-L$LIB_DIR $PKG_LIBS" +elif [ "$PKGCONFIG_CFLAGS" ] || [ "$PKGCONFIG_LIBS" ]; then + echo "Found pkg-config cflags and libs!" + PKG_CFLAGS=${PKGCONFIG_CFLAGS} + PKG_LIBS=${PKGCONFIG_LIBS} +elif [ `uname` = "Darwin" ]; then + test ! "$CI" && brew --version 2>/dev/null + if [ $? -eq 0 ]; then + BREWDIR=`brew --prefix` + PKG_CFLAGS="-I$BREWDIR/opt/openssl/include -I$BREWDIR/opt/openssl@1.1/include" + PKG_LIBS="-L$BREWDIR/opt/openssl/lib -L$BREWDIR/opt/openssl@1.1/lib $PKG_LIBS" + else + curl -sfL "https://autobrew.github.io/scripts/$PKG_BREW_NAME" > autobrew + . ./autobrew + fi +fi + +# Find compiler +CC=`${R_HOME}/bin/R CMD config CC` +CFLAGS=`${R_HOME}/bin/R CMD config CFLAGS` +CPPFLAGS=`${R_HOME}/bin/R CMD config CPPFLAGS` + +# For debugging +echo "Testing compiler using PKG_CFLAGS=$PKG_CFLAGS" + +# Test configuration +${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E ${PKG_TEST_FILE} >/dev/null 2>configure.log + +# Customize the error +if [ $? -ne 0 ]; then + echo "--------------------------- [ANTICONF] --------------------------------" + echo "Configuration failed because $PKG_CONFIG_NAME was not found. Try installing:" + echo " * deb: $PKG_DEB_NAME (Debian, Ubuntu, etc)" + echo " * rpm: $PKG_RPM_NAME (Fedora, CentOS, RHEL)" + echo " * csw: $PKG_CSW_NAME (Solaris)" + echo " * brew: $PKG_BREW_NAME (Mac OSX)" + echo "If $PKG_CONFIG_NAME is already installed, check that 'pkg-config' is in your" + echo "PATH and PKG_CONFIG_PATH contains a $PKG_CONFIG_NAME.pc file. If pkg-config" + echo "is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:" + echo "R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'" + echo "-------------------------- [ERROR MESSAGE] ---------------------------" + cat configure.log + echo "--------------------------------------------------------------------" + exit 1 +fi + +# Try to link against the correct OpenSSL version +if [ -z "$AUTOBREW" ]; then +SONAME=`${CC} -E ${PKG_CFLAGS} src/tests/soname.h | sh | xargs` +if [ "$SONAME" ]; then +if [ `uname` = "Darwin" ]; then + PKG_LIBS_VERSIONED=`echo "${PKG_LIBS}" | sed "s/-lssl/-lssl.${SONAME}/" | sed "s/-lcrypto/-lcrypto.${SONAME}/"` +else + PKG_LIBS_VERSIONED=`echo "${PKG_LIBS}" | sed "s/-lssl/-l:libssl.so.${SONAME}/" | sed "s/-lcrypto/-l:libcrypto.so.${SONAME}/"` +fi + +# Test if versioned linking works +${CC} ${PKG_CFLAGS} src/tests/main.c ${PKG_LIBS_VERSIONED} -o src/main.exe 2>/dev/null +if [ $? -eq 0 ]; then PKG_LIBS="${PKG_LIBS_VERSIONED}"; fi +rm src/main.exe || true + +# Suppress opensslv3 warnings for now +if [ "$SONAME" = "3" ]; then +PKG_CFLAGS="$PKG_CFLAGS -DOPENSSL_SUPPRESS_DEPRECATED" +fi + +fi #SONAME +fi #AUTOBREW + +# Define system endianness (compile-time endianness using system/compiler +# defines isn't detected on Solaris) +# based on endian detection from the feather package by @hadley +R_ENDIAN=`${R_HOME}/bin/Rscript -e 'cat(.Platform$endian)'` +# Trim off any warning messages that Rscript appends in front of the platform endianness +R_ENDIAN=`expr "$R_ENDIAN" : '.*\(little\)$'` +SYS_ENDIAN="" +if [ "$R_ENDIAN" = "little" ]; then + PKG_CFLAGS="$PKG_CFLAGS -DIS_LITTLE_ENDIAN" +else + PKG_CFLAGS="$PKG_CFLAGS -DIS_BIG_ENDIAN" +fi + +# From apache/arrow/r/configure: +# If on Raspberry Pi, need to manually link against latomic +# See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81358 for similar example +if grep raspbian /etc/os-release >/dev/null 2>&1; then + PKG_LIBS="-latomic $PKG_LIBS" +fi + +echo "Using PKG_LIBS=$PKG_LIBS" +echo "Using PKG_CFLAGS=$PKG_CFLAGS" + + +# Write to Makevars +sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" src/Makevars.in > src/Makevars + +# Success +exit 0 diff --git a/configure.win b/configure.win new file mode 100755 index 0000000..e69de29 diff --git a/data/s2_data_example_wkt.rda b/data/s2_data_example_wkt.rda new file mode 100644 index 0000000000000000000000000000000000000000..23820bbf6ab0d921ca0cf8cf64446a12b1047813 GIT binary patch literal 24540 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 ZyG|wjWAV=a-mD^;w*$>D?TP*M{{ZAu5(fYP literal 0 HcmV?d00001 diff --git a/data/s2_data_tbl_cities.rda b/data/s2_data_tbl_cities.rda new file mode 100644 index 0000000000000000000000000000000000000000..1362234326a815060daee5adfd41f4ae86252857 GIT binary patch literal 7120 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 G?OcGiP_Bdk literal 0 HcmV?d00001 diff --git a/data/s2_data_tbl_countries.rda b/data/s2_data_tbl_countries.rda new file mode 100644 index 0000000000000000000000000000000000000000..119290efddcf45ec4bafdc5c92b627f542fdc35d GIT binary patch literal 132763 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%0Dm8;mBbQ&iB_oe>kO^MiZE=Fkc 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 literal 0 HcmV?d00001 diff --git a/data/s2_data_tbl_timezones.rda b/data/s2_data_tbl_timezones.rda new file mode 100644 index 0000000000000000000000000000000000000000..584b1dcea1627fa738f7880b20f1944aefbbaf38 GIT binary patch literal 498373 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ƭ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�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%0DM4$ze+1e>w<9cT85o>YaKz* zg+0Xx@d!4rzD{Erl zr@Bv9LmZ!|&P&-!xQy!3{#hSMazbL;_9bCkQRplh-t^x4gyu1e;z2il&I-!z{>r*~ z)rs(-6<5&na02LtF_v9)aB*9cvY@jRD|$1kNdFR zehx~ErrH%1wTj@tf$?Krs&x67WIDN2Ti(ic$EV0nJJv(4uR-g1$%c`DbkixpM_1A+ z^%fx-w&m6Nh0%d|?!tDxB>Z&s|1T*eF`Ro1a$z@Yl8fu(SXYMifZy$3?2`DXzWaUc zKSrQ0?SH0?Se3Qiq`d38cJdW=v3FA3MUk4QMIk4I z?LB|!iZfeVVxEGypT#T))eYF*Avrh3dB}MUy4Im1DetMQU6JYHj-EgBbdX9U+xP7j zs30ZN$3>BR=|pgZXcIEd3aE~35hyPL=&meF5&}JDZW%Vg0ZLL@3OgwB)>pvMuO6e} zV%tS8{Z$ucJ>Oj0H5uGee`XjrhC(dmq|0v5ibMQI8SefNyOgHX_ScN$c-M8sB$7-f z1DtDb700*;a*tR4BV&9^ebhf=TEF2YFqn0de&HBp>xHLa>Rp=grZH`y%VhBhoeh=R zTSxJT&tFrCeg~%5JxI!I)3;Z?R<|WiB;g5%J7;jvm^0n<9XdQwB)HroCN35Onlt4l z5QM)l|8YV{&>{I<#+`LST%DE4wCccthJVbm_2@gu93%;cSzNTP7R4@ow2d*Axwp-~ zw$JoOj6<; zA^+yFESTtX1kb!a@VK3tBe2Mh<*%wj<7H=Dx1goI#@lk5Q5^TCh!s!w9mm3?mBHFR8l*_vKv#^5)6$-V>el6Ri$23doSzS^W zbiFQ=uPltb8`F}nr$`NZho)>}StZr@Lv*w>iKY2Idri~+p|sDD;xZ0O!nP%G3|g_N zc20{+{E9wC#bDzp$tQ*QDIUtUo7b%h`wo|$xJwe6vAmbg ztjI8^bzR}{k*!;7gr2HVi!5g)GLWb$%GMS2qtZ#ke#MdySr)6tp(6M< zDj$iWthkngD{at@WDv@HEZW-ZG;Z1pwxcFi1x=7}Wf181%*U*cZEsEW?0siOm&B(V z7)g9HBA>G^b34#>*fgsG$1(3=wrCc2zk!XTr)+az``6^l*+;0)(OG}Yhw!JaO*$7A zbf}>{HT6klpVC#Vhf>?5dUF1j1ldr1juho}XIUk^=%*}d>W^ri27v{pN-HlFg<*7P z(xX;c-r~m?l7hCpc5yahsfv>jpT4Jx$=6fG&4X82WP4(+xUA9=L7`3>g!RtX_L9G> z8HRbNpDZsamOnPaeAb@v{ADGILa?>*22zA~KSnZ?K@>t3(%Fb$tTQS~g37-a>ZCJ& z6?Im4>AM7uV_lVwWjk5es}j1q#3QaLvl_s6T2@u%wh5-w`+SVWiSRXVQ9HahHMIFd zNT=~xDtkn<7

    3zUBUa+QR7QQue_FS+sr_m5^>zvq6v3~_ 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

    ::emplace_value(const size_type i, + allocator_type *alloc, + Args &&... args) { + assert(i >= start()); + assert(i <= finish()); + // Shift old values to create space for new value and then construct it in + // place. + if (i < finish()) { + transfer_n_backward(finish() - i, /*dest_i=*/i + 1, /*src_i=*/i, this, + alloc); + } + value_init(i, alloc, std::forward(args)...); + set_finish(finish() + 1); + + if (is_internal() && finish() > i + 1) { + for (field_type j = finish(); j > i + 1; --j) { + set_child(j, child(j - 1)); + } + clear_child(i + 1); + } +} + +template +inline void btree_node

    ::remove_values(const field_type i, + const field_type to_erase, + allocator_type *alloc) { + // Transfer values after the removed range into their new places. + value_destroy_n(i, to_erase, alloc); + const field_type orig_finish = finish(); + const field_type src_i = i + to_erase; + transfer_n(orig_finish - src_i, i, src_i, this, alloc); + + if (is_internal()) { + // Delete all children between begin and end. + for (int j = 0; j < to_erase; ++j) { + clear_and_delete(child(i + j + 1), alloc); + } + // Rotate children after end into new positions. + for (int j = i + to_erase + 1; j <= orig_finish; ++j) { + set_child(j - to_erase, child(j)); + clear_child(j); + } + } + set_finish(orig_finish - to_erase); +} + +template +void btree_node

    ::rebalance_right_to_left(const int to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(right->count() >= count()); + assert(to_move >= 1); + assert(to_move <= right->count()); + + // 1) Move the delimiting value in the parent to the left node. + transfer(finish(), position(), parent(), alloc); + + // 2) Move the (to_move - 1) values from the right node to the left node. + transfer_n(to_move - 1, finish() + 1, right->start(), right, alloc); + + // 3) Move the new delimiting value to the parent from the right node. + parent()->transfer(position(), right->start() + to_move - 1, right, alloc); + + // 4) Shift the values in the right node to their correct positions. + right->transfer_n(right->count() - to_move, right->start(), + right->start() + to_move, right, alloc); + + if (is_internal()) { + // Move the child pointers from the right to the left node. + for (int i = 0; i < to_move; ++i) { + init_child(finish() + i + 1, right->child(i)); + } + for (int i = right->start(); i <= right->finish() - to_move; ++i) { + assert(i + to_move <= right->max_count()); + right->init_child(i, right->child(i + to_move)); + right->clear_child(i + to_move); + } + } + + // Fixup `finish` on the left and right nodes. + set_finish(finish() + to_move); + right->set_finish(right->finish() - to_move); +} + +template +void btree_node

    ::rebalance_left_to_right(const int to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(count() >= right->count()); + assert(to_move >= 1); + assert(to_move <= count()); + + // Values in the right node are shifted to the right to make room for the + // new to_move values. Then, the delimiting value in the parent and the + // other (to_move - 1) values in the left node are moved into the right node. + // Lastly, a new delimiting value is moved from the left node into the + // parent, and the remaining empty left node entries are destroyed. + + // 1) Shift existing values in the right node to their correct positions. + right->transfer_n_backward(right->count(), right->start() + to_move, + right->start(), right, alloc); + + // 2) Move the delimiting value in the parent to the right node. + right->transfer(right->start() + to_move - 1, position(), parent(), alloc); + + // 3) Move the (to_move - 1) values from the left node to the right node. + right->transfer_n(to_move - 1, right->start(), finish() - (to_move - 1), this, + alloc); + + // 4) Move the new delimiting value to the parent from the left node. + parent()->transfer(position(), finish() - to_move, this, alloc); + + if (is_internal()) { + // Move the child pointers from the left to the right node. + for (int i = right->finish(); i >= right->start(); --i) { + right->init_child(i + to_move, right->child(i)); + right->clear_child(i); + } + for (int i = 1; i <= to_move; ++i) { + right->init_child(i - 1, child(finish() - to_move + i)); + clear_child(finish() - to_move + i); + } + } + + // Fixup the counts on the left and right nodes. + set_finish(finish() - to_move); + right->set_finish(right->finish() + to_move); +} + +template +void btree_node

    ::split(const int insert_position, btree_node *dest, + allocator_type *alloc) { + assert(dest->count() == 0); + assert(max_count() == kNodeSlots); + + // We bias the split based on the position being inserted. If we're + // inserting at the beginning of the left node then bias the split to put + // more values on the right node. If we're inserting at the end of the + // right node then bias the split to put more values on the left node. + if (insert_position == start()) { + dest->set_finish(dest->start() + finish() - 1); + } else if (insert_position == kNodeSlots) { + dest->set_finish(dest->start()); + } else { + dest->set_finish(dest->start() + count() / 2); + } + set_finish(finish() - dest->count()); + assert(count() >= 1); + + // Move values from the left sibling to the right sibling. + dest->transfer_n(dest->count(), dest->start(), finish(), this, alloc); + + // The split key is the largest value in the left sibling. + --mutable_finish(); + parent()->emplace_value(position(), alloc, finish_slot()); + value_destroy(finish(), alloc); + parent()->init_child(position() + 1, dest); + + if (is_internal()) { + for (int i = dest->start(), j = finish() + 1; i <= dest->finish(); + ++i, ++j) { + assert(child(j) != nullptr); + dest->init_child(i, child(j)); + clear_child(j); + } + } +} + +template +void btree_node

    ::merge(btree_node *src, allocator_type *alloc) { + assert(parent() == src->parent()); + assert(position() + 1 == src->position()); + + // Move the delimiting value to the left node. + value_init(finish(), alloc, parent()->slot(position())); + + // Move the values from the right to the left node. + transfer_n(src->count(), finish() + 1, src->start(), src, alloc); + + if (is_internal()) { + // Move the child pointers from the right to the left node. + for (int i = src->start(), j = finish() + 1; i <= src->finish(); ++i, ++j) { + init_child(j, src->child(i)); + src->clear_child(i); + } + } + + // Fixup `finish` on the src and dest nodes. + set_finish(start() + 1 + count() + src->count()); + src->set_finish(src->start()); + + // Remove the value on the parent node and delete the src node. + parent()->remove_values(position(), /*to_erase=*/1, alloc); +} + +template +void btree_node

    ::clear_and_delete(btree_node *node, allocator_type *alloc) { + if (node->is_leaf()) { + node->value_destroy_n(node->start(), node->count(), alloc); + deallocate(LeafSize(node->max_count()), node, alloc); + return; + } + if (node->count() == 0) { + deallocate(InternalSize(), node, alloc); + return; + } + + // The parent of the root of the subtree we are deleting. + btree_node *delete_root_parent = node->parent(); + + // Navigate to the leftmost leaf under node, and then delete upwards. + while (node->is_internal()) node = node->start_child(); +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + // When generations are enabled, we delete the leftmost leaf last in case it's + // the parent of the root and we need to check whether it's a leaf before we + // can update the root's generation. + // TODO(ezb): if we change btree_node::is_root to check a bool inside the node + // instead of checking whether the parent is a leaf, we can remove this logic. + btree_node *leftmost_leaf = node; +#endif + // Use `int` because `pos` needs to be able to hold `kNodeSlots+1`, which + // isn't guaranteed to be a valid `field_type`. + int pos = node->position(); + btree_node *parent = node->parent(); + for (;;) { + // In each iteration of the next loop, we delete one leaf node and go right. + assert(pos <= parent->finish()); + do { + node = parent->child(pos); + if (node->is_internal()) { + // Navigate to the leftmost leaf under node. + while (node->is_internal()) node = node->start_child(); + pos = node->position(); + parent = node->parent(); + } + node->value_destroy_n(node->start(), node->count(), alloc); +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + if (leftmost_leaf != node) +#endif + deallocate(LeafSize(node->max_count()), node, alloc); + ++pos; + } while (pos <= parent->finish()); + + // Once we've deleted all children of parent, delete parent and go up/right. + assert(pos > parent->finish()); + do { + node = parent; + pos = node->position(); + parent = node->parent(); + node->value_destroy_n(node->start(), node->count(), alloc); + deallocate(InternalSize(), node, alloc); + if (parent == delete_root_parent) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + deallocate(LeafSize(leftmost_leaf->max_count()), leftmost_leaf, alloc); +#endif + return; + } + ++pos; + } while (pos > parent->finish()); + } +} + +//// +// btree_iterator methods +template +void btree_iterator::increment_slow() { + if (node_->is_leaf()) { + assert(position_ >= node_->finish()); + btree_iterator save(*this); + while (position_ == node_->finish() && !node_->is_root()) { + assert(node_->parent()->child(node_->position()) == node_); + position_ = node_->position(); + node_ = node_->parent(); + } + // TODO(ezb): assert we aren't incrementing end() instead of handling. + if (position_ == node_->finish()) { + *this = save; + } + } else { + assert(position_ < node_->finish()); + node_ = node_->child(position_ + 1); + while (node_->is_internal()) { + node_ = node_->start_child(); + } + position_ = node_->start(); + } +} + +template +void btree_iterator::decrement_slow() { + if (node_->is_leaf()) { + assert(position_ <= -1); + btree_iterator save(*this); + while (position_ < node_->start() && !node_->is_root()) { + assert(node_->parent()->child(node_->position()) == node_); + position_ = node_->position() - 1; + node_ = node_->parent(); + } + // TODO(ezb): assert we aren't decrementing begin() instead of handling. + if (position_ < node_->start()) { + *this = save; + } + } else { + assert(position_ >= node_->start()); + node_ = node_->child(position_); + while (node_->is_internal()) { + node_ = node_->child(node_->finish()); + } + position_ = node_->finish() - 1; + } +} + +//// +// btree methods +template +template +void btree

    ::copy_or_move_values_in_order(Btree &other) { + static_assert(std::is_same::value || + std::is_same::value, + "Btree type must be same or const."); + assert(empty()); + + // We can avoid key comparisons because we know the order of the + // values is the same order we'll store them in. + auto iter = other.begin(); + if (iter == other.end()) return; + insert_multi(iter.slot()); + ++iter; + for (; iter != other.end(); ++iter) { + // If the btree is not empty, we can just insert the new value at the end + // of the tree. + internal_emplace(end(), iter.slot()); + } +} + +template +constexpr bool btree

    ::static_assert_validation() { + static_assert(std::is_nothrow_copy_constructible::value, + "Key comparison must be nothrow copy constructible"); + static_assert(std::is_nothrow_copy_constructible::value, + "Allocator must be nothrow copy constructible"); + static_assert(type_traits_internal::is_trivially_copyable::value, + "iterator not trivially copyable."); + + // Note: We assert that kTargetValues, which is computed from + // Params::kTargetNodeSize, must fit the node_type::field_type. + static_assert( + kNodeSlots < (1 << (8 * sizeof(typename node_type::field_type))), + "target node size too large"); + + // Verify that key_compare returns an absl::{weak,strong}_ordering or bool. + static_assert( + compare_has_valid_result_type(), + "key comparison function must return absl::{weak,strong}_ordering or " + "bool."); + + // Test the assumption made in setting kNodeSlotSpace. + static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4, + "node space assumption incorrect"); + + return true; +} + +template +template +auto btree

    ::lower_bound_equal(const K &key) const + -> std::pair { + const SearchResult res = + internal_lower_bound(key); + const iterator lower = iterator(internal_end(res.value)); + const bool equal = res.HasMatch() + ? res.IsEq() + : lower != end() && !compare_keys(key, lower.key()); + return {lower, equal}; +} + +template +template +auto btree

    ::equal_range(const K &key) -> std::pair { + const std::pair lower_and_equal = lower_bound_equal(key); + const iterator lower = lower_and_equal.first; + if (!lower_and_equal.second) { + return {lower, lower}; + } + + const iterator next = std::next(lower); + if (!params_type::template can_have_multiple_equivalent_keys()) { + // The next iterator after lower must point to a key greater than `key`. + // Note: if this assert fails, then it may indicate that the comparator does + // not meet the equivalence requirements for Compare + // (see https://en.cppreference.com/w/cpp/named_req/Compare). + assert(next == end() || compare_keys(key, next.key())); + return {lower, next}; + } + // Try once more to avoid the call to upper_bound() if there's only one + // equivalent key. This should prevent all calls to upper_bound() in cases of + // unique-containers with heterogeneous comparators in which all comparison + // operators have the same equivalence classes. + if (next == end() || compare_keys(key, next.key())) return {lower, next}; + + // In this case, we need to call upper_bound() to avoid worst case O(N) + // behavior if we were to iterate over equal keys. + return {lower, upper_bound(key)}; +} + +template +template +auto btree

    ::insert_unique(const K &key, Args &&... args) + -> std::pair { + if (empty()) { + mutable_root() = mutable_rightmost() = new_leaf_root_node(1); + } + + SearchResult res = internal_locate(key); + iterator iter = res.value; + + if (res.HasMatch()) { + if (res.IsEq()) { + // The key already exists in the tree, do nothing. + return {iter, false}; + } + } else { + iterator last = internal_last(iter); + if (last.node_ && !compare_keys(key, last.key())) { + // The key already exists in the tree, do nothing. + return {last, false}; + } + } + return {internal_emplace(iter, std::forward(args)...), true}; +} + +template +template +inline auto btree

    ::insert_hint_unique(iterator position, const K &key, + Args &&... args) + -> std::pair { + if (!empty()) { + if (position == end() || compare_keys(key, position.key())) { + if (position == begin() || compare_keys(std::prev(position).key(), key)) { + // prev.key() < key < position.key() + return {internal_emplace(position, std::forward(args)...), true}; + } + } else if (compare_keys(position.key(), key)) { + ++position; + if (position == end() || compare_keys(key, position.key())) { + // {original `position`}.key() < key < {current `position`}.key() + return {internal_emplace(position, std::forward(args)...), true}; + } + } else { + // position.key() == key + return {position, false}; + } + } + return insert_unique(key, std::forward(args)...); +} + +template +template +void btree

    ::insert_iterator_unique(InputIterator b, InputIterator e, int) { + for (; b != e; ++b) { + insert_hint_unique(end(), params_type::key(*b), *b); + } +} + +template +template +void btree

    ::insert_iterator_unique(InputIterator b, InputIterator e, char) { + for (; b != e; ++b) { + // Use a node handle to manage a temp slot. + auto node_handle = + CommonAccess::Construct(get_allocator(), *b); + slot_type *slot = CommonAccess::GetSlot(node_handle); + insert_hint_unique(end(), params_type::key(slot), slot); + } +} + +template +template +auto btree

    ::insert_multi(const key_type &key, ValueType &&v) -> iterator { + if (empty()) { + mutable_root() = mutable_rightmost() = new_leaf_root_node(1); + } + + iterator iter = internal_upper_bound(key); + if (iter.node_ == nullptr) { + iter = end(); + } + return internal_emplace(iter, std::forward(v)); +} + +template +template +auto btree

    ::insert_hint_multi(iterator position, ValueType &&v) -> iterator { + if (!empty()) { + const key_type &key = params_type::key(v); + if (position == end() || !compare_keys(position.key(), key)) { + if (position == begin() || + !compare_keys(key, std::prev(position).key())) { + // prev.key() <= key <= position.key() + return internal_emplace(position, std::forward(v)); + } + } else { + ++position; + if (position == end() || !compare_keys(position.key(), key)) { + // {original `position`}.key() < key < {current `position`}.key() + return internal_emplace(position, std::forward(v)); + } + } + } + return insert_multi(std::forward(v)); +} + +template +template +void btree

    ::insert_iterator_multi(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_hint_multi(end(), *b); + } +} + +template +auto btree

    ::operator=(const btree &other) -> btree & { + if (this != &other) { + clear(); + + *mutable_key_comp() = other.key_comp(); + if (absl::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + *mutable_allocator() = other.allocator(); + } + + copy_or_move_values_in_order(other); + } + return *this; +} + +template +auto btree

    ::operator=(btree &&other) noexcept -> btree & { + if (this != &other) { + clear(); + + using std::swap; + if (absl::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + swap(root_, other.root_); + // Note: `rightmost_` also contains the allocator and the key comparator. + swap(rightmost_, other.rightmost_); + swap(size_, other.size_); + } else { + if (allocator() == other.allocator()) { + swap(mutable_root(), other.mutable_root()); + swap(*mutable_key_comp(), *other.mutable_key_comp()); + swap(mutable_rightmost(), other.mutable_rightmost()); + swap(size_, other.size_); + } else { + // We aren't allowed to propagate the allocator and the allocator is + // different so we can't take over its memory. We must move each element + // individually. We need both `other` and `this` to have `other`s key + // comparator while moving the values so we can't swap the key + // comparators. + *mutable_key_comp() = other.key_comp(); + copy_or_move_values_in_order(other); + } + } + } + return *this; +} + +template +auto btree

    ::erase(iterator iter) -> iterator { + iter.node_->value_destroy(iter.position_, mutable_allocator()); + iter.update_generation(); + + const bool internal_delete = iter.node_->is_internal(); + if (internal_delete) { + // Deletion of a value on an internal node. First, transfer the largest + // value from our left child here, then erase/rebalance from that position. + // We can get to the largest value from our left child by decrementing iter. + iterator internal_iter(iter); + --iter; + assert(iter.node_->is_leaf()); + internal_iter.node_->transfer(internal_iter.position_, iter.position_, + iter.node_, mutable_allocator()); + } else { + // Shift values after erased position in leaf. In the internal case, we + // don't need to do this because the leaf position is the end of the node. + const field_type transfer_from = iter.position_ + 1; + const field_type num_to_transfer = iter.node_->finish() - transfer_from; + iter.node_->transfer_n(num_to_transfer, iter.position_, transfer_from, + iter.node_, mutable_allocator()); + } + // Update node finish and container size. + iter.node_->set_finish(iter.node_->finish() - 1); + --size_; + + // We want to return the next value after the one we just erased. If we + // erased from an internal node (internal_delete == true), then the next + // value is ++(++iter). If we erased from a leaf node (internal_delete == + // false) then the next value is ++iter. Note that ++iter may point to an + // internal node and the value in the internal node may move to a leaf node + // (iter.node_) when rebalancing is performed at the leaf level. + + iterator res = rebalance_after_delete(iter); + + // If we erased from an internal node, advance the iterator. + if (internal_delete) { + ++res; + } + return res; +} + +template +auto btree

    ::rebalance_after_delete(iterator iter) -> iterator { + // Merge/rebalance as we walk back up the tree. + iterator res(iter); + bool first_iteration = true; + for (;;) { + if (iter.node_ == root()) { + try_shrink(); + if (empty()) { + return end(); + } + break; + } + if (iter.node_->count() >= kMinNodeValues) { + break; + } + bool merged = try_merge_or_rebalance(&iter); + // On the first iteration, we should update `res` with `iter` because `res` + // may have been invalidated. + if (first_iteration) { + res = iter; + first_iteration = false; + } + if (!merged) { + break; + } + iter.position_ = iter.node_->position(); + iter.node_ = iter.node_->parent(); + } + res.update_generation(); + + // Adjust our return value. If we're pointing at the end of a node, advance + // the iterator. + if (res.position_ == res.node_->finish()) { + res.position_ = res.node_->finish() - 1; + ++res; + } + + return res; +} + +template +auto btree

    ::erase_range(iterator begin, iterator end) + -> std::pair { + difference_type count = std::distance(begin, end); + assert(count >= 0); + + if (count == 0) { + return {0, begin}; + } + + if (static_cast(count) == size_) { + clear(); + return {count, this->end()}; + } + + if (begin.node_ == end.node_) { + assert(end.position_ > begin.position_); + begin.node_->remove_values(begin.position_, end.position_ - begin.position_, + mutable_allocator()); + size_ -= count; + return {count, rebalance_after_delete(begin)}; + } + + const size_type target_size = size_ - count; + while (size_ > target_size) { + if (begin.node_->is_leaf()) { + const size_type remaining_to_erase = size_ - target_size; + const size_type remaining_in_node = + begin.node_->finish() - begin.position_; + const size_type to_erase = + (std::min)(remaining_to_erase, remaining_in_node); + begin.node_->remove_values(begin.position_, to_erase, + mutable_allocator()); + size_ -= to_erase; + begin = rebalance_after_delete(begin); + } else { + begin = erase(begin); + } + } + begin.update_generation(); + return {count, begin}; +} + +template +void btree

    ::clear() { + if (!empty()) { + node_type::clear_and_delete(root(), mutable_allocator()); + } + mutable_root() = mutable_rightmost() = EmptyNode(); + size_ = 0; +} + +template +void btree

    ::swap(btree &other) { + using std::swap; + if (absl::allocator_traits< + allocator_type>::propagate_on_container_swap::value) { + // Note: `rightmost_` also contains the allocator and the key comparator. + swap(rightmost_, other.rightmost_); + } else { + // It's undefined behavior if the allocators are unequal here. + assert(allocator() == other.allocator()); + swap(mutable_rightmost(), other.mutable_rightmost()); + swap(*mutable_key_comp(), *other.mutable_key_comp()); + } + swap(mutable_root(), other.mutable_root()); + swap(size_, other.size_); +} + +template +void btree

    ::verify() const { + assert(root() != nullptr); + assert(leftmost() != nullptr); + assert(rightmost() != nullptr); + assert(empty() || size() == internal_verify(root(), nullptr, nullptr)); + assert(leftmost() == (++const_iterator(root(), -1)).node_); + assert(rightmost() == (--const_iterator(root(), root()->finish())).node_); + assert(leftmost()->is_leaf()); + assert(rightmost()->is_leaf()); +} + +template +void btree

    ::rebalance_or_split(iterator *iter) { + node_type *&node = iter->node_; + int &insert_position = iter->position_; + assert(node->count() == node->max_count()); + assert(kNodeSlots == node->max_count()); + + // First try to make room on the node by rebalancing. + node_type *parent = node->parent(); + if (node != root()) { + if (node->position() > parent->start()) { + // Try rebalancing with our left sibling. + node_type *left = parent->child(node->position() - 1); + assert(left->max_count() == kNodeSlots); + if (left->count() < kNodeSlots) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the end of the right node then we bias rebalancing to + // fill up the left node. + int to_move = (kNodeSlots - left->count()) / + (1 + (insert_position < static_cast(kNodeSlots))); + to_move = (std::max)(1, to_move); + + if (insert_position - to_move >= node->start() || + left->count() + to_move < static_cast(kNodeSlots)) { + left->rebalance_right_to_left(to_move, node, mutable_allocator()); + + assert(node->max_count() - node->count() == to_move); + insert_position = insert_position - to_move; + if (insert_position < node->start()) { + insert_position = insert_position + left->count() + 1; + node = left; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + if (node->position() < parent->finish()) { + // Try rebalancing with our right sibling. + node_type *right = parent->child(node->position() + 1); + assert(right->max_count() == kNodeSlots); + if (right->count() < kNodeSlots) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the beginning of the left node then we bias rebalancing + // to fill up the right node. + int to_move = (static_cast(kNodeSlots) - right->count()) / + (1 + (insert_position > node->start())); + to_move = (std::max)(1, to_move); + + if (insert_position <= node->finish() - to_move || + right->count() + to_move < static_cast(kNodeSlots)) { + node->rebalance_left_to_right(to_move, right, mutable_allocator()); + + if (insert_position > node->finish()) { + insert_position = insert_position - node->count() - 1; + node = right; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + // Rebalancing failed, make sure there is room on the parent node for a new + // value. + assert(parent->max_count() == kNodeSlots); + if (parent->count() == kNodeSlots) { + iterator parent_iter(node->parent(), node->position()); + rebalance_or_split(&parent_iter); + } + } else { + // Rebalancing not possible because this is the root node. + // Create a new root node and set the current root node as the child of the + // new root. + parent = new_internal_node(parent); + parent->set_generation(root()->generation()); + parent->init_child(parent->start(), root()); + mutable_root() = parent; + // If the former root was a leaf node, then it's now the rightmost node. + assert(parent->start_child()->is_internal() || + parent->start_child() == rightmost()); + } + + // Split the node. + node_type *split_node; + if (node->is_leaf()) { + split_node = new_leaf_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + if (rightmost() == node) mutable_rightmost() = split_node; + } else { + split_node = new_internal_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + } + + if (insert_position > node->finish()) { + insert_position = insert_position - node->count() - 1; + node = split_node; + } +} + +template +void btree

    ::merge_nodes(node_type *left, node_type *right) { + left->merge(right, mutable_allocator()); + if (rightmost() == right) mutable_rightmost() = left; +} + +template +bool btree

    ::try_merge_or_rebalance(iterator *iter) { + node_type *parent = iter->node_->parent(); + if (iter->node_->position() > parent->start()) { + // Try merging with our left sibling. + node_type *left = parent->child(iter->node_->position() - 1); + assert(left->max_count() == kNodeSlots); + if (1U + left->count() + iter->node_->count() <= kNodeSlots) { + iter->position_ += 1 + left->count(); + merge_nodes(left, iter->node_); + iter->node_ = left; + return true; + } + } + if (iter->node_->position() < parent->finish()) { + // Try merging with our right sibling. + node_type *right = parent->child(iter->node_->position() + 1); + assert(right->max_count() == kNodeSlots); + if (1U + iter->node_->count() + right->count() <= kNodeSlots) { + merge_nodes(iter->node_, right); + return true; + } + // Try rebalancing with our right sibling. We don't perform rebalancing if + // we deleted the first element from iter->node_ and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the front of the tree. + if (right->count() > kMinNodeValues && + (iter->node_->count() == 0 || iter->position_ > iter->node_->start())) { + int to_move = (right->count() - iter->node_->count()) / 2; + to_move = (std::min)(to_move, right->count() - 1); + iter->node_->rebalance_right_to_left(to_move, right, mutable_allocator()); + return false; + } + } + if (iter->node_->position() > parent->start()) { + // Try rebalancing with our left sibling. We don't perform rebalancing if + // we deleted the last element from iter->node_ and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the back of the tree. + node_type *left = parent->child(iter->node_->position() - 1); + if (left->count() > kMinNodeValues && + (iter->node_->count() == 0 || + iter->position_ < iter->node_->finish())) { + int to_move = (left->count() - iter->node_->count()) / 2; + to_move = (std::min)(to_move, left->count() - 1); + left->rebalance_left_to_right(to_move, iter->node_, mutable_allocator()); + iter->position_ += to_move; + return false; + } + } + return false; +} + +template +void btree

    ::try_shrink() { + node_type *orig_root = root(); + if (orig_root->count() > 0) { + return; + } + // Deleted the last item on the root node, shrink the height of the tree. + if (orig_root->is_leaf()) { + assert(size() == 0); + mutable_root() = mutable_rightmost() = EmptyNode(); + } else { + node_type *child = orig_root->start_child(); + child->make_root(); + mutable_root() = child; + } + node_type::clear_and_delete(orig_root, mutable_allocator()); +} + +template +template +inline IterType btree

    ::internal_last(IterType iter) { + assert(iter.node_ != nullptr); + while (iter.position_ == iter.node_->finish()) { + iter.position_ = iter.node_->position(); + iter.node_ = iter.node_->parent(); + if (iter.node_->is_leaf()) { + iter.node_ = nullptr; + break; + } + } + iter.update_generation(); + return iter; +} + +template +template +inline auto btree

    ::internal_emplace(iterator iter, Args &&... args) + -> iterator { + if (iter.node_->is_internal()) { + // We can't insert on an internal node. Instead, we'll insert after the + // previous value which is guaranteed to be on a leaf node. + --iter; + ++iter.position_; + } + const field_type max_count = iter.node_->max_count(); + allocator_type *alloc = mutable_allocator(); + if (iter.node_->count() == max_count) { + // Make room in the leaf for the new item. + if (max_count < kNodeSlots) { + // Insertion into the root where the root is smaller than the full node + // size. Simply grow the size of the root node. + assert(iter.node_ == root()); + iter.node_ = + new_leaf_root_node((std::min)(kNodeSlots, 2 * max_count)); + // Transfer the values from the old root to the new root. + node_type *old_root = root(); + node_type *new_root = iter.node_; + new_root->transfer_n(old_root->count(), new_root->start(), + old_root->start(), old_root, alloc); + new_root->set_finish(old_root->finish()); + old_root->set_finish(old_root->start()); + new_root->set_generation(old_root->generation()); + node_type::clear_and_delete(old_root, alloc); + mutable_root() = mutable_rightmost() = new_root; + } else { + rebalance_or_split(&iter); + } + } + iter.node_->emplace_value(iter.position_, alloc, std::forward(args)...); + ++size_; + iter.update_generation(); + return iter; +} + +template +template +inline auto btree

    ::internal_locate(const K &key) const + -> SearchResult { + iterator iter(const_cast(root())); + for (;;) { + SearchResult res = + iter.node_->lower_bound(key, key_comp()); + iter.position_ = res.value; + if (res.IsEq()) { + return {iter, MatchKind::kEq}; + } + // Note: in the non-key-compare-to case, we don't need to walk all the way + // down the tree if the keys are equal, but determining equality would + // require doing an extra comparison on each node on the way down, and we + // will need to go all the way to the leaf node in the expected case. + if (iter.node_->is_leaf()) { + break; + } + iter.node_ = iter.node_->child(iter.position_); + } + // Note: in the non-key-compare-to case, the key may actually be equivalent + // here (and the MatchKind::kNe is ignored). + return {iter, MatchKind::kNe}; +} + +template +template +auto btree

    ::internal_lower_bound(const K &key) const + -> SearchResult { + if (!params_type::template can_have_multiple_equivalent_keys()) { + SearchResult ret = internal_locate(key); + ret.value = internal_last(ret.value); + return ret; + } + iterator iter(const_cast(root())); + SearchResult res; + bool seen_eq = false; + for (;;) { + res = iter.node_->lower_bound(key, key_comp()); + iter.position_ = res.value; + if (iter.node_->is_leaf()) { + break; + } + seen_eq = seen_eq || res.IsEq(); + iter.node_ = iter.node_->child(iter.position_); + } + if (res.IsEq()) return {iter, MatchKind::kEq}; + return {internal_last(iter), seen_eq ? MatchKind::kEq : MatchKind::kNe}; +} + +template +template +auto btree

    ::internal_upper_bound(const K &key) const -> iterator { + iterator iter(const_cast(root())); + for (;;) { + iter.position_ = iter.node_->upper_bound(key, key_comp()); + if (iter.node_->is_leaf()) { + break; + } + iter.node_ = iter.node_->child(iter.position_); + } + return internal_last(iter); +} + +template +template +auto btree

    ::internal_find(const K &key) const -> iterator { + SearchResult res = internal_locate(key); + if (res.HasMatch()) { + if (res.IsEq()) { + return res.value; + } + } else { + const iterator iter = internal_last(res.value); + if (iter.node_ != nullptr && !compare_keys(key, iter.key())) { + return iter; + } + } + return {nullptr, 0}; +} + +template +int btree

    ::internal_verify(const node_type *node, const key_type *lo, + const key_type *hi) const { + assert(node->count() > 0); + assert(node->count() <= node->max_count()); + if (lo) { + assert(!compare_keys(node->key(node->start()), *lo)); + } + if (hi) { + assert(!compare_keys(*hi, node->key(node->finish() - 1))); + } + for (int i = node->start() + 1; i < node->finish(); ++i) { + assert(!compare_keys(node->key(i), node->key(i - 1))); + } + int count = node->count(); + if (node->is_internal()) { + for (int i = node->start(); i <= node->finish(); ++i) { + assert(node->child(i) != nullptr); + assert(node->child(i)->parent() == node); + assert(node->child(i)->position() == i); + count += internal_verify(node->child(i), + i == node->start() ? lo : &node->key(i - 1), + i == node->finish() ? hi : &node->key(i)); + } + } + return count; +} + +struct btree_access { + template + static auto erase_if(BtreeContainer &container, Pred pred) + -> typename BtreeContainer::size_type { + const auto initial_size = container.size(); + auto &tree = container.tree_; + auto *alloc = tree.mutable_allocator(); + for (auto it = container.begin(); it != container.end();) { + if (!pred(*it)) { + ++it; + continue; + } + auto *node = it.node_; + if (node->is_internal()) { + // Handle internal nodes normally. + it = container.erase(it); + continue; + } + // If this is a leaf node, then we do all the erases from this node + // at once before doing rebalancing. + + // The current position to transfer slots to. + int to_pos = it.position_; + node->value_destroy(it.position_, alloc); + while (++it.position_ < node->finish()) { + it.update_generation(); + if (pred(*it)) { + node->value_destroy(it.position_, alloc); + } else { + node->transfer(node->slot(to_pos++), node->slot(it.position_), alloc); + } + } + const int num_deleted = node->finish() - to_pos; + tree.size_ -= num_deleted; + node->set_finish(to_pos); + it.position_ = to_pos; + it = tree.rebalance_after_delete(it); + } + return initial_size - container.size(); + } +}; + +#undef ABSL_BTREE_ENABLE_GENERATIONS + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_BTREE_H_ diff --git a/src/absl/container/internal/btree_container.h b/src/absl/container/internal/btree_container.h new file mode 100644 index 0000000..fc2f740 --- /dev/null +++ b/src/absl/container/internal/btree_container.h @@ -0,0 +1,699 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ +#define ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/common.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A common base class for btree_set, btree_map, btree_multiset, and +// btree_multimap. +template +class btree_container { + using params_type = typename Tree::params_type; + + protected: + // Alias used for heterogeneous lookup functions. + // `key_arg` 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 + using key_arg = + typename KeyArg::template type< + K, typename Tree::key_type>; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using difference_type = typename Tree::difference_type; + using key_compare = typename Tree::original_key_compare; + using value_compare = typename Tree::value_compare; + using allocator_type = typename Tree::allocator_type; + using reference = typename Tree::reference; + using const_reference = typename Tree::const_reference; + using pointer = typename Tree::pointer; + using const_pointer = typename Tree::const_pointer; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using reverse_iterator = typename Tree::reverse_iterator; + using const_reverse_iterator = typename Tree::const_reverse_iterator; + using node_type = typename Tree::node_handle_type; + + // Constructors/assignments. + btree_container() : tree_(key_compare(), allocator_type()) {} + explicit btree_container(const key_compare &comp, + const allocator_type &alloc = allocator_type()) + : tree_(comp, alloc) {} + explicit btree_container(const allocator_type &alloc) + : tree_(key_compare(), alloc) {} + + btree_container(const btree_container &other) + : btree_container(other, absl::allocator_traits:: + select_on_container_copy_construction( + other.get_allocator())) {} + btree_container(const btree_container &other, const allocator_type &alloc) + : tree_(other.tree_, alloc) {} + + btree_container(btree_container &&other) noexcept( + std::is_nothrow_move_constructible::value) = default; + btree_container(btree_container &&other, const allocator_type &alloc) + : tree_(std::move(other.tree_), alloc) {} + + btree_container &operator=(const btree_container &other) = default; + btree_container &operator=(btree_container &&other) noexcept( + std::is_nothrow_move_assignable::value) = default; + + // Iterator routines. + iterator begin() { return tree_.begin(); } + const_iterator begin() const { return tree_.begin(); } + const_iterator cbegin() const { return tree_.begin(); } + iterator end() { return tree_.end(); } + const_iterator end() const { return tree_.end(); } + const_iterator cend() const { return tree_.end(); } + reverse_iterator rbegin() { return tree_.rbegin(); } + const_reverse_iterator rbegin() const { return tree_.rbegin(); } + const_reverse_iterator crbegin() const { return tree_.rbegin(); } + reverse_iterator rend() { return tree_.rend(); } + const_reverse_iterator rend() const { return tree_.rend(); } + const_reverse_iterator crend() const { return tree_.rend(); } + + // Lookup routines. + template + size_type count(const key_arg &key) const { + auto equal_range = this->equal_range(key); + return std::distance(equal_range.first, equal_range.second); + } + template + iterator find(const key_arg &key) { + return tree_.find(key); + } + template + const_iterator find(const key_arg &key) const { + return tree_.find(key); + } + template + bool contains(const key_arg &key) const { + return find(key) != end(); + } + template + iterator lower_bound(const key_arg &key) { + return tree_.lower_bound(key); + } + template + const_iterator lower_bound(const key_arg &key) const { + return tree_.lower_bound(key); + } + template + iterator upper_bound(const key_arg &key) { + return tree_.upper_bound(key); + } + template + const_iterator upper_bound(const key_arg &key) const { + return tree_.upper_bound(key); + } + template + std::pair equal_range(const key_arg &key) { + return tree_.equal_range(key); + } + template + std::pair equal_range( + const key_arg &key) const { + return tree_.equal_range(key); + } + + // Deletion routines. Note that there is also a deletion routine that is + // specific to btree_set_container/btree_multiset_container. + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); } + iterator erase(iterator iter) { return tree_.erase(iter); } + iterator erase(const_iterator first, const_iterator last) { + return tree_.erase_range(iterator(first), iterator(last)).second; + } + template + size_type erase(const key_arg &key) { + auto equal_range = this->equal_range(key); + return tree_.erase_range(equal_range.first, equal_range.second).first; + } + + // Extract routines. + node_type extract(iterator position) { + // Use Construct instead of Transfer because the rebalancing code will + // destroy the slot later. + auto node = + CommonAccess::Construct(get_allocator(), position.slot()); + erase(position); + return node; + } + node_type extract(const_iterator position) { + return extract(iterator(position)); + } + + // Utility routines. + ABSL_ATTRIBUTE_REINITIALIZES void clear() { tree_.clear(); } + void swap(btree_container &other) { tree_.swap(other.tree_); } + void verify() const { tree_.verify(); } + + // Size routines. + size_type size() const { return tree_.size(); } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { return tree_.empty(); } + + friend bool operator==(const btree_container &x, const btree_container &y) { + if (x.size() != y.size()) return false; + return std::equal(x.begin(), x.end(), y.begin()); + } + + friend bool operator!=(const btree_container &x, const btree_container &y) { + return !(x == y); + } + + friend bool operator<(const btree_container &x, const btree_container &y) { + return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); + } + + friend bool operator>(const btree_container &x, const btree_container &y) { + return y < x; + } + + friend bool operator<=(const btree_container &x, const btree_container &y) { + return !(y < x); + } + + friend bool operator>=(const btree_container &x, const btree_container &y) { + return !(x < y); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { return tree_.get_allocator(); } + + // The key comparator used by the btree. + key_compare key_comp() const { return key_compare(tree_.key_comp()); } + value_compare value_comp() const { return tree_.value_comp(); } + + // Support absl::Hash. + template + friend State AbslHashValue(State h, const btree_container &b) { + for (const auto &v : b) { + h = State::combine(std::move(h), v); + } + return State::combine(std::move(h), b.size()); + } + + protected: + friend struct btree_access; + Tree tree_; +}; + +// A common base class for btree_set and btree_map. +template +class btree_set_container : public btree_container { + using super_type = btree_container; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + friend class BtreeNodePeer; + + protected: + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::original_key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + using insert_return_type = InsertReturnType; + + // Inherit constructors. + using super_type::super_type; + btree_set_container() {} + + // Range constructors. + template + btree_set_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + template + btree_set_container(InputIterator b, InputIterator e, + const allocator_type &alloc) + : btree_set_container(b, e, key_compare(), alloc) {} + + // Initializer list constructors. + btree_set_container(std::initializer_list init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_set_container(init.begin(), init.end(), comp, alloc) {} + btree_set_container(std::initializer_list init, + const allocator_type &alloc) + : btree_set_container(init.begin(), init.end(), alloc) {} + + // Insertion routines. + std::pair insert(const value_type &v) { + return this->tree_.insert_unique(params_type::key(v), v); + } + std::pair insert(value_type &&v) { + return this->tree_.insert_unique(params_type::key(v), std::move(v)); + } + template + std::pair emplace(Args &&... args) { + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + auto *slot = CommonAccess::GetSlot(node); + return this->tree_.insert_unique(params_type::key(slot), slot); + } + iterator insert(const_iterator hint, const value_type &v) { + return this->tree_ + .insert_hint_unique(iterator(hint), params_type::key(v), v) + .first; + } + iterator insert(const_iterator hint, value_type &&v) { + return this->tree_ + .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v)) + .first; + } + template + iterator emplace_hint(const_iterator hint, Args &&... args) { + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + auto *slot = CommonAccess::GetSlot(node); + return this->tree_ + .insert_hint_unique(iterator(hint), params_type::key(slot), slot) + .first; + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_unique(b, e, 0); + } + void insert(std::initializer_list init) { + this->tree_.insert_iterator_unique(init.begin(), init.end(), 0); + } + insert_return_type insert(node_type &&node) { + if (!node) return {this->end(), false, node_type()}; + std::pair res = + this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) { + CommonAccess::Destroy(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + iterator insert(const_iterator hint, node_type &&node) { + if (!node) return this->end(); + std::pair res = this->tree_.insert_hint_unique( + iterator(hint), params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) CommonAccess::Destroy(&node); + return res.first; + } + + // Node extraction routines. + template + node_type extract(const key_arg &key) { + const std::pair lower_and_equal = + this->tree_.lower_bound_equal(key); + return lower_and_equal.second ? extract(lower_and_equal.first) + : node_type(); + } + using super_type::extract; + + // Merge routines. + // Moves elements from `src` into `this`. If the element already exists in + // `this`, it is left unmodified in `src`. + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &src) { // NOLINT + for (auto src_it = src.begin(); src_it != src.end();) { + if (insert(std::move(params_type::element(src_it.slot()))).second) { + src_it = src.erase(src_it); + } else { + ++src_it; + } + } + } + + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &&src) { + merge(src); + } +}; + +// Base class for btree_map. +template +class btree_map_container : public btree_set_container { + using super_type = btree_set_container; + using params_type = typename Tree::params_type; + friend class BtreeNodePeer; + + private: + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using mapped_type = typename params_type::mapped_type; + using value_type = typename Tree::value_type; + using key_compare = typename Tree::original_key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + + // Inherit constructors. + using super_type::super_type; + btree_map_container() {} + + // Insertion routines. + // Note: the nullptr template arguments and extra `const M&` overloads allow + // for supporting bitfield arguments. + template + std::pair insert_or_assign(const key_arg &k, + const M &obj) { + return insert_or_assign_impl(k, obj); + } + template + std::pair insert_or_assign(key_arg &&k, const M &obj) { + return insert_or_assign_impl(std::forward(k), obj); + } + template + std::pair insert_or_assign(const key_arg &k, M &&obj) { + return insert_or_assign_impl(k, std::forward(obj)); + } + template + std::pair insert_or_assign(key_arg &&k, M &&obj) { + return insert_or_assign_impl(std::forward(k), std::forward(obj)); + } + template + iterator insert_or_assign(const_iterator hint, const key_arg &k, + const M &obj) { + return insert_or_assign_hint_impl(hint, k, obj); + } + template + iterator insert_or_assign(const_iterator hint, key_arg &&k, const M &obj) { + return insert_or_assign_hint_impl(hint, std::forward(k), obj); + } + template + iterator insert_or_assign(const_iterator hint, const key_arg &k, M &&obj) { + return insert_or_assign_hint_impl(hint, k, std::forward(obj)); + } + template + iterator insert_or_assign(const_iterator hint, key_arg &&k, M &&obj) { + return insert_or_assign_hint_impl(hint, std::forward(k), + std::forward(obj)); + } + + template ::value, int> = 0> + std::pair try_emplace(const key_arg &k, Args &&... args) { + return try_emplace_impl(k, std::forward(args)...); + } + template ::value, int> = 0> + std::pair try_emplace(key_arg &&k, Args &&... args) { + return try_emplace_impl(std::forward(k), std::forward(args)...); + } + template + iterator try_emplace(const_iterator hint, const key_arg &k, + Args &&... args) { + return try_emplace_hint_impl(hint, k, std::forward(args)...); + } + template + iterator try_emplace(const_iterator hint, key_arg &&k, Args &&... args) { + return try_emplace_hint_impl(hint, std::forward(k), + std::forward(args)...); + } + + template + mapped_type &operator[](const key_arg &k) { + return try_emplace(k).first->second; + } + template + mapped_type &operator[](key_arg &&k) { + return try_emplace(std::forward(k)).first->second; + } + + template + mapped_type &at(const key_arg &key) { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + return it->second; + } + template + const mapped_type &at(const key_arg &key) const { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + return it->second; + } + + private: + // Note: when we call `std::forward(obj)` twice, it's safe because + // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when + // `ret.second` is false. + template + std::pair insert_or_assign_impl(K &&k, M &&obj) { + const std::pair ret = + this->tree_.insert_unique(k, std::forward(k), std::forward(obj)); + if (!ret.second) ret.first->second = std::forward(obj); + return ret; + } + template + iterator insert_or_assign_hint_impl(const_iterator hint, K &&k, M &&obj) { + const std::pair ret = this->tree_.insert_hint_unique( + iterator(hint), k, std::forward(k), std::forward(obj)); + if (!ret.second) ret.first->second = std::forward(obj); + return ret.first; + } + + template + std::pair try_emplace_impl(K &&k, Args &&... args) { + return this->tree_.insert_unique( + k, std::piecewise_construct, std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + } + template + iterator try_emplace_hint_impl(const_iterator hint, K &&k, Args &&... args) { + return this->tree_ + .insert_hint_unique(iterator(hint), k, std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)) + .first; + } +}; + +// A common base class for btree_multiset and btree_multimap. +template +class btree_multiset_container : public btree_container { + using super_type = btree_container; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + friend class BtreeNodePeer; + + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::original_key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + + // Inherit constructors. + using super_type::super_type; + btree_multiset_container() {} + + // Range constructors. + template + btree_multiset_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + template + btree_multiset_container(InputIterator b, InputIterator e, + const allocator_type &alloc) + : btree_multiset_container(b, e, key_compare(), alloc) {} + + // Initializer list constructors. + btree_multiset_container(std::initializer_list init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_multiset_container(init.begin(), init.end(), comp, alloc) {} + btree_multiset_container(std::initializer_list init, + const allocator_type &alloc) + : btree_multiset_container(init.begin(), init.end(), alloc) {} + + // Insertion routines. + iterator insert(const value_type &v) { return this->tree_.insert_multi(v); } + iterator insert(value_type &&v) { + return this->tree_.insert_multi(std::move(v)); + } + iterator insert(const_iterator hint, const value_type &v) { + return this->tree_.insert_hint_multi(iterator(hint), v); + } + iterator insert(const_iterator hint, value_type &&v) { + return this->tree_.insert_hint_multi(iterator(hint), std::move(v)); + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_multi(b, e); + } + void insert(std::initializer_list init) { + this->tree_.insert_iterator_multi(init.begin(), init.end()); + } + template + iterator emplace(Args &&... args) { + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + return this->tree_.insert_multi(CommonAccess::GetSlot(node)); + } + template + iterator emplace_hint(const_iterator hint, Args &&... args) { + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + return this->tree_.insert_hint_multi(iterator(hint), + CommonAccess::GetSlot(node)); + } + iterator insert(node_type &&node) { + if (!node) return this->end(); + iterator res = + this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + CommonAccess::Destroy(&node); + return res; + } + iterator insert(const_iterator hint, node_type &&node) { + if (!node) return this->end(); + iterator res = this->tree_.insert_hint_multi( + iterator(hint), + std::move(params_type::element(CommonAccess::GetSlot(node)))); + CommonAccess::Destroy(&node); + return res; + } + + // Node extraction routines. + template + node_type extract(const key_arg &key) { + const std::pair lower_and_equal = + this->tree_.lower_bound_equal(key); + return lower_and_equal.second ? extract(lower_and_equal.first) + : node_type(); + } + using super_type::extract; + + // Merge routines. + // Moves all elements from `src` into `this`. + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &src) { // NOLINT + for (auto src_it = src.begin(), end = src.end(); src_it != end; ++src_it) { + insert(std::move(params_type::element(src_it.slot()))); + } + src.clear(); + } + + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &&src) { + merge(src); + } +}; + +// A base class for btree_multimap. +template +class btree_multimap_container : public btree_multiset_container { + using super_type = btree_multiset_container; + using params_type = typename Tree::params_type; + friend class BtreeNodePeer; + + public: + using mapped_type = typename params_type::mapped_type; + + // Inherit constructors. + using super_type::super_type; + btree_multimap_container() {} +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ diff --git a/src/absl/container/internal/common.h b/src/absl/container/internal/common.h new file mode 100644 index 0000000..416d9aa --- /dev/null +++ b/src/absl/container/internal/common.h @@ -0,0 +1,207 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_H_ +#define ABSL_CONTAINER_INTERNAL_CONTAINER_H_ + +#include +#include + +#include "absl/meta/type_traits.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct IsTransparent : std::false_type {}; +template +struct IsTransparent> + : std::true_type {}; + +template +struct KeyArg { + // Transparent. Forward `K`. + template + using type = K; +}; + +template <> +struct KeyArg { + // Not transparent. Always use `key_type`. + template + using type = key_type; +}; + +// The node_handle concept from C++17. +// We specialize node_handle for sets and maps. node_handle_base holds the +// common API of both. +template +class node_handle_base { + protected: + using slot_type = typename PolicyTraits::slot_type; + + public: + using allocator_type = Alloc; + + constexpr node_handle_base() = default; + node_handle_base(node_handle_base&& other) noexcept { + *this = std::move(other); + } + ~node_handle_base() { destroy(); } + node_handle_base& operator=(node_handle_base&& other) noexcept { + destroy(); + if (!other.empty()) { + alloc_ = other.alloc_; + PolicyTraits::transfer(alloc(), slot(), other.slot()); + other.reset(); + } + return *this; + } + + bool empty() const noexcept { return !alloc_; } + explicit operator bool() const noexcept { return !empty(); } + allocator_type get_allocator() const { return *alloc_; } + + protected: + friend struct CommonAccess; + + struct transfer_tag_t {}; + node_handle_base(transfer_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::transfer(alloc(), slot(), s); + } + + struct construct_tag_t {}; + template + node_handle_base(construct_tag_t, const allocator_type& a, Args&&... args) + : alloc_(a) { + PolicyTraits::construct(alloc(), slot(), std::forward(args)...); + } + + void destroy() { + if (!empty()) { + PolicyTraits::destroy(alloc(), slot()); + reset(); + } + } + + void reset() { + assert(alloc_.has_value()); + alloc_ = absl::nullopt; + } + + slot_type* slot() const { + assert(!empty()); + return reinterpret_cast(std::addressof(slot_space_)); + } + allocator_type* alloc() { return std::addressof(*alloc_); } + + private: + absl::optional alloc_ = {}; + alignas(slot_type) mutable unsigned char slot_space_[sizeof(slot_type)] = {}; +}; + +// For sets. +template +class node_handle : public node_handle_base { + using Base = node_handle_base; + + public: + using value_type = typename PolicyTraits::value_type; + + constexpr node_handle() {} + + value_type& value() const { return PolicyTraits::element(this->slot()); } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// For maps. +template +class node_handle> + : public node_handle_base { + using Base = node_handle_base; + using slot_type = typename PolicyTraits::slot_type; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + + constexpr node_handle() {} + + // When C++17 is available, we can use std::launder to provide mutable + // access to the key. Otherwise, we provide const access. + auto key() const + -> decltype(PolicyTraits::mutable_key(std::declval())) { + return PolicyTraits::mutable_key(this->slot()); + } + + mapped_type& mapped() const { + return PolicyTraits::value(&PolicyTraits::element(this->slot())); + } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// Provide access to non-public node-handle functions. +struct CommonAccess { + template + static auto GetSlot(const Node& node) -> decltype(node.slot()) { + return node.slot(); + } + + template + static void Destroy(Node* node) { + node->destroy(); + } + + template + static void Reset(Node* node) { + node->reset(); + } + + template + static T Transfer(Args&&... args) { + return T(typename T::transfer_tag_t{}, std::forward(args)...); + } + + template + static T Construct(Args&&... args) { + return T(typename T::construct_tag_t{}, std::forward(args)...); + } +}; + +// Implement the insert_return_type<> concept of C++17. +template +struct InsertReturnType { + Iterator position; + bool inserted; + NodeType node; +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_H_ diff --git a/src/absl/container/internal/compressed_tuple.h b/src/absl/container/internal/compressed_tuple.h new file mode 100644 index 0000000..5ebe164 --- /dev/null +++ b/src/absl/container/internal/compressed_tuple.h @@ -0,0 +1,290 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Helper class to perform the Empty Base Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the optimization. If all types in Ts are empty +// classes, then CompressedTuple is itself an empty class. +// +// To access the members, use member get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// https://en.cppreference.com/w/cpp/language/ebo + +#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ +#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ + +#include +#include +#include +#include + +#include "absl/utility/utility.h" + +#if defined(_MSC_VER) && !defined(__NVCC__) +// We need to mark these classes with this declspec to ensure that +// CompressedTuple happens. +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases) +#else +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class CompressedTuple; + +namespace internal_compressed_tuple { + +template +struct Elem; +template +struct Elem, I> + : std::tuple_element> {}; +template +using ElemT = typename Elem::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 +constexpr bool IsFinal() { +#if defined(__clang__) || defined(__GNUC__) + return __is_final(T); +#else + return false; +#endif +} + +// We can't use EBCO on other CompressedTuples because that would mean that we +// derive from multiple Storage<> instantiations with the same I parameter, +// and potentially from multiple identical Storage<> instantiations. So anytime +// we use type inheritance rather than encapsulation, we mark +// CompressedTupleImpl, to make this easy to detect. +struct uses_inheritance {}; + +template +constexpr bool ShouldUseBase() { + return std::is_class::value && std::is_empty::value && !IsFinal() && + !std::is_base_of::value; +} + +// The storage class provides two specializations: +// - For empty classes, it stores T as a base class. +// - For everything else, it stores T as a member. +template ::type>()> +#else + bool UseBase = ShouldUseBase()> +#endif +struct Storage { + T value; + constexpr Storage() = default; + template + explicit constexpr Storage(absl::in_place_t, V&& v) + : value(absl::forward(v)) {} + constexpr const T& get() const& { return value; } + T& get() & { return value; } + constexpr const T&& get() const&& { return absl::move(*this).value; } + T&& get() && { return std::move(*this).value; } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage : T { + constexpr Storage() = default; + + template + explicit constexpr Storage(absl::in_place_t, V&& v) + : T(absl::forward(v)) {} + + constexpr const T& get() const& { return *this; } + T& get() & { return *this; } + constexpr const T&& get() const&& { return absl::move(*this); } + T&& get() && { return std::move(*this); } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple, absl::index_sequence, ShouldAnyUseBase> + // We use the dummy identity function through std::integral_constant to + // convince MSVC of accepting and expanding I in that context. Without it + // you would get: + // error C3548: 'I': parameter pack cannot be used in this context + : uses_inheritance, + Storage::value>... { + constexpr CompressedTupleImpl() = default; + template + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage(absl::in_place, absl::forward(args))... {} + friend CompressedTuple; +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple, absl::index_sequence, false> + // We use the dummy identity function as above... + : Storage::value, false>... { + constexpr CompressedTupleImpl() = default; + template + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage(absl::in_place, absl::forward(args))... {} + friend CompressedTuple; +}; + +std::false_type Or(std::initializer_list); +std::true_type Or(std::initializer_list); + +// MSVC requires this to be done separately rather than within the declaration +// of CompressedTuple below. +template +constexpr bool ShouldAnyUseBase() { + return decltype( + Or({std::integral_constant()>()...})){}; +} + +template +using TupleElementMoveConstructible = + typename std::conditional::value, + std::is_convertible, + std::is_constructible>::type; + +template +struct TupleMoveConstructible : std::false_type {}; + +template +struct TupleMoveConstructible, Vs...> + : std::integral_constant< + bool, absl::conjunction< + TupleElementMoveConstructible...>::value> {}; + +template +struct compressed_tuple_size; + +template +struct compressed_tuple_size> + : public std::integral_constant {}; + +template +struct TupleItemsMoveConstructible + : std::integral_constant< + bool, TupleMoveConstructible::value == + sizeof...(Vs), + T, Vs...>::value> {}; + +} // namespace internal_compressed_tuple + +// Helper class to perform the Empty Base Class Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the CompressedTuple. If all types in Ts are +// empty classes, then CompressedTuple is itself an empty class. (This +// does not apply when one or more of those empty classes is itself an empty +// CompressedTuple.) +// +// To access the members, use member .get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// https://en.cppreference.com/w/cpp/language/ebo +template +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple + : private internal_compressed_tuple::CompressedTupleImpl< + CompressedTuple, absl::index_sequence_for, + internal_compressed_tuple::ShouldAnyUseBase()> { + private: + template + using ElemT = internal_compressed_tuple::ElemT; + + template + using StorageT = internal_compressed_tuple::Storage, I>; + + public: + // There seems to be a bug in MSVC dealing in which using '=default' here will + // cause the compiler to ignore the body of other constructors. The work- + // around is to explicitly implement the default constructor. +#if defined(_MSC_VER) + constexpr CompressedTuple() : CompressedTuple::CompressedTupleImpl() {} +#else + constexpr CompressedTuple() = default; +#endif + explicit constexpr CompressedTuple(const Ts&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {} + + template )>>, + internal_compressed_tuple::TupleItemsMoveConstructible< + CompressedTuple, First, Vs...>>::value, + bool> = true> + explicit constexpr CompressedTuple(First&& first, Vs&&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, + absl::forward(first), + absl::forward(base)...) {} + + template + ElemT& get() & { + return StorageT::get(); + } + + template + constexpr const ElemT& get() const& { + return StorageT::get(); + } + + template + ElemT&& get() && { + return std::move(*this).StorageT::get(); + } + + template + constexpr const ElemT&& get() const&& { + return absl::move(*this).StorageT::get(); + } +}; + +// Explicit specialization for a zero-element tuple +// (needed to avoid ambiguous overloads for the default constructor). +template <> +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC + +#endif // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ diff --git a/src/absl/container/internal/container_memory.h b/src/absl/container/internal/container_memory.h new file mode 100644 index 0000000..00e9f6d --- /dev/null +++ b/src/absl/container/internal/container_memory.h @@ -0,0 +1,442 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ +#define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#include +#endif + +#ifdef ABSL_HAVE_MEMORY_SANITIZER +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct alignas(Alignment) AlignedType {}; + +// Allocates at least n bytes aligned to the specified alignment. +// Alignment must be a power of 2. It must be positive. +// +// Note that many allocators don't honor alignment requirements above certain +// threshold (usually either alignof(std::max_align_t) or alignof(void*)). +// Allocate() doesn't apply alignment corrections. If the underlying allocator +// returns insufficiently alignment pointer, that's what you are going to get. +template +void* Allocate(Alloc* alloc, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + using M = AlignedType; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + // On macOS, "mem_alloc" is a #define with one argument defined in + // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it + // with the "foo(bar)" syntax. + A my_mem_alloc(*alloc); + void* p = AT::allocate(my_mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); + assert(reinterpret_cast(p) % Alignment == 0 && + "allocator does not respect alignment"); + return p; +} + +// The pointer must have been previously obtained by calling +// Allocate(alloc, n). +template +void Deallocate(Alloc* alloc, void* p, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + using M = AlignedType; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + // On macOS, "mem_alloc" is a #define with one argument defined in + // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it + // with the "foo(bar)" syntax. + A my_mem_alloc(*alloc); + AT::deallocate(my_mem_alloc, static_cast(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 +void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, + absl::index_sequence) { + absl::allocator_traits::construct( + *alloc, ptr, std::get(std::forward(t))...); +} + +template +struct WithConstructedImplF { + template + decltype(std::declval()(std::declval())) operator()( + Args&&... args) const { + return std::forward(f)(T(std::forward(args)...)); + } + F&& f; +}; + +template +decltype(std::declval()(std::declval())) WithConstructedImpl( + Tuple&& t, absl::index_sequence, F&& f) { + return WithConstructedImplF{std::forward(f)}( + std::get(std::forward(t))...); +} + +template +auto TupleRefImpl(T&& t, absl::index_sequence) + -> decltype(std::forward_as_tuple(std::get(std::forward(t))...)) { + return std::forward_as_tuple(std::get(std::forward(t))...); +} + +// Returns a tuple of references to the elements of the input tuple. T must be a +// tuple. +template +auto TupleRef(T&& t) -> decltype( + TupleRefImpl(std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>())) { + return TupleRefImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +template +decltype(std::declval()(std::declval(), std::piecewise_construct, + std::declval>(), std::declval())) +DecomposePairImpl(F&& f, std::pair, V> p) { + const auto& key = std::get<0>(p.first); + return std::forward(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 +void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { + memory_internal::ConstructFromTupleImpl( + alloc, ptr, std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +// Constructs T using the args specified in the tuple and calls F with the +// constructed value. +template +decltype(std::declval()(std::declval())) WithConstructed( + Tuple&& t, F&& f) { + return memory_internal::WithConstructedImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>(), + std::forward(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 p(args...); +// +// 2. auto a = PairArgs(args...); +// std::pair p(std::piecewise_construct, +// std::move(a.first), std::move(a.second)); +inline std::pair, std::tuple<>> PairArgs() { return {}; } +template +std::pair, std::tuple> PairArgs(F&& f, S&& s) { + return {std::piecewise_construct, std::forward_as_tuple(std::forward(f)), + std::forward_as_tuple(std::forward(s))}; +} +template +std::pair, std::tuple> PairArgs( + const std::pair& p) { + return PairArgs(p.first, p.second); +} +template +std::pair, std::tuple> PairArgs(std::pair&& p) { + return PairArgs(std::forward(p.first), std::forward(p.second)); +} +template +auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) + -> decltype(std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s)))) { + return std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s))); +} + +// A helper function for implementing apply() in map policies. +template +auto DecomposePair(F&& f, Args&&... args) + -> decltype(memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...))) { + return memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...)); +} + +// A helper function for implementing apply() in set policies. +template +decltype(std::declval()(std::declval(), std::declval())) +DecomposeValue(F&& f, Arg&& arg) { + const auto& key = arg; + return std::forward(f)(key, std::forward(arg)); +} + +// Helper functions for asan and msan. +inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + ASAN_POISON_MEMORY_REGION(m, s); +#endif +#ifdef ABSL_HAVE_MEMORY_SANITIZER + __msan_poison(m, s); +#endif + (void)m; + (void)s; +} + +inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) { +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + ASAN_UNPOISON_MEMORY_REGION(m, s); +#endif +#ifdef ABSL_HAVE_MEMORY_SANITIZER + __msan_unpoison(m, s); +#endif + (void)m; + (void)s; +} + +template +inline void SanitizerPoisonObject(const T* object) { + SanitizerPoisonMemoryRegion(object, sizeof(T)); +} + +template +inline void SanitizerUnpoisonObject(const T* object) { + SanitizerUnpoisonMemoryRegion(object, sizeof(T)); +} + +namespace memory_internal { + +// If Pair is a standard-layout type, OffsetOf::kFirst and +// OffsetOf::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 +struct OffsetOf { + static constexpr size_t kFirst = static_cast(-1); + static constexpr size_t kSecond = static_cast(-1); +}; + +template +struct OffsetOf::type> { + static constexpr size_t kFirst = offsetof(Pair, first); + static constexpr size_t kSecond = offsetof(Pair, second); +}; + +template +struct IsLayoutCompatible { + private: + struct Pair { + K first; + V second; + }; + + // Is P layout-compatible with Pair? + template + static constexpr bool LayoutCompatible() { + return std::is_standard_layout

    () && sizeof(P) == sizeof(Pair) && + alignof(P) == alignof(Pair) && + memory_internal::OffsetOf

    ::kFirst == + memory_internal::OffsetOf::kFirst && + memory_internal::OffsetOf

    ::kSecond == + memory_internal::OffsetOf::kSecond; + } + + public: + // Whether pair and pair 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() && + std::is_standard_layout() && + memory_internal::OffsetOf::kFirst == 0 && + LayoutCompatible>() && + LayoutCompatible>(); +}; + +} // namespace memory_internal + +// The internal storage type for key-value containers like flat_hash_map. +// +// It is convenient for the value_type of a flat_hash_map to be +// pair; the "const K" prevents accidental modification of the key +// when dealing with the reference returned from find() and similar methods. +// However, this creates other problems; we want to be able to emplace(K, V) +// efficiently with move operations, and similarly be able to move a +// pair in insert(). +// +// The solution is this union, which aliases the const and non-const versions +// of the pair. This also allows flat_hash_map to work, even though +// that has the same efficiency issues with move in emplace() and insert() - +// but people do it anyway. +// +// If kMutableKeys is false, only the value member can be accessed. +// +// If kMutableKeys is true, key can be accessed through all slots while value +// and mutable_value must be accessed only via INITIALIZED slots. Slots are +// created and destroyed via mutable_value so that the key can be moved later. +// +// Accessing one of the union fields while the other is active is safe as +// long as they are layout-compatible, which is guaranteed by the definition of +// kMutableKeys. For C++11, the relevant section of the standard is +// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19) +template +union map_slot_type { + map_slot_type() {} + ~map_slot_type() = delete; + using value_type = std::pair; + using mutable_value_type = + std::pair, absl::remove_const_t>; + + value_type value; + mutable_value_type mutable_value; + absl::remove_const_t key; +}; + +template +struct map_slot_policy { + using slot_type = map_slot_type; + using value_type = std::pair; + using mutable_value_type = std::pair; + + 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 and pair are layout-compatible, we can accept one + // or the other via slot_type. We are also free to access the key via + // slot_type::key in this case. + using kMutableKeys = memory_internal::IsLayoutCompatible; + + public: + static value_type& element(slot_type* slot) { return slot->value; } + static const value_type& element(const slot_type* slot) { + return slot->value; + } + + // When C++17 is available, we can use std::launder to provide mutable + // access to the key for use in node handle. +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + static K& mutable_key(slot_type* slot) { + // Still check for kMutableKeys so that we can avoid calling std::launder + // unless necessary because it can interfere with optimizations. + return kMutableKeys::value ? slot->key + : *std::launder(const_cast( + std::addressof(slot->value.first))); + } +#else // !(defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606) + static const K& mutable_key(slot_type* slot) { return key(slot); } +#endif + + static const K& key(const slot_type* slot) { + return kMutableKeys::value ? slot->key : slot->value.first; + } + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct(*alloc, &slot->mutable_value, + std::forward(args)...); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::forward(args)...); + } + } + + // Construct this slot by moving from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &slot->mutable_value, std::move(other->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::move(other->value)); + } + } + + // Construct this slot by copying from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, + const slot_type* other) { + emplace(slot); + absl::allocator_traits::construct(*alloc, &slot->value, + other->value); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + if (kMutableKeys::value) { + absl::allocator_traits::destroy(*alloc, &slot->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &slot->value); + } + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + emplace(new_slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &new_slot->value, + std::move(old_slot->value)); + } + destroy(alloc, old_slot); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ diff --git a/src/absl/container/internal/counting_allocator.h b/src/absl/container/internal/counting_allocator.h new file mode 100644 index 0000000..b1a8519 --- /dev/null +++ b/src/absl/container/internal/counting_allocator.h @@ -0,0 +1,122 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ +#define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// This is a stateful allocator, but the state lives outside of the +// allocator (in whatever test is using the allocator). This is odd +// but helps in tests where the allocator is propagated into nested +// containers - that chain of allocators uses the same state and is +// thus easier to query for aggregate allocation information. +template +class CountingAllocator { + public: + using Allocator = std::allocator; + using AllocatorTraits = std::allocator_traits; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + + CountingAllocator() = default; + explicit CountingAllocator(int64_t* bytes_used) : bytes_used_(bytes_used) {} + CountingAllocator(int64_t* bytes_used, int64_t* instance_count) + : bytes_used_(bytes_used), instance_count_(instance_count) {} + + template + CountingAllocator(const CountingAllocator& x) + : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {} + + pointer allocate( + size_type n, + typename AllocatorTraits::const_void_pointer hint = nullptr) { + Allocator allocator; + pointer ptr = AllocatorTraits::allocate(allocator, n, hint); + if (bytes_used_ != nullptr) { + *bytes_used_ += n * sizeof(T); + } + return ptr; + } + + void deallocate(pointer p, size_type n) { + Allocator allocator; + AllocatorTraits::deallocate(allocator, p, n); + if (bytes_used_ != nullptr) { + *bytes_used_ -= n * sizeof(T); + } + } + + template + void construct(U* p, Args&&... args) { + Allocator allocator; + AllocatorTraits::construct(allocator, p, std::forward(args)...); + if (instance_count_ != nullptr) { + *instance_count_ += 1; + } + } + + template + void destroy(U* p) { + Allocator allocator; + // Ignore GCC warning bug. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +// #pragma GCC diagnostic push +// #pragma GCC diagnostic ignored "-Wuse-after-free" +#endif + AllocatorTraits::destroy(allocator, p); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +// #pragma GCC diagnostic pop +#endif + if (instance_count_ != nullptr) { + *instance_count_ -= 1; + } + } + + template + class rebind { + public: + using other = CountingAllocator; + }; + + friend bool operator==(const CountingAllocator& a, + const CountingAllocator& b) { + return a.bytes_used_ == b.bytes_used_ && + a.instance_count_ == b.instance_count_; + } + + friend bool operator!=(const CountingAllocator& a, + const CountingAllocator& b) { + return !(a == b); + } + + int64_t* bytes_used_ = nullptr; + int64_t* instance_count_ = nullptr; +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ diff --git a/src/absl/container/internal/hash_function_defaults.h b/src/absl/container/internal/hash_function_defaults.h new file mode 100644 index 0000000..250e662 --- /dev/null +++ b/src/absl/container/internal/hash_function_defaults.h @@ -0,0 +1,163 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Define the default Hash and Eq functions for SwissTable containers. +// +// std::hash and std::equal_to 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(a, b) returns true for any a and b of type T, then +// hash_default_hash(a) must equal hash_default_hash(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 but as a pointer when the hash +// function is hash. +// +#ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/hash/hash.h" +#include "absl/strings/cord.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// The hash of an object of type T is computed by using absl::Hash. +template +struct HashEq { + using Hash = absl::Hash; + using Eq = std::equal_to; +}; + +struct StringHash { + using is_transparent = void; + + size_t operator()(absl::string_view v) const { + return absl::Hash{}(v); + } + size_t operator()(const absl::Cord& v) const { + return absl::Hash{}(v); + } +}; + +struct StringEq { + using is_transparent = void; + bool operator()(absl::string_view lhs, absl::string_view rhs) const { + return lhs == rhs; + } + bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const { + return lhs == rhs; + } + bool operator()(const absl::Cord& lhs, absl::string_view rhs) const { + return lhs == rhs; + } + bool operator()(absl::string_view lhs, const absl::Cord& rhs) const { + return lhs == rhs; + } +}; + +// Supports heterogeneous lookup for string-like elements. +struct StringHashEq { + using Hash = StringHash; + using Eq = StringEq; +}; + +template <> +struct HashEq : StringHashEq {}; +template <> +struct HashEq : StringHashEq {}; +template <> +struct HashEq : StringHashEq {}; + +// Supports heterogeneous lookup for pointers and smart pointers. +template +struct HashEq { + struct Hash { + using is_transparent = void; + template + size_t operator()(const U& ptr) const { + return absl::Hash{}(HashEq::ToPtr(ptr)); + } + }; + struct Eq { + using is_transparent = void; + template + 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 + static const T* ToPtr(const std::unique_ptr& ptr) { + return ptr.get(); + } + template + static const T* ToPtr(const std::shared_ptr& ptr) { + return ptr.get(); + } +}; + +template +struct HashEq> : HashEq {}; +template +struct HashEq> : HashEq {}; + +// 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::hasher +template +using hash_default_hash = typename container_internal::HashEq::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::key_equal +template +using hash_default_eq = typename container_internal::HashEq::Eq; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ diff --git a/src/absl/container/internal/hash_policy_traits.h b/src/absl/container/internal/hash_policy_traits.h new file mode 100644 index 0000000..46c97b1 --- /dev/null +++ b/src/absl/container/internal/hash_policy_traits.h @@ -0,0 +1,208 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ + +#include +#include +#include +#include +#include + +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Defines how slots are initialized/destroyed/moved. +template +struct hash_policy_traits { + // The type of the keys stored in the hashtable. + using key_type = typename Policy::key_type; + + private: + struct ReturnKey { + // When C++17 is available, we can use std::launder to provide mutable + // access to the key for use in node handle. +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + template ::value, int> = 0> + static key_type& Impl(Key&& k, int) { + return *std::launder( + const_cast(std::addressof(std::forward(k)))); + } +#endif + + template + static Key Impl(Key&& k, char) { + return std::forward(k); + } + + // When Key=T&, we forward the lvalue reference. + // When Key=T, we return by value to avoid a dangling reference. + // eg, for string_hash_map. + template + auto operator()(Key&& k, const Args&...) const + -> decltype(Impl(std::forward(k), 0)) { + return Impl(std::forward(k), 0); + } + }; + + template + struct ConstantIteratorsImpl : std::false_type {}; + + template + struct ConstantIteratorsImpl> + : P::constant_iterators {}; + + public: + // The actual object stored in the hash table. + using slot_type = typename Policy::slot_type; + + // The argument type for insertions into the hashtable. This is different + // from value_type for increased performance. See initializer_list constructor + // and insert() member functions for more details. + using init_type = typename Policy::init_type; + + using reference = decltype(Policy::element(std::declval())); + using pointer = typename std::remove_reference::type*; + using value_type = typename std::remove_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 + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + Policy::construct(alloc, slot, std::forward(args)...); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is UNINITIALIZED + template + 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 + 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 + 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 + 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)(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 + static auto apply(F&& f, Ts&&... ts) + -> decltype(P::apply(std::forward(f), std::forward(ts)...)) { + return P::apply(std::forward(f), std::forward(ts)...); + } + + // Returns the "key" portion of the slot. + // Used for node handle manipulation. + template + static auto mutable_key(slot_type* slot) + -> decltype(P::apply(ReturnKey(), element(slot))) { + return P::apply(ReturnKey(), element(slot)); + } + + // Returns the "value" (as opposed to the "key") portion of the element. Used + // by maps to implement `operator[]`, `at()` and `insert_or_assign()`. + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + private: + // Use auto -> decltype as an enabler. + template + 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 + static void transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, char) { + construct(alloc, new_slot, std::move(element(old_slot))); + destroy(alloc, old_slot); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ diff --git a/src/absl/container/internal/hashtable_debug.h b/src/absl/container/internal/hashtable_debug.h new file mode 100644 index 0000000..19d5212 --- /dev/null +++ b/src/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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This library provides APIs to debug the probing behavior of hash tables. +// +// In general, the probing behavior is a black box for users and only the +// side effects can be measured in the form of performance differences. +// These APIs give a glimpse on the actual behavior of the probing algorithms in +// these hashtables given a specified hash function and a set of elements. +// +// The probe count distribution can be used to assess the quality of the hash +// function for that particular hash table. Note that a hash function that +// performs well in one hash table implementation does not necessarily performs +// well in a different one. +// +// This library supports std::unordered_{set,map}, dense_hash_{set,map} and +// absl::{flat,node,string}_hash_{set,map}. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ + +#include +#include +#include +#include + +#include "absl/container/internal/hashtable_debug_hooks.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Returns the number of probes required to lookup `key`. Returns 0 for a +// search with no collisions. Higher values mean more hash collisions occurred; +// however, the exact meaning of this number varies according to the container +// type. +template +size_t GetHashtableDebugNumProbes( + const C& c, const typename C::key_type& key) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::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 +std::vector GetHashtableDebugNumProbesHistogram(const C& container) { + std::vector v; + for (auto it = container.begin(); it != container.end(); ++it) { + size_t num_probes = GetHashtableDebugNumProbes( + container, + absl::container_internal::hashtable_debug_internal::GetKey(*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 +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 +size_t AllocatedByteSize(const C& c) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::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 +size_t LowerBoundAllocatedByteSize(size_t num_elements) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::LowerBoundAllocatedByteSize(num_elements); +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ diff --git a/src/absl/container/internal/hashtable_debug_hooks.h b/src/absl/container/internal/hashtable_debug_hooks.h new file mode 100644 index 0000000..3e9ea59 --- /dev/null +++ b/src/absl/container/internal/hashtable_debug_hooks.h @@ -0,0 +1,85 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Provides the internal API for hashtable_debug.h. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ + +#include + +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace hashtable_debug_internal { + +// If it is a map, call get<0>(). +using std::get; +template +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 +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 +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(*it, 0))) return num_probes; + } + } + + // Returns the number of bytes requested from the allocator by the container + // and not freed. + // + // static size_t AllocatedByteSize(const Container& c); + + // Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type + // `Container` and `c.size()` is equal to `num_elements`. + // + // static size_t LowerBoundAllocatedByteSize(size_t num_elements); +}; + +} // namespace hashtable_debug_internal +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ diff --git a/src/absl/container/internal/hashtablez_sampler.cc b/src/absl/container/internal/hashtablez_sampler.cc new file mode 100644 index 0000000..efc1be5 --- /dev/null +++ b/src/absl/container/internal/hashtablez_sampler.cc @@ -0,0 +1,238 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/debugging/stacktrace.h" +#include "absl/memory/memory.h" +#include "absl/profiling/internal/exponential_biased.h" +#include "absl/profiling/internal/sample_recorder.h" +#include "absl/synchronization/mutex.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +constexpr int HashtablezInfo::kMaxStackDepth; +#endif + +namespace { +ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ + false +}; +ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; +std::atomic g_hashtablez_config_listener{nullptr}; + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased + g_exponential_biased_generator; +#endif + +void TriggerHashtablezConfigListener() { + auto* listener = g_hashtablez_config_listener.load(std::memory_order_acquire); + if (listener != nullptr) listener(); +} + +} // namespace + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample = {0, 0}; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +HashtablezSampler& GlobalHashtablezSampler() { + static auto* sampler = new HashtablezSampler(); + return *sampler; +} + +HashtablezInfo::HashtablezInfo() = default; +HashtablezInfo::~HashtablezInfo() = default; + +void HashtablezInfo::PrepareForSampling(int64_t stride, + size_t inline_element_size_value) { + capacity.store(0, std::memory_order_relaxed); + size.store(0, std::memory_order_relaxed); + num_erases.store(0, std::memory_order_relaxed); + num_rehashes.store(0, std::memory_order_relaxed); + max_probe_length.store(0, std::memory_order_relaxed); + total_probe_length.store(0, std::memory_order_relaxed); + hashes_bitwise_or.store(0, std::memory_order_relaxed); + hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed); + hashes_bitwise_xor.store(0, std::memory_order_relaxed); + max_reserve.store(0, std::memory_order_relaxed); + + create_time = absl::Now(); + weight = stride; + // The inliner makes hardcoded skip_count difficult (especially when combined + // with LTO). We use the ability to exclude stacks by regex when encoding + // instead. + depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth, + /* skip_count= */ 0); + inline_element_size = inline_element_size_value; +} + +static bool ShouldForceSampling() { + enum ForceState { + kDontForce, + kForce, + kUninitialized + }; + ABSL_CONST_INIT static std::atomic global_state{ + kUninitialized}; + ForceState state = global_state.load(std::memory_order_relaxed); + if (ABSL_PREDICT_TRUE(state == kDontForce)) return false; + + if (state == kUninitialized) { + state = ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)() + ? kForce + : kDontForce; + global_state.store(state, std::memory_order_relaxed); + } + return state == kForce; +} + +HashtablezInfo* SampleSlow(SamplingState& next_sample, + size_t inline_element_size) { + if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { + next_sample.next_sample = 1; + const int64_t old_stride = exchange(next_sample.sample_stride, 1); + HashtablezInfo* result = + GlobalHashtablezSampler().Register(old_stride, inline_element_size); + return result; + } + +#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + next_sample = { + std::numeric_limits::max(), + std::numeric_limits::max(), + }; + return nullptr; +#else + bool first = next_sample.next_sample < 0; + + const int64_t next_stride = g_exponential_biased_generator.GetStride( + g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); + + next_sample.next_sample = next_stride; + const int64_t old_stride = exchange(next_sample.sample_stride, next_stride); + // Small values of interval are equivalent to just sampling next time. + ABSL_ASSERT(next_stride >= 1); + + // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold + // low enough that we will start sampling in a reasonable time, so we just use + // the default sampling rate. + if (!g_hashtablez_enabled.load(std::memory_order_relaxed)) return nullptr; + + // We will only be negative on our first count, so we should just retry in + // that case. + if (first) { + if (ABSL_PREDICT_TRUE(--next_sample.next_sample > 0)) return nullptr; + return SampleSlow(next_sample, inline_element_size); + } + + return GlobalHashtablezSampler().Register(old_stride, inline_element_size); +#endif +} + +void UnsampleSlow(HashtablezInfo* info) { + GlobalHashtablezSampler().Unregister(info); +} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired) { + // SwissTables probe in groups of 16, so scale this to count items probes and + // not offset from desired. + size_t probe_length = distance_from_desired; +#ifdef ABSL_INTERNAL_HAVE_SSE2 + probe_length /= 16; +#else + probe_length /= 8; +#endif + + info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed); + info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed); + info->hashes_bitwise_xor.fetch_xor(hash, std::memory_order_relaxed); + info->max_probe_length.store( + std::max(info->max_probe_length.load(std::memory_order_relaxed), + probe_length), + std::memory_order_relaxed); + info->total_probe_length.fetch_add(probe_length, std::memory_order_relaxed); + info->size.fetch_add(1, std::memory_order_relaxed); +} + +void SetHashtablezConfigListener(HashtablezConfigListener l) { + g_hashtablez_config_listener.store(l, std::memory_order_release); +} + +bool IsHashtablezEnabled() { + return g_hashtablez_enabled.load(std::memory_order_acquire); +} + +void SetHashtablezEnabled(bool enabled) { + SetHashtablezEnabledInternal(enabled); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezEnabledInternal(bool enabled) { + g_hashtablez_enabled.store(enabled, std::memory_order_release); +} + +int32_t GetHashtablezSampleParameter() { + return g_hashtablez_sample_parameter.load(std::memory_order_acquire); +} + +void SetHashtablezSampleParameter(int32_t rate) { + SetHashtablezSampleParameterInternal(rate); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezSampleParameterInternal(int32_t rate) { + if (rate > 0) { + g_hashtablez_sample_parameter.store(rate, std::memory_order_release); + } else { + ABSL_RAW_LOG(ERROR, "Invalid hashtablez sample rate: %lld", + static_cast(rate)); // NOLINT(runtime/int) + } +} + +int32_t GetHashtablezMaxSamples() { + return GlobalHashtablezSampler().GetMaxSamples(); +} + +void SetHashtablezMaxSamples(int32_t max) { + SetHashtablezMaxSamplesInternal(max); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezMaxSamplesInternal(int32_t max) { + if (max > 0) { + GlobalHashtablezSampler().SetMaxSamples(max); + } else { + ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld", + static_cast(max)); // NOLINT(runtime/int) + } +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/container/internal/hashtablez_sampler.h b/src/absl/container/internal/hashtablez_sampler.h new file mode 100644 index 0000000..d4016d8 --- /dev/null +++ b/src/absl/container/internal/hashtablez_sampler.h @@ -0,0 +1,299 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: hashtablez_sampler.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for a low level library to sample hashtables +// and collect runtime statistics about them. +// +// `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which +// store information about a single sample. +// +// `Record*` methods store information into samples. +// `Sample()` and `Unsample()` make use of a single global sampler with +// properties controlled by the flags hashtablez_enabled, +// hashtablez_sample_rate, and hashtablez_max_samples. +// +// WARNING +// +// Using this sampling API may cause sampled Swiss tables to use the global +// allocator (operator `new`) in addition to any custom allocator. If you +// are using a table in an unusual circumstance where allocation or calling a +// linux syscall is unacceptable, this could interfere. +// +// This utility is internal-only. Use at your own risk. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ + +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/per_thread_tls.h" +#include "absl/base/optimization.h" +#include "absl/profiling/internal/sample_recorder.h" +#include "absl/synchronization/mutex.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Stores information about a sampled hashtable. All mutations to this *must* +// be made through `Record*` functions below. All reads from this *must* only +// occur in the callback to `HashtablezSampler::Iterate`. +struct HashtablezInfo : public profiling_internal::Sample { + // Constructs the object but does not fill in any fields. + HashtablezInfo(); + ~HashtablezInfo(); + HashtablezInfo(const HashtablezInfo&) = delete; + HashtablezInfo& operator=(const HashtablezInfo&) = delete; + + // Puts the object into a clean state, fills in the logically `const` members, + // blocking for any readers that are currently sampling the object. + void PrepareForSampling(int64_t stride, size_t inline_element_size_value) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); + + // These fields are mutated by the various Record* APIs and need to be + // thread-safe. + std::atomic capacity; + std::atomic size; + std::atomic num_erases; + std::atomic num_rehashes; + std::atomic max_probe_length; + std::atomic total_probe_length; + std::atomic hashes_bitwise_or; + std::atomic hashes_bitwise_and; + std::atomic hashes_bitwise_xor; + std::atomic max_reserve; + + // All of the fields below are set by `PrepareForSampling`, they must not be + // mutated in `Record*` functions. They are logically `const` in that sense. + // These are guarded by init_mu, but that is not externalized to clients, + // which can read them only during `SampleRecorder::Iterate` which will hold + // the lock. + static constexpr int kMaxStackDepth = 64; + absl::Time create_time; + int32_t depth; + void* stack[kMaxStackDepth]; + size_t inline_element_size; // How big is the slot? +}; + +inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { +#ifdef ABSL_INTERNAL_HAVE_SSE2 + total_probe_length /= 16; +#else + total_probe_length /= 8; +#endif + info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); + // There is only one concurrent writer, so `load` then `store` is sufficient + // instead of using `fetch_add`. + info->num_rehashes.store( + 1 + info->num_rehashes.load(std::memory_order_relaxed), + std::memory_order_relaxed); +} + +inline void RecordReservationSlow(HashtablezInfo* info, + size_t target_capacity) { + info->max_reserve.store( + (std::max)(info->max_reserve.load(std::memory_order_relaxed), + target_capacity), + std::memory_order_relaxed); +} + +inline void RecordClearedReservationSlow(HashtablezInfo* info) { + info->max_reserve.store(0, std::memory_order_relaxed); +} + +inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, + size_t capacity) { + info->size.store(size, std::memory_order_relaxed); + info->capacity.store(capacity, std::memory_order_relaxed); + if (size == 0) { + // This is a clear, reset the total/num_erases too. + info->total_probe_length.store(0, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); + } +} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired); + +inline void RecordEraseSlow(HashtablezInfo* info) { + info->size.fetch_sub(1, std::memory_order_relaxed); + // There is only one concurrent writer, so `load` then `store` is sufficient + // instead of using `fetch_add`. + info->num_erases.store( + 1 + info->num_erases.load(std::memory_order_relaxed), + std::memory_order_relaxed); +} + +struct SamplingState { + int64_t next_sample; + // When we make a sampling decision, we record that distance so we can weight + // each sample. + int64_t sample_stride; +}; + +HashtablezInfo* SampleSlow(SamplingState& next_sample, + size_t inline_element_size); +void UnsampleSlow(HashtablezInfo* info); + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +class HashtablezInfoHandle { + public: + explicit HashtablezInfoHandle() : info_(nullptr) {} + explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {} + ~HashtablezInfoHandle() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + UnsampleSlow(info_); + } + + HashtablezInfoHandle(const HashtablezInfoHandle&) = delete; + HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete; + + HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept + : info_(absl::exchange(o.info_, nullptr)) {} + HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept { + if (ABSL_PREDICT_FALSE(info_ != nullptr)) { + UnsampleSlow(info_); + } + info_ = absl::exchange(o.info_, nullptr); + return *this; + } + + inline void RecordStorageChanged(size_t size, size_t capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordStorageChangedSlow(info_, size, capacity); + } + + inline void RecordRehash(size_t total_probe_length) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordRehashSlow(info_, total_probe_length); + } + + inline void RecordReservation(size_t target_capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordReservationSlow(info_, target_capacity); + } + + inline void RecordClearedReservation() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordClearedReservationSlow(info_); + } + + inline void RecordInsert(size_t hash, size_t distance_from_desired) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordInsertSlow(info_, hash, distance_from_desired); + } + + inline void RecordErase() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordEraseSlow(info_); + } + + friend inline void swap(HashtablezInfoHandle& lhs, + HashtablezInfoHandle& rhs) { + std::swap(lhs.info_, rhs.info_); + } + + private: + friend class HashtablezInfoHandlePeer; + HashtablezInfo* info_; +}; +#else +// Ensure that when Hashtablez is turned off at compile time, HashtablezInfo can +// be removed by the linker, in order to reduce the binary size. +class HashtablezInfoHandle { + public: + explicit HashtablezInfoHandle() = default; + explicit HashtablezInfoHandle(std::nullptr_t) {} + + inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {} + inline void RecordRehash(size_t /*total_probe_length*/) {} + inline void RecordReservation(size_t /*target_capacity*/) {} + inline void RecordClearedReservation() {} + inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) {} + inline void RecordErase() {} + + friend inline void swap(HashtablezInfoHandle& /*lhs*/, + HashtablezInfoHandle& /*rhs*/) {} +}; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +extern ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +// Returns an RAII sampling handle that manages registration and unregistation +// with the global sampler. +inline HashtablezInfoHandle Sample( + size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) { +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + if (ABSL_PREDICT_TRUE(--global_next_sample.next_sample > 0)) { + return HashtablezInfoHandle(nullptr); + } + return HashtablezInfoHandle( + SampleSlow(global_next_sample, inline_element_size)); +#else + return HashtablezInfoHandle(nullptr); +#endif // !ABSL_PER_THREAD_TLS +} + +using HashtablezSampler = + ::absl::profiling_internal::SampleRecorder; + +// Returns a global Sampler. +HashtablezSampler& GlobalHashtablezSampler(); + +using HashtablezConfigListener = void (*)(); +void SetHashtablezConfigListener(HashtablezConfigListener l); + +// Enables or disables sampling for Swiss tables. +bool IsHashtablezEnabled(); +void SetHashtablezEnabled(bool enabled); +void SetHashtablezEnabledInternal(bool enabled); + +// Sets the rate at which Swiss tables will be sampled. +int32_t GetHashtablezSampleParameter(); +void SetHashtablezSampleParameter(int32_t rate); +void SetHashtablezSampleParameterInternal(int32_t rate); + +// Sets a soft max for the number of samples that will be kept. +int32_t GetHashtablezMaxSamples(); +void SetHashtablezMaxSamples(int32_t max); +void SetHashtablezMaxSamplesInternal(int32_t max); + +// Configuration override. +// This allows process-wide sampling without depending on order of +// initialization of static storage duration objects. +// The definition of this constant is weak, which allows us to inject a +// different value for it at link time. +extern "C" bool ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)(); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ diff --git a/src/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/src/absl/container/internal/hashtablez_sampler_force_weak_definition.cc new file mode 100644 index 0000000..ed35a7e --- /dev/null +++ b/src/absl/container/internal/hashtablez_sampler_force_weak_definition.cc @@ -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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include "absl/base/attributes.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// See hashtablez_sampler.h for details. +extern "C" ABSL_ATTRIBUTE_WEAK bool ABSL_INTERNAL_C_SYMBOL( + AbslContainerInternalSampleEverything)() { + return false; +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/container/internal/inlined_vector.h b/src/absl/container/internal/inlined_vector.h new file mode 100644 index 0000000..6d49415 --- /dev/null +++ b/src/absl/container/internal/inlined_vector.h @@ -0,0 +1,953 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ +#define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/macros.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace inlined_vector_internal { + +// GCC does not deal very well with the below code +#if !defined(__clang__) && defined(__GNUC__) +// #pragma GCC diagnostic push +// #pragma GCC diagnostic ignored "-Warray-bounds" +#endif + +template +using AllocatorTraits = std::allocator_traits; +template +using ValueType = typename AllocatorTraits::value_type; +template +using SizeType = typename AllocatorTraits::size_type; +template +using Pointer = typename AllocatorTraits::pointer; +template +using ConstPointer = typename AllocatorTraits::const_pointer; +template +using SizeType = typename AllocatorTraits::size_type; +template +using DifferenceType = typename AllocatorTraits::difference_type; +template +using Reference = ValueType&; +template +using ConstReference = const ValueType&; +template +using Iterator = Pointer; +template +using ConstIterator = ConstPointer; +template +using ReverseIterator = typename std::reverse_iterator>; +template +using ConstReverseIterator = typename std::reverse_iterator>; +template +using MoveIterator = typename std::move_iterator>; + +template +using IsAtLeastForwardIterator = std::is_convertible< + typename std::iterator_traits::iterator_category, + std::forward_iterator_tag>; + +template +using IsMemcpyOk = + absl::conjunction>>, + absl::is_trivially_copy_constructible>, + absl::is_trivially_copy_assignable>, + absl::is_trivially_destructible>>; + +template +struct TypeIdentity { + using type = T; +}; + +// Used for function arguments in template functions to prevent ADL by forcing +// callers to explicitly specify the template parameter. +template +using NoTypeDeduction = typename TypeIdentity::type; + +template >::value> +struct DestroyAdapter; + +template +struct DestroyAdapter { + static void DestroyElements(A& allocator, Pointer destroy_first, + SizeType destroy_size) { + for (SizeType i = destroy_size; i != 0;) { + --i; + AllocatorTraits::destroy(allocator, destroy_first + i); + } + } +}; + +template +struct DestroyAdapter { + static void DestroyElements(A& allocator, Pointer destroy_first, + SizeType destroy_size) { + static_cast(allocator); + static_cast(destroy_first); + static_cast(destroy_size); + } +}; + +template +struct Allocation { + Pointer data; + SizeType capacity; +}; + +template ) > ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT)> +struct MallocAdapter { + static Allocation Allocate(A& allocator, SizeType requested_capacity) { + return {AllocatorTraits::allocate(allocator, requested_capacity), + requested_capacity}; + } + + static void Deallocate(A& allocator, Pointer pointer, + SizeType capacity) { + AllocatorTraits::deallocate(allocator, pointer, capacity); + } +}; + +template +void ConstructElements(NoTypeDeduction& allocator, + Pointer construct_first, ValueAdapter& values, + SizeType construct_size) { + for (SizeType i = 0; i < construct_size; ++i) { + ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); } + ABSL_INTERNAL_CATCH_ANY { + DestroyAdapter::DestroyElements(allocator, construct_first, i); + ABSL_INTERNAL_RETHROW; + } + } +} + +template +void AssignElements(Pointer assign_first, ValueAdapter& values, + SizeType assign_size) { + for (SizeType i = 0; i < assign_size; ++i) { + values.AssignNext(assign_first + i); + } +} + +template +struct StorageView { + Pointer data; + SizeType size; + SizeType capacity; +}; + +template +class IteratorValueAdapter { + public: + explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} + + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at, *it_); + ++it_; + } + + void AssignNext(Pointer assign_at) { + *assign_at = *it_; + ++it_; + } + + private: + Iterator it_; +}; + +template +class CopyValueAdapter { + public: + explicit CopyValueAdapter(ConstPointer p) : ptr_(p) {} + + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at, *ptr_); + } + + void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } + + private: + ConstPointer ptr_; +}; + +template +class DefaultValueAdapter { + public: + explicit DefaultValueAdapter() {} + + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at); + } + + void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } +}; + +template +class AllocationTransaction { + public: + explicit AllocationTransaction(A& allocator) + : allocator_data_(allocator, nullptr), capacity_(0) {} + + ~AllocationTransaction() { + if (DidAllocate()) { + MallocAdapter::Deallocate(GetAllocator(), GetData(), GetCapacity()); + } + } + + AllocationTransaction(const AllocationTransaction&) = delete; + void operator=(const AllocationTransaction&) = delete; + + A& GetAllocator() { return allocator_data_.template get<0>(); } + Pointer& GetData() { return allocator_data_.template get<1>(); } + SizeType& GetCapacity() { return capacity_; } + + bool DidAllocate() { return GetData() != nullptr; } + + Pointer Allocate(SizeType requested_capacity) { + Allocation result = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + GetData() = result.data; + GetCapacity() = result.capacity; + return result.data; + } + + ABSL_MUST_USE_RESULT Allocation Release() && { + Allocation result = {GetData(), GetCapacity()}; + Reset(); + return result; + } + + private: + void Reset() { + GetData() = nullptr; + GetCapacity() = 0; + } + + container_internal::CompressedTuple> allocator_data_; + SizeType capacity_; +}; + +template +class ConstructionTransaction { + public: + explicit ConstructionTransaction(A& allocator) + : allocator_data_(allocator, nullptr), size_(0) {} + + ~ConstructionTransaction() { + if (DidConstruct()) { + DestroyAdapter::DestroyElements(GetAllocator(), GetData(), GetSize()); + } + } + + ConstructionTransaction(const ConstructionTransaction&) = delete; + void operator=(const ConstructionTransaction&) = delete; + + A& GetAllocator() { return allocator_data_.template get<0>(); } + Pointer& GetData() { return allocator_data_.template get<1>(); } + SizeType& GetSize() { return size_; } + + bool DidConstruct() { return GetData() != nullptr; } + template + void Construct(Pointer data, ValueAdapter& values, SizeType size) { + ConstructElements(GetAllocator(), data, values, size); + GetData() = data; + GetSize() = size; + } + void Commit() && { + GetData() = nullptr; + GetSize() = 0; + } + + private: + container_internal::CompressedTuple> allocator_data_; + SizeType size_; +}; + +template +class Storage { + public: + static SizeType NextCapacity(SizeType current_capacity) { + return current_capacity * 2; + } + + static SizeType ComputeCapacity(SizeType current_capacity, + SizeType requested_capacity) { + return (std::max)(NextCapacity(current_capacity), requested_capacity); + } + + // --------------------------------------------------------------------------- + // Storage Constructors and Destructor + // --------------------------------------------------------------------------- + + Storage() : metadata_(A(), /* size and is_allocated */ 0u) {} + + explicit Storage(const A& allocator) + : metadata_(allocator, /* size and is_allocated */ 0u) {} + + ~Storage() { + if (GetSizeAndIsAllocated() == 0) { + // Empty and not allocated; nothing to do. + } else if (IsMemcpyOk::value) { + // No destructors need to be run; just deallocate if necessary. + DeallocateIfAllocated(); + } else { + DestroyContents(); + } + } + + // --------------------------------------------------------------------------- + // Storage Member Accessors + // --------------------------------------------------------------------------- + + SizeType& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } + + const SizeType& GetSizeAndIsAllocated() const { + return metadata_.template get<1>(); + } + + SizeType GetSize() const { return GetSizeAndIsAllocated() >> 1; } + + bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } + + Pointer GetAllocatedData() { return data_.allocated.allocated_data; } + + ConstPointer GetAllocatedData() const { + return data_.allocated.allocated_data; + } + + Pointer GetInlinedData() { + return reinterpret_cast>( + std::addressof(data_.inlined.inlined_data[0])); + } + + ConstPointer GetInlinedData() const { + return reinterpret_cast>( + std::addressof(data_.inlined.inlined_data[0])); + } + + SizeType GetAllocatedCapacity() const { + return data_.allocated.allocated_capacity; + } + + SizeType GetInlinedCapacity() const { return static_cast>(N); } + + StorageView MakeStorageView() { + return GetIsAllocated() ? StorageView{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()} + : StorageView{GetInlinedData(), GetSize(), + GetInlinedCapacity()}; + } + + A& GetAllocator() { return metadata_.template get<0>(); } + + const A& GetAllocator() const { return metadata_.template get<0>(); } + + // --------------------------------------------------------------------------- + // Storage Member Mutators + // --------------------------------------------------------------------------- + + ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other); + + template + void Initialize(ValueAdapter values, SizeType new_size); + + template + void Assign(ValueAdapter values, SizeType new_size); + + template + void Resize(ValueAdapter values, SizeType new_size); + + template + Iterator Insert(ConstIterator pos, ValueAdapter values, + SizeType insert_count); + + template + Reference EmplaceBack(Args&&... args); + + Iterator Erase(ConstIterator from, ConstIterator to); + + void Reserve(SizeType requested_capacity); + + void ShrinkToFit(); + + void Swap(Storage* other_storage_ptr); + + void SetIsAllocated() { + GetSizeAndIsAllocated() |= static_cast>(1); + } + + void UnsetIsAllocated() { + GetSizeAndIsAllocated() &= ((std::numeric_limits>::max)() - 1); + } + + void SetSize(SizeType size) { + GetSizeAndIsAllocated() = + (size << 1) | static_cast>(GetIsAllocated()); + } + + void SetAllocatedSize(SizeType size) { + GetSizeAndIsAllocated() = (size << 1) | static_cast>(1); + } + + void SetInlinedSize(SizeType size) { + GetSizeAndIsAllocated() = size << static_cast>(1); + } + + void AddSize(SizeType count) { + GetSizeAndIsAllocated() += count << static_cast>(1); + } + + void SubtractSize(SizeType count) { + ABSL_HARDENING_ASSERT(count <= GetSize()); + + GetSizeAndIsAllocated() -= count << static_cast>(1); + } + + void SetAllocation(Allocation allocation) { + data_.allocated.allocated_data = allocation.data; + data_.allocated.allocated_capacity = allocation.capacity; + } + + void MemcpyFrom(const Storage& other_storage) { + ABSL_HARDENING_ASSERT(IsMemcpyOk::value || + other_storage.GetIsAllocated()); + + GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); + data_ = other_storage.data_; + } + + void DeallocateIfAllocated() { + if (GetIsAllocated()) { + MallocAdapter::Deallocate(GetAllocator(), GetAllocatedData(), + GetAllocatedCapacity()); + } + } + + private: + ABSL_ATTRIBUTE_NOINLINE void DestroyContents(); + + using Metadata = container_internal::CompressedTuple>; + + struct Allocated { + Pointer allocated_data; + SizeType allocated_capacity; + }; + + struct Inlined { + alignas(ValueType) char inlined_data[sizeof(ValueType[N])]; + }; + + union Data { + Allocated allocated; + Inlined inlined; + }; + + template + ABSL_ATTRIBUTE_NOINLINE Reference EmplaceBackSlow(Args&&... args); + + Metadata metadata_; + Data data_; +}; + +template +void Storage::DestroyContents() { + Pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); + DestroyAdapter::DestroyElements(GetAllocator(), data, GetSize()); + DeallocateIfAllocated(); +} + +template +void Storage::InitFrom(const Storage& other) { + const SizeType n = other.GetSize(); + ABSL_HARDENING_ASSERT(n > 0); // Empty sources handled handled in caller. + ConstPointer src; + Pointer dst; + if (!other.GetIsAllocated()) { + dst = GetInlinedData(); + src = other.GetInlinedData(); + } else { + // Because this is only called from the `InlinedVector` constructors, it's + // safe to take on the allocation with size `0`. If `ConstructElements(...)` + // throws, deallocation will be automatically handled by `~Storage()`. + SizeType requested_capacity = ComputeCapacity(GetInlinedCapacity(), n); + Allocation allocation = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + SetAllocation(allocation); + dst = allocation.data; + src = other.GetAllocatedData(); + } + if (IsMemcpyOk::value) { + std::memcpy(reinterpret_cast(dst), + reinterpret_cast(src), n * sizeof(ValueType)); + } else { + auto values = IteratorValueAdapter>(src); + ConstructElements(GetAllocator(), dst, values, n); + } + GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated(); +} + +template +template +auto Storage::Initialize(ValueAdapter values, SizeType new_size) + -> void { + // Only callable from constructors! + ABSL_HARDENING_ASSERT(!GetIsAllocated()); + ABSL_HARDENING_ASSERT(GetSize() == 0); + + Pointer construct_data; + if (new_size > GetInlinedCapacity()) { + // Because this is only called from the `InlinedVector` constructors, it's + // safe to take on the allocation with size `0`. If `ConstructElements(...)` + // throws, deallocation will be automatically handled by `~Storage()`. + SizeType requested_capacity = + ComputeCapacity(GetInlinedCapacity(), new_size); + Allocation allocation = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + construct_data = allocation.data; + SetAllocation(allocation); + SetIsAllocated(); + } else { + construct_data = GetInlinedData(); + } + + ConstructElements(GetAllocator(), construct_data, values, new_size); + + // Since the initial size was guaranteed to be `0` and the allocated bit is + // already correct for either case, *adding* `new_size` gives us the correct + // result faster than setting it directly. + AddSize(new_size); +} + +template +template +auto Storage::Assign(ValueAdapter values, SizeType new_size) + -> void { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocator()); + + absl::Span> assign_loop; + absl::Span> construct_loop; + absl::Span> destroy_loop; + + if (new_size > storage_view.capacity) { + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + construct_loop = {allocation_tx.Allocate(requested_capacity), new_size}; + destroy_loop = {storage_view.data, storage_view.size}; + } else if (new_size > storage_view.size) { + assign_loop = {storage_view.data, storage_view.size}; + construct_loop = {storage_view.data + storage_view.size, + new_size - storage_view.size}; + } else { + assign_loop = {storage_view.data, new_size}; + destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; + } + + AssignElements(assign_loop.data(), values, assign_loop.size()); + + ConstructElements(GetAllocator(), construct_loop.data(), values, + construct_loop.size()); + + DestroyAdapter::DestroyElements(GetAllocator(), destroy_loop.data(), + destroy_loop.size()); + + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template +template +auto Storage::Resize(ValueAdapter values, SizeType new_size) + -> void { + StorageView storage_view = MakeStorageView(); + Pointer const base = storage_view.data; + const SizeType size = storage_view.size; + A& alloc = GetAllocator(); + if (new_size <= size) { + // Destroy extra old elements. + DestroyAdapter::DestroyElements(alloc, base + new_size, size - new_size); + } else if (new_size <= storage_view.capacity) { + // Construct new elements in place. + ConstructElements(alloc, base + size, values, new_size - size); + } else { + // Steps: + // a. Allocate new backing store. + // b. Construct new elements in new backing store. + // c. Move existing elements from old backing store to new backing store. + // d. Destroy all elements in old backing store. + // Use transactional wrappers for the first two steps so we can roll + // back if necessary due to exceptions. + AllocationTransaction allocation_tx(alloc); + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + Pointer new_data = allocation_tx.Allocate(requested_capacity); + + ConstructionTransaction construction_tx(alloc); + construction_tx.Construct(new_data + size, values, new_size - size); + + IteratorValueAdapter> move_values( + (MoveIterator(base))); + ConstructElements(alloc, new_data, move_values, size); + + DestroyAdapter::DestroyElements(alloc, base, size); + std::move(construction_tx).Commit(); + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); + } + SetSize(new_size); +} + +template +template +auto Storage::Insert(ConstIterator pos, ValueAdapter values, + SizeType insert_count) -> Iterator { + StorageView storage_view = MakeStorageView(); + + SizeType insert_index = + std::distance(ConstIterator(storage_view.data), pos); + SizeType insert_end_index = insert_index + insert_count; + SizeType new_size = storage_view.size + insert_count; + + if (new_size > storage_view.capacity) { + AllocationTransaction allocation_tx(GetAllocator()); + ConstructionTransaction construction_tx(GetAllocator()); + ConstructionTransaction move_construction_tx(GetAllocator()); + + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + Pointer new_data = allocation_tx.Allocate(requested_capacity); + + construction_tx.Construct(new_data + insert_index, values, insert_count); + + move_construction_tx.Construct(new_data, move_values, insert_index); + + ConstructElements(GetAllocator(), new_data + insert_end_index, + move_values, storage_view.size - insert_index); + + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); + + std::move(construction_tx).Commit(); + std::move(move_construction_tx).Commit(); + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + + SetAllocatedSize(new_size); + return Iterator(new_data + insert_index); + } else { + SizeType move_construction_destination_index = + (std::max)(insert_end_index, storage_view.size); + + ConstructionTransaction move_construction_tx(GetAllocator()); + + IteratorValueAdapter> move_construction_values( + MoveIterator(storage_view.data + + (move_construction_destination_index - insert_count))); + absl::Span> move_construction = { + storage_view.data + move_construction_destination_index, + new_size - move_construction_destination_index}; + + Pointer move_assignment_values = storage_view.data + insert_index; + absl::Span> move_assignment = { + storage_view.data + insert_end_index, + move_construction_destination_index - insert_end_index}; + + absl::Span> insert_assignment = {move_assignment_values, + move_construction.size()}; + + absl::Span> insert_construction = { + insert_assignment.data() + insert_assignment.size(), + insert_count - insert_assignment.size()}; + + move_construction_tx.Construct(move_construction.data(), + move_construction_values, + move_construction.size()); + + for (Pointer + destination = move_assignment.data() + move_assignment.size(), + last_destination = move_assignment.data(), + source = move_assignment_values + move_assignment.size(); + ;) { + --destination; + --source; + if (destination < last_destination) break; + *destination = std::move(*source); + } + + AssignElements(insert_assignment.data(), values, + insert_assignment.size()); + + ConstructElements(GetAllocator(), insert_construction.data(), values, + insert_construction.size()); + + std::move(move_construction_tx).Commit(); + + AddSize(insert_count); + return Iterator(storage_view.data + insert_index); + } +} + +template +template +auto Storage::EmplaceBack(Args&&... args) -> Reference { + StorageView storage_view = MakeStorageView(); + const SizeType n = storage_view.size; + if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) { + // Fast path; new element fits. + Pointer last_ptr = storage_view.data + n; + AllocatorTraits::construct(GetAllocator(), last_ptr, + std::forward(args)...); + AddSize(1); + return *last_ptr; + } + // TODO(b/173712035): Annotate with musttail attribute to prevent regression. + return EmplaceBackSlow(std::forward(args)...); +} + +template +template +auto Storage::EmplaceBackSlow(Args&&... args) -> Reference { + StorageView storage_view = MakeStorageView(); + AllocationTransaction allocation_tx(GetAllocator()); + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + SizeType requested_capacity = NextCapacity(storage_view.capacity); + Pointer construct_data = allocation_tx.Allocate(requested_capacity); + Pointer last_ptr = construct_data + storage_view.size; + + // Construct new element. + AllocatorTraits::construct(GetAllocator(), last_ptr, + std::forward(args)...); + // Move elements from old backing store to new backing store. + ABSL_INTERNAL_TRY { + ConstructElements(GetAllocator(), allocation_tx.GetData(), move_values, + storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits::destroy(GetAllocator(), last_ptr); + ABSL_INTERNAL_RETHROW; + } + // Destroy elements in old backing store. + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); + AddSize(1); + return *last_ptr; +} + +template +auto Storage::Erase(ConstIterator from, ConstIterator to) + -> Iterator { + StorageView storage_view = MakeStorageView(); + + SizeType erase_size = std::distance(from, to); + SizeType erase_index = + std::distance(ConstIterator(storage_view.data), from); + SizeType erase_end_index = erase_index + erase_size; + + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data + erase_end_index)); + + AssignElements(storage_view.data + erase_index, move_values, + storage_view.size - erase_end_index); + + DestroyAdapter::DestroyElements( + GetAllocator(), storage_view.data + (storage_view.size - erase_size), + erase_size); + + SubtractSize(erase_size); + return Iterator(storage_view.data + erase_index); +} + +template +auto Storage::Reserve(SizeType requested_capacity) -> void { + StorageView storage_view = MakeStorageView(); + + if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocator()); + + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + + SizeType new_requested_capacity = + ComputeCapacity(storage_view.capacity, requested_capacity); + Pointer new_data = allocation_tx.Allocate(new_requested_capacity); + + ConstructElements(GetAllocator(), new_data, move_values, + storage_view.size); + + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); +} + +template +auto Storage::ShrinkToFit() -> void { + // May only be called on allocated instances! + ABSL_HARDENING_ASSERT(GetIsAllocated()); + + StorageView storage_view{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()}; + + if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocator()); + + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + + Pointer construct_data; + if (storage_view.size > GetInlinedCapacity()) { + SizeType requested_capacity = storage_view.size; + construct_data = allocation_tx.Allocate(requested_capacity); + if (allocation_tx.GetCapacity() >= storage_view.capacity) { + // Already using the smallest available heap allocation. + return; + } + } else { + construct_data = GetInlinedData(); + } + + ABSL_INTERNAL_TRY { + ConstructElements(GetAllocator(), construct_data, move_values, + storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + SetAllocation({storage_view.data, storage_view.capacity}); + ABSL_INTERNAL_RETHROW; + } + + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); + + MallocAdapter::Deallocate(GetAllocator(), storage_view.data, + storage_view.capacity); + + if (allocation_tx.DidAllocate()) { + SetAllocation(std::move(allocation_tx).Release()); + } else { + UnsetIsAllocated(); + } +} + +template +auto Storage::Swap(Storage* other_storage_ptr) -> void { + using std::swap; + ABSL_HARDENING_ASSERT(this != other_storage_ptr); + + if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { + swap(data_.allocated, other_storage_ptr->data_.allocated); + } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { + Storage* small_ptr = this; + Storage* large_ptr = other_storage_ptr; + if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); + + for (SizeType i = 0; i < small_ptr->GetSize(); ++i) { + swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); + } + + IteratorValueAdapter> move_values( + MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); + + ConstructElements(large_ptr->GetAllocator(), + small_ptr->GetInlinedData() + small_ptr->GetSize(), + move_values, + large_ptr->GetSize() - small_ptr->GetSize()); + + DestroyAdapter::DestroyElements( + large_ptr->GetAllocator(), + large_ptr->GetInlinedData() + small_ptr->GetSize(), + large_ptr->GetSize() - small_ptr->GetSize()); + } else { + Storage* allocated_ptr = this; + Storage* inlined_ptr = other_storage_ptr; + if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); + + StorageView allocated_storage_view{ + allocated_ptr->GetAllocatedData(), allocated_ptr->GetSize(), + allocated_ptr->GetAllocatedCapacity()}; + + IteratorValueAdapter> move_values( + MoveIterator(inlined_ptr->GetInlinedData())); + + ABSL_INTERNAL_TRY { + ConstructElements(inlined_ptr->GetAllocator(), + allocated_ptr->GetInlinedData(), move_values, + inlined_ptr->GetSize()); + } + ABSL_INTERNAL_CATCH_ANY { + allocated_ptr->SetAllocation(Allocation{ + allocated_storage_view.data, allocated_storage_view.capacity}); + ABSL_INTERNAL_RETHROW; + } + + DestroyAdapter::DestroyElements(inlined_ptr->GetAllocator(), + inlined_ptr->GetInlinedData(), + inlined_ptr->GetSize()); + + inlined_ptr->SetAllocation(Allocation{allocated_storage_view.data, + allocated_storage_view.capacity}); + } + + swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); + swap(GetAllocator(), other_storage_ptr->GetAllocator()); +} + +// End ignore "array-bounds" +#if !defined(__clang__) && defined(__GNUC__) +// #pragma GCC diagnostic pop +#endif + +} // namespace inlined_vector_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ diff --git a/src/absl/container/internal/layout.h b/src/absl/container/internal/layout.h new file mode 100644 index 0000000..a59a243 --- /dev/null +++ b/src/absl/container/internal/layout.h @@ -0,0 +1,743 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// MOTIVATION AND TUTORIAL +// +// If you want to put in a single heap allocation N doubles followed by M ints, +// it's easy if N and M are known at compile time. +// +// struct S { +// double a[N]; +// int b[M]; +// }; +// +// S* p = new S; +// +// But what if N and M are known only in run time? Class template Layout to the +// rescue! It's a portable generalization of the technique known as struct hack. +// +// // This object will tell us everything we need to know about the memory +// // layout of double[N] followed by int[M]. It's structurally identical to +// // size_t[2] that stores N and M. It's very cheap to create. +// const Layout 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 p(new unsigned char[layout.AllocSize()]); +// +// // Obtain the pointer to the array of doubles. +// // Equivalent to `reinterpret_cast(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(p.get()); +// +// // Obtain the pointer to the array of ints. +// // Equivalent to `reinterpret_cast(p.get() + N * 8)`. +// int* b = layout.Pointer(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; +// +// 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(p); +// int* b = layout.Pointer(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` 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`. `Layout<..., Aligned, ...>` 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(p_.get()) = size; +// // Store the characters in the allocation. +// memcpy(layout.Pointer(p_.get()), s, size + 1); +// } +// +// size_t size() const { +// // Equivalent to reinterpret_cast(*p). +// return *L::Partial().Pointer(p_.get()); +// } +// +// const char* c_str() const { +// // Equivalent to reinterpret_cast(p.get() + sizeof(size_t)). +// // The argument in Partial(1) specifies that we have size_t[1] in front +// // of the characters. +// return L::Partial(1).Pointer(p_.get()); +// } +// +// private: +// // Our heap allocation contains a size_t followed by an array of chars. +// using L = Layout; +// std::unique_ptr p_; +// }; +// +// 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::Partial(count1,..., countm)` (where `m` <= `n`) returns a +// `LayoutImpl<>` object. `Layout 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/str_cat.h" +#include "absl/types/span.h" +#include "absl/utility/utility.h" + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#include +#endif + +#if defined(__GXX_RTTI) +#define ABSL_INTERNAL_HAS_CXA_DEMANGLE +#endif + +#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A type wrapper that instructs `Layout` to use the specific alignment for the +// array. `Layout<..., Aligned, ...>` 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 +struct Aligned; + +namespace internal_layout { + +template +struct NotAligned {}; + +template +struct NotAligned> { + static_assert(sizeof(T) == 0, "Aligned cannot be const-qualified"); +}; + +template +using IntToSize = size_t; + +template +using TypeToSize = size_t; + +template +struct Type : NotAligned { + using type = T; +}; + +template +struct Type> { + using type = T; +}; + +template +struct SizeOf : NotAligned, std::integral_constant {}; + +template +struct SizeOf> : std::integral_constant {}; + +// Note: workaround for https://gcc.gnu.org/PR88115 +template +struct AlignOf : NotAligned { + static constexpr size_t value = alignof(T); +}; + +template +struct AlignOf> { + 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 +using Contains = absl::disjunction...>; + +template +using CopyConst = + typename std::conditional::value, const To, To>::type; + +// Note: We're not qualifying this with absl:: because it doesn't compile under +// MSVC. +template +using SliceType = Span; + +// This namespace contains no types. It prevents functions defined in it from +// being found by ADL. +namespace adl_barrier { + +template +constexpr size_t Find(Needle, Needle, Ts...) { + static_assert(!Contains(), "Duplicate element type"); + return 0; +} + +template +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 +constexpr size_t Max(size_t a, size_t b, Ts... rest) { + return adl_barrier::Max(b < a ? a : b, rest...); +} + +template +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 +using EnableIf = typename std::enable_if::type; + +// Can `T` be a template argument of `Layout`? +template +using IsLegalElementType = std::integral_constant< + bool, !std::is_reference::value && !std::is_volatile::value && + !std::is_reference::type>::value && + !std::is_volatile::type>::value && + adl_barrier::IsPow2(AlignOf::value)>; + +template +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 LayoutImpl, absl::index_sequence, + absl::index_sequence> { + private: + static_assert(sizeof...(Elements) > 0, "At least one field is required"); + static_assert(absl::conjunction...>::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 + static constexpr size_t ElementIndex() { + static_assert(Contains, Type::type>...>(), + "Type not found"); + return adl_barrier::Find(Type(), + Type::type>()...); + } + + template + using ElementAlignment = + AlignOf>::type>; + + public: + // Element types of all arrays packed in a tuple. + using ElementTypes = std::tuple::type...>; + + // Element type of the Nth array. + template + using ElementType = typename std::tuple_element::type; + + constexpr explicit LayoutImpl(IntToSize... 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::value...); + } + + // Offset in bytes of the Nth array. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout 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 = 0> + constexpr size_t Offset() const { + return 0; + } + + template = 0> + constexpr size_t Offset() const { + static_assert(N < NumOffsets, "Index out of bounds"); + return adl_barrier::Align( + Offset() + SizeOf>::value * size_[N - 1], + ElementAlignment::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 x(3, 4); + // assert(x.Offset() == 0); // The ints starts from 0. + // assert(x.Offset() == 16); // The doubles starts from 16. + template + constexpr size_t Offset() const { + return Offset()>(); + } + + // Offsets in bytes of all arrays for which the offsets are known. + constexpr std::array Offsets() const { + return {{Offset()...}}; + } + + // 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 x(3, 4); + // assert(x.Size<0>() == 3); + // assert(x.Size<1>() == 4); + // + // Requires: `N < NumSizes`. + template + 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 x(3, 4); + // assert(x.Size() == 3); + // assert(x.Size() == 4); + template + constexpr size_t Size() const { + return Size()>(); + } + + // The number of elements of all arrays for which they are known. + constexpr std::array Sizes() const { + return {{Size()...}}; + } + + // Pointer to the beginning of the Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout 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 + CopyConst>* Pointer(Char* p) const { + using C = typename std::remove_const::type; + static_assert( + std::is_same() || std::is_same() || + std::is_same(), + "The argument must be a pointer to [const] [signed|unsigned] char"); + constexpr size_t alignment = Alignment(); + (void)alignment; + assert(reinterpret_cast(p) % alignment == 0); + return reinterpret_cast>*>(p + Offset()); + } + + // 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 x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer(p); + // double* doubles = x.Pointer(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst* Pointer(Char* p) const { + return Pointer()>(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 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 + std::tuple::type>*...> + Pointers(Char* p) const { + return std::tuple>*...>( + Pointer(p)...); + } + + // The Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice<0>(p); + // Span doubles = x.Slice<1>(p); + // + // Requires: `N < NumSizes`. + // Requires: `p` is aligned to `Alignment()`. + template + SliceType>> Slice(Char* p) const { + return SliceType>>(Pointer(p), Size()); + } + + // 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 x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice(p); + // Span doubles = x.Slice(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + SliceType> Slice(Char* p) const { + return Slice()>(p); + } + + // All arrays with known sizes. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // Span ints; + // Span 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 + std::tuple::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>>...>( + Slice(p)...); + } + + // The size of the allocation that fits all arrays. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout 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() + + SizeOf>::value * size_[NumTypes - 1]; + } + + // If built with --config=asan, poisons padding bytes (if any) in the + // allocation. The pointer must point to a memory block at least + // `AllocSize()` bytes in length. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // Requires: `p` is aligned to `Alignment()`. + template = 0> + void PoisonPadding(const Char* p) const { + Pointer<0>(p); // verify the requirements on `Char` and `p` + } + + template = 0> + void PoisonPadding(const Char* p) const { + static_assert(N < NumOffsets, "Index out of bounds"); + (void)p; +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + PoisonPadding(p); + // The `if` is an optimization. It doesn't affect the observable behaviour. + if (ElementAlignment::value % ElementAlignment::value) { + size_t start = + Offset() + SizeOf>::value * size_[N - 1]; + ASAN_POISON_MEMORY_REGION(p + start, Offset() - 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::Partial(5, 3); + // assert(x.DebugString() == + // "@0(1)[5]; @8(4)[3]; @24(8)"); + // + // Each field is in the following format: @offset(sizeof)[size] ( + // may be missing depending on the target platform). For example, + // @8(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>::value...}; + const std::string types[] = { + adl_barrier::TypeName>()...}; + 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(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 +using LayoutType = LayoutImpl< + std::tuple, absl::make_index_sequence, + absl::make_index_sequence>; + +} // 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 Layout : public internal_layout::LayoutType { + public: + static_assert(sizeof...(Ts) > 0, "At least one field is required"); + static_assert( + absl::conjunction...>::value, + "Invalid element type (see IsLegalElementType)"); + + // The result type of `Partial()` with `NumSizes` arguments. + template + using PartialType = internal_layout::LayoutType; + + // `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::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 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 + static constexpr PartialType Partial(Sizes&&... sizes) { + static_assert(sizeof...(Sizes) <= sizeof...(Ts), ""); + return PartialType(absl::forward(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... sizes) + : internal_layout::LayoutType(sizes...) {} +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ diff --git a/src/absl/container/internal/node_slot_policy.h b/src/absl/container/internal/node_slot_policy.h new file mode 100644 index 0000000..baba574 --- /dev/null +++ b/src/absl/container/internal/node_slot_policy.h @@ -0,0 +1,92 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Adapts a policy for nodes. +// +// The node policy should model: +// +// struct Policy { +// // Returns a new node allocated and constructed using the allocator, using +// // the specified arguments. +// template +// value_type* new_element(Alloc* alloc, Args&&... args) const; +// +// // Destroys and deallocates node using the allocator. +// template +// void delete_element(Alloc* alloc, value_type* node) const; +// }; +// +// It may also optionally define `value()` and `apply()`. For documentation on +// these, see hash_policy_traits.h. + +#ifndef ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ +#define ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct node_slot_policy { + static_assert(std::is_lvalue_reference::value, ""); + + using slot_type = typename std::remove_cv< + typename std::remove_reference::type>::type*; + + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + *slot = Policy::new_element(alloc, std::forward(args)...); + } + + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::delete_element(alloc, *slot); + } + + template + 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 + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + template + static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward(ts)...)) { + return P::apply(std::forward(ts)...); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ diff --git a/src/absl/container/internal/raw_hash_map.h b/src/absl/container/internal/raw_hash_map.h new file mode 100644 index 0000000..c7df2ef --- /dev/null +++ b/src/absl/container/internal/raw_hash_map.h @@ -0,0 +1,198 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ + +#include +#include +#include + +#include "absl/base/internal/throw_delegate.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class raw_hash_map : public raw_hash_set { + // P is Policy. It's passed as a template argument to support maps that have + // incomplete types as values, as in unordered_map. + // MappedReference<> may be a non-reference type. + template + using MappedReference = decltype(P::value( + std::addressof(std::declval()))); + + // MappedConstReference<> may be a non-reference type. + template + using MappedConstReference = decltype(P::value( + std::addressof(std::declval()))); + + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + template + using key_arg = typename KeyArgImpl::template type; + + static_assert(!std::is_reference::value, ""); + + // TODO(b/187807849): Evaluate whether to support reference mapped_type and + // remove this assertion if/when it is supported. + static_assert(!std::is_reference::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 m; + // m.insert_or_assign(n, n); + template + std::pair insert_or_assign(key_arg&& k, V&& v) { + return insert_or_assign_impl(std::forward(k), std::forward(v)); + } + + template + std::pair insert_or_assign(key_arg&& k, const V& v) { + return insert_or_assign_impl(std::forward(k), v); + } + + template + std::pair insert_or_assign(const key_arg& k, V&& v) { + return insert_or_assign_impl(k, std::forward(v)); + } + + template + std::pair insert_or_assign(const key_arg& k, const V& v) { + return insert_or_assign_impl(k, v); + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, V&& v) { + return insert_or_assign(std::forward(k), std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, const V& v) { + return insert_or_assign(std::forward(k), v).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, V&& v) { + return insert_or_assign(k, std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, const V& v) { + return insert_or_assign(k, v).first; + } + + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. + template ::value, int>::type = 0, + K* = nullptr> + std::pair try_emplace(key_arg&& k, Args&&... args) { + return try_emplace_impl(std::forward(k), std::forward(args)...); + } + + template ::value, int>::type = 0> + std::pair try_emplace(const key_arg& k, Args&&... args) { + return try_emplace_impl(k, std::forward(args)...); + } + + template + iterator try_emplace(const_iterator, key_arg&& k, Args&&... args) { + return try_emplace(std::forward(k), std::forward(args)...).first; + } + + template + iterator try_emplace(const_iterator, const key_arg& k, Args&&... args) { + return try_emplace(k, std::forward(args)...).first; + } + + template + MappedReference

    at(const key_arg& key) { + auto it = this->find(key); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } + return Policy::value(&*it); + } + + template + MappedConstReference

    at(const key_arg& key) const { + auto it = this->find(key); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } + return Policy::value(&*it); + } + + template + MappedReference

    operator[](key_arg&& key) { + return Policy::value(&*try_emplace(std::forward(key)).first); + } + + template + MappedReference

    operator[](const key_arg& key) { + return Policy::value(&*try_emplace(key).first); + } + + private: + template + std::pair 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), std::forward(v)); + else + Policy::value(&*this->iterator_at(res.first)) = std::forward(v); + return {this->iterator_at(res.first), res.second}; + } + + template + std::pair 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)), + std::forward_as_tuple(std::forward(args)...)); + return {this->iterator_at(res.first), res.second}; + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ diff --git a/src/absl/container/internal/raw_hash_set.cc b/src/absl/container/internal/raw_hash_set.cc new file mode 100644 index 0000000..c63a2e0 --- /dev/null +++ b/src/absl/container/internal/raw_hash_set.cc @@ -0,0 +1,71 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/raw_hash_set.h" + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A single block of empty control bytes for tables without any slots allocated. +// This enables removing a branch in the hot path of find(). +alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[16] = { + ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +constexpr size_t Group::kWidth; +#endif + +// Returns "random" seed. +inline size_t RandomSeed() { +#ifdef ABSL_HAVE_THREAD_LOCAL + static thread_local size_t counter = 0; + size_t value = ++counter; +#else // ABSL_HAVE_THREAD_LOCAL + static std::atomic counter(0); + size_t value = counter.fetch_add(1, std::memory_order_relaxed); +#endif // ABSL_HAVE_THREAD_LOCAL + return value ^ static_cast(reinterpret_cast(&counter)); +} + +bool ShouldInsertBackwards(size_t hash, const 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; +} + +void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) { + assert(ctrl[capacity] == ctrl_t::kSentinel); + assert(IsValidCapacity(capacity)); + for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) { + Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); + } + // Copy the cloned ctrl bytes. + std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes()); + ctrl[capacity] = ctrl_t::kSentinel; +} +// Extern template instantiotion for inline function. +template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/container/internal/raw_hash_set.h b/src/absl/container/internal/raw_hash_set.h new file mode 100644 index 0000000..e875e24 --- /dev/null +++ b/src/absl/container/internal/raw_hash_set.h @@ -0,0 +1,2369 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// 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 +// iterator find(const K& key); +// +// size_type erase(const key_type& key); +// template +// size_type erase(const K& key); +// +// std::pair equal_range(const key_type& key); +// template +// std::pair 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 +// 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 +// +// # Table Layout +// +// A raw_hash_set's backing array consists of control bytes followed by slots +// that may or may not contain objects. +// +// The layout of the backing array, for `capacity` slots, is thus, as a +// pseudo-struct: +// +// struct BackingArray { +// // Control bytes for the "real" slots. +// ctrl_t ctrl[capacity]; +// // Always `ctrl_t::kSentinel`. This is used by iterators to find when to +// // stop and serves no other purpose. +// ctrl_t sentinel; +// // A copy of the first `kWidth - 1` elements of `ctrl`. This is used so +// // that if a probe sequence picks a value near the end of `ctrl`, +// // `Group` will have valid control bytes to look at. +// ctrl_t clones[kWidth - 1]; +// // The actual slot data. +// slot_type slots[capacity]; +// }; +// +// The length of this array is computed by `AllocSize()` below. +// +// Control bytes (`ctrl_t`) are bytes (collected into groups of a +// platform-specific size) that define the state of the corresponding slot in +// the slot array. Group manipulation is tightly optimized to be as efficient +// as possible: SSE and friends on x86, clever bit operations on other arches. +// +// Group 1 Group 2 Group 3 +// +---------------+---------------+---------------+ +// | | | | | | | | | | | | | | | | | | | | | | | | | +// +---------------+---------------+---------------+ +// +// Each control byte is either a special value for empty slots, deleted slots +// (sometimes called *tombstones*), and a special end-of-table marker used by +// iterators, or, if occupied, seven bits (H2) from the hash of the value in the +// corresponding slot. +// +// Storing control bytes in a separate array also has beneficial cache effects, +// since more logical slots will fit into a cache line. +// +// # Hashing +// +// We compute two separate hashes, `H1` and `H2`, from the hash of an object. +// `H1(hash(x))` is an index into `slots`, and essentially the starting point +// for the probe sequence. `H2(hash(x))` is a 7-bit value used to filter out +// objects that cannot possibly be the one we are looking for. +// +// # Table operations. +// +// The key operations are `insert`, `find`, and `erase`. +// +// Since `insert` and `erase` are implemented in terms of `find`, we describe +// `find` first. To `find` a value `x`, we compute `hash(x)`. From +// `H1(hash(x))` and the capacity, we construct a `probe_seq` that visits every +// group of slots in some interesting order. +// +// We now walk through these indices. At each index, we select the entire group +// starting with that index and extract potential candidates: occupied slots +// with a control byte equal to `H2(hash(x))`. If we find an empty slot in the +// group, we stop and return an error. Each candidate slot `y` is compared with +// `x`; if `x == y`, we are done and return `&y`; otherwise we contine to the +// next probe index. Tombstones effectively behave like full slots that never +// match the value we're looking for. +// +// The `H2` bits ensure when we compare a slot to an object with `==`, we are +// likely to have actually found the object. That is, the chance is low that +// `==` is called and returns `false`. Thus, when we search for an object, we +// are unlikely to call `==` many times. This likelyhood can be analyzed as +// follows (assuming that H2 is a random enough hash function). +// +// Let's assume that there are `k` "wrong" objects that must be examined in a +// probe sequence. For example, when doing a `find` on an object that is in the +// table, `k` is the number of objects between the start of the probe sequence +// and the final found object (not including the final found object). The +// expected number of objects with an H2 match is then `k/128`. Measurements +// and analysis indicate that even at high load factors, `k` is less than 32, +// meaning that the number of "false positive" comparisons we must perform is +// less than 1/8 per `find`. + +// `insert` is implemented in terms of `unchecked_insert`, which inserts a +// value presumed to not be in the table (violating this requirement will cause +// the table to behave erratically). Given `x` and its hash `hash(x)`, to insert +// it, we construct a `probe_seq` once again, and use it to find the first +// group with an unoccupied (empty *or* deleted) slot. We place `x` into the +// first such slot in the group and mark it as full with `x`'s H2. +// +// To `insert`, we compose `unchecked_insert` with `find`. We compute `h(x)` and +// perform a `find` to see if it's already present; if it is, we're done. If +// it's not, we may decide the table is getting overcrowded (i.e. the load +// factor is greater than 7/8 for big tables; `is_small()` tables use a max load +// factor of 1); in this case, we allocate a bigger array, `unchecked_insert` +// each element of the table into the new array (we know that no insertion here +// will insert an already-present value), and discard the old backing array. At +// this point, we may `unchecked_insert` the value `x`. +// +// Below, `unchecked_insert` is partly implemented by `prepare_insert`, which +// presents a viable, initialized slot pointee to the caller. +// +// `erase` is implemented in terms of `erase_at`, which takes an index to a +// slot. Given an offset, we simply create a tombstone and destroy its contents. +// If we can prove that the slot would not appear in a probe sequence, we can +// make the slot as empty, instead. We can prove this by observing that if a +// group has any empty slots, it has never been full (assuming we never create +// an empty slot in a group with no empties, which this heuristic guarantees we +// never do) and find would stop at this group anyways (since it does not probe +// beyond groups with empties). +// +// `erase` is `erase_at` composed with `find`: if we +// have a value `x`, we can perform a `find`, and then `erase_at` the resulting +// slot. +// +// To iterate, we simply traverse the array, skipping empty and deleted slots +// and stopping when we hit a `kSentinel`. + +#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/endian.h" +#include "absl/base/internal/prefetch.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" +#include "absl/container/internal/common.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/hashtablez_sampler.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" +#include "absl/utility/utility.h" + +#ifdef ABSL_INTERNAL_HAVE_SSE2 +#include +#endif + +#ifdef ABSL_INTERNAL_HAVE_SSSE3 +#include +#endif + +#ifdef _MSC_VER +#include +#endif + +#ifdef ABSL_INTERNAL_HAVE_ARM_NEON +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +void SwapAlloc(AllocType& lhs, AllocType& rhs, + std::true_type /* propagate_on_container_swap */) { + using std::swap; + swap(lhs, rhs); +} +template +void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/, + std::false_type /* propagate_on_container_swap */) {} + +// The state for a probe sequence. +// +// Currently, the sequence is a triangular progression of the form +// +// p(i) := Width * (i^2 + i)/2 + hash (mod mask + 1) +// +// The use of `Width` ensures that each probe step does not overlap groups; +// the sequence effectively outputs the addresses of *groups* (although not +// necessarily aligned to any boundary). The `Group` machinery allows us +// to check an entire group with minimal branching. +// +// Wrapping around at `mask + 1` is important, but not for the obvious reason. +// As described above, the first few entries of the control byte array +// are mirrored at the end of the array, which `Group` will find and use +// for selecting candidates. However, when those candidates' slots are +// actually inspected, there are no corresponding slots for the cloned bytes, +// so we need to make sure we've treated those offsets as "wrapping around". +// +// It turns out that this probe sequence visits every group exactly once if the +// number of groups is a power of two, since (i^2+i)/2 is a bijection in +// Z/(2^m). See https://en.wikipedia.org/wiki/Quadratic_probing +template +class probe_seq { + public: + // Creates a new probe sequence using `hash` as the initial value of the + // sequence and `mask` (usually the capacity of the table) as the mask to + // apply to each value in the progression. + probe_seq(size_t hash, size_t mask) { + assert(((mask + 1) & mask) == 0 && "not a mask"); + mask_ = mask; + offset_ = hash & mask_; + } + + // The offset within the table, i.e., the value `p(i)` above. + 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, a multiple of `Width`. + size_t index() const { return index_; } + + private: + size_t mask_; + size_t offset_; + size_t index_ = 0; +}; + +template +struct RequireUsableKey { + template + std::pair< + decltype(std::declval()(std::declval())), + decltype(std::declval()(std::declval(), + std::declval()))>* + operator()(const PassedKey&, const Args&...) const; +}; + +template +struct IsDecomposable : std::false_type {}; + +template +struct IsDecomposable< + absl::void_t(), + std::declval()...))>, + Policy, Hash, Eq, Ts...> : std::true_type {}; + +// TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it. +template +constexpr bool IsNoThrowSwappable(std::true_type = {} /* is_swappable */) { + using std::swap; + return noexcept(swap(std::declval(), std::declval())); +} +template +constexpr bool IsNoThrowSwappable(std::false_type /* is_swappable */) { + return false; +} + +template +uint32_t TrailingZeros(T x) { + ABSL_ASSUME(x != 0); + return static_cast(countr_zero(x)); +} + +// An abstract bitmask, such as that emitted by a SIMD instruction. +// +// Specifically, this type implements a simple bitset whose representation is +// controlled by `SignificantBits` and `Shift`. `SignificantBits` is the number +// of abstract bits in the bitset, while `Shift` is the log-base-two of the +// width of an abstract bit in the representation. +// This mask provides operations for any number of real bits set in an abstract +// bit. To add iteration on top of that, implementation must guarantee no more +// than one real bit is set in an abstract bit. +template +class NonIterableBitMask { + public: + explicit NonIterableBitMask(T mask) : mask_(mask) {} + + explicit operator bool() const { return this->mask_ != 0; } + + // Returns the index of the lowest *abstract* bit set in `self`. + uint32_t LowestBitSet() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + + // Returns the index of the highest *abstract* bit set in `self`. + uint32_t HighestBitSet() const { + return static_cast((bit_width(mask_) - 1) >> Shift); + } + + // Return the number of trailing zero *abstract* bits. + uint32_t TrailingZeros() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + + // Return the number of leading zero *abstract* bits. + uint32_t LeadingZeros() const { + constexpr int total_significant_bits = SignificantBits << Shift; + constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; + return static_cast(countl_zero(mask_ << extra_bits)) >> Shift; + } + + T mask_; +}; + +// Mask that can be iterable +// +// For example, when `SignificantBits` is 16 and `Shift` is zero, this is just +// an ordinary 16-bit bitset occupying the low 16 bits of `mask`. When +// `SignificantBits` is 8 and `Shift` is 3, abstract bits are represented as +// the bytes `0x00` and `0x80`, and it occupies all 64 bits of the bitmask. +// +// For example: +// for (int i : BitMask(0b101)) -> yields 0, 2 +// for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 +template +class BitMask : public NonIterableBitMask { + using Base = NonIterableBitMask; + static_assert(std::is_unsigned::value, ""); + static_assert(Shift == 0 || Shift == 3, ""); + + public: + explicit BitMask(T mask) : Base(mask) {} + // BitMask is an iterator over the indices of its abstract bits. + using value_type = int; + using iterator = BitMask; + using const_iterator = BitMask; + + BitMask& operator++() { + this->mask_ &= (this->mask_ - 1); + return *this; + } + + uint32_t operator*() const { return Base::LowestBitSet(); } + + BitMask begin() const { return *this; } + BitMask end() const { return BitMask(0); } + + 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_; + } +}; + +using h2_t = uint8_t; + +// The values here are selected for maximum performance. See the static asserts +// below for details. + +// A `ctrl_t` is a single control byte, which can have one of four +// states: empty, deleted, full (which has an associated seven-bit h2_t value) +// and the sentinel. They have the following bit patterns: +// +// empty: 1 0 0 0 0 0 0 0 +// deleted: 1 1 1 1 1 1 1 0 +// full: 0 h h h h h h h // h represents the hash bits. +// sentinel: 1 1 1 1 1 1 1 1 +// +// These values are specifically tuned for SSE-flavored SIMD. +// The static_asserts below detail the source of these choices. +// +// We use an enum class so that when strict aliasing is enabled, the compiler +// knows ctrl_t doesn't alias other types. +enum class ctrl_t : int8_t { + kEmpty = -128, // 0b10000000 + kDeleted = -2, // 0b11111110 + kSentinel = -1, // 0b11111111 +}; +static_assert( + (static_cast(ctrl_t::kEmpty) & + static_cast(ctrl_t::kDeleted) & + static_cast(ctrl_t::kSentinel) & 0x80) != 0, + "Special markers need to have the MSB to make checking for them efficient"); +static_assert( + ctrl_t::kEmpty < ctrl_t::kSentinel && ctrl_t::kDeleted < ctrl_t::kSentinel, + "ctrl_t::kEmpty and ctrl_t::kDeleted must be smaller than " + "ctrl_t::kSentinel to make the SIMD test of IsEmptyOrDeleted() efficient"); +static_assert( + ctrl_t::kSentinel == static_cast(-1), + "ctrl_t::kSentinel must be -1 to elide loading it from memory into SIMD " + "registers (pcmpeqd xmm, xmm)"); +static_assert(ctrl_t::kEmpty == static_cast(-128), + "ctrl_t::kEmpty must be -128 to make the SIMD check for its " + "existence efficient (psignb xmm, xmm)"); +static_assert( + (~static_cast(ctrl_t::kEmpty) & + ~static_cast(ctrl_t::kDeleted) & + static_cast(ctrl_t::kSentinel) & 0x7F) != 0, + "ctrl_t::kEmpty and ctrl_t::kDeleted must share an unset bit that is not " + "shared by ctrl_t::kSentinel to make the scalar test for " + "MaskEmptyOrDeleted() efficient"); +static_assert(ctrl_t::kDeleted == static_cast(-2), + "ctrl_t::kDeleted must be -2 to make the implementation of " + "ConvertSpecialToEmptyAndFullToDeleted efficient"); + +ABSL_DLL extern const ctrl_t kEmptyGroup[16]; + +// Returns a pointer to a control byte group that can be used by empty tables. +inline ctrl_t* EmptyGroup() { + // Const must be cast away here; no uses of this function will actually write + // to it, because it is only used for empty tables. + return const_cast(kEmptyGroup); +} + +// Mixes a randomly generated per-process seed with `hash` and `ctrl` to +// randomize insertion order within groups. +bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl); + +// Returns a per-table, hash salt, which changes on resize. This gets mixed into +// H1 to randomize iteration order per-table. +// +// The seed consists of the ctrl_ pointer, which adds enough entropy to ensure +// non-determinism of iteration order in most cases. +inline size_t PerTableSalt(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(ctrl) >> 12; +} +// Extracts the H1 portion of a hash: 57 bits mixed with a per-table salt. +inline size_t H1(size_t hash, const ctrl_t* ctrl) { + return (hash >> 7) ^ PerTableSalt(ctrl); +} + +// Extracts the H2 portion of a hash: the 7 bits not used for H1. +// +// These are used as an occupied control byte. +inline h2_t H2(size_t hash) { return hash & 0x7F; } + +// Helpers for checking the state of a control byte. +inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; } +inline bool IsFull(ctrl_t c) { return c >= static_cast(0); } +inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; } +inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } + +#ifdef ABSL_INTERNAL_HAVE_SSE2 +// Quick reference guide for intrinsics used below: +// +// * __m128i: An XMM (128-bit) word. +// +// * _mm_setzero_si128: Returns a zero vector. +// * _mm_set1_epi8: Returns a vector with the same i8 in each lane. +// +// * _mm_subs_epi8: Saturating-subtracts two i8 vectors. +// * _mm_and_si128: Ands two i128s together. +// * _mm_or_si128: Ors two i128s together. +// * _mm_andnot_si128: And-nots two i128s together. +// +// * _mm_cmpeq_epi8: Component-wise compares two i8 vectors for equality, +// filling each lane with 0x00 or 0xff. +// * _mm_cmpgt_epi8: Same as above, but using > rather than ==. +// +// * _mm_loadu_si128: Performs an unaligned load of an i128. +// * _mm_storeu_si128: Performs an unaligned store of an i128. +// +// * _mm_sign_epi8: Retains, negates, or zeroes each i8 lane of the first +// argument if the corresponding lane of the second +// argument is positive, negative, or zero, respectively. +// * _mm_movemask_epi8: Selects the sign bit out of each i8 lane and produces a +// bitmask consisting of those bits. +// * _mm_shuffle_epi8: Selects i8s from the first argument, using the low +// four bits of each i8 lane in the second argument as +// indices. + +// 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) { +// dd: in the latest gcc we get warnings about overflow here. +// the linked bug was fixed in gcc 9 and fixes were backported +// to gcc 7 and 8 patch releases. To fix the warning, I just +// added a check for the GCC version. +#if defined(__GNUC__) && (__GNUC__ < 9) && !defined(__clang__) + if (std::is_unsigned::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(pos)); + } + + // Returns a bitmask representing the positions of slots that match hash. + BitMask Match(h2_t hash) const { + auto match = _mm_set1_epi8(hash); + return BitMask( + static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)))); + } + + // Returns a bitmask representing the positions of empty slots. + NonIterableBitMask MaskEmpty() const { +#ifdef ABSL_INTERNAL_HAVE_SSSE3 + // This only works because ctrl_t::kEmpty is -128. + return NonIterableBitMask( + static_cast(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)))); +#else + auto match = _mm_set1_epi8(static_cast(ctrl_t::kEmpty)); + return NonIterableBitMask( + static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)))); +#endif + } + + // Returns a bitmask representing the positions of empty or deleted slots. + NonIterableBitMask MaskEmptyOrDeleted() const { + auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); + return NonIterableBitMask(static_cast( + _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(static_cast(ctrl_t::kSentinel)); + return TrailingZeros(static_cast( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1)); + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + auto msbs = _mm_set1_epi8(static_cast(-128)); + auto x126 = _mm_set1_epi8(126); +#ifdef ABSL_INTERNAL_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 // ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 + +#if defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN) +struct GroupAArch64Impl { + static constexpr size_t kWidth = 8; + + explicit GroupAArch64Impl(const ctrl_t* pos) { + ctrl = vld1_u8(reinterpret_cast(pos)); + } + + BitMask Match(h2_t hash) const { + uint8x8_t dup = vdup_n_u8(hash); + auto mask = vceq_u8(ctrl, dup); + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask( + vget_lane_u64(vreinterpret_u64_u8(mask), 0) & msbs); + } + + NonIterableBitMask MaskEmpty() const { + uint64_t mask = + vget_lane_u64(vreinterpret_u64_u8( + vceq_s8(vdup_n_s8(static_cast(ctrl_t::kEmpty)), + vreinterpret_s8_u8(ctrl))), + 0); + return NonIterableBitMask(mask); + } + + NonIterableBitMask MaskEmptyOrDeleted() const { + uint64_t mask = + vget_lane_u64(vreinterpret_u64_u8(vcgt_s8( + vdup_n_s8(static_cast(ctrl_t::kSentinel)), + vreinterpret_s8_u8(ctrl))), + 0); + return NonIterableBitMask(mask); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0); + // ctrl | ~(ctrl >> 7) will have the lowest bit set to zero for kEmpty and + // kDeleted. We lower all other bits and count number of trailing zeros. + // Clang and GCC optimize countr_zero to rbit+clz without any check for 0, + // so we should be fine. + constexpr uint64_t bits = 0x0101010101010101ULL; + return countr_zero((mask | ~(mask >> 7)) & bits) >> 3; + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0); + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = mask & msbs; + auto res = (~x + (x >> 7)) & ~lsbs; + little_endian::Store64(dst, res); + } + + uint8x8_t ctrl; +}; +#endif // ABSL_INTERNAL_HAVE_ARM_NEON && ABSL_IS_LITTLE_ENDIAN + +struct GroupPortableImpl { + static constexpr size_t kWidth = 8; + + explicit GroupPortableImpl(const ctrl_t* pos) + : ctrl(little_endian::Load64(pos)) {} + + BitMask 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 ctrl_t::kEmpty, ctrl_t::kDeleted, ctrl_t::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((x - lsbs) & ~x & msbs); + } + + NonIterableBitMask MaskEmpty() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return NonIterableBitMask((ctrl & (~ctrl << 6)) & + msbs); + } + + NonIterableBitMask MaskEmptyOrDeleted() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return NonIterableBitMask((ctrl & (~ctrl << 7)) & + msbs); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + // ctrl | ~(ctrl >> 7) will have the lowest bit set to zero for kEmpty and + // kDeleted. We lower all other bits and count number of trailing zeros. + constexpr uint64_t bits = 0x0101010101010101ULL; + return countr_zero((ctrl | ~(ctrl >> 7)) & bits) >> 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; +}; + +#ifdef ABSL_INTERNAL_HAVE_SSE2 +using Group = GroupSse2Impl; +#elif defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN) +using Group = GroupAArch64Impl; +#else +using Group = GroupPortableImpl; +#endif + +// Returns he number of "cloned control bytes". +// +// This is the number of control bytes that are present both at the beginning +// of the control byte array and at the end, such that we can create a +// `Group::kWidth`-width probe window starting from any control byte. +constexpr size_t NumClonedBytes() { return Group::kWidth - 1; } + +template +class raw_hash_set; + +// Returns whether `n` is a valid capacity (i.e., number of slots). +// +// A valid capacity is a non-zero integer `2^m - 1`. +inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } + +// Applies the following mapping to every byte in the control array: +// * kDeleted -> kEmpty +// * kEmpty -> kEmpty +// * _ -> kDeleted +// PRECONDITION: +// IsValidCapacity(capacity) +// ctrl[capacity] == ctrl_t::kSentinel +// ctrl[i] != ctrl_t::kSentinel for all i < capacity +void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity); + +// Converts `n` into the next valid capacity, per `IsValidCapacity`. +inline size_t NormalizeCapacity(size_t n) { + return n ? ~size_t{} >> countl_zero(n) : 1; +} + +// General notes on capacity/growth methods below: +// - We use 7/8th as maximum load factor. For 16-wide groups, that gives an +// average of two empty slots per group. +// - For (capacity+1) >= Group::kWidth, growth is 7/8*capacity. +// - For (capacity+1) < Group::kWidth, growth == capacity. In this case, we +// never need to probe (the whole table fits in one group) so we don't need a +// load factor less than 1. + +// Given `capacity`, applies the load factor; i.e., it returns the maximum +// number of values we should put into the table before a resizing rehash. +inline size_t CapacityToGrowth(size_t capacity) { + assert(IsValidCapacity(capacity)); + // `capacity*7/8` + if (Group::kWidth == 8 && capacity == 7) { + // x-x/8 does not work when x==7. + return 6; + } + return capacity - capacity / 8; +} + +// Given `growth`, "unapplies" the load factor to find how large the capacity +// should be to stay within the load factor. +// +// This might not be a valid capacity and `NormalizeCapacity()` should be +// called on this. +inline size_t GrowthToLowerboundCapacity(size_t growth) { + // `growth*8/7` + if (Group::kWidth == 8 && growth == 7) { + // x+(x-1)/7 does not work when x==7. + return 8; + } + return growth + static_cast((static_cast(growth) - 1) / 7); +} + +template +size_t SelectBucketCountForIterRange(InputIter first, InputIter last, + size_t bucket_count) { + if (bucket_count != 0) { + return bucket_count; + } + using InputIterCategory = + typename std::iterator_traits::iterator_category; + if (std::is_base_of::value) { + return GrowthToLowerboundCapacity( + static_cast(std::distance(first, last))); + } + return 0; +} + +#define ABSL_INTERNAL_ASSERT_IS_FULL(ctrl, msg) \ + ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && msg) + +inline void AssertIsValid(ctrl_t* ctrl) { + ABSL_HARDENING_ASSERT( + (ctrl == nullptr || IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, the table might have rehashed, or this may " + "be an end() iterator."); +} + +struct FindInfo { + size_t offset; + size_t probe_length; +}; + +// Whether a table is "small". A small table fits entirely into a probing +// group, i.e., has a capacity < `Group::kWidth`. +// +// In small mode we are able to use the whole capacity. The extra control +// bytes give us at least one "empty" control byte to stop the iteration. +// This is important to make 1 a valid capacity. +// +// In small mode only the first `capacity` control bytes after the sentinel +// are valid. The rest contain dummy ctrl_t::kEmpty values that do not +// represent a real slot. This is important to take into account on +// `find_first_non_full()`, where we never try +// `ShouldInsertBackwards()` for small tables. +inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; } + +// Begins a probing operation on `ctrl`, using `hash`. +inline probe_seq probe(const ctrl_t* ctrl, size_t hash, + size_t capacity) { + return probe_seq(H1(hash, ctrl), capacity); +} + +// Probes an array of control bits using a probe sequence derived from `hash`, +// and returns the offset corresponding to the first deleted or empty slot. +// +// Behavior when the entire table is full is undefined. +// +// NOTE: this function must work with tables having both empty and deleted +// slots in the same group. Such tables appear during `erase()`. +template +inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, + size_t capacity) { + auto seq = probe(ctrl, hash, capacity); + while (true) { + Group g{ctrl + seq.offset()}; + auto mask = g.MaskEmptyOrDeleted(); + if (mask) { +#if !defined(NDEBUG) + // We want to add entropy even when ASLR is not enabled. + // 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 (!is_small(capacity) && ShouldInsertBackwards(hash, ctrl)) { + return {seq.offset(mask.HighestBitSet()), seq.index()}; + } +#endif + return {seq.offset(mask.LowestBitSet()), seq.index()}; + } + seq.next(); + assert(seq.index() <= capacity && "full table!"); + } +} + +// Extern template for inline function keep possibility of inlining. +// When compiler decided to not inline, no symbols will be added to the +// corresponding translation unit. +extern template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); + +// Sets `ctrl` to `{kEmpty, kSentinel, ..., kEmpty}`, marking the entire +// array as marked as empty. +inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, + size_t slot_size) { + std::memset(ctrl, static_cast(ctrl_t::kEmpty), + capacity + 1 + NumClonedBytes()); + ctrl[capacity] = ctrl_t::kSentinel; + SanitizerPoisonMemoryRegion(slot, slot_size * capacity); +} + +// Sets `ctrl[i]` to `h`. +// +// Unlike setting it directly, this function will perform bounds checks and +// mirror the value to the cloned tail if necessary. +inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl, + const void* slot, size_t slot_size) { + assert(i < capacity); + + auto* slot_i = static_cast(slot) + i * slot_size; + if (IsFull(h)) { + SanitizerUnpoisonMemoryRegion(slot_i, slot_size); + } else { + SanitizerPoisonMemoryRegion(slot_i, slot_size); + } + + ctrl[i] = h; + ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h; +} + +// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`. +inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl, + const void* slot, size_t slot_size) { + SetCtrl(i, static_cast(h), capacity, ctrl, slot, slot_size); +} + +// Given the capacity of a table, computes the offset (from the start of the +// backing allocation) at which the slots begin. +inline size_t SlotOffset(size_t capacity, size_t slot_align) { + assert(IsValidCapacity(capacity)); + const size_t num_control_bytes = capacity + 1 + NumClonedBytes(); + return (num_control_bytes + slot_align - 1) & (~slot_align + 1); +} + +// Given the capacity of a table, computes the total size of the backing +// array. +inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) { + return SlotOffset(capacity, slot_align) + capacity * slot_size; +} + +// A SwissTable. +// +// 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 +// [https://en.cppreference.com/w/cpp/named_req/Allocator] with which +// the storage of the hashtable will be allocated and the elements will be +// constructed and destroyed. +template +class raw_hash_set { + using PolicyTraits = hash_policy_traits; + using KeyArgImpl = + KeyArg::value && IsTransparent::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::pointer; + using const_pointer = typename absl::allocator_traits< + allocator_type>::template rebind_traits::const_pointer; + + // Alias used for heterogeneous lookup functions. + // `key_arg` 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 + using key_arg = typename KeyArgImpl::template 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 AllocTraits = absl::allocator_traits; + using SlotAlloc = typename absl::allocator_traits< + allocator_type>::template rebind_alloc; + using SlotAllocTraits = typename absl::allocator_traits< + allocator_type>::template rebind_traits; + + static_assert(std::is_lvalue_reference::value, + "Policy::element() must return a reference"); + + template + struct SameAsElementReference + : std::is_same::type>::type, + typename std::remove_cv< + typename std::remove_reference::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 + using RequiresInsertable = typename std::enable_if< + absl::disjunction, + SameAsElementReference>::value, + int>::type; + + // RequiresNotInit is a workaround for gcc prior to 7.1. + // See https://godbolt.org/g/Y4xsUh. + template + using RequiresNotInit = + typename std::enable_if::value, int>::type; + + template + using IsDecomposable = IsDecomposable; + + public: + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + static_assert(std::is_same::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; + using pointer = absl::remove_reference_t*; + using difference_type = typename raw_hash_set::difference_type; + + iterator() {} + + // PRECONDITION: not an end() iterator. + reference operator*() const { + ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, + "operator*() called on invalid iterator."); + return PolicyTraits::element(slot_); + } + + // PRECONDITION: not an end() iterator. + pointer operator->() const { + ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, + "operator-> called on invalid iterator."); + return &operator*(); + } + + // PRECONDITION: not an end() iterator. + iterator& operator++() { + ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, + "operator++ called on invalid iterator."); + ++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) { + AssertIsValid(a.ctrl_); + AssertIsValid(b.ctrl_); + return a.ctrl_ == b.ctrl_; + } + friend bool operator!=(const iterator& a, const iterator& b) { + return !(a == b); + } + + private: + iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) { + // This assumption helps the compiler know that any non-end iterator is + // not equal to any end iterator. + ABSL_ASSUME(ctrl != nullptr); + } + + // Fixes up `ctrl_` to point to a full by advancing it and `slot_` until + // they reach one. + // + // If a sentinel is reached, we null both of them out instead. + void skip_empty_or_deleted() { + while (IsEmptyOrDeleted(*ctrl_)) { + uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); + ctrl_ += shift; + slot_ += shift; + } + if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr; + } + + ctrl_t* ctrl_ = nullptr; + // To avoid uninitialized member warnings, put slot_ in an anonymous union. + // The member is not initialized on singleton and end iterators. + union { + 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), const_cast(slot)) {} + + iterator inner_; + }; + + using node_type = node_handle, Alloc>; + using insert_return_type = InsertReturnType; + + raw_hash_set() noexcept( + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::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, HashtablezInfoHandle(), hash, eq, alloc) { + if (bucket_count) { + capacity_ = NormalizeCapacity(bucket_count); + 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 + 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(SelectBucketCountForIterRange(first, last, bucket_count), + hash, eq, alloc) { + insert(first, last); + } + + template + 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 + 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 + 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 as the first + // argument like std::unordered_set does, we have two overloads + // that accept std::initializer_list and std::initializer_list. + // This is advantageous for performance. + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // std::unordered_set s = {"abc", "def"}; + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // absl::flat_hash_set 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 a, b{a}; + // + // RequiresNotInit is a workaround for gcc prior to 7.1. + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list 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, 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 = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list 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, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list 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, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list 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); + auto target = find_first_non_full(ctrl_, hash, capacity_); + SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_, + sizeof(slot_type)); + emplace_at(target.offset, v); + infoz().RecordInsert(hash, target.probe_length); + } + size_ = that.size(); + growth_left() -= that.size(); + } + + raw_hash_set(raw_hash_set&& that) noexcept( + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::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, moving it + // would create a nullptr functor that cannot be called. + settings_(absl::exchange(that.growth_left(), 0), + absl::exchange(that.infoz(), HashtablezInfoHandle()), + that.hash_ref(), that.eq_ref(), that.alloc_ref()) {} + + raw_hash_set(raw_hash_set&& that, const allocator_type& a) + : ctrl_(EmptyGroup()), + slots_(nullptr), + size_(0), + capacity_(0), + settings_(0, HashtablezInfoHandle(), 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()); + std::swap(infoz(), that.infoz()); + } 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::is_always_equal::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_assignable::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 {}; } + + const_iterator begin() const { + return const_cast(this)->begin(); + } + const_iterator end() const { return {}; } + 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::max)(); } + + ABSL_ATTRIBUTE_REINITIALIZES 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(); + + infoz().RecordClearedReservation(); + } else if (capacity_) { + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + size_ = 0; + ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); + reset_growth_left(); + } + assert(empty()); + infoz().RecordStorageChanged(0, capacity_); + } + + // This overload kicks in when the argument is an rvalue of insertable and + // decomposable type other than init_type. + // + // flat_hash_map m; + // m.insert(std::make_pair("abc", 42)); + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. + template = 0, class T2 = T, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + std::pair insert(T&& value) { + return emplace(std::forward(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 s; + // s.insert(n); + // + // flat_hash_set s; + // const char* p = "hello"; + // s.insert(p); + // + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + template < + class T, RequiresInsertable = 0, + typename std::enable_if::value, int>::type = 0> + std::pair 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_map s; + // s.insert({"abc", 42}); + std::pair insert(init_type&& value) { + return emplace(std::move(value)); + } + + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. + template = 0, class T2 = T, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + iterator insert(const_iterator, T&& value) { + return insert(std::forward(value)).first; + } + + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + template < + class T, RequiresInsertable = 0, + typename std::enable_if::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 + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) emplace(*first); + } + + template = 0, RequiresInsertable = 0> + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + insert_return_type insert(node_type&& node) { + if (!node) return {end(), false, node_type()}; + const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node)); + auto res = PolicyTraits::apply( + InsertSlot{*this, std::move(*CommonAccess::GetSlot(node))}, + elem); + if (res.second) { + CommonAccess::Reset(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + + iterator insert(const_iterator, node_type&& node) { + auto res = insert(std::move(node)); + node = std::move(res.node); + return res.position; + } + + // 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 m = {{"abc", "def"}}; + // // Creates no std::string copies and makes no heap allocations. + // m.emplace("abc", "xyz"); + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + return PolicyTraits::apply(EmplaceDecomposable{*this}, + std::forward(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 ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + alignas(slot_type) unsigned char raw[sizeof(slot_type)]; + slot_type* slot = reinterpret_cast(&raw); + + PolicyTraits::construct(&alloc_ref(), slot, std::forward(args)...); + const auto& elem = PolicyTraits::element(slot); + return PolicyTraits::apply(InsertSlot{*this, std::move(*slot)}, elem); + } + + template + iterator emplace_hint(const_iterator, Args&&... args) { + return emplace(std::forward(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 abide by several restrictions: + // - it MUST call `raw_hash_set::constructor` with arguments as if a + // `raw_hash_set::value_type` is constructed, + // - it MUST NOT access the container before the call to + // `raw_hash_set::constructor`, and + // - it MUST NOT erase the lazily emplaced element. + // Doing any of these is undefined behavior. + // + // For example: + // + // std::unordered_set s; + // // Makes ArenaStr even if "abc" is in the map. + // s.insert(ArenaString(&arena, "abc")); + // + // flat_hash_set 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 + void operator()(Args&&... args) const { + assert(*slot_); + PolicyTraits::construct(alloc_, *slot_, std::forward(args)...); + *slot_ = nullptr; + } + + private: + constructor(allocator_type* a, slot_type** slot) : alloc_(a), slot_(slot) {} + + allocator_type* alloc_; + slot_type** slot_; + }; + + template + iterator lazy_emplace(const key_arg& key, F&& f) { + auto res = find_or_prepare_insert(key); + if (res.second) { + slot_type* slot = slots_ + res.first; + std::forward(f)(constructor(&alloc_ref(), &slot)); + assert(!slot); + } + return iterator_at(res.first); + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.erase("abc"); + // + // flat_hash_set s; + // // Uses "abc" directly without copying it into std::string. + // s.erase("abc"); + template + size_type erase(const key_arg& 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). The + // iterator is invalidated, so any increment should be done before calling + // erase. 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;) { + // // `erase()` will invalidate `it`, so advance `it` first. + // auto copy_it = it++; + // if () { + // m.erase(copy_it); + // } + // } + void erase(const_iterator cit) { erase(cit.inner_); } + + // This overload is necessary because otherwise erase(const K&) would be + // a better match if non-const iterator is passed as an argument. + void erase(iterator it) { + ABSL_INTERNAL_ASSERT_IS_FULL(it.ctrl_, + "erase() called on invalid iterator."); + 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 + void merge(raw_hash_set& src) { // NOLINT + assert(this != &src); + for (auto it = src.begin(), e = src.end(); it != e;) { + auto next = std::next(it); + if (PolicyTraits::apply(InsertSlot{*this, std::move(*it.slot_)}, + PolicyTraits::element(it.slot_)) + .second) { + src.erase_meta_only(it); + } + it = next; + } + } + + template + void merge(raw_hash_set&& src) { + merge(src); + } + + node_type extract(const_iterator position) { + ABSL_INTERNAL_ASSERT_IS_FULL(position.inner_.ctrl_, + "extract() called on invalid iterator."); + auto node = + CommonAccess::Transfer(alloc_ref(), position.inner_.slot_); + erase_meta_only(position); + return node; + } + + template < + class K = key_type, + typename std::enable_if::value, int>::type = 0> + node_type extract(const key_arg& key) { + auto it = find(key); + return it == end() ? node_type() : extract(const_iterator{it}); + } + + void swap(raw_hash_set& that) noexcept( + IsNoThrowSwappable() && IsNoThrowSwappable() && + IsNoThrowSwappable( + typename AllocTraits::propagate_on_container_swap{})) { + 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()); + swap(infoz(), that.infoz()); + SwapAlloc(alloc_ref(), that.alloc_ref(), + typename AllocTraits::propagate_on_container_swap{}); + } + + void rehash(size_t n) { + if (n == 0 && capacity_ == 0) return; + if (n == 0 && size_ == 0) { + destroy_slots(); + infoz().RecordStorageChanged(0, 0); + infoz().RecordClearedReservation(); + return; + } + + // bitor is a faster way of doing `max` here. We will round up to the next + // power-of-2-minus-1, so bitor is good enough. + auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size())); + // n == 0 unconditionally rehashes as per the standard. + if (n == 0 || m > capacity_) { + resize(m); + + // This is after resize, to ensure that we have completed the allocation + // and have potentially sampled the hashtable. + infoz().RecordReservation(n); + } + } + + void reserve(size_t n) { + if (n > size() + growth_left()) { + size_t m = GrowthToLowerboundCapacity(n); + resize(NormalizeCapacity(m)); + + // This is after resize, to ensure that we have completed the allocation + // and have potentially sampled the hashtable. + infoz().RecordReservation(n); + } + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.count("abc"); + // + // ch_set s; + // // Uses "abc" directly without copying it into std::string. + // s.count("abc"); + template + size_t count(const key_arg& 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 + void prefetch(const key_arg& key) const { + (void)key; + // Avoid probing if we won't be able to prefetch the addresses received. +#ifdef ABSL_INTERNAL_HAVE_PREFETCH + prefetch_heap_block(); + auto seq = probe(ctrl_, hash_ref()(key), capacity_); + base_internal::PrefetchT0(ctrl_ + seq.offset()); + base_internal::PrefetchT0(slots_ + seq.offset()); +#endif // ABSL_INTERNAL_HAVE_PREFETCH + } + + // 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 + iterator find(const key_arg& key, size_t hash) { + auto seq = probe(ctrl_, hash, capacity_); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (uint32_t i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slots_ + seq.offset(i))))) + return iterator_at(seq.offset(i)); + } + if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return end(); + seq.next(); + assert(seq.index() <= capacity_ && "full table!"); + } + } + template + iterator find(const key_arg& key) { + prefetch_heap_block(); + return find(key, hash_ref()(key)); + } + + template + const_iterator find(const key_arg& key, size_t hash) const { + return const_cast(this)->find(key, hash); + } + template + const_iterator find(const key_arg& key) const { + prefetch_heap_block(); + return find(key, hash_ref()(key)); + } + + template + bool contains(const key_arg& key) const { + return find(key) != end(); + } + + template + std::pair equal_range(const key_arg& key) { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + template + std::pair equal_range( + const key_arg& 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(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); + } + + template + friend typename std::enable_if::value, + H>::type + AbslHashValue(H h, const raw_hash_set& s) { + return H::combine(H::combine_unordered(std::move(h), s.begin(), s.end()), + s.size()); + } + + friend void swap(raw_hash_set& a, + raw_hash_set& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); + } + + private: + template + friend struct absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess; + + struct FindElement { + template + const_iterator operator()(const K& key, Args&&...) const { + return s.find(key); + } + const raw_hash_set& s; + }; + + struct HashElement { + template + size_t operator()(const K& key, Args&&...) const { + return h(key); + } + const hasher& h; + }; + + template + struct EqualElement { + template + bool operator()(const K2& lhs, Args&&...) const { + return eq(lhs, rhs); + } + const K1& rhs; + const key_equal& eq; + }; + + struct EmplaceDecomposable { + template + std::pair 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)...); + } + return {s.iterator_at(res.first), res.second}; + } + raw_hash_set& s; + }; + + template + struct InsertSlot { + template + std::pair 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; + }; + + // Erases, but does not destroy, the value pointed to by `it`. + // + // This merely updates the pertinent control byte. 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 = static_cast(it.inner_.ctrl_ - ctrl_); + const size_t index_before = (index - Group::kWidth) & capacity_; + const auto empty_after = Group(it.inner_.ctrl_).MaskEmpty(); + const auto empty_before = Group(ctrl_ + index_before).MaskEmpty(); + + // 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(empty_after.TrailingZeros() + + empty_before.LeadingZeros()) < Group::kWidth; + + SetCtrl(index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted, + capacity_, ctrl_, slots_, sizeof(slot_type)); + growth_left() += was_never_full; + infoz().RecordErase(); + } + + // Allocates a backing array for `self` and initializes its control bytes. + // This reads `capacity_` and updates all other fields based on the result of + // the allocation. + // + // This does not free the currently held array; `capacity_` must be nonzero. + void initialize_slots() { + assert(capacity_); + // Folks with custom allocators often make unwarranted assumptions about the + // behavior of their classes vis-a-vis trivial destructability and what + // calls they will or wont make. Avoid sampling for people with custom + // allocators to get us out of this mess. This is not a hard guarantee but + // a workaround while we plan the exact guarantee we want to provide. + // + // People are often sloppy with the exact type of their allocator (sometimes + // it has an extra const or is missing the pair, but rebinds made it work + // anyway). To avoid the ambiguity, we work off SlotAlloc which we have + // bound more carefully. + if (std::is_same>::value && + slots_ == nullptr) { + infoz() = Sample(sizeof(slot_type)); + } + + char* mem = static_cast(Allocate( + &alloc_ref(), + AllocSize(capacity_, sizeof(slot_type), alignof(slot_type)))); + ctrl_ = reinterpret_cast(mem); + slots_ = reinterpret_cast( + mem + SlotOffset(capacity_, alignof(slot_type))); + ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); + reset_growth_left(); + infoz().RecordStorageChanged(size_, capacity_); + } + + // Destroys all slots in the backing array, frees the backing array, and + // clears all top-level book-keeping data. + // + // This essentially implements `map = raw_hash_set();`. + void destroy_slots() { + if (!capacity_) return; + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + Deallocate( + &alloc_ref(), ctrl_, + AllocSize(capacity_, sizeof(slot_type), alignof(slot_type))); + 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(); + + size_t total_probe_length = 0; + 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)); + auto target = find_first_non_full(ctrl_, hash, capacity_); + size_t new_i = target.offset; + total_probe_length += target.probe_length; + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); + } + } + if (old_capacity) { + SanitizerUnpoisonMemoryRegion(old_slots, + sizeof(slot_type) * old_capacity); + Deallocate( + &alloc_ref(), old_ctrl, + AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type))); + } + infoz().RecordRehash(total_probe_length); + } + + // Prunes control bytes to remove as many tombstones as possible. + // + // See the comment on `rehash_and_grow_if_necessary()`. + void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE { + assert(IsValidCapacity(capacity_)); + assert(!is_small(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_); + alignas(slot_type) unsigned char raw[sizeof(slot_type)]; + size_t total_probe_length = 0; + slot_type* slot = reinterpret_cast(&raw); + for (size_t i = 0; i != capacity_; ++i) { + if (!IsDeleted(ctrl_[i])) continue; + const size_t hash = PolicyTraits::apply( + HashElement{hash_ref()}, PolicyTraits::element(slots_ + i)); + const FindInfo target = find_first_non_full(ctrl_, hash, capacity_); + const size_t new_i = target.offset; + total_probe_length += target.probe_length; + + // 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 size_t probe_offset = probe(ctrl_, hash, capacity_).offset(); + const auto probe_index = [probe_offset, this](size_t pos) { + return ((pos - probe_offset) & capacity_) / Group::kWidth; + }; + + // Element doesn't move. + if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { + SetCtrl(i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); + continue; + } + if (IsEmpty(ctrl_[new_i])) { + // Transfer element to the empty spot. + // SetCtrl poisons/unpoisons the slots so we have to call it at the + // right time. + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); + SetCtrl(i, ctrl_t::kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type)); + } else { + assert(IsDeleted(ctrl_[new_i])); + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); + // 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 + } + } + reset_growth_left(); + infoz().RecordRehash(total_probe_length); + } + + // Called whenever the table *might* need to conditionally grow. + // + // This function is an optimization opportunity to perform a rehash even when + // growth is unnecessary, because vacating tombstones is beneficial for + // performance in the long-run. + void rehash_and_grow_if_necessary() { + if (capacity_ == 0) { + resize(1); + } else if (capacity_ > Group::kWidth && + // Do these calcuations in 64-bit to avoid overflow. + size() * uint64_t{32} <= capacity_ * uint64_t{25}) { + // Squash DELETED without growing if there is enough capacity. + // + // Rehash in place if the current size is <= 25/32 of capacity_. + // Rationale for such a high factor: 1) drop_deletes_without_resize() is + // faster than resize, and 2) it takes quite a bit of work to add + // tombstones. In the worst case, seems to take approximately 4 + // insert/erase pairs to create a single tombstone and so if we are + // rehashing because of tombstones, we can afford to rehash-in-place as + // long as we are reclaiming at least 1/8 the capacity without doing more + // than 2X the work. (Where "work" is defined to be size() for rehashing + // or rehashing in place, and 1 for an insert or erase.) But rehashing in + // place is faster per operation than inserting or even doubling the size + // of the table, so we actually afford to reclaim even less space from a + // resize-in-place. The decision is to rehash in place if we can reclaim + // at about 1/8th of the usable capacity (specifically 3/28 of the + // capacity) which means that the total cost of rehashing will be a small + // fraction of the total work. + // + // Here is output of an experiment using the BM_CacheInSteadyState + // benchmark running the old case (where we rehash-in-place only if we can + // reclaim at least 7/16*capacity_) vs. this code (which rehashes in place + // if we can recover 3/32*capacity_). + // + // Note that although in the worst-case number of rehashes jumped up from + // 15 to 190, but the number of operations per second is almost the same. + // + // Abridged output of running BM_CacheInSteadyState benchmark from + // raw_hash_set_benchmark. N is the number of insert/erase operations. + // + // | OLD (recover >= 7/16 | NEW (recover >= 3/32) + // size | N/s LoadFactor NRehashes | N/s LoadFactor NRehashes + // 448 | 145284 0.44 18 | 140118 0.44 19 + // 493 | 152546 0.24 11 | 151417 0.48 28 + // 538 | 151439 0.26 11 | 151152 0.53 38 + // 583 | 151765 0.28 11 | 150572 0.57 50 + // 628 | 150241 0.31 11 | 150853 0.61 66 + // 672 | 149602 0.33 12 | 150110 0.66 90 + // 717 | 149998 0.35 12 | 149531 0.70 129 + // 762 | 149836 0.37 13 | 148559 0.74 190 + // 807 | 149736 0.39 14 | 151107 0.39 14 + // 852 | 150204 0.42 15 | 151019 0.42 15 + 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(ctrl_, hash, capacity_); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (uint32_t i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) == + elem)) + return true; + } + if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return false; + seq.next(); + assert(seq.index() <= capacity_ && "full table!"); + } + return false; + } + + // 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: + // Attempts to find `key` in the table; if it isn't found, returns a slot that + // the value can be inserted into, with the control byte already set to + // `key`'s H2. + template + std::pair find_or_prepare_insert(const K& key) { + prefetch_heap_block(); + auto hash = hash_ref()(key); + auto seq = probe(ctrl_, hash, capacity_); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (uint32_t i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slots_ + seq.offset(i))))) + return {seq.offset(i), false}; + } + if (ABSL_PREDICT_TRUE(g.MaskEmpty())) break; + seq.next(); + assert(seq.index() <= capacity_ && "full table!"); + } + return {prepare_insert(hash), true}; + } + + // Given the hash of a value not currently in the table, finds the next + // viable slot index to insert it at. + // + // REQUIRES: At least one non-full slot available. + size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE { + auto target = find_first_non_full(ctrl_, hash, capacity_); + if (ABSL_PREDICT_FALSE(growth_left() == 0 && + !IsDeleted(ctrl_[target.offset]))) { + rehash_and_grow_if_necessary(); + target = find_first_non_full(ctrl_, hash, capacity_); + } + ++size_; + growth_left() -= IsEmpty(ctrl_[target.offset]); + SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_, + sizeof(slot_type)); + infoz().RecordInsert(hash, target.probe_length); + return target.offset; + } + + // 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)...`, and the bool + // returned by find_or_prepare_insert(k) was true. + // POSTCONDITION: *m.iterator_at(i) == value_type(forward(args)...). + template + void emplace_at(size_t i, Args&&... args) { + PolicyTraits::construct(&alloc_ref(), slots_ + i, + std::forward(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; + + void reset_growth_left() { + growth_left() = CapacityToGrowth(capacity()) - size_; + } + + // The number of slots we can still fill without needing to rehash. + // + // This is stored separately due to tombstones: we do not include tombstones + // in the growth capacity, because we'd like to rehash when the table is + // otherwise filled with tombstones: otherwise, probe sequences might get + // unacceptably long without triggering a rehash. Callers can also force a + // rehash via the standard `rehash(0)`, which will recompute this value as a + // side-effect. + // + // See `CapacityToGrowth()`. + size_t& growth_left() { return settings_.template get<0>(); } + + // Prefetch the heap-allocated memory region to resolve potential TLB misses. + // This is intended to overlap with execution of calculating the hash for a + // key. + void prefetch_heap_block() const { + base_internal::PrefetchT2(ctrl_); + } + + HashtablezInfoHandle& infoz() { return settings_.template get<1>(); } + + hasher& hash_ref() { return settings_.template get<2>(); } + const hasher& hash_ref() const { return settings_.template get<2>(); } + key_equal& eq_ref() { return settings_.template get<3>(); } + const key_equal& eq_ref() const { return settings_.template get<3>(); } + allocator_type& alloc_ref() { return settings_.template get<4>(); } + const allocator_type& alloc_ref() const { + return settings_.template get<4>(); + } + + // TODO(alkis): Investigate removing some of these fields: + // - ctrl/slots can be derived from each other + // - size can be moved into the slot array + + // The control bytes (and, also, a pointer to the base of the backing array). + // + // This contains `capacity_ + 1 + NumClonedBytes()` entries, even + // when the table is empty (hence EmptyGroup). + ctrl_t* ctrl_ = EmptyGroup(); + // The beginning of the slots, located at `SlotOffset()` bytes after + // `ctrl_`. May be null for empty tables. + slot_type* slots_ = nullptr; + + // The number of filled slots. + size_t size_ = 0; + + // The total number of available slots. + size_t capacity_ = 0; + absl::container_internal::CompressedTuple + settings_{0u, HashtablezInfoHandle{}, hasher{}, key_equal{}, + allocator_type{}}; +}; + +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +typename raw_hash_set::size_type EraseIf( + Predicate& pred, raw_hash_set* c) { + const auto initial_size = c->size(); + for (auto it = c->begin(), last = c->end(); it != last;) { + if (pred(*it)) { + c->erase(it++); + } else { + ++it; + } + } + return initial_size - c->size(); +} + +namespace hashtable_debug_internal { +template +struct HashtableDebugAccess> { + 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 = probe(set.ctrl_, hash, set.capacity_); + while (true) { + container_internal::Group g{set.ctrl_ + seq.offset()}; + for (uint32_t i : g.Match(container_internal::H2(hash))) { + if (Traits::apply( + typename Set::template EqualElement{ + key, set.eq_ref()}, + Traits::element(set.slots_ + seq.offset(i)))) + return num_probes; + ++num_probes; + } + if (g.MaskEmpty()) return num_probes; + seq.next(); + ++num_probes; + } + } + + static size_t AllocatedByteSize(const Set& c) { + size_t capacity = c.capacity_; + if (capacity == 0) return 0; + size_t m = AllocSize(capacity, sizeof(Slot), alignof(Slot)); + + size_t per_slot = Traits::space_used(static_cast(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 = GrowthToLowerboundCapacity(size); + if (capacity == 0) return 0; + size_t m = + AllocSize(NormalizeCapacity(capacity), sizeof(Slot), alignof(Slot)); + size_t per_slot = Traits::space_used(static_cast(nullptr)); + if (per_slot != ~size_t{}) { + m += per_slot * size; + } + return m; + } +}; + +} // namespace hashtable_debug_internal +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#undef ABSL_INTERNAL_ASSERT_IS_FULL + +#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ diff --git a/src/absl/container/internal/tracked.h b/src/absl/container/internal/tracked.h new file mode 100644 index 0000000..29f5829 --- /dev/null +++ b/src/absl/container/internal/tracked.h @@ -0,0 +1,83 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_TRACKED_H_ +#define ABSL_CONTAINER_INTERNAL_TRACKED_H_ + +#include + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A class that tracks its copies and moves so that it can be queried in tests. +template +class 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 num_moves_ = std::make_shared(0); + std::shared_ptr num_copies_ = std::make_shared(0); +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_TRACKED_H_ diff --git a/src/absl/container/node_hash_map.h b/src/absl/container/node_hash_map.h new file mode 100644 index 0000000..6868e63 --- /dev/null +++ b/src/absl/container/node_hash_map.h @@ -0,0 +1,604 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: node_hash_map.h +// ----------------------------------------------------------------------------- +// +// An `absl::node_hash_map` 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 +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/base/macros.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/node_slot_policy.h" +#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +class NodeHashMapPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::node_hash_map +// ----------------------------------------------------------------------------- +// +// An `absl::node_hash_map` 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` with +// the following notable differences: +// +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash map. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `node_hash_map` uses the `absl::Hash` hashing framework. +// All fundamental and Abseil types that support the `absl::Hash` framework have +// a compatible equality operator for comparing insertions into `node_hash_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// Using `absl::node_hash_map` at interface boundaries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// +// Example: +// +// // Create a node hash map of three strings (that map to strings) +// absl::node_hash_map 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 Eq = absl::container_internal::hash_default_eq, + class Alloc = std::allocator>> +class node_hash_map + : public absl::container_internal::raw_hash_map< + absl::container_internal::NodeHashMapPolicy, 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 map1; + // + // * Initializer List constructor + // + // absl::node_hash_map map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::node_hash_map map3(map2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::node_hash_map map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::node_hash_map map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::node_hash_map map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::node_hash_map 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`. + using Base::max_size; + + // node_hash_map::size() + // + // Returns the number of elements currently within the `node_hash_map`. + using Base::size; + + // node_hash_map::clear() + // + // Removes all elements from the `node_hash_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // node_hash_map::erase() + // + // Erases elements within the `node_hash_map`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `node_hash_map`, returning + // `void`. + // + // NOTE: this return behavior is different than that of STL containers in + // general and `std::unordered_map` in particular. + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). + using Base::erase; + + // node_hash_map::insert() + // + // Inserts an element of the specified value into the `node_hash_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair 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 insert(T&& value): + // std::pair 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 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 insert_or_assign(const init_type& k, T&& obj): + // std::pair 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 try_emplace(const key_type& k, Args&&... args): + // std::pair try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `node_hash_map`. + // + // iterator try_emplace(const_iterator hint, + // const key_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `node_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + // + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. + using Base::try_emplace; + + // node_hash_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the key,value pair of the element at the indicated position and + // returns a node handle owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the key,value pair of the element with a key matching the passed + // key value and returns a node handle owning that extracted data. If the + // `node_hash_map` does not contain an element with a matching key, this + // function returns an empty node handle. + // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). + using Base::extract; + + // node_hash_map::merge() + // + // Extracts elements from a given `source` node hash map into this + // `node_hash_map`. If the destination `node_hash_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // node_hash_map::swap(node_hash_map& other) + // + // Exchanges the contents of this `node_hash_map` with those of the `other` + // node hash map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `node_hash_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the node hash map's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // node_hash_map::rehash(count) + // + // Rehashes the `node_hash_map`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + using Base::rehash; + + // node_hash_map::reserve(count) + // + // Sets the number of slots in the `node_hash_map` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // node_hash_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // node_hash_map::contains() + // + // Determines whether an element with a key comparing equal to the given `key` + // exists within the `node_hash_map`, returning `true` if so or `false` + // otherwise. + using Base::contains; + + // node_hash_map::count(const Key& key) const + // + // Returns the number of elements with a key comparing equal to the given + // `key` within the `node_hash_map`. note that this function will return + // either `1` or `0` since duplicate keys are not allowed within a + // `node_hash_map`. + using Base::count; + + // node_hash_map::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `node_hash_map`. + using Base::equal_range; + + // node_hash_map::find() + // + // Finds an element with the passed `key` within the `node_hash_map`. + using Base::find; + + // node_hash_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `node_hash_map`, performing an `insert()` if the key does not already + // exist. If an insertion occurs and results in a rehashing of the container, + // all iterators are invalidated. Otherwise iterators are not affected and + // references are not invalidated. Overloads are listed below. + // + // T& operator[](const Key& key): + // + // Inserts an init_type object constructed in-place if the element with the + // given key does not exist. + // + // T& operator[](Key&& key): + // + // Inserts an init_type object constructed in-place provided that an element + // with the given key does not exist. + using Base::operator[]; + + // node_hash_map::bucket_count() + // + // Returns the number of "buckets" within the `node_hash_map`. + using Base::bucket_count; + + // node_hash_map::load_factor() + // + // Returns the current load factor of the `node_hash_map` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // node_hash_map::max_load_factor() + // + // Manages the maximum load factor of the `node_hash_map`. Overloads are + // listed below. + // + // float node_hash_map::max_load_factor() + // + // Returns the current maximum load factor of the `node_hash_map`. + // + // void node_hash_map::max_load_factor(float ml) + // + // Sets the maximum load factor of the `node_hash_map` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `node_hash_map` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // node_hash_map::get_allocator() + // + // Returns the allocator function associated with this `node_hash_map`. + using Base::get_allocator; + + // node_hash_map::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `node_hash_map`. + using Base::hash_function; + + // node_hash_map::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +// erase_if(node_hash_map<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. +template +typename node_hash_map::size_type erase_if( + node_hash_map& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +class NodeHashMapPolicy + : public absl::container_internal::node_slot_policy< + std::pair&, NodeHashMapPolicy> { + using value_type = std::pair; + + public: + using key_type = Key; + using mapped_type = Value; + using init_type = std::pair; + + template + static value_type* new_element(Allocator* alloc, Args&&... args) { + using PairAlloc = typename absl::allocator_traits< + Allocator>::template rebind_alloc; + PairAlloc pair_alloc(*alloc); + value_type* res = + absl::allocator_traits::allocate(pair_alloc, 1); + absl::allocator_traits::construct(pair_alloc, res, + std::forward(args)...); + return res; + } + + template + static void delete_element(Allocator* alloc, value_type* pair) { + using PairAlloc = typename absl::allocator_traits< + Allocator>::template rebind_alloc; + PairAlloc pair_alloc(*alloc); + absl::allocator_traits::destroy(pair_alloc, pair); + absl::allocator_traits::deallocate(pair_alloc, pair, 1); + } + + template + static decltype(absl::container_internal::DecomposePair( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposePair(std::forward(f), + std::forward(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 +struct IsUnorderedContainer< + absl::node_hash_map> : std::true_type {}; + +} // namespace container_algorithm_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_NODE_HASH_MAP_H_ diff --git a/src/absl/container/node_hash_set.h b/src/absl/container/node_hash_set.h new file mode 100644 index 0000000..f2cc70c --- /dev/null +++ b/src/absl/container/node_hash_set.h @@ -0,0 +1,500 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: node_hash_set.h +// ----------------------------------------------------------------------------- +// +// An `absl::node_hash_set` 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, `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 + +#include "absl/algorithm/container.h" +#include "absl/base/macros.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/node_slot_policy.h" +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +struct NodeHashSetPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::node_hash_set +// ----------------------------------------------------------------------------- +// +// An `absl::node_hash_set` 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` with the +// following notable differences: +// +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the set 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. +// +// Using `absl::node_hash_set` at interface boundaries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// +// Example: +// +// // Create a node hash set of three strings +// absl::node_hash_set ducks = +// {"huey", "dewey", "louie"}; +// +// // Insert a new element into the node hash set +// ducks.insert("donald"); +// +// // Force a rehash of the node hash set +// ducks.rehash(0); +// +// // See if "dewey" is present +// if (ducks.contains("dewey")) { +// std::cout << "We found dewey!" << std::endl; +// } +template , + class Eq = absl::container_internal::hash_default_eq, + class Alloc = std::allocator> +class node_hash_set + : public absl::container_internal::raw_hash_set< + absl::container_internal::NodeHashSetPolicy, 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_set` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::node_hash_set set1; + // + // * Initializer List constructor + // + // absl::node_hash_set set2 = + // {{"huey"}, {"dewey"}, {"louie"}}; + // + // * Copy constructor + // + // absl::node_hash_set set3(set2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::node_hash_set set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::node_hash_set set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::node_hash_set set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::node_hash_set 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_set` 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`. + 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_set` in particular. + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). + using Base::erase; + + // node_hash_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 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 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 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` node hash set 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` + // node hash set, 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 node hash set's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the set's allocator has + // `std::allocator_traits::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 node hash set 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 set). + 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; +}; + +// erase_if(node_hash_set<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. +template +typename node_hash_set::size_type erase_if( + node_hash_set& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +struct NodeHashSetPolicy + : absl::container_internal::node_slot_policy> { + using key_type = T; + using init_type = T; + using constant_iterators = std::true_type; + + template + static T* new_element(Allocator* alloc, Args&&... args) { + using ValueAlloc = + typename absl::allocator_traits::template rebind_alloc; + ValueAlloc value_alloc(*alloc); + T* res = absl::allocator_traits::allocate(value_alloc, 1); + absl::allocator_traits::construct(value_alloc, res, + std::forward(args)...); + return res; + } + + template + static void delete_element(Allocator* alloc, T* elem) { + using ValueAlloc = + typename absl::allocator_traits::template rebind_alloc; + ValueAlloc value_alloc(*alloc); + absl::allocator_traits::destroy(value_alloc, elem); + absl::allocator_traits::deallocate(value_alloc, elem, 1); + } + + template + static decltype(absl::container_internal::DecomposeValue( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposeValue( + std::forward(f), std::forward(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 +struct IsUnorderedContainer> + : std::true_type {}; + +} // namespace container_algorithm_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_NODE_HASH_SET_H_ diff --git a/src/absl/debugging/failure_signal_handler.cc b/src/absl/debugging/failure_signal_handler.cc new file mode 100644 index 0000000..affade3 --- /dev/null +++ b/src/absl/debugging/failure_signal_handler.cc @@ -0,0 +1,386 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "absl/debugging/failure_signal_handler.h" + +#include "absl/base/config.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#ifdef ABSL_HAVE_MMAP +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/sysinfo.h" +#include "absl/debugging/internal/examine_stack.h" +#include "absl/debugging/stacktrace.h" + +#ifndef _WIN32 +#define ABSL_HAVE_SIGACTION +// Apple WatchOS and TVOS don't allow sigaltstack +#if !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \ + !(defined(TARGET_OS_TV) && TARGET_OS_TV) && !defined(__QNX__) +#define ABSL_HAVE_SIGALTSTACK +#endif +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN + +ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options; + +// Resets the signal handler for signo to the default action for that +// signal, then raises the signal. +static void RaiseToDefaultHandler(int signo) { + signal(signo, SIG_DFL); + raise(signo); +} + +struct FailureSignalData { + const int signo; + const char* const as_string; +#ifdef ABSL_HAVE_SIGACTION + struct sigaction previous_action; + // StructSigaction is used to silence -Wmissing-field-initializers. + using StructSigaction = struct sigaction; + #define FSD_PREVIOUS_INIT FailureSignalData::StructSigaction() +#else + void (*previous_handler)(int); + #define FSD_PREVIOUS_INIT SIG_DFL +#endif +}; + +ABSL_CONST_INIT static FailureSignalData failure_signal_data[] = { + {SIGSEGV, "SIGSEGV", FSD_PREVIOUS_INIT}, + {SIGILL, "SIGILL", FSD_PREVIOUS_INIT}, + {SIGFPE, "SIGFPE", FSD_PREVIOUS_INIT}, + {SIGABRT, "SIGABRT", FSD_PREVIOUS_INIT}, + {SIGTERM, "SIGTERM", FSD_PREVIOUS_INIT}, +#ifndef _WIN32 + {SIGBUS, "SIGBUS", FSD_PREVIOUS_INIT}, + {SIGTRAP, "SIGTRAP", FSD_PREVIOUS_INIT}, +#endif +}; + +#undef FSD_PREVIOUS_INIT + +static void RaiseToPreviousHandler(int signo) { + // Search for the previous handler. + for (const auto& it : failure_signal_data) { + if (it.signo == signo) { +#ifdef ABSL_HAVE_SIGACTION + sigaction(signo, &it.previous_action, nullptr); +#else + signal(signo, it.previous_handler); +#endif + raise(signo); + return; + } + } + + // Not found, use the default handler. + RaiseToDefaultHandler(signo); +} + +namespace debugging_internal { + +const char* FailureSignalToString(int signo) { + for (const auto& it : failure_signal_data) { + if (it.signo == signo) { + return it.as_string; + } + } + return ""; +} + +} // namespace debugging_internal + +#ifdef ABSL_HAVE_SIGALTSTACK + +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(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER) + // Account for sanitizer instrumentation requiring additional stack space. + stack_size *= 5; +#endif + + stack_t sigstk; + memset(&sigstk, 0, sizeof(sigstk)); + sigstk.ss_size = stack_size; + +#ifdef ABSL_HAVE_MMAP +#ifndef MAP_STACK +#define MAP_STACK 0 +#endif +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +#define MAP_ANONYMOUS MAP_ANON +#endif + sigstk.ss_sp = mmap(nullptr, sigstk.ss_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + if (sigstk.ss_sp == MAP_FAILED) { + ABSL_RAW_LOG(FATAL, "mmap() for alternate signal stack failed"); + } +#else + sigstk.ss_sp = malloc(sigstk.ss_size); + if (sigstk.ss_sp == nullptr) { + ABSL_RAW_LOG(FATAL, "malloc() for alternate signal stack failed"); + } +#endif + + if (sigaltstack(&sigstk, nullptr) != 0) { + ABSL_RAW_LOG(FATAL, "sigaltstack() failed with errno=%d", errno); + } + return true; +} + +#endif + +#ifdef ABSL_HAVE_SIGACTION + +// Sets up an alternate stack for signal handlers once. +// Returns the appropriate flag for sig_action.sa_flags +// if the system supports using an alternate stack. +static int MaybeSetupAlternateStack() { +#ifdef ABSL_HAVE_SIGALTSTACK + ABSL_ATTRIBUTE_UNUSED static const bool kOnce = SetupAlternateStackOnce(); + return SA_ONSTACK; +#else + return 0; +#endif +} + +static void InstallOneFailureHandler(FailureSignalData* data, + void (*handler)(int, siginfo_t*, void*)) { + struct sigaction act; + memset(&act, 0, sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_flags |= SA_SIGINFO; + // SA_NODEFER is required to handle SIGABRT from + // ImmediateAbortSignalHandler(). + act.sa_flags |= SA_NODEFER; + if (fsh_options.use_alternate_stack) { + act.sa_flags |= MaybeSetupAlternateStack(); + } + act.sa_sigaction = handler; + ABSL_RAW_CHECK(sigaction(data->signo, &act, &data->previous_action) == 0, + "sigaction() failed"); +} + +#else + +static void InstallOneFailureHandler(FailureSignalData* data, + void (*handler)(int)) { + data->previous_handler = signal(data->signo, handler); + ABSL_RAW_CHECK(data->previous_handler != SIG_ERR, "signal() failed"); +} + +#endif + +static void WriteToStderr(const char* data) { + absl::raw_logging_internal::AsyncSignalSafeWriteToStderr(data, strlen(data)); +} + +static void WriteSignalMessage(int signo, int cpu, + void (*writerfn)(const char*)) { + char buf[96]; + char on_cpu[32] = {0}; + if (cpu != -1) { + snprintf(on_cpu, sizeof(on_cpu), " on cpu %d", cpu); + } + const char* const signal_string = + debugging_internal::FailureSignalToString(signo); + if (signal_string != nullptr && signal_string[0] != '\0') { + snprintf(buf, sizeof(buf), "*** %s received at time=%ld%s ***\n", + signal_string, + static_cast(time(nullptr)), // NOLINT(runtime/int) + on_cpu); + } else { + snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld%s ***\n", + signo, static_cast(time(nullptr)), // NOLINT(runtime/int) + on_cpu); + } + writerfn(buf); +} + +// `void*` might not be big enough to store `void(*)(const char*)`. +struct WriterFnStruct { + void (*writerfn)(const char*); +}; + +// Many of the absl::debugging_internal::Dump* functions in +// examine_stack.h take a writer function pointer that has a void* arg +// for historical reasons. failure_signal_handler_writer only takes a +// data pointer. This function converts between these types. +static void WriterFnWrapper(const char* data, void* arg) { + static_cast(arg)->writerfn(data); +} + +// Convenient wrapper around DumpPCAndFrameSizesAndStackTrace() for signal +// handlers. "noinline" so that GetStackFrames() skips the top-most stack +// frame for this function. +ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace( + void* ucontext, bool symbolize_stacktrace, + void (*writerfn)(const char*, void*), void* writerfn_arg) { + constexpr int kNumStackFrames = 32; + void* stack[kNumStackFrames]; + int frame_sizes[kNumStackFrames]; + int min_dropped_frames; + int depth = absl::GetStackFramesWithContext( + stack, frame_sizes, kNumStackFrames, + 1, // Do not include this function in stack trace. + ucontext, &min_dropped_frames); + absl::debugging_internal::DumpPCAndFrameSizesAndStackTrace( + absl::debugging_internal::GetProgramCounter(ucontext), stack, frame_sizes, + depth, min_dropped_frames, symbolize_stacktrace, writerfn, writerfn_arg); +} + +// 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, int cpu, + void (*writerfn)(const char*)) { + WriterFnStruct writerfn_struct{writerfn}; + WriteSignalMessage(signo, cpu, writerfn); + WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper, + &writerfn_struct); +} + +// absl::SleepFor() can't be used here since AbslInternalSleepFor() +// may be overridden to do something that isn't async-signal-safe on +// some platforms. +static void PortableSleepForSeconds(int seconds) { +#ifdef _WIN32 + Sleep(seconds * 1000); +#else + struct timespec sleep_time; + sleep_time.tv_sec = seconds; + sleep_time.tv_nsec = 0; + while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {} +#endif +} + +#ifdef ABSL_HAVE_ALARM +// 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 AbslFailureSignalHandler() hangs for more than +// the alarm timeout, ImmediateAbortSignalHandler() will abort the +// program. +static void ImmediateAbortSignalHandler(int) { + RaiseToDefaultHandler(SIGABRT); +} +#endif + +// absl::base_internal::GetTID() returns pid_t on most platforms, but +// returns absl::base_internal::pid_t on Windows. +using GetTidType = decltype(absl::base_internal::GetTID()); +ABSL_CONST_INIT static std::atomic failed_tid(0); + +#ifndef ABSL_HAVE_SIGACTION +static void AbslFailureSignalHandler(int signo) { + void* ucontext = nullptr; +#else +static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) { +#endif + + const GetTidType this_tid = absl::base_internal::GetTID(); + GetTidType previous_failed_tid = 0; + if (!failed_tid.compare_exchange_strong( + previous_failed_tid, static_cast(this_tid), + std::memory_order_acq_rel, std::memory_order_relaxed)) { + ABSL_RAW_LOG( + ERROR, + "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 AbslFailureSignalHandler(), so wait + // a bit for it to finish. If the other thread doesn't kill us, + // we do so after sleeping. + PortableSleepForSeconds(3); + RaiseToDefaultHandler(signo); + // The recursively raised signal may be blocked until we return. + return; + } + } + + // Increase the chance that the CPU we report was the same CPU on which the + // signal was received by doing this as early as possible, i.e. after + // verifying that this is not a recursive signal handler invocation. + int my_cpu = -1; +#ifdef ABSL_HAVE_SCHED_GETCPU + my_cpu = sched_getcpu(); +#endif + +#ifdef ABSL_HAVE_ALARM + // Set an alarm to abort the program in case this code hangs or deadlocks. + if (fsh_options.alarm_on_failure_secs > 0) { + alarm(0); // Cancel any existing alarms. + signal(SIGALRM, ImmediateAbortSignalHandler); + alarm(fsh_options.alarm_on_failure_secs); + } +#endif + + // First write to stderr. + WriteFailureInfo(signo, ucontext, my_cpu, WriteToStderr); + + // Riskier code (because it is less likely to be async-signal-safe) + // goes after this point. + if (fsh_options.writerfn != nullptr) { + WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn); + fsh_options.writerfn(nullptr); + } + + if (fsh_options.call_previous_handler) { + RaiseToPreviousHandler(signo); + } else { + RaiseToDefaultHandler(signo); + } +} + +void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) { + fsh_options = options; + for (auto& it : failure_signal_data) { + InstallOneFailureHandler(&it, AbslFailureSignalHandler); + } +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/debugging/failure_signal_handler.h b/src/absl/debugging/failure_signal_handler.h new file mode 100644 index 0000000..500115c --- /dev/null +++ b/src/absl/debugging/failure_signal_handler.h @@ -0,0 +1,121 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: failure_signal_handler.h +// ----------------------------------------------------------------------------- +// +// This file configures the Abseil *failure signal handler* to capture and dump +// useful debugging information (such as a stacktrace) upon program failure. +// +// To use the failure signal handler, call `absl::InstallFailureSignalHandler()` +// very early in your program, usually in the first few lines of main(): +// +// int main(int argc, char** argv) { +// // Initialize the symbolizer to get a human-readable stack trace +// absl::InitializeSymbolizer(argv[0]); +// +// absl::FailureSignalHandlerOptions options; +// absl::InstallFailureSignalHandler(options); +// DoSomethingInteresting(); +// return 0; +// } +// +// Any program that raises a fatal signal (such as `SIGSEGV`, `SIGILL`, +// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP`) will call the +// installed failure signal handler and provide debugging information to stderr. +// +// Note that you should *not* install the Abseil failure signal handler more +// than once. You may, of course, have another (non-Abseil) failure signal +// handler installed (which would be triggered if Abseil's failure signal +// handler sets `call_previous_handler` to `true`). + +#ifndef ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ +#define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// FailureSignalHandlerOptions +// +// Struct for holding `absl::InstallFailureSignalHandler()` configuration +// options. +struct FailureSignalHandlerOptions { + // If true, try to symbolize the stacktrace emitted on failure, provided that + // you have initialized a symbolizer for that purpose. (See symbolize.h for + // more information.) + bool symbolize_stacktrace = true; + + // If true, try to run signal handlers on an alternate stack (if supported on + // the given platform). An alternate stack is useful for program crashes due + // to a stack overflow; by running on a alternate stack, the signal handler + // may run even when normal stack space has been exausted. The downside of + // using an alternate stack is that extra memory for the alternate stack needs + // to be pre-allocated. + bool use_alternate_stack = true; + + // If positive, indicates the number of seconds after which the failure signal + // handler is invoked to abort the program. Setting such an alarm is useful in + // cases where the failure signal handler itself may become hung or + // deadlocked. + int alarm_on_failure_secs = 3; + + // If true, call the previously registered signal handler for the signal that + // was received (if one was registered) after the existing signal handler + // runs. This mechanism can be used to chain signal handlers together. + // + // If false, the signal is raised to the default handler for that signal + // (which normally terminates the program). + // + // IMPORTANT: If true, the chained fatal signal handlers must not try to + // recover from the fatal signal. Instead, they should terminate the program + // via some mechanism, like raising the default handler for the signal, or by + // calling `_exit()`. Note that the failure signal handler may put parts of + // the Abseil library into a state from which they cannot recover. + bool call_previous_handler = false; + + // If non-null, indicates a pointer to a callback function that will be called + // upon failure, with a string argument containing failure data. This function + // may be used as a hook to write failure data to a secondary location, such + // as a log file. This function will also be called with null data, as a hint + // to flush any buffered data before the program may be terminated. Consider + // flushing any buffered data in all calls to this function. + // + // Since this function runs within a signal handler, it should be + // async-signal-safe if possible. + // See http://man7.org/linux/man-pages/man7/signal-safety.7.html + void (*writerfn)(const char*) = nullptr; +}; + +// InstallFailureSignalHandler() +// +// Installs a signal handler for the common failure signals `SIGSEGV`, `SIGILL`, +// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP` (provided they exist +// on the given platform). The failure signal handler dumps program failure data +// useful for debugging in an unspecified format to stderr. This data may +// include the program counter, a stacktrace, and register information on some +// systems; do not rely on an exact format for the output, as it is subject to +// change. +void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options); + +namespace debugging_internal { +const char* FailureSignalToString(int signo); +} // namespace debugging_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ diff --git a/src/absl/debugging/internal/address_is_readable.cc b/src/absl/debugging/internal/address_is_readable.cc new file mode 100644 index 0000000..4be6256 --- /dev/null +++ b/src/absl/debugging/internal/address_is_readable.cc @@ -0,0 +1,96 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// base::AddressIsReadable() probes an address to see whether it is readable, +// without faulting. + +#include "absl/debugging/internal/address_is_readable.h" + +#if !defined(__linux__) || defined(__ANDROID__) + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// On platforms other than Linux, just return true. +bool AddressIsReadable(const void* /* addr */) { return true; } + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#else // __linux__ && !__ANDROID__ + +#include +#include +#include + +#include "absl/base/internal/errno_saver.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// NOTE: be extra careful about adding any interposable function calls here +// (such as open(), read(), etc.). These symbols may be interposed and will get +// invoked in contexts they don't expect. +// +// NOTE: any new system calls here may also require sandbox reconfiguration. +// +bool AddressIsReadable(const void *addr) { + // Align address on 8-byte boundary. On aarch64, checking last + // byte before inaccessible page returned unexpected EFAULT. + const uintptr_t u_addr = reinterpret_cast(addr) & ~7; + addr = reinterpret_cast(u_addr); + + // rt_sigprocmask below will succeed for this input. + if (addr == nullptr) return false; + + absl::base_internal::ErrnoSaver errno_saver; + + // Here we probe with some syscall which + // - accepts an 8-byte region of user memory as input + // - tests for EFAULT before other validation + // - has no problematic side-effects + // + // rt_sigprocmask(2) works for this. It copies sizeof(kernel_sigset_t)==8 + // bytes from the address into the kernel memory before any validation. + // + // The call can never succeed, since the `how` parameter is not one of + // SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK. + // + // This strategy depends on Linux implementation details, + // so we rely on the test to alert us if it stops working. + // + // Some discarded past approaches: + // - msync() doesn't reject PROT_NONE regions + // - write() on /dev/null doesn't return EFAULT + // - write() on a pipe requires creating it and draining the writes + // - connect() works but is problematic for sandboxes and needs a valid + // file descriptor + // + // This can never succeed (invalid first argument to sigprocmask). + ABSL_RAW_CHECK(syscall(SYS_rt_sigprocmask, ~0, addr, nullptr, + /*sizeof(kernel_sigset_t)*/ 8) == -1, + "unexpected success"); + ABSL_RAW_CHECK(errno == EFAULT || errno == EINVAL, "unexpected errno"); + return errno != EFAULT; +} + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // __linux__ && !__ANDROID__ diff --git a/src/absl/debugging/internal/address_is_readable.h b/src/absl/debugging/internal/address_is_readable.h new file mode 100644 index 0000000..4bbaf4d --- /dev/null +++ b/src/absl/debugging/internal/address_is_readable.h @@ -0,0 +1,32 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ +#define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Return whether the byte at *addr is readable, without faulting. +// Save and restores errno. +bool AddressIsReadable(const void *addr); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ diff --git a/src/absl/debugging/internal/demangle.cc b/src/absl/debugging/internal/demangle.cc new file mode 100644 index 0000000..93ae327 --- /dev/null +++ b/src/absl/debugging/internal/demangle.cc @@ -0,0 +1,1959 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// For reference check out: +// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling +// +// Note that we only have partial C++11 support yet. + +#include "absl/debugging/internal/demangle.h" + +#include +#include +#include + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +typedef struct { + const char *abbrev; + const char *real_name; + // Number of arguments in context, or 0 if disallowed. + int arity; +} AbbrevPair; + +// List of operators from Itanium C++ ABI. +static const AbbrevPair kOperatorList[] = { + // New has special syntax (not currently supported). + {"nw", "new", 0}, + {"na", "new[]", 0}, + + // Works except that the 'gs' prefix is not supported. + {"dl", "delete", 1}, + {"da", "delete[]", 1}, + + {"ps", "+", 1}, // "positive" + {"ng", "-", 1}, // "negative" + {"ad", "&", 1}, // "address-of" + {"de", "*", 1}, // "dereference" + {"co", "~", 1}, + + {"pl", "+", 2}, + {"mi", "-", 2}, + {"ml", "*", 2}, + {"dv", "/", 2}, + {"rm", "%", 2}, + {"an", "&", 2}, + {"or", "|", 2}, + {"eo", "^", 2}, + {"aS", "=", 2}, + {"pL", "+=", 2}, + {"mI", "-=", 2}, + {"mL", "*=", 2}, + {"dV", "/=", 2}, + {"rM", "%=", 2}, + {"aN", "&=", 2}, + {"oR", "|=", 2}, + {"eO", "^=", 2}, + {"ls", "<<", 2}, + {"rs", ">>", 2}, + {"lS", "<<=", 2}, + {"rS", ">>=", 2}, + {"eq", "==", 2}, + {"ne", "!=", 2}, + {"lt", "<", 2}, + {"gt", ">", 2}, + {"le", "<=", 2}, + {"ge", ">=", 2}, + {"nt", "!", 1}, + {"aa", "&&", 2}, + {"oo", "||", 2}, + {"pp", "++", 1}, + {"mm", "--", 1}, + {"cm", ",", 2}, + {"pm", "->*", 2}, + {"pt", "->", 0}, // Special syntax + {"cl", "()", 0}, // Special syntax + {"ix", "[]", 2}, + {"qu", "?", 3}, + {"st", "sizeof", 0}, // Special syntax + {"sz", "sizeof", 1}, // Not a real operator name, but used in expressions. + {nullptr, nullptr, 0}, +}; + +// List of builtin types from Itanium C++ ABI. +// +// Invariant: only one- or two-character type abbreviations here. +static const AbbrevPair kBuiltinTypeList[] = { + {"v", "void", 0}, + {"w", "wchar_t", 0}, + {"b", "bool", 0}, + {"c", "char", 0}, + {"a", "signed char", 0}, + {"h", "unsigned char", 0}, + {"s", "short", 0}, + {"t", "unsigned short", 0}, + {"i", "int", 0}, + {"j", "unsigned int", 0}, + {"l", "long", 0}, + {"m", "unsigned long", 0}, + {"x", "long long", 0}, + {"y", "unsigned long long", 0}, + {"n", "__int128", 0}, + {"o", "unsigned __int128", 0}, + {"f", "float", 0}, + {"d", "double", 0}, + {"e", "long double", 0}, + {"g", "__float128", 0}, + {"z", "ellipsis", 0}, + + {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) + {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) + {"Dc", "decltype(auto)", 0}, + {"Da", "auto", 0}, + {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) + {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) + {"Di", "char32_t", 0}, + {"Du", "char8_t", 0}, + {"Ds", "char16_t", 0}, + {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) + {nullptr, nullptr, 0}, +}; + +// List of substitutions Itanium C++ ABI. +static const AbbrevPair kSubstitutionList[] = { + {"St", "", 0}, + {"Sa", "allocator", 0}, + {"Sb", "basic_string", 0}, + // std::basic_string,std::allocator > + {"Ss", "string", 0}, + // std::basic_istream > + {"Si", "istream", 0}, + // std::basic_ostream > + {"So", "ostream", 0}, + // std::basic_iostream > + {"Sd", "iostream", 0}, + {nullptr, nullptr, 0}, +}; + +// State needed for demangling. This struct is copied in almost every stack +// frame, so every byte counts. +typedef struct { + int mangled_idx; // Cursor of mangled name. + int out_cur_idx; // Cursor of output string. + int prev_name_idx; // For constructors/destructors. + signed int prev_name_length : 16; // For constructors/destructors. + signed int nest_level : 15; // For nested names. + unsigned int append : 1; // Append flag. + // Note: for some reason MSVC can't pack "bool append : 1" into the same int + // with the above two fields, so we use an int instead. Amusingly it can pack + // "signed bool" as expected, but relying on that to continue to be a legal + // type seems ill-advised (as it's illegal in at least clang). +} ParseState; + +static_assert(sizeof(ParseState) == 4 * sizeof(int), + "unexpected size of ParseState"); + +// One-off state for demangling that's not subject to backtracking -- either +// constant data, data that's intentionally immune to backtracking (steps), or +// data that would never be changed by backtracking anyway (recursion_depth). +// +// Only one copy of this exists for each call to Demangle, so the size of this +// struct is nearly inconsequential. +typedef struct { + const char *mangled_begin; // Beginning of input string. + char *out; // Beginning of output string. + int out_end_idx; // One past last allowed output character. + int recursion_depth; // For stack exhaustion prevention. + int steps; // Cap how much work we'll do, regardless of depth. + ParseState parse_state; // Backtrackable state copied for most frames. +} State; + +namespace { +// Prevent deep recursion / stack exhaustion. +// Also prevent unbounded handling of complex inputs. +class ComplexityGuard { + public: + explicit ComplexityGuard(State *state) : state_(state) { + ++state->recursion_depth; + ++state->steps; + } + ~ComplexityGuard() { --state_->recursion_depth; } + + // 256 levels of recursion seems like a reasonable upper limit on depth. + // 128 is not enough to demagle synthetic tests from demangle_unittest.txt: + // "_ZaaZZZZ..." and "_ZaaZcvZcvZ..." + static constexpr int kRecursionDepthLimit = 256; + + // We're trying to pick a charitable upper-limit on how many parse steps are + // necessary to handle something that a human could actually make use of. + // This is mostly in place as a bound on how much work we'll do if we are + // asked to demangle an mangled name from an untrusted source, so it should be + // much larger than the largest expected symbol, but much smaller than the + // amount of work we can do in, e.g., a second. + // + // Some real-world symbols from an arbitrary binary started failing between + // 2^12 and 2^13, so we multiply the latter by an extra factor of 16 to set + // the limit. + // + // Spending one second on 2^17 parse steps would require each step to take + // 7.6us, or ~30000 clock cycles, so it's safe to say this can be done in + // under a second. + static constexpr int kParseStepsLimit = 1 << 17; + + bool IsTooComplex() const { + return state_->recursion_depth > kRecursionDepthLimit || + state_->steps > kParseStepsLimit; + } + + private: + State *state_; +}; +} // namespace + +// We don't use strlen() in libc since it's not guaranteed to be async +// signal safe. +static size_t StrLen(const char *str) { + size_t len = 0; + while (*str != '\0') { + ++str; + ++len; + } + return len; +} + +// Returns true if "str" has at least "n" characters remaining. +static bool AtLeastNumCharsRemaining(const char *str, int n) { + for (int i = 0; i < n; ++i) { + if (str[i] == '\0') { + return false; + } + } + return true; +} + +// Returns true if "str" has "prefix" as a prefix. +static bool StrPrefix(const char *str, const char *prefix) { + size_t i = 0; + while (str[i] != '\0' && prefix[i] != '\0' && str[i] == prefix[i]) { + ++i; + } + return prefix[i] == '\0'; // Consumed everything in "prefix". +} + +static void InitState(State *state, const char *mangled, char *out, + int out_size) { + state->mangled_begin = mangled; + state->out = out; + state->out_end_idx = out_size; + state->recursion_depth = 0; + state->steps = 0; + + state->parse_state.mangled_idx = 0; + state->parse_state.out_cur_idx = 0; + state->parse_state.prev_name_idx = 0; + state->parse_state.prev_name_length = -1; + state->parse_state.nest_level = -1; + state->parse_state.append = true; +} + +static inline const char *RemainingInput(State *state) { + return &state->mangled_begin[state->parse_state.mangled_idx]; +} + +// Returns true and advances "mangled_idx" if we find "one_char_token" +// at "mangled_idx" position. It is assumed that "one_char_token" does +// not contain '\0'. +static bool ParseOneCharToken(State *state, const char one_char_token) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == one_char_token) { + ++state->parse_state.mangled_idx; + return true; + } + return false; +} + +// Returns true and advances "mangled_cur" if we find "two_char_token" +// at "mangled_cur" position. It is assumed that "two_char_token" does +// not contain '\0'. +static bool ParseTwoCharToken(State *state, const char *two_char_token) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == two_char_token[0] && + RemainingInput(state)[1] == two_char_token[1]) { + state->parse_state.mangled_idx += 2; + return true; + } + return false; +} + +// Returns true and advances "mangled_cur" if we find any character in +// "char_class" at "mangled_cur" position. +static bool ParseCharClass(State *state, const char *char_class) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == '\0') { + return false; + } + const char *p = char_class; + for (; *p != '\0'; ++p) { + if (RemainingInput(state)[0] == *p) { + ++state->parse_state.mangled_idx; + return true; + } + } + return false; +} + +static bool ParseDigit(State *state, int *digit) { + char c = RemainingInput(state)[0]; + if (ParseCharClass(state, "0123456789")) { + if (digit != nullptr) { + *digit = c - '0'; + } + return true; + } + return false; +} + +// This function is used for handling an optional non-terminal. +static bool Optional(bool /*status*/) { return true; } + +// This function is used for handling + syntax. +typedef bool (*ParseFunc)(State *); +static bool OneOrMore(ParseFunc parse_func, State *state) { + if (parse_func(state)) { + while (parse_func(state)) { + } + return true; + } + return false; +} + +// This function is used for handling * syntax. The function +// always returns true and must be followed by a termination token or a +// terminating sequence not handled by parse_func (e.g. +// ParseOneCharToken(state, 'E')). +static bool ZeroOrMore(ParseFunc parse_func, State *state) { + while (parse_func(state)) { + } + return true; +} + +// Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is +// 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) { + if (state->parse_state.out_cur_idx + 1 < + state->out_end_idx) { // +1 for '\0' + state->out[state->parse_state.out_cur_idx++] = str[i]; + } else { + // signal overflow + state->parse_state.out_cur_idx = state->out_end_idx + 1; + break; + } + } + if (state->parse_state.out_cur_idx < state->out_end_idx) { + state->out[state->parse_state.out_cur_idx] = + '\0'; // Terminate it with '\0' + } +} + +// We don't use equivalents in libc to avoid locale issues. +static bool IsLower(char c) { return c >= 'a' && c <= 'z'; } + +static bool IsAlpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static bool IsDigit(char c) { return c >= '0' && c <= '9'; } + +// Returns true if "str" is a function clone suffix. These suffixes are used +// by GCC 4.5.x and later versions (and our locally-modified version of GCC +// 4.4.x) to indicate functions which have been cloned during optimization. +// We treat any sequence (.+.+)+ as a function clone suffix. +// Additionally, '_' is allowed along with the alphanumeric sequence. +static bool IsFunctionCloneSuffix(const char *str) { + size_t i = 0; + while (str[i] != '\0') { + bool parsed = false; + // Consume a single [. | _]*[.]* sequence. + if (str[i] == '.' && (IsAlpha(str[i + 1]) || str[i + 1] == '_')) { + parsed = true; + i += 2; + while (IsAlpha(str[i]) || str[i] == '_') { + ++i; + } + } + if (str[i] == '.' && IsDigit(str[i + 1])) { + parsed = true; + i += 2; + while (IsDigit(str[i])) { + ++i; + } + } + if (!parsed) + return false; + } + return true; // Consumed everything in "str". +} + +static bool EndsWith(State *state, const char chr) { + return state->parse_state.out_cur_idx > 0 && + state->parse_state.out_cur_idx < state->out_end_idx && + chr == state->out[state->parse_state.out_cur_idx - 1]; +} + +// Append "str" with some tweaks, iff "append" state is true. +static void MaybeAppendWithLength(State *state, const char *const str, + const int length) { + if (state->parse_state.append && length > 0) { + // Append a space if the output buffer ends with '<' and "str" + // starts with '<' to avoid <<<. + if (str[0] == '<' && EndsWith(state, '<')) { + Append(state, " ", 1); + } + // Remember the last identifier name for ctors/dtors, + // but only if we haven't yet overflown the buffer. + if (state->parse_state.out_cur_idx < state->out_end_idx && + (IsAlpha(str[0]) || str[0] == '_')) { + state->parse_state.prev_name_idx = state->parse_state.out_cur_idx; + state->parse_state.prev_name_length = length; + } + Append(state, str, length); + } +} + +// Appends a positive decimal number to the output if appending is enabled. +static bool MaybeAppendDecimal(State *state, unsigned int val) { + // Max {32-64}-bit unsigned int is 20 digits. + constexpr size_t kMaxLength = 20; + char buf[kMaxLength]; + + // We can't use itoa or sprintf as neither is specified to be + // async-signal-safe. + if (state->parse_state.append) { + // We can't have a one-before-the-beginning pointer, so instead start with + // one-past-the-end and manipulate one character before the pointer. + char *p = &buf[kMaxLength]; + do { // val=0 is the only input that should write a leading zero digit. + *--p = (val % 10) + '0'; + val /= 10; + } while (p > buf && val != 0); + + // 'p' landed on the last character we set. How convenient. + Append(state, p, kMaxLength - (p - buf)); + } + + return true; +} + +// A convenient wrapper around MaybeAppendWithLength(). +// Returns true so that it can be placed in "if" conditions. +static bool MaybeAppend(State *state, const char *const str) { + if (state->parse_state.append) { + int length = StrLen(str); + MaybeAppendWithLength(state, str, length); + } + return true; +} + +// This function is used for handling nested names. +static bool EnterNestedName(State *state) { + state->parse_state.nest_level = 0; + return true; +} + +// This function is used for handling nested names. +static bool LeaveNestedName(State *state, int16_t prev_value) { + state->parse_state.nest_level = prev_value; + return true; +} + +// Disable the append mode not to print function parameters, etc. +static bool DisableAppend(State *state) { + state->parse_state.append = false; + return true; +} + +// Restore the append mode to the previous state. +static bool RestoreAppend(State *state, bool prev_value) { + state->parse_state.append = prev_value; + return true; +} + +// Increase the nest level for nested names. +static void MaybeIncreaseNestLevel(State *state) { + if (state->parse_state.nest_level > -1) { + ++state->parse_state.nest_level; + } +} + +// Appends :: for nested names if necessary. +static void MaybeAppendSeparator(State *state) { + if (state->parse_state.nest_level >= 1) { + MaybeAppend(state, "::"); + } +} + +// Cancel the last separator if necessary. +static void MaybeCancelLastSeparator(State *state) { + if (state->parse_state.nest_level >= 1 && state->parse_state.append && + state->parse_state.out_cur_idx >= 2) { + state->parse_state.out_cur_idx -= 2; + state->out[state->parse_state.out_cur_idx] = '\0'; + } +} + +// Returns true if the identifier of the given length pointed to by +// "mangled_cur" is anonymous namespace. +static bool IdentifierIsAnonymousNamespace(State *state, int length) { + // Returns true if "anon_prefix" is a proper prefix of "mangled_cur". + static const char anon_prefix[] = "_GLOBAL__N_"; + return (length > static_cast(sizeof(anon_prefix) - 1) && + StrPrefix(RemainingInput(state), anon_prefix)); +} + +// Forward declarations of our parsing functions. +static bool ParseMangledName(State *state); +static bool ParseEncoding(State *state); +static bool ParseName(State *state); +static bool ParseUnscopedName(State *state); +static bool ParseNestedName(State *state); +static bool ParsePrefix(State *state); +static bool ParseUnqualifiedName(State *state); +static bool ParseSourceName(State *state); +static bool ParseLocalSourceName(State *state); +static bool ParseUnnamedTypeName(State *state); +static bool ParseNumber(State *state, int *number_out); +static bool ParseFloatNumber(State *state); +static bool ParseSeqId(State *state); +static bool ParseIdentifier(State *state, int length); +static bool ParseOperatorName(State *state, int *arity); +static bool ParseSpecialName(State *state); +static bool ParseCallOffset(State *state); +static bool ParseNVOffset(State *state); +static bool ParseVOffset(State *state); +static bool ParseCtorDtorName(State *state); +static bool ParseDecltype(State *state); +static bool ParseType(State *state); +static bool ParseCVQualifiers(State *state); +static bool ParseBuiltinType(State *state); +static bool ParseFunctionType(State *state); +static bool ParseBareFunctionType(State *state); +static bool ParseClassEnumType(State *state); +static bool ParseArrayType(State *state); +static bool ParsePointerToMemberType(State *state); +static bool ParseTemplateParam(State *state); +static bool ParseTemplateTemplateParam(State *state); +static bool ParseTemplateArgs(State *state); +static bool ParseTemplateArg(State *state); +static bool ParseBaseUnresolvedName(State *state); +static bool ParseUnresolvedName(State *state); +static bool ParseExpression(State *state); +static bool ParseExprPrimary(State *state); +static bool ParseExprCastValue(State *state); +static bool ParseLocalName(State *state); +static bool ParseLocalNameSuffix(State *state); +static bool ParseDiscriminator(State *state); +static bool ParseSubstitution(State *state, bool accept_std); + +// Implementation note: the following code is a straightforward +// translation of the Itanium C++ ABI defined in BNF with a couple of +// exceptions. +// +// - Support GNU extensions not defined in the Itanium C++ ABI +// - and are combined to avoid infinite loop +// - Reorder patterns to shorten the code +// - Reorder patterns to give greedier functions precedence +// We'll mark "Less greedy than" for these cases in the code +// +// Each parsing function changes the parse state and returns true on +// success, or returns false and doesn't change the parse state (note: +// the parse-steps counter increases regardless of success or failure). +// To ensure that the parse state isn't changed in the latter case, we +// save the original state before we call multiple parsing functions +// consecutively with &&, and restore it if unsuccessful. See +// ParseEncoding() as an example of this convention. We follow the +// convention throughout the code. +// +// Originally we tried to do demangling without following the full ABI +// syntax but it turned out we needed to follow the full syntax to +// parse complicated cases like nested template arguments. Note that +// implementing a full-fledged demangler isn't trivial (libiberty's +// cp-demangle.c has +4300 lines). +// +// Note that (foo) in <(foo) ...> is a modifier to be ignored. +// +// Reference: +// - Itanium C++ ABI +// + +// ::= _Z +static bool ParseMangledName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); +} + +// ::= <(function) name> +// ::= <(data) name> +// ::= +static bool ParseEncoding(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + // Implementing the first two productions together as + // [] avoids exponential blowup of backtracking. + // + // Since Optional(...) can't fail, there's no need to copy the state for + // backtracking. + if (ParseName(state) && Optional(ParseBareFunctionType(state))) { + return true; + } + + if (ParseSpecialName(state)) { + return true; + } + return false; +} + +// ::= +// ::= +// ::= +// ::= +static bool ParseName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseNestedName(state) || ParseLocalName(state)) { + return true; + } + + // We reorganize the productions to avoid re-parsing unscoped names. + // - Inline productions: + // ::= + // ::= + // ::= + // - Merge the two productions that start with unscoped-name: + // ::= [] + + ParseState copy = state->parse_state; + // "std<...>" isn't a valid name. + if (ParseSubstitution(state, /*accept_std=*/false) && + ParseTemplateArgs(state)) { + return true; + } + state->parse_state = copy; + + // Note there's no need to restore state after this since only the first + // subparser can fail. + return ParseUnscopedName(state) && Optional(ParseTemplateArgs(state)); +} + +// ::= +// ::= St +static bool ParseUnscopedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseUnqualifiedName(state)) { + return true; + } + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") && + ParseUnqualifiedName(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= R // lvalue method reference qualifier +// ::= O // rvalue method reference qualifier +static inline bool ParseRefQualifier(State *state) { + return ParseCharClass(state, "OR"); +} + +// ::= N [] [] +// E +// ::= N [] [] +// E +static bool ParseNestedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'N') && EnterNestedName(state) && + Optional(ParseCVQualifiers(state)) && + Optional(ParseRefQualifier(state)) && ParsePrefix(state) && + LeaveNestedName(state, copy.nest_level) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return false; +} + +// This part is tricky. If we literally translate them to code, we'll +// end up infinite loop. Hence we merge them to avoid the case. +// +// ::= +// ::= +// ::= +// ::= +// ::= # empty +// ::= <(template) unqualified-name> +// ::= +// ::= +static bool ParsePrefix(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + bool has_something = false; + while (true) { + MaybeAppendSeparator(state); + if (ParseTemplateParam(state) || + ParseSubstitution(state, /*accept_std=*/true) || + ParseUnscopedName(state) || + (ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) { + has_something = true; + MaybeIncreaseNestLevel(state); + continue; + } + MaybeCancelLastSeparator(state); + if (has_something && ParseTemplateArgs(state)) { + return ParsePrefix(state); + } else { + break; + } + } + return true; +} + +// ::= +// ::= +// ::= +// ::= // GCC extension; see below. +// ::= +static bool ParseUnqualifiedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || + ParseSourceName(state) || ParseLocalSourceName(state) || + ParseUnnamedTypeName(state)); +} + +// ::= +static bool ParseSourceName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + int length = -1; + if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= L [] +// +// References: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +// https://gcc.gnu.org/viewcvs?view=rev&revision=124467 +static bool ParseLocalSourceName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && + Optional(ParseDiscriminator(state))) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= Ut [<(nonnegative) number>] _ +// ::= +// ::= Ul E [<(nonnegative) number>] _ +// ::= <(parameter) type>+ +static bool ParseUnnamedTypeName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + // Type's 1-based index n is encoded as { "", n == 1; itoa(n-2), otherwise }. + // Optionally parse the encoded value into 'which' and add 2 to get the index. + int which = -1; + + // Unnamed type local to function or class. + if (ParseTwoCharToken(state, "Ut") && Optional(ParseNumber(state, &which)) && + which <= std::numeric_limits::max() - 2 && // Don't overflow. + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "{unnamed type#"); + MaybeAppendDecimal(state, 2 + which); + MaybeAppend(state, "}"); + return true; + } + state->parse_state = copy; + + // Closure type. + which = -1; + if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) && + OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) && + ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) && + which <= std::numeric_limits::max() - 2 && // Don't overflow. + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "{lambda()#"); + MaybeAppendDecimal(state, 2 + which); + MaybeAppend(state, "}"); + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= [n] +// If "number_out" is non-null, then *number_out is set to the value of the +// parsed number on success. +static bool ParseNumber(State *state, int *number_out) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + bool negative = false; + if (ParseOneCharToken(state, 'n')) { + negative = true; + } + const char *p = RemainingInput(state); + uint64_t number = 0; + for (; *p != '\0'; ++p) { + if (IsDigit(*p)) { + number = number * 10 + (*p - '0'); + } else { + break; + } + } + // Apply the sign with uint64_t arithmetic so overflows aren't UB. Gives + // "incorrect" results for out-of-range inputs, but negative values only + // appear for literals, which aren't printed. + if (negative) { + number = ~number + 1; + } + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); + if (number_out != nullptr) { + // Note: possibly truncate "number". + *number_out = number; + } + return true; + } + return false; +} + +// Floating-point literals are encoded using a fixed-length lowercase +// hexadecimal string. +static bool ParseFloatNumber(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + const char *p = RemainingInput(state); + for (; *p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { + break; + } + } + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); + return true; + } + return false; +} + +// The is a sequence number in base 36, +// using digits and upper case letters +static bool ParseSeqId(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + const char *p = RemainingInput(state); + for (; *p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { + break; + } + } + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); + return true; + } + return false; +} + +// ::= (of given length) +static bool ParseIdentifier(State *state, int length) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (length < 0 || !AtLeastNumCharsRemaining(RemainingInput(state), length)) { + return false; + } + if (IdentifierIsAnonymousNamespace(state, length)) { + MaybeAppend(state, "(anonymous namespace)"); + } else { + MaybeAppendWithLength(state, RemainingInput(state), length); + } + state->parse_state.mangled_idx += length; + return true; +} + +// ::= nw, and other two letters cases +// ::= cv # (cast) +// ::= v # vendor extended operator +static bool ParseOperatorName(State *state, int *arity) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (!AtLeastNumCharsRemaining(RemainingInput(state), 2)) { + return false; + } + // First check with "cv" (cast) case. + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") && + EnterNestedName(state) && ParseType(state) && + LeaveNestedName(state, copy.nest_level)) { + if (arity != nullptr) { + *arity = 1; + } + return true; + } + state->parse_state = copy; + + // Then vendor extended operators. + if (ParseOneCharToken(state, 'v') && ParseDigit(state, arity) && + ParseSourceName(state)) { + return true; + } + state->parse_state = copy; + + // Other operator names should start with a lower alphabet followed + // by a lower/upper alphabet. + if (!(IsLower(RemainingInput(state)[0]) && + IsAlpha(RemainingInput(state)[1]))) { + return false; + } + // We may want to perform a binary search if we really need speed. + const AbbrevPair *p; + for (p = kOperatorList; p->abbrev != nullptr; ++p) { + if (RemainingInput(state)[0] == p->abbrev[0] && + RemainingInput(state)[1] == p->abbrev[1]) { + if (arity != nullptr) { + *arity = p->arity; + } + MaybeAppend(state, "operator"); + if (IsLower(*p->real_name)) { // new, delete, etc. + MaybeAppend(state, " "); + } + MaybeAppend(state, p->real_name); + state->parse_state.mangled_idx += 2; + return true; + } + } + return false; +} + +// ::= TV +// ::= TT +// ::= TI +// ::= TS +// ::= TH # thread-local +// ::= Tc <(base) encoding> +// ::= GV <(object) name> +// ::= T <(base) encoding> +// G++ extensions: +// ::= TC <(offset) number> _ <(base) type> +// ::= TF +// ::= TJ +// ::= GR +// ::= GA +// ::= Th <(base) encoding> +// ::= Tv <(base) encoding> +// +// Note: we don't care much about them since they don't appear in +// stack traces. The are special data. +static bool ParseSpecialName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") && + ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "GV") && ParseName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && + ParseEncoding(state)) { + return true; + } + state->parse_state = copy; + + // G++ extensions + if (ParseTwoCharToken(state, "TC") && ParseType(state) && + ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + DisableAppend(state) && ParseType(state)) { + RestoreAppend(state, copy.append); + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && + ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "GR") && ParseName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= h _ +// ::= v _ +static bool ParseCallOffset(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'h') && ParseNVOffset(state) && + ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'v') && ParseVOffset(state) && + ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= <(offset) number> +static bool ParseNVOffset(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return ParseNumber(state, nullptr); +} + +// ::= <(offset) number> _ <(virtual offset) number> +static bool ParseVOffset(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + ParseNumber(state, nullptr)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= C1 | C2 | C3 | CI1 | CI2 +// +// ::= D0 | D1 | D2 +// # GCC extensions: "unified" constructor/destructor. See +// # +// https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847 +// ::= C4 | D4 +static bool ParseCtorDtorName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'C')) { + if (ParseCharClass(state, "1234")) { + const char *const prev_name = + state->out + state->parse_state.prev_name_idx; + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); + return true; + } else if (ParseOneCharToken(state, 'I') && ParseCharClass(state, "12") && + ParseClassEnumType(state)) { + return true; + } + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "0124")) { + const char *const prev_name = state->out + state->parse_state.prev_name_idx; + MaybeAppend(state, "~"); + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); + return true; + } + state->parse_state = copy; + return false; +} + +// ::= Dt E # decltype of an id-expression or class +// # member access (C++0x) +// ::= DT E # decltype of an expression (C++0x) +static bool ParseDecltype(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && + ParseExpression(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= +// ::= P # pointer-to +// ::= R # reference-to +// ::= O # rvalue reference-to (C++0x) +// ::= C # complex pair (C 2000) +// ::= G # imaginary (C 2000) +// ::= U # vendor extended type qualifier +// ::= +// ::= +// ::= # note: just an alias for +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= Dp # pack expansion of (C++0x) +// ::= Dv _ # GNU vector extension +// +static bool ParseType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + + // We should check CV-qualifers, and PRGC things first. + // + // CV-qualifiers overlap with some operator names, but an operator name is not + // valid as a type. To avoid an ambiguity that can lead to exponential time + // complexity, refuse to backtrack the CV-qualifiers. + // + // _Z4aoeuIrMvvE + // => _Z 4aoeuI rM v v E + // aoeu + // => _Z 4aoeuI r Mv v E + // aoeu + // + // By consuming the CV-qualifiers first, the former parse is disabled. + if (ParseCVQualifiers(state)) { + const bool result = ParseType(state); + if (!result) state->parse_state = copy; + return result; + } + state->parse_state = copy; + + // Similarly, these tag characters can overlap with other s resulting in + // two different parse prefixes that land on in the same + // place, such as "C3r1xI...". So, disable the "ctor-name = C3" parse by + // refusing to backtrack the tag characters. + if (ParseCharClass(state, "OPRCG")) { + const bool result = ParseType(state); + if (!result) state->parse_state = copy; + return result; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && + ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseBuiltinType(state) || ParseFunctionType(state) || + ParseClassEnumType(state) || ParseArrayType(state) || + ParsePointerToMemberType(state) || ParseDecltype(state) || + // "std" on its own isn't a type. + ParseSubstitution(state, /*accept_std=*/false)) { + return true; + } + + if (ParseTemplateTemplateParam(state) && ParseTemplateArgs(state)) { + return true; + } + state->parse_state = copy; + + // Less greedy than . + if (ParseTemplateParam(state)) { + return true; + } + + if (ParseTwoCharToken(state, "Dv") && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= [r] [V] [K] +// We don't allow empty to avoid infinite loop in +// ParseType(). +static bool ParseCVQualifiers(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + int num_cv_qualifiers = 0; + num_cv_qualifiers += ParseOneCharToken(state, 'r'); + num_cv_qualifiers += ParseOneCharToken(state, 'V'); + num_cv_qualifiers += ParseOneCharToken(state, 'K'); + return num_cv_qualifiers > 0; +} + +// ::= v, etc. # single-character builtin types +// ::= u +// ::= Dd, etc. # two-character builtin types +// +// Not supported: +// ::= DF _ # _FloatN (N bits) +// +static bool ParseBuiltinType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + const AbbrevPair *p; + for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { + // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. + if (p->abbrev[1] == '\0') { + if (ParseOneCharToken(state, p->abbrev[0])) { + MaybeAppend(state, p->real_name); + return true; + } + } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) { + MaybeAppend(state, p->real_name); + return true; + } + } + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= Do # non-throwing +// exception-specification (e.g., +// noexcept, throw()) +// ::= DO E # computed (instantiation-dependent) +// noexcept +// ::= Dw + E # dynamic exception specification +// with instantiation-dependent types +static bool ParseExceptionSpec(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (ParseTwoCharToken(state, "Do")) return true; + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "DO") && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + if (ParseTwoCharToken(state, "Dw") && OneOrMore(ParseType, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= [exception-spec] F [Y] [O] E +static bool ParseFunctionType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') && + Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) && + Optional(ParseOneCharToken(state, 'O')) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= <(signature) type>+ +static bool ParseBareFunctionType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + DisableAppend(state); + if (OneOrMore(ParseType, state)) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "()"); + return true; + } + state->parse_state = copy; + return false; +} + +// ::= +static bool ParseClassEnumType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return ParseName(state); +} + +// ::= A <(positive dimension) number> _ <(element) type> +// ::= A [<(dimension) expression>] _ <(element) type> +static bool ParseArrayType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'A') && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_') && ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && + ParseOneCharToken(state, '_') && ParseType(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= M <(class) type> <(member) type> +static bool ParsePointerToMemberType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= T_ +// ::= T _ +static bool ParseTemplateParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseTwoCharToken(state, "T_")) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + state->parse_state = copy; + return false; +} + +// ::= +// ::= +static bool ParseTemplateTemplateParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return (ParseTemplateParam(state) || + // "std" on its own isn't a template. + ParseSubstitution(state, /*accept_std=*/false)); +} + +// ::= I + E +static bool ParseTemplateArgs(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + DisableAppend(state); + if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "<>"); + return true; + } + state->parse_state = copy; + return false; +} + +// ::= +// ::= +// ::= J * E # argument pack +// ::= X E +static bool ParseTemplateArg(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'J') && ZeroOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + // There can be significant overlap between the following leading to + // exponential backtracking: + // + // ::= L E + // e.g. L 2xxIvE 1 E + // ==> + // e.g. L 2xx IvE + // + // This means parsing an entire twice, and can contain + // , so this can generate exponential backtracking. There is + // only overlap when the remaining input starts with "L ", so + // parse all cases that can start this way jointly to share the common prefix. + // + // We have: + // + // ::= + // ::= + // + // First, drop all the productions of that must start with something + // other than 'L'. All that's left is ; inline it. + // + // ::= # starts with 'N' + // ::= + // ::= + // ::= # starts with 'Z' + // + // Drop and inline again: + // + // ::= + // ::= + // ::= # starts with 'S' + // + // Merge the first two, inline , drop last: + // + // ::= [] + // ::= St [] # starts with 'S' + // + // Drop and inline: + // + // ::= [] # starts with lowercase + // ::= [] # starts with 'C' or 'D' + // ::= [] # starts with digit + // ::= [] + // ::= [] # starts with 'U' + // + // One more time: + // + // ::= L [] + // + // Likewise with : + // + // ::= L E + // ::= LZ E # cannot overlap; drop + // ::= L E # cannot overlap; drop + // + // By similar reasoning as shown above, the only s starting with + // are " []". Inline this. + // + // ::= L [] E + // + // Now inline both of these into : + // + // ::= L [] + // ::= L [] E + // + // Merge them and we're done: + // + // ::= L [] [ E] + if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) { + copy = state->parse_state; + if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return true; + } + + // Now that the overlapping cases can't reach this code, we can safely call + // both of these. + if (ParseType(state) || ParseExprPrimary(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'X') && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= [] +// ::= +// ::= +static inline bool ParseUnresolvedType(State *state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + return (ParseTemplateParam(state) && Optional(ParseTemplateArgs(state))) || + ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/false); +} + +// ::= [] +static inline bool ParseSimpleId(State *state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + + // Note: cannot be followed by a parameter pack; see comment in + // ParseUnresolvedType. + return ParseSourceName(state) && Optional(ParseTemplateArgs(state)); +} + +// ::= [] +// ::= on [] +// ::= dn +static bool ParseBaseUnresolvedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (ParseSimpleId(state)) { + return true; + } + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "on") && ParseOperatorName(state, nullptr) && + Optional(ParseTemplateArgs(state))) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "dn") && + (ParseUnresolvedType(state) || ParseSimpleId(state))) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= [gs] +// ::= sr +// ::= srN + E +// +// ::= [gs] sr + E +// +static bool ParseUnresolvedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + if (Optional(ParseTwoCharToken(state, "gs")) && + ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseUnresolvedType(state) && + ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') && + ParseUnresolvedType(state) && + OneOrMore(/* ::= */ ParseSimpleId, state) && + ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (Optional(ParseTwoCharToken(state, "gs")) && + ParseTwoCharToken(state, "sr") && + OneOrMore(/* ::= */ ParseSimpleId, state) && + ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= <1-ary operator-name> +// ::= <2-ary operator-name> +// ::= <3-ary operator-name> +// ::= cl + E +// ::= cp * E # Clang-specific. +// ::= cv # type (expression) +// ::= cv _ * E # type (expr-list) +// ::= st +// ::= +// ::= +// ::= +// ::= dt # expr.name +// ::= pt # expr->name +// ::= sp # argument pack expansion +// ::= sr +// ::= sr +// ::= fp <(top-level) CV-qualifiers> _ +// ::= fp <(top-level) CV-qualifiers> _ +// ::= fL p <(top-level) CV-qualifiers> _ +// ::= fL p <(top-level) CV-qualifiers> _ +static bool ParseExpression(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseTemplateParam(state) || ParseExprPrimary(state)) { + return true; + } + + ParseState copy = state->parse_state; + + // Object/function call expression. + if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + // Clang-specific "cp * E" + // https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338 + if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) && + ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + // Function-param expression (level 0). + if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + // Function-param expression (level 1+). + if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) && + ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + // Parse the conversion expressions jointly to avoid re-parsing the in + // their common prefix. Parsed as: + // ::= cv + // ::= _ * E + // ::= + // + // Also don't try ParseOperatorName after seeing "cv", since ParseOperatorName + // also needs to accept "cv " in other contexts. + if (ParseTwoCharToken(state, "cv")) { + if (ParseType(state)) { + ParseState copy2 = state->parse_state; + if (ParseOneCharToken(state, '_') && ZeroOrMore(ParseExpression, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy2; + if (ParseExpression(state)) { + return true; + } + } + } else { + // Parse unary, binary, and ternary operator expressions jointly, taking + // care not to re-parse subexpressions repeatedly. Parse like: + // ::= + // [] + // ::= [] + int arity = -1; + if (ParseOperatorName(state, &arity) && + arity > 0 && // 0 arity => disabled. + (arity < 3 || ParseExpression(state)) && + (arity < 2 || ParseExpression(state)) && + (arity < 1 || ParseExpression(state))) { + return true; + } + } + state->parse_state = copy; + + // sizeof type + if (ParseTwoCharToken(state, "st") && ParseType(state)) { + return true; + } + state->parse_state = copy; + + // Object and pointer member access expressions. + if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) && + ParseExpression(state) && ParseType(state)) { + return true; + } + 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; + } + state->parse_state = copy; + + return ParseUnresolvedName(state); +} + +// ::= L <(value) number> E +// ::= L <(value) float> E +// ::= L E +// // A bug in g++'s C++ ABI version 2 (-fabi-version=2). +// ::= LZ E +// +// Warning, subtle: the "bug" LZ production above is ambiguous with the first +// production where starts with , which can lead to +// exponential backtracking in two scenarios: +// +// - When whatever follows the E in the in the first production is +// not a name, we backtrack the whole and re-parse the whole thing. +// +// - When whatever follows the in the first production is not a +// number and this may be followed by a name, we backtrack the +// and re-parse it. +// +// Moreover this ambiguity isn't always resolved -- for example, the following +// has two different parses: +// +// _ZaaILZ4aoeuE1x1EvE +// => operator&& +// => operator&&<(aoeu::x)(1), void> +// +// To resolve this, we just do what GCC's demangler does, and refuse to parse +// casts to types. +static bool ParseExprPrimary(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + + // The "LZ" special case: if we see LZ, we commit to accept "LZ E" + // or fail, no backtracking. + if (ParseTwoCharToken(state, "LZ")) { + if (ParseEncoding(state) && ParseOneCharToken(state, 'E')) { + return true; + } + + state->parse_state = copy; + return false; + } + + // The merged cast production. + if (ParseOneCharToken(state, 'L') && ParseType(state) && + ParseExprCastValue(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// or , followed by 'E', as described above ParseExprPrimary. +static bool ParseExprCastValue(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + // We have to be able to backtrack after accepting a number because we could + // have e.g. "7fffE", which will accept "7" as a number but then fail to find + // the 'E'. + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + if (ParseFloatNumber(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// ::= Z <(function) encoding> E <(entity) name> [] +// ::= Z <(function) encoding> E s [] +// +// Parsing a common prefix of these two productions together avoids an +// exponential blowup of backtracking. Parse like: +// := Z E +// ::= s [] +// ::= [] + +static bool ParseLocalNameSuffix(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (MaybeAppend(state, "::") && ParseName(state) && + Optional(ParseDiscriminator(state))) { + return true; + } + + // Since we're not going to overwrite the above "::" by re-parsing the + // (whose trailing '\0' byte was in the byte now holding the + // first ':'), we have to rollback the "::" if the parse failed. + if (state->parse_state.append) { + state->out[state->parse_state.out_cur_idx - 2] = '\0'; + } + + return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state)); +} + +static bool ParseLocalName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseOneCharToken(state, 'E') && ParseLocalNameSuffix(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// := _ <(non-negative) number> +static bool ParseDiscriminator(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= S_ +// ::= S _ +// ::= St, etc. +// +// "St" is special in that it's not valid as a standalone name, and it *is* +// allowed to precede a name without being wrapped in "N...E". This means that +// if we accept it on its own, we can accept "St1a" and try to parse +// template-args, then fail and backtrack, accept "St" on its own, then "1a" as +// an unqualified name and re-parse the same template-args. To block this +// exponential backtracking, we disable it with 'accept_std=false' in +// problematic contexts. +static bool ParseSubstitution(State *state, bool accept_std) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseTwoCharToken(state, "S_")) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + state->parse_state = copy; + + // Expand abbreviations like "St" => "std". + if (ParseOneCharToken(state, 'S')) { + const AbbrevPair *p; + for (p = kSubstitutionList; p->abbrev != nullptr; ++p) { + if (RemainingInput(state)[0] == p->abbrev[1] && + (accept_std || p->abbrev[1] != 't')) { + MaybeAppend(state, "std"); + if (p->real_name[0] != '\0') { + MaybeAppend(state, "::"); + MaybeAppend(state, p->real_name); + } + ++state->parse_state.mangled_idx; + return true; + } + } + } + state->parse_state = copy; + return false; +} + +// Parse , optionally followed by either a function-clone suffix +// or version suffix. Returns true only if all of "mangled_cur" was consumed. +static bool ParseTopLevelMangledName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseMangledName(state)) { + if (RemainingInput(state)[0] != '\0') { + // Drop trailing function clone suffix, if any. + if (IsFunctionCloneSuffix(RemainingInput(state))) { + return true; + } + // Append trailing version suffix if any. + // ex. _Z3foo@@GLIBCXX_3.4 + if (RemainingInput(state)[0] == '@') { + MaybeAppend(state, RemainingInput(state)); + return true; + } + return false; // Unconsumed suffix. + } + return true; + } + return false; +} + +static bool Overflowed(const State *state) { + return state->parse_state.out_cur_idx >= state->out_end_idx; +} + +// The demangler entry point. +bool Demangle(const char *mangled, char *out, int out_size) { + State state; + InitState(&state, mangled, out, out_size); + return ParseTopLevelMangledName(&state) && !Overflowed(&state) && + state.parse_state.out_cur_idx > 0; +} + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/debugging/internal/demangle.h b/src/absl/debugging/internal/demangle.h new file mode 100644 index 0000000..c314d9b --- /dev/null +++ b/src/absl/debugging/internal/demangle.h @@ -0,0 +1,71 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// An async-signal-safe and thread-safe demangler for Itanium C++ ABI +// (aka G++ V3 ABI). +// +// The demangler is implemented to be used in async signal handlers to +// symbolize stack traces. We cannot use libstdc++'s +// abi::__cxa_demangle() in such signal handlers since it's not async +// signal safe (it uses malloc() internally). +// +// Note that this demangler doesn't support full demangling. More +// specifically, it doesn't print types of function parameters and +// types of template arguments. It just skips them. However, it's +// still very useful to extract basic information such as class, +// function, constructor, destructor, and operator names. +// +// See the implementation note in demangle.cc if you are interested. +// +// Example: +// +// | Mangled Name | The Demangler | abi::__cxa_demangle() +// |---------------|---------------|----------------------- +// | _Z1fv | f() | f() +// | _Z1fi | f() | f(int) +// | _Z3foo3bar | foo() | foo(bar) +// | _Z1fIiEvi | f<>() | void f(int) +// | _ZN1N1fE | N::f | N::f +// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar() +// | _Zrm1XS_" | operator%() | operator%(X, X) +// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo() +// | _Z1fSs | f() | f(std::basic_string, +// | | | std::allocator >) +// +// See the unit test for more examples. +// +// Note: we might want to write demanglers for ABIs other than Itanium +// C++ ABI in the future. +// + +#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ +#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Demangle `mangled`. On success, return true and write the +// demangled symbol name to `out`. Otherwise, return false. +// `out` is modified even if demangling is unsuccessful. +bool Demangle(const char *mangled, char *out, int out_size); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ diff --git a/src/absl/debugging/internal/elf_mem_image.cc b/src/absl/debugging/internal/elf_mem_image.cc new file mode 100644 index 0000000..a9d6671 --- /dev/null +++ b/src/absl/debugging/internal/elf_mem_image.cc @@ -0,0 +1,387 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Allow dynamic symbol lookup in an in-memory Elf image. +// + +#include "absl/debugging/internal/elf_mem_image.h" + +#ifdef ABSL_HAVE_ELF_MEM_IMAGE // defined in elf_mem_image.h + +#include +#include +#include +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" + +// From binutils/include/elf/common.h (this doesn't appear to be documented +// anywhere else). +// +// /* This flag appears in a Versym structure. It means that the symbol +// is hidden, and is only visible with an explicit version number. +// This is a GNU extension. */ +// #define VERSYM_HIDDEN 0x8000 +// +// /* This is the mask for the rest of the Versym information. */ +// #define VERSYM_VERSION 0x7fff + +#define VERSYM_VERSION 0x7fff + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +namespace { + +#if __SIZEOF_POINTER__ == 4 +const int kElfClass = ELFCLASS32; +int ElfBind(const ElfW(Sym) *symbol) { return ELF32_ST_BIND(symbol->st_info); } +int ElfType(const ElfW(Sym) *symbol) { return ELF32_ST_TYPE(symbol->st_info); } +#elif __SIZEOF_POINTER__ == 8 +const int kElfClass = ELFCLASS64; +int ElfBind(const ElfW(Sym) *symbol) { return ELF64_ST_BIND(symbol->st_info); } +int ElfType(const ElfW(Sym) *symbol) { return ELF64_ST_TYPE(symbol->st_info); } +#else +const int kElfClass = -1; +int ElfBind(const ElfW(Sym) *) { + ABSL_RAW_LOG(FATAL, "Unexpected word size"); + return 0; +} +int ElfType(const ElfW(Sym) *) { + ABSL_RAW_LOG(FATAL, "Unexpected word size"); + return 0; +} +#endif + +// Extract an element from one of the ELF tables, cast it to desired type. +// This is just a simple arithmetic and a glorified cast. +// Callers are responsible for bounds checking. +template +const T *GetTableElement(const ElfW(Ehdr) * ehdr, ElfW(Off) table_offset, + ElfW(Word) element_size, size_t index) { + return reinterpret_cast(reinterpret_cast(ehdr) + + table_offset + + index * element_size); +} + +} // namespace + +// The value of this variable doesn't matter; it's used only for its +// unique address. +const int ElfMemImage::kInvalidBaseSentinel = 0; + +ElfMemImage::ElfMemImage(const void *base) { + ABSL_RAW_CHECK(base != kInvalidBase, "bad pointer"); + Init(base); +} + +int ElfMemImage::GetNumSymbols() const { + if (!hash_) { + return 0; + } + // See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash + return hash_[1]; +} + +const ElfW(Sym) *ElfMemImage::GetDynsym(int index) const { + ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range"); + return dynsym_ + index; +} + +const ElfW(Versym) *ElfMemImage::GetVersym(int index) const { + ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range"); + return versym_ + index; +} + +const ElfW(Phdr) *ElfMemImage::GetPhdr(int index) const { + ABSL_RAW_CHECK(index < ehdr_->e_phnum, "index out of range"); + return GetTableElement(ehdr_, + ehdr_->e_phoff, + ehdr_->e_phentsize, + index); +} + +const char *ElfMemImage::GetDynstr(ElfW(Word) offset) const { + ABSL_RAW_CHECK(offset < strsize_, "offset out of range"); + return dynstr_ + offset; +} + +const void *ElfMemImage::GetSymAddr(const ElfW(Sym) *sym) const { + if (sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE) { + // Symbol corresponds to "special" (e.g. SHN_ABS) section. + return reinterpret_cast(sym->st_value); + } + ABSL_RAW_CHECK(link_base_ < sym->st_value, "symbol out of range"); + return GetTableElement(ehdr_, 0, 1, sym->st_value - link_base_); +} + +const ElfW(Verdef) *ElfMemImage::GetVerdef(int index) const { + ABSL_RAW_CHECK(0 <= index && static_cast(index) <= verdefnum_, + "index out of range"); + const ElfW(Verdef) *version_definition = verdef_; + while (version_definition->vd_ndx < index && version_definition->vd_next) { + const char *const version_definition_as_char = + reinterpret_cast(version_definition); + version_definition = + reinterpret_cast(version_definition_as_char + + version_definition->vd_next); + } + return version_definition->vd_ndx == index ? version_definition : nullptr; +} + +const ElfW(Verdaux) *ElfMemImage::GetVerdefAux( + const ElfW(Verdef) *verdef) const { + return reinterpret_cast(verdef+1); +} + +const char *ElfMemImage::GetVerstr(ElfW(Word) offset) const { + ABSL_RAW_CHECK(offset < strsize_, "offset out of range"); + return dynstr_ + offset; +} + +void ElfMemImage::Init(const void *base) { + ehdr_ = nullptr; + dynsym_ = nullptr; + dynstr_ = nullptr; + versym_ = nullptr; + verdef_ = nullptr; + hash_ = nullptr; + strsize_ = 0; + verdefnum_ = 0; + link_base_ = ~0L; // Sentinel: PT_LOAD .p_vaddr can't possibly be this. + if (!base) { + return; + } + const char *const base_as_char = reinterpret_cast(base); + if (base_as_char[EI_MAG0] != ELFMAG0 || base_as_char[EI_MAG1] != ELFMAG1 || + base_as_char[EI_MAG2] != ELFMAG2 || base_as_char[EI_MAG3] != ELFMAG3) { + assert(false); + return; + } + int elf_class = base_as_char[EI_CLASS]; + if (elf_class != kElfClass) { + assert(false); + return; + } + switch (base_as_char[EI_DATA]) { + case ELFDATA2LSB: { +#ifndef ABSL_IS_LITTLE_ENDIAN + assert(false); + return; +#endif + break; + } + case ELFDATA2MSB: { +#ifndef ABSL_IS_BIG_ENDIAN + assert(false); + return; +#endif + break; + } + default: { + assert(false); + return; + } + } + + ehdr_ = reinterpret_cast(base); + const ElfW(Phdr) *dynamic_program_header = nullptr; + for (int i = 0; i < ehdr_->e_phnum; ++i) { + const ElfW(Phdr) *const program_header = GetPhdr(i); + switch (program_header->p_type) { + case PT_LOAD: + if (!~link_base_) { + link_base_ = program_header->p_vaddr; + } + break; + case PT_DYNAMIC: + dynamic_program_header = program_header; + break; + } + } + if (!~link_base_ || !dynamic_program_header) { + assert(false); + // Mark this image as not present. Can not recur infinitely. + Init(nullptr); + return; + } + ptrdiff_t relocation = + base_as_char - reinterpret_cast(link_base_); + ElfW(Dyn) *dynamic_entry = + reinterpret_cast(dynamic_program_header->p_vaddr + + relocation); + for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) { + const auto value = dynamic_entry->d_un.d_val + relocation; + switch (dynamic_entry->d_tag) { + case DT_HASH: + hash_ = reinterpret_cast(value); + break; + case DT_SYMTAB: + dynsym_ = reinterpret_cast(value); + break; + case DT_STRTAB: + dynstr_ = reinterpret_cast(value); + break; + case DT_VERSYM: + versym_ = reinterpret_cast(value); + break; + case DT_VERDEF: + verdef_ = reinterpret_cast(value); + break; + case DT_VERDEFNUM: + verdefnum_ = dynamic_entry->d_un.d_val; + break; + case DT_STRSZ: + strsize_ = dynamic_entry->d_un.d_val; + break; + default: + // Unrecognized entries explicitly ignored. + break; + } + } + if (!hash_ || !dynsym_ || !dynstr_ || !versym_ || + !verdef_ || !verdefnum_ || !strsize_) { + assert(false); // invalid VDSO + // Mark this image as not present. Can not recur infinitely. + Init(nullptr); + return; + } +} + +bool ElfMemImage::LookupSymbol(const char *name, + const char *version, + int type, + SymbolInfo *info_out) const { + for (const SymbolInfo& info : *this) { + if (strcmp(info.name, name) == 0 && strcmp(info.version, version) == 0 && + ElfType(info.symbol) == type) { + if (info_out) { + *info_out = info; + } + return true; + } + } + return false; +} + +bool ElfMemImage::LookupSymbolByAddress(const void *address, + SymbolInfo *info_out) const { + for (const SymbolInfo& info : *this) { + const char *const symbol_start = + reinterpret_cast(info.address); + const char *const symbol_end = symbol_start + info.symbol->st_size; + if (symbol_start <= address && address < symbol_end) { + if (info_out) { + // Client wants to know details for that symbol (the usual case). + if (ElfBind(info.symbol) == STB_GLOBAL) { + // Strong symbol; just return it. + *info_out = info; + return true; + } else { + // Weak or local. Record it, but keep looking for a strong one. + *info_out = info; + } + } else { + // Client only cares if there is an overlapping symbol. + return true; + } + } + } + return false; +} + +ElfMemImage::SymbolIterator::SymbolIterator(const void *const image, int index) + : index_(index), image_(image) { +} + +const ElfMemImage::SymbolInfo *ElfMemImage::SymbolIterator::operator->() const { + return &info_; +} + +const ElfMemImage::SymbolInfo& ElfMemImage::SymbolIterator::operator*() const { + return info_; +} + +bool ElfMemImage::SymbolIterator::operator==(const SymbolIterator &rhs) const { + return this->image_ == rhs.image_ && this->index_ == rhs.index_; +} + +bool ElfMemImage::SymbolIterator::operator!=(const SymbolIterator &rhs) const { + return !(*this == rhs); +} + +ElfMemImage::SymbolIterator &ElfMemImage::SymbolIterator::operator++() { + this->Update(1); + return *this; +} + +ElfMemImage::SymbolIterator ElfMemImage::begin() const { + SymbolIterator it(this, 0); + it.Update(0); + return it; +} + +ElfMemImage::SymbolIterator ElfMemImage::end() const { + return SymbolIterator(this, GetNumSymbols()); +} + +void ElfMemImage::SymbolIterator::Update(int increment) { + const ElfMemImage *image = reinterpret_cast(image_); + ABSL_RAW_CHECK(image->IsPresent() || increment == 0, ""); + if (!image->IsPresent()) { + return; + } + index_ += increment; + if (index_ >= image->GetNumSymbols()) { + index_ = image->GetNumSymbols(); + return; + } + const ElfW(Sym) *symbol = image->GetDynsym(index_); + const ElfW(Versym) *version_symbol = image->GetVersym(index_); + ABSL_RAW_CHECK(symbol && version_symbol, ""); + const char *const symbol_name = image->GetDynstr(symbol->st_name); +#if defined(__NetBSD__) + const int version_index = version_symbol->vs_vers & VERSYM_VERSION; +#else + const ElfW(Versym) version_index = version_symbol[0] & VERSYM_VERSION; +#endif + const ElfW(Verdef) *version_definition = nullptr; + const char *version_name = ""; + if (symbol->st_shndx == SHN_UNDEF) { + // Undefined symbols reference DT_VERNEED, not DT_VERDEF, and + // version_index could well be greater than verdefnum_, so calling + // GetVerdef(version_index) may trigger assertion. + } else { + version_definition = image->GetVerdef(version_index); + } + if (version_definition) { + // I am expecting 1 or 2 auxiliary entries: 1 for the version itself, + // optional 2nd if the version has a parent. + ABSL_RAW_CHECK( + version_definition->vd_cnt == 1 || version_definition->vd_cnt == 2, + "wrong number of entries"); + const ElfW(Verdaux) *version_aux = image->GetVerdefAux(version_definition); + version_name = image->GetVerstr(version_aux->vda_name); + } + info_.name = symbol_name; + info_.version = version_name; + info_.address = image->GetSymAddr(symbol); + info_.symbol = symbol; +} + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/src/absl/debugging/internal/elf_mem_image.h b/src/absl/debugging/internal/elf_mem_image.h new file mode 100644 index 0000000..113071a --- /dev/null +++ b/src/absl/debugging/internal/elf_mem_image.h @@ -0,0 +1,139 @@ +/* + * Copyright 2017 The Abseil Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Allow dynamic symbol lookup for in-memory Elf images. + +#ifndef ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_ +#define ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_ + +// Including this will define the __GLIBC__ macro if glibc is being +// used. +#include + +#include "absl/base/config.h" + +// Maybe one day we can rewrite this file not to require the elf +// symbol extensions in glibc, but for right now we need them. +#ifdef ABSL_HAVE_ELF_MEM_IMAGE +#error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set +#endif + +#if defined(__ELF__) && !defined(__OpenBSD__) && !defined(__QNX__) && \ + !defined(__native_client__) && !defined(__asmjs__) && \ + !defined(__wasm__) && !defined(__HAIKU__) +#define ABSL_HAVE_ELF_MEM_IMAGE 1 +#endif + +#ifdef ABSL_HAVE_ELF_MEM_IMAGE + +#include // for ElfW + +#if defined(__FreeBSD__) && !defined(ElfW) +#define ElfW(x) __ElfN(x) +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// An in-memory ELF image (may not exist on disk). +class ElfMemImage { + private: + // Sentinel: there could never be an elf image at &kInvalidBaseSentinel. + static const int kInvalidBaseSentinel; + + public: + // Sentinel: there could never be an elf image at this address. + static constexpr const void *const kInvalidBase = + static_cast(&kInvalidBaseSentinel); + + // Information about a single vdso symbol. + // All pointers are into .dynsym, .dynstr, or .text of the VDSO. + // Do not free() them or modify through them. + struct SymbolInfo { + const char *name; // E.g. "__vdso_getcpu" + const char *version; // E.g. "LINUX_2.6", could be "" + // for unversioned symbol. + const void *address; // Relocated symbol address. + const ElfW(Sym) *symbol; // Symbol in the dynamic symbol table. + }; + + // Supports iteration over all dynamic symbols. + class SymbolIterator { + public: + friend class ElfMemImage; + const SymbolInfo *operator->() const; + const SymbolInfo &operator*() const; + SymbolIterator& operator++(); + bool operator!=(const SymbolIterator &rhs) const; + bool operator==(const SymbolIterator &rhs) const; + private: + SymbolIterator(const void *const image, int index); + void Update(int incr); + SymbolInfo info_; + int index_; + const void *const image_; + }; + + + explicit ElfMemImage(const void *base); + void Init(const void *base); + bool IsPresent() const { return ehdr_ != nullptr; } + const ElfW(Phdr)* GetPhdr(int index) const; + const ElfW(Sym)* GetDynsym(int index) const; + const ElfW(Versym)* GetVersym(int index) const; + const ElfW(Verdef)* GetVerdef(int index) const; + const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const; + const char* GetDynstr(ElfW(Word) offset) const; + const void* GetSymAddr(const ElfW(Sym) *sym) const; + const char* GetVerstr(ElfW(Word) offset) const; + int GetNumSymbols() const; + + SymbolIterator begin() const; + SymbolIterator end() const; + + // Look up versioned dynamic symbol in the image. + // Returns false if image is not present, or doesn't contain given + // symbol/version/type combination. + // If info_out is non-null, additional details are filled in. + bool LookupSymbol(const char *name, const char *version, + int symbol_type, SymbolInfo *info_out) const; + + // Find info about symbol (if any) which overlaps given address. + // Returns true if symbol was found; false if image isn't present + // or doesn't have a symbol overlapping given address. + // If info_out is non-null, additional details are filled in. + bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const; + + private: + const ElfW(Ehdr) *ehdr_; + const ElfW(Sym) *dynsym_; + const ElfW(Versym) *versym_; + const ElfW(Verdef) *verdef_; + const ElfW(Word) *hash_; + const char *dynstr_; + size_t strsize_; + size_t verdefnum_; + ElfW(Addr) link_base_; // Link-time base (p_vaddr of first PT_LOAD). +}; + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HAVE_ELF_MEM_IMAGE + +#endif // ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_ diff --git a/src/absl/debugging/internal/examine_stack.cc b/src/absl/debugging/internal/examine_stack.cc new file mode 100644 index 0000000..5bdd341 --- /dev/null +++ b/src/absl/debugging/internal/examine_stack.cc @@ -0,0 +1,315 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "absl/debugging/internal/examine_stack.h" + +#ifndef _WIN32 +#include +#endif + +#include "absl/base/config.h" + +#ifdef ABSL_HAVE_MMAP +#include +#endif + +#if defined(__linux__) || defined(__APPLE__) +#include +#endif + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +namespace { +constexpr int kDefaultDumpStackFramesLimit = 64; +// The %p field width for printf() functions is two characters per byte, +// and two extra for the leading "0x". +constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*); + +ABSL_CONST_INIT SymbolizeUrlEmitter debug_stack_trace_hook = nullptr; + +// Async-signal safe mmap allocator. +void* Allocate(size_t num_bytes) { +#ifdef ABSL_HAVE_MMAP + void* p = ::mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + return p == MAP_FAILED ? nullptr : p; +#else + (void)num_bytes; + return nullptr; +#endif // ABSL_HAVE_MMAP +} + +void Deallocate(void* p, size_t size) { +#ifdef ABSL_HAVE_MMAP + ::munmap(p, size); +#else + (void)p; + (void)size; +#endif // ABSL_HAVE_MMAP +} + +// Print a program counter only. +void DumpPC(OutputWriter* writer, void* writer_arg, void* const pc, + const char* const prefix) { + char buf[100]; + snprintf(buf, sizeof(buf), "%s@ %*p\n", prefix, kPrintfPointerFieldWidth, pc); + writer(buf, writer_arg); +} + +// Print a program counter and the corresponding stack frame size. +void DumpPCAndFrameSize(OutputWriter* writer, void* writer_arg, void* const pc, + int framesize, const char* const prefix) { + char buf[100]; + if (framesize <= 0) { + snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix, + kPrintfPointerFieldWidth, pc); + } else { + snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix, + kPrintfPointerFieldWidth, pc, framesize); + } + writer(buf, writer_arg); +} + +// Print a program counter and the corresponding symbol. +void DumpPCAndSymbol(OutputWriter* writer, void* writer_arg, void* const pc, + const char* const prefix) { + char tmp[1024]; + const char* symbol = "(unknown)"; + // Symbolizes the previous address of pc because pc may be in the + // next function. The overrun happens when the function ends with + // a call to a function annotated noreturn (e.g. CHECK). + // If symbolization of pc-1 fails, also try pc on the off-chance + // that we crashed on the first instruction of a function (that + // actually happens very often for e.g. __restore_rt). + const uintptr_t prev_pc = reinterpret_cast(pc) - 1; + if (absl::Symbolize(reinterpret_cast(prev_pc), tmp, + sizeof(tmp)) || + absl::Symbolize(pc, tmp, sizeof(tmp))) { + symbol = tmp; + } + char buf[1024]; + snprintf(buf, sizeof(buf), "%s@ %*p %s\n", prefix, kPrintfPointerFieldWidth, + pc, symbol); + writer(buf, writer_arg); +} + +// Print a program counter, its stack frame size, and its symbol name. +// Note that there is a separate symbolize_pc argument. Return addresses may be +// at the end of the function, and this allows the caller to back up from pc if +// appropriate. +void DumpPCAndFrameSizeAndSymbol(OutputWriter* writer, void* writer_arg, + void* const pc, void* const symbolize_pc, + int framesize, const char* const prefix) { + char tmp[1024]; + const char* symbol = "(unknown)"; + if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) { + symbol = tmp; + } + char buf[1024]; + if (framesize <= 0) { + snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix, + kPrintfPointerFieldWidth, pc, symbol); + } else { + snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix, + kPrintfPointerFieldWidth, pc, framesize, symbol); + } + writer(buf, writer_arg); +} + +} // namespace + +void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook) { + debug_stack_trace_hook = hook; +} + +SymbolizeUrlEmitter GetDebugStackTraceHook() { return debug_stack_trace_hook; } + +// Returns the program counter from signal context, nullptr if +// unknown. vuc is a ucontext_t*. We use void* to avoid the use of +// ucontext_t on non-POSIX systems. +void* GetProgramCounter(void* const vuc) { +#ifdef __linux__ + if (vuc != nullptr) { + ucontext_t* context = reinterpret_cast(vuc); +#if defined(__aarch64__) + return reinterpret_cast(context->uc_mcontext.pc); +#elif defined(__alpha__) + return reinterpret_cast(context->uc_mcontext.sc_pc); +#elif defined(__arm__) + return reinterpret_cast(context->uc_mcontext.arm_pc); +#elif defined(__hppa__) + return reinterpret_cast(context->uc_mcontext.sc_iaoq[0]); +#elif defined(__i386__) + if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs)) + return reinterpret_cast(context->uc_mcontext.gregs[14]); +#elif defined(__ia64__) + return reinterpret_cast(context->uc_mcontext.sc_ip); +#elif defined(__m68k__) + return reinterpret_cast(context->uc_mcontext.gregs[16]); +#elif defined(__mips__) + return reinterpret_cast(context->uc_mcontext.pc); +#elif defined(__powerpc64__) + return reinterpret_cast(context->uc_mcontext.gp_regs[32]); +#elif defined(__powerpc__) + return reinterpret_cast(context->uc_mcontext.uc_regs->gregs[32]); +#elif defined(__riscv) + return reinterpret_cast(context->uc_mcontext.__gregs[REG_PC]); +#elif defined(__s390__) && !defined(__s390x__) + return reinterpret_cast(context->uc_mcontext.psw.addr & 0x7fffffff); +#elif defined(__s390__) && defined(__s390x__) + return reinterpret_cast(context->uc_mcontext.psw.addr); +#elif defined(__sh__) + return reinterpret_cast(context->uc_mcontext.pc); +#elif defined(__sparc__) && !defined(__arch64__) + return reinterpret_cast(context->uc_mcontext.gregs[19]); +#elif defined(__sparc__) && defined(__arch64__) + return reinterpret_cast(context->uc_mcontext.mc_gregs[19]); +#elif defined(__x86_64__) + if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs)) + return reinterpret_cast(context->uc_mcontext.gregs[16]); +#elif defined(__e2k__) + return reinterpret_cast(context->uc_mcontext.cr0_hi); +#elif defined(__loongarch__) + return reinterpret_cast(context->uc_mcontext.__pc); +#else +#error "Undefined Architecture." +#endif + } +#elif defined(__APPLE__) + if (vuc != nullptr) { + ucontext_t* signal_ucontext = reinterpret_cast(vuc); +#if defined(__aarch64__) + return reinterpret_cast( + __darwin_arm_thread_state64_get_pc(signal_ucontext->uc_mcontext->__ss)); +#elif defined(__arm__) +#if __DARWIN_UNIX03 + return reinterpret_cast(signal_ucontext->uc_mcontext->__ss.__pc); +#else + return reinterpret_cast(signal_ucontext->uc_mcontext->ss.pc); +#endif +#elif defined(__i386__) +#if __DARWIN_UNIX03 + return reinterpret_cast(signal_ucontext->uc_mcontext->__ss.__eip); +#else + return reinterpret_cast(signal_ucontext->uc_mcontext->ss.eip); +#endif +#elif defined(__x86_64__) +#if __DARWIN_UNIX03 + return reinterpret_cast(signal_ucontext->uc_mcontext->__ss.__rip); +#else + return reinterpret_cast(signal_ucontext->uc_mcontext->ss.rip); +#endif +#endif + } +#elif defined(__akaros__) + auto* ctx = reinterpret_cast(vuc); + return reinterpret_cast(get_user_ctx_pc(ctx)); +#endif + static_cast(vuc); + return nullptr; +} + +void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[], + int frame_sizes[], int depth, + int min_dropped_frames, + bool symbolize_stacktrace, + OutputWriter* writer, void* writer_arg) { + if (pc != nullptr) { + // We don't know the stack frame size for PC, use 0. + if (symbolize_stacktrace) { + DumpPCAndFrameSizeAndSymbol(writer, writer_arg, pc, pc, 0, "PC: "); + } else { + DumpPCAndFrameSize(writer, writer_arg, pc, 0, "PC: "); + } + } + for (int i = 0; i < depth; i++) { + if (symbolize_stacktrace) { + // Pass the previous address of pc as the symbol address because pc is a + // return address, and an overrun may occur when the function ends with a + // call to a function annotated noreturn (e.g. CHECK). Note that we don't + // do this for pc above, as the adjustment is only correct for return + // addresses. + DumpPCAndFrameSizeAndSymbol(writer, writer_arg, stack[i], + reinterpret_cast(stack[i]) - 1, + frame_sizes[i], " "); + } else { + DumpPCAndFrameSize(writer, writer_arg, stack[i], frame_sizes[i], " "); + } + } + if (min_dropped_frames > 0) { + char buf[100]; + snprintf(buf, sizeof(buf), " @ ... and at least %d more frames\n", + min_dropped_frames); + writer(buf, writer_arg); + } +} + +// Dump current stack trace as directed by writer. +// Make sure this function is not inlined to avoid skipping too many top frames. +ABSL_ATTRIBUTE_NOINLINE +void DumpStackTrace(int min_dropped_frames, int max_num_frames, + bool symbolize_stacktrace, OutputWriter* writer, + void* writer_arg) { + // Print stack trace + void* stack_buf[kDefaultDumpStackFramesLimit]; + void** stack = stack_buf; + int num_stack = kDefaultDumpStackFramesLimit; + int allocated_bytes = 0; + + if (num_stack >= max_num_frames) { + // User requested fewer frames than we already have space for. + num_stack = max_num_frames; + } else { + const size_t needed_bytes = max_num_frames * sizeof(stack[0]); + void* p = Allocate(needed_bytes); + if (p != nullptr) { // We got the space. + num_stack = max_num_frames; + stack = reinterpret_cast(p); + allocated_bytes = needed_bytes; + } + } + + size_t depth = absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1); + for (size_t i = 0; i < depth; i++) { + if (symbolize_stacktrace) { + DumpPCAndSymbol(writer, writer_arg, stack[i], " "); + } else { + DumpPC(writer, writer_arg, stack[i], " "); + } + } + + auto hook = GetDebugStackTraceHook(); + if (hook != nullptr) { + (*hook)(stack, depth, writer, writer_arg); + } + + if (allocated_bytes != 0) Deallocate(stack, allocated_bytes); +} + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/debugging/internal/examine_stack.h b/src/absl/debugging/internal/examine_stack.h new file mode 100644 index 0000000..190af87 --- /dev/null +++ b/src/absl/debugging/internal/examine_stack.h @@ -0,0 +1,64 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ +#define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Type of function used for printing in stack trace dumping, etc. +// We avoid closures to keep things simple. +typedef void OutputWriter(const char*, void*); + +// RegisterDebugStackTraceHook() allows to register a single routine +// `hook` that is called each time DumpStackTrace() is called. +// `hook` may be called from a signal handler. +typedef void (*SymbolizeUrlEmitter)(void* const stack[], int depth, + OutputWriter* writer, void* writer_arg); + +// Registration of SymbolizeUrlEmitter for use inside of a signal handler. +// This is inherently unsafe and must be signal safe code. +void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook); +SymbolizeUrlEmitter GetDebugStackTraceHook(); + +// Returns the program counter from signal context, or nullptr if +// unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of +// ucontext_t on non-POSIX systems. +void* GetProgramCounter(void* const vuc); + +// Uses `writer` to dump the program counter, stack trace, and stack +// frame sizes. +void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[], + int frame_sizes[], int depth, + int min_dropped_frames, + bool symbolize_stacktrace, + OutputWriter* writer, void* writer_arg); + +// Dump current stack trace omitting the topmost `min_dropped_frames` stack +// frames. +void DumpStackTrace(int min_dropped_frames, int max_num_frames, + bool symbolize_stacktrace, OutputWriter* writer, + void* writer_arg); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ diff --git a/src/absl/debugging/internal/stack_consumption.cc b/src/absl/debugging/internal/stack_consumption.cc new file mode 100644 index 0000000..5134864 --- /dev/null +++ b/src/absl/debugging/internal/stack_consumption.cc @@ -0,0 +1,185 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/debugging/internal/stack_consumption.h" + +#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION + +#include +#include +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +namespace { + +// This code requires that we know the direction in which the stack +// grows. It is commonly believed that this can be detected by putting +// a variable on the stack and then passing its address to a function +// that compares the address of this variable to the address of a +// variable on the function's own stack. However, this is unspecified +// behavior in C++: If two pointers p and q of the same type point to +// different objects that are not members of the same object or +// elements of the same array or to different functions, or if only +// one of them is null, the results of pq, p<=q, and p>=q are +// unspecified. Therefore, instead we hardcode the direction of the +// stack on platforms we know about. +#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \ + defined(__aarch64__) || defined(__riscv) +constexpr bool kStackGrowsDown = true; +#else +#error Need to define kStackGrowsDown +#endif + +// To measure the stack footprint of some code, we create a signal handler +// (for SIGUSR2 say) that exercises this code on an alternate stack. This +// alternate stack is initialized to some known pattern (0x55, 0x55, 0x55, +// ...). We then self-send this signal, and after the signal handler returns, +// look at the alternate stack buffer to see what portion has been touched. +// +// This trick gives us the the stack footprint of the signal handler. But the +// signal handler, even before the code for it is exercised, consumes some +// stack already. We however only want the stack usage of the code inside the +// signal handler. To measure this accurately, we install two signal handlers: +// one that does nothing and just returns, and the user-provided signal +// handler. The difference between the stack consumption of these two signals +// handlers should give us the stack foorprint of interest. + +void EmptySignalHandler(int) {} + +// This is arbitrary value, and could be increase further, at the cost of +// memset()ting it all to known sentinel value. +constexpr int kAlternateStackSize = 64 << 10; // 64KiB + +constexpr int kSafetyMargin = 32; +constexpr char kAlternateStackFillValue = 0x55; + +// These helper functions look at the alternate stack buffer, and figure +// out what portion of this buffer has been touched - this is the stack +// consumption of the signal handler running on this alternate stack. +// This function will return -1 if the alternate stack buffer has not been +// touched. It will abort the program if the buffer has overflowed or is about +// to overflow. +int GetStackConsumption(const void* const altstack) { + const char* begin; + int increment; + if (kStackGrowsDown) { + begin = reinterpret_cast(altstack); + increment = 1; + } else { + begin = reinterpret_cast(altstack) + kAlternateStackSize - 1; + increment = -1; + } + + for (int usage_count = kAlternateStackSize; usage_count > 0; --usage_count) { + if (*begin != kAlternateStackFillValue) { + ABSL_RAW_CHECK(usage_count <= kAlternateStackSize - kSafetyMargin, + "Buffer has overflowed or is about to overflow"); + return usage_count; + } + begin += increment; + } + + ABSL_RAW_LOG(FATAL, "Unreachable code"); + return -1; +} + +} // namespace + +int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { + // The alt-signal-stack cannot be heap allocated because there is a + // bug in glibc-2.2 where some signal handler setup code looks at the + // current stack pointer to figure out what thread is currently running. + // Therefore, the alternate stack must be allocated from the main stack + // itself. + void* altstack = mmap(nullptr, kAlternateStackSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ABSL_RAW_CHECK(altstack != MAP_FAILED, "mmap() failed"); + + // Set up the alt-signal-stack (and save the older one). + stack_t sigstk; + memset(&sigstk, 0, sizeof(sigstk)); + sigstk.ss_sp = altstack; + sigstk.ss_size = kAlternateStackSize; + sigstk.ss_flags = 0; + stack_t old_sigstk; + memset(&old_sigstk, 0, sizeof(old_sigstk)); + ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0, + "sigaltstack() failed"); + + // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones). + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + struct sigaction old_sa1, old_sa2; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK; + + // SIGUSR1 maps to EmptySignalHandler. + sa.sa_handler = EmptySignalHandler; + ABSL_RAW_CHECK(sigaction(SIGUSR1, &sa, &old_sa1) == 0, "sigaction() failed"); + + // SIGUSR2 maps to signal_handler. + sa.sa_handler = signal_handler; + ABSL_RAW_CHECK(sigaction(SIGUSR2, &sa, &old_sa2) == 0, "sigaction() failed"); + + // Send SIGUSR1 signal and measure the stack consumption of the empty + // signal handler. + // The first signal might use more stack space. Run once and ignore the + // results to get that out of the way. + ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed"); + + memset(altstack, kAlternateStackFillValue, kAlternateStackSize); + ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed"); + int base_stack_consumption = GetStackConsumption(altstack); + + // Send SIGUSR2 signal and measure the stack consumption of signal_handler. + ABSL_RAW_CHECK(kill(getpid(), SIGUSR2) == 0, "kill() failed"); + int signal_handler_stack_consumption = GetStackConsumption(altstack); + + // Now restore the old alt-signal-stack and signal handlers. + if (old_sigstk.ss_sp == nullptr && old_sigstk.ss_size == 0 && + (old_sigstk.ss_flags & SS_DISABLE)) { + // https://git.musl-libc.org/cgit/musl/commit/src/signal/sigaltstack.c?id=7829f42a2c8944555439380498ab8b924d0f2070 + // The original stack has ss_size==0 and ss_flags==SS_DISABLE, but some + // versions of musl have a bug that rejects ss_size==0. Work around this by + // setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel + // when SS_DISABLE is set. + old_sigstk.ss_size = MINSIGSTKSZ; + } + ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0, + "sigaltstack() failed"); + ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0, + "sigaction() failed"); + ABSL_RAW_CHECK(sigaction(SIGUSR2, &old_sa2, nullptr) == 0, + "sigaction() failed"); + + ABSL_RAW_CHECK(munmap(altstack, kAlternateStackSize) == 0, "munmap() failed"); + if (signal_handler_stack_consumption != -1 && base_stack_consumption != -1) { + return signal_handler_stack_consumption - base_stack_consumption; + } + return -1; +} + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION diff --git a/src/absl/debugging/internal/stack_consumption.h b/src/absl/debugging/internal/stack_consumption.h new file mode 100644 index 0000000..f41b64c --- /dev/null +++ b/src/absl/debugging/internal/stack_consumption.h @@ -0,0 +1,50 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Helper function for measuring stack consumption of signal handlers. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ +#define ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ + +#include "absl/base/config.h" + +// The code in this module is not portable. +// Use this feature test macro to detect its availability. +#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION +#error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly +#elif !defined(__APPLE__) && !defined(_WIN32) && \ + (defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \ + defined(__aarch64__) || defined(__riscv)) +#define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Returns the stack consumption in bytes for the code exercised by +// signal_handler. To measure stack consumption, signal_handler is registered +// as a signal handler, so the code that it exercises must be async-signal +// safe. The argument of signal_handler is an implementation detail of signal +// handlers and should ignored by the code for signal_handler. Use global +// variables to pass information between your test code and signal_handler. +int GetSignalHandlerStackConsumption(void (*signal_handler)(int)); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION + +#endif // ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ diff --git a/src/absl/debugging/internal/stacktrace_aarch64-inl.inc b/src/absl/debugging/internal/stacktrace_aarch64-inl.inc new file mode 100644 index 0000000..4f9db9d --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -0,0 +1,204 @@ +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ + +// Generate stack tracer for aarch64 + +#if defined(__linux__) +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/debugging/internal/address_is_readable.h" +#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems +#include "absl/debugging/stacktrace.h" + +static const uintptr_t kUnknownFrameSize = 0; + +#if defined(__linux__) +// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. +static const unsigned char* GetKernelRtSigreturnAddress() { + constexpr uintptr_t kImpossibleAddress = 1; + ABSL_CONST_INIT static std::atomic memoized{kImpossibleAddress}; + uintptr_t address = memoized.load(std::memory_order_relaxed); + if (address != kImpossibleAddress) { + return reinterpret_cast(address); + } + + address = reinterpret_cast(nullptr); + +#ifdef ABSL_HAVE_VDSO_SUPPORT + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; + auto lookup = [&](int type) { + return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", type, + &symbol_info); + }; + if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || + symbol_info.address == nullptr) { + // Unexpected: VDSO is present, yet the expected symbol is missing + // or null. + assert(false && "VDSO is present, but doesn't have expected symbol"); + } else { + if (reinterpret_cast(symbol_info.address) != + kImpossibleAddress) { + address = reinterpret_cast(symbol_info.address); + } else { + assert(false && "VDSO returned invalid address"); + } + } + } +#endif + + memoized.store(address, std::memory_order_relaxed); + return reinterpret_cast(address); +} +#endif // __linux__ + +// Compute the size of a stack frame in [low..high). We assume that +// low < high. Return size of kUnknownFrameSize. +template +static inline uintptr_t ComputeStackFrameSize(const T* low, + const T* high) { + const char* low_char_ptr = reinterpret_cast(low); + const char* high_char_ptr = reinterpret_cast(high); + return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize; +} + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return null if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static void **NextStackFrame(void **old_frame_pointer, const void *uc) { + void **new_frame_pointer = reinterpret_cast(*old_frame_pointer); + bool check_frame_size = true; + +#if defined(__linux__) + if (WITH_CONTEXT && uc != nullptr) { + // Check to see if next frame's return address is __kernel_rt_sigreturn. + if (old_frame_pointer[1] == GetKernelRtSigreturnAddress()) { + const ucontext_t *ucv = static_cast(uc); + // old_frame_pointer[0] is not suitable for unwinding, look at + // ucontext to discover frame pointer before signal. + void **const pre_signal_frame_pointer = + reinterpret_cast(ucv->uc_mcontext.regs[29]); + + // Check that alleged frame pointer is actually readable. This is to + // prevent "double fault" in case we hit the first fault due to e.g. + // stack corruption. + if (!absl::debugging_internal::AddressIsReadable( + pre_signal_frame_pointer)) + return nullptr; + + // Alleged frame pointer is readable, use it for further unwinding. + new_frame_pointer = pre_signal_frame_pointer; + + // Skip frame size check if we return from a signal. We may be using a + // an alternate stack for signals. + check_frame_size = false; + } + } +#endif + + // aarch64 ABI requires stack pointer to be 16-byte-aligned. + if ((reinterpret_cast(new_frame_pointer) & 15) != 0) + return nullptr; + + // Check frame size. In strict mode, we assume frames to be under + // 100,000 bytes. In non-strict mode, we relax the limit to 1MB. + if (check_frame_size) { + const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const uintptr_t frame_size = + ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); + if (frame_size == kUnknownFrameSize || frame_size > max_size) + return nullptr; + } + + return new_frame_pointer; +} + +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { +#ifdef __GNUC__ + void **frame_pointer = reinterpret_cast(__builtin_frame_address(0)); +#else +# error reading stack point not yet supported on this platform. +#endif + + skip_count++; // Skip the frame for this function. + int n = 0; + + // The frame pointer points to low address of a frame. The first 64-bit + // word of a frame points to the next frame up the call chain, which normally + // is just after the high address of the current frame. The second word of + // a frame contains return adress of to the caller. To find a pc value + // associated with the current frame, we need to go down a level in the call + // chain. So we remember return the address of the last frame seen. This + // does not work for the first stack frame, which belongs to UnwindImp() but + // we skip the frame for UnwindImp() anyway. + void* prev_return_address = nullptr; + + while (frame_pointer && n < max_depth) { + // The absl::GetStackFrames routine is called when we are in some + // informational context (the failure signal handler for example). + // Use the non-strict unwinding rules to produce a stack trace + // that is as complete as possible (even if it contains a few bogus + // entries in some rare cases). + void **next_frame_pointer = + NextStackFrame(frame_pointer, ucp); + + if (skip_count > 0) { + skip_count--; + } else { + result[n] = prev_return_address; + if (IS_STACK_FRAMES) { + sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); + } + n++; + } + prev_return_address = frame_pointer[1]; + frame_pointer = next_frame_pointer; + } + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 200; + int num_dropped_frames = 0; + for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } + frame_pointer = + NextStackFrame(frame_pointer, ucp); + } + *min_dropped_frames = num_dropped_frames; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return true; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ diff --git a/src/absl/debugging/internal/stacktrace_arm-inl.inc b/src/absl/debugging/internal/stacktrace_arm-inl.inc new file mode 100644 index 0000000..102a2a1 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_arm-inl.inc @@ -0,0 +1,139 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This is inspired by Craig Silverstein's PowerPC stacktrace code. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ + +#include + +#include "absl/debugging/stacktrace.h" + +// WARNING: +// This only works if all your code is in either ARM or THUMB mode. With +// interworking, the frame pointer of the caller can either be in r11 (ARM +// mode) or r7 (THUMB mode). A callee only saves the frame pointer of its +// mode in a fixed location on its stack frame. If the caller is a different +// mode, there is no easy way to find the frame pointer. It can either be +// still in the designated register or saved on stack along with other callee +// saved registers. + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return nullptr if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +static void **NextStackFrame(void **old_sp) { + void **new_sp = (void**) old_sp[-1]; + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (STRICT_UNWINDING) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_sp <= old_sp) return nullptr; + // Assume stack frames larger than 100,000 bytes are bogus. + if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr; + } else { + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_sp == old_sp) return nullptr; + // And allow frames upto about 1MB. + if ((new_sp > old_sp) + && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return nullptr; + } + if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return nullptr; + return new_sp; +} + +// This ensures that absl::GetStackTrace sets up the Link Register properly. +#ifdef __GNUC__ +void StacktraceArmDummyFunction() __attribute__((noinline)); +void StacktraceArmDummyFunction() { __asm__ volatile(""); } +#else +# error StacktraceArmDummyFunction() needs to be ported to this platform. +#endif + +template +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void * /* ucp */, int *min_dropped_frames) { +#ifdef __GNUC__ + void **sp = reinterpret_cast(__builtin_frame_address(0)); +#else +# error reading stack point not yet supported on this platform. +#endif + + // On ARM, the return address is stored in the link register (r14). + // This is not saved on the stack frame of a leaf function. To + // simplify code that reads return addresses, we call a dummy + // function so that the return address of this function is also + // stored in the stack frame. This works at least for gcc. + StacktraceArmDummyFunction(); + + int n = 0; + while (sp && n < max_depth) { + // The absl::GetStackFrames routine is called when we are in some + // informational context (the failure signal handler for example). + // Use the non-strict unwinding rules to produce a stack trace + // that is as complete as possible (even if it contains a few bogus + // entries in some rare cases). + void **next_sp = NextStackFrame(sp); + + if (skip_count > 0) { + skip_count--; + } else { + result[n] = *sp; + + if (IS_STACK_FRAMES) { + if (next_sp > sp) { + sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp; + } else { + // A frame-size of 0 is used to indicate unknown frame size. + sizes[n] = 0; + } + } + n++; + } + sp = next_sp; + } + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 200; + int num_dropped_frames = 0; + for (int j = 0; sp != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } + sp = NextStackFrame(sp); + } + *min_dropped_frames = num_dropped_frames; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return false; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ diff --git a/src/absl/debugging/internal/stacktrace_config.h b/src/absl/debugging/internal/stacktrace_config.h new file mode 100644 index 0000000..3929b1b --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_config.h @@ -0,0 +1,88 @@ +/* + * Copyright 2017 The Abseil Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + * Defines ABSL_STACKTRACE_INL_HEADER to the *-inl.h containing + * actual unwinder implementation. + * This header is "private" to stacktrace.cc. + * DO NOT include it into any other files. +*/ +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ + +#include "absl/base/config.h" + +#if defined(ABSL_STACKTRACE_INL_HEADER) +#error ABSL_STACKTRACE_INL_HEADER cannot be directly set + +#elif defined(_WIN32) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_win32-inl.inc" + +#elif defined(__APPLE__) +#ifdef ABSL_HAVE_THREAD_LOCAL +// Thread local support required for UnwindImpl. +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_generic-inl.inc" +#endif // defined(ABSL_HAVE_THREAD_LOCAL) + +// Emscripten stacktraces rely on JS. Do not use them in standalone mode. +#elif defined(__EMSCRIPTEN__) && !defined(STANDALONE_WASM) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_emscripten-inl.inc" + +#elif defined(__linux__) && !defined(__ANDROID__) + +#if defined(NO_FRAME_POINTER) && \ + (defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)) +// Note: The libunwind-based implementation is not available to open-source +// users. +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_libunwind-inl.inc" +#define STACKTRACE_USES_LIBUNWIND 1 +#elif defined(NO_FRAME_POINTER) && defined(__has_include) +#if __has_include() +// Note: When using glibc this may require -funwind-tables to function properly. +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_generic-inl.inc" +#endif // __has_include() +#elif defined(__i386__) || defined(__x86_64__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_x86-inl.inc" +#elif defined(__ppc__) || defined(__PPC__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_powerpc-inl.inc" +#elif defined(__aarch64__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_aarch64-inl.inc" +#elif defined(__riscv) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_riscv-inl.inc" +#elif defined(__has_include) +#if __has_include() +// Note: When using glibc this may require -funwind-tables to function properly. +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_generic-inl.inc" +#endif // __has_include() +#endif // defined(__has_include) + +#endif // defined(__linux__) && !defined(__ANDROID__) + +// Fallback to the empty implementation. +#if !defined(ABSL_STACKTRACE_INL_HEADER) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_unimplemented-inl.inc" +#endif + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ diff --git a/src/absl/debugging/internal/stacktrace_emscripten-inl.inc b/src/absl/debugging/internal/stacktrace_emscripten-inl.inc new file mode 100644 index 0000000..0f44451 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_emscripten-inl.inc @@ -0,0 +1,110 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Portable implementation - just use glibc +// +// Note: The glibc implementation may cause a call to malloc. +// This can cause a deadlock in HeapProfiler. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ + +#include + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/debugging/stacktrace.h" + +extern "C" { +uintptr_t emscripten_stack_snapshot(); +uint32_t emscripten_stack_unwind_buffer(uintptr_t pc, void *buffer, + uint32_t depth); +} + +// Sometimes, we can try to get a stack trace from within a stack +// trace, which can cause a self-deadlock. +// Protect against such reentrant call by failing to get a stack trace. +// +// We use __thread here because the code here is extremely low level -- it is +// called while collecting stack traces from within malloc and mmap, and thus +// can not call anything which might call malloc or mmap itself. +static __thread int recursive = 0; + +// The stack trace function might be invoked very early in the program's +// execution (e.g. from the very first malloc). +// As such, we suppress usage of backtrace during this early stage of execution. +static std::atomic disable_stacktraces(true); // Disabled until healthy. +// Waiting until static initializers run seems to be late enough. +// This file is included into stacktrace.cc so this will only run once. +ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() { + // Check if we can even create stacktraces. If not, bail early and leave + // disable_stacktraces set as-is. + // clang-format off + if (!EM_ASM_INT({ return (typeof wasmOffsetConverter !== 'undefined'); })) { + return 0; + } + // clang-format on + disable_stacktraces.store(false, std::memory_order_relaxed); + return 0; +}(); + +template +static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) { + return 0; + } + ++recursive; + + static_cast(ucp); // Unused. + constexpr int kStackLength = 64; + void *stack[kStackLength]; + + int size; + uintptr_t pc = emscripten_stack_snapshot(); + size = emscripten_stack_unwind_buffer(pc, stack, kStackLength); + + int result_count = size - skip_count; + if (result_count < 0) result_count = 0; + if (result_count > max_depth) result_count = max_depth; + for (int i = 0; i < result_count; i++) result[i] = stack[i + skip_count]; + + if (IS_STACK_FRAMES) { + // No implementation for finding out the stack frame sizes yet. + memset(sizes, 0, sizeof(*sizes) * result_count); + } + if (min_dropped_frames != nullptr) { + if (size - skip_count - max_depth > 0) { + *min_dropped_frames = size - skip_count - max_depth; + } else { + *min_dropped_frames = 0; + } + } + + --recursive; + + return result_count; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { return true; } +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ diff --git a/src/absl/debugging/internal/stacktrace_generic-inl.inc b/src/absl/debugging/internal/stacktrace_generic-inl.inc new file mode 100644 index 0000000..b2792a1 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_generic-inl.inc @@ -0,0 +1,108 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Portable implementation - just use glibc +// +// Note: The glibc implementation may cause a call to malloc. +// This can cause a deadlock in HeapProfiler. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ + +#include +#include +#include + +#include "absl/debugging/stacktrace.h" +#include "absl/base/attributes.h" + +// Sometimes, we can try to get a stack trace from within a stack +// trace, because we don't block signals inside this code (which would be too +// expensive: the two extra system calls per stack trace do matter here). +// That can cause a self-deadlock. +// Protect against such reentrant call by failing to get a stack trace. +// +// We use __thread here because the code here is extremely low level -- it is +// called while collecting stack traces from within malloc and mmap, and thus +// can not call anything which might call malloc or mmap itself. +static __thread int recursive = 0; + +// The stack trace function might be invoked very early in the program's +// execution (e.g. from the very first malloc if using tcmalloc). Also, the +// glibc implementation itself will trigger malloc the first time it is called. +// As such, we suppress usage of backtrace during this early stage of execution. +static std::atomic disable_stacktraces(true); // Disabled until healthy. +// Waiting until static initializers run seems to be late enough. +// This file is included into stacktrace.cc so this will only run once. +ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() { + void* unused_stack[1]; + // Force the first backtrace to happen early to get the one-time shared lib + // loading (allocation) out of the way. After the first call it is much safer + // to use backtrace from a signal handler if we crash somewhere later. + backtrace(unused_stack, 1); + disable_stacktraces.store(false, std::memory_order_relaxed); + return 0; +}(); + +template +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) { + return 0; + } + ++recursive; + + static_cast(ucp); // Unused. + static const int kStackLength = 64; + void * stack[kStackLength]; + int size; + + size = backtrace(stack, kStackLength); + skip_count++; // we want to skip the current frame as well + int result_count = size - skip_count; + if (result_count < 0) + result_count = 0; + if (result_count > max_depth) + result_count = max_depth; + for (int i = 0; i < result_count; i++) + result[i] = stack[i + skip_count]; + + if (IS_STACK_FRAMES) { + // No implementation for finding out the stack frame sizes yet. + memset(sizes, 0, sizeof(*sizes) * result_count); + } + if (min_dropped_frames != nullptr) { + if (size - skip_count - max_depth > 0) { + *min_dropped_frames = size - skip_count - max_depth; + } else { + *min_dropped_frames = 0; + } + } + + --recursive; + + return result_count; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return true; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ diff --git a/src/absl/debugging/internal/stacktrace_powerpc-inl.inc b/src/absl/debugging/internal/stacktrace_powerpc-inl.inc new file mode 100644 index 0000000..085cef6 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -0,0 +1,258 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Produce stack trace. I'm guessing (hoping!) the code is much like +// for x86. For apple machines, at least, it seems to be; see +// https://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html +// https://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK +// Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882 + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ + +#if defined(__linux__) +#include // for PT_NIP. +#include // for ucontext_t +#endif + +#include +#include +#include +#include + +#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" +#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems + +// Given a stack pointer, return the saved link register value. +// Note that this is the link register for a callee. +static inline void *StacktracePowerPCGetLR(void **sp) { + // PowerPC has 3 main ABIs, which say where in the stack the + // Link Register is. For DARWIN and AIX (used by apple and + // linux ppc64), it's in sp[2]. For SYSV (used by linux ppc), + // it's in sp[1]. +#if defined(_CALL_AIX) || defined(_CALL_DARWIN) + return *(sp+2); +#elif defined(_CALL_SYSV) + return *(sp+1); +#elif defined(__APPLE__) || defined(__FreeBSD__) || \ + (defined(__linux__) && defined(__PPC64__)) + // This check is in case the compiler doesn't define _CALL_AIX/etc. + return *(sp+2); +#elif defined(__linux) + // This check is in case the compiler doesn't define _CALL_SYSV. + return *(sp+1); +#else +#error Need to specify the PPC ABI for your archiecture. +#endif +} + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return null if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static void **NextStackFrame(void **old_sp, const void *uc) { + void **new_sp = (void **) *old_sp; + enum { kStackAlignment = 16 }; + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (STRICT_UNWINDING) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_sp <= old_sp) return nullptr; + // Assume stack frames larger than 100,000 bytes are bogus. + if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr; + } else { + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_sp == old_sp) return nullptr; + // And allow frames upto about 1MB. + if ((new_sp > old_sp) + && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return nullptr; + } + if ((uintptr_t)new_sp % kStackAlignment != 0) return nullptr; + +#if defined(__linux__) + enum StackTraceKernelSymbolStatus { + kNotInitialized = 0, kAddressValid, kAddressInvalid }; + + if (IS_WITH_CONTEXT && uc != nullptr) { + static StackTraceKernelSymbolStatus kernel_symbol_status = + kNotInitialized; // Sentinel: not computed yet. + // Initialize with sentinel value: __kernel_rt_sigtramp_rt64 can not + // possibly be there. + static const unsigned char *kernel_sigtramp_rt64_address = nullptr; + if (kernel_symbol_status == kNotInitialized) { + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo + sigtramp_rt64_symbol_info; + if (!vdso.LookupSymbol( + "__kernel_sigtramp_rt64", "LINUX_2.6.15", + absl::debugging_internal::VDSOSupport::kVDSOSymbolType, + &sigtramp_rt64_symbol_info) || + sigtramp_rt64_symbol_info.address == nullptr) { + // Unexpected: VDSO is present, yet the expected symbol is missing + // or null. + assert(false && "VDSO is present, but doesn't have expected symbol"); + kernel_symbol_status = kAddressInvalid; + } else { + kernel_sigtramp_rt64_address = + reinterpret_cast( + sigtramp_rt64_symbol_info.address); + kernel_symbol_status = kAddressValid; + } + } else { + kernel_symbol_status = kAddressInvalid; + } + } + + if (new_sp != nullptr && + kernel_symbol_status == kAddressValid && + StacktracePowerPCGetLR(new_sp) == kernel_sigtramp_rt64_address) { + const ucontext_t* signal_context = + reinterpret_cast(uc); + void **const sp_before_signal = +#if defined(__PPC64__) + reinterpret_cast(signal_context->uc_mcontext.gp_regs[PT_R1]); +#else + reinterpret_cast( + signal_context->uc_mcontext.uc_regs->gregs[PT_R1]); +#endif + // Check that alleged sp before signal is nonnull and is reasonably + // aligned. + if (sp_before_signal != nullptr && + ((uintptr_t)sp_before_signal % kStackAlignment) == 0) { + // Check that alleged stack pointer is actually readable. This is to + // prevent a "double fault" in case we hit the first fault due to e.g. + // a stack corruption. + if (absl::debugging_internal::AddressIsReadable(sp_before_signal)) { + // Alleged stack pointer is readable, use it for further unwinding. + new_sp = sp_before_signal; + } + } + } + } +#endif + + return new_sp; +} + +// This ensures that absl::GetStackTrace sets up the Link Register properly. +ABSL_ATTRIBUTE_NOINLINE static void AbslStacktracePowerPCDummyFunction() { + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); +} + +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + void **sp; + // Apple macOS uses an old version of gnu as -- both Darwin 7.9.0 (Panther) + // and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a + // different asm syntax. I don't know quite the best way to discriminate + // systems using the old as from the new one; I've gone with __APPLE__. +#ifdef __APPLE__ + __asm__ volatile ("mr %0,r1" : "=r" (sp)); +#else + __asm__ volatile ("mr %0,1" : "=r" (sp)); +#endif + + // On PowerPC, the "Link Register" or "Link Record" (LR), is a stack + // entry that holds the return address of the subroutine call (what + // instruction we run after our function finishes). This is the + // same as the stack-pointer of our parent routine, which is what we + // 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. + AbslStacktracePowerPCDummyFunction(); + + // The LR save area is used by the callee, so the top entry is bogus. + skip_count++; + + int n = 0; + + // Unlike ABIs of X86 and ARM, PowerPC ABIs say that return address (in + // the link register) of a function call is stored in the caller's stack + // frame instead of the callee's. When we look for the return address + // associated with a stack frame, we need to make sure that there is a + // caller frame before it. So we call NextStackFrame before entering the + // loop below and check next_sp instead of sp for loop termination. + // The outermost frame is set up by runtimes and it does not have a + // caller frame, so it is skipped. + + // The absl::GetStackFrames routine is called when we are in some + // informational context (the failure signal handler for example). + // Use the non-strict unwinding rules to produce a stack trace + // that is as complete as possible (even if it contains a few + // bogus entries in some rare cases). + void **next_sp = NextStackFrame(sp, ucp); + + while (next_sp && n < max_depth) { + if (skip_count > 0) { + skip_count--; + } else { + result[n] = StacktracePowerPCGetLR(sp); + if (IS_STACK_FRAMES) { + if (next_sp > sp) { + sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp; + } else { + // A frame-size of 0 is used to indicate unknown frame size. + sizes[n] = 0; + } + } + n++; + } + + sp = next_sp; + next_sp = NextStackFrame(sp, ucp); + } + + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 1000; + int num_dropped_frames = 0; + for (int j = 0; next_sp != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } + next_sp = NextStackFrame(next_sp, ucp); + } + *min_dropped_frames = num_dropped_frames; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return true; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ diff --git a/src/absl/debugging/internal/stacktrace_riscv-inl.inc b/src/absl/debugging/internal/stacktrace_riscv-inl.inc new file mode 100644 index 0000000..7123b71 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_riscv-inl.inc @@ -0,0 +1,236 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ + +// Generate stack trace for riscv + +#include + +#include "absl/base/config.h" +#if defined(__linux__) +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/debugging/internal/address_is_readable.h" +#include "absl/debugging/internal/vdso_support.h" +#include "absl/debugging/stacktrace.h" + +static const uintptr_t kUnknownFrameSize = 0; + +#if defined(__linux__) +// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. +static const unsigned char *GetKernelRtSigreturnAddress() { + constexpr uintptr_t kImpossibleAddress = 0; + ABSL_CONST_INIT static std::atomic memoized(kImpossibleAddress); + uintptr_t address = memoized.load(std::memory_order_relaxed); + if (address != kImpossibleAddress) { + return reinterpret_cast(address); + } + + address = reinterpret_cast(nullptr); + +#if ABSL_HAVE_VDSO_SUPPORT + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; + // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10. + auto lookup = [&](int type) { + return vdso.LookupSymbol("__vdso_rt_sigreturn", "LINUX_4.15", type, + &symbol_info); + }; + if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || + symbol_info.address == nullptr) { + // Unexpected: VDSO is present, yet the expected symbol is missing or + // null. + assert(false && "VDSO is present, but doesn't have expected symbol"); + } else { + if (reinterpret_cast(symbol_info.address) != + kImpossibleAddress) { + address = reinterpret_cast(symbol_info.address); + } else { + assert(false && "VDSO returned invalid address"); + } + } + } +#endif + + memoized.store(address, std::memory_order_relaxed); + return reinterpret_cast(address); +} +#endif // __linux__ + +// Compute the size of a stack frame in [low..high). We assume that low < high. +// Return size of kUnknownFrameSize. +template +static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) { + const char *low_char_ptr = reinterpret_cast(low); + const char *high_char_ptr = reinterpret_cast(high); + return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize; +} + +// Given a pointer to a stack frame, locate and return the calling stackframe, +// or return null if no stackframe can be found. Perform sanity checks (the +// strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { + // . + // . + // . + // +-> +----------------+ + // | | return address | + // | | previous fp | + // | | ... | + // | +----------------+ <-+ + // | | return address | | + // +---|- previous fp | | + // | ... | | + // $fp ->|----------------+ | + // | return address | | + // | previous fp -|---+ + // $sp ->| ... | + // +----------------+ + void **new_frame_pointer = reinterpret_cast(old_frame_pointer[-2]); + bool check_frame_size = true; + +#if defined(__linux__) + if (WITH_CONTEXT && uc != nullptr) { + // Check to see if next frame's return address is __kernel_rt_sigreturn. + if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) { + const ucontext_t *ucv = static_cast(uc); + // old_frame_pointer is not suitable for unwinding, look at ucontext to + // discover frame pointer before signal. + // + // RISCV ELF psABI has the frame pointer at x8/fp/s0. + // -- RISCV psABI Table 18.2 + void **const pre_signal_frame_pointer = + reinterpret_cast(ucv->uc_mcontext.__gregs[8]); + + // Check the alleged frame pointer is actually readable. This is to + // prevent "double fault" in case we hit the first fault due to stack + // corruption. + if (!absl::debugging_internal::AddressIsReadable( + pre_signal_frame_pointer)) + return nullptr; + + // Alleged frame pointer is readable, use it for further unwinding. + new_frame_pointer = pre_signal_frame_pointer; + + // Skip frame size check if we return from a signal. We may be using an + // alterate stack for signals. + check_frame_size = false; + } + } +#endif + + // The RISCV ELF psABI mandates that the stack pointer is always 16-byte + // aligned. + // FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte + // alignment. + if ((reinterpret_cast(new_frame_pointer) & 15) != 0) + return nullptr; + + // Check frame size. In strict mode, we assume frames to be under 100,000 + // bytes. In non-strict mode, we relax the limit to 1MB. + if (check_frame_size) { + const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const uintptr_t frame_size = + ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); + if (frame_size == kUnknownFrameSize || frame_size > max_size) + return nullptr; + } + + return new_frame_pointer; +} + +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + // The `frame_pointer` that is computed here points to the top of the frame. + // The two words preceding the address are the return address and the previous + // frame pointer. +#if defined(__GNUC__) + void **frame_pointer = reinterpret_cast(__builtin_frame_address(0)); +#else +#error reading stack pointer not yet supported on this platform +#endif + + int n = 0; + void *return_address = nullptr; + while (frame_pointer && n < max_depth) { + return_address = frame_pointer[-1]; + + // The absl::GetStackFrames routine is called when we are in some + // informational context (the failure signal handler for example). Use the + // non-strict unwinding rules to produce a stack trace that is as complete + // as possible (even if it contains a few bogus entries in some rare cases). + void **next_frame_pointer = + NextStackFrame(frame_pointer, ucp); + + if (skip_count > 0) { + skip_count--; + } else { + result[n] = return_address; + if (IS_STACK_FRAMES) { + sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); + } + n++; + } + + frame_pointer = next_frame_pointer; + } + + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 200; + int num_dropped_frames = 0; + for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } + frame_pointer = + NextStackFrame(frame_pointer, ucp); + } + *min_dropped_frames = num_dropped_frames; + } + + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { return true; } +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif diff --git a/src/absl/debugging/internal/stacktrace_unimplemented-inl.inc b/src/absl/debugging/internal/stacktrace_unimplemented-inl.inc new file mode 100644 index 0000000..5b8fb19 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_unimplemented-inl.inc @@ -0,0 +1,24 @@ +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_ + +template +static int UnwindImpl(void** /* result */, int* /* sizes */, + int /* max_depth */, int /* skip_count */, + const void* /* ucp */, int *min_dropped_frames) { + if (min_dropped_frames != nullptr) { + *min_dropped_frames = 0; + } + return 0; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return false; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_ diff --git a/src/absl/debugging/internal/stacktrace_win32-inl.inc b/src/absl/debugging/internal/stacktrace_win32-inl.inc new file mode 100644 index 0000000..1c666c8 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_win32-inl.inc @@ -0,0 +1,93 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Produces a stack trace for Windows. Normally, one could use +// stacktrace_x86-inl.h or stacktrace_x86_64-inl.h -- and indeed, that +// should work for binaries compiled using MSVC in "debug" mode. +// However, in "release" mode, Windows uses frame-pointer +// optimization, which makes getting a stack trace very difficult. +// +// There are several approaches one can take. One is to use Windows +// intrinsics like StackWalk64. These can work, but have restrictions +// on how successful they can be. Another attempt is to write a +// version of stacktrace_x86-inl.h that has heuristic support for +// dealing with FPO, similar to what WinDbg does (see +// http://www.nynaeve.net/?p=97). There are (non-working) examples of +// these approaches, complete with TODOs, in stacktrace_win32-inl.h#1 +// +// The solution we've ended up doing is to call the undocumented +// windows function RtlCaptureStackBackTrace, which probably doesn't +// work with FPO but at least is fast, and doesn't require a symbol +// server. +// +// This code is inspired by a patch from David Vitek: +// https://code.google.com/p/google-perftools/issues/detail?id=83 + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ + +#include // for GetProcAddress and GetModuleHandle +#include + +typedef USHORT NTAPI RtlCaptureStackBackTrace_Function( + IN ULONG frames_to_skip, + IN ULONG frames_to_capture, + OUT PVOID *backtrace, + OUT PULONG backtrace_hash); + +// It is not possible to load RtlCaptureStackBackTrace at static init time in +// UWP. CaptureStackBackTrace is the public version of RtlCaptureStackBackTrace +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = + &::CaptureStackBackTrace; +#else +// Load the function we need at static init time, where we don't have +// to worry about someone else holding the loader's lock. +static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = + (RtlCaptureStackBackTrace_Function*)GetProcAddress( + GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace"); +#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP + +template +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void*, int* min_dropped_frames) { + int n = 0; + if (!RtlCaptureStackBackTrace_fn) { + // can't find a stacktrace with no function to call + } else { + n = (int)RtlCaptureStackBackTrace_fn(skip_count + 2, max_depth, result, 0); + } + if (IS_STACK_FRAMES) { + // No implementation for finding out the stack frame sizes yet. + memset(sizes, 0, sizeof(*sizes) * n); + } + if (min_dropped_frames != nullptr) { + // Not implemented. + *min_dropped_frames = 0; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return false; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ diff --git a/src/absl/debugging/internal/stacktrace_x86-inl.inc b/src/absl/debugging/internal/stacktrace_x86-inl.inc new file mode 100644 index 0000000..1b5d823 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_x86-inl.inc @@ -0,0 +1,369 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Produce stack trace + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_ + +#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__)) +#include // for ucontext_t +#endif + +#if !defined(_WIN32) +#include +#endif + +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/base/port.h" +#include "absl/debugging/internal/address_is_readable.h" +#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems +#include "absl/debugging/stacktrace.h" + +#include "absl/base/internal/raw_logging.h" + +using absl::debugging_internal::AddressIsReadable; + +#if defined(__linux__) && defined(__i386__) +// Count "push %reg" instructions in VDSO __kernel_vsyscall(), +// preceeding "syscall" or "sysenter". +// If __kernel_vsyscall uses frame pointer, answer 0. +// +// kMaxBytes tells how many instruction bytes of __kernel_vsyscall +// to analyze before giving up. Up to kMaxBytes+1 bytes of +// instructions could be accessed. +// +// Here are known __kernel_vsyscall instruction sequences: +// +// SYSENTER (linux-2.6.26/arch/x86/vdso/vdso32/sysenter.S). +// Used on Intel. +// 0xffffe400 <__kernel_vsyscall+0>: push %ecx +// 0xffffe401 <__kernel_vsyscall+1>: push %edx +// 0xffffe402 <__kernel_vsyscall+2>: push %ebp +// 0xffffe403 <__kernel_vsyscall+3>: mov %esp,%ebp +// 0xffffe405 <__kernel_vsyscall+5>: sysenter +// +// SYSCALL (see linux-2.6.26/arch/x86/vdso/vdso32/syscall.S). +// Used on AMD. +// 0xffffe400 <__kernel_vsyscall+0>: push %ebp +// 0xffffe401 <__kernel_vsyscall+1>: mov %ecx,%ebp +// 0xffffe403 <__kernel_vsyscall+3>: syscall +// + +// The sequence below isn't actually expected in Google fleet, +// here only for completeness. Remove this comment from OSS release. + +// i386 (see linux-2.6.26/arch/x86/vdso/vdso32/int80.S) +// 0xffffe400 <__kernel_vsyscall+0>: int $0x80 +// 0xffffe401 <__kernel_vsyscall+1>: ret +// +static const int kMaxBytes = 10; + +// We use assert()s instead of DCHECK()s -- this is too low level +// for DCHECK(). + +static int CountPushInstructions(const unsigned char *const addr) { + int result = 0; + for (int i = 0; i < kMaxBytes; ++i) { + if (addr[i] == 0x89) { + // "mov reg,reg" + if (addr[i + 1] == 0xE5) { + // Found "mov %esp,%ebp". + return 0; + } + ++i; // Skip register encoding byte. + } else if (addr[i] == 0x0F && + (addr[i + 1] == 0x34 || addr[i + 1] == 0x05)) { + // Found "sysenter" or "syscall". + return result; + } else if ((addr[i] & 0xF0) == 0x50) { + // Found "push %reg". + ++result; + } else if (addr[i] == 0xCD && addr[i + 1] == 0x80) { + // Found "int $0x80" + assert(result == 0); + return 0; + } else { + // Unexpected instruction. + assert(false && "unexpected instruction in __kernel_vsyscall"); + return 0; + } + } + // Unexpected: didn't find SYSENTER or SYSCALL in + // [__kernel_vsyscall, __kernel_vsyscall + kMaxBytes) interval. + assert(false && "did not find SYSENTER or SYSCALL in __kernel_vsyscall"); + return 0; +} +#endif + +// Assume stack frames larger than 100,000 bytes are bogus. +static const int kMaxFrameBytes = 100000; + +// Returns the stack frame pointer from signal context, 0 if unknown. +// vuc is a ucontext_t *. We use void* to avoid the use +// of ucontext_t on non-POSIX systems. +static uintptr_t GetFP(const void *vuc) { +#if !defined(__linux__) + static_cast(vuc); // Avoid an unused argument compiler warning. +#else + if (vuc != nullptr) { + auto *uc = reinterpret_cast(vuc); +#if defined(__i386__) + const auto bp = uc->uc_mcontext.gregs[REG_EBP]; + const auto sp = uc->uc_mcontext.gregs[REG_ESP]; +#elif defined(__x86_64__) + const auto bp = uc->uc_mcontext.gregs[REG_RBP]; + const auto sp = uc->uc_mcontext.gregs[REG_RSP]; +#else + const uintptr_t bp = 0; + const uintptr_t sp = 0; +#endif + // Sanity-check that the base pointer is valid. It's possible that some + // code in the process is compiled with --copt=-fomit-frame-pointer or + // --copt=-momit-leaf-frame-pointer. + // + // TODO(bcmills): -momit-leaf-frame-pointer is currently the default + // behavior when building with clang. Talk to the C++ toolchain team about + // fixing that. + if (bp >= sp && bp - sp <= kMaxFrameBytes) return bp; + + // If bp isn't a plausible frame pointer, return the stack pointer instead. + // If we're lucky, it points to the start of a stack frame; otherwise, we'll + // get one frame of garbage in the stack trace and fail the sanity check on + // the next iteration. + return sp; + } +#endif + return 0; +} + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return null if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static void **NextStackFrame(void **old_fp, const void *uc, + size_t stack_low, size_t stack_high) { + void **new_fp = (void **)*old_fp; + +#if defined(__linux__) && defined(__i386__) + if (WITH_CONTEXT && uc != nullptr) { + // How many "push %reg" instructions are there at __kernel_vsyscall? + // This is constant for a given kernel and processor, so compute + // it only once. + static int num_push_instructions = -1; // Sentinel: not computed yet. + // Initialize with sentinel value: __kernel_rt_sigreturn can not possibly + // be there. + static const unsigned char *kernel_rt_sigreturn_address = nullptr; + static const unsigned char *kernel_vsyscall_address = nullptr; + if (num_push_instructions == -1) { +#ifdef ABSL_HAVE_VDSO_SUPPORT + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo + rt_sigreturn_symbol_info; + absl::debugging_internal::VDSOSupport::SymbolInfo vsyscall_symbol_info; + if (!vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.5", STT_FUNC, + &rt_sigreturn_symbol_info) || + !vdso.LookupSymbol("__kernel_vsyscall", "LINUX_2.5", STT_FUNC, + &vsyscall_symbol_info) || + rt_sigreturn_symbol_info.address == nullptr || + vsyscall_symbol_info.address == nullptr) { + // Unexpected: 32-bit VDSO is present, yet one of the expected + // symbols is missing or null. + assert(false && "VDSO is present, but doesn't have expected symbols"); + num_push_instructions = 0; + } else { + kernel_rt_sigreturn_address = + reinterpret_cast( + rt_sigreturn_symbol_info.address); + kernel_vsyscall_address = + reinterpret_cast( + vsyscall_symbol_info.address); + num_push_instructions = + CountPushInstructions(kernel_vsyscall_address); + } + } else { + num_push_instructions = 0; + } +#else // ABSL_HAVE_VDSO_SUPPORT + num_push_instructions = 0; +#endif // ABSL_HAVE_VDSO_SUPPORT + } + if (num_push_instructions != 0 && kernel_rt_sigreturn_address != nullptr && + old_fp[1] == kernel_rt_sigreturn_address) { + const ucontext_t *ucv = static_cast(uc); + // This kernel does not use frame pointer in its VDSO code, + // and so %ebp is not suitable for unwinding. + void **const reg_ebp = + reinterpret_cast(ucv->uc_mcontext.gregs[REG_EBP]); + const unsigned char *const reg_eip = + reinterpret_cast(ucv->uc_mcontext.gregs[REG_EIP]); + if (new_fp == reg_ebp && kernel_vsyscall_address <= reg_eip && + reg_eip - kernel_vsyscall_address < kMaxBytes) { + // We "stepped up" to __kernel_vsyscall, but %ebp is not usable. + // Restore from 'ucv' instead. + void **const reg_esp = + reinterpret_cast(ucv->uc_mcontext.gregs[REG_ESP]); + // Check that alleged %esp is not null and is reasonably aligned. + if (reg_esp && + ((uintptr_t)reg_esp & (sizeof(reg_esp) - 1)) == 0) { + // Check that alleged %esp is actually readable. This is to prevent + // "double fault" in case we hit the first fault due to e.g. stack + // corruption. + void *const reg_esp2 = reg_esp[num_push_instructions - 1]; + if (AddressIsReadable(reg_esp2)) { + // Alleged %esp is readable, use it for further unwinding. + new_fp = reinterpret_cast(reg_esp2); + } + } + } + } + } +#endif + + const uintptr_t old_fp_u = reinterpret_cast(old_fp); + const uintptr_t new_fp_u = reinterpret_cast(new_fp); + + // Check that the transition from frame pointer old_fp to frame + // pointer new_fp isn't clearly bogus. Skip the checks if new_fp + // matches the signal context, so that we don't skip out early when + // using an alternate signal stack. + // + // TODO(bcmills): The GetFP call should be completely unnecessary when + // ENABLE_COMBINED_UNWINDER is set (because we should be back in the thread's + // stack by this point), but it is empirically still needed (e.g. when the + // stack includes a call to abort). unw_get_reg returns UNW_EBADREG for some + // frames. Figure out why GetValidFrameAddr and/or libunwind isn't doing what + // it's supposed to. + if (STRICT_UNWINDING && + (!WITH_CONTEXT || uc == nullptr || new_fp_u != GetFP(uc))) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_fp_u <= old_fp_u) return nullptr; + if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr; + + if (stack_low < old_fp_u && old_fp_u <= stack_high) { + // Old BP was in the expected stack region... + if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) { + // ... but new BP is outside of expected stack region. + // It is most likely bogus. + return nullptr; + } + } else { + // We may be here if we are executing in a co-routine with a + // separate stack. We can't do safety checks in this case. + } + } else { + if (new_fp == nullptr) return nullptr; // skip AddressIsReadable() below + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_fp == old_fp) return nullptr; + } + + if (new_fp_u & (sizeof(void *) - 1)) return nullptr; +#ifdef __i386__ + // On 32-bit machines, the stack pointer can be very close to + // 0xffffffff, so we explicitly check for a pointer into the + // last two pages in the address space + if (new_fp_u >= 0xffffe000) return nullptr; +#endif +#if !defined(_WIN32) + if (!STRICT_UNWINDING) { + // Lax sanity checks cause a crash in 32-bit tcmalloc/crash_reason_test + // on AMD-based machines with VDSO-enabled kernels. + // Make an extra sanity check to insure new_fp is readable. + // Note: NextStackFrame() is only called while the program + // is already on its last leg, so it's ok to be slow here. + + if (!AddressIsReadable(new_fp)) { + return nullptr; + } + } +#endif + return new_fp; +} + +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +ABSL_ATTRIBUTE_NOINLINE +static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + int n = 0; + void **fp = reinterpret_cast(__builtin_frame_address(0)); + + size_t stack_low = getpagesize(); // Assume that the first page is not stack. + size_t stack_high = std::numeric_limits::max() - sizeof(void *); + + while (fp && n < max_depth) { + if (*(fp + 1) == reinterpret_cast(0)) { + // In 64-bit code, we often see a frame that + // points to itself and has a return address of 0. + break; + } + void **next_fp = NextStackFrame( + fp, ucp, stack_low, stack_high); + if (skip_count > 0) { + skip_count--; + } else { + result[n] = *(fp + 1); + if (IS_STACK_FRAMES) { + if (next_fp > fp) { + sizes[n] = (uintptr_t)next_fp - (uintptr_t)fp; + } else { + // A frame-size of 0 is used to indicate unknown frame size. + sizes[n] = 0; + } + } + n++; + } + fp = next_fp; + } + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 1000; + int num_dropped_frames = 0; + for (int j = 0; fp != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } + fp = NextStackFrame(fp, ucp, stack_low, + stack_high); + } + *min_dropped_frames = num_dropped_frames; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return true; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_ diff --git a/src/absl/debugging/internal/symbolize.h b/src/absl/debugging/internal/symbolize.h new file mode 100644 index 0000000..27d5e65 --- /dev/null +++ b/src/absl/debugging/internal/symbolize.h @@ -0,0 +1,153 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains internal parts of the Abseil symbolizer. +// Do not depend on the anything in this file, it may change at anytime. + +#ifndef ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ +#define ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ + +#ifdef __cplusplus + +#include +#include + +#include "absl/base/config.h" +#include "absl/strings/string_view.h" + +#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE +#error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set +#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) \ + && !defined(__asmjs__) && !defined(__wasm__) +#define ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE 1 + +#include +#include // For ElfW() macro. +#include +#include + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Iterates over all sections, invoking callback on each with the section name +// and the section header. +// +// Returns true on success; otherwise returns false in case of errors. +// +// This is not async-signal-safe. +bool ForEachSection(int fd, + const std::function& callback); + +// Gets the section header for the given name, if it exists. Returns true on +// success. Otherwise, returns false. +bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + ElfW(Shdr) *out); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE + +#ifdef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE +#error ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE cannot be directly set +#elif defined(__APPLE__) +#define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1 +#endif + +#ifdef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE +#error ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE cannot be directly set +#elif defined(__EMSCRIPTEN__) +#define ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE 1 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +struct SymbolDecoratorArgs { + // The program counter we are getting symbolic name for. + const void *pc; + // 0 for main executable, load address for shared libraries. + ptrdiff_t relocation; + // Read-only file descriptor for ELF image covering "pc", + // or -1 if no such ELF image exists in /proc/self/maps. + int fd; + // Output buffer, size. + // Note: the buffer may not be empty -- default symbolizer may have already + // produced some output, and earlier decorators may have adorned it in + // some way. You are free to replace or augment the contents (within the + // symbol_buf_size limit). + char *const symbol_buf; + size_t symbol_buf_size; + // Temporary scratch space, size. + // Use that space in preference to allocating your own stack buffer to + // conserve stack. + char *const tmp_buf; + size_t tmp_buf_size; + // User-provided argument + void* arg; +}; +using SymbolDecorator = void (*)(const SymbolDecoratorArgs *); + +// Installs a function-pointer as a decorator. Returns a value less than zero +// if the system cannot install the decorator. Otherwise, returns a unique +// identifier corresponding to the decorator. This identifier can be used to +// uninstall the decorator - See RemoveSymbolDecorator() below. +int InstallSymbolDecorator(SymbolDecorator decorator, void* arg); + +// Removes a previously installed function-pointer decorator. Parameter "ticket" +// is the return-value from calling InstallSymbolDecorator(). +bool RemoveSymbolDecorator(int ticket); + +// Remove all installed decorators. Returns true if successful, false if +// symbolization is currently in progress. +bool RemoveAllSymbolDecorators(void); + +// Registers an address range to a file mapping. +// +// Preconditions: +// start <= end +// filename != nullptr +// +// Returns true if the file was successfully registered. +bool RegisterFileMappingHint(const void* start, const void* end, + uint64_t offset, const char* filename); + +// Looks up the file mapping registered by RegisterFileMappingHint for an +// address range. If there is one, the file name is stored in *filename and +// *start and *end are modified to reflect the registered mapping. Returns +// whether any hint was found. +bool GetFileMappingHint(const void** start, const void** end, uint64_t* offset, + const char** filename); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // __cplusplus + +#include + +#ifdef __cplusplus +extern "C" +#endif // __cplusplus + + bool + AbslInternalGetFileMappingHint(const void** start, const void** end, + uint64_t* offset, const char** filename); + +#endif // ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ diff --git a/src/absl/debugging/internal/vdso_support.cc b/src/absl/debugging/internal/vdso_support.cc new file mode 100644 index 0000000..40eb055 --- /dev/null +++ b/src/absl/debugging/internal/vdso_support.cc @@ -0,0 +1,204 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Allow dynamic symbol lookup in the kernel VDSO page. +// +// VDSOSupport -- a class representing kernel VDSO (if present). + +#include "absl/debugging/internal/vdso_support.h" + +#ifdef ABSL_HAVE_VDSO_SUPPORT // defined in vdso_support.h + +#if !defined(__has_include) +#define __has_include(header) 0 +#endif + +#include +#include +#if __has_include() +#include +#elif __has_include() +#include +#endif +#include + +#if !defined(__UCLIBC__) && defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)) +#define ABSL_HAVE_GETAUXVAL +#endif + +#ifdef ABSL_HAVE_GETAUXVAL +#include +#endif + +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/port.h" + +#ifndef AT_SYSINFO_EHDR +#define AT_SYSINFO_EHDR 33 // for crosstoolv10 +#endif + +#if defined(__NetBSD__) +using Elf32_auxv_t = Aux32Info; +using Elf64_auxv_t = Aux64Info; +#endif +#if defined(__FreeBSD__) +#if defined(__ELF_WORD_SIZE) && __ELF_WORD_SIZE == 64 +using Elf64_auxv_t = Elf64_Auxinfo; +#endif +using Elf32_auxv_t = Elf32_Auxinfo; +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +ABSL_CONST_INIT +std::atomic VDSOSupport::vdso_base_( + debugging_internal::ElfMemImage::kInvalidBase); + +ABSL_CONST_INIT std::atomic VDSOSupport::getcpu_fn_( + &InitAndGetCPU); + +VDSOSupport::VDSOSupport() + // If vdso_base_ is still set to kInvalidBase, we got here + // before VDSOSupport::Init has been called. Call it now. + : image_(vdso_base_.load(std::memory_order_relaxed) == + debugging_internal::ElfMemImage::kInvalidBase + ? Init() + : vdso_base_.load(std::memory_order_relaxed)) {} + +// NOTE: we can't use GoogleOnceInit() below, because we can be +// called by tcmalloc, and none of the *once* stuff may be functional yet. +// +// In addition, we hope that the VDSOSupportHelper constructor +// causes this code to run before there are any threads, and before +// InitGoogle() has executed any chroot or setuid calls. +// +// Finally, even if there is a race here, it is harmless, because +// the operation should be idempotent. +const void *VDSOSupport::Init() { + const auto kInvalidBase = debugging_internal::ElfMemImage::kInvalidBase; +#ifdef ABSL_HAVE_GETAUXVAL + if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) { + errno = 0; + const void *const sysinfo_ehdr = + reinterpret_cast(getauxval(AT_SYSINFO_EHDR)); + if (errno == 0) { + vdso_base_.store(sysinfo_ehdr, std::memory_order_relaxed); + } + } +#endif // ABSL_HAVE_GETAUXVAL + if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) { + int fd = open("/proc/self/auxv", O_RDONLY); + if (fd == -1) { + // Kernel too old to have a VDSO. + vdso_base_.store(nullptr, std::memory_order_relaxed); + getcpu_fn_.store(&GetCPUViaSyscall, std::memory_order_relaxed); + return nullptr; + } + ElfW(auxv_t) aux; + while (read(fd, &aux, sizeof(aux)) == sizeof(aux)) { + if (aux.a_type == AT_SYSINFO_EHDR) { +#if defined(__NetBSD__) + vdso_base_.store(reinterpret_cast(aux.a_v), + std::memory_order_relaxed); +#else + vdso_base_.store(reinterpret_cast(aux.a_un.a_val), + std::memory_order_relaxed); +#endif + break; + } + } + close(fd); + if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) { + // Didn't find AT_SYSINFO_EHDR in auxv[]. + vdso_base_.store(nullptr, std::memory_order_relaxed); + } + } + GetCpuFn fn = &GetCPUViaSyscall; // default if VDSO not present. + if (vdso_base_.load(std::memory_order_relaxed)) { + VDSOSupport vdso; + SymbolInfo info; + if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) { + fn = reinterpret_cast(const_cast(info.address)); + } + } + // Subtle: this code runs outside of any locks; prevent compiler + // from assigning to getcpu_fn_ more than once. + getcpu_fn_.store(fn, std::memory_order_relaxed); + return vdso_base_.load(std::memory_order_relaxed); +} + +const void *VDSOSupport::SetBase(const void *base) { + ABSL_RAW_CHECK(base != debugging_internal::ElfMemImage::kInvalidBase, + "internal error"); + const void *old_base = vdso_base_.load(std::memory_order_relaxed); + vdso_base_.store(base, std::memory_order_relaxed); + image_.Init(base); + // Also reset getcpu_fn_, so GetCPU could be tested with simulated VDSO. + getcpu_fn_.store(&InitAndGetCPU, std::memory_order_relaxed); + return old_base; +} + +bool VDSOSupport::LookupSymbol(const char *name, + const char *version, + int type, + SymbolInfo *info) const { + return image_.LookupSymbol(name, version, type, info); +} + +bool VDSOSupport::LookupSymbolByAddress(const void *address, + SymbolInfo *info_out) const { + return image_.LookupSymbolByAddress(address, info_out); +} + +// NOLINT on 'long' because this routine mimics kernel api. +long VDSOSupport::GetCPUViaSyscall(unsigned *cpu, // NOLINT(runtime/int) + void *, void *) { +#ifdef SYS_getcpu + return syscall(SYS_getcpu, cpu, nullptr, nullptr); +#else + // x86_64 never implemented sys_getcpu(), except as a VDSO call. + static_cast(cpu); // Avoid an unused argument compiler warning. + errno = ENOSYS; + return -1; +#endif +} + +// Use fast __vdso_getcpu if available. +long VDSOSupport::InitAndGetCPU(unsigned *cpu, // NOLINT(runtime/int) + void *x, void *y) { + Init(); + GetCpuFn fn = getcpu_fn_.load(std::memory_order_relaxed); + ABSL_RAW_CHECK(fn != &InitAndGetCPU, "Init() did not set getcpu_fn_"); + return (*fn)(cpu, x, y); +} + +// This function must be very fast, and may be called from very +// low level (e.g. tcmalloc). Hence I avoid things like +// GoogleOnceInit() and ::operator new. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY +int GetCPU() { + unsigned cpu; + int ret_code = (*VDSOSupport::getcpu_fn_)(&cpu, nullptr, nullptr); + return ret_code == 0 ? cpu : ret_code; +} + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HAVE_VDSO_SUPPORT diff --git a/src/absl/debugging/internal/vdso_support.h b/src/absl/debugging/internal/vdso_support.h new file mode 100644 index 0000000..6562c6c --- /dev/null +++ b/src/absl/debugging/internal/vdso_support.h @@ -0,0 +1,158 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Allow dynamic symbol lookup in the kernel VDSO page. +// +// VDSO stands for "Virtual Dynamic Shared Object" -- a page of +// executable code, which looks like a shared library, but doesn't +// necessarily exist anywhere on disk, and which gets mmap()ed into +// every process by kernels which support VDSO, such as 2.6.x for 32-bit +// executables, and 2.6.24 and above for 64-bit executables. +// +// More details could be found here: +// http://www.trilithium.com/johan/2005/08/linux-gate/ +// +// VDSOSupport -- a class representing kernel VDSO (if present). +// +// Example usage: +// VDSOSupport vdso; +// VDSOSupport::SymbolInfo info; +// typedef (*FN)(unsigned *, void *, void *); +// FN fn = nullptr; +// if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) { +// fn = reinterpret_cast(info.address); +// } + +#ifndef ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ +#define ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/debugging/internal/elf_mem_image.h" + +#ifdef ABSL_HAVE_ELF_MEM_IMAGE + +#ifdef ABSL_HAVE_VDSO_SUPPORT +#error ABSL_HAVE_VDSO_SUPPORT cannot be directly set +#else +#define ABSL_HAVE_VDSO_SUPPORT 1 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// NOTE: this class may be used from within tcmalloc, and can not +// use any memory allocation routines. +class VDSOSupport { + public: + VDSOSupport(); + + typedef ElfMemImage::SymbolInfo SymbolInfo; + typedef ElfMemImage::SymbolIterator SymbolIterator; + + // On PowerPC64 VDSO symbols can either be of type STT_FUNC or STT_NOTYPE + // depending on how the kernel is built. The kernel is normally built with + // STT_NOTYPE type VDSO symbols. Let's make things simpler first by using a + // compile-time constant. +#ifdef __powerpc64__ + enum { kVDSOSymbolType = STT_NOTYPE }; +#else + enum { kVDSOSymbolType = STT_FUNC }; +#endif + + // Answers whether we have a vdso at all. + bool IsPresent() const { return image_.IsPresent(); } + + // Allow to iterate over all VDSO symbols. + SymbolIterator begin() const { return image_.begin(); } + SymbolIterator end() const { return image_.end(); } + + // Look up versioned dynamic symbol in the kernel VDSO. + // Returns false if VDSO is not present, or doesn't contain given + // symbol/version/type combination. + // If info_out != nullptr, additional details are filled in. + bool LookupSymbol(const char *name, const char *version, + int symbol_type, SymbolInfo *info_out) const; + + // Find info about symbol (if any) which overlaps given address. + // Returns true if symbol was found; false if VDSO isn't present + // or doesn't have a symbol overlapping given address. + // If info_out != nullptr, additional details are filled in. + bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const; + + // Used only for testing. Replace real VDSO base with a mock. + // Returns previous value of vdso_base_. After you are done testing, + // you are expected to call SetBase() with previous value, in order to + // reset state to the way it was. + const void *SetBase(const void *s); + + // Computes vdso_base_ and returns it. Should be called as early as + // possible; before any thread creation, chroot or setuid. + static const void *Init(); + + private: + // image_ represents VDSO ELF image in memory. + // image_.ehdr_ == nullptr implies there is no VDSO. + ElfMemImage image_; + + // Cached value of auxv AT_SYSINFO_EHDR, computed once. + // This is a tri-state: + // kInvalidBase => value hasn't been determined yet. + // 0 => there is no VDSO. + // else => vma of VDSO Elf{32,64}_Ehdr. + // + // When testing with mock VDSO, low bit is set. + // The low bit is always available because vdso_base_ is + // page-aligned. + static std::atomic vdso_base_; + + // NOLINT on 'long' because these routines mimic kernel api. + // The 'cache' parameter may be used by some versions of the kernel, + // and should be nullptr or point to a static buffer containing at + // least two 'long's. + static long InitAndGetCPU(unsigned *cpu, void *cache, // NOLINT 'long'. + void *unused); + static long GetCPUViaSyscall(unsigned *cpu, void *cache, // NOLINT 'long'. + void *unused); + typedef long (*GetCpuFn)(unsigned *cpu, void *cache, // NOLINT 'long'. + void *unused); + + // This function pointer may point to InitAndGetCPU, + // GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization. + ABSL_CONST_INIT static std::atomic getcpu_fn_; + + friend int GetCPU(void); // Needs access to getcpu_fn_. + + VDSOSupport(const VDSOSupport&) = delete; + VDSOSupport& operator=(const VDSOSupport&) = delete; +}; + +// Same as sched_getcpu() on later glibc versions. +// Return current CPU, using (fast) __vdso_getcpu@LINUX_2.6 if present, +// otherwise use syscall(SYS_getcpu,...). +// May return -1 with errno == ENOSYS if the kernel doesn't +// support SYS_getcpu. +int GetCPU(); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HAVE_ELF_MEM_IMAGE + +#endif // ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ diff --git a/src/absl/debugging/leak_check.cc b/src/absl/debugging/leak_check.cc new file mode 100644 index 0000000..195e82b --- /dev/null +++ b/src/absl/debugging/leak_check.cc @@ -0,0 +1,73 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Wrappers around lsan_interface functions. +// +// These are always-available run-time functions manipulating the LeakSanitizer, +// even when the lsan_interface (and LeakSanitizer) is not available. When +// LeakSanitizer is not linked in, these functions become no-op stubs. + +#include "absl/debugging/leak_check.h" + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +#if defined(ABSL_HAVE_LEAK_SANITIZER) + +#include + +#if ABSL_HAVE_ATTRIBUTE_WEAK +extern "C" ABSL_ATTRIBUTE_WEAK int __lsan_is_turned_off(); +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +bool HaveLeakSanitizer() { return true; } + +#if ABSL_HAVE_ATTRIBUTE_WEAK +bool LeakCheckerIsActive() { + return !(&__lsan_is_turned_off && __lsan_is_turned_off()); +} +#else +bool LeakCheckerIsActive() { return true; } +#endif + +bool FindAndReportLeaks() { return __lsan_do_recoverable_leak_check(); } +void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); } +void RegisterLivePointers(const void* ptr, size_t size) { + __lsan_register_root_region(ptr, size); +} +void UnRegisterLivePointers(const void* ptr, size_t size) { + __lsan_unregister_root_region(ptr, size); +} +LeakCheckDisabler::LeakCheckDisabler() { __lsan_disable(); } +LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); } +ABSL_NAMESPACE_END +} // namespace absl + +#else // defined(ABSL_HAVE_LEAK_SANITIZER) + +namespace absl { +ABSL_NAMESPACE_BEGIN +bool HaveLeakSanitizer() { return false; } +bool LeakCheckerIsActive() { return false; } +void DoIgnoreLeak(const void*) { } +void RegisterLivePointers(const void*, size_t) { } +void UnRegisterLivePointers(const void*, size_t) { } +LeakCheckDisabler::LeakCheckDisabler() { } +LeakCheckDisabler::~LeakCheckDisabler() { } +ABSL_NAMESPACE_END +} // namespace absl + +#endif // defined(ABSL_HAVE_LEAK_SANITIZER) diff --git a/src/absl/debugging/leak_check.h b/src/absl/debugging/leak_check.h new file mode 100644 index 0000000..eff162f --- /dev/null +++ b/src/absl/debugging/leak_check.h @@ -0,0 +1,150 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: leak_check.h +// ----------------------------------------------------------------------------- +// +// This file contains functions that affect leak checking behavior within +// targets built with the LeakSanitizer (LSan), a memory leak detector that is +// integrated within the AddressSanitizer (ASan) as an additional component, or +// which can be used standalone. LSan and ASan are included (or can be provided) +// as additional components for most compilers such as Clang, gcc and MSVC. +// Note: this leak checking API is not yet supported in MSVC. +// Leak checking is enabled by default in all ASan builds. +// +// https://clang.llvm.org/docs/LeakSanitizer.html +// https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// +// GCC and Clang both automatically enable LeakSanitizer when AddressSanitizer +// is enabled. To use the mode, simply pass `-fsanitize=address` to both the +// compiler and linker. An example Bazel command could be +// +// $ bazel test --copt=-fsanitize=address --linkopt=-fsanitize=address ... +// +// GCC and Clang auto support a standalone LeakSanitizer mode (a mode which does +// not also use AddressSanitizer). To use the mode, simply pass +// `-fsanitize=leak` to both the compiler and linker. Since GCC does not +// currently provide a way of detecting this mode at compile-time, GCC users +// must also pass -DLEAK_SANIITIZER to the compiler. An example Bazel command +// could be +// +// $ bazel test --copt=-DLEAK_SANITIZER --copt=-fsanitize=leak +// --linkopt=-fsanitize=leak ... +// +// ----------------------------------------------------------------------------- +#ifndef ABSL_DEBUGGING_LEAK_CHECK_H_ +#define ABSL_DEBUGGING_LEAK_CHECK_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// HaveLeakSanitizer() +// +// Returns true if a leak-checking sanitizer (either ASan or standalone LSan) is +// currently built into this target. +bool HaveLeakSanitizer(); + +// LeakCheckerIsActive() +// +// Returns true if a leak-checking sanitizer (either ASan or standalone LSan) is +// currently built into this target and is turned on. +bool LeakCheckerIsActive(); + +// DoIgnoreLeak() +// +// Implements `IgnoreLeak()` below. This function should usually +// not be called directly; calling `IgnoreLeak()` is preferred. +void DoIgnoreLeak(const void* ptr); + +// IgnoreLeak() +// +// Instruct the leak sanitizer to ignore leak warnings on the object referenced +// by the passed pointer, as well as all heap objects transitively referenced +// by it. The passed object pointer can point to either the beginning of the +// object or anywhere within it. +// +// Example: +// +// static T* obj = IgnoreLeak(new T(...)); +// +// If the passed `ptr` does not point to an actively allocated object at the +// time `IgnoreLeak()` is called, the call is a no-op; if it is actively +// allocated, leak sanitizer will assume this object is referenced even if +// there is no actual reference in user memory. +// +template +T* IgnoreLeak(T* ptr) { + DoIgnoreLeak(ptr); + return ptr; +} + +// FindAndReportLeaks() +// +// If any leaks are detected, prints a leak report and returns true. This +// function may be called repeatedly, and does not affect end-of-process leak +// checking. +// +// Example: +// if (FindAndReportLeaks()) { +// ... diagnostic already printed. Exit with failure code. +// exit(1) +// } +bool FindAndReportLeaks(); + +// LeakCheckDisabler +// +// This helper class indicates that any heap allocations done in the code block +// covered by the scoped object, which should be allocated on the stack, will +// not be reported as leaks. Leak check disabling will occur within the code +// block and any nested function calls within the code block. +// +// Example: +// +// void Foo() { +// LeakCheckDisabler disabler; +// ... code that allocates objects whose leaks should be ignored ... +// } +// +// REQUIRES: Destructor runs in same thread as constructor +class LeakCheckDisabler { + public: + LeakCheckDisabler(); + LeakCheckDisabler(const LeakCheckDisabler&) = delete; + LeakCheckDisabler& operator=(const LeakCheckDisabler&) = delete; + ~LeakCheckDisabler(); +}; + +// RegisterLivePointers() +// +// Registers `ptr[0,size-1]` as pointers to memory that is still actively being +// referenced and for which leak checking should be ignored. This function is +// useful if you store pointers in mapped memory, for memory ranges that we know +// are correct but for which normal analysis would flag as leaked code. +void RegisterLivePointers(const void* ptr, size_t size); + +// UnRegisterLivePointers() +// +// Deregisters the pointers previously marked as active in +// `RegisterLivePointers()`, enabling leak checking of those pointers. +void UnRegisterLivePointers(const void* ptr, size_t size); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_LEAK_CHECK_H_ diff --git a/src/absl/debugging/stacktrace.cc b/src/absl/debugging/stacktrace.cc new file mode 100644 index 0000000..ff8069f --- /dev/null +++ b/src/absl/debugging/stacktrace.cc @@ -0,0 +1,142 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Produce stack trace. +// +// There are three different ways we can try to get the stack trace: +// +// 1) Our hand-coded stack-unwinder. This depends on a certain stack +// layout, which is used by gcc (and those systems using a +// gcc-compatible ABI) on x86 systems, at least since gcc 2.95. +// It uses the frame pointer to do its work. +// +// 2) The libunwind library. This is still in development, and as a +// separate library adds a new dependency, but doesn't need a frame +// pointer. It also doesn't call malloc. +// +// 3) The gdb unwinder -- also the one used by the c++ exception code. +// It's obviously well-tested, but has a fatal flaw: it can call +// malloc() from the unwinder. This is a problem because we're +// trying to use the unwinder to instrument malloc(). +// +// Note: if you add a new implementation here, make sure it works +// correctly when absl::GetStackTrace() is called with max_depth == 0. +// Some code may do that. + +#include "absl/debugging/stacktrace.h" + +#include + +#include "absl/base/attributes.h" +#include "absl/base/port.h" +#include "absl/debugging/internal/stacktrace_config.h" + +#if defined(ABSL_STACKTRACE_INL_HEADER) +#include ABSL_STACKTRACE_INL_HEADER +#else +# error Cannot calculate stack trace: will need to write for your environment + +# include "absl/debugging/internal/stacktrace_aarch64-inl.inc" +# include "absl/debugging/internal/stacktrace_arm-inl.inc" +# include "absl/debugging/internal/stacktrace_emscripten-inl.inc" +# include "absl/debugging/internal/stacktrace_generic-inl.inc" +# include "absl/debugging/internal/stacktrace_powerpc-inl.inc" +# include "absl/debugging/internal/stacktrace_riscv-inl.inc" +# include "absl/debugging/internal/stacktrace_unimplemented-inl.inc" +# include "absl/debugging/internal/stacktrace_win32-inl.inc" +# include "absl/debugging/internal/stacktrace_x86-inl.inc" +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +typedef int (*Unwinder)(void**, int*, int, int, const void*, int*); +std::atomic custom; + +template +ABSL_ATTRIBUTE_ALWAYS_INLINE inline int Unwind(void** result, int* sizes, + int max_depth, int skip_count, + const void* uc, + int* min_dropped_frames) { + Unwinder f = &UnwindImpl; + Unwinder g = custom.load(std::memory_order_acquire); + if (g != nullptr) f = g; + + // Add 1 to skip count for the unwinder function itself + int size = (*f)(result, sizes, max_depth, skip_count + 1, uc, + min_dropped_frames); + // To disable tail call to (*f)(...) + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); + return size; +} + +} // anonymous namespace + +ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackFrames( + void** result, int* sizes, int max_depth, int skip_count) { + return Unwind(result, sizes, max_depth, skip_count, nullptr, + nullptr); +} + +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(result, sizes, max_depth, skip_count, uc, + min_dropped_frames); +} + +ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackTrace( + void** result, int max_depth, int skip_count) { + return Unwind(result, nullptr, max_depth, skip_count, nullptr, + nullptr); +} + +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(result, nullptr, max_depth, skip_count, uc, + min_dropped_frames); +} + +void SetStackUnwinder(Unwinder w) { + custom.store(w, std::memory_order_release); +} + +int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip, + const void* uc, int* min_dropped_frames) { + skip++; // For this function + Unwinder f = nullptr; + if (sizes == nullptr) { + if (uc == nullptr) { + f = &UnwindImpl; + } else { + f = &UnwindImpl; + } + } else { + if (uc == nullptr) { + f = &UnwindImpl; + } else { + f = &UnwindImpl; + } + } + volatile int x = 0; + int n = (*f)(pcs, sizes, depth, skip, uc, min_dropped_frames); + x = 1; (void) x; // To disable tail call to (*f)(...) + return n; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/debugging/stacktrace.h b/src/absl/debugging/stacktrace.h new file mode 100644 index 0000000..0ec0ffd --- /dev/null +++ b/src/absl/debugging/stacktrace.h @@ -0,0 +1,231 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: stacktrace.h +// ----------------------------------------------------------------------------- +// +// This file contains routines to extract the current stack trace and associated +// stack frames. These functions are thread-safe and async-signal-safe. +// +// Note that stack trace functionality is platform dependent and requires +// additional support from the compiler/build system in most cases. (That is, +// this functionality generally only works on platforms/builds that have been +// specifically configured to support it.) +// +// Note: stack traces in Abseil that do not utilize a symbolizer will result in +// frames consisting of function addresses rather than human-readable function +// names. (See symbolize.h for information on symbolizing these values.) + +#ifndef ABSL_DEBUGGING_STACKTRACE_H_ +#define ABSL_DEBUGGING_STACKTRACE_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// GetStackFrames() +// +// Records program counter values for up to `max_depth` frames, skipping the +// most recent `skip_count` stack frames, stores their corresponding values +// and sizes in `results` and `sizes` buffers, and returns the number of frames +// stored. (Note that the frame generated for the `absl::GetStackFrames()` +// routine itself is also skipped.) +// +// Example: +// +// main() { foo(); } +// foo() { bar(); } +// bar() { +// void* result[10]; +// int sizes[10]; +// int depth = absl::GetStackFrames(result, sizes, 10, 1); +// } +// +// The current stack frame would consist of three function calls: `bar()`, +// `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets +// `skip_count` to `1`, it will skip the frame for `bar()`, the most recently +// invoked function call. It will therefore return 2 and fill `result` with +// program counters within the following functions: +// +// result[0] foo() +// result[1] main() +// +// (Note: in practice, a few more entries after `main()` may be added to account +// for startup processes.) +// +// Corresponding stack frame sizes will also be recorded: +// +// sizes[0] 16 +// sizes[1] 16 +// +// (Stack frame sizes of `16` above are just for illustration purposes.) +// +// Stack frame sizes of 0 or less indicate that those frame sizes couldn't +// be identified. +// +// This routine may return fewer stack frame entries than are +// available. Also note that `result` and `sizes` must both be non-null. +extern int GetStackFrames(void** result, int* sizes, int max_depth, + int skip_count); + +// GetStackFramesWithContext() +// +// Records program counter values obtained from a signal handler. Records +// program counter values for up to `max_depth` frames, skipping the most recent +// `skip_count` stack frames, stores their corresponding values and sizes in +// `results` and `sizes` buffers, and returns the number of frames stored. (Note +// that the frame generated for the `absl::GetStackFramesWithContext()` routine +// itself is also skipped.) +// +// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value +// passed to a signal handler registered via the `sa_sigaction` field of a +// `sigaction` struct. (See +// http://man7.org/linux/man-pages/man2/sigaction.2.html.) The `uc` value may +// help a stack unwinder to provide a better stack trace under certain +// conditions. `uc` may safely be null. +// +// The `min_dropped_frames` output parameter, if non-null, points to the +// location to note any dropped stack frames, if any, due to buffer limitations +// or other reasons. (This value will be set to `0` if no frames were dropped.) +// The number of total stack frames is guaranteed to be >= skip_count + +// max_depth + *min_dropped_frames. +extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth, + int skip_count, const void* uc, + int* min_dropped_frames); + +// GetStackTrace() +// +// Records program counter values for up to `max_depth` frames, skipping the +// most recent `skip_count` stack frames, stores their corresponding values +// in `results`, and returns the number of frames +// stored. Note that this function is similar to `absl::GetStackFrames()` +// except that it returns the stack trace only, and not stack frame sizes. +// +// Example: +// +// main() { foo(); } +// foo() { bar(); } +// bar() { +// void* result[10]; +// int depth = absl::GetStackTrace(result, 10, 1); +// } +// +// This produces: +// +// result[0] foo +// result[1] main +// .... ... +// +// `result` must not be null. +extern int GetStackTrace(void** result, int max_depth, int skip_count); + +// GetStackTraceWithContext() +// +// Records program counter values obtained from a signal handler. Records +// program counter values for up to `max_depth` frames, skipping the most recent +// `skip_count` stack frames, stores their corresponding values in `results`, +// and returns the number of frames stored. (Note that the frame generated for +// the `absl::GetStackFramesWithContext()` routine itself is also skipped.) +// +// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value +// passed to a signal handler registered via the `sa_sigaction` field of a +// `sigaction` struct. (See +// http://man7.org/linux/man-pages/man2/sigaction.2.html.) The `uc` value may +// help a stack unwinder to provide a better stack trace under certain +// conditions. `uc` may safely be null. +// +// The `min_dropped_frames` output parameter, if non-null, points to the +// location to note any dropped stack frames, if any, due to buffer limitations +// or other reasons. (This value will be set to `0` if no frames were dropped.) +// The number of total stack frames is guaranteed to be >= skip_count + +// max_depth + *min_dropped_frames. +extern int GetStackTraceWithContext(void** result, int max_depth, + int skip_count, const void* uc, + int* min_dropped_frames); + +// SetStackUnwinder() +// +// Provides a custom function for unwinding stack frames that will be used in +// place of the default stack unwinder when invoking the static +// GetStack{Frames,Trace}{,WithContext}() functions above. +// +// The arguments passed to the unwinder function will match the +// arguments passed to `absl::GetStackFramesWithContext()` except that sizes +// will be non-null iff the caller is interested in frame sizes. +// +// If unwinder is set to null, we revert to the default stack-tracing behavior. +// +// ***************************************************************************** +// WARNING +// ***************************************************************************** +// +// absl::SetStackUnwinder is not suitable for general purpose use. It is +// provided for custom runtimes. +// Some things to watch out for when calling `absl::SetStackUnwinder()`: +// +// (a) The unwinder may be called from within signal handlers and +// therefore must be async-signal-safe. +// +// (b) Even after a custom stack unwinder has been unregistered, other +// threads may still be in the process of using that unwinder. +// Therefore do not clean up any state that may be needed by an old +// unwinder. +// ***************************************************************************** +extern void SetStackUnwinder(int (*unwinder)(void** pcs, int* sizes, + int max_depth, int skip_count, + const void* uc, + int* min_dropped_frames)); + +// DefaultStackUnwinder() +// +// Records program counter values of up to `max_depth` frames, skipping the most +// recent `skip_count` stack frames, and stores their corresponding values in +// `pcs`. (Note that the frame generated for this call itself is also skipped.) +// This function acts as a generic stack-unwinder; prefer usage of the more +// specific `GetStack{Trace,Frames}{,WithContext}()` functions above. +// +// If you have set your own stack unwinder (with the `SetStackUnwinder()` +// function above, you can still get the default stack unwinder by calling +// `DefaultStackUnwinder()`, which will ignore any previously set stack unwinder +// and use the default one instead. +// +// Because this function is generic, only `pcs` is guaranteed to be non-null +// upon return. It is legal for `sizes`, `uc`, and `min_dropped_frames` to all +// be null when called. +// +// The semantics are the same as the corresponding `GetStack*()` function in the +// case where `absl::SetStackUnwinder()` was never called. Equivalents are: +// +// null sizes | non-nullptr sizes +// |==========================================================| +// null uc | GetStackTrace() | GetStackFrames() | +// non-null uc | GetStackTraceWithContext() | GetStackFramesWithContext() | +// |==========================================================| +extern int DefaultStackUnwinder(void** pcs, int* sizes, int max_depth, + int skip_count, const void* uc, + int* min_dropped_frames); + +namespace debugging_internal { +// Returns true for platforms which are expected to have functioning stack trace +// implementations. Intended to be used for tests which want to exclude +// verification of logic known to be broken because stack traces are not +// working. +extern bool StackTraceWorksForTest(); +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_STACKTRACE_H_ diff --git a/src/absl/debugging/symbolize.cc b/src/absl/debugging/symbolize.cc new file mode 100644 index 0000000..de4c276 --- /dev/null +++ b/src/absl/debugging/symbolize.cc @@ -0,0 +1,17 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/debugging/symbolize.h" + +#include "absl/debugging/symbolize_unimplemented.inc" diff --git a/src/absl/debugging/symbolize.h b/src/absl/debugging/symbolize.h new file mode 100644 index 0000000..43d93a8 --- /dev/null +++ b/src/absl/debugging/symbolize.h @@ -0,0 +1,99 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: symbolize.h +// ----------------------------------------------------------------------------- +// +// This file configures the Abseil symbolizer for use in converting instruction +// pointer addresses (program counters) into human-readable names (function +// calls, etc.) within Abseil code. +// +// The symbolizer may be invoked from several sources: +// +// * Implicitly, through the installation of an Abseil failure signal handler. +// (See failure_signal_handler.h for more information.) +// * By calling `Symbolize()` directly on a program counter you obtain through +// `absl::GetStackTrace()` or `absl::GetStackFrames()`. (See stacktrace.h +// for more information. +// * By calling `Symbolize()` directly on a program counter you obtain through +// other means (which would be platform-dependent). +// +// In all of the above cases, the symbolizer must first be initialized before +// any program counter values can be symbolized. If you are installing a failure +// signal handler, initialize the symbolizer before you do so. +// +// Example: +// +// int main(int argc, char** argv) { +// // Initialize the Symbolizer before installing the failure signal handler +// absl::InitializeSymbolizer(argv[0]); +// +// // Now you may install the failure signal handler +// absl::FailureSignalHandlerOptions options; +// absl::InstallFailureSignalHandler(options); +// +// // Start running your main program +// ... +// return 0; +// } +// +#ifndef ABSL_DEBUGGING_SYMBOLIZE_H_ +#define ABSL_DEBUGGING_SYMBOLIZE_H_ + +#include "absl/debugging/internal/symbolize.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// InitializeSymbolizer() +// +// Initializes the program counter symbolizer, given the path of the program +// (typically obtained through `main()`s `argv[0]`). The Abseil symbolizer +// allows you to read program counters (instruction pointer values) using their +// human-readable names within output such as stack traces. +// +// Example: +// +// int main(int argc, char *argv[]) { +// absl::InitializeSymbolizer(argv[0]); +// // Now you can use the symbolizer +// } +void InitializeSymbolizer(const char* argv0); +// +// Symbolize() +// +// Symbolizes a program counter (instruction pointer value) `pc` and, on +// success, writes the name to `out`. The symbol name is demangled, if possible. +// Note that the symbolized name may be truncated and will be NUL-terminated. +// Demangling is supported for symbols generated by GCC 3.x or newer). Returns +// `false` on failure. +// +// Example: +// +// // Print a program counter and its symbol name. +// static void DumpPCAndSymbol(void *pc) { +// char tmp[1024]; +// const char *symbol = "(unknown)"; +// if (absl::Symbolize(pc, tmp, sizeof(tmp))) { +// symbol = tmp; +// } +// absl::PrintF("%p %s\n", pc, symbol); +// } +bool Symbolize(const void *pc, char *out, int out_size); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_SYMBOLIZE_H_ diff --git a/src/absl/debugging/symbolize_darwin.inc b/src/absl/debugging/symbolize_darwin.inc new file mode 100644 index 0000000..443ce9e --- /dev/null +++ b/src/absl/debugging/symbolize_darwin.inc @@ -0,0 +1,101 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include + +#include "absl/base/internal/raw_logging.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +void InitializeSymbolizer(const char*) {} + +namespace debugging_internal { +namespace { + +static std::string GetSymbolString(absl::string_view backtrace_line) { + // Example Backtrace lines: + // 0 libimaging_shared.dylib 0x018c152a + // _ZNSt11_Deque_baseIN3nik7mediadb4PageESaIS2_EE17_M_initialize_mapEm + 3478 + // + // or + // 0 libimaging_shared.dylib 0x0000000001895c39 + // _ZN3nik4util19register_shared_ptrINS_3gpu7TextureEEEvPKvS5_ + 39 + // + // or + // 0 mysterious_app 0x0124000120120009 main + 17 + auto address_pos = backtrace_line.find(" 0x"); + if (address_pos == absl::string_view::npos) return std::string(); + absl::string_view symbol_view = backtrace_line.substr(address_pos + 1); + + auto space_pos = symbol_view.find(" "); + if (space_pos == absl::string_view::npos) return std::string(); + symbol_view = symbol_view.substr(space_pos + 1); // to mangled symbol + + auto plus_pos = symbol_view.find(" + "); + if (plus_pos == absl::string_view::npos) return std::string(); + symbol_view = symbol_view.substr(0, plus_pos); // strip remainng + + return std::string(symbol_view); +} + +} // namespace +} // namespace debugging_internal + +bool Symbolize(const void* pc, char* out, int out_size) { + if (out_size <= 0 || pc == nullptr) { + out = nullptr; + return false; + } + + // This allocates a char* array. + char** frame_strings = backtrace_symbols(const_cast(&pc), 1); + + if (frame_strings == nullptr) return false; + + std::string symbol = debugging_internal::GetSymbolString(frame_strings[0]); + free(frame_strings); + + char tmp_buf[1024]; + if (debugging_internal::Demangle(symbol.c_str(), tmp_buf, sizeof(tmp_buf))) { + size_t len = strlen(tmp_buf); + if (len + 1 <= static_cast(out_size)) { // +1 for '\0' + assert(len < sizeof(tmp_buf)); + memmove(out, tmp_buf, len + 1); + } + } else { + strncpy(out, symbol.c_str(), out_size); + } + + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = std::min(sizeof(kEllipsis) - 1, out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + + return true; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/debugging/symbolize_elf.inc b/src/absl/debugging/symbolize_elf.inc new file mode 100644 index 0000000..f453272 --- /dev/null +++ b/src/absl/debugging/symbolize_elf.inc @@ -0,0 +1,1613 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This library provides Symbolize() function that symbolizes program +// counters to their corresponding symbol names on linux platforms. +// This library has a minimal implementation of an ELF symbol table +// reader (i.e. it doesn't depend on libelf, etc.). +// +// The algorithm used in Symbolize() is as follows. +// +// 1. Go through a list of maps in /proc/self/maps and find the map +// containing the program counter. +// +// 2. Open the mapped file and find a regular symbol table inside. +// Iterate over symbols in the symbol table and look for the symbol +// containing the program counter. If such a symbol is found, +// obtain the symbol name, and demangle the symbol if possible. +// If the symbol isn't found in the regular symbol table (binary is +// stripped), try the same thing with a dynamic symbol table. +// +// Note that Symbolize() is originally implemented to be used in +// signal handlers, hence it doesn't use malloc() and other unsafe +// operations. It should be both thread-safe and async-signal-safe. +// +// Implementation note: +// +// We don't use heaps but only use stacks. We want to reduce the +// stack consumption so that the symbolizer can run on small stacks. +// +// Here are some numbers collected with GCC 4.1.0 on x86: +// - sizeof(Elf32_Sym) = 16 +// - sizeof(Elf32_Shdr) = 40 +// - sizeof(Elf64_Sym) = 24 +// - sizeof(Elf64_Shdr) = 64 +// +// This implementation is intended to be async-signal-safe but uses some +// functions which are not guaranteed to be so, such as memchr() and +// memmove(). We assume they are async-signal-safe. + +#include +#include +#include +#include // For ElfW() macro. +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/casts.h" +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/low_level_alloc.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" +#include "absl/base/port.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/debugging/internal/vdso_support.h" +#include "absl/strings/string_view.h" + +#if defined(__FreeBSD__) && !defined(ElfW) +#define ElfW(x) __ElfN(x) +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Value of argv[0]. Used by MaybeInitializeObjFile(). +static char *argv0_value = nullptr; + +void InitializeSymbolizer(const char *argv0) { +#ifdef ABSL_HAVE_VDSO_SUPPORT + // We need to make sure VDSOSupport::Init() is called before any setuid or + // chroot calls, so InitializeSymbolizer() should be called very early in the + // life of a program. + absl::debugging_internal::VDSOSupport::Init(); +#endif + if (argv0_value != nullptr) { + free(argv0_value); + argv0_value = nullptr; + } + if (argv0 != nullptr && argv0[0] != '\0') { + argv0_value = strdup(argv0); + } +} + +namespace debugging_internal { +namespace { + +// Re-runs fn until it doesn't cause EINTR. +#define NO_INTR(fn) \ + do { \ + } while ((fn) < 0 && errno == EINTR) + +// On Linux, ELF_ST_* are defined in . To make this portable +// we define our own ELF_ST_BIND and ELF_ST_TYPE if not available. +#ifndef ELF_ST_BIND +#define ELF_ST_BIND(info) (((unsigned char)(info)) >> 4) +#endif + +#ifndef ELF_ST_TYPE +#define ELF_ST_TYPE(info) (((unsigned char)(info)) & 0xF) +#endif + +// Some platforms use a special .opd section to store function pointers. +const char kOpdSectionName[] = ".opd"; + +#if (defined(__powerpc__) && !(_CALL_ELF > 1)) || defined(__ia64) +// Use opd section for function descriptors on these platforms, the function +// address is the first word of the descriptor. +enum { kPlatformUsesOPDSections = 1 }; +#else // not PPC or IA64 +enum { kPlatformUsesOPDSections = 0 }; +#endif + +// This works for PowerPC & IA64 only. A function descriptor consist of two +// pointers and the first one is the function's entry. +const size_t kFunctionDescriptorSize = sizeof(void *) * 2; + +const int kMaxDecorators = 10; // Seems like a reasonable upper limit. + +struct InstalledSymbolDecorator { + SymbolDecorator fn; + void *arg; + int ticket; +}; + +int g_num_decorators; +InstalledSymbolDecorator g_decorators[kMaxDecorators]; + +struct FileMappingHint { + const void *start; + const void *end; + uint64_t offset; + const char *filename; +}; + +// Protects g_decorators. +// We are using SpinLock and not a Mutex here, because we may be called +// from inside Mutex::Lock itself, and it prohibits recursive calls. +// This happens in e.g. base/stacktrace_syscall_unittest. +// Moreover, we are using only TryLock(), if the decorator list +// is being modified (is busy), we skip all decorators, and possibly +// loose some info. Sorry, that's the best we could do. +ABSL_CONST_INIT absl::base_internal::SpinLock g_decorators_mu( + absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); + +const int kMaxFileMappingHints = 8; +int g_num_file_mapping_hints; +FileMappingHint g_file_mapping_hints[kMaxFileMappingHints]; +// Protects g_file_mapping_hints. +ABSL_CONST_INIT absl::base_internal::SpinLock g_file_mapping_mu( + absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); + +// Async-signal-safe function to zero a buffer. +// memset() is not guaranteed to be async-signal-safe. +static void SafeMemZero(void* p, size_t size) { + unsigned char *c = static_cast(p); + while (size--) { + *c++ = 0; + } +} + +struct ObjFile { + ObjFile() + : filename(nullptr), + start_addr(nullptr), + end_addr(nullptr), + offset(0), + fd(-1), + elf_type(-1) { + SafeMemZero(&elf_header, sizeof(elf_header)); + SafeMemZero(&phdr[0], sizeof(phdr)); + } + + char *filename; + const void *start_addr; + const void *end_addr; + uint64_t offset; + + // The following fields are initialized on the first access to the + // object file. + int fd; + int elf_type; + ElfW(Ehdr) elf_header; + + // PT_LOAD program header describing executable code. + // Normally we expect just one, but SWIFT binaries have two. + std::array phdr; +}; + +// Build 4-way associative cache for symbols. Within each cache line, symbols +// are replaced in LRU order. +enum { + ASSOCIATIVITY = 4, +}; +struct SymbolCacheLine { + const void *pc[ASSOCIATIVITY]; + char *name[ASSOCIATIVITY]; + + // age[i] is incremented when a line is accessed. it's reset to zero if the + // i'th entry is read. + uint32_t age[ASSOCIATIVITY]; +}; + +// --------------------------------------------------------------- +// An async-signal-safe arena for LowLevelAlloc +static std::atomic g_sig_safe_arena; + +static base_internal::LowLevelAlloc::Arena *SigSafeArena() { + return g_sig_safe_arena.load(std::memory_order_acquire); +} + +static void InitSigSafeArena() { + if (SigSafeArena() == nullptr) { + base_internal::LowLevelAlloc::Arena *new_arena = + base_internal::LowLevelAlloc::NewArena( + base_internal::LowLevelAlloc::kAsyncSignalSafe); + base_internal::LowLevelAlloc::Arena *old_value = nullptr; + if (!g_sig_safe_arena.compare_exchange_strong(old_value, new_arena, + std::memory_order_release, + std::memory_order_relaxed)) { + // We lost a race to allocate an arena; deallocate. + base_internal::LowLevelAlloc::DeleteArena(new_arena); + } + } +} + +// --------------------------------------------------------------- +// An AddrMap is a vector of ObjFile, using SigSafeArena() for allocation. + +class AddrMap { + public: + AddrMap() : size_(0), allocated_(0), obj_(nullptr) {} + ~AddrMap() { base_internal::LowLevelAlloc::Free(obj_); } + int Size() const { return size_; } + ObjFile *At(int i) { return &obj_[i]; } + ObjFile *Add(); + void Clear(); + + private: + int size_; // count of valid elements (<= allocated_) + int allocated_; // count of allocated elements + ObjFile *obj_; // array of allocated_ elements + AddrMap(const AddrMap &) = delete; + AddrMap &operator=(const AddrMap &) = delete; +}; + +void AddrMap::Clear() { + for (int i = 0; i != size_; i++) { + At(i)->~ObjFile(); + } + size_ = 0; +} + +ObjFile *AddrMap::Add() { + if (size_ == allocated_) { + int new_allocated = allocated_ * 2 + 50; + ObjFile *new_obj_ = + static_cast(base_internal::LowLevelAlloc::AllocWithArena( + new_allocated * sizeof(*new_obj_), SigSafeArena())); + if (obj_) { + memcpy(new_obj_, obj_, allocated_ * sizeof(*new_obj_)); + base_internal::LowLevelAlloc::Free(obj_); + } + obj_ = new_obj_; + allocated_ = new_allocated; + } + return new (&obj_[size_++]) ObjFile; +} + +// --------------------------------------------------------------- + +enum FindSymbolResult { SYMBOL_NOT_FOUND = 1, SYMBOL_TRUNCATED, SYMBOL_FOUND }; + +class Symbolizer { + public: + Symbolizer(); + ~Symbolizer(); + const char *GetSymbol(const void *const pc); + + private: + char *CopyString(const char *s) { + int len = strlen(s); + char *dst = static_cast( + base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena())); + ABSL_RAW_CHECK(dst != nullptr, "out of memory"); + memcpy(dst, s, len + 1); + return dst; + } + ObjFile *FindObjFile(const void *const start, + size_t size) ABSL_ATTRIBUTE_NOINLINE; + static bool RegisterObjFile(const char *filename, + const void *const start_addr, + const void *const end_addr, uint64_t offset, + void *arg); + SymbolCacheLine *GetCacheLine(const void *const pc); + const char *FindSymbolInCache(const void *const pc); + const char *InsertSymbolInCache(const void *const pc, const char *name); + void AgeSymbols(SymbolCacheLine *line); + void ClearAddrMap(); + FindSymbolResult GetSymbolFromObjectFile(const ObjFile &obj, + const void *const pc, + const ptrdiff_t relocation, + char *out, int out_size, + char *tmp_buf, int tmp_buf_size); + const char *GetUncachedSymbol(const void *pc); + + enum { + SYMBOL_BUF_SIZE = 3072, + TMP_BUF_SIZE = 1024, + SYMBOL_CACHE_LINES = 128, + }; + + AddrMap addr_map_; + + bool ok_; + bool addr_map_read_; + + char symbol_buf_[SYMBOL_BUF_SIZE]; + + // tmp_buf_ will be used to store arrays of ElfW(Shdr) and ElfW(Sym) + // so we ensure that tmp_buf_ is properly aligned to store either. + alignas(16) char tmp_buf_[TMP_BUF_SIZE]; + static_assert(alignof(ElfW(Shdr)) <= 16, + "alignment of tmp buf too small for Shdr"); + static_assert(alignof(ElfW(Sym)) <= 16, + "alignment of tmp buf too small for Sym"); + + SymbolCacheLine symbol_cache_[SYMBOL_CACHE_LINES]; +}; + +static std::atomic 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; +} + +// Return (and set null) g_cached_symbolized_state if it is not null. +// Otherwise return a new symbolizer. +static Symbolizer *AllocateSymbolizer() { + InitSigSafeArena(); + Symbolizer *symbolizer = + g_cached_symbolizer.exchange(nullptr, std::memory_order_acquire); + if (symbolizer != nullptr) { + return symbolizer; + } + return new (base_internal::LowLevelAlloc::AllocWithArena( + SymbolizerSize(), SigSafeArena())) Symbolizer(); +} + +// Set g_cached_symbolize_state to s if it is null, otherwise +// delete s. +static void FreeSymbolizer(Symbolizer *s) { + Symbolizer *old_cached_symbolizer = nullptr; + if (!g_cached_symbolizer.compare_exchange_strong(old_cached_symbolizer, s, + std::memory_order_release, + std::memory_order_relaxed)) { + s->~Symbolizer(); + base_internal::LowLevelAlloc::Free(s); + } +} + +Symbolizer::Symbolizer() : ok_(true), addr_map_read_(false) { + for (SymbolCacheLine &symbol_cache_line : symbol_cache_) { + for (size_t j = 0; j < ABSL_ARRAYSIZE(symbol_cache_line.name); ++j) { + symbol_cache_line.pc[j] = nullptr; + symbol_cache_line.name[j] = nullptr; + symbol_cache_line.age[j] = 0; + } + } +} + +Symbolizer::~Symbolizer() { + for (SymbolCacheLine &symbol_cache_line : symbol_cache_) { + for (char *s : symbol_cache_line.name) { + base_internal::LowLevelAlloc::Free(s); + } + } + ClearAddrMap(); +} + +// We don't use assert() since it's not guaranteed to be +// async-signal-safe. Instead we define a minimal assertion +// macro. So far, we don't need pretty printing for __FILE__, etc. +#define SAFE_ASSERT(expr) ((expr) ? static_cast(0) : throw std::runtime_error("abort()")) + +// Read up to "count" bytes from file descriptor "fd" into the buffer +// starting at "buf" while handling short reads and EINTR. On +// success, return the number of bytes read. Otherwise, return -1. +static ssize_t ReadPersistent(int fd, void *buf, size_t count) { + SAFE_ASSERT(fd >= 0); + SAFE_ASSERT(count <= SSIZE_MAX); + char *buf0 = reinterpret_cast(buf); + size_t num_bytes = 0; + while (num_bytes < count) { + ssize_t len; + NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes)); + if (len < 0) { // There was an error other than EINTR. + ABSL_RAW_LOG(WARNING, "read failed: errno=%d", errno); + return -1; + } + if (len == 0) { // Reached EOF. + break; + } + num_bytes += len; + } + SAFE_ASSERT(num_bytes <= count); + return static_cast(num_bytes); +} + +// Read up to "count" bytes from "offset" in the file pointed by file +// descriptor "fd" into the buffer starting at "buf". On success, +// return the number of bytes read. Otherwise, return -1. +static ssize_t ReadFromOffset(const int fd, void *buf, const size_t count, + const off_t offset) { + off_t off = lseek(fd, offset, SEEK_SET); + if (off == (off_t)-1) { + ABSL_RAW_LOG(WARNING, "lseek(%d, %ju, SEEK_SET) failed: errno=%d", fd, + static_cast(offset), errno); + return -1; + } + return ReadPersistent(fd, buf, count); +} + +// Try reading exactly "count" bytes from "offset" bytes in a file +// pointed by "fd" into the buffer starting at "buf" while handling +// short reads and EINTR. On success, return true. Otherwise, return +// false. +static bool ReadFromOffsetExact(const int fd, void *buf, const size_t count, + const off_t offset) { + ssize_t len = ReadFromOffset(fd, buf, count, offset); + return len >= 0 && static_cast(len) == count; +} + +// Returns elf_header.e_type if the file pointed by fd is an ELF binary. +static int FileGetElfType(const int fd) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return -1; + } + if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) { + return -1; + } + return elf_header.e_type; +} + +// Read the section headers in the given ELF binary, and if a section +// of the specified type is found, set the output to this section header +// and return true. Otherwise, return false. +// To keep stack consumption low, we would like this function to not get +// inlined. +static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType( + const int fd, ElfW(Half) sh_num, const off_t sh_offset, ElfW(Word) type, + ElfW(Shdr) * out, char *tmp_buf, int tmp_buf_size) { + ElfW(Shdr) *buf = reinterpret_cast(tmp_buf); + const int buf_entries = tmp_buf_size / sizeof(buf[0]); + const int buf_bytes = buf_entries * sizeof(buf[0]); + + for (int i = 0; i < sh_num;) { + const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]); + const ssize_t num_bytes_to_read = + (buf_bytes > num_bytes_left) ? num_bytes_left : buf_bytes; + const off_t offset = sh_offset + i * sizeof(buf[0]); + const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, offset); + if (len % sizeof(buf[0]) != 0) { + ABSL_RAW_LOG( + WARNING, + "Reading %zd bytes from offset %ju returned %zd which is not a " + "multiple of %zu.", + num_bytes_to_read, static_cast(offset), len, + sizeof(buf[0])); + return false; + } + const ssize_t num_headers_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(num_headers_in_buf <= buf_entries); + for (int j = 0; j < num_headers_in_buf; ++j) { + if (buf[j].sh_type == type) { + *out = buf[j]; + return true; + } + } + i += num_headers_in_buf; + } + return false; +} + +// There is no particular reason to limit section name to 63 characters, +// but there has (as yet) been no need for anything longer either. +const int kMaxSectionNameLen = 64; + +bool ForEachSection(int fd, + const std::function &callback) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + ElfW(Shdr) shstrtab; + off_t shstrtab_offset = + (elf_header.e_shoff + elf_header.e_shentsize * elf_header.e_shstrndx); + if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { + return false; + } + + for (int i = 0; i < elf_header.e_shnum; ++i) { + ElfW(Shdr) out; + off_t section_header_offset = + (elf_header.e_shoff + elf_header.e_shentsize * i); + if (!ReadFromOffsetExact(fd, &out, sizeof(out), section_header_offset)) { + return false; + } + off_t name_offset = shstrtab.sh_offset + out.sh_name; + char header_name[kMaxSectionNameLen]; + ssize_t n_read = + ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset); + if (n_read == -1) { + return false; + } else if (n_read > kMaxSectionNameLen) { + // Long read? + return false; + } + + absl::string_view name(header_name, strnlen(header_name, n_read)); + if (!callback(name, out)) { + break; + } + } + return true; +} + +// name_len should include terminating '\0'. +bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + ElfW(Shdr) * out) { + char header_name[kMaxSectionNameLen]; + if (sizeof(header_name) < name_len) { + ABSL_RAW_LOG(WARNING, + "Section name '%s' is too long (%zu); " + "section will not be found (even if present).", + name, name_len); + // No point in even trying. + return false; + } + + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + ElfW(Shdr) shstrtab; + off_t shstrtab_offset = + (elf_header.e_shoff + elf_header.e_shentsize * elf_header.e_shstrndx); + if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { + return false; + } + + for (int i = 0; i < elf_header.e_shnum; ++i) { + off_t section_header_offset = + (elf_header.e_shoff + elf_header.e_shentsize * i); + if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { + return false; + } + off_t name_offset = shstrtab.sh_offset + out->sh_name; + ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset); + if (n_read < 0) { + return false; + } else if (static_cast(n_read) != name_len) { + // Short read -- name could be at end of file. + continue; + } + if (memcmp(header_name, name, name_len) == 0) { + return true; + } + } + return false; +} + +// Compare symbols at in the same address. +// Return true if we should pick symbol1. +static bool ShouldPickFirstSymbol(const ElfW(Sym) & symbol1, + const ElfW(Sym) & symbol2) { + // If one of the symbols is weak and the other is not, pick the one + // this is not a weak symbol. + char bind1 = ELF_ST_BIND(symbol1.st_info); + char bind2 = ELF_ST_BIND(symbol1.st_info); + if (bind1 == STB_WEAK && bind2 != STB_WEAK) return false; + if (bind2 == STB_WEAK && bind1 != STB_WEAK) return true; + + // If one of the symbols has zero size and the other is not, pick the + // one that has non-zero size. + if (symbol1.st_size != 0 && symbol2.st_size == 0) { + return true; + } + if (symbol1.st_size == 0 && symbol2.st_size != 0) { + return false; + } + + // If one of the symbols has no type and the other is not, pick the + // one that has a type. + char type1 = ELF_ST_TYPE(symbol1.st_info); + char type2 = ELF_ST_TYPE(symbol1.st_info); + if (type1 != STT_NOTYPE && type2 == STT_NOTYPE) { + return true; + } + if (type1 == STT_NOTYPE && type2 != STT_NOTYPE) { + return false; + } + + // Pick the first one, if we still cannot decide. + return true; +} + +// Return true if an address is inside a section. +static bool InSection(const void *address, const ElfW(Shdr) * section) { + const char *start = reinterpret_cast(section->sh_addr); + size_t size = static_cast(section->sh_size); + return start <= address && address < (start + size); +} + +static const char *ComputeOffset(const char *base, ptrdiff_t offset) { + // Note: cast to uintptr_t to avoid undefined behavior when base evaluates to + // zero and offset is non-zero. + return reinterpret_cast( + reinterpret_cast(base) + offset); +} + +// Read a symbol table and look for the symbol containing the +// pc. Iterate over symbols in a symbol table and look for the symbol +// containing "pc". If the symbol is found, and its name fits in +// out_size, the name is written into out and SYMBOL_FOUND is returned. +// If the name does not fit, truncated name is written into out, +// and SYMBOL_TRUNCATED is returned. Out is NUL-terminated. +// If the symbol is not found, SYMBOL_NOT_FOUND is returned; +// To keep stack consumption low, we would like this function to not get +// inlined. +static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( + const void *const pc, const int fd, char *out, int out_size, + ptrdiff_t relocation, const ElfW(Shdr) * strtab, const ElfW(Shdr) * symtab, + const ElfW(Shdr) * opd, char *tmp_buf, int tmp_buf_size) { + if (symtab == nullptr) { + return SYMBOL_NOT_FOUND; + } + + // Read multiple symbols at once to save read() calls. + ElfW(Sym) *buf = reinterpret_cast(tmp_buf); + const int buf_entries = tmp_buf_size / sizeof(buf[0]); + + const int num_symbols = symtab->sh_size / symtab->sh_entsize; + + // On platforms using an .opd section (PowerPC & IA64), a function symbol + // has the address of a function descriptor, which contains the real + // starting address. However, we do not always want to use the real + // starting address because we sometimes want to symbolize a function + // pointer into the .opd section, e.g. FindSymbol(&foo,...). + const bool pc_in_opd = + kPlatformUsesOPDSections && opd != nullptr && InSection(pc, opd); + const bool deref_function_descriptor_pointer = + kPlatformUsesOPDSections && opd != nullptr && !pc_in_opd; + + ElfW(Sym) best_match; + SafeMemZero(&best_match, sizeof(best_match)); + bool found_match = false; + for (int i = 0; i < num_symbols;) { + off_t offset = symtab->sh_offset + i * symtab->sh_entsize; + const int num_remaining_symbols = num_symbols - i; + const int entries_in_chunk = std::min(num_remaining_symbols, buf_entries); + const int bytes_in_chunk = entries_in_chunk * sizeof(buf[0]); + const ssize_t len = ReadFromOffset(fd, buf, bytes_in_chunk, offset); + SAFE_ASSERT(len % sizeof(buf[0]) == 0); + const ssize_t num_symbols_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(num_symbols_in_buf <= entries_in_chunk); + for (int j = 0; j < num_symbols_in_buf; ++j) { + const ElfW(Sym) &symbol = buf[j]; + + // For a DSO, a symbol address is relocated by the loading address. + // We keep the original address for opd redirection below. + const char *const original_start_address = + reinterpret_cast(symbol.st_value); + const char *start_address = + ComputeOffset(original_start_address, relocation); + +#ifdef __arm__ + // ARM functions are always aligned to multiples of two bytes; the + // lowest-order bit in start_address is ignored by the CPU and indicates + // whether the function contains ARM (0) or Thumb (1) code. We don't care + // about what encoding is being used; we just want the real start address + // of the function. + start_address = reinterpret_cast( + reinterpret_cast(start_address) & ~1); +#endif + + if (deref_function_descriptor_pointer && + InSection(original_start_address, opd)) { + // The opd section is mapped into memory. Just dereference + // start_address to get the first double word, which points to the + // function entry. + start_address = *reinterpret_cast(start_address); + } + + // If pc is inside the .opd section, it points to a function descriptor. + const size_t size = pc_in_opd ? kFunctionDescriptorSize : symbol.st_size; + const void *const end_address = ComputeOffset(start_address, size); + if (symbol.st_value != 0 && // Skip null value symbols. + symbol.st_shndx != 0 && // Skip undefined symbols. +#ifdef STT_TLS + ELF_ST_TYPE(symbol.st_info) != STT_TLS && // Skip thread-local data. +#endif // STT_TLS + ((start_address <= pc && pc < end_address) || + (start_address == pc && pc == end_address))) { + if (!found_match || ShouldPickFirstSymbol(symbol, best_match)) { + found_match = true; + best_match = symbol; + } + } + } + i += num_symbols_in_buf; + } + + if (found_match) { + const size_t off = strtab->sh_offset + best_match.st_name; + const ssize_t n_read = ReadFromOffset(fd, out, out_size, off); + if (n_read <= 0) { + // This should never happen. + ABSL_RAW_LOG(WARNING, + "Unable to read from fd %d at offset %zu: n_read = %zd", fd, + off, n_read); + return SYMBOL_NOT_FOUND; + } + ABSL_RAW_CHECK(n_read <= out_size, "ReadFromOffset read too much data."); + + // strtab->sh_offset points into .strtab-like section that contains + // NUL-terminated strings: '\0foo\0barbaz\0...". + // + // sh_offset+st_name points to the start of symbol name, but we don't know + // how long the symbol is, so we try to read as much as we have space for, + // and usually over-read (i.e. there is a NUL somewhere before n_read). + if (memchr(out, '\0', n_read) == nullptr) { + // Either out_size was too small (n_read == out_size and no NUL), or + // we tried to read past the EOF (n_read < out_size) and .strtab is + // corrupt (missing terminating NUL; should never happen for valid ELF). + out[n_read - 1] = '\0'; + return SYMBOL_TRUNCATED; + } + return SYMBOL_FOUND; + } + + return SYMBOL_NOT_FOUND; +} + +// Get the symbol name of "pc" from the file pointed by "fd". Process +// both regular and dynamic symbol tables if necessary. +// See FindSymbol() comment for description of return value. +FindSymbolResult Symbolizer::GetSymbolFromObjectFile( + const ObjFile &obj, const void *const pc, const ptrdiff_t relocation, + char *out, int out_size, char *tmp_buf, int tmp_buf_size) { + ElfW(Shdr) symtab; + ElfW(Shdr) strtab; + ElfW(Shdr) opd; + ElfW(Shdr) *opd_ptr = nullptr; + + // On platforms using an .opd sections for function descriptor, read + // the section header. The .opd section is in data segment and should be + // loaded but we check that it is mapped just to be extra careful. + if (kPlatformUsesOPDSections) { + if (GetSectionHeaderByName(obj.fd, kOpdSectionName, + sizeof(kOpdSectionName) - 1, &opd) && + FindObjFile(reinterpret_cast(opd.sh_addr) + relocation, + opd.sh_size) != nullptr) { + opd_ptr = &opd; + } else { + return SYMBOL_NOT_FOUND; + } + } + + // Consult a regular symbol table, then fall back to the dynamic symbol table. + for (const auto symbol_table_type : {SHT_SYMTAB, SHT_DYNSYM}) { + if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, + obj.elf_header.e_shoff, symbol_table_type, + &symtab, tmp_buf, tmp_buf_size)) { + continue; + } + if (!ReadFromOffsetExact( + obj.fd, &strtab, sizeof(strtab), + obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { + continue; + } + const FindSymbolResult rc = + FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab, + opd_ptr, tmp_buf, tmp_buf_size); + if (rc != SYMBOL_NOT_FOUND) { + return rc; + } + } + + return SYMBOL_NOT_FOUND; +} + +namespace { +// Thin wrapper around a file descriptor so that the file descriptor +// gets closed for sure. +class FileDescriptor { + public: + explicit FileDescriptor(int fd) : fd_(fd) {} + FileDescriptor(const FileDescriptor &) = delete; + FileDescriptor &operator=(const FileDescriptor &) = delete; + + ~FileDescriptor() { + if (fd_ >= 0) { + NO_INTR(close(fd_)); + } + } + + int get() const { return fd_; } + + private: + const int fd_; +}; + +// Helper class for reading lines from file. +// +// Note: we don't use ProcMapsIterator since the object is big (it has +// a 5k array member) and uses async-unsafe functions such as sscanf() +// and snprintf(). +class LineReader { + public: + explicit LineReader(int fd, char *buf, int buf_len) + : fd_(fd), + buf_len_(buf_len), + buf_(buf), + bol_(buf), + eol_(buf), + eod_(buf) {} + + LineReader(const LineReader &) = delete; + LineReader &operator=(const LineReader &) = delete; + + // Read '\n'-terminated line from file. On success, modify "bol" + // and "eol", then return true. Otherwise, return false. + // + // Note: if the last line doesn't end with '\n', the line will be + // dropped. It's an intentional behavior to make the code simple. + bool ReadLine(const char **bol, const char **eol) { + if (BufferIsEmpty()) { // First time. + const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_); + if (num_bytes <= 0) { // EOF or error. + return false; + } + eod_ = buf_ + num_bytes; + bol_ = buf_; + } else { + bol_ = eol_ + 1; // Advance to the next line in the buffer. + SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_". + if (!HasCompleteLine()) { + const int incomplete_line_length = eod_ - bol_; + // Move the trailing incomplete line to the beginning. + memmove(buf_, bol_, incomplete_line_length); + // Read text from file and append it. + char *const append_pos = buf_ + incomplete_line_length; + const int capacity_left = buf_len_ - incomplete_line_length; + const ssize_t num_bytes = + ReadPersistent(fd_, append_pos, capacity_left); + if (num_bytes <= 0) { // EOF or error. + return false; + } + eod_ = append_pos + num_bytes; + bol_ = buf_; + } + } + eol_ = FindLineFeed(); + if (eol_ == nullptr) { // '\n' not found. Malformed line. + return false; + } + *eol_ = '\0'; // Replace '\n' with '\0'. + + *bol = bol_; + *eol = eol_; + return true; + } + + private: + char *FindLineFeed() const { + return reinterpret_cast(memchr(bol_, '\n', eod_ - bol_)); + } + + bool BufferIsEmpty() const { return buf_ == eod_; } + + bool HasCompleteLine() const { + return !BufferIsEmpty() && FindLineFeed() != nullptr; + } + + const int fd_; + const int buf_len_; + char *const buf_; + char *bol_; + char *eol_; + const char *eod_; // End of data in "buf_". +}; +} // namespace + +// Place the hex number read from "start" into "*hex". The pointer to +// the first non-hex character or "end" is returned. +static const char *GetHex(const char *start, const char *end, + uint64_t *const value) { + uint64_t hex = 0; + const char *p; + for (p = start; p < end; ++p) { + int ch = *p; + if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')) { + hex = (hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9); + } else { // Encountered the first non-hex character. + break; + } + } + SAFE_ASSERT(p <= end); + *value = hex; + return p; +} + +static const char *GetHex(const char *start, const char *end, + const void **const addr) { + uint64_t hex = 0; + const char *p = GetHex(start, end, &hex); + *addr = reinterpret_cast(hex); + return p; +} + +// Normally we are only interested in "r?x" maps. +// On the PowerPC, function pointers point to descriptors in the .opd +// section. The descriptors themselves are not executable code, so +// we need to relax the check below to "r??". +static bool ShouldUseMapping(const char *const flags) { + return flags[0] == 'r' && (kPlatformUsesOPDSections || flags[2] == 'x'); +} + +// Read /proc/self/maps and run "callback" for each mmapped file found. If +// "callback" returns false, stop scanning and return true. Else continue +// scanning /proc/self/maps. Return true if no parse error is found. +static ABSL_ATTRIBUTE_NOINLINE bool ReadAddrMap( + bool (*callback)(const char *filename, const void *const start_addr, + const void *const end_addr, uint64_t offset, void *arg), + void *arg, void *tmp_buf, int tmp_buf_size) { + // Use /proc/self/task//maps instead of /proc/self/maps. The latter + // requires kernel to stop all threads, and is significantly slower when there + // are 1000s of threads. + char maps_path[80]; + snprintf(maps_path, sizeof(maps_path), "/proc/self/task/%d/maps", getpid()); + + int maps_fd; + NO_INTR(maps_fd = open(maps_path, O_RDONLY)); + FileDescriptor wrapped_maps_fd(maps_fd); + if (wrapped_maps_fd.get() < 0) { + ABSL_RAW_LOG(WARNING, "%s: errno=%d", maps_path, errno); + return false; + } + + // Iterate over maps and look for the map containing the pc. Then + // look into the symbol tables inside. + LineReader reader(wrapped_maps_fd.get(), static_cast(tmp_buf), + tmp_buf_size); + while (true) { + const char *cursor; + const char *eol; + if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. + break; + } + + const char *line = cursor; + const void *start_address; + // Start parsing line in /proc/self/maps. Here is an example: + // + // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat + // + // We want start address (08048000), end address (0804c000), flags + // (r-xp) and file name (/bin/cat). + + // Read start address. + cursor = GetHex(cursor, eol, &start_address); + if (cursor == eol || *cursor != '-') { + ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps line: %s", line); + return false; + } + ++cursor; // Skip '-'. + + // Read end address. + const void *end_address; + cursor = GetHex(cursor, eol, &end_address); + if (cursor == eol || *cursor != ' ') { + ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps line: %s", line); + return false; + } + ++cursor; // Skip ' '. + + // Read flags. Skip flags until we encounter a space or eol. + const char *const flags_start = cursor; + while (cursor < eol && *cursor != ' ') { + ++cursor; + } + // We expect at least four letters for flags (ex. "r-xp"). + if (cursor == eol || cursor < flags_start + 4) { + ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps: %s", line); + return false; + } + + // Check flags. + if (!ShouldUseMapping(flags_start)) { + continue; // We skip this map. + } + ++cursor; // Skip ' '. + + // Read file offset. + uint64_t offset; + cursor = GetHex(cursor, eol, &offset); + ++cursor; // Skip ' '. + + // Skip to file name. "cursor" now points to dev. We need to skip at least + // two spaces for dev and inode. + int num_spaces = 0; + while (cursor < eol) { + if (*cursor == ' ') { + ++num_spaces; + } else if (num_spaces >= 2) { + // The first non-space character after skipping two spaces + // is the beginning of the file name. + break; + } + ++cursor; + } + + // Check whether this entry corresponds to our hint table for the true + // filename. + bool hinted = + GetFileMappingHint(&start_address, &end_address, &offset, &cursor); + if (!hinted && (cursor == eol || cursor[0] == '[')) { + // not an object file, typically [vdso] or [vsyscall] + continue; + } + if (!callback(cursor, start_address, end_address, offset, arg)) break; + } + return true; +} + +// Find the objfile mapped in address region containing [addr, addr + len). +ObjFile *Symbolizer::FindObjFile(const void *const addr, size_t len) { + for (int i = 0; i < 2; ++i) { + if (!ok_) return nullptr; + + // Read /proc/self/maps if necessary + if (!addr_map_read_) { + addr_map_read_ = true; + if (!ReadAddrMap(RegisterObjFile, this, tmp_buf_, TMP_BUF_SIZE)) { + ok_ = false; + return nullptr; + } + } + + int lo = 0; + int hi = addr_map_.Size(); + while (lo < hi) { + int mid = (lo + hi) / 2; + if (addr < addr_map_.At(mid)->end_addr) { + hi = mid; + } else { + lo = mid + 1; + } + } + if (lo != addr_map_.Size()) { + ObjFile *obj = addr_map_.At(lo); + SAFE_ASSERT(obj->end_addr > addr); + if (addr >= obj->start_addr && + reinterpret_cast(addr) + len <= obj->end_addr) + return obj; + } + + // The address mapping may have changed since it was last read. Retry. + ClearAddrMap(); + } + return nullptr; +} + +void Symbolizer::ClearAddrMap() { + for (int i = 0; i != addr_map_.Size(); i++) { + ObjFile *o = addr_map_.At(i); + base_internal::LowLevelAlloc::Free(o->filename); + if (o->fd >= 0) { + NO_INTR(close(o->fd)); + } + } + addr_map_.Clear(); + addr_map_read_ = false; +} + +// Callback for ReadAddrMap to register objfiles in an in-memory table. +bool Symbolizer::RegisterObjFile(const char *filename, + const void *const start_addr, + const void *const end_addr, uint64_t offset, + void *arg) { + Symbolizer *impl = static_cast(arg); + + // Files are supposed to be added in the increasing address order. Make + // sure that's the case. + int addr_map_size = impl->addr_map_.Size(); + if (addr_map_size != 0) { + ObjFile *old = impl->addr_map_.At(addr_map_size - 1); + if (old->end_addr > end_addr) { + ABSL_RAW_LOG(ERROR, + "Unsorted addr map entry: 0x%" PRIxPTR ": %s <-> 0x%" PRIxPTR + ": %s", + reinterpret_cast(end_addr), filename, + reinterpret_cast(old->end_addr), old->filename); + return true; + } else if (old->end_addr == end_addr) { + // The same entry appears twice. This sometimes happens for [vdso]. + if (old->start_addr != start_addr || + strcmp(old->filename, filename) != 0) { + ABSL_RAW_LOG(ERROR, + "Duplicate addr 0x%" PRIxPTR ": %s <-> 0x%" PRIxPTR ": %s", + reinterpret_cast(end_addr), filename, + reinterpret_cast(old->end_addr), old->filename); + } + return true; + } else if (old->end_addr == start_addr && + reinterpret_cast(old->start_addr) - old->offset == + reinterpret_cast(start_addr) - offset && + strcmp(old->filename, filename) == 0) { + // Two contiguous map entries that span a contiguous region of the file, + // perhaps because some part of the file was mlock()ed. Combine them. + old->end_addr = end_addr; + return true; + } + } + ObjFile *obj = impl->addr_map_.Add(); + obj->filename = impl->CopyString(filename); + obj->start_addr = start_addr; + obj->end_addr = end_addr; + obj->offset = offset; + obj->elf_type = -1; // filled on demand + obj->fd = -1; // opened on demand + return true; +} + +// This function wraps the Demangle function to provide an interface +// where the input symbol is demangled in-place. +// To keep stack consumption low, we would like this function to not +// get inlined. +static ABSL_ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size, + char *tmp_buf, + int tmp_buf_size) { + if (Demangle(out, tmp_buf, tmp_buf_size)) { + // Demangling succeeded. Copy to out if the space allows. + int len = strlen(tmp_buf); + if (len + 1 <= out_size) { // +1 for '\0'. + SAFE_ASSERT(len < tmp_buf_size); + memmove(out, tmp_buf, len + 1); + } + } +} + +SymbolCacheLine *Symbolizer::GetCacheLine(const void *const pc) { + uintptr_t pc0 = reinterpret_cast(pc); + pc0 >>= 3; // drop the low 3 bits + + // Shuffle bits. + pc0 ^= (pc0 >> 6) ^ (pc0 >> 12) ^ (pc0 >> 18); + return &symbol_cache_[pc0 % SYMBOL_CACHE_LINES]; +} + +void Symbolizer::AgeSymbols(SymbolCacheLine *line) { + for (uint32_t &age : line->age) { + ++age; + } +} + +const char *Symbolizer::FindSymbolInCache(const void *const pc) { + if (pc == nullptr) return nullptr; + + SymbolCacheLine *line = GetCacheLine(pc); + for (size_t i = 0; i < ABSL_ARRAYSIZE(line->pc); ++i) { + if (line->pc[i] == pc) { + AgeSymbols(line); + line->age[i] = 0; + return line->name[i]; + } + } + return nullptr; +} + +const char *Symbolizer::InsertSymbolInCache(const void *const pc, + const char *name) { + SAFE_ASSERT(pc != nullptr); + + SymbolCacheLine *line = GetCacheLine(pc); + uint32_t max_age = 0; + int oldest_index = -1; + for (size_t i = 0; i < ABSL_ARRAYSIZE(line->pc); ++i) { + if (line->pc[i] == nullptr) { + AgeSymbols(line); + line->pc[i] = pc; + line->name[i] = CopyString(name); + line->age[i] = 0; + return line->name[i]; + } + if (line->age[i] >= max_age) { + max_age = line->age[i]; + oldest_index = i; + } + } + + AgeSymbols(line); + ABSL_RAW_CHECK(oldest_index >= 0, "Corrupt cache"); + base_internal::LowLevelAlloc::Free(line->name[oldest_index]); + line->pc[oldest_index] = pc; + line->name[oldest_index] = CopyString(name); + line->age[oldest_index] = 0; + return line->name[oldest_index]; +} + +static void MaybeOpenFdFromSelfExe(ObjFile *obj) { + if (memcmp(obj->start_addr, ELFMAG, SELFMAG) != 0) { + return; + } + int fd = open("/proc/self/exe", O_RDONLY); + if (fd == -1) { + return; + } + // Verify that contents of /proc/self/exe matches in-memory image of + // the binary. This can fail if the "deleted" binary is in fact not + // the main executable, or for binaries that have the first PT_LOAD + // segment smaller than 4K. We do it in four steps so that the + // buffer is smaller and we don't consume too much stack space. + const char *mem = reinterpret_cast(obj->start_addr); + for (int i = 0; i < 4; ++i) { + char buf[1024]; + ssize_t n = read(fd, buf, sizeof(buf)); + if (n != sizeof(buf) || memcmp(buf, mem, sizeof(buf)) != 0) { + close(fd); + return; + } + mem += sizeof(buf); + } + obj->fd = fd; +} + +static bool MaybeInitializeObjFile(ObjFile *obj) { + if (obj->fd < 0) { + obj->fd = open(obj->filename, O_RDONLY); + + if (obj->fd < 0) { + // Getting /proc/self/exe here means that we were hinted. + if (strcmp(obj->filename, "/proc/self/exe") == 0) { + // /proc/self/exe may be inaccessible (due to setuid, etc.), so try + // accessing the binary via argv0. + if (argv0_value != nullptr) { + obj->fd = open(argv0_value, O_RDONLY); + } + } else { + MaybeOpenFdFromSelfExe(obj); + } + } + + if (obj->fd < 0) { + ABSL_RAW_LOG(WARNING, "%s: open failed: errno=%d", obj->filename, errno); + return false; + } + obj->elf_type = FileGetElfType(obj->fd); + if (obj->elf_type < 0) { + ABSL_RAW_LOG(WARNING, "%s: wrong elf type: %d", obj->filename, + obj->elf_type); + return false; + } + + if (!ReadFromOffsetExact(obj->fd, &obj->elf_header, sizeof(obj->elf_header), + 0)) { + ABSL_RAW_LOG(WARNING, "%s: failed to read elf header", obj->filename); + return false; + } + const int phnum = obj->elf_header.e_phnum; + const int phentsize = obj->elf_header.e_phentsize; + size_t phoff = obj->elf_header.e_phoff; + size_t num_executable_load_segments = 0; + for (int j = 0; j < phnum; j++) { + ElfW(Phdr) phdr; + if (!ReadFromOffsetExact(obj->fd, &phdr, sizeof(phdr), phoff)) { + ABSL_RAW_LOG(WARNING, "%s: failed to read program header %d", + obj->filename, j); + return false; + } + phoff += phentsize; + constexpr int rx = PF_X | PF_R; + if (phdr.p_type != PT_LOAD || (phdr.p_flags & rx) != rx) { + // Not a LOAD segment, or not executable code. + continue; + } + if (num_executable_load_segments < obj->phdr.size()) { + memcpy(&obj->phdr[num_executable_load_segments++], &phdr, sizeof(phdr)); + } else { + ABSL_RAW_LOG(WARNING, "%s: too many executable LOAD segments", + obj->filename); + break; + } + } + if (num_executable_load_segments == 0) { + // This object has no "r-x" LOAD segments. That's unexpected. + ABSL_RAW_LOG(WARNING, "%s: no executable LOAD segments", obj->filename); + return false; + } + } + return true; +} + +// The implementation of our symbolization routine. If it +// successfully finds the symbol containing "pc" and obtains the +// symbol name, returns pointer to that symbol. Otherwise, returns nullptr. +// If any symbol decorators have been installed via InstallSymbolDecorator(), +// they are called here as well. +// To keep stack consumption low, we would like this function to not +// get inlined. +const char *Symbolizer::GetUncachedSymbol(const void *pc) { + ObjFile *const obj = FindObjFile(pc, 1); + ptrdiff_t relocation = 0; + int fd = -1; + if (obj != nullptr) { + if (MaybeInitializeObjFile(obj)) { + const size_t start_addr = reinterpret_cast(obj->start_addr); + if (obj->elf_type == ET_DYN && start_addr >= obj->offset) { + // This object was relocated. + // + // For obj->offset > 0, adjust the relocation since a mapping at offset + // X in the file will have a start address of [true relocation]+X. + relocation = start_addr - obj->offset; + + // Note: some binaries have multiple "rx" LOAD segments. We must + // find the right one. + ElfW(Phdr) *phdr = nullptr; + for (size_t j = 0; j < obj->phdr.size(); j++) { + ElfW(Phdr) &p = obj->phdr[j]; + if (p.p_type != PT_LOAD) { + // We only expect PT_LOADs. This must be PT_NULL that we didn't + // write over (i.e. we exhausted all interesting PT_LOADs). + ABSL_RAW_CHECK(p.p_type == PT_NULL, "unexpected p_type"); + break; + } + if (pc < reinterpret_cast(start_addr + p.p_memsz)) { + phdr = &p; + break; + } + } + if (phdr == nullptr) { + // That's unexpected. Hope for the best. + ABSL_RAW_LOG( + WARNING, + "%s: unable to find LOAD segment for pc: %p, start_addr: %zx", + obj->filename, pc, start_addr); + } else { + // Adjust relocation in case phdr.p_vaddr != 0. + // This happens for binaries linked with `lld --rosegment`, and for + // binaries linked with BFD `ld -z separate-code`. + relocation -= phdr->p_vaddr - phdr->p_offset; + } + } + + fd = obj->fd; + if (GetSymbolFromObjectFile(*obj, pc, relocation, symbol_buf_, + sizeof(symbol_buf_), tmp_buf_, + sizeof(tmp_buf_)) == SYMBOL_FOUND) { + // Only try to demangle the symbol name if it fit into symbol_buf_. + DemangleInplace(symbol_buf_, sizeof(symbol_buf_), tmp_buf_, + sizeof(tmp_buf_)); + } + } + } else { +#if ABSL_HAVE_VDSO_SUPPORT + VDSOSupport vdso; + if (vdso.IsPresent()) { + VDSOSupport::SymbolInfo symbol_info; + if (vdso.LookupSymbolByAddress(pc, &symbol_info)) { + // All VDSO symbols are known to be short. + size_t len = strlen(symbol_info.name); + ABSL_RAW_CHECK(len + 1 < sizeof(symbol_buf_), + "VDSO symbol unexpectedly long"); + memcpy(symbol_buf_, symbol_info.name, len + 1); + } + } +#endif + } + + if (g_decorators_mu.TryLock()) { + if (g_num_decorators > 0) { + SymbolDecoratorArgs decorator_args = { + pc, relocation, fd, symbol_buf_, sizeof(symbol_buf_), + tmp_buf_, sizeof(tmp_buf_), nullptr}; + for (int i = 0; i < g_num_decorators; ++i) { + decorator_args.arg = g_decorators[i].arg; + g_decorators[i].fn(&decorator_args); + } + } + g_decorators_mu.Unlock(); + } + if (symbol_buf_[0] == '\0') { + return nullptr; + } + symbol_buf_[sizeof(symbol_buf_) - 1] = '\0'; // Paranoia. + return InsertSymbolInCache(pc, symbol_buf_); +} + +const char *Symbolizer::GetSymbol(const void *pc) { + const char *entry = FindSymbolInCache(pc); + if (entry != nullptr) { + return entry; + } + symbol_buf_[0] = '\0'; + +#ifdef __hppa__ + { + // In some contexts (e.g., return addresses), PA-RISC uses the lowest two + // bits of the address to indicate the privilege level. Clear those bits + // before trying to symbolize. + const auto pc_bits = reinterpret_cast(pc); + const auto address = pc_bits & ~0x3; + entry = GetUncachedSymbol(reinterpret_cast(address)); + if (entry != nullptr) { + return entry; + } + + // In some contexts, PA-RISC also uses bit 1 of the address to indicate that + // this is a cross-DSO function pointer. Such function pointers actually + // point to a procedure label, a struct whose first 32-bit (pointer) element + // actually points to the function text. With no symbol found for this + // address so far, try interpreting it as a cross-DSO function pointer and + // see how that goes. + if (pc_bits & 0x2) { + return GetUncachedSymbol(*reinterpret_cast(address)); + } + + return nullptr; + } +#else + return GetUncachedSymbol(pc); +#endif +} + +bool RemoveAllSymbolDecorators(void) { + if (!g_decorators_mu.TryLock()) { + // Someone else is using decorators. Get out. + return false; + } + g_num_decorators = 0; + g_decorators_mu.Unlock(); + return true; +} + +bool RemoveSymbolDecorator(int ticket) { + if (!g_decorators_mu.TryLock()) { + // Someone else is using decorators. Get out. + return false; + } + for (int i = 0; i < g_num_decorators; ++i) { + if (g_decorators[i].ticket == ticket) { + while (i < g_num_decorators - 1) { + g_decorators[i] = g_decorators[i + 1]; + ++i; + } + g_num_decorators = i; + break; + } + } + g_decorators_mu.Unlock(); + return true; // Decorator is known to be removed. +} + +int InstallSymbolDecorator(SymbolDecorator decorator, void *arg) { + static int ticket = 0; + + if (!g_decorators_mu.TryLock()) { + // Someone else is using decorators. Get out. + return -2; + } + int ret = ticket; + if (g_num_decorators >= kMaxDecorators) { + ret = -1; + } else { + g_decorators[g_num_decorators] = {decorator, arg, ticket++}; + ++g_num_decorators; + } + g_decorators_mu.Unlock(); + return ret; +} + +bool RegisterFileMappingHint(const void *start, const void *end, uint64_t offset, + const char *filename) { + SAFE_ASSERT(start <= end); + SAFE_ASSERT(filename != nullptr); + + InitSigSafeArena(); + + if (!g_file_mapping_mu.TryLock()) { + return false; + } + + bool ret = true; + if (g_num_file_mapping_hints >= kMaxFileMappingHints) { + ret = false; + } else { + // TODO(ckennelly): Move this into a string copy routine. + int len = strlen(filename); + char *dst = static_cast( + base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena())); + ABSL_RAW_CHECK(dst != nullptr, "out of memory"); + memcpy(dst, filename, len + 1); + + auto &hint = g_file_mapping_hints[g_num_file_mapping_hints++]; + hint.start = start; + hint.end = end; + hint.offset = offset; + hint.filename = dst; + } + + g_file_mapping_mu.Unlock(); + return ret; +} + +bool GetFileMappingHint(const void **start, const void **end, uint64_t *offset, + const char **filename) { + if (!g_file_mapping_mu.TryLock()) { + return false; + } + bool found = false; + for (int i = 0; i < g_num_file_mapping_hints; i++) { + if (g_file_mapping_hints[i].start <= *start && + *end <= g_file_mapping_hints[i].end) { + // We assume that the start_address for the mapping is the base + // address of the ELF section, but when [start_address,end_address) is + // not strictly equal to [hint.start, hint.end), that assumption is + // invalid. + // + // This uses the hint's start address (even though hint.start is not + // necessarily equal to start_address) to ensure the correct + // relocation is computed later. + *start = g_file_mapping_hints[i].start; + *end = g_file_mapping_hints[i].end; + *offset = g_file_mapping_hints[i].offset; + *filename = g_file_mapping_hints[i].filename; + found = true; + break; + } + } + g_file_mapping_mu.Unlock(); + return found; +} + +} // namespace debugging_internal + +bool Symbolize(const void *pc, char *out, int out_size) { + // Symbolization is very slow under tsan. + ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN(); + SAFE_ASSERT(out_size >= 0); + debugging_internal::Symbolizer *s = debugging_internal::AllocateSymbolizer(); + const char *name = s->GetSymbol(pc); + bool ok = false; + if (name != nullptr && out_size > 0) { + strncpy(out, name, out_size); + ok = true; + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. Do so, with + // trailing ellipsis. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = + std::min(implicit_cast(strlen(kEllipsis)), out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + } + debugging_internal::FreeSymbolizer(s); + ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END(); + return ok; +} + +ABSL_NAMESPACE_END +} // namespace absl + +extern "C" bool AbslInternalGetFileMappingHint(const void **start, + const void **end, uint64_t *offset, + const char **filename) { + return absl::debugging_internal::GetFileMappingHint(start, end, offset, + filename); +} diff --git a/src/absl/debugging/symbolize_emscripten.inc b/src/absl/debugging/symbolize_emscripten.inc new file mode 100644 index 0000000..c226c45 --- /dev/null +++ b/src/absl/debugging/symbolize_emscripten.inc @@ -0,0 +1,72 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include + +#include "absl/base/internal/raw_logging.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +extern "C" { +const char* emscripten_pc_get_function(const void* pc); +} + +// clang-format off +EM_JS(bool, HaveOffsetConverter, (), + { return typeof wasmOffsetConverter !== 'undefined'; }); +// clang-format on + +namespace absl { +ABSL_NAMESPACE_BEGIN + +void InitializeSymbolizer(const char*) { + if (!HaveOffsetConverter()) { + ABSL_RAW_LOG(INFO, + "Symbolization unavailable. Rebuild with -sWASM=1 " + "and -sUSE_OFFSET_CONVERTER=1."); + } +} + +bool Symbolize(const void* pc, char* out, int out_size) { + // Check if we have the offset converter necessary for pc_get_function. + // Without it, the program will abort(). + if (!HaveOffsetConverter()) { + return false; + } + const char* func_name = emscripten_pc_get_function(pc); + if (func_name == nullptr) { + return false; + } + + strncpy(out, func_name, out_size); + + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = std::min(sizeof(kEllipsis) - 1, out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + + return true; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/debugging/symbolize_unimplemented.inc b/src/absl/debugging/symbolize_unimplemented.inc new file mode 100644 index 0000000..db24456 --- /dev/null +++ b/src/absl/debugging/symbolize_unimplemented.inc @@ -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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace debugging_internal { + +int InstallSymbolDecorator(SymbolDecorator, void*) { return -1; } +bool RemoveSymbolDecorator(int) { return false; } +bool RemoveAllSymbolDecorators(void) { return false; } +bool RegisterFileMappingHint(const void *, const void *, uint64_t, const char *) { + return false; +} +bool GetFileMappingHint(const void **, const void **, uint64_t *, const char **) { + return false; +} + +} // namespace debugging_internal + +void InitializeSymbolizer(const char*) {} +bool Symbolize(const void *, char *, int) { return false; } + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/debugging/symbolize_win32.inc b/src/absl/debugging/symbolize_win32.inc new file mode 100644 index 0000000..c3df46f --- /dev/null +++ b/src/absl/debugging/symbolize_win32.inc @@ -0,0 +1,81 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// See "Retrieving Symbol Information by Address": +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx + +#include + +// MSVC header dbghelp.h has a warning for an ignored typedef. +#pragma warning(push) +#pragma warning(disable:4091) +#include +#pragma warning(pop) + +#pragma comment(lib, "dbghelp.lib") + +#include +#include + +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +static HANDLE process = NULL; + +void InitializeSymbolizer(const char*) { + if (process != nullptr) { + return; + } + process = GetCurrentProcess(); + + // Symbols are not loaded until a reference is made requiring the + // symbols be loaded. This is the fastest, most efficient way to use + // the symbol handler. + SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME); + if (!SymInitialize(process, nullptr, true)) { + // GetLastError() returns a Win32 DWORD, but we assign to + // unsigned long long to simplify the ABSL_RAW_LOG case below. The uniform + // initialization guarantees this is not a narrowing conversion. + const unsigned long long error{GetLastError()}; // NOLINT(runtime/int) + ABSL_RAW_LOG(FATAL, "SymInitialize() failed: %llu", error); + } +} + +bool Symbolize(const void* pc, char* out, int out_size) { + if (out_size <= 0) { + return false; + } + alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + SYMBOL_INFO* symbol = reinterpret_cast(buf); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + if (!SymFromAddr(process, reinterpret_cast(pc), nullptr, symbol)) { + return false; + } + strncpy(out, symbol->Name, out_size); + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = + std::min(sizeof(kEllipsis) - 1, out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + return true; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/functional/any_invocable.h b/src/absl/functional/any_invocable.h new file mode 100644 index 0000000..0c5faca --- /dev/null +++ b/src/absl/functional/any_invocable.h @@ -0,0 +1,313 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: any_invocable.h +// ----------------------------------------------------------------------------- +// +// This header file defines an `absl::AnyInvocable` type that assumes ownership +// and wraps an object of an invocable type. (Invocable types adhere to the +// concept specified in https://en.cppreference.com/w/cpp/concepts/invocable.) +// +// In general, prefer `absl::AnyInvocable` when you need a type-erased +// function parameter that needs to take ownership of the type. +// +// NOTE: `absl::AnyInvocable` is similar to the C++23 `std::move_only_function` +// abstraction, but has a slightly different API and is not designed to be a +// drop-in replacement or C++11-compatible backfill of that type. + +#ifndef ABSL_FUNCTIONAL_ANY_INVOCABLE_H_ +#define ABSL_FUNCTIONAL_ANY_INVOCABLE_H_ + +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/functional/internal/any_invocable.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// absl::AnyInvocable +// +// `absl::AnyInvocable` is a functional wrapper type, like `std::function`, that +// assumes ownership of an invocable object. Unlike `std::function`, an +// `absl::AnyInvocable` is more type-safe and provides the following additional +// benefits: +// +// * Properly adheres to const correctness of the underlying type +// * Is move-only so avoids concurrency problems with copied invocables and +// unnecessary copies in general. +// * Supports reference qualifiers allowing it to perform unique actions (noted +// below). +// +// `absl::AnyInvocable` is a template, and an `absl::AnyInvocable` instantiation +// may wrap any invocable object with a compatible function signature, e.g. +// having arguments and return types convertible to types matching the +// `absl::AnyInvocable` signature, and also matching any stated reference +// qualifiers, as long as that type is moveable. It therefore provides broad +// type erasure for functional objects. +// +// An `absl::AnyInvocable` is typically used as a type-erased function parameter +// for accepting various functional objects: +// +// // Define a function taking an AnyInvocable parameter. +// void my_func(absl::AnyInvocable f) { +// ... +// }; +// +// // That function can accept any invocable type: +// +// // Accept a function reference. We don't need to move a reference. +// int func1() { return 0; }; +// my_func(func1); +// +// // Accept a lambda. We use std::move here because otherwise my_func would +// // copy the lambda. +// auto lambda = []() { return 0; }; +// my_func(std::move(lambda)); +// +// // Accept a function pointer. We don't need to move a function pointer. +// func2 = &func1; +// my_func(func2); +// +// // Accept an std::function by moving it. Note that the lambda is copyable +// // (satisfying std::function requirements) and moveable (satisfying +// // absl::AnyInvocable requirements). +// std::function func6 = []() { return 0; }; +// my_func(std::move(func6)); +// +// `AnyInvocable` also properly respects `const` qualifiers, reference +// qualifiers, and the `noexcept` specification (only in C++ 17 and beyond) as +// part of the user-specified function type (e.g. +// `AnyInvocable`). These qualifiers will be applied to +// the `AnyInvocable` object's `operator()`, and the underlying invocable must +// be compatible with those qualifiers. +// +// Comparison of const and non-const function types: +// +// // Store a closure inside of `func` with the function type `int()`. +// // Note that we have made `func` itself `const`. +// const AnyInvocable func = [](){ return 0; }; +// +// func(); // Compile-error: the passed type `int()` isn't `const`. +// +// // Store a closure inside of `const_func` with the function type +// // `int() const`. +// // Note that we have also made `const_func` itself `const`. +// const AnyInvocable const_func = [](){ return 0; }; +// +// const_func(); // Fine: `int() const` is `const`. +// +// In the above example, the call `func()` would have compiled if +// `std::function` were used even though the types are not const compatible. +// This is a bug, and using `absl::AnyInvocable` properly detects that bug. +// +// In addition to affecting the signature of `operator()`, the `const` and +// reference qualifiers of the function type also appropriately constrain which +// kinds of invocable objects you are allowed to place into the `AnyInvocable` +// instance. If you specify a function type that is const-qualified, then +// anything that you attempt to put into the `AnyInvocable` must be callable on +// a `const` instance of that type. +// +// Constraint example: +// +// // Fine because the lambda is callable when `const`. +// AnyInvocable func = [=](){ return 0; }; +// +// // This is a compile-error because the lambda isn't callable when `const`. +// AnyInvocable error = [=]() mutable { return 0; }; +// +// An `&&` qualifier can be used to express that an `absl::AnyInvocable` +// instance should be invoked at most once: +// +// // Invokes `continuation` with the logical result of an operation when +// // that operation completes (common in asynchronous code). +// void CallOnCompletion(AnyInvocable continuation) { +// int result_of_foo = foo(); +// +// // `std::move` is required because the `operator()` of `continuation` is +// // rvalue-reference qualified. +// std::move(continuation)(result_of_foo); +// } +// +// Credits to Matt Calabrese (https://github.com/mattcalabrese) for the original +// implementation. +template +class AnyInvocable : private internal_any_invocable::Impl { + private: + static_assert( + std::is_function::value, + "The template argument of AnyInvocable must be a function type."); + + using Impl = internal_any_invocable::Impl; + + public: + // The return type of Sig + using result_type = typename Impl::result_type; + + // Constructors + + // Constructs the `AnyInvocable` in an empty state. + AnyInvocable() noexcept = default; + AnyInvocable(std::nullptr_t) noexcept {} // NOLINT + + // Constructs the `AnyInvocable` from an existing `AnyInvocable` by a move. + // Note that `f` is not guaranteed to be empty after move-construction, + // although it may be. + AnyInvocable(AnyInvocable&& /*f*/) noexcept = default; + + // Constructs an `AnyInvocable` from an invocable object. + // + // Upon construction, `*this` is only empty if `f` is a function pointer or + // member pointer type and is null, or if `f` is an `AnyInvocable` that is + // empty. + template ::value>> + AnyInvocable(F&& f) // NOLINT + : Impl(internal_any_invocable::ConversionConstruct(), + std::forward(f)) {} + + // Constructs an `AnyInvocable` that holds an invocable object of type `T`, + // which is constructed in-place from the given arguments. + // + // Example: + // + // AnyInvocable func( + // absl::in_place_type, arg1, arg2); + // + template ::value>> + explicit AnyInvocable(absl::in_place_type_t, Args&&... args) + : Impl(absl::in_place_type>, + std::forward(args)...) { + static_assert(std::is_same>::value, + "The explicit template argument of in_place_type is required " + "to be an unqualified object type."); + } + + // Overload of the above constructor to support list-initialization. + template &, Args...>::value>> + explicit AnyInvocable(absl::in_place_type_t, + std::initializer_list ilist, Args&&... args) + : Impl(absl::in_place_type>, ilist, + std::forward(args)...) { + static_assert(std::is_same>::value, + "The explicit template argument of in_place_type is required " + "to be an unqualified object type."); + } + + // Assignment Operators + + // Assigns an `AnyInvocable` through move-assignment. + // Note that `f` is not guaranteed to be empty after move-assignment + // although it may be. + AnyInvocable& operator=(AnyInvocable&& /*f*/) noexcept = default; + + // Assigns an `AnyInvocable` from a nullptr, clearing the `AnyInvocable`. If + // not empty, destroys the target, putting `*this` into an empty state. + AnyInvocable& operator=(std::nullptr_t) noexcept { + this->Clear(); + return *this; + } + + // Assigns an `AnyInvocable` from an existing `AnyInvocable` instance. + // + // Upon assignment, `*this` is only empty if `f` is a function pointer or + // member pointer type and is null, or if `f` is an `AnyInvocable` that is + // empty. + template ::value>> + AnyInvocable& operator=(F&& f) { + *this = AnyInvocable(std::forward(f)); + return *this; + } + + // Assigns an `AnyInvocable` from a reference to an invocable object. + // Upon assignment, stores a reference to the invocable object in the + // `AnyInvocable` instance. + template < + class F, + typename = absl::enable_if_t< + internal_any_invocable::CanAssignReferenceWrapper::value>> + AnyInvocable& operator=(std::reference_wrapper f) noexcept { + *this = AnyInvocable(f); + return *this; + } + + // Destructor + + // If not empty, destroys the target. + ~AnyInvocable() = default; + + // absl::AnyInvocable::swap() + // + // Exchanges the targets of `*this` and `other`. + void swap(AnyInvocable& other) noexcept { std::swap(*this, other); } + + // abl::AnyInvocable::operator bool() + // + // Returns `true` if `*this` is not empty. + explicit operator bool() const noexcept { return this->HasValue(); } + + // Invokes the target object of `*this`. `*this` must not be empty. + // + // Note: The signature of this function call operator is the same as the + // template parameter `Sig`. + using Impl::operator(); + + // Equality operators + + // Returns `true` if `*this` is empty. + friend bool operator==(const AnyInvocable& f, std::nullptr_t) noexcept { + return !f.HasValue(); + } + + // Returns `true` if `*this` is empty. + friend bool operator==(std::nullptr_t, const AnyInvocable& f) noexcept { + return !f.HasValue(); + } + + // Returns `false` if `*this` is empty. + friend bool operator!=(const AnyInvocable& f, std::nullptr_t) noexcept { + return f.HasValue(); + } + + // Returns `false` if `*this` is empty. + friend bool operator!=(std::nullptr_t, const AnyInvocable& f) noexcept { + return f.HasValue(); + } + + // swap() + // + // Exchanges the targets of `f1` and `f2`. + friend void swap(AnyInvocable& f1, AnyInvocable& f2) noexcept { f1.swap(f2); } + + private: + // Friending other instantiations is necessary for conversions. + template + friend class internal_any_invocable::CoreImpl; +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_ANY_INVOCABLE_H_ diff --git a/src/absl/functional/bind_front.h b/src/absl/functional/bind_front.h new file mode 100644 index 0000000..f9075bd --- /dev/null +++ b/src/absl/functional/bind_front.h @@ -0,0 +1,193 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: bind_front.h +// ----------------------------------------------------------------------------- +// +// `absl::bind_front()` returns a functor by binding a number of arguments to +// the front of a provided (usually more generic) functor. Unlike `std::bind`, +// it does not require the use of argument placeholders. The simpler syntax of +// `absl::bind_front()` allows you to avoid known misuses with `std::bind()`. +// +// `absl::bind_front()` is meant as a drop-in replacement for C++20's upcoming +// `std::bind_front()`, which similarly resolves these issues with +// `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, allow +// partial function application. (See +// https://en.wikipedia.org/wiki/Partial_application). + +#ifndef ABSL_FUNCTIONAL_BIND_FRONT_H_ +#define ABSL_FUNCTIONAL_BIND_FRONT_H_ + +#if defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +#include // For std::bind_front. +#endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L + +#include "absl/functional/internal/front_binder.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// bind_front() +// +// Binds the first N arguments of an invocable object and stores them by value. +// +// Like `std::bind()`, `absl::bind_front()` is implicitly convertible to +// `std::function`. In particular, it may be used as a simpler replacement for +// `std::bind()` in most cases, as it does not require placeholders to be +// specified. More importantly, it provides more reliable correctness guarantees +// than `std::bind()`; while `std::bind()` will silently ignore passing more +// parameters than expected, for example, `absl::bind_front()` will report such +// mis-uses as errors. In C++20, `absl::bind_front` is replaced by +// `std::bind_front`. +// +// absl::bind_front(a...) can be seen as storing the results of +// std::make_tuple(a...). +// +// Example: Binding a free function. +// +// int Minus(int a, int b) { return a - b; } +// +// assert(absl::bind_front(Minus)(3, 2) == 3 - 2); +// assert(absl::bind_front(Minus, 3)(2) == 3 - 2); +// assert(absl::bind_front(Minus, 3, 2)() == 3 - 2); +// +// Example: Binding a member function. +// +// struct Math { +// int Double(int a) const { return 2 * a; } +// }; +// +// Math math; +// +// assert(absl::bind_front(&Math::Double)(&math, 3) == 2 * 3); +// // Stores a pointer to math inside the functor. +// assert(absl::bind_front(&Math::Double, &math)(3) == 2 * 3); +// // Stores a copy of math inside the functor. +// assert(absl::bind_front(&Math::Double, math)(3) == 2 * 3); +// // Stores std::unique_ptr inside the functor. +// assert(absl::bind_front(&Math::Double, +// std::unique_ptr(new Math))(3) == 2 * 3); +// +// Example: Using `absl::bind_front()`, instead of `std::bind()`, with +// `std::function`. +// +// class FileReader { +// public: +// void ReadFileAsync(const std::string& filename, std::string* content, +// const std::function& done) { +// // Calls Executor::Schedule(std::function). +// Executor::DefaultExecutor()->Schedule( +// absl::bind_front(&FileReader::BlockingRead, this, +// filename, content, done)); +// } +// +// private: +// void BlockingRead(const std::string& filename, std::string* content, +// const std::function& done) { +// CHECK_OK(file::GetContents(filename, content, {})); +// done(); +// } +// }; +// +// `absl::bind_front()` stores bound arguments explicitly using the type passed +// rather than implicitly based on the type accepted by its functor. +// +// Example: Binding arguments explicitly. +// +// void LogStringView(absl::string_view sv) { +// LOG(INFO) << sv; +// } +// +// Executor* e = Executor::DefaultExecutor(); +// std::string s = "hello"; +// absl::string_view sv = s; +// +// // absl::bind_front(LogStringView, arg) makes a copy of arg and stores it. +// e->Schedule(absl::bind_front(LogStringView, sv)); // ERROR: dangling +// // string_view. +// +// e->Schedule(absl::bind_front(LogStringView, s)); // OK: stores a copy of +// // s. +// +// To store some of the arguments passed to `absl::bind_front()` by reference, +// use std::ref()` and `std::cref()`. +// +// Example: Storing some of the bound arguments by reference. +// +// class Service { +// public: +// void Serve(const Request& req, std::function* done) { +// // The request protocol buffer won't be deleted until done is called. +// // It's safe to store a reference to it inside the functor. +// Executor::DefaultExecutor()->Schedule( +// absl::bind_front(&Service::BlockingServe, this, std::cref(req), +// done)); +// } +// +// private: +// void BlockingServe(const Request& req, std::function* done); +// }; +// +// Example: Storing bound arguments by reference. +// +// void Print(const std::string& a, const std::string& b) { +// std::cerr << a << b; +// } +// +// std::string hi = "Hello, "; +// std::vector names = {"Chuk", "Gek"}; +// // Doesn't copy hi. +// for_each(names.begin(), names.end(), +// absl::bind_front(Print, std::ref(hi))); +// +// // DO NOT DO THIS: the functor may outlive "hi", resulting in +// // dangling references. +// foo->DoInFuture(absl::bind_front(Print, std::ref(hi), "Guest")); // BAD! +// auto f = absl::bind_front(Print, std::ref(hi), "Guest"); // BAD! +// +// Example: Storing reference-like types. +// +// void Print(absl::string_view a, const std::string& b) { +// std::cerr << a << b; +// } +// +// std::string hi = "Hello, "; +// // Copies "hi". +// absl::bind_front(Print, hi)("Chuk"); +// +// // Compile error: std::reference_wrapper is not implicitly +// // convertible to string_view. +// // absl::bind_front(Print, std::cref(hi))("Chuk"); +// +// // Doesn't copy "hi". +// absl::bind_front(Print, absl::string_view(hi))("Chuk"); +// +#if defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +using std::bind_front; +#else // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +template +constexpr functional_internal::bind_front_t bind_front( + F&& func, BoundArgs&&... args) { + return functional_internal::bind_front_t( + absl::in_place, absl::forward(func), + absl::forward(args)...); +} +#endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_BIND_FRONT_H_ diff --git a/src/absl/functional/function_ref.h b/src/absl/functional/function_ref.h new file mode 100644 index 0000000..f977960 --- /dev/null +++ b/src/absl/functional/function_ref.h @@ -0,0 +1,143 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: function_ref.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::FunctionRef` type for holding a +// non-owning reference to an object of any invocable type. This function +// reference is typically most useful as a type-erased argument type for +// accepting function types that neither take ownership nor copy the type; using +// the reference type in this case avoids a copy and an allocation. Best +// practices of other non-owning reference-like objects (such as +// `absl::string_view`) apply here. +// +// An `absl::FunctionRef` is similar in usage to a `std::function` but has the +// following differences: +// +// * It doesn't own the underlying object. +// * It doesn't have a null or empty state. +// * It never performs deep copies or allocations. +// * It's much faster and cheaper to construct. +// * It's trivially copyable and destructable. +// +// Generally, `absl::FunctionRef` should not be used as a return value, data +// member, or to initialize a `std::function`. Such usages will often lead to +// problematic lifetime issues. Once you convert something to an +// `absl::FunctionRef` you cannot make a deep copy later. +// +// This class is suitable for use wherever a "const std::function<>&" +// would be used without making a copy. ForEach functions and other versions of +// the visitor pattern are a good example of when this class should be used. +// +// This class is trivial to copy and should be passed by value. +#ifndef ABSL_FUNCTIONAL_FUNCTION_REF_H_ +#define ABSL_FUNCTIONAL_FUNCTION_REF_H_ + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/functional/internal/function_ref.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// FunctionRef +// +// Dummy class declaration to allow the partial specialization based on function +// types below. +template +class FunctionRef; + +// FunctionRef +// +// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with +// a compatible signature. Generally, an `absl::FunctionRef` should only be used +// as an argument type and should be preferred as an argument over a const +// reference to a `std::function`. `absl::FunctionRef` itself does not allocate, +// although the wrapped invokable may. +// +// Example: +// +// // The following function takes a function callback by const reference +// bool Visitor(const std::function& callback); +// +// // Assuming that the function is not stored or otherwise copied, it can be +// // replaced by an `absl::FunctionRef`: +// bool Visitor(absl::FunctionRef +// callback); +// +// Note: the assignment operator within an `absl::FunctionRef` is intentionally +// deleted to prevent misuse; because the `absl::FunctionRef` does not own the +// underlying type, assignment likely indicates misuse. +template +class FunctionRef { + private: + // Used to disable constructors for objects that are not compatible with the + // signature of this FunctionRef. + template > + using EnableIfCompatible = + typename std::enable_if::value || + std::is_convertible::value>::type; + + public: + // Constructs a FunctionRef from any invokable type. + template > + // NOLINTNEXTLINE(runtime/explicit) + FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND) + : invoker_(&absl::functional_internal::InvokeObject) { + absl::functional_internal::AssertNonNull(f); + ptr_.obj = &f; + } + + // Overload for function pointers. This eliminates a level of indirection that + // would happen if the above overload was used (it lets us store the pointer + // instead of a pointer to a pointer). + // + // This overload is also used for references to functions, since references to + // functions can decay to function pointers implicitly. + template < + typename F, typename = EnableIfCompatible, + absl::functional_internal::EnableIf::value> = 0> + FunctionRef(F* f) // NOLINT(runtime/explicit) + : invoker_(&absl::functional_internal::InvokeFunction) { + assert(f != nullptr); + ptr_.fun = reinterpret_cast(f); + } + + // To help prevent subtle lifetime bugs, FunctionRef is not assignable. + // Typically, it should only be used as an argument type. + FunctionRef& operator=(const FunctionRef& rhs) = delete; + FunctionRef(const FunctionRef& rhs) = default; + + // Call the underlying object. + R operator()(Args... args) const { + return invoker_(ptr_, std::forward(args)...); + } + + private: + absl::functional_internal::VoidPtr ptr_; + absl::functional_internal::Invoker invoker_; +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_FUNCTION_REF_H_ diff --git a/src/absl/functional/internal/any_invocable.h b/src/absl/functional/internal/any_invocable.h new file mode 100644 index 0000000..0b9a621 --- /dev/null +++ b/src/absl/functional/internal/any_invocable.h @@ -0,0 +1,857 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Implementation details for `absl::AnyInvocable` + +#ifndef ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ +#define ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ + +//////////////////////////////////////////////////////////////////////////////// +// // +// This implementation of the proposed `any_invocable` uses an approach that // +// chooses between local storage and remote storage for the contained target // +// object based on the target object's size, alignment requirements, and // +// whether or not it has a nothrow move constructor. Additional optimizations // +// are performed when the object is a trivially copyable type [basic.types]. // +// // +// There are three datamembers per `AnyInvocable` instance // +// // +// 1) A union containing either // +// - A pointer to the target object referred to via a void*, or // +// - the target object, emplaced into a raw char buffer // +// // +// 2) A function pointer to a "manager" function operation that takes a // +// discriminator and logically branches to either perform a move operation // +// or destroy operation based on that discriminator. // +// // +// 3) A function pointer to an "invoker" function operation that invokes the // +// target object, directly returning the result. // +// // +// When in the logically empty state, the manager function is an empty // +// function and the invoker function is one that would be undefined-behavior // +// to call. // +// // +// An additional optimization is performed when converting from one // +// AnyInvocable to another where only the noexcept specification and/or the // +// cv/ref qualifiers of the function type differ. In these cases, the // +// conversion works by "moving the guts", similar to if they were the same // +// exact type, as opposed to having to perform an additional layer of // +// wrapping through remote storage. // +// // +//////////////////////////////////////////////////////////////////////////////// + +// IWYU pragma: private, include "absl/functional/any_invocable.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/invoke.h" +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Helper macro used to prevent spelling `noexcept` in language versions older +// than C++17, where it is not part of the type system, in order to avoid +// compilation failures and internal compiler errors. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L +#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) noexcept(noex) +#else +#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) +#endif + +// Defined in functional/any_invocable.h +template +class AnyInvocable; + +namespace internal_any_invocable { + +// Constants relating to the small-object-storage for AnyInvocable +enum StorageProperty : std::size_t { + kAlignment = alignof(std::max_align_t), // The alignment of the storage + kStorageSize = sizeof(void*) * 2 // The size of the storage +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction for checking if a type is an AnyInvocable instantiation. +// This is used during conversion operations. +template +struct IsAnyInvocable : std::false_type {}; + +template +struct IsAnyInvocable> : std::true_type {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// A type trait that tells us whether or not a target function type should be +// stored locally in the small object optimization storage +template +using IsStoredLocally = std::integral_constant< + bool, sizeof(T) <= kStorageSize && alignof(T) <= kAlignment && + kAlignment % alignof(T) == 0 && + std::is_nothrow_move_constructible::value>; + +// An implementation of std::remove_cvref_t of C++20. +template +using RemoveCVRef = + typename std::remove_cv::type>::type; + +//////////////////////////////////////////////////////////////////////////////// +// +// An implementation of the C++ standard INVOKE pseudo-macro, operation is +// equivalent to std::invoke except that it forces an implicit conversion to the +// specified return type. If "R" is void, the function is executed and the +// return value is simply ignored. +template ::value>> +void InvokeR(F&& f, P&&... args) { + absl::base_internal::invoke(std::forward(f), std::forward

    (args)...); +} + +template ::value, int> = 0> +ReturnType InvokeR(F&& f, P&&... args) { + return absl::base_internal::invoke(std::forward(f), + std::forward

    (args)...); +} + +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// +// A metafunction that takes a "T" corresponding to a parameter type of the +// user's specified function type, and yields the parameter type to use for the +// type-erased invoker. In order to prevent observable moves, this must be +// either a reference or, if the type is trivial, the original parameter type +// itself. Since the parameter type may be incomplete at the point that this +// metafunction is used, we can only do this optimization for scalar types +// rather than for any trivial type. +template +T ForwardImpl(std::true_type); + +template +T&& ForwardImpl(std::false_type); + +// NOTE: We deliberately use an intermediate struct instead of a direct alias, +// as a workaround for b/206991861 on MSVC versions < 1924. +template +struct ForwardedParameter { + using type = decltype(( + ForwardImpl)(std::integral_constant::value>())); +}; + +template +using ForwardedParameterType = typename ForwardedParameter::type; +// +//////////////////////////////////////////////////////////////////////////////// + +// A discriminator when calling the "manager" function that describes operation +// type-erased operation should be invoked. +// +// "relocate_from_to" specifies that the manager should perform a move. +// +// "dispose" specifies that the manager should perform a destroy. +enum class FunctionToCall : bool { relocate_from_to, dispose }; + +// The portion of `AnyInvocable` state that contains either a pointer to the +// target object or the object itself in local storage +union TypeErasedState { + struct { + // A pointer to the type-erased object when remotely stored + void* target; + // The size of the object for `RemoteManagerTrivial` + std::size_t size; + } remote; + + // Local-storage for the type-erased object when small and trivial enough + alignas(kAlignment) char storage[kStorageSize]; +}; + +// A typed accessor for the object in `TypeErasedState` storage +template +T& ObjectInLocalStorage(TypeErasedState* const state) { + // We launder here because the storage may be reused with the same type. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + return *std::launder(reinterpret_cast(&state->storage)); +#elif ABSL_HAVE_BUILTIN(__builtin_launder) + return *__builtin_launder(reinterpret_cast(&state->storage)); +#else + + // When `std::launder` or equivalent are not available, we rely on undefined + // behavior, which works as intended on Abseil's officially supported + // platforms as of Q2 2022. +#if !defined(__clang__) && defined(__GNUC__) +// #pragma GCC diagnostic ignored "-Wstrict-aliasing" +// #pragma GCC diagnostic push +#endif + return *reinterpret_cast(&state->storage); +#if !defined(__clang__) && defined(__GNUC__) +// #pragma GCC diagnostic pop +#endif + +#endif +} + +// The type for functions issuing lifetime-related operations: move and dispose +// A pointer to such a function is contained in each `AnyInvocable` instance. +// NOTE: When specifying `FunctionToCall::`dispose, the same state must be +// passed as both "from" and "to". +using ManagerType = void(FunctionToCall /*operation*/, + TypeErasedState* /*from*/, TypeErasedState* /*to*/) + ABSL_INTERNAL_NOEXCEPT_SPEC(true); + +// The type for functions issuing the actual invocation of the object +// A pointer to such a function is contained in each AnyInvocable instance. +template +using InvokerType = ReturnType(TypeErasedState*, ForwardedParameterType

    ...) + ABSL_INTERNAL_NOEXCEPT_SPEC(SigIsNoexcept); + +// The manager that is used when AnyInvocable is empty +inline void EmptyManager(FunctionToCall /*operation*/, + TypeErasedState* /*from*/, + TypeErasedState* /*to*/) noexcept {} + +// The manager that is used when a target function is in local storage and is +// a trivially copyable type. +inline void LocalManagerTrivial(FunctionToCall /*operation*/, + TypeErasedState* const from, + TypeErasedState* const to) noexcept { + // This single statement without branching handles both possible operations. + // + // For FunctionToCall::dispose, "from" and "to" point to the same state, and + // so this assignment logically would do nothing. + // + // Note: Correctness here relies on http://wg21.link/p0593, which has only + // become standard in C++20, though implementations do not break it in + // practice for earlier versions of C++. + // + // The correct way to do this without that paper is to first placement-new a + // default-constructed T in "to->storage" prior to the memmove, but doing so + // requires a different function to be created for each T that is stored + // locally, which can cause unnecessary bloat and be less cache friendly. + *to = *from; + + // Note: Because the type is trivially copyable, the destructor does not need + // to be called ("trivially copyable" requires a trivial destructor). +} + +// The manager that is used when a target function is in local storage and is +// not a trivially copyable type. +template +void LocalManagerNontrivial(FunctionToCall operation, + TypeErasedState* const from, + TypeErasedState* const to) noexcept { + static_assert(IsStoredLocally::value, + "Local storage must only be used for supported types."); + static_assert(!std::is_trivially_copyable::value, + "Locally stored types must be trivially copyable."); + + T& from_object = (ObjectInLocalStorage)(from); + + switch (operation) { + case FunctionToCall::relocate_from_to: + // NOTE: Requires that the left-hand operand is already empty. + ::new (static_cast(&to->storage)) T(std::move(from_object)); + ABSL_FALLTHROUGH_INTENDED; + case FunctionToCall::dispose: + from_object.~T(); // Must not throw. // NOLINT + return; + } + ABSL_INTERNAL_UNREACHABLE; +} + +// The invoker that is used when a target function is in local storage +// Note: QualTRef here is the target function type along with cv and reference +// qualifiers that must be used when calling the function. +template +ReturnType LocalInvoker( + TypeErasedState* const state, + ForwardedParameterType

    ... args) noexcept(SigIsNoexcept) { + using RawT = RemoveCVRef; + static_assert( + IsStoredLocally::value, + "Target object must be in local storage in order to be invoked from it."); + + auto& f = (ObjectInLocalStorage)(state); + return (InvokeR)(static_cast(f), + static_cast>(args)...); +} + +// The manager that is used when a target function is in remote storage and it +// has a trivial destructor +inline void RemoteManagerTrivial(FunctionToCall operation, + TypeErasedState* const from, + TypeErasedState* const to) noexcept { + switch (operation) { + case FunctionToCall::relocate_from_to: + // NOTE: Requires that the left-hand operand is already empty. + to->remote = from->remote; + return; + case FunctionToCall::dispose: +#if defined(__cpp_sized_deallocation) + ::operator delete(from->remote.target, from->remote.size); +#else // __cpp_sized_deallocation + ::operator delete(from->remote.target); +#endif // __cpp_sized_deallocation + return; + } + ABSL_INTERNAL_UNREACHABLE; +} + +// The manager that is used when a target function is in remote storage and the +// destructor of the type is not trivial +template +void RemoteManagerNontrivial(FunctionToCall operation, + TypeErasedState* const from, + TypeErasedState* const to) noexcept { + static_assert(!IsStoredLocally::value, + "Remote storage must only be used for types that do not " + "qualify for local storage."); + + switch (operation) { + case FunctionToCall::relocate_from_to: + // NOTE: Requires that the left-hand operand is already empty. + to->remote.target = from->remote.target; + return; + case FunctionToCall::dispose: + ::delete static_cast(from->remote.target); // Must not throw. + return; + } + ABSL_INTERNAL_UNREACHABLE; +} + +// The invoker that is used when a target function is in remote storage +template +ReturnType RemoteInvoker( + TypeErasedState* const state, + ForwardedParameterType

    ... args) noexcept(SigIsNoexcept) { + using RawT = RemoveCVRef; + static_assert(!IsStoredLocally::value, + "Target object must be in remote storage in order to be " + "invoked from it."); + + auto& f = *static_cast(state->remote.target); + return (InvokeR)(static_cast(f), + static_cast>(args)...); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that checks if a type T is an instantiation of +// absl::in_place_type_t (needed for constructor constraints of AnyInvocable). +template +struct IsInPlaceType : std::false_type {}; + +template +struct IsInPlaceType> : std::true_type {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// A constructor name-tag used with CoreImpl (below) to request the +// conversion-constructor. QualDecayedTRef is the decayed-type of the object to +// wrap, along with the cv and reference qualifiers that must be applied when +// performing an invocation of the wrapped object. +template +struct TypedConversionConstruct {}; + +// A helper base class for all core operations of AnyInvocable. Most notably, +// this class creates the function call operator and constraint-checkers so that +// the top-level class does not have to be a series of partial specializations. +// +// Note: This definition exists (as opposed to being a declaration) so that if +// the user of the top-level template accidentally passes a template argument +// that is not a function type, they will get a static_assert in AnyInvocable's +// class body rather than an error stating that Impl is not defined. +template +class Impl {}; // Note: This is partially-specialized later. + +// A std::unique_ptr deleter that deletes memory allocated via ::operator new. +#if defined(__cpp_sized_deallocation) +class TrivialDeleter { + public: + explicit TrivialDeleter(std::size_t size) : size_(size) {} + + void operator()(void* target) const { + ::operator delete(target, size_); + } + + private: + std::size_t size_; +}; +#else // __cpp_sized_deallocation +class TrivialDeleter { + public: + explicit TrivialDeleter(std::size_t) {} + + void operator()(void* target) const { ::operator delete(target); } +}; +#endif // __cpp_sized_deallocation + +template +class CoreImpl; + +constexpr bool IsCompatibleConversion(void*, void*) { return false; } +template +constexpr bool IsCompatibleConversion(CoreImpl*, + CoreImpl*) { + return !NoExceptDest || NoExceptSrc; +} + +// A helper base class for all core operations of AnyInvocable that do not +// depend on the cv/ref qualifiers of the function type. +template +class CoreImpl { + public: + using result_type = ReturnType; + + CoreImpl() noexcept : manager_(EmptyManager), invoker_(nullptr) {} + + enum class TargetType : int { + kPointer = 0, + kCompatibleAnyInvocable = 1, + kIncompatibleAnyInvocable = 2, + kOther = 3, + }; + + // Note: QualDecayedTRef here includes the cv-ref qualifiers associated with + // the invocation of the Invocable. The unqualified type is the target object + // type to be stored. + template + explicit CoreImpl(TypedConversionConstruct, F&& f) { + using DecayedT = RemoveCVRef; + + constexpr TargetType kTargetType = + (std::is_pointer::value || + std::is_member_pointer::value) + ? TargetType::kPointer + : IsCompatibleAnyInvocable::value + ? TargetType::kCompatibleAnyInvocable + : IsAnyInvocable::value + ? TargetType::kIncompatibleAnyInvocable + : TargetType::kOther; + // NOTE: We only use integers instead of enums as template parameters in + // order to work around a bug on C++14 under MSVC 2017. + // See b/236131881. + Initialize(kTargetType), QualDecayedTRef>( + std::forward(f)); + } + + // Note: QualTRef here includes the cv-ref qualifiers associated with the + // invocation of the Invocable. The unqualified type is the target object + // type to be stored. + template + explicit CoreImpl(absl::in_place_type_t, Args&&... args) { + InitializeStorage(std::forward(args)...); + } + + CoreImpl(CoreImpl&& other) noexcept { + other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_); + manager_ = other.manager_; + invoker_ = other.invoker_; + other.manager_ = EmptyManager; + other.invoker_ = nullptr; + } + + CoreImpl& operator=(CoreImpl&& other) noexcept { + // Put the left-hand operand in an empty state. + // + // Note: A full reset that leaves us with an object that has its invariants + // intact is necessary in order to handle self-move. This is required by + // types that are used with certain operations of the standard library, such + // as the default definition of std::swap when both operands target the same + // object. + Clear(); + + // Perform the actual move/destory operation on the target function. + other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_); + manager_ = other.manager_; + invoker_ = other.invoker_; + other.manager_ = EmptyManager; + other.invoker_ = nullptr; + + return *this; + } + + ~CoreImpl() { manager_(FunctionToCall::dispose, &state_, &state_); } + + // Check whether or not the AnyInvocable is in the empty state. + bool HasValue() const { return invoker_ != nullptr; } + + // Effects: Puts the object into its empty state. + void Clear() { + manager_(FunctionToCall::dispose, &state_, &state_); + manager_ = EmptyManager; + invoker_ = nullptr; + } + + template = 0> + void Initialize(F&& f) { +// This condition handles types that decay into pointers, which includes +// function references. Since function references cannot be null, GCC warns +// against comparing their decayed form with nullptr. +// Since this is template-heavy code, we prefer to disable these warnings +// locally instead of adding yet another overload of this function. +#if !defined(__clang__) && defined(__GNUC__) +// #pragma GCC diagnostic ignored "-Wpragmas" +// #pragma GCC diagnostic ignored "-Waddress" +// #pragma GCC diagnostic ignored "-Wnonnull-compare" +// #pragma GCC diagnostic push +#endif + if (static_cast>(f) == nullptr) { +#if !defined(__clang__) && defined(__GNUC__) +// #pragma GCC diagnostic pop +#endif + manager_ = EmptyManager; + invoker_ = nullptr; + return; + } + InitializeStorage(std::forward(f)); + } + + template = 0> + void Initialize(F&& f) { + // In this case we can "steal the guts" of the other AnyInvocable. + f.manager_(FunctionToCall::relocate_from_to, &f.state_, &state_); + manager_ = f.manager_; + invoker_ = f.invoker_; + + f.manager_ = EmptyManager; + f.invoker_ = nullptr; + } + + template = 0> + void Initialize(F&& f) { + if (f.HasValue()) { + InitializeStorage(std::forward(f)); + } else { + manager_ = EmptyManager; + invoker_ = nullptr; + } + } + + template > + void Initialize(F&& f) { + InitializeStorage(std::forward(f)); + } + + // Use local (inline) storage for applicable target object types. + template >::value>> + void InitializeStorage(Args&&... args) { + using RawT = RemoveCVRef; + ::new (static_cast(&state_.storage)) + RawT(std::forward(args)...); + + invoker_ = LocalInvoker; + // We can simplify our manager if we know the type is trivially copyable. + InitializeLocalManager(); + } + + // Use remote storage for target objects that cannot be stored locally. + template >::value, + int> = 0> + void InitializeStorage(Args&&... args) { + InitializeRemoteManager>(std::forward(args)...); + // This is set after everything else in case an exception is thrown in an + // earlier step of the initialization. + invoker_ = RemoteInvoker; + } + + template ::value>> + void InitializeLocalManager() { + manager_ = LocalManagerTrivial; + } + + template ::value, int> = 0> + void InitializeLocalManager() { + manager_ = LocalManagerNontrivial; + } + + template + using HasTrivialRemoteStorage = + std::integral_constant::value && + alignof(T) <= + ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT>; + + template ::value>> + void InitializeRemoteManager(Args&&... args) { + // unique_ptr is used for exception-safety in case construction throws. + std::unique_ptr uninitialized_target( + ::operator new(sizeof(T)), TrivialDeleter(sizeof(T))); + ::new (uninitialized_target.get()) T(std::forward(args)...); + state_.remote.target = uninitialized_target.release(); + state_.remote.size = sizeof(T); + manager_ = RemoteManagerTrivial; + } + + template ::value, int> = 0> + void InitializeRemoteManager(Args&&... args) { + state_.remote.target = ::new T(std::forward(args)...); + manager_ = RemoteManagerNontrivial; + } + + ////////////////////////////////////////////////////////////////////////////// + // + // Type trait to determine if the template argument is an AnyInvocable whose + // function type is compatible enough with ours such that we can + // "move the guts" out of it when moving, rather than having to place a new + // object into remote storage. + + template + struct IsCompatibleAnyInvocable { + static constexpr bool value = false; + }; + + template + struct IsCompatibleAnyInvocable> { + static constexpr bool value = + (IsCompatibleConversion)(static_cast< + typename AnyInvocable::CoreImpl*>( + nullptr), + static_cast(nullptr)); + }; + + // + ////////////////////////////////////////////////////////////////////////////// + + TypeErasedState state_; + ManagerType* manager_; + InvokerType* invoker_; +}; + +// A constructor name-tag used with Impl to request the +// conversion-constructor +struct ConversionConstruct {}; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that is normally an identity metafunction except that when +// given a std::reference_wrapper, it yields T&. This is necessary because +// currently std::reference_wrapper's operator() is not conditionally noexcept, +// so when checking if such an Invocable is nothrow-invocable, we must pull out +// the underlying type. +template +struct UnwrapStdReferenceWrapperImpl { + using type = T; +}; + +template +struct UnwrapStdReferenceWrapperImpl> { + using type = T&; +}; + +template +using UnwrapStdReferenceWrapper = + typename UnwrapStdReferenceWrapperImpl::type; +// +//////////////////////////////////////////////////////////////////////////////// + +// An alias that always yields std::true_type (used with constraints) where +// substitution failures happen when forming the template arguments. +template +using True = + std::integral_constant*) != 0>; + +/*SFINAE constraints for the conversion-constructor.*/ +template , AnyInvocable>::value>> +using CanConvert = + True>::value>, + absl::enable_if_t::template CallIsValid::value>, + absl::enable_if_t< + Impl::template CallIsNoexceptIfSigIsNoexcept::value>, + absl::enable_if_t, F>::value>>; + +/*SFINAE constraints for the std::in_place constructors.*/ +template +using CanEmplace = True< + absl::enable_if_t::template CallIsValid::value>, + absl::enable_if_t< + Impl::template CallIsNoexceptIfSigIsNoexcept::value>, + absl::enable_if_t, Args...>::value>>; + +/*SFINAE constraints for the conversion-assign operator.*/ +template , AnyInvocable>::value>> +using CanAssign = + True::template CallIsValid::value>, + absl::enable_if_t< + Impl::template CallIsNoexceptIfSigIsNoexcept::value>, + absl::enable_if_t, F>::value>>; + +/*SFINAE constraints for the reference-wrapper conversion-assign operator.*/ +template +using CanAssignReferenceWrapper = + True::template CallIsValid>::value>, + absl::enable_if_t::template CallIsNoexceptIfSigIsNoexcept< + std::reference_wrapper>::value>>; + +//////////////////////////////////////////////////////////////////////////////// +// +// The constraint for checking whether or not a call meets the noexcept +// callability requirements. This is a preprocessor macro because specifying it +// this way as opposed to a disjunction/branch can improve the user-side error +// messages and avoids an instantiation of std::is_nothrow_invocable_r in the +// cases where the user did not specify a noexcept function type. +// +#define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT(inv_quals, noex) \ + ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_##noex(inv_quals) + +// The disjunction below is because we can't rely on std::is_nothrow_invocable_r +// to give the right result when ReturnType is non-moveable in toolchains that +// don't treat non-moveable result types correctly. For example this was the +// case in libc++ before commit c3a24882 (2022-05). +#define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true(inv_quals) \ + absl::enable_if_t> inv_quals, \ + P...>, \ + std::conjunction< \ + std::is_nothrow_invocable< \ + UnwrapStdReferenceWrapper> inv_quals, P...>, \ + std::is_same< \ + ReturnType, \ + absl::base_internal::invoke_result_t< \ + UnwrapStdReferenceWrapper> inv_quals, \ + P...>>>>::value> + +#define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false(inv_quals) +// +//////////////////////////////////////////////////////////////////////////////// + +// A macro to generate partial specializations of Impl with the different +// combinations of supported cv/reference qualifiers and noexcept specifier. +// +// Here, `cv` are the cv-qualifiers if any, `ref` is the ref-qualifier if any, +// inv_quals is the reference type to be used when invoking the target, and +// noex is "true" if the function type is noexcept, or false if it is not. +// +// The CallIsValid condition is more complicated than simply using +// absl::base_internal::is_invocable_r because we can't rely on it to give the +// right result when ReturnType is non-moveable in toolchains that don't treat +// non-moveable result types correctly. For example this was the case in libc++ +// before commit c3a24882 (2022-05). +#define ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, noex) \ + template \ + class Impl \ + : public CoreImpl { \ + public: \ + /*The base class, which contains the datamembers and core operations*/ \ + using Core = CoreImpl; \ + \ + /*SFINAE constraint to check if F is invocable with the proper signature*/ \ + template \ + using CallIsValid = True inv_quals, P...>, \ + std::is_same inv_quals, P...>>>::value>>; \ + \ + /*SFINAE constraint to check if F is nothrow-invocable when necessary*/ \ + template \ + using CallIsNoexceptIfSigIsNoexcept = \ + True; \ + \ + /*Put the AnyInvocable into an empty state.*/ \ + Impl() = default; \ + \ + /*The implementation of a conversion-constructor from "f*/ \ + /*This forwards to Core, attaching inv_quals so that the base class*/ \ + /*knows how to properly type-erase the invocation.*/ \ + template \ + explicit Impl(ConversionConstruct, F&& f) \ + : Core(TypedConversionConstruct< \ + typename std::decay::type inv_quals>(), \ + std::forward(f)) {} \ + \ + /*Forward along the in-place construction parameters.*/ \ + template \ + explicit Impl(absl::in_place_type_t, Args&&... args) \ + : Core(absl::in_place_type inv_quals>, \ + std::forward(args)...) {} \ + \ + /*The actual invocation operation with the proper signature*/ \ + ReturnType operator()(P... args) cv ref noexcept(noex) { \ + assert(this->invoker_ != nullptr); \ + return this->invoker_(const_cast(&this->state_), \ + static_cast>(args)...); \ + } \ + } + +// Define the `noexcept(true)` specialization only for C++17 and beyond, when +// `noexcept` is part of the type system. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L +// A convenience macro that defines specializations for the noexcept(true) and +// noexcept(false) forms, given the other properties. +#define ABSL_INTERNAL_ANY_INVOCABLE_IMPL(cv, ref, inv_quals) \ + ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, false); \ + ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, true) +#else +#define ABSL_INTERNAL_ANY_INVOCABLE_IMPL(cv, ref, inv_quals) \ + ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, false) +#endif + +// Non-ref-qualified partial specializations +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, , &); +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, , const&); + +// Lvalue-ref-qualified partial specializations +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, &, &); +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, &, const&); + +// Rvalue-ref-qualified partial specializations +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, &&, &&); +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, &&, const&&); + +// Undef the detail-only macros. +#undef ABSL_INTERNAL_ANY_INVOCABLE_IMPL +#undef ABSL_INTERNAL_ANY_INVOCABLE_IMPL_ +#undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false +#undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true +#undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT +#undef ABSL_INTERNAL_NOEXCEPT_SPEC + +} // namespace internal_any_invocable +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ diff --git a/src/absl/functional/internal/front_binder.h b/src/absl/functional/internal/front_binder.h new file mode 100644 index 0000000..45f52de --- /dev/null +++ b/src/absl/functional/internal/front_binder.h @@ -0,0 +1,95 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Implementation details for `absl::bind_front()`. + +#ifndef ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ +#define ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ + +#include +#include +#include + +#include "absl/base/internal/invoke.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace functional_internal { + +// Invoke the method, expanding the tuple of bound arguments. +template +R Apply(Tuple&& bound, absl::index_sequence, Args&&... free) { + return base_internal::invoke( + absl::forward(bound).template get()..., + absl::forward(free)...); +} + +template +class FrontBinder { + using BoundArgsT = absl::container_internal::CompressedTuple; + using Idx = absl::make_index_sequence; + + BoundArgsT bound_args_; + + public: + template + constexpr explicit FrontBinder(absl::in_place_t, Ts&&... ts) + : bound_args_(absl::forward(ts)...) {} + + template > + R operator()(FreeArgs&&... free_args) & { + return functional_internal::Apply(bound_args_, Idx(), + absl::forward(free_args)...); + } + + template > + R operator()(FreeArgs&&... free_args) const& { + return functional_internal::Apply(bound_args_, Idx(), + absl::forward(free_args)...); + } + + template > + R operator()(FreeArgs&&... free_args) && { + // This overload is called when *this is an rvalue. If some of the bound + // arguments are stored by value or rvalue reference, we move them. + return functional_internal::Apply(absl::move(bound_args_), Idx(), + absl::forward(free_args)...); + } + + template > + R operator()(FreeArgs&&... free_args) const&& { + // This overload is called when *this is an rvalue. If some of the bound + // arguments are stored by value or rvalue reference, we move them. + return functional_internal::Apply(absl::move(bound_args_), Idx(), + absl::forward(free_args)...); + } +}; + +template +using bind_front_t = FrontBinder, absl::decay_t...>; + +} // namespace functional_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ diff --git a/src/absl/functional/internal/function_ref.h b/src/absl/functional/internal/function_ref.h new file mode 100644 index 0000000..b5bb8b4 --- /dev/null +++ b/src/absl/functional/internal/function_ref.h @@ -0,0 +1,106 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ +#define ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ + +#include +#include +#include + +#include "absl/base/internal/invoke.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace functional_internal { + +// Like a void* that can handle function pointers as well. The standard does not +// allow function pointers to round-trip through void*, but void(*)() is fine. +// +// Note: It's important that this class remains trivial and is the same size as +// a pointer, since this allows the compiler to perform tail-call optimizations +// when the underlying function is a callable object with a matching signature. +union VoidPtr { + const void* obj; + void (*fun)(); +}; + +// Chooses the best type for passing T as an argument. +// Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are +// passed by value. +template +constexpr bool PassByValue() { + return !std::is_lvalue_reference::value && + absl::is_trivially_copy_constructible::value && + absl::is_trivially_copy_assignable< + typename std::remove_cv::type>::value && + std::is_trivially_destructible::value && + sizeof(T) <= 2 * sizeof(void*); +} + +template +struct ForwardT : std::conditional(), T, T&&> {}; + +// An Invoker takes a pointer to the type-erased invokable object, followed by +// the arguments that the invokable object expects. +// +// Note: The order of arguments here is an optimization, since member functions +// have an implicit "this" pointer as their first argument, putting VoidPtr +// first allows the compiler to perform tail-call optimization in many cases. +template +using Invoker = R (*)(VoidPtr, typename ForwardT::type...); + +// +// InvokeObject and InvokeFunction provide static "Invoke" functions that can be +// used as Invokers for objects or functions respectively. +// +// static_cast handles the case the return type is void. +template +R InvokeObject(VoidPtr ptr, typename ForwardT::type... args) { + auto o = static_cast(ptr.obj); + return static_cast( + absl::base_internal::invoke(*o, std::forward(args)...)); +} + +template +R InvokeFunction(VoidPtr ptr, typename ForwardT::type... args) { + auto f = reinterpret_cast(ptr.fun); + return static_cast( + absl::base_internal::invoke(f, std::forward(args)...)); +} + +template +void AssertNonNull(const std::function& f) { + assert(f != nullptr); + (void)f; +} + +template +void AssertNonNull(const F&) {} + +template +void AssertNonNull(F C::*f) { + assert(f != nullptr); + (void)f; +} + +template +using EnableIf = typename ::std::enable_if::type; + +} // namespace functional_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ diff --git a/src/absl/memory/memory.h b/src/absl/memory/memory.h new file mode 100644 index 0000000..d633260 --- /dev/null +++ b/src/absl/memory/memory.h @@ -0,0 +1,699 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: memory.h +// ----------------------------------------------------------------------------- +// +// This header file contains utility functions for managing the creation and +// conversion of smart pointers. This file is an extension to the C++ +// standard library header file. + +#ifndef ABSL_MEMORY_MEMORY_H_ +#define ABSL_MEMORY_MEMORY_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// ----------------------------------------------------------------------------- +// Function Template: 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. +// +// Do not call WrapUnique with an explicit type, as in +// `WrapUnique(NewX(1, 2))`. The purpose of WrapUnique is to automatically +// deduce the pointer type. If you wish to make the type explicit, just use +// `std::unique_ptr` directly. +// +// auto x = std::unique_ptr(NewX(1, 2)); +// - or - +// std::unique_ptr x(NewX(1, 2)); +// +// While `absl::WrapUnique` is useful for capturing the output of a raw +// pointer factory, prefer 'absl::make_unique(args...)' over +// 'absl::WrapUnique(new T(args...))'. +// +// auto x = WrapUnique(new X(1, 2)); // works, but nonideal. +// auto x = make_unique(1, 2); // safer, standard, avoids raw 'new'. +// +// Note that `absl::WrapUnique(p)` is valid only if `delete p` is a valid +// expression. In particular, `absl::WrapUnique()` cannot wrap pointers to +// arrays, functions or void, and it must not be used to capture pointers +// obtained from array-new expressions (even though that would compile!). +template +std::unique_ptr WrapUnique(T* ptr) { + static_assert(!std::is_array::value, "array types are unsupported"); + static_assert(std::is_object::value, "non-object types are unsupported"); + return std::unique_ptr(ptr); +} + +namespace memory_internal { + +// Traits to select proper overload and return type for `absl::make_unique<>`. +template +struct MakeUniqueResult { + using scalar = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using array = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using invalid = void; +}; + +} // namespace memory_internal + +// gcc 4.8 has __cplusplus at 201301 but the libstdc++ shipped with it 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(__GLIBCXX__) && !defined(__cpp_lib_make_unique)) +using std::make_unique; +#else +// ----------------------------------------------------------------------------- +// Function Template: make_unique() +// ----------------------------------------------------------------------------- +// +// Creates a `std::unique_ptr<>`, while avoiding issues creating temporaries +// during the construction process. `absl::make_unique<>` also avoids redundant +// type declarations, by avoiding the need to explicitly use the `new` operator. +// +// This implementation of `absl::make_unique<>` is designed for C++11 code and +// will be replaced in C++14 by the equivalent `std::make_unique<>` abstraction. +// `absl::make_unique<>` is designed to be 100% compatible with +// `std::make_unique<>` so that the eventual migration will involve a simple +// rename operation. +// +// For more background on why `std::unique_ptr(new T(a,b))` is problematic, +// see Herb Sutter's explanation on +// (Exception-Safe Function Calls)[https://herbsutter.com/gotw/_102/]. +// (In general, reviewers should treat `new T(a,b)` with scrutiny.) +// +// Example usage: +// +// auto p = make_unique(args...); // 'p' is a std::unique_ptr +// auto pa = make_unique(5); // 'pa' is a std::unique_ptr +// +// Three overloads of `absl::make_unique` are required: +// +// - For non-array T: +// +// Allocates a T with `new T(std::forward args...)`, +// forwarding all `args` to T's constructor. +// Returns a `std::unique_ptr` owning that object. +// +// - For an array of unknown bounds T[]: +// +// `absl::make_unique<>` will allocate an array T of type U[] with +// `new U[n]()` and return a `std::unique_ptr` owning that array. +// +// Note that 'U[n]()' is different from 'U[n]', and elements will be +// value-initialized. Note as well that `std::unique_ptr` will perform its +// own destruction of the array elements upon leaving scope, even though +// the array [] does not have a default destructor. +// +// NOTE: an array of unknown bounds T[] may still be (and often will be) +// initialized to have a size, and will still use this overload. E.g: +// +// auto my_array = absl::make_unique(10); +// +// - For an array of known bounds T[N]: +// +// `absl::make_unique<>` is deleted (like with `std::make_unique<>`) as +// this overload is not useful. +// +// NOTE: an array of known bounds T[N] is not considered a useful +// construction, and may cause undefined behavior in templates. E.g: +// +// auto my_array = absl::make_unique(); +// +// In those cases, of course, you can still use the overload above and +// simply initialize it to its desired size: +// +// auto my_array = absl::make_unique(10); + +// `absl::make_unique` overload for non-array types. +template +typename memory_internal::MakeUniqueResult::scalar make_unique( + Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +// `absl::make_unique` overload for an array T[] of unknown bounds. +// The array allocation needs to use the `new T[size]` form and cannot take +// element constructor arguments. The `std::unique_ptr` will manage destructing +// these array elements. +template +typename memory_internal::MakeUniqueResult::array make_unique(size_t n) { + return std::unique_ptr(new typename absl::remove_extent_t[n]()); +} + +// `absl::make_unique` overload for an array T[N] of known bounds. +// This construction will be rejected. +template +typename memory_internal::MakeUniqueResult::invalid make_unique( + Args&&... /* args */) = delete; +#endif + +// ----------------------------------------------------------------------------- +// Function Template: RawPtr() +// ----------------------------------------------------------------------------- +// +// Extracts the raw pointer from a pointer-like value `ptr`. `absl::RawPtr` is +// useful within templates that need to handle a complement of raw pointers, +// `std::nullptr_t`, and smart pointers. +template +auto RawPtr(T&& ptr) -> decltype(std::addressof(*ptr)) { + // ptr is a forwarding reference to support Ts with non-const operators. + return (ptr != nullptr) ? std::addressof(*ptr) : nullptr; +} +inline std::nullptr_t RawPtr(std::nullptr_t) { return nullptr; } + +// ----------------------------------------------------------------------------- +// Function Template: ShareUniquePtr() +// ----------------------------------------------------------------------------- +// +// Adopts a `std::unique_ptr` rvalue and returns a `std::shared_ptr` of deduced +// type. Ownership (if any) of the held value is transferred to the returned +// shared pointer. +// +// Example: +// +// auto up = absl::make_unique(10); +// auto sp = absl::ShareUniquePtr(std::move(up)); // shared_ptr +// CHECK_EQ(*sp, 10); +// CHECK(up == nullptr); +// +// Note that this conversion is correct even when T is an array type, and more +// generally it works for *any* deleter of the `unique_ptr` (single-object +// deleter, array deleter, or any custom deleter), since the deleter is adopted +// by the shared pointer as well. The deleter is copied (unless it is a +// reference). +// +// Implements the resolution of [LWG 2415](http://wg21.link/lwg2415), by which a +// null shared pointer does not attempt to call the deleter. +template +std::shared_ptr ShareUniquePtr(std::unique_ptr&& ptr) { + return ptr ? std::shared_ptr(std::move(ptr)) : std::shared_ptr(); +} + +// ----------------------------------------------------------------------------- +// Function Template: WeakenPtr() +// ----------------------------------------------------------------------------- +// +// Creates a weak pointer associated with a given shared pointer. The returned +// value is a `std::weak_ptr` of deduced type. +// +// Example: +// +// auto sp = std::make_shared(10); +// auto wp = absl::WeakenPtr(sp); +// CHECK_EQ(sp.get(), wp.lock().get()); +// sp.reset(); +// CHECK(wp.lock() == nullptr); +// +template +std::weak_ptr WeakenPtr(const std::shared_ptr& ptr) { + return std::weak_ptr(ptr); +} + +namespace memory_internal { + +// ExtractOr::type evaluates to E if possible. Otherwise, D. +template

    l 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 WR~FxgOuzmEc9s@b5UUa~3iuzL1+`oN literal 0 HcmV?d00001 diff --git a/man/s2-package.Rd b/man/s2-package.Rd new file mode 100644 index 0000000..980c7dc --- /dev/null +++ b/man/s2-package.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-package.R +\docType{package} +\name{s2-package} +\alias{s2} +\alias{s2-package} +\title{s2: Spherical Geometry Operators Using the S2 Geometry Library} +\description{ +Provides R bindings for Google's s2 library for geometric calculations on the sphere. High-performance constructors and exporters provide high compatibility with existing spatial packages, transformers construct new geometries from existing geometries, predicates provide a means to select geometries based on spatial relationships, and accessors extract information about geometries. +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://r-spatial.github.io/s2/} + \item \url{https://github.com/r-spatial/s2} + \item \url{https://s2geometry.io/} + \item Report bugs at \url{https://github.com/r-spatial/s2/issues} +} + +} +\author{ +\strong{Maintainer}: Edzer Pebesma \email{edzer.pebesma@uni-muenster.de} (\href{https://orcid.org/0000-0001-8049-7069}{ORCID}) + +Authors: +\itemize{ + \item Dewey Dunnington \email{dewey@fishandwhistle.net} (\href{https://orcid.org/0000-0002-9415-4582}{ORCID}) + \item Ege Rubak \email{rubak@math.aau.dk} +} + +Other contributors: +\itemize{ + \item Jeroen Ooms \email{jeroen.ooms@stat.ucla.edu} (configure script) [contributor] + \item Google, Inc. (Original s2geometry.io source code) [copyright holder] +} + +} +\keyword{internal} diff --git a/man/s2_boundary.Rd b/man/s2_boundary.Rd new file mode 100644 index 0000000..d6a5665 --- /dev/null +++ b/man/s2_boundary.Rd @@ -0,0 +1,215 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-transformers.R +\name{s2_boundary} +\alias{s2_boundary} +\alias{s2_centroid} +\alias{s2_closest_point} +\alias{s2_minimum_clearance_line_between} +\alias{s2_difference} +\alias{s2_sym_difference} +\alias{s2_intersection} +\alias{s2_union} +\alias{s2_snap_to_grid} +\alias{s2_simplify} +\alias{s2_rebuild} +\alias{s2_buffer_cells} +\alias{s2_convex_hull} +\alias{s2_centroid_agg} +\alias{s2_coverage_union_agg} +\alias{s2_rebuild_agg} +\alias{s2_union_agg} +\alias{s2_convex_hull_agg} +\alias{s2_point_on_surface} +\title{S2 Geography Transformations} +\usage{ +s2_boundary(x) + +s2_centroid(x) + +s2_closest_point(x, y) + +s2_minimum_clearance_line_between(x, y) + +s2_difference(x, y, options = s2_options()) + +s2_sym_difference(x, y, options = s2_options()) + +s2_intersection(x, y, options = s2_options()) + +s2_union(x, y = NULL, options = s2_options()) + +s2_snap_to_grid(x, grid_size) + +s2_simplify(x, tolerance, radius = s2_earth_radius_meters()) + +s2_rebuild(x, options = s2_options()) + +s2_buffer_cells( + x, + distance, + max_cells = 1000, + min_level = -1, + radius = s2_earth_radius_meters() +) + +s2_convex_hull(x) + +s2_centroid_agg(x, na.rm = FALSE) + +s2_coverage_union_agg(x, options = s2_options(), na.rm = FALSE) + +s2_rebuild_agg(x, options = s2_options(), na.rm = FALSE) + +s2_union_agg(x, options = s2_options(), na.rm = FALSE) + +s2_convex_hull_agg(x, na.rm = FALSE) + +s2_point_on_surface(x, na.rm = FALSE) +} +\arguments{ +\item{x, y}{\link[=as_s2_geography]{geography vectors}. These inputs +are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects +(e.g., character vectors of well-known text) directly.} + +\item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline +model to use and the snap level.} + +\item{grid_size}{The grid size to which coordinates should be snapped; +will be rounded to the nearest power of 10.} + +\item{tolerance}{The minimum distance between vertexes to use when +simplifying a geography.} + +\item{radius}{Radius of the earth. Defaults to the average radius of +the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} + +\item{distance}{The distance to buffer, in units of \code{radius}.} + +\item{max_cells}{The maximum number of cells to approximate a buffer.} + +\item{min_level}{The minimum cell level used to approximate a buffer +(1 - 30). Setting this value too high will result in unnecessarily +large geographies, but may help improve buffers along long, narrow +regions.} + +\item{na.rm}{For aggregate calculations use \code{na.rm = TRUE} +to drop missing values.} +} +\description{ +These functions operate on one or more geography vectors and +return a geography vector. +} +\section{Model}{ + +The geometry model indicates whether or not a geometry includes its boundaries. +Boundaries of line geometries are its end points. +OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED +geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries +(\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons +do not overlap or two lines do not cross, no point exist that belong to +more than one of the geometries. (This latter form, half-closed, is +not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on +which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) +and covers/covered_by (closed) correspond to the SFA standard specification +of these operators. +} + +\examples{ +# returns the boundary: +# empty for point, endpoints of a linestring, +# perimeter of a polygon +s2_boundary("POINT (-64 45)") +s2_boundary("LINESTRING (0 0, 10 0)") +s2_boundary("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") + +# returns the area-weighted centroid, element-wise +s2_centroid("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") +s2_centroid("LINESTRING (0 0, 10 0)") + +# s2_point_on_surface guarantees a point on surface +# Note: this is not the same as st_point_on_surface +s2_centroid("POLYGON ((0 0, 10 0, 1 1, 0 10, 0 0))") +s2_point_on_surface("POLYGON ((0 0, 10 0, 1 1, 0 10, 0 0))") + +# returns the unweighted centroid of the entire input +s2_centroid_agg(c("POINT (0 0)", "POINT (10 0)")) + +# returns the closest point on x to y +s2_closest_point( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POINT (0 90)" # north pole! +) + +# returns the shortest possible line between x and y +s2_minimum_clearance_line_between( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POINT (0 90)" # north pole! +) + +# binary operations: difference, symmetric difference, intersection and union +s2_difference( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", + # 32 bit platforms may need to set snap rounding + s2_options(snap = s2_snap_level(30)) +) + +s2_sym_difference( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", + # 32 bit platforms may need to set snap rounding + s2_options(snap = s2_snap_level(30)) +) + +s2_intersection( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", + # 32 bit platforms may need to set snap rounding + s2_options(snap = s2_snap_level(30)) +) + +s2_union( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", + # 32 bit platforms may need to set snap rounding + s2_options(snap = s2_snap_level(30)) +) + +# s2_convex_hull_agg builds the convex hull of a list of geometries +s2_convex_hull_agg( + c( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" + ) +) + +# use s2_union_agg() to aggregate geographies in a vector +s2_coverage_union_agg( + c( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" + ), + # 32 bit platforms may need to set snap rounding + s2_options(snap = s2_snap_level(30)) +) + +# snap to grid rounds coordinates to a specified grid size +s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2) + + +} +\seealso{ +BigQuery's geography function reference: +\itemize{ +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_boundary}{ST_BOUNDARY} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_centroid}{ST_CENTROID} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_closestpoint}{ST_CLOSESTPOINT} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_difference}{ST_DIFFERENCE} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersection}{ST_INTERSECTION} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union}{ST_UNION} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_snaptogrid}{ST_SNAPTOGRID} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_simplify}{ST_SIMPLIFY} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union_agg}{ST_UNION_AGG} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#s2_centroid_agg}{ST_CENTROID_AGG} +} +} diff --git a/man/s2_bounds_cap.Rd b/man/s2_bounds_cap.Rd new file mode 100644 index 0000000..659534f --- /dev/null +++ b/man/s2_bounds_cap.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-bounds.R +\name{s2_bounds_cap} +\alias{s2_bounds_cap} +\alias{s2_bounds_rect} +\title{Compute feature-wise and aggregate bounds} +\usage{ +s2_bounds_cap(x) + +s2_bounds_rect(x) +} +\arguments{ +\item{x}{An \code{\link[=s2_geography]{s2_geography()}} vector.} +} +\value{ +Both functions return a \code{data.frame}: +\itemize{ +\item \code{\link[=s2_bounds_rect]{s2_bounds_rect()}}: Columns \code{minlng}, \code{minlat}, \code{maxlng}, \code{maxlat} (degrees) +\item \code{\link[=s2_bounds_cap]{s2_bounds_cap()}}: Columns \code{lng}, \code{lat}, \code{angle} (degrees) +} +} +\description{ +\code{\link[=s2_bounds_rect]{s2_bounds_rect()}} returns a bounding latitude-longitude +rectangle that contains the region; \code{\link[=s2_bounds_cap]{s2_bounds_cap()}} returns a bounding circle +represented by a centre point (lat, lng) and an angle. The bound may not be tight +for points, polylines and geometry collections. The rectangle returned may depend on +the order of points or polylines. \code{lng_lo} values larger than \code{lng_hi} indicate +regions that span the antimeridian, see the Fiji example. +} +\examples{ +s2_bounds_cap(s2_data_countries("Antarctica")) +s2_bounds_cap(s2_data_countries("Netherlands")) +s2_bounds_cap(s2_data_countries("Fiji")) + +s2_bounds_rect(s2_data_countries("Antarctica")) +s2_bounds_rect(s2_data_countries("Netherlands")) +s2_bounds_rect(s2_data_countries("Fiji")) + +} diff --git a/man/s2_cell.Rd b/man/s2_cell.Rd new file mode 100644 index 0000000..d525599 --- /dev/null +++ b/man/s2_cell.Rd @@ -0,0 +1,74 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-cell.R +\name{s2_cell} +\alias{s2_cell} +\alias{s2_cell_sentinel} +\alias{s2_cell_invalid} +\alias{as_s2_cell} +\alias{as_s2_cell.s2_cell} +\alias{as_s2_cell.character} +\alias{as_s2_cell.s2_geography} +\alias{as_s2_cell.wk_xy} +\alias{new_s2_cell} +\title{Create S2 Cell vectors} +\usage{ +s2_cell(x = character()) + +s2_cell_sentinel() + +s2_cell_invalid() + +s2_cell_sentinel() + +as_s2_cell(x, ...) + +\method{as_s2_cell}{s2_cell}(x, ...) + +\method{as_s2_cell}{character}(x, ...) + +\method{as_s2_cell}{s2_geography}(x, ...) + +\method{as_s2_cell}{wk_xy}(x, ...) + +new_s2_cell(x) +} +\arguments{ +\item{x}{The canonical S2 cell identifier as a character vector.} + +\item{...}{Passed to methods} +} +\value{ +An object of class s2_cell +} +\description{ +The S2 cell indexing system forms the basis for spatial indexing +in the S2 library. On their own, S2 cells can represent points +or areas. As a union, a vector of S2 cells can approximate a +line or polygon. These functions allow direct access to the +S2 cell indexing system and are designed to have minimal overhead +such that looping and recursion have acceptable performance +when used within R code. +} +\details{ +Under the hood, S2 cell vectors are represented in R as vectors +of type \code{\link[=double]{double()}}. This works because S2 cell identifiers are +64 bits wide, as are \code{double}s on all systems where R runs (The +same trick is used by the bit64 package to represent signed +64-bit integers). As a happy accident, \code{NA_real_} is not a valid +or meaningful cell identifier, so missing value support in the +way R users might expect is preserved. It is worth noting that +the underlying value of \code{s2_cell_sentinel()} would normally be +considered \code{NA}; however, as it is meaningful and useful when +programming with S2 cells, custom \code{is.na()} and comparison methods +are implemented such that \code{s2_cell_sentinel()} is greater than +all valid S2 cells and not considered missing. Users can and should +implement compiled code that uses the underlying bytes of the +vector, ensuring that the class of any returned object that should +be interpreted in this way is constructed with \code{new_s2_cell()}. +} +\examples{ +s2_cell("4b59a0cd83b5de49") +as_s2_cell(s2_lnglat(-64, 45)) +as_s2_cell(s2_data_cities("Ottawa")) + +} diff --git a/man/s2_cell_is_valid.Rd b/man/s2_cell_is_valid.Rd new file mode 100644 index 0000000..d6cfd24 --- /dev/null +++ b/man/s2_cell_is_valid.Rd @@ -0,0 +1,82 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-cell.R +\name{s2_cell_is_valid} +\alias{s2_cell_is_valid} +\alias{s2_cell_debug_string} +\alias{s2_cell_to_lnglat} +\alias{s2_cell_center} +\alias{s2_cell_boundary} +\alias{s2_cell_polygon} +\alias{s2_cell_vertex} +\alias{s2_cell_level} +\alias{s2_cell_is_leaf} +\alias{s2_cell_is_face} +\alias{s2_cell_area} +\alias{s2_cell_area_approx} +\alias{s2_cell_parent} +\alias{s2_cell_child} +\alias{s2_cell_edge_neighbour} +\alias{s2_cell_contains} +\alias{s2_cell_distance} +\alias{s2_cell_max_distance} +\alias{s2_cell_may_intersect} +\alias{s2_cell_common_ancestor_level} +\alias{s2_cell_common_ancestor_level_agg} +\title{S2 cell operators} +\usage{ +s2_cell_is_valid(x) + +s2_cell_debug_string(x) + +s2_cell_to_lnglat(x) + +s2_cell_center(x) + +s2_cell_boundary(x) + +s2_cell_polygon(x) + +s2_cell_vertex(x, k) + +s2_cell_level(x) + +s2_cell_is_leaf(x) + +s2_cell_is_face(x) + +s2_cell_area(x, radius = s2_earth_radius_meters()) + +s2_cell_area_approx(x, radius = s2_earth_radius_meters()) + +s2_cell_parent(x, level = -1L) + +s2_cell_child(x, k) + +s2_cell_edge_neighbour(x, k) + +s2_cell_contains(x, y) + +s2_cell_distance(x, y, radius = s2_earth_radius_meters()) + +s2_cell_max_distance(x, y, radius = s2_earth_radius_meters()) + +s2_cell_may_intersect(x, y) + +s2_cell_common_ancestor_level(x, y) + +s2_cell_common_ancestor_level_agg(x, na.rm = FALSE) +} +\arguments{ +\item{x, y}{An \code{\link[=s2_cell]{s2_cell()}} vector} + +\item{k}{An integer between 0 and 3} + +\item{radius}{The radius to use (e.g., \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}})} + +\item{level}{An integer between 0 and 30, inclusive.} + +\item{na.rm}{Remove NAs prior to computing aggregate?} +} +\description{ +S2 cell operators +} diff --git a/man/s2_cell_union.Rd b/man/s2_cell_union.Rd new file mode 100644 index 0000000..4af519a --- /dev/null +++ b/man/s2_cell_union.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-cell-union.R +\name{s2_cell_union} +\alias{s2_cell_union} +\alias{as_s2_geography.s2_cell_union} +\alias{as_s2_cell_union} +\alias{as_s2_cell_union.s2_cell_union} +\alias{as_s2_cell_union.s2_cell} +\alias{as_s2_cell_union.character} +\title{Create S2 Cell Union vectors} +\usage{ +s2_cell_union(x = list()) + +\method{as_s2_geography}{s2_cell_union}(x, ...) + +as_s2_cell_union(x, ...) + +\method{as_s2_cell_union}{s2_cell_union}(x, ...) + +\method{as_s2_cell_union}{s2_cell}(x, ...) + +\method{as_s2_cell_union}{character}(x, ...) +} +\arguments{ +\item{x}{A \code{list()} of \code{\link[=s2_cell]{s2_cell()}} vectors.} + +\item{...}{Passed to S3 methods} +} +\value{ +An object of class "s2_cell_union". +} +\description{ +Create S2 Cell Union vectors +} diff --git a/man/s2_cell_union_normalize.Rd b/man/s2_cell_union_normalize.Rd new file mode 100644 index 0000000..208da9a --- /dev/null +++ b/man/s2_cell_union_normalize.Rd @@ -0,0 +1,66 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-cell-union.R +\name{s2_cell_union_normalize} +\alias{s2_cell_union_normalize} +\alias{s2_cell_union_contains} +\alias{s2_cell_union_intersects} +\alias{s2_cell_union_intersection} +\alias{s2_cell_union_union} +\alias{s2_cell_union_difference} +\alias{s2_covering_cell_ids} +\alias{s2_covering_cell_ids_agg} +\title{S2 cell union operators} +\usage{ +s2_cell_union_normalize(x) + +s2_cell_union_contains(x, y) + +s2_cell_union_intersects(x, y) + +s2_cell_union_intersection(x, y) + +s2_cell_union_union(x, y) + +s2_cell_union_difference(x, y) + +s2_covering_cell_ids( + x, + min_level = 0, + max_level = 30, + max_cells = 8, + buffer = 0, + interior = FALSE, + radius = s2_earth_radius_meters() +) + +s2_covering_cell_ids_agg( + x, + min_level = 0, + max_level = 30, + max_cells = 8, + buffer = 0, + interior = FALSE, + radius = s2_earth_radius_meters(), + na.rm = FALSE +) +} +\arguments{ +\item{x, y}{An \link[=as_s2_geography]{s2_geography} or \code{\link[=s2_cell_union]{s2_cell_union()}}.} + +\item{min_level, max_level}{The minimum and maximum levels to constrain the +covering.} + +\item{max_cells}{The maximum number of cells in the covering. Defaults to +8.} + +\item{buffer}{A distance to buffer outside the geography} + +\item{interior}{Use \code{TRUE} to force the covering inside the geography.} + +\item{radius}{The radius to use (e.g., \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}})} + +\item{na.rm}{Remove NAs prior to computing aggregate?} +} +\description{ +S2 cell union operators +} diff --git a/man/s2_closest_feature.Rd b/man/s2_closest_feature.Rd new file mode 100644 index 0000000..093109e --- /dev/null +++ b/man/s2_closest_feature.Rd @@ -0,0 +1,132 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-matrix.R +\name{s2_closest_feature} +\alias{s2_closest_feature} +\alias{s2_closest_edges} +\alias{s2_farthest_feature} +\alias{s2_distance_matrix} +\alias{s2_max_distance_matrix} +\alias{s2_contains_matrix} +\alias{s2_within_matrix} +\alias{s2_covers_matrix} +\alias{s2_covered_by_matrix} +\alias{s2_intersects_matrix} +\alias{s2_disjoint_matrix} +\alias{s2_equals_matrix} +\alias{s2_touches_matrix} +\alias{s2_dwithin_matrix} +\alias{s2_may_intersect_matrix} +\title{Matrix Functions} +\usage{ +s2_closest_feature(x, y) + +s2_closest_edges( + x, + y, + k, + min_distance = -1, + max_distance = Inf, + radius = s2_earth_radius_meters() +) + +s2_farthest_feature(x, y) + +s2_distance_matrix(x, y, radius = s2_earth_radius_meters()) + +s2_max_distance_matrix(x, y, radius = s2_earth_radius_meters()) + +s2_contains_matrix(x, y, options = s2_options(model = "open")) + +s2_within_matrix(x, y, options = s2_options(model = "open")) + +s2_covers_matrix(x, y, options = s2_options(model = "closed")) + +s2_covered_by_matrix(x, y, options = s2_options(model = "closed")) + +s2_intersects_matrix(x, y, options = s2_options()) + +s2_disjoint_matrix(x, y, options = s2_options()) + +s2_equals_matrix(x, y, options = s2_options()) + +s2_touches_matrix(x, y, options = s2_options()) + +s2_dwithin_matrix(x, y, distance, radius = s2_earth_radius_meters()) + +s2_may_intersect_matrix(x, y, max_edges_per_cell = 50, max_feature_cells = 4) +} +\arguments{ +\item{x, y}{Geography vectors, coerced using \code{\link[=as_s2_geography]{as_s2_geography()}}. +\code{x} is considered the source, where as \code{y} is considered the target.} + +\item{k}{The number of closest edges to consider when searching. Note +that in S2 a point is also considered an edge.} + +\item{min_distance}{The minimum distance to consider when searching for +edges. This filter is applied after the search is complete (i.e., +may cause fewer than \code{k} values to be returned).} + +\item{max_distance}{The maximum distance to consider when searching for +edges. This filter is applied before the search.} + +\item{radius}{Radius of the earth. Defaults to the average radius of +the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} + +\item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline +model to use and the snap level.} + +\item{distance}{A distance on the surface of the earth in the same units +as \code{radius}.} + +\item{max_edges_per_cell}{For \code{\link[=s2_may_intersect_matrix]{s2_may_intersect_matrix()}}, +this values controls the nature of the index on \code{y}, with higher values +leading to coarser index. Values should be between 10 and 50; the default +of 50 is adequate for most use cases, but for specialized operations users +may wish to use a lower value to increase performance.} + +\item{max_feature_cells}{For \code{\link[=s2_may_intersect_matrix]{s2_may_intersect_matrix()}}, this value +controls the approximation of \code{x} used to identify potential intersections +on \code{y}. The default value of 4 gives the best performance for most operations, +but for specialized operations users may wish to use a higher value to increase +performance.} +} +\value{ +A vector of length \code{x}. +} +\description{ +These functions are similar to accessors and predicates, but instead of +recycling \code{x} and \code{y} to a common length and returning a vector of that +length, these functions return a vector of length \code{x} with each element +\code{i} containing information about how the entire vector \code{y} relates to +the feature at \code{x[i]}. +} +\examples{ +city_names <- c("Vatican City", "San Marino", "Luxembourg") +cities <- s2_data_cities(city_names) +country_names <- s2_data_tbl_countries$name +countries <- s2_data_countries() + +# closest feature returns y indices of the closest feature +# for each feature in x +country_names[s2_closest_feature(cities, countries)] + +# farthest feature returns y indices of the farthest feature +# for each feature in x +country_names[s2_farthest_feature(cities, countries)] + +# use s2_closest_edges() to find the k-nearest neighbours +nearest <- s2_closest_edges(cities, cities, k = 2, min_distance = 0) +city_names +city_names[unlist(nearest)] + +# predicate matrices +country_names[s2_intersects_matrix(cities, countries)[[1]]] + +# distance matrices +s2_distance_matrix(cities, cities) +s2_max_distance_matrix(cities, countries[1:4]) + +} +\seealso{ +See pairwise predicate functions (e.g., \code{\link[=s2_intersects]{s2_intersects()}}). +} diff --git a/man/s2_contains.Rd b/man/s2_contains.Rd new file mode 100644 index 0000000..514d01e --- /dev/null +++ b/man/s2_contains.Rd @@ -0,0 +1,169 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-predicates.R +\name{s2_contains} +\alias{s2_contains} +\alias{s2_within} +\alias{s2_covered_by} +\alias{s2_covers} +\alias{s2_disjoint} +\alias{s2_intersects} +\alias{s2_equals} +\alias{s2_intersects_box} +\alias{s2_touches} +\alias{s2_dwithin} +\alias{s2_prepared_dwithin} +\title{S2 Geography Predicates} +\usage{ +s2_contains(x, y, options = s2_options(model = "open")) + +s2_within(x, y, options = s2_options(model = "open")) + +s2_covered_by(x, y, options = s2_options(model = "closed")) + +s2_covers(x, y, options = s2_options(model = "closed")) + +s2_disjoint(x, y, options = s2_options()) + +s2_intersects(x, y, options = s2_options()) + +s2_equals(x, y, options = s2_options()) + +s2_intersects_box( + x, + lng1, + lat1, + lng2, + lat2, + detail = 1000, + options = s2_options() +) + +s2_touches(x, y, options = s2_options()) + +s2_dwithin(x, y, distance, radius = s2_earth_radius_meters()) + +s2_prepared_dwithin(x, y, distance, radius = s2_earth_radius_meters()) +} +\arguments{ +\item{x, y}{\link[=as_s2_geography]{geography vectors}. These inputs +are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects +(e.g., character vectors of well-known text) directly.} + +\item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline +model to use and the snap level.} + +\item{lng1, lat1, lng2, lat2}{A latitude/longitude range} + +\item{detail}{The number of points with which to approximate +non-geodesic edges.} + +\item{distance}{A distance on the surface of the earth in the same units +as \code{radius}.} + +\item{radius}{Radius of the earth. Defaults to the average radius of +the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} +} +\description{ +These functions operate two geography vectors (pairwise), and return +a logical vector. +} +\section{Model}{ + +The geometry model indicates whether or not a geometry includes its boundaries. +Boundaries of line geometries are its end points. +OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED +geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries +(\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons +do not overlap or two lines do not cross, no point exist that belong to +more than one of the geometries. (This latter form, half-closed, is +not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on +which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) +and covers/covered_by (closed) correspond to the SFA standard specification +of these operators. +} + +\examples{ +s2_contains( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c("POINT (5 5)", "POINT (-1 1)") +) + +s2_within( + c("POINT (5 5)", "POINT (-1 1)"), + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))" +) + +s2_covered_by( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c("POINT (5 5)", "POINT (-1 1)") +) + +s2_covers( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c("POINT (5 5)", "POINT (-1 1)") +) + +s2_disjoint( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c("POINT (5 5)", "POINT (-1 1)") +) + +s2_intersects( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c("POINT (5 5)", "POINT (-1 1)") +) + +s2_equals( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0))", + "POLYGON ((-1 -1, 10 0, 10 10, 0 10, -1 -1))" + ) +) + +s2_intersects( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c("POINT (5 5)", "POINT (-1 1)") +) + +s2_intersects_box( + c("POINT (5 5)", "POINT (-1 1)"), + 0, 0, 10, 10 +) + +s2_touches( + "POLYGON ((0 0, 0 1, 1 1, 0 0))", + c("POINT (0 0)", "POINT (0.5 0.75)", "POINT (0 0.5)") +) + +s2_dwithin( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c("POINT (5 5)", "POINT (-1 1)"), + 0 # distance in meters +) + +s2_dwithin( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + c("POINT (5 5)", "POINT (-1 1)"), + 1e6 # distance in meters +) + +} +\seealso{ +Matrix versions of these predicates (e.g., \code{\link[=s2_intersects_matrix]{s2_intersects_matrix()}}). + +BigQuery's geography function reference: +\itemize{ +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_contains}{ST_CONTAINS} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_coveredby}{ST_COVEREDBY} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_covers}{ST_COVERS} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_disjoint}{ST_DISJOINT} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_equals}{ST_EQUALS} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersects}{ST_INTERSECTS} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersectsbox}{ST_INTERSECTSBOX} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_touches}{ST_TOUCHES} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_within}{ST_WITHIN} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dwithin}{ST_DWITHIN} +} +} diff --git a/man/s2_data_example_wkt.Rd b/man/s2_data_example_wkt.Rd new file mode 100644 index 0000000..1dea278 --- /dev/null +++ b/man/s2_data_example_wkt.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{s2_data_example_wkt} +\alias{s2_data_example_wkt} +\title{Example Geometries} +\format{ +An object of class \code{list} of length 29. +} +\usage{ +s2_data_example_wkt +} +\description{ +These geometries are toy examples useful for testing various coordinate +shuffling operations in the s2 package. +} +\keyword{datasets} diff --git a/man/s2_data_tbl_countries.Rd b/man/s2_data_tbl_countries.Rd new file mode 100644 index 0000000..a2a6ce8 --- /dev/null +++ b/man/s2_data_tbl_countries.Rd @@ -0,0 +1,59 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{s2_data_tbl_countries} +\alias{s2_data_tbl_countries} +\alias{s2_data_tbl_timezones} +\alias{s2_data_tbl_cities} +\alias{s2_data_countries} +\alias{s2_data_timezones} +\alias{s2_data_cities} +\title{Low-resolution world boundaries, timezones, and cities} +\format{ +A data.frame with columns \code{name} (character), and +\code{geometry} (wk_wkb) + +An object of class \code{data.frame} with 120 rows and 2 columns. + +An object of class \code{data.frame} with 243 rows and 3 columns. +} +\source{ +\href{https://www.naturalearthdata.com/}{Natural Earth Data} +} +\usage{ +s2_data_tbl_countries + +s2_data_tbl_timezones + +s2_data_tbl_cities + +s2_data_countries(name = NULL) + +s2_data_timezones(utc_offset_min = NULL, utc_offset_max = utc_offset_min) + +s2_data_cities(name = NULL) +} +\arguments{ +\item{name}{The name of a country, continent, city, or \code{NULL} +for all features.} + +\item{utc_offset_min, utc_offset_max}{Minimum and/or maximum timezone +offsets.} +} +\description{ +Well-known binary versions of the \href{https://www.naturalearthdata.com/}{Natural Earth} +low-resolution world boundaries and timezone boundaries. +} +\examples{ +head(s2_data_countries()) +s2_data_countries("Germany") +s2_data_countries("Europe") + +head(s2_data_timezones()) +s2_data_timezones(-4) + +head(s2_data_cities()) +s2_data_cities("Cairo") + +} +\keyword{datasets} diff --git a/man/s2_earth_radius_meters.Rd b/man/s2_earth_radius_meters.Rd new file mode 100644 index 0000000..249e411 --- /dev/null +++ b/man/s2_earth_radius_meters.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-earth.R +\name{s2_earth_radius_meters} +\alias{s2_earth_radius_meters} +\title{Earth Constants} +\usage{ +s2_earth_radius_meters() +} +\description{ +According to Yoder (1995), the radius of the earth is +6371.01 km. These functions are used to set the +default radis for functions that return a distance +or accept a distance as input +(e.g., \code{\link[=s2_distance]{s2_distance()}} and \code{\link[=s2_dwithin]{s2_dwithin()}}). +} +\examples{ +s2_earth_radius_meters() + +} +\references{ +Yoder, C.F. 1995. "Astrometric and Geodetic Properties of Earth and the +Solar System" in Global Earth Physics, A Handbook of Physical Constants, +AGU Reference Shelf 1, American Geophysical Union, Table 2. +\doi{10.1029/RF001p0001} +} diff --git a/man/s2_geog_point.Rd b/man/s2_geog_point.Rd new file mode 100644 index 0000000..d72ae62 --- /dev/null +++ b/man/s2_geog_point.Rd @@ -0,0 +1,164 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-constructors-formatters.R +\name{s2_geog_point} +\alias{s2_geog_point} +\alias{s2_make_line} +\alias{s2_make_polygon} +\alias{s2_geog_from_text} +\alias{s2_geog_from_wkb} +\alias{s2_as_text} +\alias{s2_as_binary} +\alias{s2_tessellate_tol_default} +\title{Create and Format Geography Vectors} +\usage{ +s2_geog_point(longitude, latitude) + +s2_make_line(longitude, latitude, feature_id = 1L) + +s2_make_polygon( + longitude, + latitude, + feature_id = 1L, + ring_id = 1L, + oriented = FALSE, + check = TRUE +) + +s2_geog_from_text( + wkt_string, + oriented = FALSE, + check = TRUE, + planar = FALSE, + tessellate_tol_m = s2_tessellate_tol_default() +) + +s2_geog_from_wkb( + wkb_bytes, + oriented = FALSE, + check = TRUE, + planar = FALSE, + tessellate_tol_m = s2_tessellate_tol_default() +) + +s2_as_text( + x, + precision = 16, + trim = TRUE, + planar = FALSE, + tessellate_tol_m = s2_tessellate_tol_default() +) + +s2_as_binary( + x, + endian = wk::wk_platform_endian(), + planar = FALSE, + tessellate_tol_m = s2_tessellate_tol_default() +) + +s2_tessellate_tol_default() +} +\arguments{ +\item{longitude, latitude}{Vectors of latitude and longitude} + +\item{feature_id, ring_id}{Vectors for which a change in +sequential values indicates a new feature or ring. Use \code{\link[=factor]{factor()}} +to convert from a character vector.} + +\item{oriented}{TRUE if polygon ring directions are known to be correct +(i.e., exterior rings are defined counter clockwise and interior +rings are defined clockwise).} + +\item{check}{Use \code{check = FALSE} to skip error on invalid geometries} + +\item{wkt_string}{Well-known text} + +\item{planar}{Use \code{TRUE} to force planar edges in import or export.} + +\item{tessellate_tol_m}{The maximum number of meters to that a point must +be moved to satisfy the planar edge constraint.} + +\item{wkb_bytes}{A \code{list()} of \code{raw()}} + +\item{x}{An object that can be converted to an s2_geography vector} + +\item{precision}{The number of significant digits to export when +writing well-known text. If \code{trim = FALSE}, the number of +digits after the decimal place.} + +\item{trim}{Should trailing zeroes be included after the decimal place?} + +\item{endian}{The endian-ness of the well-known binary. See \code{\link[wk:deprecated]{wk::wkb_translate_wkb()}}.} +} +\description{ +These functions create and export \link[=as_s2_geography]{geography vectors}. +Unlike the BigQuery geography constructors, these functions do not sanitize +invalid or redundant input using \code{\link[=s2_union]{s2_union()}}. Note that when creating polygons +using \code{\link[=s2_make_polygon]{s2_make_polygon()}}, rings can be open or closed. +} +\examples{ +# create point geographies using coordinate values: +s2_geog_point(-64, 45) + +# create line geographies using coordinate values: +s2_make_line(c(-64, 8), c(45, 71)) + +# optionally, separate features using feature_id: +s2_make_line( + c(-64, 8, -27, -27), c(45, 71, 0, 45), + feature_id = c(1, 1, 2, 2) +) + +# create polygon geographies using coordinate values: +# (rings can be open or closed) +s2_make_polygon(c(-45, 8, 0), c(64, 71, 90)) + +# optionally, separate rings and/or features using +# ring_id and/or feature_id +s2_make_polygon( + c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45), + c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30), + feature_id = c(rep(1, 8), rep(2, 3)), + ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1) +) + +# import and export well-known text +(geog <- s2_geog_from_text("POINT (-64 45)")) +s2_as_text(geog) + +# import and export well-known binary +(geog <- s2_geog_from_wkb(wk::as_wkb("POINT (-64 45)"))) +s2_as_binary(geog) + +# import geometry from planar space +s2_geog_from_text( + "POLYGON ((0 0, 1 0, 0 1, 0 0))", + planar = TRUE, + tessellate_tol_m = 1 +) + +# export geographies into planar space +geog <- s2_make_polygon(c(179, -179, 179), c(10, 10, 11)) +s2_as_text(geog, planar = TRUE) + +# polygons containing a pole need an extra step +geog <- s2_data_countries("Antarctica") +geom <- s2_as_text( + s2_intersection(geog, s2_world_plate_carree()), + planar = TRUE +) + +} +\seealso{ +See \code{\link[=as_s2_geography]{as_s2_geography()}} for other ways to construct geography vectors. + +BigQuery's geography function reference: +\itemize{ +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogpoint}{ST_GEOGPOINT} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makeline}{ST_MAKELINE} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makepolygon}{ST_MAKEPOLYGON} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromtext}{ST_GEOGFROMTEXT} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromwkb}{ST_GEOGFROMWKB} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_astext}{ST_ASTEXT} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_asbinary}{ST_ASBINARY} +} +} diff --git a/man/s2_interpolate.Rd b/man/s2_interpolate.Rd new file mode 100644 index 0000000..f59eb83 --- /dev/null +++ b/man/s2_interpolate.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-accessors.R, R/s2-transformers.R +\name{s2_project} +\alias{s2_project} +\alias{s2_project_normalized} +\alias{s2_interpolate} +\alias{s2_interpolate_normalized} +\title{Linear referencing} +\usage{ +s2_project(x, y, radius = s2_earth_radius_meters()) + +s2_project_normalized(x, y) + +s2_interpolate(x, distance, radius = s2_earth_radius_meters()) + +s2_interpolate_normalized(x, distance_normalized) +} +\arguments{ +\item{x}{A simple polyline geography vector} + +\item{y}{A simple point geography vector. The point will be +snapped to the nearest point on \code{x} for the purposes of +interpolation.} + +\item{radius}{Radius of the earth. Defaults to the average radius of +the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} + +\item{distance}{A distance along \code{x} in \code{radius} units.} + +\item{distance_normalized}{A \code{distance} normalized to \code{\link[=s2_length]{s2_length()}} of +\code{x}.} +} +\value{ +\itemize{ +\item \code{s2_interpolate()} returns the point on \code{x}, \code{distance} meters +along the line. +\item \code{s2_interpolate_normalized()} returns the point on \code{x} interpolated +to a fraction along the line. +\item \code{s2_project()} returns the \code{distance} that \code{point} occurs along \code{x}. +\item \code{s2_project_normalized()} returns the \code{distance_normalized} along \code{x} +where \code{point} occurs. +} +} +\description{ +Linear referencing +} +\examples{ +s2_project_normalized("LINESTRING (0 0, 0 90)", "POINT (0 22.5)") +s2_project("LINESTRING (0 0, 0 90)", "POINT (0 22.5)") +s2_interpolate_normalized("LINESTRING (0 0, 0 90)", 0.25) +s2_interpolate("LINESTRING (0 0, 0 90)", 2501890) + +} diff --git a/man/s2_is_collection.Rd b/man/s2_is_collection.Rd new file mode 100644 index 0000000..bec93c0 --- /dev/null +++ b/man/s2_is_collection.Rd @@ -0,0 +1,112 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-accessors.R +\name{s2_is_collection} +\alias{s2_is_collection} +\alias{s2_is_valid} +\alias{s2_is_valid_detail} +\alias{s2_dimension} +\alias{s2_num_points} +\alias{s2_is_empty} +\alias{s2_area} +\alias{s2_length} +\alias{s2_perimeter} +\alias{s2_x} +\alias{s2_y} +\alias{s2_distance} +\alias{s2_max_distance} +\title{S2 Geography Accessors} +\usage{ +s2_is_collection(x) + +s2_is_valid(x) + +s2_is_valid_detail(x) + +s2_dimension(x) + +s2_num_points(x) + +s2_is_empty(x) + +s2_area(x, radius = s2_earth_radius_meters()) + +s2_length(x, radius = s2_earth_radius_meters()) + +s2_perimeter(x, radius = s2_earth_radius_meters()) + +s2_x(x) + +s2_y(x) + +s2_distance(x, y, radius = s2_earth_radius_meters()) + +s2_max_distance(x, y, radius = s2_earth_radius_meters()) +} +\arguments{ +\item{x, y}{\link[=as_s2_geography]{geography vectors}. These inputs +are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects +(e.g., character vectors of well-known text) directly.} + +\item{radius}{Radius of the earth. Defaults to the average radius of +the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} +} +\description{ +Accessors extract information about \link[=as_s2_geography]{geography vectors}. +} +\examples{ +# s2_is_collection() tests for multiple geometries in one feature +s2_is_collection(c("POINT (-64 45)", "MULTIPOINT ((-64 45), (8 72))")) + +# s2_dimension() returns 0 for point, 1 for line, 2 for polygon +s2_dimension( + c( + "GEOMETRYCOLLECTION EMPTY", + "POINT (-64 45)", + "LINESTRING (-64 45, 8 72)", + "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", + "GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 8 72))" + ) +) + +# s2_num_points() counts points +s2_num_points(c("POINT (-64 45)", "LINESTRING (-64 45, 8 72)")) + +# s2_is_empty tests for emptiness +s2_is_empty(c("POINT (-64 45)", "POINT EMPTY")) + +# calculate area, length, and perimeter +s2_area("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") +s2_perimeter("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") +s2_length(s2_boundary("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")) + +# extract x and y coordinates from points +s2_x(c("POINT (-64 45)", "POINT EMPTY")) +s2_y(c("POINT (-64 45)", "POINT EMPTY")) + +# calculate minimum and maximum distance between two geometries +s2_distance( + "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", + "POINT (-64 45)" +) +s2_max_distance( + "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", + "POINT (-64 45)" +) + +} +\seealso{ +BigQuery's geography function reference: +\itemize{ +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_iscollection}{ST_ISCOLLECTION} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dimension}{ST_DIMENSION} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_numpoints}{ST_NUMPOINTS} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_isempty}{ST_ISEMPTY} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_area}{ST_AREA} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_length}{ST_LENGTH} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_perimeter}{ST_PERIMETER} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_x}{ST_X} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_y}{ST_Y} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_distance}{ST_DISTANCE} +\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_maxdistance}{ST_MAXDISTANCE} +} +} diff --git a/man/s2_lnglat.Rd b/man/s2_lnglat.Rd new file mode 100644 index 0000000..53bc070 --- /dev/null +++ b/man/s2_lnglat.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-lnglat.R +\name{s2_lnglat} +\alias{s2_lnglat} +\alias{as_s2_lnglat} +\alias{as_s2_lnglat.default} +\alias{as_s2_lnglat.wk_xy} +\alias{as_s2_lnglat.wk_xyz} +\title{Create an S2 LngLat Vector} +\usage{ +s2_lnglat(lng, lat) + +as_s2_lnglat(x, ...) + +\method{as_s2_lnglat}{default}(x, ...) + +\method{as_s2_lnglat}{wk_xy}(x, ...) + +\method{as_s2_lnglat}{wk_xyz}(x, ...) +} +\arguments{ +\item{lat, lng}{Vectors of latitude and longitude values in degrees.} + +\item{x}{A \code{\link[=s2_lnglat]{s2_lnglat()}} vector or an object that can be coerced to one.} + +\item{...}{Unused} +} +\value{ +An object with class s2_lnglat +} +\description{ +This class represents a latitude and longitude on the Earth's surface. +Most calculations in S2 convert this to a \code{\link[=as_s2_point]{as_s2_point()}}, which is a +unit vector representation of this value. +} +\examples{ +s2_lnglat(45, -64) # Halifax, Nova Scotia! +as.data.frame(s2_lnglat(45, -64)) + +} diff --git a/man/s2_options.Rd b/man/s2_options.Rd new file mode 100644 index 0000000..d1b4865 --- /dev/null +++ b/man/s2_options.Rd @@ -0,0 +1,109 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-options.R +\name{s2_options} +\alias{s2_options} +\alias{s2_snap_identity} +\alias{s2_snap_level} +\alias{s2_snap_precision} +\alias{s2_snap_distance} +\title{Geography Operation Options} +\usage{ +s2_options( + model = NULL, + snap = s2_snap_identity(), + snap_radius = -1, + duplicate_edges = FALSE, + edge_type = "directed", + validate = FALSE, + polyline_type = "path", + polyline_sibling_pairs = "keep", + simplify_edge_chains = FALSE, + split_crossing_edges = FALSE, + idempotent = FALSE, + dimensions = c("point", "polyline", "polygon") +) + +s2_snap_identity() + +s2_snap_level(level) + +s2_snap_precision(precision) + +s2_snap_distance(distance) +} +\arguments{ +\item{model}{One of 'open', 'semi-open' (default for polygons), +or 'closed' (default for polylines). See section 'Model'} + +\item{snap}{Use \code{s2_snap_identity()}, \code{s2_snap_distance()}, \code{s2_snap_level()}, +or \code{s2_snap_precision()} to specify how or if coordinate rounding should +occur.} + +\item{snap_radius}{As opposed to the snap function, which specifies +the maximum distance a vertex should move, the snap radius (in radians) sets +the minimum distance between vertices of the output that don't cause vertices +to move more than the distance specified by the snap function. This can be used +to simplify the result of a boolean operation. Use -1 to specify that any +minimum distance is acceptable.} + +\item{duplicate_edges}{Use \code{TRUE} to keep duplicate edges (e.g., duplicate +points).} + +\item{edge_type}{One of 'directed' (default) or 'undirected'.} + +\item{validate}{Use \code{TRUE} to validate the result from the builder.} + +\item{polyline_type}{One of 'path' (default) or 'walk'. If 'walk', +polylines that backtrack are preserved.} + +\item{polyline_sibling_pairs}{One of 'discard' (default) or 'keep'.} + +\item{simplify_edge_chains}{Use \code{TRUE} to remove vertices that are within +\code{snap_radius} of the original vertex.} + +\item{split_crossing_edges}{Use \code{TRUE} to split crossing polyline edges +when creating geometries.} + +\item{idempotent}{Use \code{FALSE} to apply snap even if snapping is not necessary +to satisfy vertex constraints.} + +\item{dimensions}{A combination of 'point', 'polyline', and/or 'polygon' +that can used to constrain the output of \code{\link[=s2_rebuild]{s2_rebuild()}} or a +boolean operation.} + +\item{level}{A value from 0 to 30 corresponding to the cell level +at which snapping should occur.} + +\item{precision}{A number by which coordinates should be multiplied +before being rounded. Rounded to the nearest exponent of 10.} + +\item{distance}{A distance (in radians) denoting the maximum +distance a vertex should move in the snapping process.} +} +\description{ +These functions specify defaults for options used to perform operations +and construct geometries. These are used in predicates (e.g., \code{\link[=s2_intersects]{s2_intersects()}}), +and boolean operations (e.g., \code{\link[=s2_intersection]{s2_intersection()}}) to specify the model for +containment and how new geometries should be constructed. +} +\section{Model}{ + +The geometry model indicates whether or not a geometry includes its boundaries. +Boundaries of line geometries are its end points. +OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED +geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries +(\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons +do not overlap or two lines do not cross, no point exist that belong to +more than one of the geometries. (This latter form, half-closed, is +not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on +which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) +and covers/covered_by (closed) correspond to the SFA standard specification +of these operators. +} + +\examples{ +# use s2_options() to specify containment models, snap level +# layer creation options, and builder options +s2_options(model = "closed", snap = s2_snap_level(30)) + +} diff --git a/man/s2_plot.Rd b/man/s2_plot.Rd new file mode 100644 index 0000000..8de0c1b --- /dev/null +++ b/man/s2_plot.Rd @@ -0,0 +1,52 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plot.R +\name{s2_plot} +\alias{s2_plot} +\title{Plot S2 Geographies} +\usage{ +s2_plot( + x, + ..., + asp = 1, + xlab = "", + ylab = "", + rule = "evenodd", + add = FALSE, + plot_hemisphere = FALSE, + simplify = TRUE, + centre = NULL +) +} +\arguments{ +\item{x}{A \code{\link[wk:wkb]{wkb()}} or \code{\link[wk:wkt]{wkt()}}} + +\item{...}{Passed to plotting functions for features: \code{\link[graphics:points]{graphics::points()}} +for point and multipoint geometries, \code{\link[graphics:lines]{graphics::lines()}} for linestring +and multilinestring geometries, and \code{\link[graphics:polypath]{graphics::polypath()}} for polygon +and multipolygon geometries.} + +\item{asp, xlab, ylab}{Passed to \code{\link[graphics:plot.default]{graphics::plot()}}} + +\item{rule}{The rule to use for filling polygons (see \code{\link[graphics:polypath]{graphics::polypath()}})} + +\item{add}{Should a new plot be created, or should \code{handleable} be added to the +existing plot?} + +\item{plot_hemisphere}{Plot the outline of the earth} + +\item{simplify}{Use \code{FALSE} to skip the simplification step} + +\item{centre}{The longitude/latitude point of the centre of the +orthographic projection} +} +\value{ +The input, invisibly +} +\description{ +Plot S2 Geographies +} +\examples{ +s2_plot(s2_data_countries()) +s2_plot(s2_data_cities(), add = TRUE) + +} diff --git a/man/s2_point.Rd b/man/s2_point.Rd new file mode 100644 index 0000000..2046478 --- /dev/null +++ b/man/s2_point.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-point.R +\name{s2_point} +\alias{s2_point} +\alias{s2_point_crs} +\alias{as_s2_point} +\alias{as_s2_point.default} +\alias{as_s2_point.wk_xy} +\alias{as_s2_point.wk_xyz} +\title{Create an S2 Point Vector} +\usage{ +s2_point(x, y, z) + +s2_point_crs() + +as_s2_point(x, ...) + +\method{as_s2_point}{default}(x, ...) + +\method{as_s2_point}{wk_xy}(x, ...) + +\method{as_s2_point}{wk_xyz}(x, ...) +} +\arguments{ +\item{x, y, z}{Vectors of latitude and longitude values in degrees.} + +\item{...}{Unused} +} +\value{ +An object with class s2_point +} +\description{ +In S2 terminology, a "point" is a 3-dimensional unit vector representation +of an \code{\link[=s2_point]{s2_point()}}. Internally, all s2 objects are stored as +3-dimensional unit vectors. +} +\examples{ +point <- s2_lnglat(-64, 45) # Halifax, Nova Scotia! +as_s2_point(point) +as.data.frame(as_s2_point(point)) + +} diff --git a/man/wk_handle.s2_geography.Rd b/man/wk_handle.s2_geography.Rd new file mode 100644 index 0000000..88ff6ec --- /dev/null +++ b/man/wk_handle.s2_geography.Rd @@ -0,0 +1,85 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/wk-utils.R +\name{wk_handle.s2_geography} +\alias{wk_handle.s2_geography} +\alias{s2_geography_writer} +\alias{wk_writer.s2_geography} +\alias{s2_trans_point} +\alias{s2_trans_lnglat} +\alias{s2_projection_plate_carree} +\alias{s2_projection_mercator} +\alias{s2_hemisphere} +\alias{s2_world_plate_carree} +\alias{s2_projection_orthographic} +\title{Low-level wk filters and handlers} +\usage{ +\method{wk_handle}{s2_geography}( + handleable, + handler, + ..., + s2_projection = s2_projection_plate_carree(), + s2_tessellate_tol = Inf +) + +s2_geography_writer( + oriented = FALSE, + check = TRUE, + projection = s2_projection_plate_carree(), + tessellate_tol = Inf +) + +\method{wk_writer}{s2_geography}(handleable, ...) + +s2_trans_point() + +s2_trans_lnglat() + +s2_projection_plate_carree(x_scale = 180) + +s2_projection_mercator(x_scale = 20037508.3427892) + +s2_hemisphere(centre) + +s2_world_plate_carree(epsilon_east_west = 0, epsilon_north_south = 0) + +s2_projection_orthographic(centre = s2_lnglat(0, 0)) +} +\arguments{ +\item{handleable}{A geometry vector (e.g., \code{\link[wk:wkb]{wkb()}}, \code{\link[wk:wkt]{wkt()}}, \code{\link[wk:xy]{xy()}}, +\code{\link[wk:rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[wk:wk_handle]{wk_handle()}} is defined.} + +\item{handler}{A \link[wk:wk_handle]{wk_handler} object.} + +\item{...}{Passed to the \code{\link[wk:wk_handle]{wk_handle()}} method.} + +\item{oriented}{TRUE if polygon ring directions are known to be correct +(i.e., exterior rings are defined counter clockwise and interior +rings are defined clockwise).} + +\item{check}{Use \code{check = FALSE} to skip error on invalid geometries} + +\item{projection, s2_projection}{One of \code{\link[=s2_projection_plate_carree]{s2_projection_plate_carree()}} or +\code{\link[=s2_projection_mercator]{s2_projection_mercator()}}} + +\item{tessellate_tol, s2_tessellate_tol}{An angle in radians. +Points will not be added if a line segment is within this +distance of a point.} + +\item{x_scale}{The maximum x value of the projection} + +\item{centre}{The center point of the orthographic projection} + +\item{epsilon_east_west, epsilon_north_south}{Use a positive number to +define the edges of a Cartesian world slightly inward from -180, -90, +180, 90. This may be used to define a world outline for a projection where +projecting at the extreme edges of the earth results in a non-finite value.} +} +\value{ +\itemize{ +\item \code{s2_projection_plate_carree()}, \code{s2_projection_mercator()}: An external pointer +to an S2 projection. +} +} +\description{ +Low-level wk filters and handlers +} diff --git a/src/Makevars.in b/src/Makevars.in new file mode 100644 index 0000000..2bc496e --- /dev/null +++ b/src/Makevars.in @@ -0,0 +1,219 @@ +PKG_CPPFLAGS = -I../src -DSTRICT_R_HEADERS +PKG_LIBS = @libs@ +PKG_CXXFLAGS = @cflags@ -pthread + +CXX_STD = CXX11 + +ABSL_LIBS = absl/base/internal/cycleclock.o \ + absl/base/internal/low_level_alloc.o \ + absl/base/internal/raw_logging.o \ + absl/base/internal/scoped_set_env.o \ + absl/base/internal/spinlock_wait.o \ + absl/base/internal/spinlock.o \ + absl/base/internal/strerror.o \ + absl/base/internal/sysinfo.o \ + absl/base/internal/thread_identity.o \ + absl/base/internal/throw_delegate.o \ + absl/base/internal/unscaledcycleclock.o \ + absl/base/log_severity.o \ + absl/container/internal/hashtablez_sampler_force_weak_definition.o \ + absl/container/internal/hashtablez_sampler.o \ + absl/container/internal/raw_hash_set.o \ + absl/debugging/failure_signal_handler.o \ + absl/debugging/internal/address_is_readable.o \ + absl/debugging/internal/demangle.o \ + absl/debugging/internal/elf_mem_image.o \ + absl/debugging/internal/examine_stack.o \ + absl/debugging/internal/stack_consumption.o \ + absl/debugging/internal/vdso_support.o \ + absl/debugging/leak_check.o \ + absl/debugging/stacktrace.o \ + absl/debugging/symbolize.o \ + absl/numeric/int128.o \ + absl/profiling/internal/exponential_biased.o \ + absl/profiling/internal/periodic_sampler.o \ + absl/strings/ascii.o \ + absl/strings/charconv.o \ + absl/strings/cord_analysis.o \ + absl/strings/cord_buffer.o \ + absl/strings/cord.o \ + absl/strings/escaping.o \ + absl/strings/internal/charconv_bigint.o \ + absl/strings/internal/charconv_parse.o \ + absl/strings/internal/cord_internal.o \ + absl/strings/internal/cord_rep_btree_navigator.o \ + absl/strings/internal/cord_rep_btree_reader.o \ + absl/strings/internal/cord_rep_btree.o \ + absl/strings/internal/cord_rep_consume.o \ + absl/strings/internal/cord_rep_crc.o \ + absl/strings/internal/cord_rep_ring.o \ + absl/strings/internal/cordz_functions.o \ + absl/strings/internal/cordz_handle.o \ + absl/strings/internal/cordz_info.o \ + absl/strings/internal/cordz_sample_token.o \ + absl/strings/internal/escaping.o \ + absl/strings/internal/memutil.o \ + absl/strings/internal/ostringstream.o \ + absl/strings/internal/pow10_helper.o \ + absl/strings/internal/str_format/arg.o \ + absl/strings/internal/str_format/bind.o \ + absl/strings/internal/str_format/extension.o \ + absl/strings/internal/str_format/float_conversion.o \ + absl/strings/internal/str_format/output.o \ + absl/strings/internal/str_format/parser.o \ + absl/strings/internal/utf8.o \ + absl/strings/match.o \ + absl/strings/numbers.o \ + absl/strings/str_cat.o \ + absl/strings/str_replace.o \ + absl/strings/str_split.o \ + absl/strings/string_view.o \ + absl/strings/substitute.o \ + absl/synchronization/barrier.o \ + absl/synchronization/blocking_counter.o \ + absl/synchronization/internal/create_thread_identity.o \ + absl/synchronization/internal/graphcycles.o \ + absl/synchronization/internal/per_thread_sem.o \ + absl/synchronization/internal/waiter.o \ + absl/synchronization/mutex.o \ + absl/synchronization/notification.o \ + absl/time/civil_time.o \ + absl/time/clock.o \ + absl/time/duration.o \ + absl/time/format.o \ + absl/time/internal/cctz/src/civil_time_detail.o \ + absl/time/internal/cctz/src/time_zone_fixed.o \ + absl/time/internal/cctz/src/time_zone_format.o \ + absl/time/internal/cctz/src/time_zone_if.o \ + absl/time/internal/cctz/src/time_zone_impl.o \ + absl/time/internal/cctz/src/time_zone_info.o \ + absl/time/internal/cctz/src/time_zone_libc.o \ + absl/time/internal/cctz/src/time_zone_lookup.o \ + absl/time/internal/cctz/src/time_zone_posix.o \ + absl/time/internal/cctz/src/zone_info_source.o \ + absl/time/time.o \ + absl/types/bad_any_cast.o \ + absl/types/bad_optional_access.o \ + absl/types/bad_variant_access.o + +OBJECTS = $(ABSL_LIBS) \ + cpp-compat.o \ + s2-accessors.o \ + s2-bounds.o \ + s2-cell.o \ + s2-cell-union.o \ + s2-constructors-formatters.o \ + s2-predicates.o \ + s2-transformers.o \ + init.o \ + RcppExports.o \ + s2-geography.o \ + s2-lnglat.o \ + s2-matrix.o \ + wk-impl.o \ + s2geography/accessors.o \ + s2geography/accessors-geog.o \ + s2geography/linear-referencing.o \ + s2geography/distance.o \ + s2geography/build.o \ + s2geography/coverings.o \ + s2geography/geography.o \ + s2geography/predicates.o \ + s2/base/stringprintf.o \ + s2/base/strtoint.o \ + s2/encoded_s2cell_id_vector.o \ + s2/encoded_s2point_vector.o \ + s2/encoded_s2shape_index.o \ + s2/encoded_string_vector.o \ + s2/id_set_lexicon.o \ + s2/mutable_s2shape_index.o \ + s2/r2rect.o \ + s2/s1angle.o \ + s2/s1chord_angle.o \ + s2/s1interval.o \ + s2/s2boolean_operation.o \ + s2/s2builder_graph.o \ + s2/s2builder.o \ + s2/s2builderutil_closed_set_normalizer.o \ + s2/s2builderutil_find_polygon_degeneracies.o \ + s2/s2builderutil_lax_polygon_layer.o \ + s2/s2builderutil_s2point_vector_layer.o \ + s2/s2builderutil_s2polygon_layer.o \ + s2/s2builderutil_s2polyline_layer.o \ + s2/s2builderutil_s2polyline_vector_layer.o \ + s2/s2builderutil_snap_functions.o \ + s2/s2builderutil_testing.o \ + s2/s2cap.o \ + s2/s2cell_id.o \ + s2/s2cell_index.o \ + s2/s2cell_union.o \ + s2/s2cell.o \ + s2/s2centroids.o \ + s2/s2closest_cell_query.o \ + s2/s2closest_edge_query.o \ + s2/s2closest_point_query.o \ + s2/s2contains_vertex_query.o \ + s2/s2convex_hull_query.o \ + s2/s2coords.o \ + s2/s2crossing_edge_query.o \ + s2/s2debug.o \ + s2/s2earth.o \ + s2/s2edge_clipping.o \ + s2/s2edge_crosser.o \ + s2/s2edge_crossings.o \ + s2/s2edge_distances.o \ + s2/s2edge_tessellator.o \ + s2/s2error.o \ + s2/s2furthest_edge_query.o \ + s2/s2latlng_rect_bounder.o \ + s2/s2latlng_rect.o \ + s2/s2latlng.o \ + s2/s2lax_loop_shape.o \ + s2/s2lax_polygon_shape.o \ + s2/s2lax_polyline_shape.o \ + s2/s2loop_measures.o \ + s2/s2loop.o \ + s2/s2max_distance_targets.o \ + s2/s2measures.o \ + s2/s2metrics.o \ + s2/s2min_distance_targets.o \ + s2/s2padded_cell.o \ + s2/s2point_compression.o \ + s2/s2point_region.o \ + s2/s2pointutil.o \ + s2/s2polygon.o \ + s2/s2polyline_alignment.o \ + s2/s2polyline_measures.o \ + s2/s2polyline_simplifier.o \ + s2/s2polyline.o \ + s2/s2predicates.o \ + s2/s2projections.o \ + s2/s2r2rect.o \ + s2/s2region_coverer.o \ + s2/s2region_intersection.o \ + s2/s2region_term_indexer.o \ + s2/s2region_union.o \ + s2/s2region.o \ + s2/s2shape_index_buffered_region.o \ + s2/s2shape_index_measures.o \ + s2/s2shape_index.o \ + s2/s2shape_measures.o \ + s2/s2shapeutil_build_polygon_boundaries.o \ + s2/s2shapeutil_coding.o \ + s2/s2shapeutil_contains_brute_force.o \ + s2/s2shapeutil_edge_iterator.o \ + s2/s2shapeutil_get_reference_point.o \ + s2/s2shapeutil_range_iterator.o \ + s2/s2shapeutil_visit_crossing_edge_pairs.o \ + s2/s2testing.o \ + s2/s2text_format.o \ + s2/s2wedge_relations.o \ + s2/strings/ostringstream.o \ + s2/strings/serialize.o \ + s2/util/bits/bit-interleave.o \ + s2/util/bits/bits.o \ + s2/util/coding/coder.o \ + s2/util/coding/varint.o \ + s2/util/math/exactfloat/exactfloat.o \ + s2/util/math/mathutil.o \ + s2/util/units/length-units.o diff --git a/src/Makevars.ucrt b/src/Makevars.ucrt new file mode 100644 index 0000000..51eed48 --- /dev/null +++ b/src/Makevars.ucrt @@ -0,0 +1,3 @@ +CRT=-ucrt +include Makevars.win +CXX_STD = CXX11 diff --git a/src/Makevars.win b/src/Makevars.win new file mode 100644 index 0000000..f4c8cfd --- /dev/null +++ b/src/Makevars.win @@ -0,0 +1,224 @@ +VERSION = 1.1.1k +PKG_CPPFLAGS = -DS2_USE_EXACTFLOAT -D_USE_MATH_DEFINES -DNDEBUG -DIS_LITTLE_ENDIAN -DOMIT_STRPTIME -I../windows/openssl-$(VERSION)/include -I../src +PKG_LIBS = -Ls2 -ls2static -L../windows/openssl-$(VERSION)/lib${R_ARCH}${CRT} -lssl -lcrypto -lws2_32 -lgdi32 -lcrypt32 + +CXX_STD = CXX11 + +STATLIB = s2/libs2static.a + +ABSL_LIBS = absl/base/internal/cycleclock.o \ + absl/base/internal/low_level_alloc.o \ + absl/base/internal/raw_logging.o \ + absl/base/internal/scoped_set_env.o \ + absl/base/internal/spinlock_wait.o \ + absl/base/internal/spinlock.o \ + absl/base/internal/strerror.o \ + absl/base/internal/sysinfo.o \ + absl/base/internal/thread_identity.o \ + absl/base/internal/throw_delegate.o \ + absl/base/internal/unscaledcycleclock.o \ + absl/base/log_severity.o \ + absl/container/internal/hashtablez_sampler_force_weak_definition.o \ + absl/container/internal/hashtablez_sampler.o \ + absl/container/internal/raw_hash_set.o \ + absl/debugging/failure_signal_handler.o \ + absl/debugging/internal/address_is_readable.o \ + absl/debugging/internal/demangle.o \ + absl/debugging/internal/elf_mem_image.o \ + absl/debugging/internal/examine_stack.o \ + absl/debugging/internal/stack_consumption.o \ + absl/debugging/internal/vdso_support.o \ + absl/debugging/leak_check.o \ + absl/debugging/stacktrace.o \ + absl/debugging/symbolize.o \ + absl/numeric/int128.o \ + absl/profiling/internal/exponential_biased.o \ + absl/profiling/internal/periodic_sampler.o \ + absl/strings/ascii.o \ + absl/strings/charconv.o \ + absl/strings/cord_analysis.o \ + absl/strings/cord_buffer.o \ + absl/strings/cord.o \ + absl/strings/escaping.o \ + absl/strings/internal/charconv_bigint.o \ + absl/strings/internal/charconv_parse.o \ + absl/strings/internal/cord_internal.o \ + absl/strings/internal/cord_rep_btree_navigator.o \ + absl/strings/internal/cord_rep_btree_reader.o \ + absl/strings/internal/cord_rep_btree.o \ + absl/strings/internal/cord_rep_consume.o \ + absl/strings/internal/cord_rep_crc.o \ + absl/strings/internal/cord_rep_ring.o \ + absl/strings/internal/cordz_functions.o \ + absl/strings/internal/cordz_handle.o \ + absl/strings/internal/cordz_info.o \ + absl/strings/internal/cordz_sample_token.o \ + absl/strings/internal/escaping.o \ + absl/strings/internal/memutil.o \ + absl/strings/internal/ostringstream.o \ + absl/strings/internal/pow10_helper.o \ + absl/strings/internal/str_format/arg.o \ + absl/strings/internal/str_format/bind.o \ + absl/strings/internal/str_format/extension.o \ + absl/strings/internal/str_format/float_conversion.o \ + absl/strings/internal/str_format/output.o \ + absl/strings/internal/str_format/parser.o \ + absl/strings/internal/utf8.o \ + absl/strings/match.o \ + absl/strings/numbers.o \ + absl/strings/str_cat.o \ + absl/strings/str_replace.o \ + absl/strings/str_split.o \ + absl/strings/string_view.o \ + absl/strings/substitute.o \ + absl/synchronization/barrier.o \ + absl/synchronization/blocking_counter.o \ + absl/synchronization/internal/create_thread_identity.o \ + absl/synchronization/internal/graphcycles.o \ + absl/synchronization/internal/per_thread_sem.o \ + absl/synchronization/internal/waiter.o \ + absl/synchronization/mutex.o \ + absl/synchronization/notification.o \ + absl/time/civil_time.o \ + absl/time/clock.o \ + absl/time/duration.o \ + absl/time/format.o \ + absl/time/internal/cctz/src/civil_time_detail.o \ + absl/time/internal/cctz/src/time_zone_fixed.o \ + absl/time/internal/cctz/src/time_zone_format.o \ + absl/time/internal/cctz/src/time_zone_if.o \ + absl/time/internal/cctz/src/time_zone_impl.o \ + absl/time/internal/cctz/src/time_zone_info.o \ + absl/time/internal/cctz/src/time_zone_libc.o \ + absl/time/internal/cctz/src/time_zone_lookup.o \ + absl/time/internal/cctz/src/time_zone_posix.o \ + absl/time/internal/cctz/src/zone_info_source.o \ + absl/time/time.o \ + absl/types/bad_any_cast.o \ + absl/types/bad_optional_access.o \ + absl/types/bad_variant_access.o + +S2LIBS = $(ABSL_LIBS) \ + s2geography/linear-referencing.o \ + s2geography/distance.o \ + s2geography/accessors.o \ + s2geography/accessors-geog.o \ + s2geography/build.o \ + s2geography/coverings.o \ + s2geography/geography.o \ + s2geography/predicates.o \ + s2/base/stringprintf.o \ + s2/base/strtoint.o \ + s2/encoded_s2cell_id_vector.o \ + s2/encoded_s2point_vector.o \ + s2/encoded_s2shape_index.o \ + s2/encoded_string_vector.o \ + s2/id_set_lexicon.o \ + s2/mutable_s2shape_index.o \ + s2/r2rect.o \ + s2/s1angle.o \ + s2/s1chord_angle.o \ + s2/s1interval.o \ + s2/s2boolean_operation.o \ + s2/s2builder_graph.o \ + s2/s2builder.o \ + s2/s2builderutil_closed_set_normalizer.o \ + s2/s2builderutil_find_polygon_degeneracies.o \ + s2/s2builderutil_lax_polygon_layer.o \ + s2/s2builderutil_s2point_vector_layer.o \ + s2/s2builderutil_s2polygon_layer.o \ + s2/s2builderutil_s2polyline_layer.o \ + s2/s2builderutil_s2polyline_vector_layer.o \ + s2/s2builderutil_snap_functions.o \ + s2/s2builderutil_testing.o \ + s2/s2cap.o \ + s2/s2cell_id.o \ + s2/s2cell_index.o \ + s2/s2cell_union.o \ + s2/s2cell.o \ + s2/s2centroids.o \ + s2/s2closest_cell_query.o \ + s2/s2closest_edge_query.o \ + s2/s2closest_point_query.o \ + s2/s2contains_vertex_query.o \ + s2/s2convex_hull_query.o \ + s2/s2coords.o \ + s2/s2crossing_edge_query.o \ + s2/s2debug.o \ + s2/s2earth.o \ + s2/s2edge_clipping.o \ + s2/s2edge_crosser.o \ + s2/s2edge_crossings.o \ + s2/s2edge_distances.o \ + s2/s2edge_tessellator.o \ + s2/s2error.o \ + s2/s2furthest_edge_query.o \ + s2/s2latlng_rect_bounder.o \ + s2/s2latlng_rect.o \ + s2/s2latlng.o \ + s2/s2lax_loop_shape.o \ + s2/s2lax_polygon_shape.o \ + s2/s2lax_polyline_shape.o \ + s2/s2loop_measures.o \ + s2/s2loop.o \ + s2/s2max_distance_targets.o \ + s2/s2measures.o \ + s2/s2metrics.o \ + s2/s2min_distance_targets.o \ + s2/s2padded_cell.o \ + s2/s2point_compression.o \ + s2/s2point_region.o \ + s2/s2pointutil.o \ + s2/s2polygon.o \ + s2/s2polyline_alignment.o \ + s2/s2polyline_measures.o \ + s2/s2polyline_simplifier.o \ + s2/s2polyline.o \ + s2/s2predicates.o \ + s2/s2projections.o \ + s2/s2r2rect.o \ + s2/s2region_coverer.o \ + s2/s2region_intersection.o \ + s2/s2region_term_indexer.o \ + s2/s2region_union.o \ + s2/s2region.o \ + s2/s2shape_index_buffered_region.o \ + s2/s2shape_index_measures.o \ + s2/s2shape_index.o \ + s2/s2shape_measures.o \ + s2/s2shapeutil_build_polygon_boundaries.o \ + s2/s2shapeutil_coding.o \ + s2/s2shapeutil_contains_brute_force.o \ + s2/s2shapeutil_edge_iterator.o \ + s2/s2shapeutil_get_reference_point.o \ + s2/s2shapeutil_range_iterator.o \ + s2/s2shapeutil_visit_crossing_edge_pairs.o \ + s2/s2testing.o \ + s2/s2text_format.o \ + s2/s2wedge_relations.o \ + s2/strings/ostringstream.o \ + s2/strings/serialize.o \ + s2/util/bits/bit-interleave.o \ + s2/util/bits/bits.o \ + s2/util/coding/coder.o \ + s2/util/coding/varint.o \ + s2/util/math/exactfloat/exactfloat.o \ + s2/util/math/mathutil.o \ + s2/util/units/length-units.o + +$(SHLIB): $(STATLIB) + +$(STATLIB): $(S2LIBS) + +$(S2LIBS): winlibs + +#all: clean + +winlibs: + mkdir -p ../inst + "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" "../tools/winlibs.R" $(VERSION) + +clean: + rm -f $(SHLIB) $(STATLIB) $(OBJECTS) $(S2LIBS) + +.PHONY: all winlibs clean diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp new file mode 100644 index 0000000..927a6bf --- /dev/null +++ b/src/RcppExports.cpp @@ -0,0 +1,1482 @@ +// Generated by using Rcpp::compileAttributes() -> do not edit by hand +// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +#include + +using namespace Rcpp; + +#ifdef RCPP_USE_GLOBAL_ROSTREAM +Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); +Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); +#endif + +// cpp_s2_init +void cpp_s2_init(); +RcppExport SEXP _s2_cpp_s2_init() { +BEGIN_RCPP + Rcpp::RNGScope rcpp_rngScope_gen; + cpp_s2_init(); + return R_NilValue; +END_RCPP +} +// cpp_s2_is_collection +LogicalVector cpp_s2_is_collection(List geog); +RcppExport SEXP _s2_cpp_s2_is_collection(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_is_collection(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_is_valid +LogicalVector cpp_s2_is_valid(List geog); +RcppExport SEXP _s2_cpp_s2_is_valid(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_is_valid(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_is_valid_reason +CharacterVector cpp_s2_is_valid_reason(List geog); +RcppExport SEXP _s2_cpp_s2_is_valid_reason(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_is_valid_reason(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_dimension +IntegerVector cpp_s2_dimension(List geog); +RcppExport SEXP _s2_cpp_s2_dimension(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_dimension(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_num_points +IntegerVector cpp_s2_num_points(List geog); +RcppExport SEXP _s2_cpp_s2_num_points(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_num_points(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_is_empty +LogicalVector cpp_s2_is_empty(List geog); +RcppExport SEXP _s2_cpp_s2_is_empty(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_is_empty(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_area +NumericVector cpp_s2_area(List geog); +RcppExport SEXP _s2_cpp_s2_area(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_area(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_length +NumericVector cpp_s2_length(List geog); +RcppExport SEXP _s2_cpp_s2_length(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_length(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_perimeter +NumericVector cpp_s2_perimeter(List geog); +RcppExport SEXP _s2_cpp_s2_perimeter(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_perimeter(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_x +NumericVector cpp_s2_x(List geog); +RcppExport SEXP _s2_cpp_s2_x(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_x(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_y +NumericVector cpp_s2_y(List geog); +RcppExport SEXP _s2_cpp_s2_y(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_y(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_project_normalized +NumericVector cpp_s2_project_normalized(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_project_normalized(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_project_normalized(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_distance +NumericVector cpp_s2_distance(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_distance(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_distance(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_max_distance +NumericVector cpp_s2_max_distance(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_max_distance(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_max_distance(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_bounds_cap +DataFrame cpp_s2_bounds_cap(List geog); +RcppExport SEXP _s2_cpp_s2_bounds_cap(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_bounds_cap(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_bounds_rect +DataFrame cpp_s2_bounds_rect(List geog); +RcppExport SEXP _s2_cpp_s2_bounds_rect(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_bounds_rect(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_union_normalize +List cpp_s2_cell_union_normalize(List cellUnionVector); +RcppExport SEXP _s2_cpp_s2_cell_union_normalize(SEXP cellUnionVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector(cellUnionVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_union_normalize(cellUnionVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_union_is_na +LogicalVector cpp_s2_cell_union_is_na(List cellUnionVector); +RcppExport SEXP _s2_cpp_s2_cell_union_is_na(SEXP cellUnionVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector(cellUnionVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_union_is_na(cellUnionVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_union_contains +LogicalVector cpp_s2_cell_union_contains(List cellUnionVector1, List cellUnionVector2); +RcppExport SEXP _s2_cpp_s2_cell_union_contains(SEXP cellUnionVector1SEXP, SEXP cellUnionVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector1(cellUnionVector1SEXP); + Rcpp::traits::input_parameter< List >::type cellUnionVector2(cellUnionVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_union_contains(cellUnionVector1, cellUnionVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_union_contains_cell +LogicalVector cpp_s2_cell_union_contains_cell(List cellUnionVector, NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_union_contains_cell(SEXP cellUnionVectorSEXP, SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector(cellUnionVectorSEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_union_contains_cell(cellUnionVector, cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_union_intersects +LogicalVector cpp_s2_cell_union_intersects(List cellUnionVector1, List cellUnionVector2); +RcppExport SEXP _s2_cpp_s2_cell_union_intersects(SEXP cellUnionVector1SEXP, SEXP cellUnionVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector1(cellUnionVector1SEXP); + Rcpp::traits::input_parameter< List >::type cellUnionVector2(cellUnionVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_union_intersects(cellUnionVector1, cellUnionVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_union_intersection +List cpp_s2_cell_union_intersection(List cellUnionVector1, List cellUnionVector2); +RcppExport SEXP _s2_cpp_s2_cell_union_intersection(SEXP cellUnionVector1SEXP, SEXP cellUnionVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector1(cellUnionVector1SEXP); + Rcpp::traits::input_parameter< List >::type cellUnionVector2(cellUnionVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_union_intersection(cellUnionVector1, cellUnionVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_union_union +List cpp_s2_cell_union_union(List cellUnionVector1, List cellUnionVector2); +RcppExport SEXP _s2_cpp_s2_cell_union_union(SEXP cellUnionVector1SEXP, SEXP cellUnionVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector1(cellUnionVector1SEXP); + Rcpp::traits::input_parameter< List >::type cellUnionVector2(cellUnionVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_union_union(cellUnionVector1, cellUnionVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_union_difference +List cpp_s2_cell_union_difference(List cellUnionVector1, List cellUnionVector2); +RcppExport SEXP _s2_cpp_s2_cell_union_difference(SEXP cellUnionVector1SEXP, SEXP cellUnionVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector1(cellUnionVector1SEXP); + Rcpp::traits::input_parameter< List >::type cellUnionVector2(cellUnionVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_union_difference(cellUnionVector1, cellUnionVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_geography_from_cell_union +List cpp_s2_geography_from_cell_union(List cellUnionVector); +RcppExport SEXP _s2_cpp_s2_geography_from_cell_union(SEXP cellUnionVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type cellUnionVector(cellUnionVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_geography_from_cell_union(cellUnionVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_covering_cell_ids +List cpp_s2_covering_cell_ids(List geog, int min_level, int max_level, int max_cells, NumericVector buffer, bool interior); +RcppExport SEXP _s2_cpp_s2_covering_cell_ids(SEXP geogSEXP, SEXP min_levelSEXP, SEXP max_levelSEXP, SEXP max_cellsSEXP, SEXP bufferSEXP, SEXP interiorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< int >::type min_level(min_levelSEXP); + Rcpp::traits::input_parameter< int >::type max_level(max_levelSEXP); + Rcpp::traits::input_parameter< int >::type max_cells(max_cellsSEXP); + Rcpp::traits::input_parameter< NumericVector >::type buffer(bufferSEXP); + Rcpp::traits::input_parameter< bool >::type interior(interiorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_covering_cell_ids(geog, min_level, max_level, max_cells, buffer, interior)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_covering_cell_ids_agg +List cpp_s2_covering_cell_ids_agg(List geog, int min_level, int max_level, int max_cells, double buffer, bool interior, bool naRm); +RcppExport SEXP _s2_cpp_s2_covering_cell_ids_agg(SEXP geogSEXP, SEXP min_levelSEXP, SEXP max_levelSEXP, SEXP max_cellsSEXP, SEXP bufferSEXP, SEXP interiorSEXP, SEXP naRmSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< int >::type min_level(min_levelSEXP); + Rcpp::traits::input_parameter< int >::type max_level(max_levelSEXP); + Rcpp::traits::input_parameter< int >::type max_cells(max_cellsSEXP); + Rcpp::traits::input_parameter< double >::type buffer(bufferSEXP); + Rcpp::traits::input_parameter< bool >::type interior(interiorSEXP); + Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_covering_cell_ids_agg(geog, min_level, max_level, max_cells, buffer, interior, naRm)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_sentinel +NumericVector cpp_s2_cell_sentinel(); +RcppExport SEXP _s2_cpp_s2_cell_sentinel() { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_sentinel()); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_from_string +NumericVector cpp_s2_cell_from_string(CharacterVector cellString); +RcppExport SEXP _s2_cpp_s2_cell_from_string(SEXP cellStringSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< CharacterVector >::type cellString(cellStringSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_from_string(cellString)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_from_lnglat +NumericVector cpp_s2_cell_from_lnglat(List lnglat); +RcppExport SEXP _s2_cpp_s2_cell_from_lnglat(SEXP lnglatSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type lnglat(lnglatSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_from_lnglat(lnglat)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_to_lnglat +List cpp_s2_cell_to_lnglat(NumericVector cellId); +RcppExport SEXP _s2_cpp_s2_cell_to_lnglat(SEXP cellIdSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellId(cellIdSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_to_lnglat(cellId)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_to_cell_union +List cpp_s2_cell_to_cell_union(NumericVector cellId); +RcppExport SEXP _s2_cpp_s2_cell_to_cell_union(SEXP cellIdSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellId(cellIdSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_to_cell_union(cellId)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_is_na +LogicalVector cpp_s2_cell_is_na(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_is_na(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_is_na(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_sort +NumericVector cpp_s2_cell_sort(NumericVector cellIdVector, bool decreasing); +RcppExport SEXP _s2_cpp_s2_cell_sort(SEXP cellIdVectorSEXP, SEXP decreasingSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + Rcpp::traits::input_parameter< bool >::type decreasing(decreasingSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_sort(cellIdVector, decreasing)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_range +NumericVector cpp_s2_cell_range(NumericVector cellIdVector, bool naRm); +RcppExport SEXP _s2_cpp_s2_cell_range(SEXP cellIdVectorSEXP, SEXP naRmSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_range(cellIdVector, naRm)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_unique +NumericVector cpp_s2_cell_unique(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_unique(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_unique(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_to_string +CharacterVector cpp_s2_cell_to_string(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_to_string(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_to_string(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_debug_string +CharacterVector cpp_s2_cell_debug_string(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_debug_string(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_debug_string(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_is_valid +LogicalVector cpp_s2_cell_is_valid(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_is_valid(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_is_valid(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_center +List cpp_s2_cell_center(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_center(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_center(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_polygon +List cpp_s2_cell_polygon(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_polygon(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_polygon(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_vertex +List cpp_s2_cell_vertex(NumericVector cellIdVector, IntegerVector k); +RcppExport SEXP _s2_cpp_s2_cell_vertex(SEXP cellIdVectorSEXP, SEXP kSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + Rcpp::traits::input_parameter< IntegerVector >::type k(kSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_vertex(cellIdVector, k)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_level +IntegerVector cpp_s2_cell_level(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_level(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_level(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_area +NumericVector cpp_s2_cell_area(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_area(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_area(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_area_approx +NumericVector cpp_s2_cell_area_approx(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_area_approx(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_area_approx(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_parent +NumericVector cpp_s2_cell_parent(NumericVector cellIdVector, IntegerVector level); +RcppExport SEXP _s2_cpp_s2_cell_parent(SEXP cellIdVectorSEXP, SEXP levelSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + Rcpp::traits::input_parameter< IntegerVector >::type level(levelSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_parent(cellIdVector, level)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_child +NumericVector cpp_s2_cell_child(NumericVector cellIdVector, IntegerVector k); +RcppExport SEXP _s2_cpp_s2_cell_child(SEXP cellIdVectorSEXP, SEXP kSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + Rcpp::traits::input_parameter< IntegerVector >::type k(kSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_child(cellIdVector, k)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_edge_neighbour +NumericVector cpp_s2_cell_edge_neighbour(NumericVector cellIdVector, IntegerVector k); +RcppExport SEXP _s2_cpp_s2_cell_edge_neighbour(SEXP cellIdVectorSEXP, SEXP kSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + Rcpp::traits::input_parameter< IntegerVector >::type k(kSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_edge_neighbour(cellIdVector, k)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_cummax +NumericVector cpp_s2_cell_cummax(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_cummax(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_cummax(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_cummin +NumericVector cpp_s2_cell_cummin(NumericVector cellIdVector); +RcppExport SEXP _s2_cpp_s2_cell_cummin(SEXP cellIdVectorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector(cellIdVectorSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_cummin(cellIdVector)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_eq +LogicalVector cpp_s2_cell_eq(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_eq(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_eq(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_neq +LogicalVector cpp_s2_cell_neq(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_neq(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_neq(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_lt +LogicalVector cpp_s2_cell_lt(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_lt(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_lt(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_lte +LogicalVector cpp_s2_cell_lte(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_lte(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_lte(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_gte +LogicalVector cpp_s2_cell_gte(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_gte(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_gte(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_gt +LogicalVector cpp_s2_cell_gt(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_gt(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_gt(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_contains +LogicalVector cpp_s2_cell_contains(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_contains(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_contains(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_may_intersect +LogicalVector cpp_s2_cell_may_intersect(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_may_intersect(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_may_intersect(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_distance +NumericVector cpp_s2_cell_distance(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_distance(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_distance(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_max_distance +NumericVector cpp_s2_cell_max_distance(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_max_distance(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_max_distance(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_common_ancestor_level +IntegerVector cpp_s2_cell_common_ancestor_level(NumericVector cellIdVector1, NumericVector cellIdVector2); +RcppExport SEXP _s2_cpp_s2_cell_common_ancestor_level(SEXP cellIdVector1SEXP, SEXP cellIdVector2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector1(cellIdVector1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type cellIdVector2(cellIdVector2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_common_ancestor_level(cellIdVector1, cellIdVector2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_cell_common_ancestor_level_agg +int cpp_s2_cell_common_ancestor_level_agg(NumericVector cellId); +RcppExport SEXP _s2_cpp_s2_cell_common_ancestor_level_agg(SEXP cellIdSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type cellId(cellIdSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_cell_common_ancestor_level_agg(cellId)); + return rcpp_result_gen; +END_RCPP +} +// s2_geography_full +List s2_geography_full(LogicalVector x); +RcppExport SEXP _s2_s2_geography_full(SEXP xSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< LogicalVector >::type x(xSEXP); + rcpp_result_gen = Rcpp::wrap(s2_geography_full(x)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_geography_is_na +LogicalVector cpp_s2_geography_is_na(List geog); +RcppExport SEXP _s2_cpp_s2_geography_is_na(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_geography_is_na(geog)); + return rcpp_result_gen; +END_RCPP +} +// s2_lnglat_from_s2_point +List s2_lnglat_from_s2_point(List s2_point); +RcppExport SEXP _s2_s2_lnglat_from_s2_point(SEXP s2_pointSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type s2_point(s2_pointSEXP); + rcpp_result_gen = Rcpp::wrap(s2_lnglat_from_s2_point(s2_point)); + return rcpp_result_gen; +END_RCPP +} +// s2_point_from_s2_lnglat +List s2_point_from_s2_lnglat(List s2_lnglat); +RcppExport SEXP _s2_s2_point_from_s2_lnglat(SEXP s2_lnglatSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type s2_lnglat(s2_lnglatSEXP); + rcpp_result_gen = Rcpp::wrap(s2_point_from_s2_lnglat(s2_lnglat)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_closest_feature +IntegerVector cpp_s2_closest_feature(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_closest_feature(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_closest_feature(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_farthest_feature +IntegerVector cpp_s2_farthest_feature(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_farthest_feature(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_farthest_feature(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_closest_edges +List cpp_s2_closest_edges(List geog1, List geog2, int n, double min_distance, double max_distance); +RcppExport SEXP _s2_cpp_s2_closest_edges(SEXP geog1SEXP, SEXP geog2SEXP, SEXP nSEXP, SEXP min_distanceSEXP, SEXP max_distanceSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< int >::type n(nSEXP); + Rcpp::traits::input_parameter< double >::type min_distance(min_distanceSEXP); + Rcpp::traits::input_parameter< double >::type max_distance(max_distanceSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_closest_edges(geog1, geog2, n, min_distance, max_distance)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_may_intersect_matrix +List cpp_s2_may_intersect_matrix(List geog1, List geog2, int maxEdgesPerCell, int maxFeatureCells, List s2options); +RcppExport SEXP _s2_cpp_s2_may_intersect_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP maxEdgesPerCellSEXP, SEXP maxFeatureCellsSEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< int >::type maxEdgesPerCell(maxEdgesPerCellSEXP); + Rcpp::traits::input_parameter< int >::type maxFeatureCells(maxFeatureCellsSEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_may_intersect_matrix(geog1, geog2, maxEdgesPerCell, maxFeatureCells, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_contains_matrix +List cpp_s2_contains_matrix(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_contains_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_contains_matrix(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_within_matrix +List cpp_s2_within_matrix(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_within_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_within_matrix(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_intersects_matrix +List cpp_s2_intersects_matrix(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_intersects_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_intersects_matrix(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_equals_matrix +List cpp_s2_equals_matrix(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_equals_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_equals_matrix(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_touches_matrix +List cpp_s2_touches_matrix(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_touches_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_touches_matrix(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_dwithin_matrix +List cpp_s2_dwithin_matrix(List geog1, List geog2, double distance); +RcppExport SEXP _s2_cpp_s2_dwithin_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP distanceSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< double >::type distance(distanceSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_dwithin_matrix(geog1, geog2, distance)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_distance_matrix +NumericMatrix cpp_s2_distance_matrix(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_distance_matrix(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_distance_matrix(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_max_distance_matrix +NumericMatrix cpp_s2_max_distance_matrix(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_max_distance_matrix(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_max_distance_matrix(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_contains_matrix_brute_force +List cpp_s2_contains_matrix_brute_force(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_contains_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_contains_matrix_brute_force(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_within_matrix_brute_force +List cpp_s2_within_matrix_brute_force(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_within_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_within_matrix_brute_force(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_intersects_matrix_brute_force +List cpp_s2_intersects_matrix_brute_force(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_intersects_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_intersects_matrix_brute_force(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_disjoint_matrix_brute_force +List cpp_s2_disjoint_matrix_brute_force(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_disjoint_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_disjoint_matrix_brute_force(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_equals_matrix_brute_force +List cpp_s2_equals_matrix_brute_force(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_equals_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_equals_matrix_brute_force(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_dwithin_matrix_brute_force +List cpp_s2_dwithin_matrix_brute_force(List geog1, List geog2, double distance); +RcppExport SEXP _s2_cpp_s2_dwithin_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP distanceSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< double >::type distance(distanceSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_dwithin_matrix_brute_force(geog1, geog2, distance)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_intersects +LogicalVector cpp_s2_intersects(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_intersects(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_intersects(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_equals +LogicalVector cpp_s2_equals(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_equals(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_equals(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_contains +LogicalVector cpp_s2_contains(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_contains(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_contains(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_touches +LogicalVector cpp_s2_touches(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_touches(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_touches(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_dwithin +LogicalVector cpp_s2_dwithin(List geog1, List geog2, NumericVector distance); +RcppExport SEXP _s2_cpp_s2_dwithin(SEXP geog1SEXP, SEXP geog2SEXP, SEXP distanceSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< NumericVector >::type distance(distanceSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_dwithin(geog1, geog2, distance)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_prepared_dwithin +LogicalVector cpp_s2_prepared_dwithin(List geog1, List geog2, NumericVector distance); +RcppExport SEXP _s2_cpp_s2_prepared_dwithin(SEXP geog1SEXP, SEXP geog2SEXP, SEXP distanceSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< NumericVector >::type distance(distanceSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_prepared_dwithin(geog1, geog2, distance)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_intersects_box +LogicalVector cpp_s2_intersects_box(List geog, NumericVector lng1, NumericVector lat1, NumericVector lng2, NumericVector lat2, IntegerVector detail, List s2options); +RcppExport SEXP _s2_cpp_s2_intersects_box(SEXP geogSEXP, SEXP lng1SEXP, SEXP lat1SEXP, SEXP lng2SEXP, SEXP lat2SEXP, SEXP detailSEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< NumericVector >::type lng1(lng1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type lat1(lat1SEXP); + Rcpp::traits::input_parameter< NumericVector >::type lng2(lng2SEXP); + Rcpp::traits::input_parameter< NumericVector >::type lat2(lat2SEXP); + Rcpp::traits::input_parameter< IntegerVector >::type detail(detailSEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_intersects_box(geog, lng1, lat1, lng2, lat2, detail, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_intersection +List cpp_s2_intersection(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_intersection(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_intersection(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_union +List cpp_s2_union(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_union(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_union(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_difference +List cpp_s2_difference(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_difference(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_difference(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_sym_difference +List cpp_s2_sym_difference(List geog1, List geog2, List s2options); +RcppExport SEXP _s2_cpp_s2_sym_difference(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_sym_difference(geog1, geog2, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_coverage_union_agg +List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm); +RcppExport SEXP _s2_cpp_s2_coverage_union_agg(SEXP geogSEXP, SEXP s2optionsSEXP, SEXP naRmSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_coverage_union_agg(geog, s2options, naRm)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_union_agg +List cpp_s2_union_agg(List geog, List s2options, bool naRm); +RcppExport SEXP _s2_cpp_s2_union_agg(SEXP geogSEXP, SEXP s2optionsSEXP, SEXP naRmSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_union_agg(geog, s2options, naRm)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_centroid_agg +List cpp_s2_centroid_agg(List geog, bool naRm); +RcppExport SEXP _s2_cpp_s2_centroid_agg(SEXP geogSEXP, SEXP naRmSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_centroid_agg(geog, naRm)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_rebuild_agg +List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm); +RcppExport SEXP _s2_cpp_s2_rebuild_agg(SEXP geogSEXP, SEXP s2optionsSEXP, SEXP naRmSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_rebuild_agg(geog, s2options, naRm)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_closest_point +List cpp_s2_closest_point(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_closest_point(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_closest_point(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_minimum_clearance_line_between +List cpp_s2_minimum_clearance_line_between(List geog1, List geog2); +RcppExport SEXP _s2_cpp_s2_minimum_clearance_line_between(SEXP geog1SEXP, SEXP geog2SEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP); + Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_minimum_clearance_line_between(geog1, geog2)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_centroid +List cpp_s2_centroid(List geog); +RcppExport SEXP _s2_cpp_s2_centroid(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_centroid(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_point_on_surface +List cpp_s2_point_on_surface(List geog); +RcppExport SEXP _s2_cpp_s2_point_on_surface(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_point_on_surface(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_boundary +List cpp_s2_boundary(List geog); +RcppExport SEXP _s2_cpp_s2_boundary(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_boundary(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_rebuild +List cpp_s2_rebuild(List geog, List s2options); +RcppExport SEXP _s2_cpp_s2_rebuild(SEXP geogSEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_rebuild(geog, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_unary_union +List cpp_s2_unary_union(List geog, List s2options); +RcppExport SEXP _s2_cpp_s2_unary_union(SEXP geogSEXP, SEXP s2optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_unary_union(geog, s2options)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_interpolate_normalized +List cpp_s2_interpolate_normalized(List geog, NumericVector distanceNormalized); +RcppExport SEXP _s2_cpp_s2_interpolate_normalized(SEXP geogSEXP, SEXP distanceNormalizedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< NumericVector >::type distanceNormalized(distanceNormalizedSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_interpolate_normalized(geog, distanceNormalized)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_buffer_cells +List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int minLevel); +RcppExport SEXP _s2_cpp_s2_buffer_cells(SEXP geogSEXP, SEXP distanceSEXP, SEXP maxCellsSEXP, SEXP minLevelSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< NumericVector >::type distance(distanceSEXP); + Rcpp::traits::input_parameter< int >::type maxCells(maxCellsSEXP); + Rcpp::traits::input_parameter< int >::type minLevel(minLevelSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_buffer_cells(geog, distance, maxCells, minLevel)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_convex_hull +List cpp_s2_convex_hull(List geog); +RcppExport SEXP _s2_cpp_s2_convex_hull(SEXP geogSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_convex_hull(geog)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_convex_hull_agg +List cpp_s2_convex_hull_agg(List geog, bool naRm); +RcppExport SEXP _s2_cpp_s2_convex_hull_agg(SEXP geogSEXP, SEXP naRmSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type geog(geogSEXP); + Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_convex_hull_agg(geog, naRm)); + return rcpp_result_gen; +END_RCPP +} + +RcppExport SEXP c_s2_geography_writer_new(SEXP, SEXP, SEXP, SEXP); +RcppExport SEXP c_s2_handle_geography(SEXP, SEXP); +RcppExport SEXP c_s2_handle_geography_tessellated(SEXP, SEXP); +RcppExport SEXP c_s2_projection_mercator(SEXP); +RcppExport SEXP c_s2_projection_orthographic(SEXP); +RcppExport SEXP c_s2_projection_plate_carree(SEXP); +RcppExport SEXP c_s2_trans_s2_lnglat_new(); +RcppExport SEXP c_s2_trans_s2_point_new(); + +static const R_CallMethodDef CallEntries[] = { + {"_s2_cpp_s2_init", (DL_FUNC) &_s2_cpp_s2_init, 0}, + {"_s2_cpp_s2_is_collection", (DL_FUNC) &_s2_cpp_s2_is_collection, 1}, + {"_s2_cpp_s2_is_valid", (DL_FUNC) &_s2_cpp_s2_is_valid, 1}, + {"_s2_cpp_s2_is_valid_reason", (DL_FUNC) &_s2_cpp_s2_is_valid_reason, 1}, + {"_s2_cpp_s2_dimension", (DL_FUNC) &_s2_cpp_s2_dimension, 1}, + {"_s2_cpp_s2_num_points", (DL_FUNC) &_s2_cpp_s2_num_points, 1}, + {"_s2_cpp_s2_is_empty", (DL_FUNC) &_s2_cpp_s2_is_empty, 1}, + {"_s2_cpp_s2_area", (DL_FUNC) &_s2_cpp_s2_area, 1}, + {"_s2_cpp_s2_length", (DL_FUNC) &_s2_cpp_s2_length, 1}, + {"_s2_cpp_s2_perimeter", (DL_FUNC) &_s2_cpp_s2_perimeter, 1}, + {"_s2_cpp_s2_x", (DL_FUNC) &_s2_cpp_s2_x, 1}, + {"_s2_cpp_s2_y", (DL_FUNC) &_s2_cpp_s2_y, 1}, + {"_s2_cpp_s2_project_normalized", (DL_FUNC) &_s2_cpp_s2_project_normalized, 2}, + {"_s2_cpp_s2_distance", (DL_FUNC) &_s2_cpp_s2_distance, 2}, + {"_s2_cpp_s2_max_distance", (DL_FUNC) &_s2_cpp_s2_max_distance, 2}, + {"_s2_cpp_s2_bounds_cap", (DL_FUNC) &_s2_cpp_s2_bounds_cap, 1}, + {"_s2_cpp_s2_bounds_rect", (DL_FUNC) &_s2_cpp_s2_bounds_rect, 1}, + {"_s2_cpp_s2_cell_union_normalize", (DL_FUNC) &_s2_cpp_s2_cell_union_normalize, 1}, + {"_s2_cpp_s2_cell_union_is_na", (DL_FUNC) &_s2_cpp_s2_cell_union_is_na, 1}, + {"_s2_cpp_s2_cell_union_contains", (DL_FUNC) &_s2_cpp_s2_cell_union_contains, 2}, + {"_s2_cpp_s2_cell_union_contains_cell", (DL_FUNC) &_s2_cpp_s2_cell_union_contains_cell, 2}, + {"_s2_cpp_s2_cell_union_intersects", (DL_FUNC) &_s2_cpp_s2_cell_union_intersects, 2}, + {"_s2_cpp_s2_cell_union_intersection", (DL_FUNC) &_s2_cpp_s2_cell_union_intersection, 2}, + {"_s2_cpp_s2_cell_union_union", (DL_FUNC) &_s2_cpp_s2_cell_union_union, 2}, + {"_s2_cpp_s2_cell_union_difference", (DL_FUNC) &_s2_cpp_s2_cell_union_difference, 2}, + {"_s2_cpp_s2_geography_from_cell_union", (DL_FUNC) &_s2_cpp_s2_geography_from_cell_union, 1}, + {"_s2_cpp_s2_covering_cell_ids", (DL_FUNC) &_s2_cpp_s2_covering_cell_ids, 6}, + {"_s2_cpp_s2_covering_cell_ids_agg", (DL_FUNC) &_s2_cpp_s2_covering_cell_ids_agg, 7}, + {"_s2_cpp_s2_cell_sentinel", (DL_FUNC) &_s2_cpp_s2_cell_sentinel, 0}, + {"_s2_cpp_s2_cell_from_string", (DL_FUNC) &_s2_cpp_s2_cell_from_string, 1}, + {"_s2_cpp_s2_cell_from_lnglat", (DL_FUNC) &_s2_cpp_s2_cell_from_lnglat, 1}, + {"_s2_cpp_s2_cell_to_lnglat", (DL_FUNC) &_s2_cpp_s2_cell_to_lnglat, 1}, + {"_s2_cpp_s2_cell_to_cell_union", (DL_FUNC) &_s2_cpp_s2_cell_to_cell_union, 1}, + {"_s2_cpp_s2_cell_is_na", (DL_FUNC) &_s2_cpp_s2_cell_is_na, 1}, + {"_s2_cpp_s2_cell_sort", (DL_FUNC) &_s2_cpp_s2_cell_sort, 2}, + {"_s2_cpp_s2_cell_range", (DL_FUNC) &_s2_cpp_s2_cell_range, 2}, + {"_s2_cpp_s2_cell_unique", (DL_FUNC) &_s2_cpp_s2_cell_unique, 1}, + {"_s2_cpp_s2_cell_to_string", (DL_FUNC) &_s2_cpp_s2_cell_to_string, 1}, + {"_s2_cpp_s2_cell_debug_string", (DL_FUNC) &_s2_cpp_s2_cell_debug_string, 1}, + {"_s2_cpp_s2_cell_is_valid", (DL_FUNC) &_s2_cpp_s2_cell_is_valid, 1}, + {"_s2_cpp_s2_cell_center", (DL_FUNC) &_s2_cpp_s2_cell_center, 1}, + {"_s2_cpp_s2_cell_polygon", (DL_FUNC) &_s2_cpp_s2_cell_polygon, 1}, + {"_s2_cpp_s2_cell_vertex", (DL_FUNC) &_s2_cpp_s2_cell_vertex, 2}, + {"_s2_cpp_s2_cell_level", (DL_FUNC) &_s2_cpp_s2_cell_level, 1}, + {"_s2_cpp_s2_cell_area", (DL_FUNC) &_s2_cpp_s2_cell_area, 1}, + {"_s2_cpp_s2_cell_area_approx", (DL_FUNC) &_s2_cpp_s2_cell_area_approx, 1}, + {"_s2_cpp_s2_cell_parent", (DL_FUNC) &_s2_cpp_s2_cell_parent, 2}, + {"_s2_cpp_s2_cell_child", (DL_FUNC) &_s2_cpp_s2_cell_child, 2}, + {"_s2_cpp_s2_cell_edge_neighbour", (DL_FUNC) &_s2_cpp_s2_cell_edge_neighbour, 2}, + {"_s2_cpp_s2_cell_cummax", (DL_FUNC) &_s2_cpp_s2_cell_cummax, 1}, + {"_s2_cpp_s2_cell_cummin", (DL_FUNC) &_s2_cpp_s2_cell_cummin, 1}, + {"_s2_cpp_s2_cell_eq", (DL_FUNC) &_s2_cpp_s2_cell_eq, 2}, + {"_s2_cpp_s2_cell_neq", (DL_FUNC) &_s2_cpp_s2_cell_neq, 2}, + {"_s2_cpp_s2_cell_lt", (DL_FUNC) &_s2_cpp_s2_cell_lt, 2}, + {"_s2_cpp_s2_cell_lte", (DL_FUNC) &_s2_cpp_s2_cell_lte, 2}, + {"_s2_cpp_s2_cell_gte", (DL_FUNC) &_s2_cpp_s2_cell_gte, 2}, + {"_s2_cpp_s2_cell_gt", (DL_FUNC) &_s2_cpp_s2_cell_gt, 2}, + {"_s2_cpp_s2_cell_contains", (DL_FUNC) &_s2_cpp_s2_cell_contains, 2}, + {"_s2_cpp_s2_cell_may_intersect", (DL_FUNC) &_s2_cpp_s2_cell_may_intersect, 2}, + {"_s2_cpp_s2_cell_distance", (DL_FUNC) &_s2_cpp_s2_cell_distance, 2}, + {"_s2_cpp_s2_cell_max_distance", (DL_FUNC) &_s2_cpp_s2_cell_max_distance, 2}, + {"_s2_cpp_s2_cell_common_ancestor_level", (DL_FUNC) &_s2_cpp_s2_cell_common_ancestor_level, 2}, + {"_s2_cpp_s2_cell_common_ancestor_level_agg", (DL_FUNC) &_s2_cpp_s2_cell_common_ancestor_level_agg, 1}, + {"_s2_s2_geography_full", (DL_FUNC) &_s2_s2_geography_full, 1}, + {"_s2_cpp_s2_geography_is_na", (DL_FUNC) &_s2_cpp_s2_geography_is_na, 1}, + {"_s2_s2_lnglat_from_s2_point", (DL_FUNC) &_s2_s2_lnglat_from_s2_point, 1}, + {"_s2_s2_point_from_s2_lnglat", (DL_FUNC) &_s2_s2_point_from_s2_lnglat, 1}, + {"_s2_cpp_s2_closest_feature", (DL_FUNC) &_s2_cpp_s2_closest_feature, 2}, + {"_s2_cpp_s2_farthest_feature", (DL_FUNC) &_s2_cpp_s2_farthest_feature, 2}, + {"_s2_cpp_s2_closest_edges", (DL_FUNC) &_s2_cpp_s2_closest_edges, 5}, + {"_s2_cpp_s2_may_intersect_matrix", (DL_FUNC) &_s2_cpp_s2_may_intersect_matrix, 5}, + {"_s2_cpp_s2_contains_matrix", (DL_FUNC) &_s2_cpp_s2_contains_matrix, 3}, + {"_s2_cpp_s2_within_matrix", (DL_FUNC) &_s2_cpp_s2_within_matrix, 3}, + {"_s2_cpp_s2_intersects_matrix", (DL_FUNC) &_s2_cpp_s2_intersects_matrix, 3}, + {"_s2_cpp_s2_equals_matrix", (DL_FUNC) &_s2_cpp_s2_equals_matrix, 3}, + {"_s2_cpp_s2_touches_matrix", (DL_FUNC) &_s2_cpp_s2_touches_matrix, 3}, + {"_s2_cpp_s2_dwithin_matrix", (DL_FUNC) &_s2_cpp_s2_dwithin_matrix, 3}, + {"_s2_cpp_s2_distance_matrix", (DL_FUNC) &_s2_cpp_s2_distance_matrix, 2}, + {"_s2_cpp_s2_max_distance_matrix", (DL_FUNC) &_s2_cpp_s2_max_distance_matrix, 2}, + {"_s2_cpp_s2_contains_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_contains_matrix_brute_force, 3}, + {"_s2_cpp_s2_within_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_within_matrix_brute_force, 3}, + {"_s2_cpp_s2_intersects_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_intersects_matrix_brute_force, 3}, + {"_s2_cpp_s2_disjoint_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_disjoint_matrix_brute_force, 3}, + {"_s2_cpp_s2_equals_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_equals_matrix_brute_force, 3}, + {"_s2_cpp_s2_dwithin_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_dwithin_matrix_brute_force, 3}, + {"_s2_cpp_s2_intersects", (DL_FUNC) &_s2_cpp_s2_intersects, 3}, + {"_s2_cpp_s2_equals", (DL_FUNC) &_s2_cpp_s2_equals, 3}, + {"_s2_cpp_s2_contains", (DL_FUNC) &_s2_cpp_s2_contains, 3}, + {"_s2_cpp_s2_touches", (DL_FUNC) &_s2_cpp_s2_touches, 3}, + {"_s2_cpp_s2_dwithin", (DL_FUNC) &_s2_cpp_s2_dwithin, 3}, + {"_s2_cpp_s2_prepared_dwithin", (DL_FUNC) &_s2_cpp_s2_prepared_dwithin, 3}, + {"_s2_cpp_s2_intersects_box", (DL_FUNC) &_s2_cpp_s2_intersects_box, 7}, + {"_s2_cpp_s2_intersection", (DL_FUNC) &_s2_cpp_s2_intersection, 3}, + {"_s2_cpp_s2_union", (DL_FUNC) &_s2_cpp_s2_union, 3}, + {"_s2_cpp_s2_difference", (DL_FUNC) &_s2_cpp_s2_difference, 3}, + {"_s2_cpp_s2_sym_difference", (DL_FUNC) &_s2_cpp_s2_sym_difference, 3}, + {"_s2_cpp_s2_coverage_union_agg", (DL_FUNC) &_s2_cpp_s2_coverage_union_agg, 3}, + {"_s2_cpp_s2_union_agg", (DL_FUNC) &_s2_cpp_s2_union_agg, 3}, + {"_s2_cpp_s2_centroid_agg", (DL_FUNC) &_s2_cpp_s2_centroid_agg, 2}, + {"_s2_cpp_s2_rebuild_agg", (DL_FUNC) &_s2_cpp_s2_rebuild_agg, 3}, + {"_s2_cpp_s2_closest_point", (DL_FUNC) &_s2_cpp_s2_closest_point, 2}, + {"_s2_cpp_s2_minimum_clearance_line_between", (DL_FUNC) &_s2_cpp_s2_minimum_clearance_line_between, 2}, + {"_s2_cpp_s2_centroid", (DL_FUNC) &_s2_cpp_s2_centroid, 1}, + {"_s2_cpp_s2_point_on_surface", (DL_FUNC) &_s2_cpp_s2_point_on_surface, 1}, + {"_s2_cpp_s2_boundary", (DL_FUNC) &_s2_cpp_s2_boundary, 1}, + {"_s2_cpp_s2_rebuild", (DL_FUNC) &_s2_cpp_s2_rebuild, 2}, + {"_s2_cpp_s2_unary_union", (DL_FUNC) &_s2_cpp_s2_unary_union, 2}, + {"_s2_cpp_s2_interpolate_normalized", (DL_FUNC) &_s2_cpp_s2_interpolate_normalized, 2}, + {"_s2_cpp_s2_buffer_cells", (DL_FUNC) &_s2_cpp_s2_buffer_cells, 4}, + {"_s2_cpp_s2_convex_hull", (DL_FUNC) &_s2_cpp_s2_convex_hull, 1}, + {"_s2_cpp_s2_convex_hull_agg", (DL_FUNC) &_s2_cpp_s2_convex_hull_agg, 2}, + {"c_s2_geography_writer_new", (DL_FUNC) &c_s2_geography_writer_new, 4}, + {"c_s2_handle_geography", (DL_FUNC) &c_s2_handle_geography, 2}, + {"c_s2_handle_geography_tessellated", (DL_FUNC) &c_s2_handle_geography_tessellated, 2}, + {"c_s2_projection_mercator", (DL_FUNC) &c_s2_projection_mercator, 1}, + {"c_s2_projection_orthographic", (DL_FUNC) &c_s2_projection_orthographic, 1}, + {"c_s2_projection_plate_carree", (DL_FUNC) &c_s2_projection_plate_carree, 1}, + {"c_s2_trans_s2_lnglat_new", (DL_FUNC) &c_s2_trans_s2_lnglat_new, 0}, + {"c_s2_trans_s2_point_new", (DL_FUNC) &c_s2_trans_s2_point_new, 0}, + {NULL, NULL, 0} +}; + +RcppExport void R_init_s2(DllInfo *dll) { + R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/absl/algorithm/algorithm.h b/src/absl/algorithm/algorithm.h new file mode 100644 index 0000000..e9b4733 --- /dev/null +++ b/src/absl/algorithm/algorithm.h @@ -0,0 +1,159 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: algorithm.h +// ----------------------------------------------------------------------------- +// +// This header file contains Google extensions to the standard C++ +// header. + +#ifndef ABSL_ALGORITHM_ALGORITHM_H_ +#define ABSL_ALGORITHM_ALGORITHM_H_ + +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace algorithm_internal { + +// Performs comparisons with operator==, similar to C++14's `std::equal_to<>`. +struct EqualTo { + template + bool operator()(const T& a, const U& b) const { + return a == b; + } +}; + +template +bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2, Pred pred, std::input_iterator_tag, + std::input_iterator_tag) { + while (true) { + if (first1 == last1) return first2 == last2; + if (first2 == last2) return false; + if (!pred(*first1, *first2)) return false; + ++first1; + ++first2; + } +} + +template +bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2, Pred&& pred, std::random_access_iterator_tag, + std::random_access_iterator_tag) { + return (last1 - first1 == last2 - first2) && + std::equal(first1, last1, first2, std::forward(pred)); +} + +// When we are using our own internal predicate that just applies operator==, we +// forward to the non-predicate form of std::equal. This enables an optimization +// in libstdc++ that can result in std::memcmp being used for integer types. +template +bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2, algorithm_internal::EqualTo /* unused */, + std::random_access_iterator_tag, + std::random_access_iterator_tag) { + return (last1 - first1 == last2 - first2) && + std::equal(first1, last1, first2); +} + +template +It RotateImpl(It first, It middle, It last, std::true_type) { + return std::rotate(first, middle, last); +} + +template +It RotateImpl(It first, It middle, It last, std::false_type) { + std::rotate(first, middle, last); + return std::next(first, std::distance(middle, last)); +} + +} // namespace algorithm_internal + +// equal() +// +// Compares the equality of two ranges specified by pairs of iterators, using +// the given predicate, returning true iff for each corresponding iterator i1 +// and i2 in the first and second range respectively, pred(*i1, *i2) == true +// +// This comparison takes at most min(`last1` - `first1`, `last2` - `first2`) +// invocations of the predicate. Additionally, if InputIter1 and InputIter2 are +// both random-access iterators, and `last1` - `first1` != `last2` - `first2`, +// then the predicate is never invoked and the function returns false. +// +// This is a C++11-compatible implementation of C++14 `std::equal`. See +// https://en.cppreference.com/w/cpp/algorithm/equal for more information. +template +bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2, Pred&& pred) { + return algorithm_internal::EqualImpl( + first1, last1, first2, last2, std::forward(pred), + typename std::iterator_traits::iterator_category{}, + typename std::iterator_traits::iterator_category{}); +} + +// Overload of equal() that performs comparison of two ranges specified by pairs +// of iterators using operator==. +template +bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2) { + return absl::equal(first1, last1, first2, last2, + algorithm_internal::EqualTo{}); +} + +// linear_search() +// +// Performs a linear search for `value` using the iterator `first` up to +// but not including `last`, returning true if [`first`, `last`) contains an +// element equal to `value`. +// +// A linear search is of O(n) complexity which is guaranteed to make at most +// n = (`last` - `first`) comparisons. A linear search over short containers +// may be faster than a binary search, even when the container is sorted. +template +bool linear_search(InputIterator first, InputIterator last, + const EqualityComparable& value) { + return std::find(first, last, value) != last; +} + +// rotate() +// +// Performs a left rotation on a range of elements (`first`, `last`) such that +// `middle` is now the first element. `rotate()` returns an iterator pointing to +// the first element before rotation. This function is exactly the same as +// `std::rotate`, but fixes a bug in gcc +// <= 4.9 where `std::rotate` returns `void` instead of an iterator. +// +// The complexity of this algorithm is the same as that of `std::rotate`, but if +// `ForwardIterator` is not a random-access iterator, then `absl::rotate` +// performs an additional pass over the range to construct the return value. +template +ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, + ForwardIterator last) { + return algorithm_internal::RotateImpl( + first, middle, last, + std::is_same()); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/src/absl/algorithm/container.h b/src/absl/algorithm/container.h new file mode 100644 index 0000000..26b1952 --- /dev/null +++ b/src/absl/algorithm/container.h @@ -0,0 +1,1774 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: container.h +// ----------------------------------------------------------------------------- +// +// This header file provides Container-based versions of algorithmic functions +// within the C++ standard library. The following standard library sets of +// functions are covered within this file: +// +// * Algorithmic functions +// * Algorithmic functions +// * functions +// +// The standard library functions operate on iterator ranges; the functions +// within this API operate on containers, though many return iterator ranges. +// +// All functions within this API are named with a `c_` prefix. Calls such as +// `absl::c_xx(container, ...) are equivalent to std:: functions such as +// `std::xx(std::begin(cont), std::end(cont), ...)`. Functions that act on +// iterators but not conceptually on iterator ranges (e.g. `std::iter_swap`) +// have no equivalent here. +// +// For template parameter and variable naming, `C` indicates the container type +// to which the function is applied, `Pred` indicates the predicate object type +// to be used by the function and `T` indicates the applicable element type. + +#ifndef ABSL_ALGORITHM_CONTAINER_H_ +#define ABSL_ALGORITHM_CONTAINER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_algorithm_internal { + +// NOTE: it is important to defer to ADL lookup for building with C++ modules, +// especially for headers like which are not visible from this file +// but specialize std::begin and std::end. +using std::begin; +using std::end; + +// The type of the iterator given by begin(c) (possibly std::begin(c)). +// ContainerIter> gives vector::const_iterator, +// while ContainerIter> gives vector::iterator. +template +using ContainerIter = decltype(begin(std::declval())); + +// An MSVC bug involving template parameter substitution requires us to use +// decltype() here instead of just std::pair. +template +using ContainerIterPairType = + decltype(std::make_pair(ContainerIter(), ContainerIter())); + +template +using ContainerDifferenceType = + decltype(std::distance(std::declval>(), + std::declval>())); + +template +using ContainerPointerType = + typename std::iterator_traits>::pointer; + +// container_algorithm_internal::c_begin and +// container_algorithm_internal::c_end are abbreviations for proper ADL +// lookup of std::begin and std::end, i.e. +// using std::begin; +// using std::end; +// std::foo(begin(c), end(c)); +// becomes +// std::foo(container_algorithm_internal::begin(c), +// container_algorithm_internal::end(c)); +// These are meant for internal use only. + +template +ContainerIter c_begin(C& c) { return begin(c); } + +template +ContainerIter c_end(C& c) { return end(c); } + +template +struct IsUnorderedContainer : std::false_type {}; + +template +struct IsUnorderedContainer< + std::unordered_map> : std::true_type {}; + +template +struct IsUnorderedContainer> + : std::true_type {}; + +// container_algorithm_internal::c_size. It is meant for internal use only. + +template +auto c_size(C& c) -> decltype(c.size()) { + return c.size(); +} + +template +constexpr std::size_t c_size(T (&)[N]) { + return N; +} + +} // namespace container_algorithm_internal + +// PUBLIC API + +//------------------------------------------------------------------------------ +// Abseil algorithm.h functions +//------------------------------------------------------------------------------ + +// c_linear_search() +// +// Container-based version of absl::linear_search() for performing a linear +// search within a container. +template +bool c_linear_search(const C& c, EqualityComparable&& value) { + return linear_search(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +//------------------------------------------------------------------------------ +// algorithms +//------------------------------------------------------------------------------ + +// c_distance() +// +// Container-based version of the `std::distance()` function to +// return the number of elements within a container. +template +container_algorithm_internal::ContainerDifferenceType c_distance( + const C& c) { + return std::distance(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +//------------------------------------------------------------------------------ +// Non-modifying sequence operations +//------------------------------------------------------------------------------ + +// c_all_of() +// +// Container-based version of the `std::all_of()` function to +// test if all elements within a container satisfy a condition. +template +bool c_all_of(const C& c, Pred&& pred) { + return std::all_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_any_of() +// +// Container-based version of the `std::any_of()` function to +// test if any element in a container fulfills a condition. +template +bool c_any_of(const C& c, Pred&& pred) { + return std::any_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_none_of() +// +// Container-based version of the `std::none_of()` function to +// test if no elements in a container fulfill a condition. +template +bool c_none_of(const C& c, Pred&& pred) { + return std::none_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_for_each() +// +// Container-based version of the `std::for_each()` function to +// apply a function to a container's elements. +template +decay_t c_for_each(C&& c, Function&& f) { + return std::for_each(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(f)); +} + +// c_find() +// +// Container-based version of the `std::find()` function to find +// the first element containing the passed value within a container value. +template +container_algorithm_internal::ContainerIter c_find(C& c, T&& value) { + return std::find(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +// c_find_if() +// +// Container-based version of the `std::find_if()` function to find +// the first element in a container matching the given condition. +template +container_algorithm_internal::ContainerIter c_find_if(C& c, Pred&& pred) { + return std::find_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_find_if_not() +// +// Container-based version of the `std::find_if_not()` function to +// find the first element in a container not matching the given condition. +template +container_algorithm_internal::ContainerIter c_find_if_not(C& c, + Pred&& pred) { + return std::find_if_not(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_find_end() +// +// Container-based version of the `std::find_end()` function to +// find the last subsequence within a container. +template +container_algorithm_internal::ContainerIter c_find_end( + Sequence1& sequence, Sequence2& subsequence) { + return std::find_end(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence)); +} + +// Overload of c_find_end() for using a predicate evaluation other than `==` as +// the function's test condition. +template +container_algorithm_internal::ContainerIter c_find_end( + Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) { + return std::find_end(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence), + std::forward(pred)); +} + +// c_find_first_of() +// +// Container-based version of the `std::find_first_of()` function to +// find the first element within the container that is also within the options +// container. +template +container_algorithm_internal::ContainerIter c_find_first_of(C1& container, + C2& options) { + return std::find_first_of(container_algorithm_internal::c_begin(container), + container_algorithm_internal::c_end(container), + container_algorithm_internal::c_begin(options), + container_algorithm_internal::c_end(options)); +} + +// Overload of c_find_first_of() for using a predicate evaluation other than +// `==` as the function's test condition. +template +container_algorithm_internal::ContainerIter c_find_first_of( + C1& container, C2& options, BinaryPredicate&& pred) { + return std::find_first_of(container_algorithm_internal::c_begin(container), + container_algorithm_internal::c_end(container), + container_algorithm_internal::c_begin(options), + container_algorithm_internal::c_end(options), + std::forward(pred)); +} + +// c_adjacent_find() +// +// Container-based version of the `std::adjacent_find()` function to +// find equal adjacent elements within a container. +template +container_algorithm_internal::ContainerIter c_adjacent_find( + Sequence& sequence) { + return std::adjacent_find(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_adjacent_find() for using a predicate evaluation other than +// `==` as the function's test condition. +template +container_algorithm_internal::ContainerIter c_adjacent_find( + Sequence& sequence, BinaryPredicate&& pred) { + return std::adjacent_find(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(pred)); +} + +// c_count() +// +// Container-based version of the `std::count()` function to count +// values that match within a container. +template +container_algorithm_internal::ContainerDifferenceType c_count( + const C& c, T&& value) { + return std::count(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +// c_count_if() +// +// Container-based version of the `std::count_if()` function to +// count values matching a condition within a container. +template +container_algorithm_internal::ContainerDifferenceType c_count_if( + const C& c, Pred&& pred) { + return std::count_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_mismatch() +// +// Container-based version of the `std::mismatch()` function to +// return the first element where two ordered containers differ. Applies `==` to +// the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). +template +container_algorithm_internal::ContainerIterPairType +c_mismatch(C1& c1, C2& c2) { + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + // Negates equality because Cpp17EqualityComparable doesn't require clients + // to overload both `operator==` and `operator!=`. + if (!(*first1 == *first2)) { + break; + } + } + + return std::make_pair(first1, first2); +} + +// Overload of c_mismatch() for using a predicate evaluation other than `==` as +// the function's test condition. Applies `pred`to the first N elements of `c1` +// and `c2`, where N = min(size(c1), size(c2)). +template +container_algorithm_internal::ContainerIterPairType +c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) { + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + if (!pred(*first1, *first2)) { + break; + } + } + + return std::make_pair(first1, first2); +} + +// c_equal() +// +// Container-based version of the `std::equal()` function to +// test whether two containers are equal. +// +// NOTE: the semantics of c_equal() are slightly different than those of +// equal(): while the latter iterates over the second container only up to the +// size of the first container, c_equal() also checks whether the container +// sizes are equal. This better matches expectations about c_equal() based on +// its signature. +// +// Example: +// vector v1 = <1, 2, 3>; +// vector v2 = <1, 2, 3, 4>; +// equal(std::begin(v1), std::end(v1), std::begin(v2)) returns true +// c_equal(v1, v2) returns false + +template +bool c_equal(const C1& c1, const C2& c2) { + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && + std::equal(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2))); +} + +// Overload of c_equal() for using a predicate evaluation other than `==` as +// the function's test condition. +template +bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) { + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && + std::equal(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + std::forward(pred))); +} + +// c_is_permutation() +// +// Container-based version of the `std::is_permutation()` function +// to test whether a container is a permutation of another. +template +bool c_is_permutation(const C1& c1, const C2& c2) { + using std::begin; + using std::end; + return c1.size() == c2.size() && + std::is_permutation(begin(c1), end(c1), begin(c2)); +} + +// Overload of c_is_permutation() for using a predicate evaluation other than +// `==` as the function's test condition. +template +bool c_is_permutation(const C1& c1, const C2& c2, BinaryPredicate&& pred) { + using std::begin; + using std::end; + return c1.size() == c2.size() && + std::is_permutation(begin(c1), end(c1), begin(c2), + std::forward(pred)); +} + +// c_search() +// +// Container-based version of the `std::search()` function to search +// a container for a subsequence. +template +container_algorithm_internal::ContainerIter c_search( + Sequence1& sequence, Sequence2& subsequence) { + return std::search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence)); +} + +// Overload of c_search() for using a predicate evaluation other than +// `==` as the function's test condition. +template +container_algorithm_internal::ContainerIter c_search( + Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) { + return std::search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence), + std::forward(pred)); +} + +// c_search_n() +// +// Container-based version of the `std::search_n()` function to +// search a container for the first sequence of N elements. +template +container_algorithm_internal::ContainerIter c_search_n( + Sequence& sequence, Size count, T&& value) { + return std::search_n(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), count, + std::forward(value)); +} + +// Overload of c_search_n() for using a predicate evaluation other than +// `==` as the function's test condition. +template +container_algorithm_internal::ContainerIter c_search_n( + Sequence& sequence, Size count, T&& value, BinaryPredicate&& pred) { + return std::search_n(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), count, + std::forward(value), + std::forward(pred)); +} + +//------------------------------------------------------------------------------ +// Modifying sequence operations +//------------------------------------------------------------------------------ + +// c_copy() +// +// Container-based version of the `std::copy()` function to copy a +// container's elements into an iterator. +template +OutputIterator c_copy(const InputSequence& input, OutputIterator output) { + return std::copy(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output); +} + +// c_copy_n() +// +// Container-based version of the `std::copy_n()` function to copy a +// container's first N elements into an iterator. +template +OutputIterator c_copy_n(const C& input, Size n, OutputIterator output) { + return std::copy_n(container_algorithm_internal::c_begin(input), n, output); +} + +// c_copy_if() +// +// Container-based version of the `std::copy_if()` function to copy +// a container's elements satisfying some condition into an iterator. +template +OutputIterator c_copy_if(const InputSequence& input, OutputIterator output, + Pred&& pred) { + return std::copy_if(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output, + std::forward(pred)); +} + +// c_copy_backward() +// +// Container-based version of the `std::copy_backward()` function to +// copy a container's elements in reverse order into an iterator. +template +BidirectionalIterator c_copy_backward(const C& src, + BidirectionalIterator dest) { + return std::copy_backward(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_move() +// +// Container-based version of the `std::move()` function to move +// a container's elements into an iterator. +template +OutputIterator c_move(C&& src, OutputIterator dest) { + return std::move(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_move_backward() +// +// Container-based version of the `std::move_backward()` function to +// move a container's elements into an iterator in reverse order. +template +BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { + return std::move_backward(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_swap_ranges() +// +// Container-based version of the `std::swap_ranges()` function to +// swap a container's elements with another container's elements. Swaps the +// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). +template +container_algorithm_internal::ContainerIter c_swap_ranges(C1& c1, C2& c2) { + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + using std::swap; + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + swap(*first1, *first2); + } + return first2; +} + +// c_transform() +// +// Container-based version of the `std::transform()` function to +// transform a container's elements using the unary operation, storing the +// result in an iterator pointing to the last transformed element in the output +// range. +template +OutputIterator c_transform(const InputSequence& input, OutputIterator output, + UnaryOp&& unary_op) { + return std::transform(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output, + std::forward(unary_op)); +} + +// Overload of c_transform() for performing a transformation using a binary +// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`, +// where N = min(size(c1), size(c2)). +template +OutputIterator c_transform(const InputSequence1& input1, + const InputSequence2& input2, OutputIterator output, + BinaryOp&& binary_op) { + auto first1 = container_algorithm_internal::c_begin(input1); + auto last1 = container_algorithm_internal::c_end(input1); + auto first2 = container_algorithm_internal::c_begin(input2); + auto last2 = container_algorithm_internal::c_end(input2); + for (; first1 != last1 && first2 != last2; + ++first1, (void)++first2, ++output) { + *output = binary_op(*first1, *first2); + } + + return output; +} + +// c_replace() +// +// Container-based version of the `std::replace()` function to +// replace a container's elements of some value with a new value. The container +// is modified in place. +template +void c_replace(Sequence& sequence, const T& old_value, const T& new_value) { + std::replace(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), old_value, + new_value); +} + +// c_replace_if() +// +// Container-based version of the `std::replace_if()` function to +// replace a container's elements of some value with a new value based on some +// condition. The container is modified in place. +template +void c_replace_if(C& c, Pred&& pred, T&& new_value) { + std::replace_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred), std::forward(new_value)); +} + +// c_replace_copy() +// +// Container-based version of the `std::replace_copy()` function to +// replace a container's elements of some value with a new value and return the +// results within an iterator. +template +OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, + T&& new_value) { + return std::replace_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(old_value), + std::forward(new_value)); +} + +// c_replace_copy_if() +// +// Container-based version of the `std::replace_copy_if()` function +// to replace a container's elements of some value with a new value based on +// some condition, and return the results within an iterator. +template +OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, + T&& new_value) { + return std::replace_copy_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred), + std::forward(new_value)); +} + +// c_fill() +// +// Container-based version of the `std::fill()` function to fill a +// container with some value. +template +void c_fill(C& c, T&& value) { + std::fill(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), std::forward(value)); +} + +// c_fill_n() +// +// Container-based version of the `std::fill_n()` function to fill +// the first N elements in a container with some value. +template +void c_fill_n(C& c, Size n, T&& value) { + std::fill_n(container_algorithm_internal::c_begin(c), n, + std::forward(value)); +} + +// c_generate() +// +// Container-based version of the `std::generate()` function to +// assign a container's elements to the values provided by the given generator. +template +void c_generate(C& c, Generator&& gen) { + std::generate(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(gen)); +} + +// c_generate_n() +// +// Container-based version of the `std::generate_n()` function to +// assign a container's first N elements to the values provided by the given +// generator. +template +container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, + Generator&& gen) { + return std::generate_n(container_algorithm_internal::c_begin(c), n, + std::forward(gen)); +} + +// Note: `c_xx()` container versions for `remove()`, `remove_if()`, +// and `unique()` are omitted, because it's not clear whether or not such +// functions should call erase on their supplied sequences afterwards. Either +// behavior would be surprising for a different set of users. + +// c_remove_copy() +// +// Container-based version of the `std::remove_copy()` function to +// copy a container's elements while removing any elements matching the given +// `value`. +template +OutputIterator c_remove_copy(const C& c, OutputIterator result, T&& value) { + return std::remove_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(value)); +} + +// c_remove_copy_if() +// +// Container-based version of the `std::remove_copy_if()` function +// to copy a container's elements while removing any elements matching the given +// condition. +template +OutputIterator c_remove_copy_if(const C& c, OutputIterator result, + Pred&& pred) { + return std::remove_copy_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred)); +} + +// c_unique_copy() +// +// Container-based version of the `std::unique_copy()` function to +// copy a container's elements while removing any elements containing duplicate +// values. +template +OutputIterator c_unique_copy(const C& c, OutputIterator result) { + return std::unique_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result); +} + +// Overload of c_unique_copy() for using a predicate evaluation other than +// `==` for comparing uniqueness of the element values. +template +OutputIterator c_unique_copy(const C& c, OutputIterator result, + BinaryPredicate&& pred) { + return std::unique_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred)); +} + +// c_reverse() +// +// Container-based version of the `std::reverse()` function to +// reverse a container's elements. +template +void c_reverse(Sequence& sequence) { + std::reverse(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// c_reverse_copy() +// +// Container-based version of the `std::reverse()` function to +// reverse a container's elements and write them to an iterator range. +template +OutputIterator c_reverse_copy(const C& sequence, OutputIterator result) { + return std::reverse_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + result); +} + +// c_rotate() +// +// Container-based version of the `std::rotate()` function to +// shift a container's elements leftward such that the `middle` element becomes +// the first element in the container. +template > +Iterator c_rotate(C& sequence, Iterator middle) { + return absl::rotate(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence)); +} + +// c_rotate_copy() +// +// Container-based version of the `std::rotate_copy()` function to +// shift a container's elements leftward such that the `middle` element becomes +// the first element in a new iterator range. +template +OutputIterator c_rotate_copy( + const C& sequence, + container_algorithm_internal::ContainerIter middle, + OutputIterator result) { + return std::rotate_copy(container_algorithm_internal::c_begin(sequence), + middle, container_algorithm_internal::c_end(sequence), + result); +} + +// c_shuffle() +// +// Container-based version of the `std::shuffle()` function to +// randomly shuffle elements within the container using a `gen()` uniform random +// number generator. +template +void c_shuffle(RandomAccessContainer& c, UniformRandomBitGenerator&& gen) { + std::shuffle(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(gen)); +} + +//------------------------------------------------------------------------------ +// Partition functions +//------------------------------------------------------------------------------ + +// c_is_partitioned() +// +// Container-based version of the `std::is_partitioned()` function +// to test whether all elements in the container for which `pred` returns `true` +// precede those for which `pred` is `false`. +template +bool c_is_partitioned(const C& c, Pred&& pred) { + return std::is_partitioned(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_partition() +// +// Container-based version of the `std::partition()` function +// to rearrange all elements in a container in such a way that all elements for +// which `pred` returns `true` precede all those for which it returns `false`, +// returning an iterator to the first element of the second group. +template +container_algorithm_internal::ContainerIter c_partition(C& c, Pred&& pred) { + return std::partition(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_stable_partition() +// +// Container-based version of the `std::stable_partition()` function +// to rearrange all elements in a container in such a way that all elements for +// which `pred` returns `true` precede all those for which it returns `false`, +// preserving the relative ordering between the two groups. The function returns +// an iterator to the first element of the second group. +template +container_algorithm_internal::ContainerIter c_stable_partition(C& c, + Pred&& pred) { + return std::stable_partition(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_partition_copy() +// +// Container-based version of the `std::partition_copy()` function +// to partition a container's elements and return them into two iterators: one +// for which `pred` returns `true`, and one for which `pred` returns `false.` + +template +std::pair c_partition_copy( + const C& c, OutputIterator1 out_true, OutputIterator2 out_false, + Pred&& pred) { + return std::partition_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), out_true, + out_false, std::forward(pred)); +} + +// c_partition_point() +// +// Container-based version of the `std::partition_point()` function +// to return the first element of an already partitioned container for which +// the given `pred` is not `true`. +template +container_algorithm_internal::ContainerIter c_partition_point(C& c, + Pred&& pred) { + return std::partition_point(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +//------------------------------------------------------------------------------ +// Sorting functions +//------------------------------------------------------------------------------ + +// c_sort() +// +// Container-based version of the `std::sort()` function +// to sort elements in ascending order of their values. +template +void c_sort(C& c) { + std::sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_sort() for performing a `comp` comparison other than the +// default `operator<`. +template +void c_sort(C& c, LessThan&& comp) { + std::sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_stable_sort() +// +// Container-based version of the `std::stable_sort()` function +// to sort elements in ascending order of their values, preserving the order +// of equivalents. +template +void c_stable_sort(C& c) { + std::stable_sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_stable_sort() for performing a `comp` comparison other than the +// default `operator<`. +template +void c_stable_sort(C& c, LessThan&& comp) { + std::stable_sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_is_sorted() +// +// Container-based version of the `std::is_sorted()` function +// to evaluate whether the given container is sorted in ascending order. +template +bool c_is_sorted(const C& c) { + return std::is_sorted(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// c_is_sorted() overload for performing a `comp` comparison other than the +// default `operator<`. +template +bool c_is_sorted(const C& c, LessThan&& comp) { + return std::is_sorted(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_partial_sort() +// +// Container-based version of the `std::partial_sort()` function +// to rearrange elements within a container such that elements before `middle` +// are sorted in ascending order. +template +void c_partial_sort( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter middle) { + std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_partial_sort() for performing a `comp` comparison other than +// the default `operator<`. +template +void c_partial_sort( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter middle, + LessThan&& comp) { + std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_partial_sort_copy() +// +// Container-based version of the `std::partial_sort_copy()` +// function to sort the elements in the given range `result` within the larger +// `sequence` in ascending order (and using `result` as the output parameter). +// At most min(result.last - result.first, sequence.last - sequence.first) +// elements from the sequence will be stored in the result. +template +container_algorithm_internal::ContainerIter +c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { + return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(result), + container_algorithm_internal::c_end(result)); +} + +// Overload of c_partial_sort_copy() for performing a `comp` comparison other +// than the default `operator<`. +template +container_algorithm_internal::ContainerIter +c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, + LessThan&& comp) { + return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(result), + container_algorithm_internal::c_end(result), + std::forward(comp)); +} + +// c_is_sorted_until() +// +// Container-based version of the `std::is_sorted_until()` function +// to return the first element within a container that is not sorted in +// ascending order as an iterator. +template +container_algorithm_internal::ContainerIter c_is_sorted_until(C& c) { + return std::is_sorted_until(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_is_sorted_until() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_is_sorted_until( + C& c, LessThan&& comp) { + return std::is_sorted_until(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_nth_element() +// +// Container-based version of the `std::nth_element()` function +// to rearrange the elements within a container such that the `nth` element +// would be in that position in an ordered sequence; other elements may be in +// any order, except that all preceding `nth` will be less than that element, +// and all following `nth` will be greater than that element. +template +void c_nth_element( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter nth) { + std::nth_element(container_algorithm_internal::c_begin(sequence), nth, + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_nth_element() for performing a `comp` comparison other than +// the default `operator<`. +template +void c_nth_element( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter nth, + LessThan&& comp) { + std::nth_element(container_algorithm_internal::c_begin(sequence), nth, + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Binary Search +//------------------------------------------------------------------------------ + +// c_lower_bound() +// +// Container-based version of the `std::lower_bound()` function +// to return an iterator pointing to the first element in a sorted container +// which does not compare less than `value`. +template +container_algorithm_internal::ContainerIter c_lower_bound( + Sequence& sequence, T&& value) { + return std::lower_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} + +// Overload of c_lower_bound() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_lower_bound( + Sequence& sequence, T&& value, LessThan&& comp) { + return std::lower_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value), std::forward(comp)); +} + +// c_upper_bound() +// +// Container-based version of the `std::upper_bound()` function +// to return an iterator pointing to the first element in a sorted container +// which is greater than `value`. +template +container_algorithm_internal::ContainerIter c_upper_bound( + Sequence& sequence, T&& value) { + return std::upper_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} + +// Overload of c_upper_bound() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_upper_bound( + Sequence& sequence, T&& value, LessThan&& comp) { + return std::upper_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value), std::forward(comp)); +} + +// c_equal_range() +// +// Container-based version of the `std::equal_range()` function +// to return an iterator pair pointing to the first and last elements in a +// sorted container which compare equal to `value`. +template +container_algorithm_internal::ContainerIterPairType +c_equal_range(Sequence& sequence, T&& value) { + return std::equal_range(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} + +// Overload of c_equal_range() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIterPairType +c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) { + return std::equal_range(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value), std::forward(comp)); +} + +// c_binary_search() +// +// Container-based version of the `std::binary_search()` function +// to test if any element in the sorted container contains a value equivalent to +// 'value'. +template +bool c_binary_search(Sequence&& sequence, T&& value) { + return std::binary_search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} + +// Overload of c_binary_search() for performing a `comp` comparison other than +// the default `operator<`. +template +bool c_binary_search(Sequence&& sequence, T&& value, LessThan&& comp) { + return std::binary_search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Merge functions +//------------------------------------------------------------------------------ + +// c_merge() +// +// Container-based version of the `std::merge()` function +// to merge two sorted containers into a single sorted iterator. +template +OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { + return std::merge(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), result); +} + +// Overload of c_merge() for performing a `comp` comparison other than +// the default `operator<`. +template +OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, + LessThan&& comp) { + return std::merge(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), result, + std::forward(comp)); +} + +// c_inplace_merge() +// +// Container-based version of the `std::inplace_merge()` function +// to merge a supplied iterator `middle` into a container. +template +void c_inplace_merge(C& c, + container_algorithm_internal::ContainerIter middle) { + std::inplace_merge(container_algorithm_internal::c_begin(c), middle, + container_algorithm_internal::c_end(c)); +} + +// Overload of c_inplace_merge() for performing a merge using a `comp` other +// than `operator<`. +template +void c_inplace_merge(C& c, + container_algorithm_internal::ContainerIter middle, + LessThan&& comp) { + std::inplace_merge(container_algorithm_internal::c_begin(c), middle, + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_includes() +// +// Container-based version of the `std::includes()` function +// to test whether a sorted container `c1` entirely contains another sorted +// container `c2`. +template +bool c_includes(const C1& c1, const C2& c2) { + return std::includes(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); +} + +// Overload of c_includes() for performing a merge using a `comp` other than +// `operator<`. +template +bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) { + return std::includes(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), + std::forward(comp)); +} + +// c_set_union() +// +// Container-based version of the `std::set_union()` function +// to return an iterator containing the union of two containers; duplicate +// values are not copied into the output. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::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), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_union() for performing a merge using a `comp` other than +// `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, + LessThan&& comp) { + return std::set_union(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_intersection() +// +// Container-based version of the `std::set_intersection()` function +// to return an iterator containing the intersection of two sorted containers. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_intersection(const C1& c1, const C2& c2, + OutputIterator output) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using operator<. + assert(absl::c_is_sorted(c1)); + assert(absl::c_is_sorted(c2)); + return std::set_intersection(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_intersection() for performing a merge using a `comp` other +// than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_intersection(const C1& c1, const C2& c2, + OutputIterator output, LessThan&& comp) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using the same comparator. + assert(absl::c_is_sorted(c1, comp)); + assert(absl::c_is_sorted(c2, comp)); + return std::set_intersection(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_difference() +// +// Container-based version of the `std::set_difference()` function +// to return an iterator containing elements present in the first container but +// not in the second. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::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), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_difference() for performing a merge using a `comp` other +// than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_difference(const C1& c1, const C2& c2, + OutputIterator output, LessThan&& comp) { + return std::set_difference(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_symmetric_difference() +// +// Container-based version of the `std::set_symmetric_difference()` +// function to return an iterator containing elements present in either one +// container or the other, but not both. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, + OutputIterator output) { + return std::set_symmetric_difference( + container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_symmetric_difference() for performing a merge using a +// `comp` other than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, + OutputIterator output, + LessThan&& comp) { + return std::set_symmetric_difference( + container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Heap functions +//------------------------------------------------------------------------------ + +// c_push_heap() +// +// Container-based version of the `std::push_heap()` function +// to push a value onto a container heap. +template +void c_push_heap(RandomAccessContainer& sequence) { + std::push_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_push_heap() for performing a push operation on a heap using a +// `comp` other than `operator<`. +template +void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) { + std::push_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_pop_heap() +// +// Container-based version of the `std::pop_heap()` function +// to pop a value from a heap container. +template +void c_pop_heap(RandomAccessContainer& sequence) { + std::pop_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_pop_heap() for performing a pop operation on a heap using a +// `comp` other than `operator<`. +template +void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) { + std::pop_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_make_heap() +// +// Container-based version of the `std::make_heap()` function +// to make a container a heap. +template +void c_make_heap(RandomAccessContainer& sequence) { + std::make_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_make_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) { + std::make_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_sort_heap() +// +// Container-based version of the `std::sort_heap()` function +// to sort a heap into ascending order (after which it is no longer a heap). +template +void c_sort_heap(RandomAccessContainer& sequence) { + std::sort_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_sort_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) { + std::sort_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_is_heap() +// +// Container-based version of the `std::is_heap()` function +// to check whether the given container is a heap. +template +bool c_is_heap(const RandomAccessContainer& sequence) { + return std::is_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_is_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) { + return std::is_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_is_heap_until() +// +// Container-based version of the `std::is_heap_until()` function +// to find the first element in a given container which is not in heap order. +template +container_algorithm_internal::ContainerIter +c_is_heap_until(RandomAccessContainer& sequence) { + return std::is_heap_until(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_is_heap_until() for performing heap comparisons using a +// `comp` other than `operator<` +template +container_algorithm_internal::ContainerIter +c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) { + return std::is_heap_until(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Min/max +//------------------------------------------------------------------------------ + +// c_min_element() +// +// Container-based version of the `std::min_element()` function +// to return an iterator pointing to the element with the smallest value, using +// `operator<` to make the comparisons. +template +container_algorithm_internal::ContainerIter c_min_element( + Sequence& sequence) { + return std::min_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_min_element() for performing a `comp` comparison other than +// `operator<`. +template +container_algorithm_internal::ContainerIter c_min_element( + Sequence& sequence, LessThan&& comp) { + return std::min_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_max_element() +// +// Container-based version of the `std::max_element()` function +// to return an iterator pointing to the element with the largest value, using +// `operator<` to make the comparisons. +template +container_algorithm_internal::ContainerIter c_max_element( + Sequence& sequence) { + return std::max_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_max_element() for performing a `comp` comparison other than +// `operator<`. +template +container_algorithm_internal::ContainerIter c_max_element( + Sequence& sequence, LessThan&& comp) { + return std::max_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_minmax_element() +// +// Container-based version of the `std::minmax_element()` function +// to return a pair of iterators pointing to the elements containing the +// smallest and largest values, respectively, using `operator<` to make the +// comparisons. +template +container_algorithm_internal::ContainerIterPairType +c_minmax_element(C& c) { + return std::minmax_element(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_minmax_element() for performing `comp` comparisons other than +// `operator<`. +template +container_algorithm_internal::ContainerIterPairType +c_minmax_element(C& c, LessThan&& comp) { + return std::minmax_element(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Lexicographical Comparisons +//------------------------------------------------------------------------------ + +// c_lexicographical_compare() +// +// Container-based version of the `std::lexicographical_compare()` +// function to lexicographically compare (e.g. sort words alphabetically) two +// container sequences. The comparison is performed using `operator<`. Note +// that capital letters ("A-Z") have ASCII values less than lowercase letters +// ("a-z"). +template +bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { + return std::lexicographical_compare( + container_algorithm_internal::c_begin(sequence1), + container_algorithm_internal::c_end(sequence1), + container_algorithm_internal::c_begin(sequence2), + container_algorithm_internal::c_end(sequence2)); +} + +// Overload of c_lexicographical_compare() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, + LessThan&& comp) { + return std::lexicographical_compare( + container_algorithm_internal::c_begin(sequence1), + container_algorithm_internal::c_end(sequence1), + container_algorithm_internal::c_begin(sequence2), + container_algorithm_internal::c_end(sequence2), + std::forward(comp)); +} + +// c_next_permutation() +// +// Container-based version of the `std::next_permutation()` function +// to rearrange a container's elements into the next lexicographically greater +// permutation. +template +bool c_next_permutation(C& c) { + return std::next_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_next_permutation() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_next_permutation(C& c, LessThan&& comp) { + return std::next_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_prev_permutation() +// +// Container-based version of the `std::prev_permutation()` function +// to rearrange a container's elements into the next lexicographically lesser +// permutation. +template +bool c_prev_permutation(C& c) { + return std::prev_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_prev_permutation() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_prev_permutation(C& c, LessThan&& comp) { + return std::prev_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// algorithms +//------------------------------------------------------------------------------ + +// c_iota() +// +// Container-based version of the `std::iota()` function +// to compute successive values of `value`, as if incremented with `++value` +// after each element is written. and write them to the container. +template +void c_iota(Sequence& sequence, T&& value) { + std::iota(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} +// c_accumulate() +// +// Container-based version of the `std::accumulate()` function +// to accumulate the element values of a container to `init` and return that +// accumulation by value. +// +// Note: Due to a language technicality this function has return type +// absl::decay_t. As a user of this function you can casually read +// this as "returns T by value" and assume it does the right thing. +template +decay_t c_accumulate(const Sequence& sequence, T&& init) { + return std::accumulate(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(init)); +} + +// Overload of c_accumulate() for using a binary operations other than +// addition for computing the accumulation. +template +decay_t c_accumulate(const Sequence& sequence, T&& init, + BinaryOp&& binary_op) { + return std::accumulate(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(init), + std::forward(binary_op)); +} + +// c_inner_product() +// +// Container-based version of the `std::inner_product()` function +// to compute the cumulative inner product of container element pairs. +// +// Note: Due to a language technicality this function has return type +// absl::decay_t. As a user of this function you can casually read +// this as "returns T by value" and assume it does the right thing. +template +decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, + T&& sum) { + return std::inner_product(container_algorithm_internal::c_begin(factors1), + container_algorithm_internal::c_end(factors1), + container_algorithm_internal::c_begin(factors2), + std::forward(sum)); +} + +// Overload of c_inner_product() for using binary operations other than +// `operator+` (for computing the accumulation) and `operator*` (for computing +// the product between the two container's element pair). +template +decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, + T&& sum, BinaryOp1&& op1, BinaryOp2&& op2) { + return std::inner_product(container_algorithm_internal::c_begin(factors1), + container_algorithm_internal::c_end(factors1), + container_algorithm_internal::c_begin(factors2), + std::forward(sum), std::forward(op1), + std::forward(op2)); +} + +// c_adjacent_difference() +// +// Container-based version of the `std::adjacent_difference()` +// function to compute the difference between each element and the one preceding +// it and write it to an iterator. +template +OutputIt c_adjacent_difference(const InputSequence& input, + OutputIt output_first) { + return std::adjacent_difference(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first); +} + +// Overload of c_adjacent_difference() for using a binary operation other than +// subtraction to compute the adjacent difference. +template +OutputIt c_adjacent_difference(const InputSequence& input, + OutputIt output_first, BinaryOp&& op) { + return std::adjacent_difference(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first, std::forward(op)); +} + +// c_partial_sum() +// +// Container-based version of the `std::partial_sum()` function +// to compute the partial sum of the elements in a sequence and write them +// to an iterator. The partial sum is the sum of all element values so far in +// the sequence. +template +OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first) { + return std::partial_sum(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first); +} + +// Overload of c_partial_sum() for using a binary operation other than addition +// to compute the "partial sum". +template +OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, + BinaryOp&& op) { + return std::partial_sum(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first, std::forward(op)); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/src/absl/base/attributes.h b/src/absl/base/attributes.h new file mode 100644 index 0000000..cf611c6 --- /dev/null +++ b/src/absl/base/attributes.h @@ -0,0 +1,764 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This header file defines macros for declaring attributes for functions, +// types, and variables. +// +// These macros are used within Abseil and allow the compiler to optimize, where +// applicable, certain function calls. +// +// Most macros here are exposing GCC or Clang features, and are stubbed out for +// other compilers. +// +// GCC attributes documentation: +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html +// +// Most attributes in this file are already supported by GCC 4.7. However, some +// of them are not supported in older version of Clang. Thus, we check +// `__has_attribute()` first. If the check fails, we check if we are on GCC and +// assume the attribute exists on GCC (which is verified on GCC 4.7). + +#ifndef ABSL_BASE_ATTRIBUTES_H_ +#define ABSL_BASE_ATTRIBUTES_H_ + +#include "absl/base/config.h" + +// ABSL_HAVE_ATTRIBUTE +// +// A function-like feature checking macro that is a wrapper around +// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a +// nonzero constant integer if the attribute is supported or 0 if not. +// +// It evaluates to zero if `__has_attribute` is not defined by the compiler. +// +// GCC: https://gcc.gnu.org/gcc-5/changes.html +// Clang: https://clang.llvm.org/docs/LanguageExtensions.html +#ifdef __has_attribute +#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) +#else +#define ABSL_HAVE_ATTRIBUTE(x) 0 +#endif + +// ABSL_HAVE_CPP_ATTRIBUTE +// +// A function-like feature checking macro that accepts C++11 style attributes. +// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 +// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't +// find `__has_cpp_attribute`, will evaluate to 0. +#if defined(__cplusplus) && defined(__has_cpp_attribute) +// NOTE: requiring __cplusplus above should not be necessary, but +// works around https://bugs.llvm.org/show_bug.cgi?id=23435. +#define ABSL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define ABSL_HAVE_CPP_ATTRIBUTE(x) 0 +#endif + +// ----------------------------------------------------------------------------- +// Function Attributes +// ----------------------------------------------------------------------------- +// +// GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html +// Clang: https://clang.llvm.org/docs/AttributeReference.html + +// ABSL_PRINTF_ATTRIBUTE +// ABSL_SCANF_ATTRIBUTE +// +// Tells the compiler to perform `printf` format string checking if the +// compiler supports it; see the 'format' attribute in +// . +// +// Note: As the GCC manual states, "[s]ince non-static C++ methods +// have an implicit 'this' argument, the arguments of such methods +// should be counted from two, not one." +#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) +#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(__scanf__, string_index, first_to_check))) +#else +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) +#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) +#endif + +// ABSL_ATTRIBUTE_ALWAYS_INLINE +// ABSL_ATTRIBUTE_NOINLINE +// +// Forces functions to either inline or not inline. Introduced in gcc 3.1. +#if ABSL_HAVE_ATTRIBUTE(always_inline) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) +#define ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE 1 +#else +#define ABSL_ATTRIBUTE_ALWAYS_INLINE +#endif + +#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#define ABSL_HAVE_ATTRIBUTE_NOINLINE 1 +#else +#define ABSL_ATTRIBUTE_NOINLINE +#endif + +// ABSL_ATTRIBUTE_NO_TAIL_CALL +// +// Prevents the compiler from optimizing away stack frames for functions which +// end in a call to another function. +#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls) +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 +#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls)) +#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__) +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 +#define ABSL_ATTRIBUTE_NO_TAIL_CALL \ + __attribute__((optimize("no-optimize-sibling-calls"))) +#else +#define ABSL_ATTRIBUTE_NO_TAIL_CALL +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0 +#endif + +// ABSL_ATTRIBUTE_WEAK +// +// Tags a function as weak for the purposes of compilation and linking. +// Weak attributes did not work properly in LLVM's Windows backend before +// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// for further information. +// The MinGW compiler doesn't complain about the weak attribute until the link +// step, presumably because Windows doesn't use ELF binaries. +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + (!defined(_WIN32) || (defined(__clang__) && __clang_major__ >= 9)) && \ + !defined(__MINGW32__) +#undef ABSL_ATTRIBUTE_WEAK +#define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) +#define ABSL_HAVE_ATTRIBUTE_WEAK 1 +#else +#define ABSL_ATTRIBUTE_WEAK +#define ABSL_HAVE_ATTRIBUTE_WEAK 0 +#endif + +// ABSL_ATTRIBUTE_NONNULL +// +// Tells the compiler either (a) that a particular function parameter +// should be a non-null pointer, or (b) that all pointer arguments should +// be non-null. +// +// Note: As the GCC manual states, "[s]ince non-static C++ methods +// have an implicit 'this' argument, the arguments of such methods +// should be counted from two, not one." +// +// Args are indexed starting at 1. +// +// For non-static class member functions, the implicit `this` argument +// is arg 1, and the first explicit argument is arg 2. For static class member +// functions, there is no implicit `this`, and the first explicit argument is +// arg 1. +// +// Example: +// +// /* arg_a cannot be null, but arg_b can */ +// void Function(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(1); +// +// class C { +// /* arg_a cannot be null, but arg_b can */ +// void Method(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(2); +// +// /* arg_a cannot be null, but arg_b can */ +// static void StaticMethod(void* arg_a, void* arg_b) +// ABSL_ATTRIBUTE_NONNULL(1); +// }; +// +// If no arguments are provided, then all pointer arguments should be non-null. +// +// /* No pointer arguments may be null. */ +// void Function(void* arg_a, void* arg_b, int arg_c) ABSL_ATTRIBUTE_NONNULL(); +// +// NOTE: The GCC nonnull attribute actually accepts a list of arguments, but +// ABSL_ATTRIBUTE_NONNULL does not. +#if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index))) +#else +#define ABSL_ATTRIBUTE_NONNULL(...) +#endif + +// ABSL_ATTRIBUTE_NORETURN +// +// Tells the compiler that a given function never returns. +#if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define ABSL_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define ABSL_ATTRIBUTE_NORETURN +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS +// +// Tells the AddressSanitizer (or other memory testing tools) to ignore a given +// function. Useful for cases when a function reads random locations on stack, +// calls _exit from a cloned subprocess, deliberately accesses buffer +// out of bounds or does other scary things with memory. +// NOTE: GCC supports AddressSanitizer(asan) since 4.8. +// https://gcc.gnu.org/gcc-4.8/changes.html +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_address) +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#elif defined(_MSC_VER) && _MSC_VER >= 1928 +// https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY +// +// Tells the MemorySanitizer to relax the handling of a given function. All "Use +// of uninitialized value" warnings from such functions will be suppressed, and +// all values loaded from memory will be considered fully initialized. This +// attribute is similar to the ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS attribute +// above, but deals with initialized-ness rather than addressability issues. +// NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_memory) +#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_THREAD +// +// Tells the ThreadSanitizer to not instrument a given function. +// NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. +// https://gcc.gnu.org/gcc-4.8/changes.html +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_thread) +#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED +// +// Tells the UndefinedSanitizer to ignore a given function. Useful for cases +// where certain behavior (eg. division by zero) is being used intentionally. +// NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9. +// https://gcc.gnu.org/gcc-4.9/changes.html +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_undefined) +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ + __attribute__((no_sanitize_undefined)) +#elif ABSL_HAVE_ATTRIBUTE(no_sanitize) +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ + __attribute__((no_sanitize("undefined"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_CFI +// +// Tells the ControlFlowIntegrity sanitizer to not instrument a given function. +// See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details. +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) +#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +// +// Tells the SafeStack to not instrument a given function. +// See https://clang.llvm.org/docs/SafeStack.html for details. +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ + __attribute__((no_sanitize("safe-stack"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +#endif + +// ABSL_ATTRIBUTE_RETURNS_NONNULL +// +// Tells the compiler that a particular function never returns a null pointer. +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) +#define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) +#else +#define ABSL_ATTRIBUTE_RETURNS_NONNULL +#endif + +// ABSL_HAVE_ATTRIBUTE_SECTION +// +// Indicates whether labeled sections are supported. Weak symbol support is +// a prerequisite. Labeled sections are not supported on Darwin/iOS. +#ifdef ABSL_HAVE_ATTRIBUTE_SECTION +#error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set +#elif (ABSL_HAVE_ATTRIBUTE(section) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK +#define ABSL_HAVE_ATTRIBUTE_SECTION 1 + +// ABSL_ATTRIBUTE_SECTION +// +// Tells the compiler/linker to put a given function into a section and define +// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. +// This functionality is supported by GNU linker. Any function annotated with +// `ABSL_ATTRIBUTE_SECTION` must not be inlined, or it will be placed into +// whatever section its caller is placed into. +// +#ifndef ABSL_ATTRIBUTE_SECTION +#define ABSL_ATTRIBUTE_SECTION(name) \ + __attribute__((section(#name))) __attribute__((noinline)) +#endif + +// ABSL_ATTRIBUTE_SECTION_VARIABLE +// +// Tells the compiler/linker to put a given variable into a section and define +// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. +// This functionality is supported by GNU linker. +#ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE +#ifdef _AIX +// __attribute__((section(#name))) on AIX is achived by using the `.csect` psudo +// op which includes an additional integer as part of its syntax indcating +// alignment. If data fall under different alignments then you might get a +// compilation error indicating a `Section type conflict`. +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#else +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) +#endif +#endif + +// ABSL_DECLARE_ATTRIBUTE_SECTION_VARS +// +// A weak section declaration to be used as a global declaration +// for ABSL_ATTRIBUTE_SECTION_START|STOP(name) to compile and link +// even without functions with ABSL_ATTRIBUTE_SECTION(name). +// ABSL_DEFINE_ATTRIBUTE_SECTION should be in the exactly one file; it's +// a no-op on ELF but not on Mach-O. +// +#ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ + extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ + extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK +#endif +#ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS +#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name) +#endif + +// ABSL_ATTRIBUTE_SECTION_START +// +// Returns `void*` pointers to start/end of a section of code with +// functions having ABSL_ATTRIBUTE_SECTION(name). +// Returns 0 if no such functions exist. +// One must ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) for this to compile and +// link. +// +#define ABSL_ATTRIBUTE_SECTION_START(name) \ + (reinterpret_cast(__start_##name)) +#define ABSL_ATTRIBUTE_SECTION_STOP(name) \ + (reinterpret_cast(__stop_##name)) + +#else // !ABSL_HAVE_ATTRIBUTE_SECTION + +#define ABSL_HAVE_ATTRIBUTE_SECTION 0 + +// provide dummy definitions +#define ABSL_ATTRIBUTE_SECTION(name) +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast(0)) +#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast(0)) + +#endif // ABSL_ATTRIBUTE_SECTION + +// ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +// +// Support for aligning the stack on 32-bit x86. +#if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || \ + (defined(__GNUC__) && !defined(__clang__)) +#if defined(__i386__) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \ + __attribute__((force_align_arg_pointer)) +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#elif defined(__x86_64__) +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (1) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#else // !__i386__ && !__x86_64 +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#endif // __i386__ +#else +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#endif + +// ABSL_MUST_USE_RESULT +// +// Tells the compiler to warn about unused results. +// +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard `[[nodiscard]]` directly over this macro. +// +// When annotating a function, it must appear as the first part of the +// declaration or definition. The compiler will warn if the return value from +// such a function is unused: +// +// ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket(); +// AllocateSprocket(); // Triggers a warning. +// +// When annotating a class, it is equivalent to annotating every function which +// returns an instance. +// +// class ABSL_MUST_USE_RESULT Sprocket {}; +// Sprocket(); // Triggers a warning. +// +// Sprocket MakeSprocket(); +// MakeSprocket(); // Triggers a warning. +// +// Note that references and pointers are not instances: +// +// Sprocket* SprocketPointer(); +// SprocketPointer(); // Does *not* trigger a warning. +// +// ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result +// warning. For that, warn_unused_result is used only for clang but not for gcc. +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 +// +// Note: past advice was to place the macro after the argument list. +// +// TODO(b/176172494): Use ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) when all code is +// compliant with the stricter [[nodiscard]]. +#if defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) +#define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define ABSL_MUST_USE_RESULT +#endif + +// ABSL_ATTRIBUTE_HOT, ABSL_ATTRIBUTE_COLD +// +// Tells GCC that a function is hot or cold. GCC can use this information to +// improve static analysis, i.e. a conditional branch to a cold function +// is likely to be not-taken. +// This annotation is used for function declarations. +// +// Example: +// +// int foo() ABSL_ATTRIBUTE_HOT; +#if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_HOT __attribute__((hot)) +#else +#define ABSL_ATTRIBUTE_HOT +#endif + +#if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_COLD __attribute__((cold)) +#else +#define ABSL_ATTRIBUTE_COLD +#endif + +// ABSL_XRAY_ALWAYS_INSTRUMENT, ABSL_XRAY_NEVER_INSTRUMENT, ABSL_XRAY_LOG_ARGS +// +// We define the ABSL_XRAY_ALWAYS_INSTRUMENT and ABSL_XRAY_NEVER_INSTRUMENT +// macro used as an attribute to mark functions that must always or never be +// instrumented by XRay. Currently, this is only supported in Clang/LLVM. +// +// For reference on the LLVM XRay instrumentation, see +// http://llvm.org/docs/XRay.html. +// +// A function with the XRAY_ALWAYS_INSTRUMENT macro attribute in its declaration +// will always get the XRay instrumentation sleds. These sleds may introduce +// some binary size and runtime overhead and must be used sparingly. +// +// These attributes only take effect when the following conditions are met: +// +// * The file/target is built in at least C++11 mode, with a Clang compiler +// that supports XRay attributes. +// * The file/target is built with the -fxray-instrument flag set for the +// Clang/LLVM compiler. +// * The function is defined in the translation unit (the compiler honors the +// attribute in either the definition or the declaration, and must match). +// +// There are cases when, even when building with XRay instrumentation, users +// might want to control specifically which functions are instrumented for a +// particular build using special-case lists provided to the compiler. These +// special case lists are provided to Clang via the +// -fxray-always-instrument=... and -fxray-never-instrument=... flags. The +// attributes in source take precedence over these special-case lists. +// +// To disable the XRay attributes at build-time, users may define +// ABSL_NO_XRAY_ATTRIBUTES. Do NOT define ABSL_NO_XRAY_ATTRIBUTES on specific +// packages/targets, as this may lead to conflicting definitions of functions at +// link-time. +// +// XRay isn't currently supported on Android: +// https://github.com/android/ndk/issues/368 +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \ + !defined(ABSL_NO_XRAY_ATTRIBUTES) && !defined(__ANDROID__) +#define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]] +#define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) +#define ABSL_XRAY_LOG_ARGS(N) \ + [[clang::xray_always_instrument, clang::xray_log_args(N)]] +#else +#define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]] +#endif +#else +#define ABSL_XRAY_ALWAYS_INSTRUMENT +#define ABSL_XRAY_NEVER_INSTRUMENT +#define ABSL_XRAY_LOG_ARGS(N) +#endif + +// ABSL_ATTRIBUTE_REINITIALIZES +// +// Indicates that a member function reinitializes the entire object to a known +// state, independent of the previous state of the object. +// +// The clang-tidy check bugprone-use-after-move allows member functions marked +// with this attribute to be called on objects that have been moved from; +// without the attribute, this would result in a use-after-move warning. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::reinitializes) +#define ABSL_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] +#else +#define ABSL_ATTRIBUTE_REINITIALIZES +#endif + +// ----------------------------------------------------------------------------- +// Variable Attributes +// ----------------------------------------------------------------------------- + +// ABSL_ATTRIBUTE_UNUSED +// +// Prevents the compiler from complaining about variables that appear unused. +// +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard '[[maybe_unused]]' directly over this macro. +// +// Due to differences in positioning requirements between the old, compiler +// specific __attribute__ syntax and the now standard [[maybe_unused]], this +// macro does not attempt to take advantage of '[[maybe_unused]]'. +#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) +#undef ABSL_ATTRIBUTE_UNUSED +#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) +#else +#define ABSL_ATTRIBUTE_UNUSED +#endif + +// ABSL_ATTRIBUTE_INITIAL_EXEC +// +// Tells the compiler to use "initial-exec" mode for a thread-local variable. +// See http://people.redhat.com/drepper/tls.pdf for the gory details. +#if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec"))) +#else +#define ABSL_ATTRIBUTE_INITIAL_EXEC +#endif + +// ABSL_ATTRIBUTE_PACKED +// +// Instructs the compiler not to use natural alignment for a tagged data +// structure, but instead to reduce its alignment to 1. +// +// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing +// so can cause atomic variables to be mis-aligned and silently violate +// atomicity on x86. +// +// This attribute can either be applied to members of a structure or to a +// structure in its entirety. Applying this attribute (judiciously) to a +// structure in its entirety to optimize the memory footprint of very +// commonly-used structs is fine. Do not apply this attribute to a structure in +// its entirety if the purpose is to control the offsets of the members in the +// structure. Instead, apply this attribute only to structure members that need +// it. +// +// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the +// natural alignment of structure members not annotated is preserved. Aligned +// member accesses are faster than non-aligned member accesses even if the +// targeted microprocessor supports non-aligned accesses. +#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__)) +#else +#define ABSL_ATTRIBUTE_PACKED +#endif + +// ABSL_ATTRIBUTE_FUNC_ALIGN +// +// Tells the compiler to align the function start at least to certain +// alignment boundary +#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes))) +#else +#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) +#endif + +// ABSL_FALLTHROUGH_INTENDED +// +// Annotates implicit fall-through between switch labels, allowing a case to +// indicate intentional fallthrough and turn off warnings about any lack of a +// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by +// a semicolon and can be used in most places where `break` can, provided that +// no statements exist between it and the next switch label. +// +// Example: +// +// switch (x) { +// case 40: +// case 41: +// if (truth_is_out_there) { +// ++x; +// ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations +// // in comments +// } else { +// return x; +// } +// case 42: +// ... +// +// Notes: When supported, GCC and Clang can issue a warning on switch labels +// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See +// clang documentation on language extensions for details: +// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough +// +// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has +// no effect on diagnostics. In any case this macro has no effect on runtime +// behavior and performance of code. + +// The use of ABSL_HAVE_CPP_ATTRIBUTE() here uses C++17 attributes +// even if -std=c++17 is not set, which does not work with -Wpedantic on R +// #ifdef ABSL_FALLTHROUGH_INTENDED +// #error "ABSL_FALLTHROUGH_INTENDED should not be defined." +// #elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough) +// #define ABSL_FALLTHROUGH_INTENDED [[fallthrough]] +// #elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough) +// #define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] +// #elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough) +// #define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] +// #else +#define ABSL_FALLTHROUGH_INTENDED \ + do { \ + } while (0) +// #endif + +// ABSL_DEPRECATED() +// +// Marks a deprecated class, struct, enum, function, method and variable +// declarations. The macro argument is used as a custom diagnostic message (e.g. +// suggestion of a better alternative). +// +// For code or headers that are assured to only build with C++14 and up, prefer +// just using the standard `[[deprecated("message")]]` directly over this macro. +// +// Examples: +// +// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; +// +// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} +// +// template +// ABSL_DEPRECATED("Use DoThat() instead") +// void DoThis(); +// +// enum FooEnum { +// kBar ABSL_DEPRECATED("Use kBaz instead"), +// }; +// +// Every usage of a deprecated entity will trigger a warning when compiled with +// GCC/Clang's `-Wdeprecated-declarations` option. Google's production toolchain +// turns this warning off by default, instead relying on clang-tidy to report +// new uses of deprecated code. +#if ABSL_HAVE_ATTRIBUTE(deprecated) +#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define ABSL_DEPRECATED(message) +#endif + +// ABSL_CONST_INIT +// +// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will +// not compile (on supported platforms) unless the variable has a constant +// initializer. This is useful for variables with static and thread storage +// duration, because it guarantees that they will not suffer from the so-called +// "static init order fiasco". +// +// This attribute must be placed on the initializing declaration of the +// variable. Some compilers will give a -Wmissing-constinit warning when this +// attribute is placed on some other declaration but missing from the +// initializing declaration. +// +// In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can +// also be used in a non-initializing declaration to tell the compiler that a +// variable is already initialized, reducing overhead that would otherwise be +// incurred by a hidden guard variable. Thus annotating all declarations with +// this attribute is recommended to potentially enhance optimization. +// +// Example: +// +// class MyClass { +// public: +// ABSL_CONST_INIT static MyType my_var; +// }; +// +// ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); +// +// For code or headers that are assured to only build with C++20 and up, prefer +// just using the standard `constinit` keyword directly over this macro. +// +// Note that this attribute is redundant if the variable is declared constexpr. +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +#define ABSL_CONST_INIT constinit +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define ABSL_CONST_INIT [[clang::require_constant_initialization]] +#else +#define ABSL_CONST_INIT +#endif + +// ABSL_ATTRIBUTE_PURE_FUNCTION +// +// ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure" +// functions. A function is pure if its return value is only a function of its +// arguments. The pure attribute prohibits a function from modifying the state +// of the program that is observable by means other than inspecting the +// function's return value. Declaring such functions with the pure attribute +// allows the compiler to avoid emitting some calls in repeated invocations of +// the function with the same argument values. +// +// Example: +// +// ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d); +#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure) +#define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]] +#elif ABSL_HAVE_ATTRIBUTE(pure) +#define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure)) +#else +#define ABSL_ATTRIBUTE_PURE_FUNCTION +#endif + +// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function +// parameter or implicit object parameter is retained by the return value of the +// annotated function (or, for a parameter of a constructor, in the value of the +// constructed object). This attribute causes warnings to be produced if a +// temporary object does not live long enough. +// +// When applied to a reference parameter, the referenced object is assumed to be +// retained by the return value of the function. When applied to a non-reference +// parameter (for example, a pointer or a class type), all temporaries +// referenced by the parameter are assumed to be retained by the return value of +// the function. +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] +#elif ABSL_HAVE_ATTRIBUTE(lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) +#else +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif + +#endif // ABSL_BASE_ATTRIBUTES_H_ diff --git a/src/absl/base/call_once.h b/src/absl/base/call_once.h new file mode 100644 index 0000000..96109f5 --- /dev/null +++ b/src/absl/base/call_once.h @@ -0,0 +1,219 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: call_once.h +// ----------------------------------------------------------------------------- +// +// This header file provides an Abseil version of `std::call_once` for invoking +// a given function at most once, across all threads. This Abseil version is +// faster than the C++11 version and incorporates the C++17 argument-passing +// fix, so that (for example) non-const references may be passed to the invoked +// function. + +#ifndef ABSL_BASE_CALL_ONCE_H_ +#define ABSL_BASE_CALL_ONCE_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/internal/invoke.h" +#include "absl/base/internal/low_level_scheduling.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/internal/spinlock_wait.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class once_flag; + +namespace base_internal { +std::atomic* ControlWord(absl::once_flag* flag); +} // namespace base_internal + +// call_once() +// +// For all invocations using a given `once_flag`, invokes a given `fn` exactly +// once across all threads. The first call to `call_once()` with a particular +// `once_flag` argument (that does not throw an exception) will run the +// specified function with the provided `args`; other calls with the same +// `once_flag` argument will not run the function, but will wait +// for the provided function to finish running (if it is still running). +// +// This mechanism provides a safe, simple, and fast mechanism for one-time +// initialization in a multi-threaded process. +// +// Example: +// +// class MyInitClass { +// public: +// ... +// mutable absl::once_flag once_; +// +// MyInitClass* init() const { +// absl::call_once(once_, &MyInitClass::Init, this); +// return ptr_; +// } +// +template +void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args); + +// once_flag +// +// Objects of this type are used to distinguish calls to `call_once()` and +// ensure the provided function is only invoked once across all threads. This +// type is not copyable or movable. However, it has a `constexpr` +// constructor, and is safe to use as a namespace-scoped global variable. +class once_flag { + public: + constexpr once_flag() : control_(0) {} + once_flag(const once_flag&) = delete; + once_flag& operator=(const once_flag&) = delete; + + private: + friend std::atomic* base_internal::ControlWord(once_flag* flag); + std::atomic control_; +}; + +//------------------------------------------------------------------------------ +// End of public interfaces. +// Implementation details follow. +//------------------------------------------------------------------------------ + +namespace base_internal { + +// Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to +// initialize entities used by the scheduler implementation. +template +void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args); + +// Disables scheduling while on stack when scheduling mode is non-cooperative. +// No effect for cooperative scheduling modes. +class SchedulingHelper { + public: + explicit SchedulingHelper(base_internal::SchedulingMode mode) : mode_(mode) { + if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) { + guard_result_ = base_internal::SchedulingGuard::DisableRescheduling(); + } + } + + ~SchedulingHelper() { + if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) { + base_internal::SchedulingGuard::EnableRescheduling(guard_result_); + } + } + + private: + base_internal::SchedulingMode mode_; + bool guard_result_; +}; + +// Bit patterns for call_once state machine values. Internal implementation +// detail, not for use by clients. +// +// The bit patterns are arbitrarily chosen from unlikely values, to aid in +// debugging. However, kOnceInit must be 0, so that a zero-initialized +// once_flag will be valid for immediate use. +enum { + kOnceInit = 0, + kOnceRunning = 0x65C2937B, + kOnceWaiter = 0x05A308D2, + // A very small constant is chosen for kOnceDone so that it fit in a single + // compare with immediate instruction for most common ISAs. This is verified + // for x86, POWER and ARM. + kOnceDone = 221, // Random Number +}; + +template +ABSL_ATTRIBUTE_NOINLINE +void CallOnceImpl(std::atomic* control, + base_internal::SchedulingMode scheduling_mode, Callable&& fn, + Args&&... args) { +#ifndef NDEBUG + { + uint32_t old_control = control->load(std::memory_order_relaxed); + if (old_control != kOnceInit && + old_control != kOnceRunning && + old_control != kOnceWaiter && + old_control != kOnceDone) { + ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx", + static_cast(old_control)); // NOLINT + } + } +#endif // NDEBUG + static const base_internal::SpinLockWaitTransition trans[] = { + {kOnceInit, kOnceRunning, true}, + {kOnceRunning, kOnceWaiter, false}, + {kOnceDone, kOnceDone, true}}; + + // Must do this before potentially modifying control word's state. + base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode); + // Short circuit the simplest case to avoid procedure call overhead. + // The base_internal::SpinLockWait() call returns either kOnceInit or + // kOnceDone. If it returns kOnceDone, it must have loaded the control word + // with std::memory_order_acquire and seen a value of kOnceDone. + uint32_t old_control = kOnceInit; + if (control->compare_exchange_strong(old_control, kOnceRunning, + std::memory_order_relaxed) || + base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans, + scheduling_mode) == kOnceInit) { + base_internal::invoke(std::forward(fn), + std::forward(args)...); + old_control = + control->exchange(base_internal::kOnceDone, std::memory_order_release); + if (old_control == base_internal::kOnceWaiter) { + base_internal::SpinLockWake(control, true); + } + } // else *control is already kOnceDone +} + +inline std::atomic* ControlWord(once_flag* flag) { + return &flag->control_; +} + +template +void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args) { + std::atomic* once = base_internal::ControlWord(flag); + uint32_t s = once->load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { + base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY, + std::forward(fn), + std::forward(args)...); + } +} + +} // namespace base_internal + +template +void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { + std::atomic* once = base_internal::ControlWord(&flag); + uint32_t s = once->load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { + base_internal::CallOnceImpl( + once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL, + std::forward(fn), std::forward(args)...); + } +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_CALL_ONCE_H_ diff --git a/src/absl/base/casts.h b/src/absl/base/casts.h new file mode 100644 index 0000000..b99adb0 --- /dev/null +++ b/src/absl/base/casts.h @@ -0,0 +1,180 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: casts.h +// ----------------------------------------------------------------------------- +// +// This header file defines casting templates to fit use cases not covered by +// the standard casts provided in the C++ standard. As with all cast operations, +// use these with caution and only if alternatives do not exist. + +#ifndef ABSL_BASE_CASTS_H_ +#define ABSL_BASE_CASTS_H_ + +#include +#include +#include +#include + +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L +#include // For std::bit_cast. +#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +#include "absl/base/internal/identity.h" +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// implicit_cast() +// +// Performs an implicit conversion between types following the language +// rules for implicit conversion; if an implicit conversion is otherwise +// allowed by the language in the given context, this function performs such an +// implicit conversion. +// +// Example: +// +// // If the context allows implicit conversion: +// From from; +// To to = from; +// +// // Such code can be replaced by: +// implicit_cast(from); +// +// An `implicit_cast()` may also be used to annotate numeric type conversions +// that, although safe, may produce compiler warnings (such as `long` to `int`). +// Additionally, an `implicit_cast()` is also useful within return statements to +// indicate a specific implicit conversion is being undertaken. +// +// Example: +// +// return implicit_cast(size_in_bytes) / capacity_; +// +// Annotating code with `implicit_cast()` allows you to explicitly select +// particular overloads and template instantiations, while providing a safer +// cast than `reinterpret_cast()` or `static_cast()`. +// +// Additionally, an `implicit_cast()` can be used to allow upcasting within a +// type hierarchy where incorrect use of `static_cast()` could accidentally +// allow downcasting. +// +// Finally, an `implicit_cast()` can be used to perform implicit conversions +// from unrelated types that otherwise couldn't be implicitly cast directly; +// C++ will normally only implicitly cast "one step" in such conversions. +// +// That is, if C is a type which can be implicitly converted to B, with B being +// a type that can be implicitly converted to A, an `implicit_cast()` can be +// used to convert C to B (which the compiler can then implicitly convert to A +// using language rules). +// +// Example: +// +// // Assume an object C is convertible to B, which is implicitly convertible +// // to A +// A a = implicit_cast(C); +// +// Such implicit cast chaining may be useful within template logic. +template +constexpr To implicit_cast(typename absl::internal::identity_t to) { + return to; +} + +// bit_cast() +// +// Creates a value of the new type `Dest` whose representation is the same as +// that of the argument, which is of (deduced) type `Source` (a "bitwise cast"; +// every bit in the value representation of the result is equal to the +// corresponding bit in the object representation of the source). Source and +// destination types must be of the same size, and both types must be trivially +// copyable. +// +// As with most casts, use with caution. A `bit_cast()` might be needed when you +// need to treat a value as the value of some other type, for example, to access +// the individual bits of an object which are not normally accessible through +// the object's type, such as for working with the binary representation of a +// floating point value: +// +// float f = 3.14159265358979; +// int i = bit_cast(f); +// // i = 0x40490fdb +// +// Reinterpreting and accessing a value directly as a different type (as shown +// below) usually results in undefined behavior. +// +// Example: +// +// // WRONG +// float f = 3.14159265358979; +// int i = reinterpret_cast(f); // Wrong +// int j = *reinterpret_cast(&f); // Equally wrong +// int k = *bit_cast(&f); // Equally wrong +// +// Reinterpret-casting results in undefined behavior according to the ISO C++ +// specification, section [basic.lval]. Roughly, this section says: if an object +// in memory has one type, and a program accesses it with a different type, the +// result is undefined behavior for most "different type". +// +// Using bit_cast on a pointer and then dereferencing it is no better than using +// reinterpret_cast. You should only use bit_cast on the value itself. +// +// Such casting results in type punning: holding an object in memory of one type +// and reading its bits back using a different type. A `bit_cast()` avoids this +// issue by copying the object representation to a new value, which avoids +// introducing this undefined behavior (since the original value is never +// accessed in the wrong way). +// +// The requirements of `absl::bit_cast` are more strict than that of +// `std::bit_cast` unless compiler support is available. Specifically, without +// compiler support, this implementation also requires `Dest` to be +// default-constructible. In C++20, `absl::bit_cast` is replaced by +// `std::bit_cast`. +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +using std::bit_cast; + +#else // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +template ::value && + type_traits_internal::is_trivially_copyable::value +#if !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + && std::is_default_constructible::value +#endif // !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + , + int>::type = 0> +#if ABSL_HAVE_BUILTIN(__builtin_bit_cast) +inline constexpr Dest bit_cast(const Source& source) { + return __builtin_bit_cast(Dest, source); +} +#else // ABSL_HAVE_BUILTIN(__builtin_bit_cast) +inline Dest bit_cast(const Source& source) { + Dest dest; + memcpy(static_cast(std::addressof(dest)), + static_cast(std::addressof(source)), sizeof(dest)); + return dest; +} +#endif // ABSL_HAVE_BUILTIN(__builtin_bit_cast) + +#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_CASTS_H_ diff --git a/src/absl/base/config.h b/src/absl/base/config.h new file mode 100644 index 0000000..e7bf165 --- /dev/null +++ b/src/absl/base/config.h @@ -0,0 +1,915 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: config.h +// ----------------------------------------------------------------------------- +// +// This header file defines a set of macros for checking the presence of +// important compiler and platform features. Such macros can be used to +// produce portable code by parameterizing compilation based on the presence or +// lack of a given feature. +// +// We define a "feature" as some interface we wish to program to: for example, +// a library function or system call. A value of `1` indicates support for +// that feature; any other value indicates the feature support is undefined. +// +// Example: +// +// Suppose a programmer wants to write a program that uses the 'mmap()' system +// call. The Abseil macro for that feature (`ABSL_HAVE_MMAP`) allows you to +// selectively include the `mmap.h` header and bracket code using that feature +// in the macro: +// +// #include "absl/base/config.h" +// +// #ifdef ABSL_HAVE_MMAP +// #include "sys/mman.h" +// #endif //ABSL_HAVE_MMAP +// +// ... +// #ifdef ABSL_HAVE_MMAP +// void *ptr = mmap(...); +// ... +// #endif // ABSL_HAVE_MMAP + +#ifndef ABSL_BASE_CONFIG_H_ +#define ABSL_BASE_CONFIG_H_ + +// Included for the __GLIBC__ macro (or similar macros on other systems). +#include + +#ifdef __cplusplus +// Included for __GLIBCXX__, _LIBCPP_VERSION +#include +#endif // __cplusplus + +// ABSL_INTERNAL_CPLUSPLUS_LANG +// +// MSVC does not set the value of __cplusplus correctly, but instead uses +// _MSVC_LANG as a stand-in. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +// +// However, there are reports that MSVC even sets _MSVC_LANG incorrectly at +// times, for example: +// https://github.com/microsoft/vscode-cpptools/issues/1770 +// https://reviews.llvm.org/D70996 +// +// For this reason, this symbol is considered INTERNAL and code outside of +// Abseil must not use it. +#if defined(_MSVC_LANG) +#define ABSL_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG +#elif defined(__cplusplus) +#define ABSL_INTERNAL_CPLUSPLUS_LANG __cplusplus +#endif + +#if defined(__APPLE__) +// Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED, +// __IPHONE_8_0. +#include +#include +#endif + +#include "absl/base/options.h" +#include "absl/base/policy_checks.h" + +// Abseil long-term support (LTS) releases will define +// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the +// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the +// integer representing the patch-level for that release. +// +// For example, for LTS release version "20300401.2", this would give us +// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2 +// +// These symbols will not be defined in non-LTS code. +// +// Abseil recommends that clients live-at-head. Therefore, if you are using +// these symbols to assert a minimum version requirement, we recommend you do it +// as +// +// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401 +// #error Project foo requires Abseil LTS version >= 20300401 +// #endif +// +// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes +// live-at-head clients from the minimum version assertion. +// +// See https://abseil.io/about/releases for more information on Abseil release +// management. +// +// LTS releases can be obtained from +// https://github.com/abseil/abseil-cpp/releases. +#define ABSL_LTS_RELEASE_VERSION 20220623 +#define ABSL_LTS_RELEASE_PATCH_LEVEL 1 + +// Helper macro to convert a CPP variable to a string literal. +#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x +#define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) + +// ----------------------------------------------------------------------------- +// Abseil namespace annotations +// ----------------------------------------------------------------------------- + +// ABSL_NAMESPACE_BEGIN/ABSL_NAMESPACE_END +// +// An annotation placed at the beginning/end of each `namespace absl` scope. +// This is used to inject an inline namespace. +// +// The proper way to write Abseil code in the `absl` namespace is: +// +// namespace absl { +// ABSL_NAMESPACE_BEGIN +// +// void Foo(); // absl::Foo(). +// +// ABSL_NAMESPACE_END +// } // namespace absl +// +// Users of Abseil should not use these macros, because users of Abseil should +// not write `namespace absl {` in their own code for any reason. (Abseil does +// not support forward declarations of its own types, nor does it support +// user-provided specialization of Abseil templates. Code that violates these +// rules may be broken without warning.) +#if !defined(ABSL_OPTION_USE_INLINE_NAMESPACE) || \ + !defined(ABSL_OPTION_INLINE_NAMESPACE_NAME) +#error options.h is misconfigured. +#endif + +// Check that ABSL_OPTION_INLINE_NAMESPACE_NAME is neither "head" nor "" +#if defined(__cplusplus) && ABSL_OPTION_USE_INLINE_NAMESPACE == 1 + +#define ABSL_INTERNAL_INLINE_NAMESPACE_STR \ + ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) + +static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != '\0', + "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " + "not be empty."); +static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[1] != 'e' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[2] != 'a' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[3] != 'd' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[4] != '\0', + "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " + "be changed to a new, unique identifier name."); + +#endif + +#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 +#define ABSL_NAMESPACE_BEGIN +#define ABSL_NAMESPACE_END +#define ABSL_INTERNAL_C_SYMBOL(x) x +#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1 +#define ABSL_NAMESPACE_BEGIN \ + inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME { +#define ABSL_NAMESPACE_END } +#define ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) x##_##v +#define ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, v) \ + ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) +#define ABSL_INTERNAL_C_SYMBOL(x) \ + ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, ABSL_OPTION_INLINE_NAMESPACE_NAME) +#else +#error options.h is misconfigured. +#endif + +// ----------------------------------------------------------------------------- +// Compiler Feature Checks +// ----------------------------------------------------------------------------- + +// ABSL_HAVE_BUILTIN() +// +// Checks whether the compiler supports a Clang Feature Checking Macro, and if +// so, checks whether it supports the provided builtin function "x" where x +// is one of the functions noted in +// https://clang.llvm.org/docs/LanguageExtensions.html +// +// Note: Use this macro to avoid an extra level of #ifdef __has_builtin check. +// http://releases.llvm.org/3.3/tools/clang/docs/LanguageExtensions.html +#ifdef __has_builtin +#define ABSL_HAVE_BUILTIN(x) __has_builtin(x) +#else +#define ABSL_HAVE_BUILTIN(x) 0 +#endif + +#ifdef __has_feature +#define ABSL_HAVE_FEATURE(f) __has_feature(f) +#else +#define ABSL_HAVE_FEATURE(f) 0 +#endif + +// Portable check for GCC minimum version: +// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \ + (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0 +#endif + +#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \ + (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0 +#endif + +// ABSL_HAVE_TLS is defined to 1 when __thread should be supported. +// We assume __thread is supported on Linux or Asylo when compiled with Clang or +// compiled against libstdc++ with _GLIBCXX_HAVE_TLS defined. +#ifdef ABSL_HAVE_TLS +#error ABSL_HAVE_TLS cannot be directly set +#elif (defined(__linux__) || defined(__ASYLO__)) && \ + (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) +#define ABSL_HAVE_TLS 1 +#endif + +// ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE +// +// Checks whether `std::is_trivially_destructible` is supported. +// +// Notes: All supported compilers using libc++ support this feature, as does +// gcc >= 4.8.1 using libstdc++, and Visual Studio. +#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE +#error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set +#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \ + (!defined(__clang__) && defined(__GLIBCXX__) && \ + ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8)) +#define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1 +#endif + +// ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +// +// Checks whether `std::is_trivially_default_constructible` and +// `std::is_trivially_copy_constructible` are supported. + +// ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE +// +// Checks whether `std::is_trivially_copy_assignable` is supported. + +// Notes: Clang with libc++ supports these features, as does gcc >= 7.4 with +// libstdc++, or gcc >= 8.2 with libc++, and Visual Studio (but not NVCC). +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) +#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set +#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) +#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set +#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ + (!defined(__clang__) && \ + ((ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \ + (ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) && \ + defined(_LIBCPP_VERSION)))) || \ + (defined(_MSC_VER) && !defined(__NVCC__)) +#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 +#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 +#endif + +// ABSL_HAVE_THREAD_LOCAL +// +// Checks whether C++11's `thread_local` storage duration specifier is +// supported. +#ifdef ABSL_HAVE_THREAD_LOCAL +#error ABSL_HAVE_THREAD_LOCAL cannot be directly set +#elif defined(__APPLE__) +// Notes: +// * Xcode's clang did not support `thread_local` until version 8, and +// even then not for all iOS < 9.0. +// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator +// targeting iOS 9.x. +// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time +// making ABSL_HAVE_FEATURE unreliable there. +// +#if ABSL_HAVE_FEATURE(cxx_thread_local) && \ + !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) +#define ABSL_HAVE_THREAD_LOCAL 1 +#endif +#else // !defined(__APPLE__) +#define ABSL_HAVE_THREAD_LOCAL 1 +#endif + +// There are platforms for which TLS should not be used even though the compiler +// makes it seem like it's supported (Android NDK < r12b for example). +// This is primarily because of linker problems and toolchain misconfiguration: +// Abseil does not intend to support this indefinitely. Currently, the newest +// toolchain that we intend to support that requires this behavior is the +// r11 NDK - allowing for a 5 year support window on that means this option +// is likely to be removed around June of 2021. +// TLS isn't supported until NDK r12b per +// https://developer.android.com/ndk/downloads/revision_history.html +// Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in +// . For NDK < r16, users should define these macros, +// e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. +#if defined(__ANDROID__) && defined(__clang__) +#if __has_include() +#include +#endif // __has_include() +#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \ + defined(__NDK_MINOR__) && \ + ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1))) +#undef ABSL_HAVE_TLS +#undef ABSL_HAVE_THREAD_LOCAL +#endif +#endif // defined(__ANDROID__) && defined(__clang__) + +// ABSL_HAVE_INTRINSIC_INT128 +// +// Checks whether the __int128 compiler extension for a 128-bit integral type is +// supported. +// +// Note: __SIZEOF_INT128__ is defined by Clang and GCC when __int128 is +// supported, but we avoid using it in certain cases: +// * On Clang: +// * Building using Clang for Windows, where the Clang runtime library has +// 128-bit support only on LP64 architectures, but Windows is LLP64. +// * On Nvidia's nvcc: +// * nvcc also defines __GNUC__ and __SIZEOF_INT128__, but not all versions +// actually support __int128. +// R CMD check on CRAN uses -Wpedantic, which forces us not to use the __int128 +// intrinsic as implemented here. +// #ifdef ABSL_HAVE_INTRINSIC_INT128 +// #error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set +// #elif defined(__SIZEOF_INT128__) +// #if (defined(__clang__) && !defined(_WIN32)) || +// (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || +// (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__)) +// #define ABSL_HAVE_INTRINSIC_INT128 1 +// #elif defined(__CUDACC__) +// // __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a +// // string explaining that it has been removed starting with CUDA 9. We use +// // nested #ifs because there is no short-circuiting in the preprocessor. +// // NOTE: `__CUDACC__` could be undefined while `__CUDACC_VER__` is defined. +// #if __CUDACC_VER__ >= 70000 +// #define ABSL_HAVE_INTRINSIC_INT128 1 +// #endif // __CUDACC_VER__ >= 70000 +// #endif // defined(__CUDACC__) +// #endif // ABSL_HAVE_INTRINSIC_INT128 + +// ABSL_HAVE_EXCEPTIONS +// +// Checks whether the compiler both supports and enables exceptions. Many +// compilers support a "no exceptions" mode that disables exceptions. +// +// Generally, when ABSL_HAVE_EXCEPTIONS is not defined: +// +// * Code using `throw` and `try` may not compile. +// * The `noexcept` specifier will still compile and behave as normal. +// * The `noexcept` operator may still return `false`. +// +// For further details, consult the compiler's documentation. +#ifdef ABSL_HAVE_EXCEPTIONS +#error ABSL_HAVE_EXCEPTIONS cannot be directly set. +#elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6) +// Clang >= 3.6 +#if ABSL_HAVE_FEATURE(cxx_exceptions) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif // ABSL_HAVE_FEATURE(cxx_exceptions) +#elif defined(__clang__) +// Clang < 3.6 +// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro +#if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) +// Handle remaining special cases and default to exceptions being supported. +#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ + !(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) && \ + !defined(__cpp_exceptions)) && \ + !(defined(_MSC_VER) && !defined(_CPPUNWIND)) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif + +// ----------------------------------------------------------------------------- +// Platform Feature Checks +// ----------------------------------------------------------------------------- + +// Currently supported operating systems and associated preprocessor +// symbols: +// +// Linux and Linux-derived __linux__ +// Android __ANDROID__ (implies __linux__) +// Linux (non-Android) __linux__ && !__ANDROID__ +// Darwin (macOS and iOS) __APPLE__ +// Akaros (http://akaros.org) __ros__ +// Windows _WIN32 +// NaCL __native_client__ +// AsmJS __asmjs__ +// WebAssembly __wasm__ +// Fuchsia __Fuchsia__ +// +// Note that since Android defines both __ANDROID__ and __linux__, one +// may probe for either Linux or Android by simply testing for __linux__. + +// ABSL_HAVE_MMAP +// +// Checks whether the platform has an mmap(2) implementation as defined in +// POSIX.1-2001. +#ifdef ABSL_HAVE_MMAP +#error ABSL_HAVE_MMAP cannot be directly set +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ + defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ + defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ + defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ + defined(__QNX__) +#define ABSL_HAVE_MMAP 1 +#endif + +// ABSL_HAVE_PTHREAD_GETSCHEDPARAM +// +// Checks whether the platform implements the pthread_(get|set)schedparam(3) +// functions as defined in POSIX.1-2001. +#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM +#error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) +#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 +#endif + +// ABSL_HAVE_SCHED_GETCPU +// +// Checks whether sched_getcpu is available. +#ifdef ABSL_HAVE_SCHED_GETCPU +#error ABSL_HAVE_SCHED_GETCPU cannot be directly set +#elif defined(__linux__) +#define ABSL_HAVE_SCHED_GETCPU 1 +#endif + +// ABSL_HAVE_SCHED_YIELD +// +// Checks whether the platform implements sched_yield(2) as defined in +// POSIX.1-2001. +#ifdef ABSL_HAVE_SCHED_YIELD +#error ABSL_HAVE_SCHED_YIELD cannot be directly set +#elif defined(__linux__) || defined(__ros__) || defined(__native_client__) +#define ABSL_HAVE_SCHED_YIELD 1 +#endif + +// ABSL_HAVE_SEMAPHORE_H +// +// Checks whether the platform supports the header and sem_init(3) +// family of functions as standardized in POSIX.1-2001. +// +// Note: While Apple provides for both iOS and macOS, it is +// explicitly deprecated and will cause build failures if enabled for those +// platforms. We side-step the issue by not defining it here for Apple +// platforms. +#ifdef ABSL_HAVE_SEMAPHORE_H +#error ABSL_HAVE_SEMAPHORE_H cannot be directly set +#elif defined(__linux__) || defined(__ros__) +#define ABSL_HAVE_SEMAPHORE_H 1 +#endif + +// ABSL_HAVE_ALARM +// +// Checks whether the platform supports the header and alarm(2) +// function as standardized in POSIX.1-2001. +#ifdef ABSL_HAVE_ALARM +#error ABSL_HAVE_ALARM cannot be directly set +#elif defined(__GOOGLE_GRTE_VERSION__) +// feature tests for Google's GRTE +#define ABSL_HAVE_ALARM 1 +#elif defined(__GLIBC__) +// feature test for glibc +#define ABSL_HAVE_ALARM 1 +#elif defined(_MSC_VER) +// feature tests for Microsoft's library +#elif defined(__MINGW32__) +// mingw32 doesn't provide alarm(2): +// https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h +// mingw-w64 provides a no-op implementation: +// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c +#elif defined(__EMSCRIPTEN__) +// emscripten doesn't support signals +#elif defined(__Fuchsia__) +// Signals don't exist on fuchsia. +#elif defined(__native_client__) +#else +// other standard libraries +#define ABSL_HAVE_ALARM 1 +#endif + +// ABSL_IS_LITTLE_ENDIAN +// ABSL_IS_BIG_ENDIAN +// +// Checks the endianness of the platform. +// +// Notes: uses the built in endian macros provided by GCC (since 4.6) and +// Clang (since 3.2); see +// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html. +// Otherwise, if _WIN32, assume little endian. Otherwise, bail with an error. +#if defined(ABSL_IS_BIG_ENDIAN) +#error "ABSL_IS_BIG_ENDIAN cannot be directly set." +#endif +#if defined(ABSL_IS_LITTLE_ENDIAN) +#error "ABSL_IS_LITTLE_ENDIAN cannot be directly set." +#endif + +#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define ABSL_IS_LITTLE_ENDIAN 1 +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define ABSL_IS_BIG_ENDIAN 1 +#elif defined(_WIN32) +#define ABSL_IS_LITTLE_ENDIAN 1 +#else +#error "absl endian detection needs to be set up for your compiler" +#endif + +// macOS < 10.13 and iOS < 11 don't let you use , , or +// even though the headers exist and are publicly noted to work, because the +// libc++ shared library shipped on the system doesn't have the requisite +// exported symbols. See https://github.com/abseil/abseil-cpp/issues/207 and +// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +// +// libc++ spells out the availability requirements in the file +// llvm-project/libcxx/include/__config via the #define +// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. +// +// Unfortunately, Apple initially mis-stated the requirements as macOS < 10.14 +// and iOS < 12 in the libc++ headers. This was corrected by +// https://github.com/llvm/llvm-project/commit/7fb40e1569dd66292b647f4501b85517e9247953 +// which subsequently made it into the XCode 12.5 release. We need to match the +// old (incorrect) conditions when built with old XCode, but can use the +// corrected earlier versions with new XCode. +#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ + ((_LIBCPP_VERSION >= 11000 && /* XCode 12.5 or later: */ \ + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 110000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 40000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 110000))) || \ + (_LIBCPP_VERSION < 11000 && /* Pre-XCode 12.5: */ \ + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000)))) +#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 +#else +#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 +#endif + +// ABSL_HAVE_STD_ANY +// +// Checks whether C++17 std::any is available by checking whether exists. +#ifdef ABSL_HAVE_STD_ANY +#error "ABSL_HAVE_STD_ANY cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && defined(__cplusplus) && __cplusplus >= 201703L && \ + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#define ABSL_HAVE_STD_ANY 1 +#endif +#endif + +// ABSL_HAVE_STD_OPTIONAL +// +// Checks whether C++17 std::optional is available. +#ifdef ABSL_HAVE_STD_OPTIONAL +#error "ABSL_HAVE_STD_OPTIONAL cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && defined(__cplusplus) && \ + __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#define ABSL_HAVE_STD_OPTIONAL 1 +#endif +#endif + +// ABSL_HAVE_STD_VARIANT +// +// Checks whether C++17 std::variant is available. +#ifdef ABSL_HAVE_STD_VARIANT +#error "ABSL_HAVE_STD_VARIANT cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && defined(__cplusplus) && \ + __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#define ABSL_HAVE_STD_VARIANT 1 +#endif +#endif + +// ABSL_HAVE_STD_STRING_VIEW +// +// Checks whether C++17 std::string_view is available. +#ifdef ABSL_HAVE_STD_STRING_VIEW +#error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && defined(__cplusplus) && \ + __cplusplus >= 201703L +#define ABSL_HAVE_STD_STRING_VIEW 1 +#endif +#endif + +// For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than +// the support for , , , . So we use +// _MSC_VER to check whether we have VS 2017 RTM (when , , +// , is implemented) or higher. Also, `__cplusplus` is +// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language +// version. +// TODO(zhangxy): fix tests before enabling aliasing for `std::any`. +#if defined(_MSC_VER) && _MSC_VER >= 1910 && \ + ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || \ + (defined(__cplusplus) && __cplusplus > 201402)) +// #define ABSL_HAVE_STD_ANY 1 +#define ABSL_HAVE_STD_OPTIONAL 1 +#define ABSL_HAVE_STD_VARIANT 1 +#define ABSL_HAVE_STD_STRING_VIEW 1 +#endif + +// ABSL_USES_STD_ANY +// +// Indicates whether absl::any is an alias for std::any. +#if !defined(ABSL_OPTION_USE_STD_ANY) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_ANY == 0 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY)) +#undef ABSL_USES_STD_ANY +#elif ABSL_OPTION_USE_STD_ANY == 1 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY)) +#define ABSL_USES_STD_ANY 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_OPTIONAL +// +// Indicates whether absl::optional is an alias for std::optional. +#if !defined(ABSL_OPTION_USE_STD_OPTIONAL) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL)) +#undef ABSL_USES_STD_OPTIONAL +#elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL)) +#define ABSL_USES_STD_OPTIONAL 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_VARIANT +// +// Indicates whether absl::variant is an alias for std::variant. +#if !defined(ABSL_OPTION_USE_STD_VARIANT) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT)) +#undef ABSL_USES_STD_VARIANT +#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT)) +#define ABSL_USES_STD_VARIANT 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_STRING_VIEW +// +// Indicates whether absl::string_view is an alias for std::string_view. +#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + !defined(ABSL_HAVE_STD_STRING_VIEW)) +#undef ABSL_USES_STD_STRING_VIEW +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + defined(ABSL_HAVE_STD_STRING_VIEW)) +#define ABSL_USES_STD_STRING_VIEW 1 +#else +#error options.h is misconfigured. +#endif + +// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION +// SEH exception from emplace for variant when constructing the +// struct can throw. This defeats some of variant_test and +// variant_exception_safety_test. +#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG) +#define ABSL_INTERNAL_MSVC_2017_DBG_MODE +#endif + +// ABSL_INTERNAL_MANGLED_NS +// ABSL_INTERNAL_MANGLED_BACKREFERENCE +// +// Internal macros for building up mangled names in our internal fork of CCTZ. +// This implementation detail is only needed and provided for the MSVC build. +// +// These macros both expand to string literals. ABSL_INTERNAL_MANGLED_NS is +// the mangled spelling of the `absl` namespace, and +// ABSL_INTERNAL_MANGLED_BACKREFERENCE is a back-reference integer representing +// the proper count to skip past the CCTZ fork namespace names. (This number +// is one larger when there is an inline namespace name to skip.) +#if defined(_MSC_VER) +#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 +#define ABSL_INTERNAL_MANGLED_NS "absl" +#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "5" +#else +#define ABSL_INTERNAL_MANGLED_NS \ + ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) "@absl" +#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "6" +#endif +#endif + +// ABSL_DLL +// +// When building Abseil as a DLL, this macro expands to `__declspec(dllexport)` +// so we can annotate symbols appropriately as being exported. When used in +// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so +// that consumers know the symbol is defined inside the DLL. In all other cases, +// the macro expands to nothing. +#if defined(_MSC_VER) +#if defined(ABSL_BUILD_DLL) +#define ABSL_DLL __declspec(dllexport) +#elif defined(ABSL_CONSUME_DLL) +#define ABSL_DLL __declspec(dllimport) +#else +#define ABSL_DLL +#endif +#else +#define ABSL_DLL +#endif // defined(_MSC_VER) + +// ABSL_HAVE_MEMORY_SANITIZER +// +// MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of +// a compiler instrumentation module and a run-time library. +#ifdef ABSL_HAVE_MEMORY_SANITIZER +#error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set." +#elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer) +#define ABSL_HAVE_MEMORY_SANITIZER 1 +#endif + +// ABSL_HAVE_THREAD_SANITIZER +// +// ThreadSanitizer (TSan) is a fast data race detector. +#ifdef ABSL_HAVE_THREAD_SANITIZER +#error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_THREAD__) +#define ABSL_HAVE_THREAD_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(thread_sanitizer) +#define ABSL_HAVE_THREAD_SANITIZER 1 +#endif + +// ABSL_HAVE_ADDRESS_SANITIZER +// +// AddressSanitizer (ASan) is a fast memory error detector. +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_ADDRESS__) +#define ABSL_HAVE_ADDRESS_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(address_sanitizer) +#define ABSL_HAVE_ADDRESS_SANITIZER 1 +#endif + +// ABSL_HAVE_HWADDRESS_SANITIZER +// +// Hardware-Assisted AddressSanitizer (or HWASAN) is even faster than asan +// memory error detector which can use CPU features like ARM TBI, Intel LAM or +// AMD UAI. +#ifdef ABSL_HAVE_HWADDRESS_SANITIZER +#error "ABSL_HAVE_HWADDRESS_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_HWADDRESS__) +#define ABSL_HAVE_HWADDRESS_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(hwaddress_sanitizer) +#define ABSL_HAVE_HWADDRESS_SANITIZER 1 +#endif + +// ABSL_HAVE_LEAK_SANITIZER +// +// LeakSanitizer (or lsan) is a detector of memory leaks. +// https://clang.llvm.org/docs/LeakSanitizer.html +// https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// +// The macro ABSL_HAVE_LEAK_SANITIZER can be used to detect at compile-time +// whether the LeakSanitizer is potentially available. However, just because the +// LeakSanitizer is available does not mean it is active. Use the +// always-available run-time interface in //absl/debugging/leak_check.h for +// interacting with LeakSanitizer. +#ifdef ABSL_HAVE_LEAK_SANITIZER +#error "ABSL_HAVE_LEAK_SANITIZER cannot be directly set." +#elif defined(LEAK_SANITIZER) +// GCC provides no method for detecting the presense of the standalone +// LeakSanitizer (-fsanitize=leak), so GCC users of -fsanitize=leak should also +// use -DLEAK_SANITIZER. +#define ABSL_HAVE_LEAK_SANITIZER 1 +// Clang standalone LeakSanitizer (-fsanitize=leak) +#elif ABSL_HAVE_FEATURE(leak_sanitizer) +#define ABSL_HAVE_LEAK_SANITIZER 1 +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) +// GCC or Clang using the LeakSanitizer integrated into AddressSanitizer. +#define ABSL_HAVE_LEAK_SANITIZER 1 +#endif + +// ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +// +// Class template argument deduction is a language feature added in C++17. +#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +#error "ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION cannot be directly set." +#elif defined(__cpp_deduction_guides) +#define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 +#endif + +// ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// +// Prior to C++17, static constexpr variables defined in classes required a +// separate definition outside of the class body, for example: +// +// class Foo { +// static constexpr int kBar = 0; +// }; +// constexpr int Foo::kBar; +// +// In C++17, these variables defined in classes are considered inline variables, +// and the extra declaration is redundant. Since some compilers warn on the +// extra declarations, ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL can be used +// conditionally ignore them: +// +// #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// constexpr int Foo::kBar; +// #endif +#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L +#define ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1 +#endif + +// `ABSL_INTERNAL_HAS_RTTI` determines whether abseil is being compiled with +// RTTI support. +#ifdef ABSL_INTERNAL_HAS_RTTI +#error ABSL_INTERNAL_HAS_RTTI cannot be directly set +#elif !defined(__GNUC__) || defined(__GXX_RTTI) +#define ABSL_INTERNAL_HAS_RTTI 1 +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + +// ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +#ifdef ABSL_INTERNAL_HAVE_SSE +#error ABSL_INTERNAL_HAVE_SSE cannot be directly set +#elif defined(__SSE__) +#define ABSL_INTERNAL_HAVE_SSE 1 +#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1) +// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 1 +// indicates that at least SSE was targeted with the /arch:SSE option. +// All x86-64 processors support SSE, so support can be assumed. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#define ABSL_INTERNAL_HAVE_SSE 1 +#endif + +// ABSL_INTERNAL_HAVE_SSE2 is used for compile-time detection of SSE2 support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +#ifdef ABSL_INTERNAL_HAVE_SSE2 +#error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set +#elif defined(__SSE2__) +#define ABSL_INTERNAL_HAVE_SSE2 1 +#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) +// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2 +// indicates that at least SSE2 was targeted with the /arch:SSE2 option. +// All x86-64 processors support SSE2, so support can be assumed. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#define ABSL_INTERNAL_HAVE_SSE2 1 +#endif + +// ABSL_INTERNAL_HAVE_SSSE3 is used for compile-time detection of SSSE3 support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +// +// MSVC does not have a mode that targets SSSE3 at compile-time. To use SSSE3 +// with MSVC requires either assuming that the code will only every run on CPUs +// that support SSSE3, otherwise __cpuid() can be used to detect support at +// runtime and fallback to a non-SSSE3 implementation when SSSE3 is unsupported +// by the CPU. +#ifdef ABSL_INTERNAL_HAVE_SSSE3 +#error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set +#elif defined(__SSSE3__) +#define ABSL_INTERNAL_HAVE_SSSE3 1 +#endif + +// ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM +// SIMD). +#ifdef ABSL_INTERNAL_HAVE_ARM_NEON +#error ABSL_INTERNAL_HAVE_ARM_NEON cannot be directly set +#elif defined(__ARM_NEON) +#define ABSL_INTERNAL_HAVE_ARM_NEON 1 +#endif + +#endif // ABSL_BASE_CONFIG_H_ diff --git a/src/absl/base/const_init.h b/src/absl/base/const_init.h new file mode 100644 index 0000000..16520b6 --- /dev/null +++ b/src/absl/base/const_init.h @@ -0,0 +1,76 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// kConstInit +// ----------------------------------------------------------------------------- +// +// A constructor tag used to mark an object as safe for use as a global +// variable, avoiding the usual lifetime issues that can affect globals. + +#ifndef ABSL_BASE_CONST_INIT_H_ +#define ABSL_BASE_CONST_INIT_H_ + +#include "absl/base/config.h" + +// In general, objects with static storage duration (such as global variables) +// can trigger tricky object lifetime situations. Attempting to access them +// from the constructors or destructors of other global objects can result in +// undefined behavior, unless their constructors and destructors are designed +// with this issue in mind. +// +// The normal way to deal with this issue in C++11 is to use constant +// initialization and trivial destructors. +// +// Constant initialization is guaranteed to occur before any other code +// executes. Constructors that are declared 'constexpr' are eligible for +// constant initialization. You can annotate a variable declaration with the +// ABSL_CONST_INIT macro to express this intent. For compilers that support +// it, this annotation will cause a compilation error for declarations that +// aren't subject to constant initialization (perhaps because a runtime value +// was passed as a constructor argument). +// +// On program shutdown, lifetime issues can be avoided on global objects by +// ensuring that they contain trivial destructors. A class has a trivial +// destructor unless it has a user-defined destructor, a virtual method or base +// class, or a data member or base class with a non-trivial destructor of its +// own. Objects with static storage duration and a trivial destructor are not +// cleaned up on program shutdown, and are thus safe to access from other code +// running during shutdown. +// +// For a few core Abseil classes, we make a best effort to allow for safe global +// instances, even though these classes have non-trivial destructors. These +// objects can be created with the absl::kConstInit tag. For example: +// ABSL_CONST_INIT absl::Mutex global_mutex(absl::kConstInit); +// +// The line above declares a global variable of type absl::Mutex which can be +// accessed at any point during startup or shutdown. global_mutex's destructor +// will still run, but will not invalidate the object. Note that C++ specifies +// that accessing an object after its destructor has run results in undefined +// behavior, but this pattern works on the toolchains we support. +// +// The absl::kConstInit tag should only be used to define objects with static +// or thread_local storage duration. + +namespace absl { +ABSL_NAMESPACE_BEGIN + +enum ConstInitType { + kConstInit, +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_CONST_INIT_H_ diff --git a/src/absl/base/dynamic_annotations.h b/src/absl/base/dynamic_annotations.h new file mode 100644 index 0000000..3ea7c15 --- /dev/null +++ b/src/absl/base/dynamic_annotations.h @@ -0,0 +1,471 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file defines dynamic annotations for use with dynamic analysis tool +// such as valgrind, PIN, etc. +// +// Dynamic annotation is a source code annotation that affects the generated +// code (that is, the annotation is not a comment). Each such annotation is +// attached to a particular instruction and/or to a particular object (address) +// in the program. +// +// The annotations that should be used by users are macros in all upper-case +// (e.g., ABSL_ANNOTATE_THREAD_NAME). +// +// Actual implementation of these macros may differ depending on the dynamic +// analysis tool being used. +// +// This file supports the following configurations: +// - Dynamic Annotations enabled (with static thread-safety warnings disabled). +// In this case, macros expand to functions implemented by Thread Sanitizer, +// when building with TSan. When not provided an external implementation, +// dynamic_annotations.cc provides no-op implementations. +// +// - Static Clang thread-safety warnings enabled. +// When building with a Clang compiler that supports thread-safety warnings, +// a subset of annotations can be statically-checked at compile-time. We +// expand these macros to static-inline functions that can be analyzed for +// thread-safety, but afterwards elided when building the final binary. +// +// - All annotations are disabled. +// If neither Dynamic Annotations nor Clang thread-safety warnings are +// enabled, then all annotation-macros expand to empty. + +#ifndef ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ +#define ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#ifdef __cplusplus +#include "absl/base/macros.h" +#endif + +// TODO(rogeeff): Remove after the backward compatibility period. +#include "absl/base/internal/dynamic_annotations.h" // IWYU pragma: export + +// ------------------------------------------------------------------------- +// Decide which features are enabled. + +#ifdef ABSL_HAVE_THREAD_SANITIZER + +#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 +#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1 + +#else + +#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0 +#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0 +#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0 + +// Clang provides limited support for static thread-safety analysis through a +// feature called Annotalysis. We configure macro-definitions according to +// whether Annotalysis support is available. When running in opt-mode, GCC +// will issue a warning, if these attributes are compiled. Only include them +// when compiling using Clang. + +#if defined(__clang__) +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 1 +#if !defined(SWIG) +#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1 +#endif +#else +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 +#endif + +// Read/write annotations are enabled in Annotalysis mode; disabled otherwise. +#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \ + ABSL_INTERNAL_ANNOTALYSIS_ENABLED + +#endif // ABSL_HAVE_THREAD_SANITIZER + +#ifdef __cplusplus +#define ABSL_INTERNAL_BEGIN_EXTERN_C extern "C" { +#define ABSL_INTERNAL_END_EXTERN_C } // extern "C" +#define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F +#define ABSL_INTERNAL_STATIC_INLINE inline +#else +#define ABSL_INTERNAL_BEGIN_EXTERN_C // empty +#define ABSL_INTERNAL_END_EXTERN_C // empty +#define ABSL_INTERNAL_GLOBAL_SCOPED(F) F +#define ABSL_INTERNAL_STATIC_INLINE static inline +#endif + +// ------------------------------------------------------------------------- +// Define race annotations. + +#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1 +// Some of the symbols used in this section (e.g. AnnotateBenignRaceSized) are +// defined by the compiler-based santizer implementation, not by the Abseil +// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL. + +// ------------------------------------------------------------- +// Annotations that suppress errors. It is usually better to express the +// program's synchronization using the other annotations, but these can be used +// when all else fails. + +// Report that we may have a benign race at `pointer`, with size +// "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the +// point where `pointer` has been allocated, preferably close to the point +// where the race happens. See also ABSL_ANNOTATE_BENIGN_RACE_STATIC. +#define ABSL_ANNOTATE_BENIGN_RACE(pointer, description) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ + (__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) + +// Same as ABSL_ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to +// the memory range [`address`, `address`+`size`). +#define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ + (__FILE__, __LINE__, address, size, description) + +// Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads. +// This annotation could be useful if you want to skip expensive race analysis +// during some period of program execution, e.g. during initialization. +#define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \ + (__FILE__, __LINE__, enable) + +// ------------------------------------------------------------- +// Annotations useful for debugging. + +// Report the current thread `name` to a race detector. +#define ABSL_ANNOTATE_THREAD_NAME(name) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName)(__FILE__, __LINE__, name) + +// ------------------------------------------------------------- +// Annotations useful when implementing locks. They are not normally needed by +// modules that merely use locks. The `lock` argument is a pointer to the lock +// object. + +// Report that a lock has been created at address `lock`. +#define ABSL_ANNOTATE_RWLOCK_CREATE(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate)(__FILE__, __LINE__, lock) + +// Report that a linker initialized lock has been created at address `lock`. +#ifdef ABSL_HAVE_THREAD_SANITIZER +#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \ + (__FILE__, __LINE__, lock) +#else +#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ + ABSL_ANNOTATE_RWLOCK_CREATE(lock) +#endif + +// Report that the lock at address `lock` is about to be destroyed. +#define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock) + +// Report that the lock at address `lock` has been acquired. +// `is_w`=1 for writer lock, `is_w`=0 for reader lock. +#define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \ + (__FILE__, __LINE__, lock, is_w) + +// Report that the lock at address `lock` is about to be released. +// `is_w`=1 for writer lock, `is_w`=0 for reader lock. +#define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \ + (__FILE__, __LINE__, lock, is_w) + +// Apply ABSL_ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`. +#define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ + namespace { \ + class static_var##_annotator { \ + public: \ + static_var##_annotator() { \ + ABSL_ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \ + #static_var ": " description); \ + } \ + }; \ + static static_var##_annotator the##static_var##_annotator; \ + } // namespace + +// Function prototypes of annotations provided by the compiler-based sanitizer +// implementation. +ABSL_INTERNAL_BEGIN_EXTERN_C +void AnnotateRWLockCreate(const char* file, int line, + const volatile void* lock); +void AnnotateRWLockCreateStatic(const char* file, int line, + const volatile void* lock); +void AnnotateRWLockDestroy(const char* file, int line, + const volatile void* lock); +void AnnotateRWLockAcquired(const char* file, int line, + const volatile void* lock, long is_w); // NOLINT +void AnnotateRWLockReleased(const char* file, int line, + const volatile void* lock, long is_w); // NOLINT +void AnnotateBenignRace(const char* file, int line, + const volatile void* address, const char* description); +void AnnotateBenignRaceSized(const char* file, int line, + const volatile void* address, size_t size, + const char* description); +void AnnotateThreadName(const char* file, int line, const char* name); +void AnnotateEnableRaceDetection(const char* file, int line, int enable); +ABSL_INTERNAL_END_EXTERN_C + +#else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0 + +#define ABSL_ANNOTATE_RWLOCK_CREATE(lock) // empty +#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) // empty +#define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) // empty +#define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) // empty +#define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w) // empty +#define ABSL_ANNOTATE_BENIGN_RACE(address, description) // empty +#define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) // empty +#define ABSL_ANNOTATE_THREAD_NAME(name) // empty +#define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable) // empty +#define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) // empty + +#endif // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED + +// ------------------------------------------------------------------------- +// Define memory annotations. + +#ifdef ABSL_HAVE_MEMORY_SANITIZER + +#include + +#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + __msan_unpoison(address, size) + +#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + __msan_allocated_memory(address, size) + +#else // !defined(ABSL_HAVE_MEMORY_SANITIZER) + +// TODO(rogeeff): remove this branch +#ifdef ABSL_HAVE_THREAD_SANITIZER +#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + do { \ + (void)(address); \ + (void)(size); \ + } while (0) +#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + do { \ + (void)(address); \ + (void)(size); \ + } while (0) +#else + +#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) // empty +#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) // empty + +#endif + +#endif // ABSL_HAVE_MEMORY_SANITIZER + +// ------------------------------------------------------------------------- +// Define IGNORE_READS_BEGIN/_END attributes. + +#if defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \ + __attribute((exclusive_lock_function("*"))) +#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \ + __attribute((unlock_function("*"))) + +#else // !defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE // empty +#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE // empty + +#endif // defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +// ------------------------------------------------------------------------- +// Define IGNORE_READS_BEGIN/_END annotations. + +#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1 +// Some of the symbols used in this section (e.g. AnnotateIgnoreReadsBegin) are +// defined by the compiler-based implementation, not by the Abseil +// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL. + +// Request the analysis tool to ignore all reads in the current thread until +// ABSL_ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey +// reads, while still checking other reads and all writes. +// See also ABSL_ANNOTATE_UNPROTECTED_READ. +#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin) \ + (__FILE__, __LINE__) + +// Stop ignoring reads. +#define ABSL_ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd) \ + (__FILE__, __LINE__) + +// Function prototypes of annotations provided by the compiler-based sanitizer +// implementation. +ABSL_INTERNAL_BEGIN_EXTERN_C +void AnnotateIgnoreReadsBegin(const char* file, int line) + ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE; +void AnnotateIgnoreReadsEnd(const char* file, + int line) ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE; +ABSL_INTERNAL_END_EXTERN_C + +#elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED) + +// When Annotalysis is enabled without Dynamic Annotations, the use of +// static-inline functions allows the annotations to be read at compile-time, +// while still letting the compiler elide the functions from the final build. +// +// TODO(delesley) -- The exclusive lock here ignores writes as well, but +// allows IGNORE_READS_AND_WRITES to work properly. + +#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED( \ + ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsBegin)) \ + () + +#define ABSL_ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED( \ + ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsEnd)) \ + () + +ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL( + AbslInternalAnnotateIgnoreReadsBegin)() + ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE {} + +ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL( + AbslInternalAnnotateIgnoreReadsEnd)() + ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE {} + +#else + +#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() // empty +#define ABSL_ANNOTATE_IGNORE_READS_END() // empty + +#endif + +// ------------------------------------------------------------------------- +// Define IGNORE_WRITES_BEGIN/_END annotations. + +#if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1 + +// Similar to ABSL_ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. +#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__) + +// Stop ignoring writes. +#define ABSL_ANNOTATE_IGNORE_WRITES_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__) + +// Function prototypes of annotations provided by the compiler-based sanitizer +// implementation. +ABSL_INTERNAL_BEGIN_EXTERN_C +void AnnotateIgnoreWritesBegin(const char* file, int line); +void AnnotateIgnoreWritesEnd(const char* file, int line); +ABSL_INTERNAL_END_EXTERN_C + +#else + +#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() // empty +#define ABSL_ANNOTATE_IGNORE_WRITES_END() // empty + +#endif + +// ------------------------------------------------------------------------- +// Define the ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more +// primitive annotations defined above. +// +// Instead of doing +// ABSL_ANNOTATE_IGNORE_READS_BEGIN(); +// ... = x; +// ABSL_ANNOTATE_IGNORE_READS_END(); +// one can use +// ... = ABSL_ANNOTATE_UNPROTECTED_READ(x); + +#if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED) + +// Start ignoring all memory accesses (both reads and writes). +#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ + do { \ + ABSL_ANNOTATE_IGNORE_READS_BEGIN(); \ + ABSL_ANNOTATE_IGNORE_WRITES_BEGIN(); \ + } while (0) + +// Stop ignoring both reads and writes. +#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() \ + do { \ + ABSL_ANNOTATE_IGNORE_WRITES_END(); \ + ABSL_ANNOTATE_IGNORE_READS_END(); \ + } while (0) + +#ifdef __cplusplus +// ABSL_ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. +#define ABSL_ANNOTATE_UNPROTECTED_READ(x) \ + absl::base_internal::AnnotateUnprotectedRead(x) + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +template +inline T AnnotateUnprotectedRead(const volatile T& x) { // NOLINT + ABSL_ANNOTATE_IGNORE_READS_BEGIN(); + T res = x; + ABSL_ANNOTATE_IGNORE_READS_END(); + return res; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl +#endif + +#else + +#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() // empty +#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() // empty +#define ABSL_ANNOTATE_UNPROTECTED_READ(x) (x) + +#endif + +// ------------------------------------------------------------------------- +// Address sanitizer annotations + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +// Describe the current state of a contiguous container such as e.g. +// std::vector or std::string. For more details see +// sanitizer/common_interface_defs.h, which is provided by the compiler. +#include + +#define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \ + __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) +#define ABSL_ADDRESS_SANITIZER_REDZONE(name) \ + struct { \ + alignas(8) char x[8]; \ + } name + +#else + +#define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) // empty +#define ABSL_ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") + +#endif // ABSL_HAVE_ADDRESS_SANITIZER + +// ------------------------------------------------------------------------- +// Undefine the macros intended only for this file. + +#undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED +#undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_BEGIN_EXTERN_C +#undef ABSL_INTERNAL_END_EXTERN_C +#undef ABSL_INTERNAL_STATIC_INLINE + +#endif // ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ diff --git a/src/absl/base/internal/atomic_hook.h b/src/absl/base/internal/atomic_hook.h new file mode 100644 index 0000000..ae21cd7 --- /dev/null +++ b/src/absl/base/internal/atomic_hook.h @@ -0,0 +1,200 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ +#define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +#if defined(_MSC_VER) && !defined(__clang__) +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0 +#else +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1 +#endif + +#if defined(_MSC_VER) +#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0 +#else +#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +template +class AtomicHook; + +// To workaround AtomicHook not being constant-initializable on some platforms, +// prefer to annotate instances with `ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES` +// instead of `ABSL_CONST_INIT`. +#if ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT +#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_CONST_INIT +#else +#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +#endif + +// `AtomicHook` is a helper class, templatized on a raw function pointer type, +// for implementing Abseil customization hooks. It is a callable object that +// dispatches to the registered hook. Objects of type `AtomicHook` must have +// static or thread storage duration. +// +// A default constructed object performs a no-op (and returns a default +// constructed object) if no hook has been registered. +// +// Hooks can be pre-registered via constant initialization, for example: +// +// ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static AtomicHook +// my_hook(DefaultAction); +// +// and then changed at runtime via a call to `Store()`. +// +// Reads and writes guarantee memory_order_acquire/memory_order_release +// semantics. +template +class AtomicHook { + public: + using FnPtr = ReturnType (*)(Args...); + + // Constructs an object that by default performs a no-op (and + // returns a default constructed object) when no hook as been registered. + constexpr AtomicHook() : AtomicHook(DummyFunction) {} + + // Constructs an object that by default dispatches to/returns the + // pre-registered default_fn when no hook has been registered at runtime. +#if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT + explicit constexpr AtomicHook(FnPtr default_fn) + : hook_(default_fn), default_fn_(default_fn) {} +#elif ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT + explicit constexpr AtomicHook(FnPtr default_fn) + : hook_(kUninitialized), default_fn_(default_fn) {} +#else + // As of January 2020, on all known versions of MSVC this constructor runs in + // the global constructor sequence. If `Store()` is called by a dynamic + // initializer, we want to preserve the value, even if this constructor runs + // after the call to `Store()`. If not, `hook_` will be + // zero-initialized by the linker and we have no need to set it. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html + explicit constexpr AtomicHook(FnPtr default_fn) + : /* hook_(deliberately omitted), */ default_fn_(default_fn) { + static_assert(kUninitialized == 0, "here we rely on zero-initialization"); + } +#endif + + // Stores the provided function pointer as the value for this hook. + // + // This is intended to be called once. Multiple calls are legal only if the + // same function pointer is provided for each call. The store is implemented + // as a memory_order_release operation, and read accesses are implemented as + // memory_order_acquire. + void Store(FnPtr fn) { + bool success = DoStore(fn); + static_cast(success); + assert(success); + } + + // Invokes the registered callback. If no callback has yet been registered, a + // default-constructed object of the appropriate type is returned instead. + template + ReturnType operator()(CallArgs&&... args) const { + return DoLoad()(std::forward(args)...); + } + + // Returns the registered callback, or nullptr if none has been registered. + // Useful if client code needs to conditionalize behavior based on whether a + // callback was registered. + // + // Note that atomic_hook.Load()() and atomic_hook() have different semantics: + // operator()() will perform a no-op if no callback was registered, while + // Load()() will dereference a null function pointer. Prefer operator()() to + // Load()() unless you must conditionalize behavior on whether a hook was + // registered. + FnPtr Load() const { + FnPtr ptr = DoLoad(); + return (ptr == DummyFunction) ? nullptr : ptr; + } + + private: + static ReturnType DummyFunction(Args...) { + return ReturnType(); + } + + // Current versions of MSVC (as of September 2017) have a broken + // implementation of std::atomic: Its constructor attempts to do the + // equivalent of a reinterpret_cast in a constexpr context, which is not + // allowed. + // + // This causes an issue when building with LLVM under Windows. To avoid this, + // we use a less-efficient, intptr_t-based implementation on Windows. +#if ABSL_HAVE_WORKING_ATOMIC_POINTER + // Return the stored value, or DummyFunction if no value has been stored. + FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); } + + // Store the given value. Returns false if a different value was already + // stored to this object. + bool DoStore(FnPtr fn) { + assert(fn); + FnPtr expected = default_fn_; + const bool store_succeeded = hook_.compare_exchange_strong( + expected, fn, std::memory_order_acq_rel, std::memory_order_acquire); + const bool same_value_already_stored = (expected == fn); + return store_succeeded || same_value_already_stored; + } + + std::atomic hook_; +#else // !ABSL_HAVE_WORKING_ATOMIC_POINTER + // Use a sentinel value unlikely to be the address of an actual function. + static constexpr intptr_t kUninitialized = 0; + + static_assert(sizeof(intptr_t) >= sizeof(FnPtr), + "intptr_t can't contain a function pointer"); + + FnPtr DoLoad() const { + const intptr_t value = hook_.load(std::memory_order_acquire); + if (value == kUninitialized) { + return default_fn_; + } + return reinterpret_cast(value); + } + + bool DoStore(FnPtr fn) { + assert(fn); + const auto value = reinterpret_cast(fn); + intptr_t expected = kUninitialized; + const bool store_succeeded = hook_.compare_exchange_strong( + expected, value, std::memory_order_acq_rel, std::memory_order_acquire); + const bool same_value_already_stored = (expected == value); + return store_succeeded || same_value_already_stored; + } + + std::atomic hook_; +#endif + + const FnPtr default_fn_; +}; + +#undef ABSL_HAVE_WORKING_ATOMIC_POINTER +#undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ diff --git a/src/absl/base/internal/cycleclock.cc b/src/absl/base/internal/cycleclock.cc new file mode 100644 index 0000000..902e3f5 --- /dev/null +++ b/src/absl/base/internal/cycleclock.cc @@ -0,0 +1,77 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The implementation of CycleClock::Frequency. +// +// NOTE: only i386 and x86_64 have been well tested. +// PPC, sparc, alpha, and ia64 are based on +// http://peter.kuscsik.com/wordpress/?p=14 +// with modifications by m3b. See also +// https://setisvn.ssl.berkeley.edu/svn/lib/fftw-3.0.1/kernel/cycle.h + +#include "absl/base/internal/cycleclock.h" + +#include +#include // NOLINT(build/c++11) + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/unscaledcycleclock.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +#if ABSL_USE_UNSCALED_CYCLECLOCK + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +constexpr int32_t CycleClock::kShift; +constexpr double CycleClock::kFrequencyScale; +#endif + +ABSL_CONST_INIT std::atomic + CycleClock::cycle_clock_source_{nullptr}; + +void CycleClockSource::Register(CycleClockSourceFunc source) { + // Corresponds to the load(std::memory_order_acquire) in LoadCycleClockSource. + CycleClock::cycle_clock_source_.store(source, std::memory_order_release); +} + +#ifdef _WIN32 +int64_t CycleClock::Now() { + auto fn = LoadCycleClockSource(); + if (fn == nullptr) { + return base_internal::UnscaledCycleClock::Now() >> kShift; + } + return fn() >> kShift; +} +#endif + +#else + +int64_t CycleClock::Now() { + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); +} + +double CycleClock::Frequency() { + return 1e9; +} + +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/cycleclock.h b/src/absl/base/internal/cycleclock.h new file mode 100644 index 0000000..9704e38 --- /dev/null +++ b/src/absl/base/internal/cycleclock.h @@ -0,0 +1,159 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// ----------------------------------------------------------------------------- +// File: cycleclock.h +// ----------------------------------------------------------------------------- +// +// This header file defines a `CycleClock`, which yields the value and frequency +// of a cycle counter that increments at a rate that is approximately constant. +// +// NOTE: +// +// The cycle counter frequency is not necessarily related to the core clock +// frequency and should not be treated as such. That is, `CycleClock` cycles are +// not necessarily "CPU cycles" and code should not rely on that behavior, even +// if experimentally observed. +// +// An arbitrary offset may have been added to the counter at power on. +// +// On some platforms, the rate and offset of the counter may differ +// slightly when read from different CPUs of a multiprocessor. Usually, +// we try to ensure that the operating system adjusts values periodically +// so that values agree approximately. If you need stronger guarantees, +// consider using alternate interfaces. +// +// The CPU is not required to maintain the ordering of a cycle counter read +// with respect to surrounding instructions. + +#ifndef ABSL_BASE_INTERNAL_CYCLECLOCK_H_ +#define ABSL_BASE_INTERNAL_CYCLECLOCK_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/unscaledcycleclock.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +using CycleClockSourceFunc = int64_t (*)(); + +// ----------------------------------------------------------------------------- +// CycleClock +// ----------------------------------------------------------------------------- +class CycleClock { + public: + // CycleClock::Now() + // + // Returns the value of a cycle counter that counts at a rate that is + // approximately constant. + static int64_t Now(); + + // CycleClock::Frequency() + // + // Returns the amount by which `CycleClock::Now()` increases per second. Note + // that this value may not necessarily match the core CPU clock frequency. + static double Frequency(); + + private: +#if ABSL_USE_UNSCALED_CYCLECLOCK + static CycleClockSourceFunc LoadCycleClockSource(); + +#ifdef NDEBUG +#ifdef ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY + // Not debug mode and the UnscaledCycleClock frequency is the CPU + // frequency. Scale the CycleClock to prevent overflow if someone + // tries to represent the time as cycles since the Unix epoch. + static constexpr int32_t kShift = 1; +#else + // Not debug mode and the UnscaledCycleClock isn't operating at the + // raw CPU frequency. There is no need to do any scaling, so don't + // needlessly sacrifice precision. + static constexpr int32_t kShift = 0; +#endif +#else // NDEBUG + // In debug mode use a different shift to discourage depending on a + // particular shift value. + static constexpr int32_t kShift = 2; +#endif // NDEBUG + + static constexpr double kFrequencyScale = 1.0 / (1 << kShift); + ABSL_CONST_INIT static std::atomic cycle_clock_source_; +#endif // ABSL_USE_UNSCALED_CYCLECLOC + + CycleClock() = delete; // no instances + CycleClock(const CycleClock&) = delete; + CycleClock& operator=(const CycleClock&) = delete; + + friend class CycleClockSource; +}; + +class CycleClockSource { + private: + // CycleClockSource::Register() + // + // Register a function that provides an alternate source for the unscaled CPU + // cycle count value. The source function must be async signal safe, must not + // call CycleClock::Now(), and must have a frequency that matches that of the + // unscaled clock used by CycleClock. A nullptr value resets CycleClock to use + // the default source. + static void Register(CycleClockSourceFunc source); +}; + +#if ABSL_USE_UNSCALED_CYCLECLOCK + +inline CycleClockSourceFunc CycleClock::LoadCycleClockSource() { +#if !defined(__x86_64__) + // Optimize for the common case (no callback) by first doing a relaxed load; + // this is significantly faster on non-x86 platforms. + if (cycle_clock_source_.load(std::memory_order_relaxed) == nullptr) { + return nullptr; + } +#endif // !defined(__x86_64__) + + // This corresponds to the store(std::memory_order_release) in + // CycleClockSource::Register, and makes sure that any updates made prior to + // registering the callback are visible to this thread before the callback + // is invoked. + return cycle_clock_source_.load(std::memory_order_acquire); +} + +// Accessing globals in inlined code in Window DLLs is problematic. +#ifndef _WIN32 +inline int64_t CycleClock::Now() { + auto fn = LoadCycleClockSource(); + if (fn == nullptr) { + return base_internal::UnscaledCycleClock::Now() >> kShift; + } + return fn() >> kShift; +} +#endif + +inline double CycleClock::Frequency() { + return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency(); +} + +#endif // ABSL_USE_UNSCALED_CYCLECLOCK + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_CYCLECLOCK_H_ diff --git a/src/absl/base/internal/direct_mmap.h b/src/absl/base/internal/direct_mmap.h new file mode 100644 index 0000000..e492bb0 --- /dev/null +++ b/src/absl/base/internal/direct_mmap.h @@ -0,0 +1,169 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Functions for directly invoking mmap() via syscall, avoiding the case where +// mmap() has been locally overridden. + +#ifndef ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ +#define ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ + +#include "absl/base/config.h" + +#ifdef ABSL_HAVE_MMAP + +#include + +#ifdef __linux__ + +#include +#ifdef __BIONIC__ +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef __mips__ +// Include definitions of the ABI currently in use. +#if defined(__BIONIC__) || !defined(__GLIBC__) +// Android doesn't have sgidefs.h, but does have asm/sgidefs.h, which has the +// definitions we need. +#include +#else +#include +#endif // __BIONIC__ || !__GLIBC__ +#endif // __mips__ + +// SYS_mmap and SYS_munmap are not defined in Android. +#ifdef __BIONIC__ +extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); +#if defined(__NR_mmap) && !defined(SYS_mmap) +#define SYS_mmap __NR_mmap +#endif +#ifndef SYS_munmap +#define SYS_munmap __NR_munmap +#endif +#endif // __BIONIC__ + +#if defined(__NR_mmap2) && !defined(SYS_mmap2) +#define SYS_mmap2 __NR_mmap2 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Platform specific logic extracted from +// https://chromium.googlesource.com/linux-syscall-support/+/master/linux_syscall_support.h +inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, + off64_t offset) noexcept { +#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + defined(__m68k__) || defined(__sh__) || \ + (defined(__hppa__) && !defined(__LP64__)) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + (defined(__PPC__) && !defined(__PPC64__)) || \ + (defined(__riscv) && __riscv_xlen == 32) || \ + (defined(__s390__) && !defined(__s390x__)) || \ + (defined(__sparc__) && !defined(__arch64__)) + // On these architectures, implement mmap with mmap2. + static int pagesize = 0; + if (pagesize == 0) { +#if defined(__wasm__) || defined(__asmjs__) + pagesize = getpagesize(); +#else + pagesize = sysconf(_SC_PAGESIZE); +#endif + } + if (offset < 0 || offset % pagesize != 0) { + errno = EINVAL; + return MAP_FAILED; + } +#ifdef __BIONIC__ + // SYS_mmap2 has problems on Android API level <= 16. + // Workaround by invoking __mmap2() instead. + return __mmap2(start, length, prot, flags, fd, offset / pagesize); +#else + return reinterpret_cast( + syscall(SYS_mmap2, start, length, prot, flags, fd, + static_cast(offset / pagesize))); +#endif +#elif defined(__s390x__) + // On s390x, mmap() arguments are passed in memory. + unsigned long buf[6] = {reinterpret_cast(start), // NOLINT + static_cast(length), // NOLINT + static_cast(prot), // NOLINT + static_cast(flags), // NOLINT + static_cast(fd), // NOLINT + static_cast(offset)}; // NOLINT + return reinterpret_cast(syscall(SYS_mmap, buf)); +#elif defined(__x86_64__) +// The x32 ABI has 32 bit longs, but the syscall interface is 64 bit. +// We need to explicitly cast to an unsigned 64 bit type to avoid implicit +// sign extension. We can't cast pointers directly because those are +// 32 bits, and gcc will dump ugly warnings about casting from a pointer +// to an integer of a different size. We also need to make sure __off64_t +// isn't truncated to 32-bits under x32. +#define MMAP_SYSCALL_ARG(x) ((uint64_t)(uintptr_t)(x)) + return reinterpret_cast( + syscall(SYS_mmap, MMAP_SYSCALL_ARG(start), MMAP_SYSCALL_ARG(length), + MMAP_SYSCALL_ARG(prot), MMAP_SYSCALL_ARG(flags), + MMAP_SYSCALL_ARG(fd), static_cast(offset))); +#undef MMAP_SYSCALL_ARG +#else // Remaining 64-bit aritectures. + static_assert(sizeof(unsigned long) == 8, "Platform is not 64-bit"); + return reinterpret_cast( + syscall(SYS_mmap, start, length, prot, flags, fd, offset)); +#endif +} + +inline int DirectMunmap(void* start, size_t length) { + return static_cast(syscall(SYS_munmap, start, length)); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#else // !__linux__ + +// For non-linux platforms where we have mmap, just dispatch directly to the +// actual mmap()/munmap() methods. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return mmap(start, length, prot, flags, fd, offset); +} + +inline int DirectMunmap(void* start, size_t length) { + return munmap(start, length); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // __linux__ + +#endif // ABSL_HAVE_MMAP + +#endif // ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ diff --git a/src/absl/base/internal/dynamic_annotations.h b/src/absl/base/internal/dynamic_annotations.h new file mode 100644 index 0000000..b23c5ec --- /dev/null +++ b/src/absl/base/internal/dynamic_annotations.h @@ -0,0 +1,398 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file defines dynamic annotations for use with dynamic analysis tool +// such as valgrind, PIN, etc. +// +// Dynamic annotation is a source code annotation that affects the generated +// code (that is, the annotation is not a comment). Each such annotation is +// attached to a particular instruction and/or to a particular object (address) +// in the program. +// +// The annotations that should be used by users are macros in all upper-case +// (e.g., ANNOTATE_THREAD_NAME). +// +// Actual implementation of these macros may differ depending on the dynamic +// analysis tool being used. +// +// This file supports the following configurations: +// - Dynamic Annotations enabled (with static thread-safety warnings disabled). +// In this case, macros expand to functions implemented by Thread Sanitizer, +// when building with TSan. When not provided an external implementation, +// dynamic_annotations.cc provides no-op implementations. +// +// - Static Clang thread-safety warnings enabled. +// When building with a Clang compiler that supports thread-safety warnings, +// a subset of annotations can be statically-checked at compile-time. We +// expand these macros to static-inline functions that can be analyzed for +// thread-safety, but afterwards elided when building the final binary. +// +// - All annotations are disabled. +// If neither Dynamic Annotations nor Clang thread-safety warnings are +// enabled, then all annotation-macros expand to empty. + +#ifndef ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ +#define ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ + +#include + +#include "absl/base/config.h" + +// ------------------------------------------------------------------------- +// Decide which features are enabled + +#ifndef DYNAMIC_ANNOTATIONS_ENABLED +#define DYNAMIC_ANNOTATIONS_ENABLED 0 +#endif + +#if defined(__clang__) && !defined(SWIG) +#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1 +#endif + +#if DYNAMIC_ANNOTATIONS_ENABLED != 0 + +#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 +#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1 + +#else + +#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0 +#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0 +#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0 + +// Clang provides limited support for static thread-safety analysis through a +// feature called Annotalysis. We configure macro-definitions according to +// whether Annotalysis support is available. When running in opt-mode, GCC +// will issue a warning, if these attributes are compiled. Only include them +// when compiling using Clang. + +// ANNOTALYSIS_ENABLED == 1 when IGNORE_READ_ATTRIBUTE_ENABLED == 1 +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED \ + defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) +// Read/write annotations are enabled in Annotalysis mode; disabled otherwise. +#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \ + ABSL_INTERNAL_ANNOTALYSIS_ENABLED +#endif + +// Memory annotations are also made available to LLVM's Memory Sanitizer +#if defined(ABSL_HAVE_MEMORY_SANITIZER) && !defined(__native_client__) +#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 1 +#endif + +#ifndef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED +#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 0 +#endif + +#ifdef __cplusplus +#define ABSL_INTERNAL_BEGIN_EXTERN_C extern "C" { +#define ABSL_INTERNAL_END_EXTERN_C } // extern "C" +#define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F +#define ABSL_INTERNAL_STATIC_INLINE inline +#else +#define ABSL_INTERNAL_BEGIN_EXTERN_C // empty +#define ABSL_INTERNAL_END_EXTERN_C // empty +#define ABSL_INTERNAL_GLOBAL_SCOPED(F) F +#define ABSL_INTERNAL_STATIC_INLINE static inline +#endif + +// ------------------------------------------------------------------------- +// Define race annotations. + +#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1 + +// ------------------------------------------------------------- +// Annotations that suppress errors. It is usually better to express the +// program's synchronization using the other annotations, but these can be used +// when all else fails. + +// Report that we may have a benign race at `pointer`, with size +// "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the +// point where `pointer` has been allocated, preferably close to the point +// where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. +#define ANNOTATE_BENIGN_RACE(pointer, description) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ + (__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) + +// Same as ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to +// the memory range [`address`, `address`+`size`). +#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ + (__FILE__, __LINE__, address, size, description) + +// Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads. +// This annotation could be useful if you want to skip expensive race analysis +// during some period of program execution, e.g. during initialization. +#define ANNOTATE_ENABLE_RACE_DETECTION(enable) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \ + (__FILE__, __LINE__, enable) + +// ------------------------------------------------------------- +// Annotations useful for debugging. + +// Report the current thread `name` to a race detector. +#define ANNOTATE_THREAD_NAME(name) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName)(__FILE__, __LINE__, name) + +// ------------------------------------------------------------- +// Annotations useful when implementing locks. They are not normally needed by +// modules that merely use locks. The `lock` argument is a pointer to the lock +// object. + +// Report that a lock has been created at address `lock`. +#define ANNOTATE_RWLOCK_CREATE(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate)(__FILE__, __LINE__, lock) + +// Report that a linker initialized lock has been created at address `lock`. +#ifdef ABSL_HAVE_THREAD_SANITIZER +#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \ + (__FILE__, __LINE__, lock) +#else +#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock) +#endif + +// Report that the lock at address `lock` is about to be destroyed. +#define ANNOTATE_RWLOCK_DESTROY(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock) + +// Report that the lock at address `lock` has been acquired. +// `is_w`=1 for writer lock, `is_w`=0 for reader lock. +#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \ + (__FILE__, __LINE__, lock, is_w) + +// Report that the lock at address `lock` is about to be released. +// `is_w`=1 for writer lock, `is_w`=0 for reader lock. +#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \ + (__FILE__, __LINE__, lock, is_w) + +// Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`. +#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ + namespace { \ + class static_var##_annotator { \ + public: \ + static_var##_annotator() { \ + ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \ + #static_var ": " description); \ + } \ + }; \ + static static_var##_annotator the##static_var##_annotator; \ + } // namespace + +#else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0 + +#define ANNOTATE_RWLOCK_CREATE(lock) // empty +#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) // empty +#define ANNOTATE_RWLOCK_DESTROY(lock) // empty +#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) // empty +#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) // empty +#define ANNOTATE_BENIGN_RACE(address, description) // empty +#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) // empty +#define ANNOTATE_THREAD_NAME(name) // empty +#define ANNOTATE_ENABLE_RACE_DETECTION(enable) // empty +#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) // empty + +#endif // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED + +// ------------------------------------------------------------------------- +// Define memory annotations. + +#if ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 1 + +#include + +#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + __msan_unpoison(address, size) + +#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + __msan_allocated_memory(address, size) + +#else // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 0 + +#if DYNAMIC_ANNOTATIONS_ENABLED == 1 +#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + do { \ + (void)(address); \ + (void)(size); \ + } while (0) +#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + do { \ + (void)(address); \ + (void)(size); \ + } while (0) +#else +#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) // empty +#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) // empty +#endif + +#endif // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED + +// ------------------------------------------------------------------------- +// Define IGNORE_READS_BEGIN/_END attributes. + +#if defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \ + __attribute((exclusive_lock_function("*"))) +#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \ + __attribute((unlock_function("*"))) + +#else // !defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE // empty +#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE // empty + +#endif // defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +// ------------------------------------------------------------------------- +// Define IGNORE_READS_BEGIN/_END annotations. + +#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1 + +// Request the analysis tool to ignore all reads in the current thread until +// ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey +// reads, while still checking other reads and all writes. +// See also ANNOTATE_UNPROTECTED_READ. +#define ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__) + +// Stop ignoring reads. +#define ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__) + +#elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED) + +// When Annotalysis is enabled without Dynamic Annotations, the use of +// static-inline functions allows the annotations to be read at compile-time, +// while still letting the compiler elide the functions from the final build. +// +// TODO(delesley) -- The exclusive lock here ignores writes as well, but +// allows IGNORE_READS_AND_WRITES to work properly. + +#define ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)() + +#define ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)() + +#else + +#define ANNOTATE_IGNORE_READS_BEGIN() // empty +#define ANNOTATE_IGNORE_READS_END() // empty + +#endif + +// ------------------------------------------------------------------------- +// Define IGNORE_WRITES_BEGIN/_END annotations. + +#if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1 + +// Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. +#define ANNOTATE_IGNORE_WRITES_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__) + +// Stop ignoring writes. +#define ANNOTATE_IGNORE_WRITES_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__) + +#else + +#define ANNOTATE_IGNORE_WRITES_BEGIN() // empty +#define ANNOTATE_IGNORE_WRITES_END() // empty + +#endif + +// ------------------------------------------------------------------------- +// Define the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more +// primitive annotations defined above. +// +// Instead of doing +// ANNOTATE_IGNORE_READS_BEGIN(); +// ... = x; +// ANNOTATE_IGNORE_READS_END(); +// one can use +// ... = ANNOTATE_UNPROTECTED_READ(x); + +#if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED) + +// Start ignoring all memory accesses (both reads and writes). +#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ + do { \ + ANNOTATE_IGNORE_READS_BEGIN(); \ + ANNOTATE_IGNORE_WRITES_BEGIN(); \ + } while (0) + +// Stop ignoring both reads and writes. +#define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ + do { \ + ANNOTATE_IGNORE_WRITES_END(); \ + ANNOTATE_IGNORE_READS_END(); \ + } while (0) + +#ifdef __cplusplus +// ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. +#define ANNOTATE_UNPROTECTED_READ(x) \ + absl::base_internal::AnnotateUnprotectedRead(x) + +#endif + +#else + +#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() // empty +#define ANNOTATE_IGNORE_READS_AND_WRITES_END() // empty +#define ANNOTATE_UNPROTECTED_READ(x) (x) + +#endif + +// ------------------------------------------------------------------------- +// Address sanitizer annotations + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +// Describe the current state of a contiguous container such as e.g. +// std::vector or std::string. For more details see +// sanitizer/common_interface_defs.h, which is provided by the compiler. +#include + +#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \ + __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) +#define ADDRESS_SANITIZER_REDZONE(name) \ + struct { \ + char x[8] __attribute__((aligned(8))); \ + } name + +#else + +#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) +#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") + +#endif // ABSL_HAVE_ADDRESS_SANITIZER + +// ------------------------------------------------------------------------- +// Undefine the macros intended only for this file. + +#undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED +#undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_BEGIN_EXTERN_C +#undef ABSL_INTERNAL_END_EXTERN_C +#undef ABSL_INTERNAL_STATIC_INLINE + +#endif // ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ diff --git a/src/absl/base/internal/endian.h b/src/absl/base/internal/endian.h new file mode 100644 index 0000000..50747d7 --- /dev/null +++ b/src/absl/base/internal/endian.h @@ -0,0 +1,282 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_ENDIAN_H_ +#define ABSL_BASE_INTERNAL_ENDIAN_H_ + +#include +#include + +#include "absl/base/casts.h" +#include "absl/base/config.h" +#include "absl/base/internal/unaligned_access.h" +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +inline uint64_t gbswap_64(uint64_t host_int) { +#if ABSL_HAVE_BUILTIN(__builtin_bswap64) || defined(__GNUC__) + return __builtin_bswap64(host_int); +#elif defined(_MSC_VER) + return _byteswap_uint64(host_int); +#else + return (((host_int & uint64_t{0xFF}) << 56) | + ((host_int & uint64_t{0xFF00}) << 40) | + ((host_int & uint64_t{0xFF0000}) << 24) | + ((host_int & uint64_t{0xFF000000}) << 8) | + ((host_int & uint64_t{0xFF00000000}) >> 8) | + ((host_int & uint64_t{0xFF0000000000}) >> 24) | + ((host_int & uint64_t{0xFF000000000000}) >> 40) | + ((host_int & uint64_t{0xFF00000000000000}) >> 56)); +#endif +} + +inline uint32_t gbswap_32(uint32_t host_int) { +#if ABSL_HAVE_BUILTIN(__builtin_bswap32) || defined(__GNUC__) + return __builtin_bswap32(host_int); +#elif defined(_MSC_VER) + return _byteswap_ulong(host_int); +#else + return (((host_int & uint32_t{0xFF}) << 24) | + ((host_int & uint32_t{0xFF00}) << 8) | + ((host_int & uint32_t{0xFF0000}) >> 8) | + ((host_int & uint32_t{0xFF000000}) >> 24)); +#endif +} + +inline uint16_t gbswap_16(uint16_t host_int) { +#if ABSL_HAVE_BUILTIN(__builtin_bswap16) || defined(__GNUC__) + return __builtin_bswap16(host_int); +#elif defined(_MSC_VER) + return _byteswap_ushort(host_int); +#else + return (((host_int & uint16_t{0xFF}) << 8) | + ((host_int & uint16_t{0xFF00}) >> 8)); +#endif +} + +#ifdef ABSL_IS_LITTLE_ENDIAN + +// Portable definitions for htonl (host-to-network) and friends on little-endian +// architectures. +inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); } +inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); } +inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); } + +#elif defined ABSL_IS_BIG_ENDIAN + +// Portable definitions for htonl (host-to-network) etc on big-endian +// architectures. These definitions are simpler since the host byte order is the +// same as network byte order. +inline uint16_t ghtons(uint16_t x) { return x; } +inline uint32_t ghtonl(uint32_t x) { return x; } +inline uint64_t ghtonll(uint64_t x) { return x; } + +#else +#error \ + "Unsupported byte order: Either ABSL_IS_BIG_ENDIAN or " \ + "ABSL_IS_LITTLE_ENDIAN must be defined" +#endif // byte order + +inline uint16_t gntohs(uint16_t x) { return ghtons(x); } +inline uint32_t gntohl(uint32_t x) { return ghtonl(x); } +inline uint64_t gntohll(uint64_t x) { return ghtonll(x); } + +// Utilities to convert numbers between the current hosts's native byte +// order and little-endian byte order +// +// Load/Store methods are alignment safe +namespace little_endian { +// Conversion functions. +#ifdef ABSL_IS_LITTLE_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return x; } +inline uint16_t ToHost16(uint16_t x) { return x; } + +inline uint32_t FromHost32(uint32_t x) { return x; } +inline uint32_t ToHost32(uint32_t x) { return x; } + +inline uint64_t FromHost64(uint64_t x) { return x; } +inline uint64_t ToHost64(uint64_t x) { return x; } + +inline constexpr bool IsLittleEndian() { return true; } + +#elif defined ABSL_IS_BIG_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); } +inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); } + +inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); } +inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); } + +inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); } +inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); } + +inline constexpr bool IsLittleEndian() { return false; } + +#endif /* ENDIAN */ + +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast(FromHost16(bit_cast(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast(FromHost32(bit_cast(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast(FromHost64(bit_cast(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast(ToHost16(bit_cast(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast(ToHost32(bit_cast(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast(ToHost64(bit_cast(x))); +} + +// Functions to do unaligned loads and stores in little-endian order. +inline uint16_t Load16(const void *p) { + return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); +} + +inline void Store16(void *p, uint16_t v) { + ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); +} + +inline uint32_t Load32(const void *p) { + return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); +} + +inline void Store32(void *p, uint32_t v) { + ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); +} + +inline uint64_t Load64(const void *p) { + return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); +} + +inline void Store64(void *p, uint64_t v) { + ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); +} + +} // namespace little_endian + +// Utilities to convert numbers between the current hosts's native byte +// order and big-endian byte order (same as network byte order) +// +// Load/Store methods are alignment safe +namespace big_endian { +#ifdef ABSL_IS_LITTLE_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); } +inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); } + +inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); } +inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); } + +inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); } +inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); } + +inline constexpr bool IsLittleEndian() { return true; } + +#elif defined ABSL_IS_BIG_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return x; } +inline uint16_t ToHost16(uint16_t x) { return x; } + +inline uint32_t FromHost32(uint32_t x) { return x; } +inline uint32_t ToHost32(uint32_t x) { return x; } + +inline uint64_t FromHost64(uint64_t x) { return x; } +inline uint64_t ToHost64(uint64_t x) { return x; } + +inline constexpr bool IsLittleEndian() { return false; } + +#endif /* ENDIAN */ + +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast(FromHost16(bit_cast(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast(FromHost32(bit_cast(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast(FromHost64(bit_cast(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast(ToHost16(bit_cast(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast(ToHost32(bit_cast(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast(ToHost64(bit_cast(x))); +} + +// Functions to do unaligned loads and stores in big-endian order. +inline uint16_t Load16(const void *p) { + return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); +} + +inline void Store16(void *p, uint16_t v) { + ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); +} + +inline uint32_t Load32(const void *p) { + return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); +} + +inline void Store32(void *p, uint32_t v) { + ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); +} + +inline uint64_t Load64(const void *p) { + return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); +} + +inline void Store64(void *p, uint64_t v) { + ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); +} + +} // namespace big_endian + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ENDIAN_H_ diff --git a/src/absl/base/internal/errno_saver.h b/src/absl/base/internal/errno_saver.h new file mode 100644 index 0000000..251de51 --- /dev/null +++ b/src/absl/base/internal/errno_saver.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ +#define ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// `ErrnoSaver` captures the value of `errno` upon construction and restores it +// upon deletion. It is used in low-level code and must be super fast. Do not +// add instrumentation, even in debug modes. +class ErrnoSaver { + public: + ErrnoSaver() : saved_errno_(errno) {} + ~ErrnoSaver() { errno = saved_errno_; } + int operator()() const { return saved_errno_; } + + private: + const int saved_errno_; +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ diff --git a/src/absl/base/internal/fast_type_id.h b/src/absl/base/internal/fast_type_id.h new file mode 100644 index 0000000..a547b3a --- /dev/null +++ b/src/absl/base/internal/fast_type_id.h @@ -0,0 +1,50 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ +#define ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +template +struct FastTypeTag { + constexpr static char dummy_var = 0; +}; + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +template +constexpr char FastTypeTag::dummy_var; +#endif + +// FastTypeId() evaluates at compile/link-time to a unique pointer for the +// passed-in type. These are meant to be good match for keys into maps or +// straight up comparisons. +using FastTypeIdType = const void*; + +template +constexpr inline FastTypeIdType FastTypeId() { + return &FastTypeTag::dummy_var; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ diff --git a/src/absl/base/internal/hide_ptr.h b/src/absl/base/internal/hide_ptr.h new file mode 100644 index 0000000..1dba809 --- /dev/null +++ b/src/absl/base/internal/hide_ptr.h @@ -0,0 +1,51 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_HIDE_PTR_H_ +#define ABSL_BASE_INTERNAL_HIDE_PTR_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Arbitrary value with high bits set. Xor'ing with it is unlikely +// to map one valid pointer to another valid pointer. +constexpr uintptr_t HideMask() { + return (uintptr_t{0xF03A5F7BU} << (sizeof(uintptr_t) - 4) * 8) | 0xF03A5F7BU; +} + +// Hide a pointer from the leak checker. For internal use only. +// Differs from absl::IgnoreLeak(ptr) in that absl::IgnoreLeak(ptr) causes ptr +// and all objects reachable from ptr to be ignored by the leak checker. +template +inline uintptr_t HidePtr(T* ptr) { + return reinterpret_cast(ptr) ^ HideMask(); +} + +// Return a pointer that has been hidden from the leak checker. +// For internal use only. +template +inline T* UnhidePtr(uintptr_t hidden) { + return reinterpret_cast(hidden ^ HideMask()); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_ diff --git a/src/absl/base/internal/identity.h b/src/absl/base/internal/identity.h new file mode 100644 index 0000000..a3154ed --- /dev/null +++ b/src/absl/base/internal/identity.h @@ -0,0 +1,37 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_IDENTITY_H_ +#define ABSL_BASE_INTERNAL_IDENTITY_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace internal { + +template +struct identity { + typedef T type; +}; + +template +using identity_t = typename identity::type; + +} // namespace internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_IDENTITY_H_ diff --git a/src/absl/base/internal/inline_variable.h b/src/absl/base/internal/inline_variable.h new file mode 100644 index 0000000..130d8c2 --- /dev/null +++ b/src/absl/base/internal/inline_variable.h @@ -0,0 +1,107 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ +#define ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ + +#include + +#include "absl/base/internal/identity.h" + +// File: +// This file define a macro that allows the creation of or emulation of C++17 +// inline variables based on whether or not the feature is supported. + +//////////////////////////////////////////////////////////////////////////////// +// Macro: ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) +// +// Description: +// Expands to the equivalent of an inline constexpr instance of the specified +// `type` and `name`, initialized to the value `init`. If the compiler being +// used is detected as supporting actual inline variables as a language +// feature, then the macro expands to an actual inline variable definition. +// +// Requires: +// `type` is a type that is usable in an extern variable declaration. +// +// Requires: `name` is a valid identifier +// +// Requires: +// `init` is an expression that can be used in the following definition: +// constexpr type name = init; +// +// Usage: +// +// // Equivalent to: `inline constexpr size_t variant_npos = -1;` +// ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1); +// +// Differences in implementation: +// For a direct, language-level inline variable, decltype(name) will be the +// type that was specified along with const qualification, whereas for +// emulated inline variables, decltype(name) may be different (in practice +// it will likely be a reference type). +//////////////////////////////////////////////////////////////////////////////// + +#ifdef __cpp_inline_variables + +// Clang's -Wmissing-variable-declarations option erroneously warned that +// inline constexpr objects need to be pre-declared. This has now been fixed, +// but we will need to support this workaround for people building with older +// versions of clang. +// +// Bug: https://bugs.llvm.org/show_bug.cgi?id=35862 +// +// Note: +// identity_t is used here so that the const and name are in the +// appropriate place for pointer types, reference types, function pointer +// types, etc.. +#if defined(__clang__) +#define ABSL_INTERNAL_EXTERN_DECL(type, name) \ + extern const ::absl::internal::identity_t name; +#else // Otherwise, just define the macro to do nothing. +#define ABSL_INTERNAL_EXTERN_DECL(type, name) +#endif // defined(__clang__) + +// See above comment at top of file for details. +#define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \ + ABSL_INTERNAL_EXTERN_DECL(type, name) \ + inline constexpr ::absl::internal::identity_t name = init + +#else + +// See above comment at top of file for details. +// +// Note: +// identity_t is used here so that the const and name are in the +// appropriate place for pointer types, reference types, function pointer +// types, etc.. +#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init) \ + template \ + struct AbslInternalInlineVariableHolder##name { \ + static constexpr ::absl::internal::identity_t kInstance = init; \ + }; \ + \ + template \ + constexpr ::absl::internal::identity_t \ + AbslInternalInlineVariableHolder##name::kInstance; \ + \ + static constexpr const ::absl::internal::identity_t& \ + name = /* NOLINT */ \ + AbslInternalInlineVariableHolder##name<>::kInstance; \ + static_assert(sizeof(void (*)(decltype(name))) != 0, \ + "Silence unused variable warnings.") + +#endif // __cpp_inline_variables + +#endif // ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ diff --git a/src/absl/base/internal/invoke.h b/src/absl/base/internal/invoke.h new file mode 100644 index 0000000..80befbb --- /dev/null +++ b/src/absl/base/internal/invoke.h @@ -0,0 +1,241 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// absl::base_internal::invoke(f, args...) is an implementation of +// INVOKE(f, args...) from section [func.require] of the C++ standard. +// When compiled as C++17 and later versions, it is implemented as an alias of +// std::invoke. +// +// [func.require] +// Define INVOKE (f, t1, t2, ..., tN) as follows: +// 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T +// and t1 is an object of type T or a reference to an object of type T or a +// reference to an object of a type derived from T; +// 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and t1 is not one of the types described in the previous item; +// 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is +// an object of type T or a reference to an object of type T or a reference +// to an object of a type derived from T; +// 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 +// is not one of the types described in the previous item; +// 5. f(t1, t2, ..., tN) in all other cases. +// +// The implementation is SFINAE-friendly: substitution failure within invoke() +// isn't an error. + +#ifndef ABSL_BASE_INTERNAL_INVOKE_H_ +#define ABSL_BASE_INTERNAL_INVOKE_H_ + +#include "absl/base/config.h" + +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +#include + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +using std::invoke; +using std::invoke_result_t; +using std::is_invocable_r; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#else // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +#include +#include +#include + +#include "absl/meta/type_traits.h" + +// The following code is internal implementation detail. See the comment at the +// top of this file for the API documentation. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// The five classes below each implement one of the clauses from the definition +// of INVOKE. The inner class template Accept checks whether the +// clause is applicable; static function template Invoke(f, args...) does the +// invocation. +// +// By separating the clause selection logic from invocation we make sure that +// Invoke() does exactly what the standard says. + +template +struct StrippedAccept { + template + struct Accept : Derived::template AcceptImpl::type>::type...> {}; +}; + +// (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T +// and t1 is an object of type T or a reference to an object of type T or a +// reference to an object of a type derived from T. +struct MemFunAndRef : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + absl::is_function::value> { + }; + + template + static decltype((std::declval().* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) { +// Ignore bogus GCC warnings on this line. +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101436 for similar example. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) +// #pragma GCC diagnostic push +// #pragma GCC diagnostic ignored "-Warray-bounds" +// #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + return (std::forward(obj).* + std::forward(mem_fun))(std::forward(args)...); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) +// #pragma GCC diagnostic pop +#endif + } +}; + +// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and t1 is not one of the types described in the previous item. +struct MemFunAndPtr : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + absl::is_function::value> { + }; + + template + static decltype(((*std::declval()).* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) { + return ((*std::forward(ptr)).* + std::forward(mem_fun))(std::forward(args)...); + } +}; + +// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is +// an object of type T or a reference to an object of type T or a reference +// to an object of a type derived from T. +struct DataMemAndRef : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + !absl::is_function::value> {}; + + template + static decltype(std::declval().*std::declval()) Invoke( + DataMem&& data_mem, Ref&& ref) { + return std::forward(ref).*std::forward(data_mem); + } +}; + +// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 +// is not one of the types described in the previous item. +struct DataMemAndPtr : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + !absl::is_function::value> {}; + + template + static decltype((*std::declval()).*std::declval()) Invoke( + DataMem&& data_mem, Ptr&& ptr) { + return (*std::forward(ptr)).*std::forward(data_mem); + } +}; + +// f(t1, t2, ..., tN) in all other cases. +struct Callable { + // Callable doesn't have Accept because it's the last clause that gets picked + // when none of the previous clauses are applicable. + template + static decltype(std::declval()(std::declval()...)) Invoke( + F&& f, Args&&... args) { + return std::forward(f)(std::forward(args)...); + } +}; + +// Resolves to the first matching clause. +template +struct Invoker { + typedef typename std::conditional< + MemFunAndRef::Accept::value, MemFunAndRef, + typename std::conditional< + MemFunAndPtr::Accept::value, MemFunAndPtr, + typename std::conditional< + DataMemAndRef::Accept::value, DataMemAndRef, + typename std::conditional::value, + DataMemAndPtr, Callable>::type>::type>:: + type>::type type; +}; + +// The result type of Invoke. +template +using invoke_result_t = decltype(Invoker::type::Invoke( + std::declval(), std::declval()...)); + +// Invoke(f, args...) is an implementation of INVOKE(f, args...) from section +// [func.require] of the C++ standard. +template +invoke_result_t invoke(F&& f, Args&&... args) { + return Invoker::type::Invoke(std::forward(f), + std::forward(args)...); +} + +template +struct IsInvocableRImpl : std::false_type {}; + +template +struct IsInvocableRImpl< + absl::void_t >, R, F, + Args...> + : std::integral_constant< + bool, + std::is_convertible, + R>::value || + std::is_void::value> {}; + +// Type trait whose member `value` is true if invoking `F` with `Args` is valid, +// and either the return type is convertible to `R`, or `R` is void. +// C++11-compatible version of `std::is_invocable_r`. +template +using is_invocable_r = IsInvocableRImpl; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +#endif // ABSL_BASE_INTERNAL_INVOKE_H_ diff --git a/src/absl/base/internal/low_level_alloc.cc b/src/absl/base/internal/low_level_alloc.cc new file mode 100644 index 0000000..229ab91 --- /dev/null +++ b/src/absl/base/internal/low_level_alloc.cc @@ -0,0 +1,620 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A low-level allocator that can be used by other low-level +// modules without introducing dependency cycles. +// This allocator is slow and wasteful of memory; +// it should not be used when performance is key. + +#include "absl/base/internal/low_level_alloc.h" + +#include + +#include "absl/base/call_once.h" +#include "absl/base/config.h" +#include "absl/base/internal/direct_mmap.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/macros.h" +#include "absl/base/thread_annotations.h" + +// LowLevelAlloc requires that the platform support low-level +// allocation of virtual memory. Platforms lacking this cannot use +// LowLevelAlloc. +#ifndef ABSL_LOW_LEVEL_ALLOC_MISSING + +#ifndef _WIN32 +#include +#include +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include // for placement-new + +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" + +// MAP_ANONYMOUS +#if defined(__APPLE__) +// For mmap, Linux defines both MAP_ANONYMOUS and MAP_ANON and says MAP_ANON is +// deprecated. In Darwin, MAP_ANON is all there is. +#if !defined MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif // !MAP_ANONYMOUS +#endif // __APPLE__ + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// A first-fit allocator with amortized logarithmic free() time. + +// --------------------------------------------------------------------------- +static const int kMaxLevel = 30; + +namespace { +// This struct describes one allocated block, or one free block. +struct AllocList { + struct Header { + // Size of entire region, including this field. Must be + // first. Valid in both allocated and unallocated blocks. + uintptr_t size; + + // kMagicAllocated or kMagicUnallocated xor this. + uintptr_t magic; + + // Pointer to parent arena. + LowLevelAlloc::Arena *arena; + + // Aligns regions to 0 mod 2*sizeof(void*). + void *dummy_for_alignment; + } header; + + // Next two fields: in unallocated blocks: freelist skiplist data + // in allocated blocks: overlaps with client data + + // Levels in skiplist used. + int levels; + + // Actually has levels elements. The AllocList node may not have room + // for all kMaxLevel entries. See max_fit in LLA_SkiplistLevels(). + AllocList *next[kMaxLevel]; +}; +} // namespace + +// --------------------------------------------------------------------------- +// A trivial skiplist implementation. This is used to keep the freelist +// in address order while taking only logarithmic time per insert and delete. + +// An integer approximation of log2(size/base) +// Requires size >= base. +static int IntLog2(size_t size, size_t base) { + int result = 0; + for (size_t i = size; i > base; i >>= 1) { // i == floor(size/2**result) + result++; + } + // floor(size / 2**result) <= base < floor(size / 2**(result-1)) + // => log2(size/(base+1)) <= result < 1+log2(size/base) + // => result ~= log2(size/base) + return result; +} + +// Return a random integer n: p(n)=1/(2**n) if 1 <= n; p(n)=0 if n < 1. +static int Random(uint32_t *state) { + uint32_t r = *state; + int result = 1; + while ((((r = r*1103515245 + 12345) >> 30) & 1) == 0) { + result++; + } + *state = r; + return result; +} + +// Return a number of skiplist levels for a node of size bytes, where +// base is the minimum node size. Compute level=log2(size / base)+n +// where n is 1 if random is false and otherwise a random number generated with +// the standard distribution for a skiplist: See Random() above. +// Bigger nodes tend to have more skiplist levels due to the log2(size / base) +// term, so first-fit searches touch fewer nodes. "level" is clipped so +// level(level) > max_fit) level = static_cast(max_fit); + if (level > kMaxLevel-1) level = kMaxLevel - 1; + ABSL_RAW_CHECK(level >= 1, "block not big enough for even one level"); + return level; +} + +// Return "atleast", the first element of AllocList *head s.t. *atleast >= *e. +// For 0 <= i < head->levels, set prev[i] to "no_greater", where no_greater +// points to the last element at level i in the AllocList less than *e, or is +// head if no such element exists. +static AllocList *LLA_SkiplistSearch(AllocList *head, + AllocList *e, AllocList **prev) { + AllocList *p = head; + for (int level = head->levels - 1; level >= 0; level--) { + for (AllocList *n; (n = p->next[level]) != nullptr && n < e; p = n) { + } + prev[level] = p; + } + return (head->levels == 0) ? nullptr : prev[0]->next[0]; +} + +// Insert element *e into AllocList *head. Set prev[] as LLA_SkiplistSearch. +// Requires that e->levels be previously set by the caller (using +// LLA_SkiplistLevels()) +static void LLA_SkiplistInsert(AllocList *head, AllocList *e, + AllocList **prev) { + LLA_SkiplistSearch(head, e, prev); + for (; head->levels < e->levels; head->levels++) { // extend prev pointers + prev[head->levels] = head; // to all *e's levels + } + for (int i = 0; i != e->levels; i++) { // add element to list + e->next[i] = prev[i]->next[i]; + prev[i]->next[i] = e; + } +} + +// Remove element *e from AllocList *head. Set prev[] as LLA_SkiplistSearch(). +// Requires that e->levels be previous set by the caller (using +// LLA_SkiplistLevels()) +static void LLA_SkiplistDelete(AllocList *head, AllocList *e, + AllocList **prev) { + AllocList *found = LLA_SkiplistSearch(head, e, prev); + ABSL_RAW_CHECK(e == found, "element not in freelist"); + for (int i = 0; i != e->levels && prev[i]->next[i] == e; i++) { + prev[i]->next[i] = e->next[i]; + } + while (head->levels > 0 && head->next[head->levels - 1] == nullptr) { + head->levels--; // reduce head->levels if level unused + } +} + +// --------------------------------------------------------------------------- +// Arena implementation + +// Metadata for an LowLevelAlloc arena instance. +struct LowLevelAlloc::Arena { + // Constructs an arena with the given LowLevelAlloc flags. + explicit Arena(uint32_t flags_value); + + base_internal::SpinLock mu; + // Head of free list, sorted by address + AllocList freelist ABSL_GUARDED_BY(mu); + // Count of allocated blocks + int32_t allocation_count ABSL_GUARDED_BY(mu); + // flags passed to NewArena + const uint32_t flags; + // Result of sysconf(_SC_PAGESIZE) + const size_t pagesize; + // Lowest power of two >= max(16, sizeof(AllocList)) + const size_t round_up; + // Smallest allocation block size + const size_t min_size; + // PRNG state + uint32_t random ABSL_GUARDED_BY(mu); +}; + +namespace { +// Static storage space for the lazily-constructed, default global arena +// instances. We require this space because the whole point of LowLevelAlloc +// is to avoid relying on malloc/new. +alignas(LowLevelAlloc::Arena) unsigned char default_arena_storage[sizeof( + LowLevelAlloc::Arena)]; +alignas(LowLevelAlloc::Arena) unsigned char unhooked_arena_storage[sizeof( + LowLevelAlloc::Arena)]; +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING +alignas( + LowLevelAlloc::Arena) unsigned char unhooked_async_sig_safe_arena_storage + [sizeof(LowLevelAlloc::Arena)]; +#endif + +// We must use LowLevelCallOnce here to construct the global arenas, rather than +// using function-level statics, to avoid recursively invoking the scheduler. +absl::once_flag create_globals_once; + +void CreateGlobalArenas() { + new (&default_arena_storage) + LowLevelAlloc::Arena(LowLevelAlloc::kCallMallocHook); + new (&unhooked_arena_storage) LowLevelAlloc::Arena(0); +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING + new (&unhooked_async_sig_safe_arena_storage) + LowLevelAlloc::Arena(LowLevelAlloc::kAsyncSignalSafe); +#endif +} + +// Returns a global arena that does not call into hooks. Used by NewArena() +// when kCallMallocHook is not set. +LowLevelAlloc::Arena* UnhookedArena() { + base_internal::LowLevelCallOnce(&create_globals_once, CreateGlobalArenas); + return reinterpret_cast(&unhooked_arena_storage); +} + +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING +// Returns a global arena that is async-signal safe. Used by NewArena() when +// kAsyncSignalSafe is set. +LowLevelAlloc::Arena *UnhookedAsyncSigSafeArena() { + base_internal::LowLevelCallOnce(&create_globals_once, CreateGlobalArenas); + return reinterpret_cast( + &unhooked_async_sig_safe_arena_storage); +} +#endif + +} // namespace + +// Returns the default arena, as used by LowLevelAlloc::Alloc() and friends. +LowLevelAlloc::Arena *LowLevelAlloc::DefaultArena() { + base_internal::LowLevelCallOnce(&create_globals_once, CreateGlobalArenas); + return reinterpret_cast(&default_arena_storage); +} + +// magic numbers to identify allocated and unallocated blocks +static const uintptr_t kMagicAllocated = 0x4c833e95U; +static const uintptr_t kMagicUnallocated = ~kMagicAllocated; + +namespace { +class ABSL_SCOPED_LOCKABLE ArenaLock { + public: + explicit ArenaLock(LowLevelAlloc::Arena *arena) + ABSL_EXCLUSIVE_LOCK_FUNCTION(arena->mu) + : arena_(arena) { +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING + if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { + sigset_t all; + sigfillset(&all); + mask_valid_ = pthread_sigmask(SIG_BLOCK, &all, &mask_) == 0; + } +#endif + arena_->mu.Lock(); + } + ~ArenaLock() { ABSL_RAW_CHECK(left_, "haven't left Arena region"); } + void Leave() ABSL_UNLOCK_FUNCTION() { + arena_->mu.Unlock(); +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING + if (mask_valid_) { + const int err = pthread_sigmask(SIG_SETMASK, &mask_, nullptr); + if (err != 0) { + ABSL_RAW_LOG(FATAL, "pthread_sigmask failed: %d", err); + } + } +#endif + left_ = true; + } + + private: + bool left_ = false; // whether left region +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING + bool mask_valid_ = false; + sigset_t mask_; // old mask of blocked signals +#endif + LowLevelAlloc::Arena *arena_; + ArenaLock(const ArenaLock &) = delete; + ArenaLock &operator=(const ArenaLock &) = delete; +}; +} // namespace + +// create an appropriate magic number for an object at "ptr" +// "magic" should be kMagicAllocated or kMagicUnallocated +inline static uintptr_t Magic(uintptr_t magic, AllocList::Header *ptr) { + return magic ^ reinterpret_cast(ptr); +} + +namespace { +size_t GetPageSize() { +#ifdef _WIN32 + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + return std::max(system_info.dwPageSize, system_info.dwAllocationGranularity); +#elif defined(__wasm__) || defined(__asmjs__) + return getpagesize(); +#else + return sysconf(_SC_PAGESIZE); +#endif +} + +size_t RoundedUpBlockSize() { + // Round up block sizes to a power of two close to the header size. + size_t round_up = 16; + while (round_up < sizeof(AllocList::Header)) { + round_up += round_up; + } + return round_up; +} + +} // namespace + +LowLevelAlloc::Arena::Arena(uint32_t flags_value) + : mu(base_internal::SCHEDULE_KERNEL_ONLY), + allocation_count(0), + flags(flags_value), + pagesize(GetPageSize()), + round_up(RoundedUpBlockSize()), + min_size(2 * round_up), + random(0) { + freelist.header.size = 0; + freelist.header.magic = + Magic(kMagicUnallocated, &freelist.header); + freelist.header.arena = this; + freelist.levels = 0; + memset(freelist.next, 0, sizeof(freelist.next)); +} + +// L < meta_data_arena->mu +LowLevelAlloc::Arena *LowLevelAlloc::NewArena(int32_t flags) { + Arena *meta_data_arena = DefaultArena(); +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING + if ((flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { + meta_data_arena = UnhookedAsyncSigSafeArena(); + } else // NOLINT(readability/braces) +#endif + if ((flags & LowLevelAlloc::kCallMallocHook) == 0) { + meta_data_arena = UnhookedArena(); + } + Arena *result = + new (AllocWithArena(sizeof (*result), meta_data_arena)) Arena(flags); + return result; +} + +// L < arena->mu, L < arena->arena->mu +bool LowLevelAlloc::DeleteArena(Arena *arena) { + ABSL_RAW_CHECK( + arena != nullptr && arena != DefaultArena() && arena != UnhookedArena(), + "may not delete default arena"); + ArenaLock section(arena); + if (arena->allocation_count != 0) { + section.Leave(); + return false; + } + while (arena->freelist.next[0] != nullptr) { + AllocList *region = arena->freelist.next[0]; + size_t size = region->header.size; + arena->freelist.next[0] = region->next[0]; + ABSL_RAW_CHECK( + region->header.magic == Magic(kMagicUnallocated, ®ion->header), + "bad magic number in DeleteArena()"); + ABSL_RAW_CHECK(region->header.arena == arena, + "bad arena pointer in DeleteArena()"); + ABSL_RAW_CHECK(size % arena->pagesize == 0, + "empty arena has non-page-aligned block size"); + ABSL_RAW_CHECK(reinterpret_cast(region) % arena->pagesize == 0, + "empty arena has non-page-aligned block"); + int munmap_result; +#ifdef _WIN32 + munmap_result = VirtualFree(region, 0, MEM_RELEASE); + 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 // _WIN32 + } + section.Leave(); + arena->~Arena(); + Free(arena); + return true; +} + +// --------------------------------------------------------------------------- + +// Addition, checking for overflow. The intent is to die if an external client +// manages to push through a request that would cause arithmetic to fail. +static inline uintptr_t CheckedAdd(uintptr_t a, uintptr_t b) { + uintptr_t sum = a + b; + ABSL_RAW_CHECK(sum >= a, "LowLevelAlloc arithmetic overflow"); + return sum; +} + +// Return value rounded up to next multiple of align. +// align must be a power of two. +static inline uintptr_t RoundUp(uintptr_t addr, uintptr_t align) { + return CheckedAdd(addr, align - 1) & ~(align - 1); +} + +// Equivalent to "return prev->next[i]" but with sanity checking +// that the freelist is in the correct order, that it +// consists of regions marked "unallocated", and that no two regions +// are adjacent in memory (they should have been coalesced). +// L >= arena->mu +static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) { + ABSL_RAW_CHECK(i < prev->levels, "too few levels in Next()"); + AllocList *next = prev->next[i]; + if (next != nullptr) { + ABSL_RAW_CHECK( + next->header.magic == Magic(kMagicUnallocated, &next->header), + "bad magic number in Next()"); + ABSL_RAW_CHECK(next->header.arena == arena, "bad arena pointer in Next()"); + if (prev != &arena->freelist) { + ABSL_RAW_CHECK(prev < next, "unordered freelist"); + ABSL_RAW_CHECK(reinterpret_cast(prev) + prev->header.size < + reinterpret_cast(next), + "malformed freelist"); + } + } + return next; +} + +// Coalesce list item "a" with its successor if they are adjacent. +static void Coalesce(AllocList *a) { + AllocList *n = a->next[0]; + if (n != nullptr && reinterpret_cast(a) + a->header.size == + reinterpret_cast(n)) { + LowLevelAlloc::Arena *arena = a->header.arena; + a->header.size += n->header.size; + n->header.magic = 0; + n->header.arena = nullptr; + AllocList *prev[kMaxLevel]; + LLA_SkiplistDelete(&arena->freelist, n, prev); + LLA_SkiplistDelete(&arena->freelist, a, prev); + a->levels = LLA_SkiplistLevels(a->header.size, arena->min_size, + &arena->random); + LLA_SkiplistInsert(&arena->freelist, a, prev); + } +} + +// Adds block at location "v" to the free list +// L >= arena->mu +static void AddToFreelist(void *v, LowLevelAlloc::Arena *arena) { + AllocList *f = reinterpret_cast( + reinterpret_cast(v) - sizeof (f->header)); + ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header), + "bad magic number in AddToFreelist()"); + ABSL_RAW_CHECK(f->header.arena == arena, + "bad arena pointer in AddToFreelist()"); + f->levels = LLA_SkiplistLevels(f->header.size, arena->min_size, + &arena->random); + AllocList *prev[kMaxLevel]; + LLA_SkiplistInsert(&arena->freelist, f, prev); + f->header.magic = Magic(kMagicUnallocated, &f->header); + Coalesce(f); // maybe coalesce with successor + Coalesce(prev[0]); // maybe coalesce with predecessor +} + +// Frees storage allocated by LowLevelAlloc::Alloc(). +// L < arena->mu +void LowLevelAlloc::Free(void *v) { + if (v != nullptr) { + AllocList *f = reinterpret_cast( + reinterpret_cast(v) - sizeof (f->header)); + LowLevelAlloc::Arena *arena = f->header.arena; + ArenaLock section(arena); + AddToFreelist(v, arena); + ABSL_RAW_CHECK(arena->allocation_count > 0, "nothing in arena to free"); + arena->allocation_count--; + section.Leave(); + } +} + +// allocates and returns a block of size bytes, to be freed with Free() +// L < arena->mu +static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { + void *result = nullptr; + if (request != 0) { + AllocList *s; // will point to region that satisfies request + ArenaLock section(arena); + // round up with header + size_t req_rnd = RoundUp(CheckedAdd(request, sizeof (s->header)), + arena->round_up); + for (;;) { // loop until we find a suitable region + // find the minimum levels that a block of this size must have + int i = LLA_SkiplistLevels(req_rnd, arena->min_size, nullptr) - 1; + if (i < arena->freelist.levels) { // potential blocks exist + AllocList *before = &arena->freelist; // predecessor of s + while ((s = Next(i, before, arena)) != nullptr && + s->header.size < req_rnd) { + before = s; + } + if (s != nullptr) { // we found a region + break; + } + } + // we unlock before mmap() both because mmap() may call a callback hook, + // and because it may be slow. + arena->mu.Unlock(); + // mmap generous 64K chunks to decrease + // the chances/impact of fragmentation: + size_t new_pages_size = RoundUp(req_rnd, arena->pagesize * 16); + void *new_pages; +#ifdef _WIN32 + new_pages = VirtualAlloc(0, new_pages_size, + 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); + } else { + 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 // _WIN32 + arena->mu.Lock(); + s = reinterpret_cast(new_pages); + s->header.size = new_pages_size; + // Pretend the block is allocated; call AddToFreelist() to free it. + s->header.magic = Magic(kMagicAllocated, &s->header); + s->header.arena = arena; + AddToFreelist(&s->levels, arena); // insert new region into free list + } + AllocList *prev[kMaxLevel]; + LLA_SkiplistDelete(&arena->freelist, s, prev); // remove from free list + // s points to the first free region that's big enough + if (CheckedAdd(req_rnd, arena->min_size) <= s->header.size) { + // big enough to split + AllocList *n = reinterpret_cast + (req_rnd + reinterpret_cast(s)); + n->header.size = s->header.size - req_rnd; + n->header.magic = Magic(kMagicAllocated, &n->header); + n->header.arena = arena; + s->header.size = req_rnd; + AddToFreelist(&n->levels, arena); + } + s->header.magic = Magic(kMagicAllocated, &s->header); + ABSL_RAW_CHECK(s->header.arena == arena, ""); + arena->allocation_count++; + section.Leave(); + result = &s->levels; + } + ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(result, request); + return result; +} + +void *LowLevelAlloc::Alloc(size_t request) { + void *result = DoAllocWithArena(request, DefaultArena()); + return result; +} + +void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) { + ABSL_RAW_CHECK(arena != nullptr, "must pass a valid arena"); + void *result = DoAllocWithArena(request, arena); + return result; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/src/absl/base/internal/low_level_alloc.h b/src/absl/base/internal/low_level_alloc.h new file mode 100644 index 0000000..daf3ebe --- /dev/null +++ b/src/absl/base/internal/low_level_alloc.h @@ -0,0 +1,130 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ +#define ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ + +#if defined(Free) +#undef Free +#endif + +// A simple thread-safe memory allocator that does not depend on +// mutexes or thread-specific data. It is intended to be used +// sparingly, and only when malloc() would introduce an unwanted +// dependency, such as inside the heap-checker, or the Mutex +// implementation. + +// IWYU pragma: private, include "base/low_level_alloc.h" + +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +// LowLevelAlloc requires that the platform support low-level +// allocation of virtual memory. Platforms lacking this cannot use +// LowLevelAlloc. +#ifdef ABSL_LOW_LEVEL_ALLOC_MISSING +#error ABSL_LOW_LEVEL_ALLOC_MISSING cannot be directly set +#elif !defined(ABSL_HAVE_MMAP) && !defined(_WIN32) +#define ABSL_LOW_LEVEL_ALLOC_MISSING 1 +#endif + +// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows or +// asm.js / WebAssembly. +// See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html +// for more information. +#ifdef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING +#error ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING cannot be directly set +#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__) +#define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1 +#endif + +#include + +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +class LowLevelAlloc { + public: + struct Arena; // an arena from which memory may be allocated + + // Returns a pointer to a block of at least "request" bytes + // that have been newly allocated from the specific arena. + // for Alloc() call the DefaultArena() is used. + // Returns 0 if passed request==0. + // Does not return 0 under other circumstances; it crashes if memory + // is not available. + static void *Alloc(size_t request) ABSL_ATTRIBUTE_SECTION(malloc_hook); + static void *AllocWithArena(size_t request, Arena *arena) + ABSL_ATTRIBUTE_SECTION(malloc_hook); + + // Deallocates a region of memory that was previously allocated with + // Alloc(). Does nothing if passed 0. "s" must be either 0, + // or must have been returned from a call to Alloc() and not yet passed to + // Free() since that call to Alloc(). The space is returned to the arena + // from which it was allocated. + static void Free(void *s) ABSL_ATTRIBUTE_SECTION(malloc_hook); + + // ABSL_ATTRIBUTE_SECTION(malloc_hook) for Alloc* and Free + // are to put all callers of MallocHook::Invoke* in this module + // into special section, + // so that MallocHook::GetCallerStackTrace can function accurately. + + // Create a new arena. + // The root metadata for the new arena is allocated in the + // meta_data_arena; the DefaultArena() can be passed for meta_data_arena. + // These values may be ored into flags: + enum { + // Report calls to Alloc() and Free() via the MallocHook interface. + // Set in the DefaultArena. + kCallMallocHook = 0x0001, + +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING + // Make calls to Alloc(), Free() be async-signal-safe. Not set in + // DefaultArena(). Not supported on all platforms. + kAsyncSignalSafe = 0x0002, +#endif + }; + // Construct a new arena. The allocation of the underlying metadata honors + // the provided flags. For example, the call NewArena(kAsyncSignalSafe) + // is itself async-signal-safe, as well as generatating an arena that provides + // async-signal-safe Alloc/Free. + static Arena *NewArena(int32_t flags); + + // Destroys an arena allocated by NewArena and returns true, + // provided no allocated blocks remain in the arena. + // If allocated blocks remain in the arena, does nothing and + // returns false. + // It is illegal to attempt to destroy the DefaultArena(). + static bool DeleteArena(Arena *arena); + + // The default arena that always exists. + static Arena *DefaultArena(); + + private: + LowLevelAlloc(); // no instances +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ diff --git a/src/absl/base/internal/low_level_scheduling.h b/src/absl/base/internal/low_level_scheduling.h new file mode 100644 index 0000000..9baccc0 --- /dev/null +++ b/src/absl/base/internal/low_level_scheduling.h @@ -0,0 +1,134 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Core interfaces and definitions used by by low-level interfaces such as +// SpinLock. + +#ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ +#define ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ + +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/macros.h" + +// The following two declarations exist so SchedulingGuard may friend them with +// the appropriate language linkage. These callbacks allow libc internals, such +// as function level statics, to schedule cooperatively when locking. +extern "C" bool __google_disable_rescheduling(void); +extern "C" void __google_enable_rescheduling(bool disable_result); + +namespace absl { +ABSL_NAMESPACE_BEGIN +class CondVar; +class Mutex; + +namespace synchronization_internal { +int MutexDelay(int32_t c, int mode); +} // namespace synchronization_internal + +namespace base_internal { + +class SchedulingHelper; // To allow use of SchedulingGuard. +class SpinLock; // To allow use of SchedulingGuard. + +// SchedulingGuard +// Provides guard semantics that may be used to disable cooperative rescheduling +// of the calling thread within specific program blocks. This is used to +// protect resources (e.g. low-level SpinLocks or Domain code) that cooperative +// scheduling depends on. +// +// Domain implementations capable of rescheduling in reaction to involuntary +// kernel thread actions (e.g blocking due to a pagefault or syscall) must +// guarantee that an annotated thread is not allowed to (cooperatively) +// reschedule until the annotated region is complete. +// +// It is an error to attempt to use a cooperatively scheduled resource (e.g. +// Mutex) within a rescheduling-disabled region. +// +// All methods are async-signal safe. +class SchedulingGuard { + public: + // Returns true iff the calling thread may be cooperatively rescheduled. + static bool ReschedulingIsAllowed(); + SchedulingGuard(const SchedulingGuard&) = delete; + SchedulingGuard& operator=(const SchedulingGuard&) = delete; + + private: + // Disable cooperative rescheduling of the calling thread. It may still + // initiate scheduling operations (e.g. wake-ups), however, it may not itself + // reschedule. Nestable. The returned result is opaque, clients should not + // attempt to interpret it. + // REQUIRES: Result must be passed to a pairing EnableScheduling(). + static bool DisableRescheduling(); + + // Marks the end of a rescheduling disabled region, previously started by + // DisableRescheduling(). + // REQUIRES: Pairs with innermost call (and result) of DisableRescheduling(). + static void EnableRescheduling(bool disable_result); + + // A scoped helper for {Disable, Enable}Rescheduling(). + // REQUIRES: destructor must run in same thread as constructor. + struct ScopedDisable { + ScopedDisable() { disabled = SchedulingGuard::DisableRescheduling(); } + ~ScopedDisable() { SchedulingGuard::EnableRescheduling(disabled); } + + bool disabled; + }; + + // A scoped helper to enable rescheduling temporarily. + // REQUIRES: destructor must run in same thread as constructor. + class ScopedEnable { + public: + ScopedEnable(); + ~ScopedEnable(); + + private: + int scheduling_disabled_depth_; + }; + + // Access to SchedulingGuard is explicitly permitted. + friend class absl::CondVar; + friend class absl::Mutex; + friend class SchedulingHelper; + friend class SpinLock; + friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode); +}; + +//------------------------------------------------------------------------------ +// End of public interfaces. +//------------------------------------------------------------------------------ + +inline bool SchedulingGuard::ReschedulingIsAllowed() { + return false; +} + +inline bool SchedulingGuard::DisableRescheduling() { + return false; +} + +inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { + return; +} + +inline SchedulingGuard::ScopedEnable::ScopedEnable() + : scheduling_disabled_depth_(0) {} +inline SchedulingGuard::ScopedEnable::~ScopedEnable() { + ABSL_RAW_CHECK(scheduling_disabled_depth_ == 0, "disable unused warning"); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ diff --git a/src/absl/base/internal/per_thread_tls.h b/src/absl/base/internal/per_thread_tls.h new file mode 100644 index 0000000..cf5e97a --- /dev/null +++ b/src/absl/base/internal/per_thread_tls.h @@ -0,0 +1,52 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ +#define ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ + +// This header defines two macros: +// +// If the platform supports thread-local storage: +// +// * ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a +// thread-local variable +// * ABSL_PER_THREAD_TLS is 1 +// +// Otherwise: +// +// * ABSL_PER_THREAD_TLS_KEYWORD is empty +// * ABSL_PER_THREAD_TLS is 0 +// +// Microsoft C supports thread-local storage. +// GCC supports it if the appropriate version of glibc is available, +// which the programmer can indicate by defining ABSL_HAVE_TLS + +#include "absl/base/port.h" // For ABSL_HAVE_TLS + +#if defined(ABSL_PER_THREAD_TLS) +#error ABSL_PER_THREAD_TLS cannot be directly set +#elif defined(ABSL_PER_THREAD_TLS_KEYWORD) +#error ABSL_PER_THREAD_TLS_KEYWORD cannot be directly set +#elif defined(ABSL_HAVE_TLS) +#define ABSL_PER_THREAD_TLS_KEYWORD __thread +#define ABSL_PER_THREAD_TLS 1 +#elif defined(_MSC_VER) +#define ABSL_PER_THREAD_TLS_KEYWORD __declspec(thread) +#define ABSL_PER_THREAD_TLS 1 +#else +#define ABSL_PER_THREAD_TLS_KEYWORD +#define ABSL_PER_THREAD_TLS 0 +#endif + +#endif // ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ diff --git a/src/absl/base/internal/prefetch.h b/src/absl/base/internal/prefetch.h new file mode 100644 index 0000000..0641928 --- /dev/null +++ b/src/absl/base/internal/prefetch.h @@ -0,0 +1,138 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_PREFETCH_H_ +#define ABSL_BASE_INTERNAL_PREFETCH_H_ + +#include "absl/base/config.h" + +#ifdef __SSE__ +#include +#endif + +#if defined(_MSC_VER) && defined(ABSL_INTERNAL_HAVE_SSE) +#include +#pragma intrinsic(_mm_prefetch) +#endif + +// Compatibility wrappers around __builtin_prefetch, to prefetch data +// for read if supported by the toolchain. + +// Move data into the cache before it is read, or "prefetch" it. +// +// The value of `addr` is the address of the memory to prefetch. If +// the target and compiler support it, data prefetch instructions are +// generated. If the prefetch is done some time before the memory is +// read, it may be in the cache by the time the read occurs. +// +// The function names specify the temporal locality heuristic applied, +// using the names of Intel prefetch instructions: +// +// T0 - high degree of temporal locality; data should be left in as +// many levels of the cache possible +// T1 - moderate degree of temporal locality +// T2 - low degree of temporal locality +// Nta - no temporal locality, data need not be left in the cache +// after the read +// +// Incorrect or gratuitous use of these functions can degrade +// performance, so use them only when representative benchmarks show +// an improvement. +// +// Example usage: +// +// absl::base_internal::PrefetchT0(addr); +// +// Currently, the different prefetch calls behave on some Intel +// architectures as follows: +// +// SNB..SKL SKX +// PrefetchT0() L1/L2/L3 L1/L2 +// PrefetchT1() L2/L3 L2 +// PrefetchT2() L2/L3 L2 +// PrefetchNta() L1/--/L3 L1* +// +// * On SKX PrefetchNta() will bring the line into L1 but will evict +// from L3 cache. This might result in surprising behavior. +// +// SNB = Sandy Bridge, SKL = Skylake, SKX = Skylake Xeon. +// +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +void PrefetchT0(const void* addr); +void PrefetchT1(const void* addr); +void PrefetchT2(const void* addr); +void PrefetchNta(const void* addr); + +// Implementation details follow. + +#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__) + +#define ABSL_INTERNAL_HAVE_PREFETCH 1 + +// See __builtin_prefetch: +// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html. +// +// These functions speculatively load for read only. This is +// safe for all currently supported platforms. However, prefetch for +// store may have problems depending on the target platform. +// +inline void PrefetchT0(const void* addr) { + // Note: this uses prefetcht0 on Intel. + __builtin_prefetch(addr, 0, 3); +} +inline void PrefetchT1(const void* addr) { + // Note: this uses prefetcht1 on Intel. + __builtin_prefetch(addr, 0, 2); +} +inline void PrefetchT2(const void* addr) { + // Note: this uses prefetcht2 on Intel. + __builtin_prefetch(addr, 0, 1); +} +inline void PrefetchNta(const void* addr) { + // Note: this uses prefetchtnta on Intel. + __builtin_prefetch(addr, 0, 0); +} + +#elif defined(ABSL_INTERNAL_HAVE_SSE) + +#define ABSL_INTERNAL_HAVE_PREFETCH 1 + +inline void PrefetchT0(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T0); +} +inline void PrefetchT1(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T1); +} +inline void PrefetchT2(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T2); +} +inline void PrefetchNta(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_NTA); +} + +#else +inline void PrefetchT0(const void*) {} +inline void PrefetchT1(const void*) {} +inline void PrefetchT2(const void*) {} +inline void PrefetchNta(const void*) {} +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_PREFETCH_H_ diff --git a/src/absl/base/internal/pretty_function.h b/src/absl/base/internal/pretty_function.h new file mode 100644 index 0000000..35d5167 --- /dev/null +++ b/src/absl/base/internal/pretty_function.h @@ -0,0 +1,33 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ +#define ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ + +// ABSL_PRETTY_FUNCTION +// +// In C++11, __func__ gives the undecorated name of the current function. That +// is, "main", not "int main()". Various compilers give extra macros to get the +// decorated function name, including return type and arguments, to +// differentiate between overload sets. ABSL_PRETTY_FUNCTION is a portable +// version of these macros which forwards to the correct macro on each compiler. +#if defined(_MSC_VER) +#define ABSL_PRETTY_FUNCTION __FUNCSIG__ +#elif defined(__GNUC__) +#define ABSL_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#error "Unsupported compiler" +#endif + +#endif // ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ diff --git a/src/absl/base/internal/raw_logging.cc b/src/absl/base/internal/raw_logging.cc new file mode 100644 index 0000000..8092687 --- /dev/null +++ b/src/absl/base/internal/raw_logging.cc @@ -0,0 +1,249 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/raw_logging.h" + +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/atomic_hook.h" +#include "absl/base/internal/errno_saver.h" +#include "absl/base/log_severity.h" + +// We know how to perform low-level writes to stderr in POSIX and Windows. For +// these platforms, we define the token ABSL_LOW_LEVEL_WRITE_SUPPORTED. +// Much of raw_logging.cc becomes a no-op when we can't output messages, +// although a FATAL ABSL_RAW_LOG message will still abort the process. + +// ABSL_HAVE_POSIX_WRITE is defined when the platform provides posix write() +// (as from unistd.h) +// +// This preprocessor token is also defined in raw_io.cc. If you need to copy +// this, consider moving both to config.h instead. +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__Fuchsia__) || defined(__native_client__) || \ + defined(__OpenBSD__) || defined(__EMSCRIPTEN__) || defined(__ASYLO__) + +#include + +#define ABSL_HAVE_POSIX_WRITE 1 +#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 +#else +#undef ABSL_HAVE_POSIX_WRITE +#endif + +// ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall +// syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len); +// for low level operations that want to avoid libc. +#if (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && \ + !defined(__ANDROID__) +#include +#define ABSL_HAVE_SYSCALL_WRITE 1 +#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 +#else +#undef ABSL_HAVE_SYSCALL_WRITE +#endif + +#ifdef _WIN32 +#include + +#define ABSL_HAVE_RAW_IO 1 +#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 +#else +#undef ABSL_HAVE_RAW_IO +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace raw_logging_internal { +namespace { + +// TODO(gfalcon): We want raw-logging to work on as many platforms as possible. +// Explicitly `#error` out when not `ABSL_LOW_LEVEL_WRITE_SUPPORTED`, except for +// a selected set of platforms for which we expect not to be able to raw log. + +#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED +constexpr char kTruncated[] = " ... (message truncated)\n"; + +// sprintf the format to the buffer, adjusting *buf and *size to reflect the +// consumed bytes, and return whether the message fit without truncation. If +// truncation occurred, if possible leave room in the buffer for the message +// kTruncated[]. +bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) + ABSL_PRINTF_ATTRIBUTE(3, 0); +bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) { + int n = vsnprintf(*buf, *size, format, ap); + bool result = true; + if (n < 0 || n > *size) { + result = false; + if (static_cast(*size) > sizeof(kTruncated)) { + n = *size - sizeof(kTruncated); // room for truncation message + } else { + n = 0; // no room for truncation message + } + } + *size -= n; + *buf += n; + return result; +} +#endif // ABSL_LOW_LEVEL_WRITE_SUPPORTED + +constexpr int kLogBufSize = 3000; + +// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths +// that invoke malloc() and getenv() that might acquire some locks. + +// Helper for RawLog below. +// *DoRawLog writes to *buf of *size and move them past the written portion. +// It returns true iff there was no overflow or error. +bool DoRawLog(char** buf, int* size, const char* format, ...) + ABSL_PRINTF_ATTRIBUTE(3, 4); +bool DoRawLog(char** buf, int* size, const char* format, ...) { + va_list ap; + va_start(ap, format); + int n = vsnprintf(*buf, *size, format, ap); + va_end(ap); + if (n < 0 || n > *size) return false; + *size -= n; + *buf += n; + return true; +} + +bool DefaultLogFilterAndPrefix(absl::LogSeverity, const char* file, int line, + char** buf, int* buf_size) { + DoRawLog(buf, buf_size, "[%s : %d] RAW: ", file, line); + return true; +} + +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +absl::base_internal::AtomicHook + log_filter_and_prefix_hook(DefaultLogFilterAndPrefix); +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +absl::base_internal::AtomicHook abort_hook; + +void RawLogVA(absl::LogSeverity severity, const char* file, int line, + const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0); +void RawLogVA(absl::LogSeverity severity, const char* file, int line, + const char* format, va_list ap) { + char buffer[kLogBufSize]; + char* buf = buffer; + int size = sizeof(buffer); +#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED + bool enabled = true; +#else + bool enabled = false; +#endif + +#ifdef ABSL_MIN_LOG_LEVEL + if (severity < static_cast(ABSL_MIN_LOG_LEVEL) && + severity < absl::LogSeverity::kFatal) { + enabled = false; + } +#endif + + enabled = log_filter_and_prefix_hook(severity, file, line, &buf, &size); + const char* const prefix_end = buf; + +#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED + if (enabled) { + bool no_chop = VADoRawLog(&buf, &size, format, ap); + if (no_chop) { + DoRawLog(&buf, &size, "\n"); + } else { + DoRawLog(&buf, &size, "%s", kTruncated); + } + AsyncSignalSafeWriteToStderr(buffer, strlen(buffer)); + } +#else + static_cast(format); + static_cast(ap); + static_cast(enabled); +#endif + + // Abort the process after logging a FATAL message, even if the output itself + // was suppressed. + if (severity == absl::LogSeverity::kFatal) { + abort_hook(file, line, buffer, prefix_end, buffer + kLogBufSize); + throw std::runtime_error("abort()"); + } +} + +// 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. +void DefaultInternalLog(absl::LogSeverity severity, const char* file, int line, + const std::string& message) { + RawLog(severity, file, line, "%.*s", static_cast(message.size()), + message.data()); +} + +} // namespace + +void AsyncSignalSafeWriteToStderr(const char* s, size_t len) { + absl::base_internal::ErrnoSaver errno_saver; +#if defined(ABSL_HAVE_SYSCALL_WRITE) + // We prefer calling write via `syscall` to minimize the risk of libc doing + // something "helpful". + syscall(SYS_write, STDERR_FILENO, s, len); +#elif defined(ABSL_HAVE_POSIX_WRITE) + write(STDERR_FILENO, s, len); +#elif defined(ABSL_HAVE_RAW_IO) + _write(/* stderr */ 2, s, len); +#else + // stderr logging unsupported on this platform + (void) s; + (void) len; +#endif +} + +void RawLog(absl::LogSeverity severity, const char* file, int line, + const char* format, ...) { + va_list ap; + va_start(ap, format); + RawLogVA(severity, file, line, format, ap); + va_end(ap); +} + +bool RawLoggingFullySupported() { +#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED + return true; +#else // !ABSL_LOW_LEVEL_WRITE_SUPPORTED + return false; +#endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED +} + +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL + absl::base_internal::AtomicHook + internal_log_function(DefaultInternalLog); + +void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func) { + log_filter_and_prefix_hook.Store(func); +} + +void RegisterAbortHook(AbortHook func) { abort_hook.Store(func); } + +void RegisterInternalLogFunction(InternalLogFunction func) { + internal_log_function.Store(func); +} + +} // namespace raw_logging_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/raw_logging.h b/src/absl/base/internal/raw_logging.h new file mode 100644 index 0000000..6d67087 --- /dev/null +++ b/src/absl/base/internal/raw_logging.h @@ -0,0 +1,198 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Thread-safe logging routines that do not allocate any memory or +// acquire any locks, and can therefore be used by low-level memory +// allocation, synchronization, and signal-handling code. + +#ifndef ABSL_BASE_INTERNAL_RAW_LOGGING_H_ +#define ABSL_BASE_INTERNAL_RAW_LOGGING_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/atomic_hook.h" +#include "absl/base/log_severity.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" + +// This is similar to LOG(severity) << format..., but +// * it is to be used ONLY by low-level modules that can't use normal LOG() +// * it is designed to be a low-level logger that does not allocate any +// memory and does not need any locks, hence: +// * it logs straight and ONLY to STDERR w/o buffering +// * it uses an explicit printf-format and arguments list +// * it will silently chop off really long message strings +// Usage example: +// ABSL_RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); +// This will print an almost standard log line like this to stderr only: +// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file + +#define ABSL_RAW_LOG(severity, ...) + // R/Windows gives compilation warnings here that fail the CMD + // check. + // do { + // constexpr const char* absl_raw_logging_internal_basename = + // ::absl::raw_logging_internal::Basename(__FILE__, + // sizeof(__FILE__) - 1); + // ::absl::raw_logging_internal::RawLog(ABSL_RAW_LOGGING_INTERNAL_##severity, + // absl_raw_logging_internal_basename, + // __LINE__, __VA_ARGS__); + // } while (0) + +// Similar to CHECK(condition) << message, but for low-level modules: +// we use only ABSL_RAW_LOG that does not allocate memory. +// We do not want to provide args list here to encourage this usage: +// if (!cond) ABSL_RAW_LOG(FATAL, "foo ...", hard_to_compute_args); +// so that the args are not computed when not needed. +#define ABSL_RAW_CHECK(condition, message) \ + do { \ + if (ABSL_PREDICT_FALSE(!(condition))) { \ + ABSL_RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ + } \ + } while (0) + +// ABSL_INTERNAL_LOG and ABSL_INTERNAL_CHECK work like the RAW variants above, +// except that if the richer log library is linked into the binary, we dispatch +// to that instead. This is potentially useful for internal logging and +// assertions, where we are using RAW_LOG neither for its async-signal-safety +// nor for its non-allocating nature, but rather because raw logging has very +// few other dependencies. +// +// The API is a subset of the above: each macro only takes two arguments. Use +// StrCat if you need to build a richer message. +#define ABSL_INTERNAL_LOG(severity, message) \ + do { \ + constexpr const char* absl_raw_logging_internal_filename = __FILE__; \ + ::absl::raw_logging_internal::internal_log_function( \ + ABSL_RAW_LOGGING_INTERNAL_##severity, \ + absl_raw_logging_internal_filename, __LINE__, message); \ + if (ABSL_RAW_LOGGING_INTERNAL_##severity == ::absl::LogSeverity::kFatal) \ + ABSL_INTERNAL_UNREACHABLE; \ + } while (0) + +#define ABSL_INTERNAL_CHECK(condition, message) \ + do { \ + if (ABSL_PREDICT_FALSE(!(condition))) { \ + std::string death_message = "Check " #condition " failed: "; \ + death_message += std::string(message); \ + ABSL_INTERNAL_LOG(FATAL, death_message); \ + } \ + } while (0) + +#define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo +#define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning +#define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError +#define ABSL_RAW_LOGGING_INTERNAL_FATAL ::absl::LogSeverity::kFatal +#define ABSL_RAW_LOGGING_INTERNAL_LEVEL(severity) \ + ::absl::NormalizeLogSeverity(severity) + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace raw_logging_internal { + +// Helper function to implement ABSL_RAW_LOG +// Logs format... at "severity" level, reporting it +// as called from file:line. +// This does not allocate memory or acquire locks. +void RawLog(absl::LogSeverity severity, const char* file, int line, + const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); + +// Writes the provided buffer directly to stderr, in a signal-safe, low-level +// manner. +void AsyncSignalSafeWriteToStderr(const char* s, size_t len); + +// compile-time function to get the "base" filename, that is, the part of +// a filename after the last "/" or "\" path separator. The search starts at +// the end of the string; the second parameter is the length of the string. +constexpr const char* Basename(const char* fname, int offset) { + return offset == 0 || fname[offset - 1] == '/' || fname[offset - 1] == '\\' + ? fname + offset + : Basename(fname, offset - 1); +} + +// For testing only. +// Returns true if raw logging is fully supported. When it is not +// fully supported, no messages will be emitted, but a log at FATAL +// severity will cause an abort. +// +// TODO(gfalcon): Come up with a better name for this method. +bool RawLoggingFullySupported(); + +// Function type for a raw_logging customization hook for suppressing messages +// by severity, and for writing custom prefixes on non-suppressed messages. +// +// The installed hook is called for every raw log invocation. The message will +// be logged to stderr only if the hook returns true. FATAL errors will cause +// the process to abort, even if writing to stderr is suppressed. The hook is +// also provided with an output buffer, where it can write a custom log message +// prefix. +// +// The raw_logging system does not allocate memory or grab locks. User-provided +// hooks must avoid these operations, and must not throw exceptions. +// +// 'severity' is the severity level of the message being written. +// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro +// was located. +// 'buf' and 'buf_size' are pointers to the buffer and buffer size. If the +// hook writes a prefix, it must increment *buf and decrement *buf_size +// accordingly. +using LogFilterAndPrefixHook = bool (*)(absl::LogSeverity severity, + const char* file, int line, char** buf, + int* buf_size); + +// Function type for a raw_logging customization hook called to abort a process +// when a FATAL message is logged. If the provided AbortHook() returns, the +// logging system will call abort(). +// +// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro +// was located. +// The NUL-terminated logged message lives in the buffer between 'buf_start' +// and 'buf_end'. 'prefix_end' points to the first non-prefix character of the +// buffer (as written by the LogFilterAndPrefixHook.) +// +// The lifetime of the filename and message buffers will not end while the +// process remains alive. +using AbortHook = void (*)(const char* file, int line, const char* buf_start, + const char* prefix_end, const char* buf_end); + +// Internal logging function for ABSL_INTERNAL_LOG to dispatch to. +// +// TODO(gfalcon): When string_view no longer depends on base, change this +// interface to take its message as a string_view instead. +using InternalLogFunction = void (*)(absl::LogSeverity severity, + const char* file, int line, + const std::string& message); + +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL extern base_internal::AtomicHook< + InternalLogFunction> + internal_log_function; + +// Registers hooks of the above types. Only a single hook of each type may be +// registered. It is an error to call these functions multiple times with +// different input arguments. +// +// These functions are safe to call at any point during initialization; they do +// not block or malloc, and are async-signal safe. +void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func); +void RegisterAbortHook(AbortHook func); +void RegisterInternalLogFunction(InternalLogFunction func); + +} // namespace raw_logging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_RAW_LOGGING_H_ diff --git a/src/absl/base/internal/scheduling_mode.h b/src/absl/base/internal/scheduling_mode.h new file mode 100644 index 0000000..8be5ab6 --- /dev/null +++ b/src/absl/base/internal/scheduling_mode.h @@ -0,0 +1,58 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Core interfaces and definitions used by by low-level interfaces such as +// SpinLock. + +#ifndef ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ +#define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Used to describe how a thread may be scheduled. Typically associated with +// the declaration of a resource supporting synchronized access. +// +// SCHEDULE_COOPERATIVE_AND_KERNEL: +// Specifies that when waiting, a cooperative thread (e.g. a Fiber) may +// reschedule (using base::scheduling semantics); allowing other cooperative +// threads to proceed. +// +// SCHEDULE_KERNEL_ONLY: (Also described as "non-cooperative") +// Specifies that no cooperative scheduling semantics may be used, even if the +// current thread is itself cooperatively scheduled. This means that +// cooperative threads will NOT allow other cooperative threads to execute in +// their place while waiting for a resource of this type. Host operating system +// semantics (e.g. a futex) may still be used. +// +// When optional, clients should strongly prefer SCHEDULE_COOPERATIVE_AND_KERNEL +// by default. SCHEDULE_KERNEL_ONLY should only be used for resources on which +// base::scheduling (e.g. the implementation of a Scheduler) may depend. +// +// NOTE: Cooperative resources may not be nested below non-cooperative ones. +// This means that it is invalid to to acquire a SCHEDULE_COOPERATIVE_AND_KERNEL +// resource if a SCHEDULE_KERNEL_ONLY resource is already held. +enum SchedulingMode { + SCHEDULE_KERNEL_ONLY = 0, // Allow scheduling only the host OS. + SCHEDULE_COOPERATIVE_AND_KERNEL, // Also allow cooperative scheduling. +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ diff --git a/src/absl/base/internal/scoped_set_env.cc b/src/absl/base/internal/scoped_set_env.cc new file mode 100644 index 0000000..8a934cb --- /dev/null +++ b/src/absl/base/internal/scoped_set_env.cc @@ -0,0 +1,81 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/scoped_set_env.h" + +#ifdef _WIN32 +#include +#endif + +#include + +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +namespace { + +#ifdef _WIN32 +const int kMaxEnvVarValueSize = 1024; +#endif + +void SetEnvVar(const char* name, const char* value) { +#ifdef _WIN32 + SetEnvironmentVariableA(name, value); +#else + if (value == nullptr) { + ::unsetenv(name); + } else { + ::setenv(name, value, 1); + } +#endif +} + +} // namespace + +ScopedSetEnv::ScopedSetEnv(const char* var_name, const char* new_value) + : var_name_(var_name), was_unset_(false) { +#ifdef _WIN32 + char buf[kMaxEnvVarValueSize]; + auto get_res = GetEnvironmentVariableA(var_name_.c_str(), buf, sizeof(buf)); + ABSL_INTERNAL_CHECK(get_res < sizeof(buf), "value exceeds buffer size"); + + if (get_res == 0) { + was_unset_ = (GetLastError() == ERROR_ENVVAR_NOT_FOUND); + } else { + old_value_.assign(buf, get_res); + } + + SetEnvironmentVariableA(var_name_.c_str(), new_value); +#else + const char* val = ::getenv(var_name_.c_str()); + if (val == nullptr) { + was_unset_ = true; + } else { + old_value_ = val; + } +#endif + + SetEnvVar(var_name_.c_str(), new_value); +} + +ScopedSetEnv::~ScopedSetEnv() { + SetEnvVar(var_name_.c_str(), was_unset_ ? nullptr : old_value_.c_str()); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/scoped_set_env.h b/src/absl/base/internal/scoped_set_env.h new file mode 100644 index 0000000..19ec7b5 --- /dev/null +++ b/src/absl/base/internal/scoped_set_env.h @@ -0,0 +1,45 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ +#define ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +class ScopedSetEnv { + public: + ScopedSetEnv(const char* var_name, const char* new_value); + ~ScopedSetEnv(); + + private: + std::string var_name_; + std::string old_value_; + + // True if the environment variable was initially not set. + bool was_unset_; +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ diff --git a/src/absl/base/internal/spinlock.cc b/src/absl/base/internal/spinlock.cc new file mode 100644 index 0000000..9b5ed6e --- /dev/null +++ b/src/absl/base/internal/spinlock.cc @@ -0,0 +1,232 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/spinlock.h" + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/atomic_hook.h" +#include "absl/base/internal/cycleclock.h" +#include "absl/base/internal/spinlock_wait.h" +#include "absl/base/internal/sysinfo.h" /* For NumCPUs() */ +#include "absl/base/call_once.h" + +// Description of lock-word: +// 31..00: [............................3][2][1][0] +// +// [0]: kSpinLockHeld +// [1]: kSpinLockCooperative +// [2]: kSpinLockDisabledScheduling +// [31..3]: ONLY kSpinLockSleeper OR +// Wait time in cycles >> PROFILE_TIMESTAMP_SHIFT +// +// Detailed descriptions: +// +// Bit [0]: The lock is considered held iff kSpinLockHeld is set. +// +// Bit [1]: Eligible waiters (e.g. Fibers) may co-operatively reschedule when +// contended iff kSpinLockCooperative is set. +// +// Bit [2]: This bit is exclusive from bit [1]. It is used only by a +// non-cooperative lock. When set, indicates that scheduling was +// successfully disabled when the lock was acquired. May be unset, +// even if non-cooperative, if a ThreadIdentity did not yet exist at +// time of acquisition. +// +// Bit [3]: If this is the only upper bit ([31..3]) set then this lock was +// acquired without contention, however, at least one waiter exists. +// +// Otherwise, bits [31..3] represent the time spent by the current lock +// holder to acquire the lock. There may be outstanding waiter(s). + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static base_internal::AtomicHook + submit_profile_data; + +void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock, + int64_t wait_cycles)) { + submit_profile_data.Store(fn); +} + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// Static member variable definitions. +constexpr uint32_t SpinLock::kSpinLockHeld; +constexpr uint32_t SpinLock::kSpinLockCooperative; +constexpr uint32_t SpinLock::kSpinLockDisabledScheduling; +constexpr uint32_t SpinLock::kSpinLockSleeper; +constexpr uint32_t SpinLock::kWaitTimeMask; +#endif + +// Uncommon constructors. +SpinLock::SpinLock(base_internal::SchedulingMode mode) + : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) { + ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); +} + +// Monitor the lock to see if its value changes within some time period +// (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; + ABSL_CONST_INIT static int adaptive_spin_count = 0; + base_internal::LowLevelCallOnce(&init_adaptive_spin_count, []() { + adaptive_spin_count = base_internal::NumCPUs() > 1 ? 1000 : 1; + }); + + int c = adaptive_spin_count; + uint32_t lock_value; + do { + lock_value = lockword_.load(std::memory_order_relaxed); + } while ((lock_value & kSpinLockHeld) != 0 && --c > 0); + return lock_value; +} + +void SpinLock::SlowLock() { + uint32_t lock_value = SpinLoop(); + lock_value = TryLockInternal(lock_value, 0); + if ((lock_value & kSpinLockHeld) == 0) { + return; + } + + base_internal::SchedulingMode scheduling_mode; + if ((lock_value & kSpinLockCooperative) != 0) { + scheduling_mode = base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; + } else { + scheduling_mode = base_internal::SCHEDULE_KERNEL_ONLY; + } + + // 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 = 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 + // it as having a sleeper. + if ((lock_value & kWaitTimeMask) == 0) { + // Here, just "mark" that the thread is going to sleep. Don't store the + // lock wait time in the lock -- the lock word stores the amount of time + // that the current holder waited before acquiring the lock, not the wait + // time of any thread currently waiting to acquire it. + if (lockword_.compare_exchange_strong( + lock_value, lock_value | kSpinLockSleeper, + 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. + lock_value |= kSpinLockSleeper; + } else if ((lock_value & kSpinLockHeld) == 0) { + // Lock is free again, so try and acquire it before sleeping. The + // new lock state will be the number of cycles this thread waited if + // this thread obtains the lock. + lock_value = TryLockInternal(lock_value, wait_cycles); + continue; // Skip the delay at the end of the loop. + } else if ((lock_value & kWaitTimeMask) == 0) { + // The lock is still held, without a waiter being marked, but something + // else about the lock word changed, causing our CAS to fail. For + // example, a new lock holder may have acquired the lock with + // kSpinLockDisabledScheduling set, whereas the previous holder had not + // set that flag. In this case, attempt again to mark ourselves as a + // waiter. + continue; + } + } + + // SpinLockDelay() calls into fiber scheduler, we need to see + // synchronization there to avoid false positives. + ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); + // Wait for an OS specific delay. + base_internal::SpinLockDelay(&lockword_, lock_value, ++lock_wait_call_count, + scheduling_mode); + 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_cycles = EncodeWaitCycles(wait_start_time, CycleClock::Now()); + lock_value = TryLockInternal(lock_value, wait_cycles); + } +} + +void SpinLock::SlowUnlock(uint32_t lock_value) { + base_internal::SpinLockWake(&lockword_, + false); // wake waiter if necessary + + // If our acquisition was contended, collect contentionz profile info. We + // reserve a unitary wait time to represent that a waiter exists without our + // own acquisition having been contended. + if ((lock_value & kWaitTimeMask) != kSpinLockSleeper) { + const uint64_t wait_cycles = DecodeWaitCycles(lock_value); + ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); + submit_profile_data(this, wait_cycles); + ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); + } +} + +// We use the upper 29 bits of the lock word to store the time spent waiting to +// acquire this lock. This is reported by contentionz profiling. Since the +// lower bits of the cycle counter wrap very quickly on high-frequency +// processors we divide to reduce the granularity to 2^kProfileTimestampShift +// sized units. On a 4Ghz machine this will lose track of wait times greater +// than (2^29/4 Ghz)*128 =~ 17.2 seconds. Such waits should be extremely rare. +static constexpr int kProfileTimestampShift = 7; + +// We currently reserve the lower 3 bits. +static constexpr int kLockwordReservedShift = 3; + +uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time, + int64_t wait_end_time) { + static const int64_t kMaxWaitTime = + std::numeric_limits::max() >> kLockwordReservedShift; + int64_t scaled_wait_time = + (wait_end_time - wait_start_time) >> kProfileTimestampShift; + + // Return a representation of the time spent waiting that can be stored in + // the lock word's upper bits. + uint32_t clamped = static_cast( + std::min(scaled_wait_time, kMaxWaitTime) << kLockwordReservedShift); + + 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 << kLockwordReservedShift); + if (clamped == kSpinLockSleeper) { + return kMinWaitTime; + } + return clamped; +} + +uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { + // Cast to uint32_t first to ensure bits [63:32] are cleared. + const uint64_t scaled_wait_time = + static_cast(lock_value & kWaitTimeMask); + return scaled_wait_time << (kProfileTimestampShift - kLockwordReservedShift); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/spinlock.h b/src/absl/base/internal/spinlock.h new file mode 100644 index 0000000..6d8d8dd --- /dev/null +++ b/src/absl/base/internal/spinlock.h @@ -0,0 +1,256 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Most users requiring mutual exclusion should use Mutex. +// SpinLock is provided for use in two situations: +// - for use by Abseil internal code that Mutex itself depends on +// - for async signal safety (see below) + +// SpinLock is async signal safe. If a spinlock is used within a signal +// handler, all code that acquires the lock must ensure that the signal cannot +// arrive while they are holding the lock. Typically, this is done by blocking +// the signal. +// +// Threads waiting on a SpinLock may be woken in an arbitrary order. + +#ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_ +#define ABSL_BASE_INTERNAL_SPINLOCK_H_ + +#include +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/base/const_init.h" +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/low_level_scheduling.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/internal/tsan_mutex_interface.h" +#include "absl/base/macros.h" +#include "absl/base/port.h" +#include "absl/base/thread_annotations.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +class ABSL_LOCKABLE SpinLock { + public: + SpinLock() : lockword_(kSpinLockCooperative) { + ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); + } + + // Constructors that allow non-cooperative spinlocks to be created for use + // inside thread schedulers. Normal clients should not use these. + explicit SpinLock(base_internal::SchedulingMode mode); + + // Constructor for global SpinLock instances. See absl/base/const_init.h. + constexpr SpinLock(absl::ConstInitType, base_internal::SchedulingMode mode) + : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) {} + + // For global SpinLock instances prefer trivial destructor when possible. + // Default but non-trivial destructor in some build configurations causes an + // extra static initializer. +#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE + ~SpinLock() { ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); } +#else + ~SpinLock() = default; +#endif + + // Acquire this SpinLock. + inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { + ABSL_TSAN_MUTEX_PRE_LOCK(this, 0); + if (!TryLockImpl()) { + SlowLock(); + } + ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0); + } + + // Try to acquire this SpinLock without blocking and return true if the + // acquisition was successful. If the lock was not acquired, false is + // returned. If this SpinLock is free at the time of the call, TryLock + // will return true with high probability. + inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock); + bool res = TryLockImpl(); + ABSL_TSAN_MUTEX_POST_LOCK( + this, __tsan_mutex_try_lock | (res ? 0 : __tsan_mutex_try_lock_failed), + 0); + return res; + } + + // Release this SpinLock, which must be held by the calling thread. + inline void Unlock() ABSL_UNLOCK_FUNCTION() { + ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0); + uint32_t lock_value = lockword_.load(std::memory_order_relaxed); + lock_value = lockword_.exchange(lock_value & kSpinLockCooperative, + std::memory_order_release); + + if ((lock_value & kSpinLockDisabledScheduling) != 0) { + base_internal::SchedulingGuard::EnableRescheduling(true); + } + if ((lock_value & kWaitTimeMask) != 0) { + // Collect contentionz profile info, and speed the wakeup of any waiter. + // The wait_cycles value indicates how long this thread spent waiting + // for the lock. + SlowUnlock(lock_value); + } + ABSL_TSAN_MUTEX_POST_UNLOCK(this, 0); + } + + // Determine if the lock is held. When the lock is held by the invoking + // thread, true will always be returned. Intended to be used as + // CHECK(lock.IsHeld()). + inline bool IsHeld() const { + return (lockword_.load(std::memory_order_relaxed) & kSpinLockHeld) != 0; + } + + // Return immediately if this thread holds the SpinLock exclusively. + // Otherwise, report an error by crashing with a diagnostic. + inline void AssertHeld() const ABSL_ASSERT_EXCLUSIVE_LOCK() { + if (!IsHeld()) { + ABSL_RAW_LOG(FATAL, "thread should hold the lock on SpinLock"); + } + } + + protected: + // These should not be exported except for testing. + + // Store number of cycles between wait_start_time and wait_end_time in a + // lock value. + static uint32_t EncodeWaitCycles(int64_t wait_start_time, + int64_t wait_end_time); + + // Extract number of wait cycles in a lock value. + static uint64_t DecodeWaitCycles(uint32_t lock_value); + + // Provide access to protected method above. Use for testing only. + friend struct SpinLockTest; + + private: + // lockword_ is used to store the following: + // + // bit[0] encodes whether a lock is being held. + // bit[1] encodes whether a lock uses cooperative scheduling. + // bit[2] encodes whether the current lock holder disabled scheduling when + // acquiring the lock. Only set when kSpinLockHeld is also set. + // bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int. + // This is set by the lock holder to indicate how long it waited on + // the lock before eventually acquiring it. The number of cycles is + // encoded as a 29-bit unsigned int, or in the case that the current + // holder did not wait but another waiter is queued, the LSB + // (kSpinLockSleeper) is set. The implementation does not explicitly + // track the number of queued waiters beyond this. It must always be + // assumed that waiters may exist if the current holder was required to + // queue. + // + // Invariant: if the lock is not held, the value is either 0 or + // kSpinLockCooperative. + static constexpr uint32_t kSpinLockHeld = 1; + static constexpr uint32_t kSpinLockCooperative = 2; + static constexpr uint32_t kSpinLockDisabledScheduling = 4; + static constexpr uint32_t kSpinLockSleeper = 8; + // Includes kSpinLockSleeper. + static constexpr uint32_t kWaitTimeMask = + ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling); + + // Returns true if the provided scheduling mode is cooperative. + static constexpr bool IsCooperative( + base_internal::SchedulingMode scheduling_mode) { + return scheduling_mode == base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; + } + + uint32_t TryLockInternal(uint32_t lock_value, uint32_t wait_cycles); + void SlowLock() ABSL_ATTRIBUTE_COLD; + void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD; + uint32_t SpinLoop(); + + inline bool TryLockImpl() { + uint32_t lock_value = lockword_.load(std::memory_order_relaxed); + return (TryLockInternal(lock_value, 0) & kSpinLockHeld) == 0; + } + + std::atomic lockword_; + + SpinLock(const SpinLock&) = delete; + SpinLock& operator=(const SpinLock&) = delete; +}; + +// Corresponding locker object that arranges to acquire a spinlock for +// the duration of a C++ scope. +class ABSL_SCOPED_LOCKABLE SpinLockHolder { + public: + inline explicit SpinLockHolder(SpinLock* l) ABSL_EXCLUSIVE_LOCK_FUNCTION(l) + : lock_(l) { + l->Lock(); + } + inline ~SpinLockHolder() ABSL_UNLOCK_FUNCTION() { lock_->Unlock(); } + + SpinLockHolder(const SpinLockHolder&) = delete; + SpinLockHolder& operator=(const SpinLockHolder&) = delete; + + private: + SpinLock* lock_; +}; + +// Register a hook for profiling support. +// +// The function pointer registered here will be called whenever a spinlock is +// contended. The callback is given an opaque handle to the contended spinlock +// and the number of wait cycles. This is thread-safe, but only a single +// profiler can be registered. It is an error to call this function multiple +// times with different arguments. +void RegisterSpinLockProfiler(void (*fn)(const void* lock, + int64_t wait_cycles)); + +//------------------------------------------------------------------------------ +// Public interface ends here. +//------------------------------------------------------------------------------ + +// If (result & kSpinLockHeld) == 0, then *this was successfully locked. +// Otherwise, returns last observed value for lockword_. +inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, + uint32_t wait_cycles) { + if ((lock_value & kSpinLockHeld) != 0) { + return lock_value; + } + + uint32_t sched_disabled_bit = 0; + if ((lock_value & kSpinLockCooperative) == 0) { + // For non-cooperative locks we must make sure we mark ourselves as + // non-reschedulable before we attempt to CompareAndSwap. + if (base_internal::SchedulingGuard::DisableRescheduling()) { + sched_disabled_bit = kSpinLockDisabledScheduling; + } + } + + if (!lockword_.compare_exchange_strong( + lock_value, + kSpinLockHeld | lock_value | wait_cycles | sched_disabled_bit, + std::memory_order_acquire, std::memory_order_relaxed)) { + base_internal::SchedulingGuard::EnableRescheduling(sched_disabled_bit != 0); + } + + return lock_value; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SPINLOCK_H_ diff --git a/src/absl/base/internal/spinlock_akaros.inc b/src/absl/base/internal/spinlock_akaros.inc new file mode 100644 index 0000000..7b0cada --- /dev/null +++ b/src/absl/base/internal/spinlock_akaros.inc @@ -0,0 +1,35 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is an Akaros-specific part of spinlock_wait.cc + +#include + +#include "absl/base/internal/scheduling_mode.h" + +extern "C" { + +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( + std::atomic* /* lock_word */, uint32_t /* value */, + int /* loop */, absl::base_internal::SchedulingMode /* mode */) { + // In Akaros, one must take care not to call anything that could cause a + // malloc(), a blocking system call, or a uthread_yield() while holding a + // spinlock. Our callers assume will not call into libraries or other + // arbitrary code. +} + +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( + std::atomic* /* lock_word */, bool /* all */) {} + +} // extern "C" diff --git a/src/absl/base/internal/spinlock_linux.inc b/src/absl/base/internal/spinlock_linux.inc new file mode 100644 index 0000000..fe8ba67 --- /dev/null +++ b/src/absl/base/internal/spinlock_linux.inc @@ -0,0 +1,71 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a Linux-specific part of spinlock_wait.cc + +#include +#include +#include + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/internal/errno_saver.h" + +// The SpinLock lockword is `std::atomic`. Here we assert that +// `std::atomic` 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) == sizeof(int), + "SpinLock lockword has the wrong size for a futex"); + +// Some Android headers are missing these definitions even though they +// support these futex operations. +#ifdef __BIONIC__ +#ifndef SYS_futex +#define SYS_futex __NR_futex +#endif +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif +#endif + +#if defined(__NR_futex_time64) && !defined(SYS_futex_time64) +#define SYS_futex_time64 __NR_futex_time64 +#endif + +#if defined(SYS_futex_time64) && !defined(SYS_futex) +#define SYS_futex SYS_futex_time64 +#endif + +extern "C" { + +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( + std::atomic *w, uint32_t value, int, + absl::base_internal::SchedulingMode) { + absl::base_internal::ErrnoSaver errno_saver; + syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, nullptr); +} + +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( + std::atomic *w, bool all) { + syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0); +} + +} // extern "C" diff --git a/src/absl/base/internal/spinlock_posix.inc b/src/absl/base/internal/spinlock_posix.inc new file mode 100644 index 0000000..4f6f887 --- /dev/null +++ b/src/absl/base/internal/spinlock_posix.inc @@ -0,0 +1,46 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a Posix-specific part of spinlock_wait.cc + +#include + +#include +#include + +#include "absl/base/internal/errno_saver.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/port.h" + +extern "C" { + +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( + std::atomic* /* lock_word */, uint32_t /* value */, int loop, + absl::base_internal::SchedulingMode /* mode */) { + absl::base_internal::ErrnoSaver errno_saver; + if (loop == 0) { + } else if (loop == 1) { + sched_yield(); + } else { + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); + nanosleep(&tm, nullptr); + } +} + +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( + std::atomic* /* lock_word */, bool /* all */) {} + +} // extern "C" diff --git a/src/absl/base/internal/spinlock_wait.cc b/src/absl/base/internal/spinlock_wait.cc new file mode 100644 index 0000000..fa824be --- /dev/null +++ b/src/absl/base/internal/spinlock_wait.cc @@ -0,0 +1,81 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The OS-specific header included below must provide two calls: +// AbslInternalSpinLockDelay() and AbslInternalSpinLockWake(). +// See spinlock_wait.h for the specs. + +#include +#include + +#include "absl/base/internal/spinlock_wait.h" + +#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 +#include "absl/base/internal/spinlock_posix.inc" +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// See spinlock_wait.h for spec. +uint32_t SpinLockWait(std::atomic *w, int n, + const SpinLockWaitTransition trans[], + base_internal::SchedulingMode scheduling_mode) { + 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 + w->compare_exchange_strong(v, trans[i].to, + std::memory_order_acquire, + std::memory_order_relaxed)) { + if (trans[i].done) return v; + } + } +} + +static std::atomic delay_rand; + +// Return a suggested delay in nanoseconds for iteration number "loop" +int SpinLockSuggestedDelayNS(int loop) { + // Weak pseudo-random number generator to get some spread between threads + // when many are spinning. + uint64_t r = delay_rand.load(std::memory_order_relaxed); + r = 0x5deece66dLL * r + 0xb; // numbers from nrand48() + delay_rand.store(r, std::memory_order_relaxed); + + if (loop < 0 || loop > 32) { // limit loop to 0..32 + loop = 32; + } + const int kMinDelay = 128 << 10; // 128us + // Double delay every 8 iterations, up to 16x (2ms). + int delay = kMinDelay << (loop / 8); + // Randomize in delay..2*delay range, for resulting 128us..4ms range. + return delay | ((delay - 1) & static_cast(r)); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/spinlock_wait.h b/src/absl/base/internal/spinlock_wait.h new file mode 100644 index 0000000..9a1adcd --- /dev/null +++ b/src/absl/base/internal/spinlock_wait.h @@ -0,0 +1,95 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ +#define ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ + +// Operations to make atomic transitions on a word, and to allow +// waiting for those transitions to become possible. + +#include +#include + +#include "absl/base/internal/scheduling_mode.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// SpinLockWait() waits until it can perform one of several transitions from +// "from" to "to". It returns when it performs a transition where done==true. +struct SpinLockWaitTransition { + uint32_t from; + uint32_t to; + bool done; +}; + +// Wait until *w can transition from trans[i].from to trans[i].to for some i +// satisfying 0<=i *w, int n, + const SpinLockWaitTransition trans[], + SchedulingMode scheduling_mode); + +// If possible, wake some thread that has called SpinLockDelay(w, ...). If `all` +// is true, wake all such threads. On some systems, this may be a no-op; on +// those systems, threads calling SpinLockDelay() will always wake eventually +// even if SpinLockWake() is never called. +void SpinLockWake(std::atomic *w, bool all); + +// Wait for an appropriate spin delay on iteration "loop" of a +// spin loop on location *w, whose previously observed value was "value". +// SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick, +// or may wait for a call to SpinLockWake(w). +void SpinLockDelay(std::atomic *w, uint32_t value, int loop, + base_internal::SchedulingMode scheduling_mode); + +// Helper used by AbslInternalSpinLockDelay. +// Returns a suggested delay in nanoseconds for iteration number "loop". +int SpinLockSuggestedDelayNS(int loop); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +// In some build configurations we pass --detect-odr-violations to the +// gold linker. This causes it to flag weak symbol overrides as ODR +// violations. Because ODR only applies to C++ and not C, +// --detect-odr-violations ignores symbols not mangled with C++ names. +// By changing our extension points to be extern "C", we dodge this +// check. +extern "C" { +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(std::atomic *w, + bool all); +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( + std::atomic *w, uint32_t value, int loop, + absl::base_internal::SchedulingMode scheduling_mode); +} + +inline void absl::base_internal::SpinLockWake(std::atomic *w, + bool all) { + ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(w, all); +} + +inline void absl::base_internal::SpinLockDelay( + std::atomic *w, uint32_t value, int loop, + absl::base_internal::SchedulingMode scheduling_mode) { + ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay) + (w, value, loop, scheduling_mode); +} + +#endif // ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ diff --git a/src/absl/base/internal/spinlock_win32.inc b/src/absl/base/internal/spinlock_win32.inc new file mode 100644 index 0000000..9d22481 --- /dev/null +++ b/src/absl/base/internal/spinlock_win32.inc @@ -0,0 +1,37 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a Win32-specific part of spinlock_wait.cc + +#include +#include +#include "absl/base/internal/scheduling_mode.h" + +extern "C" { + +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( + std::atomic* /* lock_word */, uint32_t /* value */, int loop, + absl::base_internal::SchedulingMode /* mode */) { + if (loop == 0) { + } else if (loop == 1) { + Sleep(0); + } else { + Sleep(absl::base_internal::SpinLockSuggestedDelayNS(loop) / 1000000); + } +} + +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( + std::atomic* /* lock_word */, bool /* all */) {} + +} // extern "C" diff --git a/src/absl/base/internal/strerror.cc b/src/absl/base/internal/strerror.cc new file mode 100644 index 0000000..0d6226f --- /dev/null +++ b/src/absl/base/internal/strerror.cc @@ -0,0 +1,88 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/strerror.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/internal/errno_saver.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { +namespace { + +const char* StrErrorAdaptor(int errnum, char* buf, size_t buflen) { +#if defined(_WIN32) + int rc = strerror_s(buf, buflen, errnum); + buf[buflen - 1] = '\0'; // guarantee NUL termination + if (rc == 0 && strncmp(buf, "Unknown error", buflen) == 0) *buf = '\0'; + return buf; +#else + // The type of `ret` is platform-specific; both of these branches must compile + // either way but only one will execute on any given platform: + auto ret = strerror_r(errnum, buf, buflen); + if (std::is_same::value) { + // XSI `strerror_r`; `ret` is `int`: + if (ret) *buf = '\0'; + return buf; + } else { + // GNU `strerror_r`; `ret` is `char *`: + return reinterpret_cast(ret); + } +#endif +} + +std::string StrErrorInternal(int errnum) { + char buf[100]; + const char* str = StrErrorAdaptor(errnum, buf, sizeof buf); + if (*str == '\0') { + snprintf(buf, sizeof buf, "Unknown error %d", errnum); + str = buf; + } + return str; +} + +// kSysNerr is the number of errors from a recent glibc. `StrError()` falls back +// to `StrErrorAdaptor()` if the value is larger than this. +constexpr int kSysNerr = 135; + +std::array* NewStrErrorTable() { + auto* table = new std::array; + for (int i = 0; i < static_cast(table->size()); ++i) { + (*table)[i] = StrErrorInternal(i); + } + return table; +} + +} // namespace + +std::string StrError(int errnum) { + absl::base_internal::ErrnoSaver errno_saver; + static const auto* table = NewStrErrorTable(); + if (errnum >= 0 && errnum < static_cast(table->size())) { + return (*table)[errnum]; + } + return StrErrorInternal(errnum); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/strerror.h b/src/absl/base/internal/strerror.h new file mode 100644 index 0000000..3500973 --- /dev/null +++ b/src/absl/base/internal/strerror.h @@ -0,0 +1,39 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_STRERROR_H_ +#define ABSL_BASE_INTERNAL_STRERROR_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// A portable and thread-safe alternative to C89's `strerror`. +// +// The C89 specification of `strerror` is not suitable for use in a +// multi-threaded application as the returned string may be changed by calls to +// `strerror` from another thread. The many non-stdlib alternatives differ +// enough in their names, availability, and semantics to justify this wrapper +// around them. `errno` will not be modified by a call to `absl::StrError`. +std::string StrError(int errnum); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_STRERROR_H_ diff --git a/src/absl/base/internal/sysinfo.cc b/src/absl/base/internal/sysinfo.cc new file mode 100644 index 0000000..3d78f8a --- /dev/null +++ b/src/absl/base/internal/sysinfo.cc @@ -0,0 +1,507 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/sysinfo.h" + +#include "absl/base/attributes.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#include +#endif + +#ifdef __linux__ +#include +#endif + +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#endif + +#if defined(__myriad2__) +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include // NOLINT(build/c++11) +#include +#include + +#include "absl/base/call_once.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" +#include "absl/base/internal/unscaledcycleclock.h" +#include "absl/base/thread_annotations.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +namespace { + +#if defined(_WIN32) + +// Returns number of bits set in `bitMask` +DWORD Win32CountSetBits(ULONG_PTR bitMask) { + for (DWORD bitSetCount = 0; ; ++bitSetCount) { + if (bitMask == 0) return bitSetCount; + bitMask &= bitMask - 1; + } +} + +// Returns the number of logical CPUs using GetLogicalProcessorInformation(), or +// 0 if the number of processors is not available or can not be computed. +// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation +int Win32NumCPUs() { +#pragma comment(lib, "kernel32.lib") + using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION; + + DWORD info_size = sizeof(Info); + Info* info(static_cast(malloc(info_size))); + if (info == nullptr) return 0; + + bool success = GetLogicalProcessorInformation(info, &info_size); + if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(info); + info = static_cast(malloc(info_size)); + if (info == nullptr) return 0; + success = GetLogicalProcessorInformation(info, &info_size); + } + + DWORD logicalProcessorCount = 0; + if (success) { + Info* ptr = info; + DWORD byteOffset = 0; + while (byteOffset + sizeof(Info) <= info_size) { + switch (ptr->Relationship) { + case RelationProcessorCore: + logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask); + break; + + case RelationNumaNode: + case RelationCache: + case RelationProcessorPackage: + // Ignore other entries + break; + + default: + // Ignore unknown entries + break; + } + byteOffset += sizeof(Info); + ptr++; + } + } + free(info); + return logicalProcessorCount; +} + +#endif + +} // namespace + +static int GetNumCPUs() { +#if defined(__myriad2__) + return 1; +#elif defined(_WIN32) + const unsigned hardware_concurrency = Win32NumCPUs(); + return hardware_concurrency ? hardware_concurrency : 1; +#elif defined(_AIX) + return sysconf(_SC_NPROCESSORS_ONLN); +#else + // Other possibilities: + // - Read /sys/devices/system/cpu/online and use cpumask_parse() + // - sysconf(_SC_NPROCESSORS_ONLN) + return std::thread::hardware_concurrency(); +#endif +} + +#if defined(_WIN32) + +static double GetNominalCPUFrequency() { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + // UWP apps don't have access to the registry and currently don't provide an + // API informing about CPU nominal frequency. + return 1.0; +#else +#pragma comment(lib, "advapi32.lib") // For Reg* functions. + HKEY key; + // Use the Reg* functions rather than the SH functions because shlwapi.dll + // pulls in gdi32.dll which makes process destruction much more costly. + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, + KEY_READ, &key) == ERROR_SUCCESS) { + DWORD type = 0; + DWORD data = 0; + DWORD data_size = sizeof(data); + auto result = RegQueryValueExA(key, "~MHz", 0, &type, + reinterpret_cast(&data), &data_size); + RegCloseKey(key); + if (result == ERROR_SUCCESS && type == REG_DWORD && + data_size == sizeof(data)) { + return data * 1e6; // Value is MHz. + } + } + return 1.0; +#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP +} + +#elif defined(CTL_HW) && defined(HW_CPU_FREQ) + +static double GetNominalCPUFrequency() { + unsigned freq; + size_t size = sizeof(freq); + int mib[2] = {CTL_HW, HW_CPU_FREQ}; + if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) { + return static_cast(freq); + } + return 1.0; +} + +#else + +// Helper function for reading a long from a file. Returns true if successful +// and the memory location pointed to by value is set to the value read. +static bool ReadLongFromFile(const char *file, long *value) { + bool ret = false; + int fd = open(file, O_RDONLY); + if (fd != -1) { + char line[1024]; + char *err; + memset(line, '\0', sizeof(line)); + int len = read(fd, line, sizeof(line) - 1); + if (len <= 0) { + ret = false; + } else { + const long temp_value = strtol(line, &err, 10); + if (line[0] != '\0' && (*err == '\n' || *err == '\0')) { + *value = temp_value; + ret = true; + } + } + close(fd); + } + return ret; +} + +#if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY) + +// Reads a monotonic time source and returns a value in +// nanoseconds. The returned value uses an arbitrary epoch, not the +// Unix epoch. +static int64_t ReadMonotonicClockNanos() { + struct timespec t; +#ifdef CLOCK_MONOTONIC_RAW + int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t); +#else + int rc = clock_gettime(CLOCK_MONOTONIC, &t); +#endif + if (rc != 0) { + perror("clock_gettime() failed"); + throw std::runtime_error("abort()"); + } + return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec; +} + +class UnscaledCycleClockWrapperForInitializeFrequency { + public: + static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); } +}; + +struct TimeTscPair { + int64_t time; // From ReadMonotonicClockNanos(). + int64_t tsc; // From UnscaledCycleClock::Now(). +}; + +// Returns a pair of values (monotonic kernel time, TSC ticks) that +// approximately correspond to each other. This is accomplished by +// doing several reads and picking the reading with the lowest +// latency. This approach is used to minimize the probability that +// our thread was preempted between clock reads. +static TimeTscPair GetTimeTscPair() { + int64_t best_latency = std::numeric_limits::max(); + TimeTscPair best; + for (int i = 0; i < 10; ++i) { + int64_t t0 = ReadMonotonicClockNanos(); + int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now(); + int64_t t1 = ReadMonotonicClockNanos(); + int64_t latency = t1 - t0; + if (latency < best_latency) { + best_latency = latency; + best.time = t0; + best.tsc = tsc; + } + } + return best; +} + +// Measures and returns the TSC frequency by taking a pair of +// measurements approximately `sleep_nanoseconds` apart. +static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) { + auto t0 = GetTimeTscPair(); + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = sleep_nanoseconds; + while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {} + auto t1 = GetTimeTscPair(); + double elapsed_ticks = t1.tsc - t0.tsc; + double elapsed_time = (t1.time - t0.time) * 1e-9; + return elapsed_ticks / elapsed_time; +} + +// Measures and returns the TSC frequency by calling +// MeasureTscFrequencyWithSleep(), doubling the sleep interval until the +// frequency measurement stabilizes. +static double MeasureTscFrequency() { + double last_measurement = -1.0; + int sleep_nanoseconds = 1000000; // 1 millisecond. + for (int i = 0; i < 8; ++i) { + double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds); + if (measurement * 0.99 < last_measurement && + last_measurement < measurement * 1.01) { + // Use the current measurement if it is within 1% of the + // previous measurement. + return measurement; + } + last_measurement = measurement; + sleep_nanoseconds *= 2; + } + return last_measurement; +} + +#endif // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY + +static double GetNominalCPUFrequency() { + long freq = 0; + + // Google's production kernel has a patch to export the TSC + // frequency through sysfs. If the kernel is exporting the TSC + // frequency use that. There are issues where cpuinfo_max_freq + // cannot be relied on because the BIOS may be exporting an invalid + // p-state (on x86) or p-states may be used to put the processor in + // a new mode (turbo mode). Essentially, those frequencies cannot + // always be relied upon. The same reasons apply to /proc/cpuinfo as + // well. + if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) { + return freq * 1e3; // Value is kHz. + } + +#if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY) + // On these platforms, the TSC frequency is the nominal CPU + // frequency. But without having the kernel export it directly + // though /sys/devices/system/cpu/cpu0/tsc_freq_khz, there is no + // other way to reliably get the TSC frequency, so we have to + // measure it ourselves. Some CPUs abuse cpuinfo_max_freq by + // exporting "fake" frequencies for implementing new features. For + // example, Intel's turbo mode is enabled by exposing a p-state + // value with a higher frequency than that of the real TSC + // rate. Because of this, we prefer to measure the TSC rate + // ourselves on i386 and x86-64. + return MeasureTscFrequency(); +#else + + // If CPU scaling is in effect, we want to use the *maximum* + // frequency, not whatever CPU speed some random processor happens + // to be using now. + if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", + &freq)) { + return freq * 1e3; // Value is kHz. + } + + return 1.0; +#endif // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY +} + +#endif + +ABSL_CONST_INIT static once_flag init_num_cpus_once; +ABSL_CONST_INIT static int num_cpus = 0; + +// NumCPUs() may be called before main() and before malloc is properly +// initialized, therefore this must not allocate memory. +int NumCPUs() { + base_internal::LowLevelCallOnce( + &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); }); + return num_cpus; +} + +// A default frequency of 0.0 might be dangerous if it is used in division. +ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once; +ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0; + +// NominalCPUFrequency() may be called before main() and before malloc is +// properly initialized, therefore this must not allocate memory. +double NominalCPUFrequency() { + base_internal::LowLevelCallOnce( + &init_nominal_cpu_frequency_once, + []() { nominal_cpu_frequency = GetNominalCPUFrequency(); }); + return nominal_cpu_frequency; +} + +#if defined(_WIN32) + +pid_t GetTID() { + return pid_t{GetCurrentThreadId()}; +} + +#elif defined(__linux__) + +#ifndef SYS_gettid +#define SYS_gettid __NR_gettid +#endif + +pid_t GetTID() { + return syscall(SYS_gettid); +} + +#elif defined(__akaros__) + +pid_t GetTID() { + // Akaros has a concept of "vcore context", which is the state the program + // is forced into when we need to make a user-level scheduling decision, or + // run a signal handler. This is analogous to the interrupt context that a + // CPU might enter if it encounters some kind of exception. + // + // There is no current thread context in vcore context, but we need to give + // a reasonable answer if asked for a thread ID (e.g., in a signal handler). + // Thread 0 always exists, so if we are in vcore context, we return that. + // + // Otherwise, we know (since we are using pthreads) that the uthread struct + // current_uthread is pointing to is the first element of a + // struct pthread_tcb, so we extract and return the thread ID from that. + // + // TODO(dcross): Akaros anticipates moving the thread ID to the uthread + // structure at some point. We should modify this code to remove the cast + // when that happens. + if (in_vcore_context()) + return 0; + return reinterpret_cast(current_uthread)->id; +} + +#elif defined(__myriad2__) + +pid_t GetTID() { + uint32_t tid; + rtems_task_ident(RTEMS_SELF, 0, &tid); + return tid; +} + +#else + +// Fallback implementation of GetTID using pthread_getspecific. +ABSL_CONST_INIT static once_flag tid_once; +ABSL_CONST_INIT static pthread_key_t tid_key; +ABSL_CONST_INIT static absl::base_internal::SpinLock tid_lock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); + +// We set a bit per thread in this array to indicate that an ID is in +// use. ID 0 is unused because it is the default value returned by +// pthread_getspecific(). +ABSL_CONST_INIT static std::vector *tid_array + ABSL_GUARDED_BY(tid_lock) = nullptr; +static constexpr int kBitsPerWord = 32; // tid_array is uint32_t. + +// Returns the TID to tid_array. +static void FreeTID(void *v) { + intptr_t tid = reinterpret_cast(v); + int word = tid / kBitsPerWord; + uint32_t mask = ~(1u << (tid % kBitsPerWord)); + absl::base_internal::SpinLockHolder lock(&tid_lock); + assert(0 <= word && static_cast(word) < tid_array->size()); + (*tid_array)[word] &= mask; +} + +static void InitGetTID() { + if (pthread_key_create(&tid_key, FreeTID) != 0) { + // The logging system calls GetTID() so it can't be used here. + perror("pthread_key_create failed"); + throw std::runtime_error("abort()"); + } + + // Initialize tid_array. + absl::base_internal::SpinLockHolder lock(&tid_lock); + tid_array = new std::vector(1); + (*tid_array)[0] = 1; // ID 0 is never-allocated. +} + +// Return a per-thread small integer ID from pthread's thread-specific data. +pid_t GetTID() { + absl::call_once(tid_once, InitGetTID); + + intptr_t tid = reinterpret_cast(pthread_getspecific(tid_key)); + if (tid != 0) { + return tid; + } + + int bit; // tid_array[word] = 1u << bit; + size_t word; + { + // Search for the first unused ID. + absl::base_internal::SpinLockHolder lock(&tid_lock); + // First search for a word in the array that is not all ones. + word = 0; + while (word < tid_array->size() && ~(*tid_array)[word] == 0) { + ++word; + } + if (word == tid_array->size()) { + tid_array->push_back(0); // No space left, add kBitsPerWord more IDs. + } + // Search for a zero bit in the word. + bit = 0; + while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) { + ++bit; + } + tid = (word * kBitsPerWord) + bit; + (*tid_array)[word] |= 1u << bit; // Mark the TID as allocated. + } + + if (pthread_setspecific(tid_key, reinterpret_cast(tid)) != 0) { + perror("pthread_setspecific failed"); + throw std::runtime_error("abort()"); + } + + return static_cast(tid); +} + +#endif + +// GetCachedTID() caches the thread ID in thread-local storage (which is a +// userspace construct) to avoid unnecessary system calls. Without this caching, +// it can take roughly 98ns, while it takes roughly 1ns with this caching. +pid_t GetCachedTID() { +#ifdef ABSL_HAVE_THREAD_LOCAL + static thread_local pid_t thread_id = GetTID(); + return thread_id; +#else + return GetTID(); +#endif // ABSL_HAVE_THREAD_LOCAL +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/sysinfo.h b/src/absl/base/internal/sysinfo.h new file mode 100644 index 0000000..119cf1f --- /dev/null +++ b/src/absl/base/internal/sysinfo.h @@ -0,0 +1,74 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file includes routines to find out characteristics +// of the machine a program is running on. It is undoubtedly +// system-dependent. + +// Functions listed here that accept a pid_t as an argument act on the +// current process if the pid_t argument is 0 +// All functions here are thread-hostile due to file caching unless +// commented otherwise. + +#ifndef ABSL_BASE_INTERNAL_SYSINFO_H_ +#define ABSL_BASE_INTERNAL_SYSINFO_H_ + +#ifndef _WIN32 +#include +#endif + +#include + +#include "absl/base/config.h" +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Nominal core processor cycles per second of each processor. This is _not_ +// necessarily the frequency of the CycleClock counter (see cycleclock.h) +// Thread-safe. +double NominalCPUFrequency(); + +// Number of logical processors (hyperthreads) in system. Thread-safe. +int NumCPUs(); + +// Return the thread id of the current thread, as told by the system. +// No two currently-live threads implemented by the OS shall have the same ID. +// Thread ids of exited threads may be reused. Multiple user-level threads +// may have the same thread ID if multiplexed on the same OS thread. +// +// On Linux, you may send a signal to the resulting ID with kill(). However, +// it is recommended for portability that you use pthread_kill() instead. +#ifdef _WIN32 +// On Windows, process id and thread id are of the same type according to the +// return types of GetProcessId() and GetThreadId() are both DWORD, an unsigned +// 32-bit type. +using pid_t = uint32_t; +#endif +pid_t GetTID(); + +// Like GetTID(), but caches the result in thread-local storage in order +// to avoid unnecessary system calls. Note that there are some cases where +// one must call through to GetTID directly, which is why this exists as a +// separate function. For example, GetCachedTID() is not safe to call in +// an asynchronous signal-handling context nor right after a call to fork(). +pid_t GetCachedTID(); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SYSINFO_H_ diff --git a/src/absl/base/internal/thread_annotations.h b/src/absl/base/internal/thread_annotations.h new file mode 100644 index 0000000..4dab6a9 --- /dev/null +++ b/src/absl/base/internal/thread_annotations.h @@ -0,0 +1,271 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: thread_annotations.h +// ----------------------------------------------------------------------------- +// +// WARNING: This is a backwards compatible header and it will be removed after +// the migration to prefixed thread annotations is finished; please include +// "absl/base/thread_annotations.h". +// +// This header file contains macro definitions for thread safety annotations +// that allow developers to document the locking policies of multi-threaded +// code. The annotations can also help program analysis tools to identify +// potential thread safety issues. +// +// These annotations are implemented using compiler attributes. Using the macros +// defined here instead of raw attributes allow for portability and future +// compatibility. +// +// When referring to mutexes in the arguments of the attributes, you should +// use variable names or more complex expressions (e.g. my_object->mutex_) +// that evaluate to a concrete mutex object whenever possible. If the mutex +// you want to refer to is not in scope, you may use a member pointer +// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. + +#ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ +#define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ + +#if defined(__clang__) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +// GUARDED_BY() +// +// Documents if a shared field or global variable needs to be protected by a +// mutex. GUARDED_BY() allows the user to specify a particular mutex that +// should be held when accessing the annotated variable. +// +// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to +// local variables, a local variable and its associated mutex can often be +// combined into a small class or struct, thereby allowing the annotation. +// +// Example: +// +// class Foo { +// Mutex mu_; +// int p1_ GUARDED_BY(mu_); +// ... +// }; +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +// PT_GUARDED_BY() +// +// Documents if the memory location pointed to by a pointer should be guarded +// by a mutex when dereferencing the pointer. +// +// Example: +// class Foo { +// Mutex mu_; +// int *p1_ PT_GUARDED_BY(mu_); +// ... +// }; +// +// Note that a pointer variable to a shared memory location could itself be a +// shared variable. +// +// Example: +// +// // `q_`, guarded by `mu1_`, points to a shared memory location that is +// // guarded by `mu2_`: +// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_); +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +// ACQUIRED_AFTER() / ACQUIRED_BEFORE() +// +// Documents the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER +// and ACQUIRED_BEFORE.) +// +// As with GUARDED_BY, this is only applicable to mutexes that are shared +// fields or global variables. +// +// Example: +// +// Mutex m1_; +// Mutex m2_ ACQUIRED_AFTER(m1_); +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED() +// +// Documents a function that expects a mutex to be held prior to entry. +// The mutex is expected to be held both on entry to, and exit from, the +// function. +// +// An exclusive lock allows read-write access to the guarded data member(s), and +// only one thread can acquire a lock exclusively at any one time. A shared lock +// allows read-only access, and any number of threads can acquire a shared lock +// concurrently. +// +// Generally, non-const methods should be annotated with +// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with +// SHARED_LOCKS_REQUIRED. +// +// Example: +// +// Mutex mu1, mu2; +// int a GUARDED_BY(mu1); +// int b GUARDED_BY(mu2); +// +// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } +// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) + +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// LOCKS_EXCLUDED() +// +// Documents the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as Abseil's `Mutex` locks are +// non-reentrant). +#define LOCKS_EXCLUDED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +// LOCK_RETURNED() +// +// Documents a function that returns a mutex without acquiring it. For example, +// a public getter method that returns a pointer to a private mutex should +// be annotated with LOCK_RETURNED. +#define LOCK_RETURNED(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +// LOCKABLE +// +// Documents if a class/type is a lockable type (such as the `Mutex` class). +#define LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// SCOPED_LOCKABLE +// +// Documents if a class does RAII locking (such as the `MutexLock` class). +// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is +// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no +// arguments; the analysis will assume that the destructor unlocks whatever the +// constructor locked. +#define SCOPED_LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// EXCLUSIVE_LOCK_FUNCTION() +// +// Documents functions that acquire a lock in the body of a function, and do +// not release it. +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +// SHARED_LOCK_FUNCTION() +// +// Documents functions that acquire a shared (reader) lock in the body of a +// function, and do not release it. +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +// UNLOCK_FUNCTION() +// +// Documents functions that expect a lock to be held on entry to the function, +// and release it in the body of the function. +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION() +// +// Documents functions that try to acquire a lock, and return success or failure +// (or a non-boolean value that can be interpreted as a boolean). +// The first argument should be `true` for functions that return `true` on +// success, or `false` for functions that return `false` on success. The second +// argument specifies the mutex that is locked on success. If unspecified, this +// mutex is assumed to be `this`. +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK() +// +// Documents functions that dynamically check to see if a lock is held, and fail +// if it is not held. +#define ASSERT_EXCLUSIVE_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) + +#define ASSERT_SHARED_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) + +// NO_THREAD_SAFETY_ANALYSIS +// +// Turns off thread safety checking within the body of a particular function. +// This annotation is used to mark functions that are known to be correct, but +// the locking behavior is more complicated than the analyzer can handle. +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +//------------------------------------------------------------------------------ +// Tool-Supplied Annotations +//------------------------------------------------------------------------------ + +// TS_UNCHECKED should be placed around lock expressions that are not valid +// C++ syntax, but which are present for documentation purposes. These +// annotations will be ignored by the analysis. +#define TS_UNCHECKED(x) "" + +// TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// It is used by automated tools to mark and disable invalid expressions. +// The annotation should either be fixed, or changed to TS_UNCHECKED. +#define TS_FIXME(x) "" + +// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of +// a particular function. However, this attribute is used to mark functions +// that are incorrect and need to be fixed. It is used by automated tools to +// avoid breaking the build when the analysis is updated. +// Code owners are expected to eventually fix the routine. +#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS + +// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY +// annotation that needs to be fixed, because it is producing thread safety +// warning. It disables the GUARDED_BY. +#define GUARDED_BY_FIXME(x) + +// Disables warnings for a single read operation. This can be used to avoid +// warnings when it is known that the read is not actually involved in a race, +// but the compiler cannot confirm that. +#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x) + + +namespace thread_safety_analysis { + +// Takes a reference to a guarded data member, and returns an unguarded +// reference. +template +inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +template +inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +} // namespace thread_safety_analysis + +#endif // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ diff --git a/src/absl/base/internal/thread_identity.cc b/src/absl/base/internal/thread_identity.cc new file mode 100644 index 0000000..79853f0 --- /dev/null +++ b/src/absl/base/internal/thread_identity.cc @@ -0,0 +1,156 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/thread_identity.h" + +#if !defined(_WIN32) || defined(__MINGW32__) +#include +#include +#endif + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/call_once.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +#if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +namespace { +// Used to co-ordinate one-time creation of our pthread_key +absl::once_flag init_thread_identity_key_once; +pthread_key_t thread_identity_pthread_key; +std::atomic pthread_key_initialized(false); + +void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) { + pthread_key_create(&thread_identity_pthread_key, reclaimer); + pthread_key_initialized.store(true, std::memory_order_release); +} +} // namespace +#endif + +#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ + ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +// The actual TLS storage for a thread's currently associated ThreadIdentity. +// This is referenced by inline accessors in the header. +// "protected" visibility ensures that if multiple instances of Abseil code +// exist within a process (via dlopen() or similar), references to +// thread_identity_ptr from each instance of the code will refer to +// *different* instances of this ptr. +// Apple platforms have the visibility attribute, but issue a compile warning +// that protected visibility is unsupported. +ABSL_CONST_INIT // Must come before __attribute__((visibility("protected"))) +#if ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__) +__attribute__((visibility("protected"))) +#endif // ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__) +#if ABSL_PER_THREAD_TLS +// Prefer __thread to thread_local as benchmarks indicate it is a bit faster. +ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr = nullptr; +#elif defined(ABSL_HAVE_THREAD_LOCAL) +thread_local ThreadIdentity* thread_identity_ptr = nullptr; +#endif // ABSL_PER_THREAD_TLS +#endif // TLS or CPP11 + +void SetCurrentThreadIdentity( + ThreadIdentity* identity, ThreadIdentityReclaimerFunction reclaimer) { + assert(CurrentThreadIdentityIfPresent() == nullptr); + // Associate our destructor. + // NOTE: This call to pthread_setspecific is currently the only immovable + // barrier to CurrentThreadIdentity() always being async signal safe. +#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC + // NOTE: Not async-safe. But can be open-coded. + absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey, + reclaimer); + +#if defined(__EMSCRIPTEN__) || defined(__MINGW32__) + // Emscripten and MinGW pthread implementations 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(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. + // + // While not officially async-signal safe, getspecific within a signal handler + // is otherwise OK. + sigset_t all_signals; + sigset_t curr_signals; + sigfillset(&all_signals); + pthread_sigmask(SIG_SETMASK, &all_signals, &curr_signals); + pthread_setspecific(thread_identity_pthread_key, + reinterpret_cast(identity)); + pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr); +#endif // !__EMSCRIPTEN__ && !__MINGW32__ + +#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, + reclaimer); + pthread_setspecific(thread_identity_pthread_key, + reinterpret_cast(identity)); + thread_identity_ptr = identity; +#elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 + thread_local std::unique_ptr + holder(identity, reclaimer); + thread_identity_ptr = identity; +#else +#error Unimplemented ABSL_THREAD_IDENTITY_MODE +#endif +} + +#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ + ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 + +// Please see the comment on `CurrentThreadIdentityIfPresent` in +// thread_identity.h. When we cannot expose thread_local variables in +// headers, we opt for the correct-but-slower option of not inlining this +// function. +#ifndef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT +ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } +#endif +#endif + +void ClearCurrentThreadIdentity() { +#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ + ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 + thread_identity_ptr = nullptr; +#elif ABSL_THREAD_IDENTITY_MODE == \ + ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC + // pthread_setspecific expected to clear value on destruction + assert(CurrentThreadIdentityIfPresent() == nullptr); +#endif +} + +#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC +ThreadIdentity* CurrentThreadIdentityIfPresent() { + bool initialized = pthread_key_initialized.load(std::memory_order_acquire); + if (!initialized) { + return nullptr; + } + return reinterpret_cast( + pthread_getspecific(thread_identity_pthread_key)); +} +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/thread_identity.h b/src/absl/base/internal/thread_identity.h new file mode 100644 index 0000000..659694b --- /dev/null +++ b/src/absl/base/internal/thread_identity.h @@ -0,0 +1,265 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Each active thread has an ThreadIdentity that may represent the thread in +// various level interfaces. ThreadIdentity objects are never deallocated. +// When a thread terminates, its ThreadIdentity object may be reused for a +// thread created later. + +#ifndef ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ +#define ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ + +#ifndef _WIN32 +#include +// Defines __GOOGLE_GRTE_VERSION__ (via glibc-specific features.h) when +// supported. +#include +#endif + +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/per_thread_tls.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +struct SynchLocksHeld; +struct SynchWaitParams; + +namespace base_internal { + +class SpinLock; +struct ThreadIdentity; + +// Used by the implementation of absl::Mutex and absl::CondVar. +struct PerThreadSynch { + // The internal representation of absl::Mutex and absl::CondVar rely + // on the alignment of PerThreadSynch. Both store the address of the + // PerThreadSynch in the high-order bits of their internal state, + // which means the low kLowZeroBits of the address of PerThreadSynch + // must be zero. + static constexpr int kLowZeroBits = 8; + static constexpr int kAlignment = 1 << kLowZeroBits; + + // Returns the associated ThreadIdentity. + // This can be implemented as a cast because we guarantee + // PerThreadSynch is the first element of ThreadIdentity. + ThreadIdentity* thread_identity() { + return reinterpret_cast(this); + } + + PerThreadSynch *next; // Circular waiter queue; initialized to 0. + PerThreadSynch *skip; // If non-zero, all entries in Mutex queue + // up to and including "skip" have same + // condition as this, and will be woken later + bool may_skip; // if false while on mutex queue, a mutex unlocker + // is using this PerThreadSynch as a terminator. Its + // skip field must not be filled in because the loop + // might then skip over the terminator. + bool wake; // This thread is to be woken from a Mutex. + // If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the + // waiter is waiting on the mutex as part of a CV Wait or Mutex Await. + // + // The value of "x->cond_waiter" is meaningless if "x" is not on a + // Mutex waiter list. + bool cond_waiter; + bool maybe_unlocking; // Valid at head of Mutex waiter queue; + // true if UnlockSlow could be searching + // for a waiter to wake. Used for an optimization + // in Enqueue(). true is always a valid value. + // Can be reset to false when the unlocker or any + // writer releases the lock, or a reader fully + // releases the lock. It may not be set to false + // by a reader that decrements the count to + // non-zero. protected by mutex spinlock + bool suppress_fatal_errors; // If true, try to proceed even in the face + // of broken invariants. This is used within + // fatal signal handlers to improve the + // chances of debug logging information being + // output successfully. + int priority; // Priority of thread (updated every so often). + + // State values: + // kAvailable: This PerThreadSynch is available. + // kQueued: This PerThreadSynch is unavailable, it's currently queued on a + // Mutex or CondVar waistlist. + // + // Transitions from kQueued to kAvailable require a release + // barrier. This is needed as a waiter may use "state" to + // independently observe that it's no longer queued. + // + // Transitions from kAvailable to kQueued require no barrier, they + // are externally ordered by the Mutex. + enum State { + kAvailable, + kQueued + }; + std::atomic state; + + // The wait parameters of the current wait. waitp is null if the + // thread is not waiting. Transitions from null to non-null must + // occur before the enqueue commit point (state = kQueued in + // Enqueue() and CondVarEnqueue()). Transitions from non-null to + // null must occur after the wait is finished (state = kAvailable in + // Mutex::Block() and CondVar::WaitCommon()). This field may be + // changed only by the thread that describes this PerThreadSynch. A + // special case is Fer(), which calls Enqueue() on another thread, + // but with an identical SynchWaitParams pointer, thus leaving the + // pointer unchanged. + SynchWaitParams* waitp; + + intptr_t readers; // Number of readers in mutex. + + // When priority will next be read (cycles). + int64_t next_priority_read_cycles; + + // Locks held; used during deadlock detection. + // Allocated in Synch_GetAllLocks() and freed in ReclaimThreadIdentity(). + SynchLocksHeld *all_locks; +}; + +// The instances of this class are allocated in NewThreadIdentity() with an +// alignment of PerThreadSynch::kAlignment. +struct ThreadIdentity { + // Must be the first member. The Mutex implementation requires that + // the PerThreadSynch object associated with each thread is + // PerThreadSynch::kAlignment aligned. We provide this alignment on + // ThreadIdentity itself. + PerThreadSynch per_thread_synch; + + // Private: Reserved for absl::synchronization_internal::Waiter. + struct WaiterState { + alignas(void*) char data[128]; + } waiter_state; + + // Used by PerThreadSem::{Get,Set}ThreadBlockedCounter(). + std::atomic* blocked_count_ptr; + + // The following variables are mostly read/written just by the + // thread itself. The only exception is that these are read by + // a ticker thread as a hint. + std::atomic ticker; // Tick counter, incremented once per second. + std::atomic wait_start; // Ticker value when thread started waiting. + std::atomic is_idle; // Has thread become idle yet? + + ThreadIdentity* next; +}; + +// Returns the ThreadIdentity object representing the calling thread; guaranteed +// to be unique for its lifetime. The returned object will remain valid for the +// program's lifetime; although it may be re-assigned to a subsequent thread. +// If one does not exist, return nullptr instead. +// +// Does not malloc(*), and is async-signal safe. +// [*] Technically pthread_setspecific() does malloc on first use; however this +// is handled internally within tcmalloc's initialization already. +// +// New ThreadIdentity objects can be constructed and associated with a thread +// by calling GetOrCreateCurrentThreadIdentity() in per-thread-sem.h. +ThreadIdentity* CurrentThreadIdentityIfPresent(); + +using ThreadIdentityReclaimerFunction = void (*)(void*); + +// Sets the current thread identity to the given value. 'reclaimer' is a +// pointer to the global function for cleaning up instances on thread +// destruction. +void SetCurrentThreadIdentity(ThreadIdentity* identity, + ThreadIdentityReclaimerFunction reclaimer); + +// Removes the currently associated ThreadIdentity from the running thread. +// This must be called from inside the ThreadIdentityReclaimerFunction, and only +// from that function. +void ClearCurrentThreadIdentity(); + +// May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE= +#ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC +#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be directly set +#else +#define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0 +#endif + +#ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS +#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be directly set +#else +#define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1 +#endif + +#ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be directly set +#else +#define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2 +#endif + +#ifdef ABSL_THREAD_IDENTITY_MODE +#error ABSL_THREAD_IDENTITY_MODE cannot be directly set +#elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE) +#define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE +#elif defined(_WIN32) && !defined(__MINGW32__) +#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +#elif defined(__APPLE__) && defined(ABSL_HAVE_THREAD_LOCAL) +#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +#elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \ + (__GOOGLE_GRTE_VERSION__ >= 20140228L) +// Support for async-safe TLS was specifically added in GRTEv4. It's not +// present in the upstream eglibc. +// Note: Current default for production systems. +#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_TLS +#else +#define ABSL_THREAD_IDENTITY_MODE \ + ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC +#endif + +#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ + ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 + +#if ABSL_PER_THREAD_TLS +ABSL_CONST_INIT extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* + thread_identity_ptr; +#elif defined(ABSL_HAVE_THREAD_LOCAL) +ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr; +#else +#error Thread-local storage not detected on this platform +#endif + +// thread_local variables cannot be in headers exposed by DLLs or in certain +// build configurations on Apple platforms. However, it is important for +// performance reasons in general that `CurrentThreadIdentityIfPresent` be +// inlined. In the other cases we opt to have the function not be inlined. Note +// that `CurrentThreadIdentityIfPresent` is declared above so we can exclude +// this entire inline definition. +#if !defined(__APPLE__) && !defined(ABSL_BUILD_DLL) && \ + !defined(ABSL_CONSUME_DLL) +#define ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT 1 +#endif + +#ifdef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT +inline ThreadIdentity* CurrentThreadIdentityIfPresent() { + return thread_identity_ptr; +} +#endif + +#elif ABSL_THREAD_IDENTITY_MODE != \ + ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC +#error Unknown ABSL_THREAD_IDENTITY_MODE +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ diff --git a/src/absl/base/internal/throw_delegate.cc b/src/absl/base/internal/throw_delegate.cc new file mode 100644 index 0000000..c260ff1 --- /dev/null +++ b/src/absl/base/internal/throw_delegate.cc @@ -0,0 +1,212 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/throw_delegate.h" + +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// NOTE: The various STL exception throwing functions are placed within the +// #ifdef blocks so the symbols aren't exposed on platforms that don't support +// them, such as the Android NDK. For example, ANGLE fails to link when building +// within AOSP without them, since the STL functions don't exist. +namespace { +#ifdef ABSL_HAVE_EXCEPTIONS +template +[[noreturn]] void Throw(const T& error) { + throw error; +} +#endif +} // namespace + +void ThrowStdLogicError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::logic_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdLogicError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::logic_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} +void ThrowStdInvalidArgument(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::invalid_argument(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdInvalidArgument(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::invalid_argument(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdDomainError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::domain_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdDomainError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::domain_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdLengthError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::length_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdLengthError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::length_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdOutOfRange(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::out_of_range(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdOutOfRange(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::out_of_range(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdRuntimeError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::runtime_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdRuntimeError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::runtime_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdRangeError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::range_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdRangeError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::range_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdOverflowError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::overflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdOverflowError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::overflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdUnderflowError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::underflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdUnderflowError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::underflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdBadFunctionCall() { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::bad_function_call()); +#else + std::abort(); +#endif +} + +void ThrowStdBadAlloc() { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::bad_alloc()); +#else + std::abort(); +#endif +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/throw_delegate.h b/src/absl/base/internal/throw_delegate.h new file mode 100644 index 0000000..075f527 --- /dev/null +++ b/src/absl/base/internal/throw_delegate.h @@ -0,0 +1,75 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ +#define ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Helper functions that allow throwing exceptions consistently from anywhere. +// The main use case is for header-based libraries (eg templates), as they will +// be built by many different targets with their own compiler options. +// In particular, this will allow a safe way to throw exceptions even if the +// caller is compiled with -fno-exceptions. This is intended for implementing +// things like map<>::at(), which the standard documents as throwing an +// exception on error. +// +// Using other techniques like #if tricks could lead to ODR violations. +// +// You shouldn't use it unless you're writing code that you know will be built +// both with and without exceptions and you need to conform to an interface +// that uses exceptions. + +[[noreturn]] void ThrowStdLogicError(const std::string& what_arg); +[[noreturn]] void ThrowStdLogicError(const char* what_arg); +[[noreturn]] void ThrowStdInvalidArgument(const std::string& what_arg); +[[noreturn]] void ThrowStdInvalidArgument(const char* what_arg); +[[noreturn]] void ThrowStdDomainError(const std::string& what_arg); +[[noreturn]] void ThrowStdDomainError(const char* what_arg); +[[noreturn]] void ThrowStdLengthError(const std::string& what_arg); +[[noreturn]] void ThrowStdLengthError(const char* what_arg); +[[noreturn]] void ThrowStdOutOfRange(const std::string& what_arg); +[[noreturn]] void ThrowStdOutOfRange(const char* what_arg); +[[noreturn]] void ThrowStdRuntimeError(const std::string& what_arg); +[[noreturn]] void ThrowStdRuntimeError(const char* what_arg); +[[noreturn]] void ThrowStdRangeError(const std::string& what_arg); +[[noreturn]] void ThrowStdRangeError(const char* what_arg); +[[noreturn]] void ThrowStdOverflowError(const std::string& what_arg); +[[noreturn]] void ThrowStdOverflowError(const char* what_arg); +[[noreturn]] void ThrowStdUnderflowError(const std::string& what_arg); +[[noreturn]] void ThrowStdUnderflowError(const char* what_arg); + +[[noreturn]] void ThrowStdBadFunctionCall(); +[[noreturn]] void ThrowStdBadAlloc(); + +// ThrowStdBadArrayNewLength() cannot be consistently supported because +// std::bad_array_new_length is missing in libstdc++ until 4.9.0. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.3/libstdc++/api/a01379_source.html +// https://gcc.gnu.org/onlinedocs/gcc-4.9.0/libstdc++/api/a01327_source.html +// libcxx (as of 3.2) and msvc (as of 2015) both have it. +// [[noreturn]] void ThrowStdBadArrayNewLength(); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ diff --git a/src/absl/base/internal/tsan_mutex_interface.h b/src/absl/base/internal/tsan_mutex_interface.h new file mode 100644 index 0000000..39207d8 --- /dev/null +++ b/src/absl/base/internal/tsan_mutex_interface.h @@ -0,0 +1,68 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is intended solely for spinlock.h. +// It provides ThreadSanitizer annotations for custom mutexes. +// See for meaning of these annotations. + +#ifndef ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ +#define ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ + +#include "absl/base/config.h" + +// ABSL_INTERNAL_HAVE_TSAN_INTERFACE +// Macro intended only for internal use. +// +// Checks whether LLVM Thread Sanitizer interfaces are available. +// First made available in LLVM 5.0 (Sep 2017). +#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE +#error "ABSL_INTERNAL_HAVE_TSAN_INTERFACE cannot be directly set." +#endif + +#if defined(ABSL_HAVE_THREAD_SANITIZER) && defined(__has_include) +#if __has_include() +#define ABSL_INTERNAL_HAVE_TSAN_INTERFACE 1 +#endif +#endif + +#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE +#include + +#define ABSL_TSAN_MUTEX_CREATE __tsan_mutex_create +#define ABSL_TSAN_MUTEX_DESTROY __tsan_mutex_destroy +#define ABSL_TSAN_MUTEX_PRE_LOCK __tsan_mutex_pre_lock +#define ABSL_TSAN_MUTEX_POST_LOCK __tsan_mutex_post_lock +#define ABSL_TSAN_MUTEX_PRE_UNLOCK __tsan_mutex_pre_unlock +#define ABSL_TSAN_MUTEX_POST_UNLOCK __tsan_mutex_post_unlock +#define ABSL_TSAN_MUTEX_PRE_SIGNAL __tsan_mutex_pre_signal +#define ABSL_TSAN_MUTEX_POST_SIGNAL __tsan_mutex_post_signal +#define ABSL_TSAN_MUTEX_PRE_DIVERT __tsan_mutex_pre_divert +#define ABSL_TSAN_MUTEX_POST_DIVERT __tsan_mutex_post_divert + +#else + +#define ABSL_TSAN_MUTEX_CREATE(...) +#define ABSL_TSAN_MUTEX_DESTROY(...) +#define ABSL_TSAN_MUTEX_PRE_LOCK(...) +#define ABSL_TSAN_MUTEX_POST_LOCK(...) +#define ABSL_TSAN_MUTEX_PRE_UNLOCK(...) +#define ABSL_TSAN_MUTEX_POST_UNLOCK(...) +#define ABSL_TSAN_MUTEX_PRE_SIGNAL(...) +#define ABSL_TSAN_MUTEX_POST_SIGNAL(...) +#define ABSL_TSAN_MUTEX_PRE_DIVERT(...) +#define ABSL_TSAN_MUTEX_POST_DIVERT(...) + +#endif + +#endif // ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ diff --git a/src/absl/base/internal/unaligned_access.h b/src/absl/base/internal/unaligned_access.h new file mode 100644 index 0000000..093dd9b --- /dev/null +++ b/src/absl/base/internal/unaligned_access.h @@ -0,0 +1,82 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ +#define ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ + +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +// unaligned APIs + +// Portable handling of unaligned loads, stores, and copies. + +// The unaligned API is C++ only. The declarations use C++ features +// (namespaces, inline) which are absent or incompatible in C. +#if defined(__cplusplus) +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +inline uint16_t UnalignedLoad16(const void *p) { + uint16_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint32_t UnalignedLoad32(const void *p) { + uint32_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint64_t UnalignedLoad64(const void *p) { + uint64_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); } + +inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); } + +inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ + (absl::base_internal::UnalignedLoad16(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ + (absl::base_internal::UnalignedLoad32(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ + (absl::base_internal::UnalignedLoad64(_p)) + +#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ + (absl::base_internal::UnalignedStore16(_p, _val)) +#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ + (absl::base_internal::UnalignedStore32(_p, _val)) +#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ + (absl::base_internal::UnalignedStore64(_p, _val)) + +#endif // defined(__cplusplus), end of unaligned API + +#endif // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ diff --git a/src/absl/base/internal/unscaledcycleclock.cc b/src/absl/base/internal/unscaledcycleclock.cc new file mode 100644 index 0000000..b1c396c --- /dev/null +++ b/src/absl/base/internal/unscaledcycleclock.cc @@ -0,0 +1,153 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/unscaledcycleclock.h" + +#if ABSL_USE_UNSCALED_CYCLECLOCK + +#if defined(_WIN32) +#include +#endif + +#if defined(__powerpc__) || defined(__ppc__) +#ifdef __GLIBC__ +#include +#elif defined(__FreeBSD__) +// clang-format off +// This order does actually matter =(. +#include +#include +// clang-format on + +#include "absl/base/call_once.h" +#endif +#endif + +#include "absl/base/internal/sysinfo.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +#if defined(__i386__) + +int64_t UnscaledCycleClock::Now() { + int64_t ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + return ret; +} + +double UnscaledCycleClock::Frequency() { + return base_internal::NominalCPUFrequency(); +} + +#elif defined(__x86_64__) + +double UnscaledCycleClock::Frequency() { + return base_internal::NominalCPUFrequency(); +} + +#elif defined(__powerpc__) || defined(__ppc__) + +int64_t UnscaledCycleClock::Now() { +#ifdef __GLIBC__ + return __ppc_get_timebase(); +#else +#ifdef __powerpc64__ + int64_t tbr; + asm volatile("mfspr %0, 268" : "=r"(tbr)); + return tbr; +#else + int32_t tbu, tbl, tmp; + asm volatile( + "0:\n" + "mftbu %[hi32]\n" + "mftb %[lo32]\n" + "mftbu %[tmp]\n" + "cmpw %[tmp],%[hi32]\n" + "bne 0b\n" + : [ hi32 ] "=r"(tbu), [ lo32 ] "=r"(tbl), [ tmp ] "=r"(tmp)); + return (static_cast(tbu) << 32) | tbl; +#endif +#endif +} + +double UnscaledCycleClock::Frequency() { +#ifdef __GLIBC__ + return __ppc_get_timebase_freq(); +#elif defined(_AIX) + // This is the same constant value as returned by + // __ppc_get_timebase_freq(). + return static_cast(512000000); +#elif defined(__FreeBSD__) + static once_flag init_timebase_frequency_once; + static double timebase_frequency = 0.0; + base_internal::LowLevelCallOnce(&init_timebase_frequency_once, [&]() { + size_t length = sizeof(timebase_frequency); + sysctlbyname("kern.timecounter.tc.timebase.frequency", &timebase_frequency, + &length, nullptr, 0); + }); + return timebase_frequency; +#else +#error Must implement UnscaledCycleClock::Frequency() +#endif +} + +#elif defined(__aarch64__) + +// System timer of ARMv8 runs at a different frequency than the CPU's. +// The frequency is fixed, typically in the range 1-50MHz. It can be +// read at CNTFRQ special register. We assume the OS has set up +// the virtual timer properly. +int64_t UnscaledCycleClock::Now() { + int64_t virtual_timer_value; + asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value)); + return virtual_timer_value; +} + +double UnscaledCycleClock::Frequency() { + uint64_t aarch64_timer_frequency; + asm volatile("mrs %0, cntfrq_el0" : "=r"(aarch64_timer_frequency)); + return aarch64_timer_frequency; +} + +#elif defined(__riscv) + +int64_t UnscaledCycleClock::Now() { + int64_t virtual_timer_value; + asm volatile("rdcycle %0" : "=r"(virtual_timer_value)); + return virtual_timer_value; +} + +double UnscaledCycleClock::Frequency() { + return base_internal::NominalCPUFrequency(); +} + +#elif defined(_M_IX86) || defined(_M_X64) + +#pragma intrinsic(__rdtsc) + +int64_t UnscaledCycleClock::Now() { return __rdtsc(); } + +double UnscaledCycleClock::Frequency() { + return base_internal::NominalCPUFrequency(); +} + +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/src/absl/base/internal/unscaledcycleclock.h b/src/absl/base/internal/unscaledcycleclock.h new file mode 100644 index 0000000..2cbeae3 --- /dev/null +++ b/src/absl/base/internal/unscaledcycleclock.h @@ -0,0 +1,133 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// UnscaledCycleClock +// An UnscaledCycleClock yields the value and frequency of a cycle counter +// that increments at a rate that is approximately constant. +// This class is for internal use only, you should consider using CycleClock +// instead. +// +// Notes: +// The cycle counter frequency is not necessarily the core clock frequency. +// That is, CycleCounter cycles are not necessarily "CPU cycles". +// +// An arbitrary offset may have been added to the counter at power on. +// +// On some platforms, the rate and offset of the counter may differ +// slightly when read from different CPUs of a multiprocessor. Usually, +// we try to ensure that the operating system adjusts values periodically +// so that values agree approximately. If you need stronger guarantees, +// consider using alternate interfaces. +// +// The CPU is not required to maintain the ordering of a cycle counter read +// with respect to surrounding instructions. + +#ifndef ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ +#define ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ + +#include + +#if defined(__APPLE__) +#include +#endif + +#include "absl/base/port.h" + +// The following platforms have an implementation of a hardware counter. +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \ + defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) +#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 +#else +#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 +#endif + +// The following platforms often disable access to the hardware +// counter (through a sandbox) even if the underlying hardware has a +// usable counter. The CycleTimer interface also requires a *scaled* +// CycleClock that runs at atleast 1 MHz. We've found some Android +// ARM64 devices where this is not the case, so we disable it by +// default on Android ARM64. +#if defined(__native_client__) || (defined(__APPLE__)) || \ + (defined(__ANDROID__) && defined(__aarch64__)) +#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 +#else +#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 1 +#endif + +// UnscaledCycleClock is an optional internal feature. +// Use "#if ABSL_USE_UNSCALED_CYCLECLOCK" to test for its presence. +// Can be overridden at compile-time via -DABSL_USE_UNSCALED_CYCLECLOCK=0|1 +#if !defined(ABSL_USE_UNSCALED_CYCLECLOCK) +#define ABSL_USE_UNSCALED_CYCLECLOCK \ + (ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION && \ + ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT) +#endif + +#if ABSL_USE_UNSCALED_CYCLECLOCK + +// This macro can be used to test if UnscaledCycleClock::Frequency() +// is NominalCPUFrequency() on a particular platform. +#if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || \ + defined(_M_IX86) || defined(_M_X64)) +#define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace time_internal { +class UnscaledCycleClockWrapperForGetCurrentTime; +} // namespace time_internal + +namespace base_internal { +class CycleClock; +class UnscaledCycleClockWrapperForInitializeFrequency; + +class UnscaledCycleClock { + private: + UnscaledCycleClock() = delete; + + // Return the value of a cycle counter that counts at a rate that is + // approximately constant. + static int64_t Now(); + + // Return the how much UnscaledCycleClock::Now() increases per second. + // This is not necessarily the core CPU clock frequency. + // It may be the nominal value report by the kernel, rather than a measured + // value. + static double Frequency(); + + // Allowed users + friend class base_internal::CycleClock; + friend class time_internal::UnscaledCycleClockWrapperForGetCurrentTime; + friend class base_internal::UnscaledCycleClockWrapperForInitializeFrequency; +}; + +#if defined(__x86_64__) + +inline int64_t UnscaledCycleClock::Now() { + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + return (high << 32) | low; +} + +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_USE_UNSCALED_CYCLECLOCK + +#endif // ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ diff --git a/src/absl/base/log_severity.cc b/src/absl/base/log_severity.cc new file mode 100644 index 0000000..60a8fc1 --- /dev/null +++ b/src/absl/base/log_severity.cc @@ -0,0 +1,55 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/log_severity.h" + +#include + +#include "absl/base/attributes.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +std::ostream& operator<<(std::ostream& os, absl::LogSeverity s) { + if (s == absl::NormalizeLogSeverity(s)) return os << absl::LogSeverityName(s); + return os << "absl::LogSeverity(" << static_cast(s) << ")"; +} + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtLeast s) { + switch (s) { + case absl::LogSeverityAtLeast::kInfo: + case absl::LogSeverityAtLeast::kWarning: + case absl::LogSeverityAtLeast::kError: + case absl::LogSeverityAtLeast::kFatal: + return os << ">=" << static_cast(s); + case absl::LogSeverityAtLeast::kInfinity: + return os << "INFINITY"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtMost s) { + switch (s) { + case absl::LogSeverityAtMost::kInfo: + case absl::LogSeverityAtMost::kWarning: + case absl::LogSeverityAtMost::kError: + case absl::LogSeverityAtMost::kFatal: + return os << "<=" << static_cast(s); + case absl::LogSeverityAtMost::kNegativeInfinity: + return os << "NEGATIVE_INFINITY"; + } + return os; +} +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/log_severity.h b/src/absl/base/log_severity.h new file mode 100644 index 0000000..8bdca38 --- /dev/null +++ b/src/absl/base/log_severity.h @@ -0,0 +1,172 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_LOG_SEVERITY_H_ +#define ABSL_BASE_LOG_SEVERITY_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// absl::LogSeverity +// +// Four severity levels are defined. Logging APIs should terminate the program +// when a message is logged at severity `kFatal`; the other levels have no +// special semantics. +// +// Values other than the four defined levels (e.g. produced by `static_cast`) +// are valid, but their semantics when passed to a function, macro, or flag +// depend on the function, macro, or flag. The usual behavior is to normalize +// such values to a defined severity level, however in some cases values other +// than the defined levels are useful for comparison. +// +// Example: +// +// // Effectively disables all logging: +// SetMinLogLevel(static_cast(100)); +// +// Abseil flags may be defined with type `LogSeverity`. Dependency layering +// constraints require that the `AbslParseFlag()` overload be declared and +// defined in the flags library itself rather than here. The `AbslUnparseFlag()` +// overload is defined there as well for consistency. +// +// absl::LogSeverity Flag String Representation +// +// An `absl::LogSeverity` has a string representation used for parsing +// command-line flags based on the enumerator name (e.g. `kFatal`) or +// its unprefixed name (without the `k`) in any case-insensitive form. (E.g. +// "FATAL", "fatal" or "Fatal" are all valid.) Unparsing such flags produces an +// unprefixed string representation in all caps (e.g. "FATAL") or an integer. +// +// Additionally, the parser accepts arbitrary integers (as if the type were +// `int`). +// +// Examples: +// +// --my_log_level=kInfo +// --my_log_level=INFO +// --my_log_level=info +// --my_log_level=0 +// +// Unparsing a flag produces the same result as `absl::LogSeverityName()` for +// the standard levels and a base-ten integer otherwise. +enum class LogSeverity : int { + kInfo = 0, + kWarning = 1, + kError = 2, + kFatal = 3, +}; + +// LogSeverities() +// +// Returns an iterable of all standard `absl::LogSeverity` values, ordered from +// least to most severe. +constexpr std::array LogSeverities() { + return {{absl::LogSeverity::kInfo, absl::LogSeverity::kWarning, + absl::LogSeverity::kError, absl::LogSeverity::kFatal}}; +} + +// LogSeverityName() +// +// Returns the all-caps string representation (e.g. "INFO") of the specified +// severity level if it is one of the standard levels and "UNKNOWN" otherwise. +constexpr const char* LogSeverityName(absl::LogSeverity s) { + return s == absl::LogSeverity::kInfo + ? "INFO" + : s == absl::LogSeverity::kWarning + ? "WARNING" + : s == absl::LogSeverity::kError + ? "ERROR" + : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN"; +} + +// NormalizeLogSeverity() +// +// Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal` +// normalize to `kError` (**NOT** `kFatal`). +constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) { + return s < absl::LogSeverity::kInfo + ? absl::LogSeverity::kInfo + : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s; +} +constexpr absl::LogSeverity NormalizeLogSeverity(int s) { + return absl::NormalizeLogSeverity(static_cast(s)); +} + +// operator<< +// +// The exact representation of a streamed `absl::LogSeverity` is deliberately +// unspecified; do not rely on it. +std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); + +// Enums representing a lower bound for LogSeverity. APIs that only operate on +// messages of at least a certain level (for example, `SetMinLogLevel()`) use +// this type to specify that level. absl::LogSeverityAtLeast::kInfinity is +// a level above all threshold levels and therefore no log message will +// ever meet this threshold. +enum class LogSeverityAtLeast : int { + kInfo = static_cast(absl::LogSeverity::kInfo), + kWarning = static_cast(absl::LogSeverity::kWarning), + kError = static_cast(absl::LogSeverity::kError), + kFatal = static_cast(absl::LogSeverity::kFatal), + kInfinity = 1000, +}; + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtLeast s); + +// Enums representing an upper bound for LogSeverity. APIs that only operate on +// messages of at most a certain level (for example, buffer all messages at or +// below a certain level) use this type to specify that level. +// absl::LogSeverityAtMost::kNegativeInfinity is a level below all threshold +// levels and therefore will exclude all log messages. +enum class LogSeverityAtMost : int { + kNegativeInfinity = -1000, + kInfo = static_cast(absl::LogSeverity::kInfo), + kWarning = static_cast(absl::LogSeverity::kWarning), + kError = static_cast(absl::LogSeverity::kError), + kFatal = static_cast(absl::LogSeverity::kFatal), +}; + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtMost s); + +#define COMPOP(op1, op2, T) \ + constexpr bool operator op1(absl::T lhs, absl::LogSeverity rhs) { \ + return static_cast(lhs) op1 rhs; \ + } \ + constexpr bool operator op2(absl::LogSeverity lhs, absl::T rhs) { \ + return lhs op2 static_cast(rhs); \ + } + +// Comparisons between `LogSeverity` and `LogSeverityAtLeast`/ +// `LogSeverityAtMost` are only supported in one direction. +// Valid checks are: +// LogSeverity >= LogSeverityAtLeast +// LogSeverity < LogSeverityAtLeast +// LogSeverity <= LogSeverityAtMost +// LogSeverity > LogSeverityAtMost +COMPOP(>, <, LogSeverityAtLeast) +COMPOP(<=, >=, LogSeverityAtLeast) +COMPOP(<, >, LogSeverityAtMost) +COMPOP(>=, <=, LogSeverityAtMost) +#undef COMPOP + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_LOG_SEVERITY_H_ diff --git a/src/absl/base/macros.h b/src/absl/base/macros.h new file mode 100644 index 0000000..3e085a9 --- /dev/null +++ b/src/absl/base/macros.h @@ -0,0 +1,158 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: macros.h +// ----------------------------------------------------------------------------- +// +// This header file defines the set of language macros used within Abseil code. +// For the set of macros used to determine supported compilers and platforms, +// see absl/base/config.h instead. +// +// This code is compiled directly on many platforms, including client +// platforms like Windows, Mac, and embedded systems. Before making +// any changes here, make sure that you're not breaking any platforms. + +#ifndef ABSL_BASE_MACROS_H_ +#define ABSL_BASE_MACROS_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" + +// ABSL_ARRAYSIZE() +// +// Returns the number of elements in an array as a compile-time constant, which +// can be used in defining new arrays. If you use this macro on a pointer by +// mistake, you will get a compile-time error. +#define ABSL_ARRAYSIZE(array) \ + (sizeof(::absl::macros_internal::ArraySizeHelper(array))) + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace macros_internal { +// Note: this internal template function declaration is used by ABSL_ARRAYSIZE. +// The function doesn't need a definition, as we only use its type. +template +auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; +} // namespace macros_internal +ABSL_NAMESPACE_END +} // namespace absl + +// ABSL_BAD_CALL_IF() +// +// Used on a function overload to trap bad calls: any call that matches the +// overload will cause a compile-time error. This macro uses a clang-specific +// "enable_if" attribute, as described at +// https://clang.llvm.org/docs/AttributeReference.html#enable-if +// +// Overloads which use this macro should be bracketed by +// `#ifdef ABSL_BAD_CALL_IF`. +// +// Example: +// +// int isdigit(int c); +// #ifdef ABSL_BAD_CALL_IF +// int isdigit(int c) +// ABSL_BAD_CALL_IF(c <= -1 || c > 255, +// "'c' must have the value of an unsigned char or EOF"); +// #endif // ABSL_BAD_CALL_IF +#if ABSL_HAVE_ATTRIBUTE(enable_if) +#define ABSL_BAD_CALL_IF(expr, msg) \ + __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) +#endif + +// ABSL_ASSERT() +// +// In C++11, `assert` can't be used portably within constexpr functions. +// ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr +// functions. Example: +// +// constexpr double Divide(double a, double b) { +// return ABSL_ASSERT(b != 0), a / b; +// } +// +// This macro is inspired by +// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ +#if defined(NDEBUG) +#define ABSL_ASSERT(expr) \ + (false ? static_cast(expr) : static_cast(0)) +#else +#define ABSL_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ + : [] { assert(false && #expr); }()) // NOLINT +#endif + +// `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()` +// aborts the program in release mode (when NDEBUG is defined). The +// implementation should abort the program as quickly as possible and ideally it +// should not be possible to ignore the abort request. +#if (ABSL_HAVE_BUILTIN(__builtin_trap) && \ + ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_INTERNAL_HARDENING_ABORT() \ + do { \ + __builtin_trap(); \ + __builtin_unreachable(); \ + } while (false) +#else +#define ABSL_INTERNAL_HARDENING_ABORT() abort() +#endif + +// ABSL_HARDENING_ASSERT() +// +// `ABSL_HARDENING_ASSERT()` is like `ABSL_ASSERT()`, but used to implement +// runtime assertions that should be enabled in hardened builds even when +// `NDEBUG` is defined. +// +// When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT()` is identical to +// `ABSL_ASSERT()`. +// +// See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on +// hardened mode. +#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) +#define ABSL_HARDENING_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ + : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) +#else +#define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr) +#endif + +#ifdef ABSL_HAVE_EXCEPTIONS +#define ABSL_INTERNAL_TRY try +#define ABSL_INTERNAL_CATCH_ANY catch (...) +#define ABSL_INTERNAL_RETHROW do { throw; } while (false) +#else // ABSL_HAVE_EXCEPTIONS +#define ABSL_INTERNAL_TRY if (true) +#define ABSL_INTERNAL_CATCH_ANY else if (false) +#define ABSL_INTERNAL_RETHROW do {} while (false) +#endif // ABSL_HAVE_EXCEPTIONS + +// `ABSL_INTERNAL_UNREACHABLE` is an unreachable statement. A program which +// reaches one has undefined behavior, and the compiler may optimize +// accordingly. +#if defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_INTERNAL_UNREACHABLE __builtin_unreachable() +#elif defined(_MSC_VER) +#define ABSL_INTERNAL_UNREACHABLE __assume(0) +#else +#define ABSL_INTERNAL_UNREACHABLE +#endif + +#endif // ABSL_BASE_MACROS_H_ diff --git a/src/absl/base/optimization.h b/src/absl/base/optimization.h new file mode 100644 index 0000000..db5cc09 --- /dev/null +++ b/src/absl/base/optimization.h @@ -0,0 +1,252 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: optimization.h +// ----------------------------------------------------------------------------- +// +// This header file defines portable macros for performance optimization. + +#ifndef ABSL_BASE_OPTIMIZATION_H_ +#define ABSL_BASE_OPTIMIZATION_H_ + +#include + +#include "absl/base/config.h" + +// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION +// +// Instructs the compiler to avoid optimizing tail-call recursion. This macro is +// useful when you wish to preserve the existing function order within a stack +// trace for logging, debugging, or profiling purposes. +// +// Example: +// +// int f() { +// int result = g(); +// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); +// return result; +// } +#if defined(__pnacl__) +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; } +#elif defined(__clang__) +// Clang will not tail call given inline volatile assembly. +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") +#elif defined(__GNUC__) +// GCC will not tail call given inline volatile assembly. +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") +#elif defined(_MSC_VER) +#include +// The __nop() intrinsic blocks the optimisation. +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __nop() +#else +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; } +#endif + +// ABSL_CACHELINE_SIZE +// +// Explicitly defines the size of the L1 cache for purposes of alignment. +// Setting the cacheline size allows you to specify that certain objects be +// aligned on a cacheline boundary with `ABSL_CACHELINE_ALIGNED` declarations. +// (See below.) +// +// NOTE: this macro should be replaced with the following C++17 features, when +// those are generally available: +// +// * `std::hardware_constructive_interference_size` +// * `std::hardware_destructive_interference_size` +// +// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html +// for more information. +#if defined(__GNUC__) +// Cache line alignment +#if defined(__i386__) || defined(__x86_64__) +#define ABSL_CACHELINE_SIZE 64 +#elif defined(__powerpc64__) +#define ABSL_CACHELINE_SIZE 128 +#elif defined(__aarch64__) +// We would need to read special register ctr_el0 to find out L1 dcache size. +// This value is a good estimate based on a real aarch64 machine. +#define ABSL_CACHELINE_SIZE 64 +#elif defined(__arm__) +// Cache line sizes for ARM: These values are not strictly correct since +// cache line sizes depend on implementations, not architectures. There +// are even implementations with cache line sizes configurable at boot +// time. +#if defined(__ARM_ARCH_5T__) +#define ABSL_CACHELINE_SIZE 32 +#elif defined(__ARM_ARCH_7A__) +#define ABSL_CACHELINE_SIZE 64 +#endif +#endif + +#ifndef ABSL_CACHELINE_SIZE +// A reasonable default guess. Note that overestimates tend to waste more +// space, while underestimates tend to waste more time. +#define ABSL_CACHELINE_SIZE 64 +#endif + +// ABSL_CACHELINE_ALIGNED +// +// Indicates that the declared object be cache aligned using +// `ABSL_CACHELINE_SIZE` (see above). Cacheline aligning objects allows you to +// load a set of related objects in the L1 cache for performance improvements. +// Cacheline aligning objects properly allows constructive memory sharing and +// prevents destructive (or "false") memory sharing. +// +// NOTE: callers should replace uses of this macro with `alignas()` using +// `std::hardware_constructive_interference_size` and/or +// `std::hardware_destructive_interference_size` when C++17 becomes available to +// them. +// +// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html +// for more information. +// +// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__` +// or `__declspec` attribute. For compilers where this is not known to work, +// the macro expands to nothing. +// +// No further guarantees are made here. The result of applying the macro +// to variables and types is always implementation-defined. +// +// WARNING: It is easy to use this attribute incorrectly, even to the point +// of causing bugs that are difficult to diagnose, crash, etc. It does not +// of itself guarantee that objects are aligned to a cache line. +// +// NOTE: Some compilers are picky about the locations of annotations such as +// this attribute, so prefer to put it at the beginning of your declaration. +// For example, +// +// ABSL_CACHELINE_ALIGNED static Foo* foo = ... +// +// class ABSL_CACHELINE_ALIGNED Bar { ... +// +// Recommendations: +// +// 1) Consult compiler documentation; this comment is not kept in sync as +// toolchains evolve. +// 2) Verify your use has the intended effect. This often requires inspecting +// the generated machine code. +// 3) Prefer applying this attribute to individual variables. Avoid +// applying it to types. This tends to localize the effect. +#define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE))) +#elif defined(_MSC_VER) +#define ABSL_CACHELINE_SIZE 64 +#define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE)) +#else +#define ABSL_CACHELINE_SIZE 64 +#define ABSL_CACHELINE_ALIGNED +#endif + +// ABSL_PREDICT_TRUE, ABSL_PREDICT_FALSE +// +// Enables the compiler to prioritize compilation using static analysis for +// likely paths within a boolean branch. +// +// Example: +// +// if (ABSL_PREDICT_TRUE(expression)) { +// return result; // Faster if more likely +// } else { +// return 0; +// } +// +// Compilers can use the information that a certain branch is not likely to be +// taken (for instance, a CHECK failure) to optimize for the common case in +// the absence of better information (ie. compiling gcc with `-fprofile-arcs`). +// +// Recommendation: Modern CPUs dynamically predict branch execution paths, +// typically with accuracy greater than 97%. As a result, annotating every +// branch in a codebase is likely counterproductive; however, annotating +// specific branches that are both hot and consistently mispredicted is likely +// to yield performance improvements. +#if ABSL_HAVE_BUILTIN(__builtin_expect) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_PREDICT_FALSE(x) (__builtin_expect(false || (x), false)) +#define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true)) +#else +#define ABSL_PREDICT_FALSE(x) (x) +#define ABSL_PREDICT_TRUE(x) (x) +#endif + +// ABSL_ASSUME(cond) +// +// Informs the compiler that a condition is always true and that it can assume +// it to be true for optimization purposes. +// +// WARNING: If the condition is false, the program can produce undefined and +// potentially dangerous behavior. +// +// In !NDEBUG mode, the condition is checked with an assert(). +// +// NOTE: The expression must not have side effects, as it may only be evaluated +// in some compilation modes and not others. Some compilers may issue a warning +// if the compiler cannot prove the expression has no side effects. For example, +// the expression should not use a function call since the compiler cannot prove +// that a function call does not have side effects. +// +// Example: +// +// int x = ...; +// ABSL_ASSUME(x >= 0); +// // The compiler can optimize the division to a simple right shift using the +// // assumption specified above. +// int y = x / 16; +// +#if !defined(NDEBUG) +#define ABSL_ASSUME(cond) assert(cond) +#elif ABSL_HAVE_BUILTIN(__builtin_assume) +#define ABSL_ASSUME(cond) __builtin_assume(cond) +#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_ASSUME(cond) \ + do { \ + if (!(cond)) __builtin_unreachable(); \ + } while (0) +#elif defined(_MSC_VER) +#define ABSL_ASSUME(cond) __assume(cond) +#else +#define ABSL_ASSUME(cond) \ + do { \ + static_cast(false && (cond)); \ + } while (0) +#endif + +// ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) +// This macro forces small unique name on a static file level symbols like +// static local variables or static functions. This is intended to be used in +// macro definitions to optimize the cost of generated code. Do NOT use it on +// symbols exported from translation unit since it may cause a link time +// conflict. +// +// Example: +// +// #define MY_MACRO(txt) +// namespace { +// char VeryVeryLongVarName[] ABSL_INTERNAL_UNIQUE_SMALL_NAME() = txt; +// const char* VeryVeryLongFuncName() ABSL_INTERNAL_UNIQUE_SMALL_NAME(); +// const char* VeryVeryLongFuncName() { return txt; } +// } +// + +#if defined(__GNUC__) +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) #x +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME1(x) ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME() \ + asm(ABSL_INTERNAL_UNIQUE_SMALL_NAME1(.absl.__COUNTER__)) +#else +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME() +#endif + +#endif // ABSL_BASE_OPTIMIZATION_H_ diff --git a/src/absl/base/options.h b/src/absl/base/options.h new file mode 100644 index 0000000..bc59847 --- /dev/null +++ b/src/absl/base/options.h @@ -0,0 +1,238 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: options.h +// ----------------------------------------------------------------------------- +// +// This file contains Abseil configuration options for setting specific +// implementations instead of letting Abseil determine which implementation to +// use at compile-time. Setting these options may be useful for package or build +// managers who wish to guarantee ABI stability within binary builds (which are +// otherwise difficult to enforce). +// +// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS: It is important that +// maintainers of package managers who wish to package Abseil read and +// understand this file! *** +// +// Abseil contains a number of possible configuration endpoints, based on +// parameters such as the detected platform, language version, or command-line +// flags used to invoke the underlying binary. As is the case with all +// libraries, binaries which contain Abseil code must ensure that separate +// packages use the same compiled copy of Abseil to avoid a diamond dependency +// problem, which can occur if two packages built with different Abseil +// configuration settings are linked together. Diamond dependency problems in +// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in +// linker errors), or undefined behavior (resulting in crashes). +// +// Diamond dependency problems can be avoided if all packages utilize the same +// exact version of Abseil. Building from source code with the same compilation +// parameters is the easiest way to avoid such dependency problems. However, for +// package managers who cannot control such compilation parameters, we are +// providing the file to allow you to inject ABI (Application Binary Interface) +// stability across builds. Settings options in this file will neither change +// API nor ABI, providing a stable copy of Abseil between packages. +// +// Care must be taken to keep options within these configurations isolated +// from any other dynamic settings, such as command-line flags which could alter +// these options. This file is provided specifically to help build and package +// managers provide a stable copy of Abseil within their libraries and binaries; +// other developers should not have need to alter the contents of this file. +// +// ----------------------------------------------------------------------------- +// Usage +// ----------------------------------------------------------------------------- +// +// For any particular package release, set the appropriate definitions within +// this file to whatever value makes the most sense for your package(s). Note +// that, by default, most of these options, at the moment, affect the +// implementation of types; future options may affect other implementation +// details. +// +// NOTE: the defaults within this file all assume that Abseil can select the +// proper Abseil implementation at compile-time, which will not be sufficient +// to guarantee ABI stability to package managers. + +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + +// Include a standard library header to allow configuration based on the +// standard library in use. +#ifdef __cplusplus +#include +#endif + +// ----------------------------------------------------------------------------- +// Type Compatibility Options +// ----------------------------------------------------------------------------- +// +// ABSL_OPTION_USE_STD_ANY +// +// This option controls whether absl::any is implemented as an alias to +// std::any, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::any. This requires that all code +// using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::any is available. This option is +// useful when you are building your entire program, including all of its +// dependencies, from source. It should not be used otherwise -- for example, +// if you are distributing Abseil in a binary package manager -- since in +// mode 2, absl::any will name a different type, with a different mangled name +// and binary layout, depending on the compiler flags passed by the end user. +// For more info, see https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY. + +#define ABSL_OPTION_USE_STD_ANY 2 + + +// ABSL_OPTION_USE_STD_OPTIONAL +// +// This option controls whether absl::optional is implemented as an alias to +// std::optional, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::optional. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::optional is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::optional will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. + +// User code should not inspect this macro. To check in the preprocessor if +// absl::optional is a typedef of std::optional, use the feature macro +// ABSL_USES_STD_OPTIONAL. + +#define ABSL_OPTION_USE_STD_OPTIONAL 2 + + +// ABSL_OPTION_USE_STD_STRING_VIEW +// +// This option controls whether absl::string_view is implemented as an alias to +// std::string_view, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::string_view. This requires that +// all code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::string_view is available. This +// option is useful when you are building your program from source. It should +// not be used otherwise -- for example, if you are distributing Abseil in a +// binary package manager -- since in mode 2, absl::string_view will name a +// different type, with a different mangled name and binary layout, depending on +// the compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::string_view is a typedef of std::string_view, use the feature macro +// ABSL_USES_STD_STRING_VIEW. + +#define ABSL_OPTION_USE_STD_STRING_VIEW 2 + +// ABSL_OPTION_USE_STD_VARIANT +// +// This option controls whether absl::variant is implemented as an alias to +// std::variant, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::variant. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::variant is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::variant will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::variant is a typedef of std::variant, use the feature macro +// ABSL_USES_STD_VARIANT. + +#define ABSL_OPTION_USE_STD_VARIANT 2 + + +// ABSL_OPTION_USE_INLINE_NAMESPACE +// ABSL_OPTION_INLINE_NAMESPACE_NAME +// +// These options controls whether all entities in the absl namespace are +// contained within an inner inline namespace. This does not affect the +// user-visible API of Abseil, but it changes the mangled names of all symbols. +// +// This can be useful as a version tag if you are distributing Abseil in +// precompiled form. This will prevent a binary library build of Abseil with +// one inline namespace being used with headers configured with a different +// inline namespace name. Binary packagers are reminded that Abseil does not +// guarantee any ABI stability in Abseil, so any update of Abseil or +// configuration change in such a binary package should be combined with a +// new, unique value for the inline namespace name. +// +// A value of 0 means not to use inline namespaces. +// +// A value of 1 means to use an inline namespace with the given name inside +// namespace absl. If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also +// be changed to a new, unique identifier name. In particular "head" is not +// allowed. + +#define ABSL_OPTION_USE_INLINE_NAMESPACE 1 +#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20220623 + +// ABSL_OPTION_HARDENED +// +// This option enables a "hardened" build in release mode (in this context, +// release mode is defined as a build where the `NDEBUG` macro is defined). +// +// A value of 0 means that "hardened" mode is not enabled. +// +// A value of 1 means that "hardened" mode is enabled. +// +// Hardened builds have additional security checks enabled when `NDEBUG` is +// defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a +// no-op, as well as disabling other bespoke program consistency checks. By +// defining ABSL_OPTION_HARDENED to 1, a select set of checks remain enabled in +// release mode. These checks guard against programming errors that may lead to +// security vulnerabilities. In release mode, when one of these programming +// errors is encountered, the program will immediately abort, possibly without +// any attempt at logging. +// +// The checks enabled by this option are not free; they do incur runtime cost. +// +// The checks enabled by this option are always active when `NDEBUG` is not +// defined, even in the case when ABSL_OPTION_HARDENED is defined to 0. The +// checks enabled by this option may abort the program in a different way and +// log additional information when `NDEBUG` is not defined. + +#define ABSL_OPTION_HARDENED 0 + +#endif // ABSL_BASE_OPTIONS_H_ diff --git a/src/absl/base/policy_checks.h b/src/absl/base/policy_checks.h new file mode 100644 index 0000000..06b3243 --- /dev/null +++ b/src/absl/base/policy_checks.h @@ -0,0 +1,111 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: policy_checks.h +// ----------------------------------------------------------------------------- +// +// This header enforces a minimum set of policies at build time, such as the +// supported compiler and library versions. Unsupported configurations are +// reported with `#error`. This enforcement is best effort, so successfully +// compiling this header does not guarantee a supported configuration. + +#ifndef ABSL_BASE_POLICY_CHECKS_H_ +#define ABSL_BASE_POLICY_CHECKS_H_ + +// Included for the __GLIBC_PREREQ macro used below. +#include + +// Included for the _STLPORT_VERSION macro used below. +#if defined(__cplusplus) +#include +#endif + +// ----------------------------------------------------------------------------- +// Operating System Check +// ----------------------------------------------------------------------------- + +#if defined(__CYGWIN__) +#error "Cygwin is not supported." +#endif + +// ----------------------------------------------------------------------------- +// Toolchain Check +// ----------------------------------------------------------------------------- + +// We support MSVC++ 14.0 update 2 and later. +// This minimum will go up. +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__) +#error "This package requires Visual Studio 2015 Update 2 or higher." +#endif + +// We support gcc 4.7 and later. +// This minimum will go up. +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) +#error "This package requires gcc 4.7 or higher." +#endif +#endif + +// We support Apple Xcode clang 4.2.1 (version 421.11.65) and later. +// This corresponds to Apple Xcode version 4.5. +// This minimum will go up. +#if defined(__apple_build_version__) && __apple_build_version__ < 4211165 +#error "This package requires __apple_build_version__ of 4211165 or higher." +#endif + +// ----------------------------------------------------------------------------- +// C++ Version Check +// ----------------------------------------------------------------------------- + +// Enforce C++11 as the minimum. Note that Visual Studio has not +// advanced __cplusplus despite being good enough for our purposes, so +// so we exempt it from the check. +#if defined(__cplusplus) && !defined(_MSC_VER) +#if __cplusplus < 201103L +#error "C++ versions less than C++11 are not supported." +#endif +#endif + +// ----------------------------------------------------------------------------- +// Standard Library Check +// ----------------------------------------------------------------------------- + +#if defined(_STLPORT_VERSION) +#error "STLPort is not supported." +#endif + +// ----------------------------------------------------------------------------- +// `char` Size Check +// ----------------------------------------------------------------------------- + +// Abseil currently assumes CHAR_BIT == 8. If you would like to use Abseil on a +// platform where this is not the case, please provide us with the details about +// your platform so we can consider relaxing this requirement. +#if CHAR_BIT != 8 +#error "Abseil assumes CHAR_BIT == 8." +#endif + +// ----------------------------------------------------------------------------- +// `int` Size Check +// ----------------------------------------------------------------------------- + +// Abseil currently assumes that an int is 4 bytes. If you would like to use +// Abseil on a platform where this is not the case, please provide us with the +// details about your platform so we can consider relaxing this requirement. +#if INT_MAX < 2147483647 +#error "Abseil assumes that int is at least 4 bytes. " +#endif + +#endif // ABSL_BASE_POLICY_CHECKS_H_ diff --git a/src/absl/base/port.h b/src/absl/base/port.h new file mode 100644 index 0000000..5bc4d6c --- /dev/null +++ b/src/absl/base/port.h @@ -0,0 +1,25 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This files is a forwarding header for other headers containing various +// portability macros and functions. + +#ifndef ABSL_BASE_PORT_H_ +#define ABSL_BASE_PORT_H_ + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" + +#endif // ABSL_BASE_PORT_H_ diff --git a/src/absl/base/thread_annotations.h b/src/absl/base/thread_annotations.h new file mode 100644 index 0000000..bc8a620 --- /dev/null +++ b/src/absl/base/thread_annotations.h @@ -0,0 +1,335 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: thread_annotations.h +// ----------------------------------------------------------------------------- +// +// This header file contains macro definitions for thread safety annotations +// that allow developers to document the locking policies of multi-threaded +// code. The annotations can also help program analysis tools to identify +// potential thread safety issues. +// +// These annotations are implemented using compiler attributes. Using the macros +// defined here instead of raw attributes allow for portability and future +// compatibility. +// +// When referring to mutexes in the arguments of the attributes, you should +// use variable names or more complex expressions (e.g. my_object->mutex_) +// that evaluate to a concrete mutex object whenever possible. If the mutex +// you want to refer to is not in scope, you may use a member pointer +// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. + +#ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_ +#define ABSL_BASE_THREAD_ANNOTATIONS_H_ + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +// TODO(mbonadei): Remove after the backward compatibility period. +#include "absl/base/internal/thread_annotations.h" // IWYU pragma: export + +// ABSL_GUARDED_BY() +// +// Documents if a shared field or global variable needs to be protected by a +// mutex. ABSL_GUARDED_BY() allows the user to specify a particular mutex that +// should be held when accessing the annotated variable. +// +// Although this annotation (and ABSL_PT_GUARDED_BY, below) cannot be applied to +// local variables, a local variable and its associated mutex can often be +// combined into a small class or struct, thereby allowing the annotation. +// +// Example: +// +// class Foo { +// Mutex mu_; +// int p1_ ABSL_GUARDED_BY(mu_); +// ... +// }; +#if ABSL_HAVE_ATTRIBUTE(guarded_by) +#define ABSL_GUARDED_BY(x) __attribute__((guarded_by(x))) +#else +#define ABSL_GUARDED_BY(x) +#endif + +// ABSL_PT_GUARDED_BY() +// +// Documents if the memory location pointed to by a pointer should be guarded +// by a mutex when dereferencing the pointer. +// +// Example: +// class Foo { +// Mutex mu_; +// int *p1_ ABSL_PT_GUARDED_BY(mu_); +// ... +// }; +// +// Note that a pointer variable to a shared memory location could itself be a +// shared variable. +// +// Example: +// +// // `q_`, guarded by `mu1_`, points to a shared memory location that is +// // guarded by `mu2_`: +// int *q_ ABSL_GUARDED_BY(mu1_) ABSL_PT_GUARDED_BY(mu2_); +#if ABSL_HAVE_ATTRIBUTE(pt_guarded_by) +#define ABSL_PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x))) +#else +#define ABSL_PT_GUARDED_BY(x) +#endif + +// ABSL_ACQUIRED_AFTER() / ABSL_ACQUIRED_BEFORE() +// +// Documents the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ABSL_ACQUIRED_AFTER +// and ABSL_ACQUIRED_BEFORE.) +// +// As with ABSL_GUARDED_BY, this is only applicable to mutexes that are shared +// fields or global variables. +// +// Example: +// +// Mutex m1_; +// Mutex m2_ ABSL_ACQUIRED_AFTER(m1_); +#if ABSL_HAVE_ATTRIBUTE(acquired_after) +#define ABSL_ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__))) +#else +#define ABSL_ACQUIRED_AFTER(...) +#endif + +#if ABSL_HAVE_ATTRIBUTE(acquired_before) +#define ABSL_ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__))) +#else +#define ABSL_ACQUIRED_BEFORE(...) +#endif + +// ABSL_EXCLUSIVE_LOCKS_REQUIRED() / ABSL_SHARED_LOCKS_REQUIRED() +// +// Documents a function that expects a mutex to be held prior to entry. +// The mutex is expected to be held both on entry to, and exit from, the +// function. +// +// An exclusive lock allows read-write access to the guarded data member(s), and +// only one thread can acquire a lock exclusively at any one time. A shared lock +// allows read-only access, and any number of threads can acquire a shared lock +// concurrently. +// +// Generally, non-const methods should be annotated with +// ABSL_EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with +// ABSL_SHARED_LOCKS_REQUIRED. +// +// Example: +// +// Mutex mu1, mu2; +// int a ABSL_GUARDED_BY(mu1); +// int b ABSL_GUARDED_BY(mu2); +// +// void foo() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } +// void bar() const ABSL_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } +#if ABSL_HAVE_ATTRIBUTE(exclusive_locks_required) +#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) \ + __attribute__((exclusive_locks_required(__VA_ARGS__))) +#else +#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) +#endif + +#if ABSL_HAVE_ATTRIBUTE(shared_locks_required) +#define ABSL_SHARED_LOCKS_REQUIRED(...) \ + __attribute__((shared_locks_required(__VA_ARGS__))) +#else +#define ABSL_SHARED_LOCKS_REQUIRED(...) +#endif + +// ABSL_LOCKS_EXCLUDED() +// +// Documents the locks that cannot be held by callers of this function, as they +// might be acquired by this function (Abseil's `Mutex` locks are +// non-reentrant). +#if ABSL_HAVE_ATTRIBUTE(locks_excluded) +#define ABSL_LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__))) +#else +#define ABSL_LOCKS_EXCLUDED(...) +#endif + +// ABSL_LOCK_RETURNED() +// +// Documents a function that returns a mutex without acquiring it. For example, +// a public getter method that returns a pointer to a private mutex should +// be annotated with ABSL_LOCK_RETURNED. +#if ABSL_HAVE_ATTRIBUTE(lock_returned) +#define ABSL_LOCK_RETURNED(x) __attribute__((lock_returned(x))) +#else +#define ABSL_LOCK_RETURNED(x) +#endif + +// ABSL_LOCKABLE +// +// Documents if a class/type is a lockable type (such as the `Mutex` class). +#if ABSL_HAVE_ATTRIBUTE(lockable) +#define ABSL_LOCKABLE __attribute__((lockable)) +#else +#define ABSL_LOCKABLE +#endif + +// ABSL_SCOPED_LOCKABLE +// +// Documents if a class does RAII locking (such as the `MutexLock` class). +// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is +// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no +// arguments; the analysis will assume that the destructor unlocks whatever the +// constructor locked. +#if ABSL_HAVE_ATTRIBUTE(scoped_lockable) +#define ABSL_SCOPED_LOCKABLE __attribute__((scoped_lockable)) +#else +#define ABSL_SCOPED_LOCKABLE +#endif + +// ABSL_EXCLUSIVE_LOCK_FUNCTION() +// +// Documents functions that acquire a lock in the body of a function, and do +// not release it. +#if ABSL_HAVE_ATTRIBUTE(exclusive_lock_function) +#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) \ + __attribute__((exclusive_lock_function(__VA_ARGS__))) +#else +#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) +#endif + +// ABSL_SHARED_LOCK_FUNCTION() +// +// Documents functions that acquire a shared (reader) lock in the body of a +// function, and do not release it. +#if ABSL_HAVE_ATTRIBUTE(shared_lock_function) +#define ABSL_SHARED_LOCK_FUNCTION(...) \ + __attribute__((shared_lock_function(__VA_ARGS__))) +#else +#define ABSL_SHARED_LOCK_FUNCTION(...) +#endif + +// ABSL_UNLOCK_FUNCTION() +// +// Documents functions that expect a lock to be held on entry to the function, +// and release it in the body of the function. +#if ABSL_HAVE_ATTRIBUTE(unlock_function) +#define ABSL_UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__))) +#else +#define ABSL_UNLOCK_FUNCTION(...) +#endif + +// ABSL_EXCLUSIVE_TRYLOCK_FUNCTION() / ABSL_SHARED_TRYLOCK_FUNCTION() +// +// Documents functions that try to acquire a lock, and return success or failure +// (or a non-boolean value that can be interpreted as a boolean). +// The first argument should be `true` for functions that return `true` on +// success, or `false` for functions that return `false` on success. The second +// argument specifies the mutex that is locked on success. If unspecified, this +// mutex is assumed to be `this`. +#if ABSL_HAVE_ATTRIBUTE(exclusive_trylock_function) +#define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + __attribute__((exclusive_trylock_function(__VA_ARGS__))) +#else +#define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) +#endif + +#if ABSL_HAVE_ATTRIBUTE(shared_trylock_function) +#define ABSL_SHARED_TRYLOCK_FUNCTION(...) \ + __attribute__((shared_trylock_function(__VA_ARGS__))) +#else +#define ABSL_SHARED_TRYLOCK_FUNCTION(...) +#endif + +// ABSL_ASSERT_EXCLUSIVE_LOCK() / ABSL_ASSERT_SHARED_LOCK() +// +// Documents functions that dynamically check to see if a lock is held, and fail +// if it is not held. +#if ABSL_HAVE_ATTRIBUTE(assert_exclusive_lock) +#define ABSL_ASSERT_EXCLUSIVE_LOCK(...) \ + __attribute__((assert_exclusive_lock(__VA_ARGS__))) +#else +#define ABSL_ASSERT_EXCLUSIVE_LOCK(...) +#endif + +#if ABSL_HAVE_ATTRIBUTE(assert_shared_lock) +#define ABSL_ASSERT_SHARED_LOCK(...) \ + __attribute__((assert_shared_lock(__VA_ARGS__))) +#else +#define ABSL_ASSERT_SHARED_LOCK(...) +#endif + +// ABSL_NO_THREAD_SAFETY_ANALYSIS +// +// Turns off thread safety checking within the body of a particular function. +// This annotation is used to mark functions that are known to be correct, but +// the locking behavior is more complicated than the analyzer can handle. +#if ABSL_HAVE_ATTRIBUTE(no_thread_safety_analysis) +#define ABSL_NO_THREAD_SAFETY_ANALYSIS \ + __attribute__((no_thread_safety_analysis)) +#else +#define ABSL_NO_THREAD_SAFETY_ANALYSIS +#endif + +//------------------------------------------------------------------------------ +// Tool-Supplied Annotations +//------------------------------------------------------------------------------ + +// ABSL_TS_UNCHECKED should be placed around lock expressions that are not valid +// C++ syntax, but which are present for documentation purposes. These +// annotations will be ignored by the analysis. +#define ABSL_TS_UNCHECKED(x) "" + +// ABSL_TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// It is used by automated tools to mark and disable invalid expressions. +// The annotation should either be fixed, or changed to ABSL_TS_UNCHECKED. +#define ABSL_TS_FIXME(x) "" + +// Like ABSL_NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body +// of a particular function. However, this attribute is used to mark functions +// that are incorrect and need to be fixed. It is used by automated tools to +// avoid breaking the build when the analysis is updated. +// Code owners are expected to eventually fix the routine. +#define ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME ABSL_NO_THREAD_SAFETY_ANALYSIS + +// Similar to ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a +// ABSL_GUARDED_BY annotation that needs to be fixed, because it is producing +// thread safety warning. It disables the ABSL_GUARDED_BY. +#define ABSL_GUARDED_BY_FIXME(x) + +// Disables warnings for a single read operation. This can be used to avoid +// warnings when it is known that the read is not actually involved in a race, +// but the compiler cannot confirm that. +#define ABSL_TS_UNCHECKED_READ(x) absl::base_internal::ts_unchecked_read(x) + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Takes a reference to a guarded data member, and returns an unguarded +// reference. +// Do not use this function directly, use ABSL_TS_UNCHECKED_READ instead. +template +inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +template +inline T& ts_unchecked_read(T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_THREAD_ANNOTATIONS_H_ diff --git a/src/absl/container/btree_map.h b/src/absl/container/btree_map.h new file mode 100644 index 0000000..286817f --- /dev/null +++ b/src/absl/container/btree_map.h @@ -0,0 +1,851 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: btree_map.h +// ----------------------------------------------------------------------------- +// +// This header file defines B-tree maps: sorted associative containers mapping +// keys to values. +// +// * `absl::btree_map<>` +// * `absl::btree_multimap<>` +// +// These B-tree types are similar to the corresponding types in the STL +// (`std::map` and `std::multimap`) and generally conform to the STL interfaces +// of those types. However, because they are implemented using B-trees, they +// are more efficient in most situations. +// +// Unlike `std::map` and `std::multimap`, which are commonly implemented using +// red-black tree nodes, B-tree maps use more generic B-tree nodes able to hold +// multiple values per node. Holding multiple values per node often makes +// B-tree maps perform better than their `std::map` counterparts, because +// multiple entries can be checked within the same cache hit. +// +// However, these types should not be considered drop-in replacements for +// `std::map` and `std::multimap` as there are some API differences, which are +// noted in this header file. The most consequential differences with respect to +// migrating to b-tree from the STL types are listed in the next paragraph. +// Other API differences are minor. +// +// Importantly, insertions and deletions may invalidate outstanding iterators, +// pointers, and references to elements. Such invalidations are typically only +// an issue if insertion and deletion operations are interleaved with the use of +// more than one iterator, pointer, or reference simultaneously. For this +// reason, `insert()` and `erase()` return a valid iterator at the current +// position. Another important difference is that key-types must be +// copy-constructible. + +#ifndef ABSL_CONTAINER_BTREE_MAP_H_ +#define ABSL_CONTAINER_BTREE_MAP_H_ + +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/btree_container.h" // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace container_internal { + +template +struct map_params; + +} // namespace container_internal + +// absl::btree_map<> +// +// An `absl::btree_map` is an ordered associative container of +// unique keys and associated values designed to be a more efficient replacement +// for `std::map` (in most cases). +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less`. +// +// An `absl::btree_map` uses a default allocator of +// `std::allocator>` to allocate (and deallocate) +// nodes, and construct and destruct values within those nodes. You may +// instead specify a custom allocator `A` (which in turn requires specifying a +// custom comparator `C`) as in `absl::btree_map`. +// +template , + typename Alloc = std::allocator>> +class btree_map + : public container_internal::btree_map_container< + container_internal::btree>> { + using Base = typename btree_map::btree_map_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_map` supports the same overload set as `std::map` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_map map1; + // + // * Initializer List constructor + // + // absl::btree_map map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::btree_map map3(map2); + // + // * Copy assignment operator + // + // absl::btree_map map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_map map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_map map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::btree_map map7(v.begin(), v.end()); + btree_map() {} + using Base::Base; + + // btree_map::begin() + // + // Returns an iterator to the beginning of the `btree_map`. + using Base::begin; + + // btree_map::cbegin() + // + // Returns a const iterator to the beginning of the `btree_map`. + using Base::cbegin; + + // btree_map::end() + // + // Returns an iterator to the end of the `btree_map`. + using Base::end; + + // btree_map::cend() + // + // Returns a const iterator to the end of the `btree_map`. + using Base::cend; + + // btree_map::empty() + // + // Returns whether or not the `btree_map` is empty. + using Base::empty; + + // btree_map::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_map` under current memory constraints. This value can be thought + // of as the largest value of `std::distance(begin(), end())` for a + // `btree_map`. + using Base::max_size; + + // btree_map::size() + // + // Returns the number of elements currently within the `btree_map`. + using Base::size; + + // btree_map::clear() + // + // Removes all elements from the `btree_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_map::erase() + // + // Erases elements within the `btree_map`. If an erase occurs, any references, + // pointers, or iterators are invalidated. + // Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_map`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template size_type erase(const K& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). + using Base::erase; + + // btree_map::insert() + // + // Inserts an element of the specified value into the `btree_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If an insertion + // occurs, any references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // std::pair insert(const value_type& value): + // + // Inserts a value into the `btree_map`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(value_type&& value): + // + // Inserts a moveable value into the `btree_map`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_map::insert_or_assign() + // + // Inserts an element of the specified value into the `btree_map` provided + // that a value with the given key does not already exist, or replaces the + // corresponding mapped type with the forwarded `obj` argument if a key for + // that value already exists, returning an iterator pointing to the newly + // inserted element. Overloads are listed below. + // + // pair insert_or_assign(const key_type& k, M&& obj): + // pair insert_or_assign(key_type&& k, M&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `btree_map`. If the returned bool is true, insertion took place, and if + // it's false, assignment took place. + // + // iterator insert_or_assign(const_iterator hint, + // const key_type& k, M&& obj): + // iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `btree_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::insert_or_assign; + + // btree_map::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_map::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace_hint; + + // btree_map::try_emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, provided that no element with the given key + // already exists. Unlike `emplace()`, if an element with the given key + // already exists, we guarantee that no element is constructed. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + // + // Overloads are listed below. + // + // std::pair try_emplace(const key_type& k, Args&&... args): + // std::pair try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `btree_map`. + // + // iterator try_emplace(const_iterator hint, + // const key_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `btree_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::try_emplace; + + // btree_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template node_type extract(const K& k): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_map` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_map::merge() + // + // Extracts elements from a given `source` btree_map into this + // `btree_map`. If the destination `btree_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_map::swap(btree_map& other) + // + // Exchanges the contents of this `btree_map` with those of the `other` + // btree_map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `btree_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // btree_map::contains() + // + // template bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_map`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::contains; + + // btree_map::count() + // + // template size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_map`. Note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `btree_map`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::count; + + // btree_map::equal_range() + // + // Returns a half-open range [first, last), defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the `btree_map`. + using Base::equal_range; + + // btree_map::find() + // + // template iterator find(const K& key): + // template const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_map`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::find; + + // btree_map::lower_bound() + // + // template iterator lower_bound(const K& key): + // template const_iterator lower_bound(const K& key) const: + // + // Finds the first element with a key that is not less than `key` within the + // `btree_map`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_map::upper_bound() + // + // template iterator upper_bound(const K& key): + // template const_iterator upper_bound(const K& key) const: + // + // Finds the first element with a key that is greater than `key` within the + // `btree_map`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::upper_bound; + + // btree_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `btree_map`, performing an `insert()` if the key does not already + // exist. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. Otherwise iterators are not affected and references are not + // invalidated. Overloads are listed below. + // + // T& operator[](key_type&& key): + // T& operator[](const key_type& key): + // + // Inserts a value_type object constructed in-place if the element with the + // given key does not exist. + using Base::operator[]; + + // btree_map::get_allocator() + // + // Returns the allocator function associated with this `btree_map`. + using Base::get_allocator; + + // btree_map::key_comp(); + // + // Returns the key comparator associated with this `btree_map`. + using Base::key_comp; + + // btree_map::value_comp(); + // + // Returns the value comparator associated with this `btree_map`. + using Base::value_comp; +}; + +// absl::swap(absl::btree_map<>, absl::btree_map<>) +// +// Swaps the contents of two `absl::btree_map` containers. +template +void swap(btree_map &x, btree_map &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_map<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +// Returns the number of erased elements. +template +typename btree_map::size_type erase_if( + btree_map &map, Pred pred) { + return container_internal::btree_access::erase_if(map, std::move(pred)); +} + +// absl::btree_multimap +// +// An `absl::btree_multimap` is an ordered associative container of +// keys and associated values designed to be a more efficient replacement for +// `std::multimap` (in most cases). Unlike `absl::btree_map`, a B-tree multimap +// allows multiple elements with equivalent keys. +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less`. +// +// An `absl::btree_multimap` uses a default allocator of +// `std::allocator>` to allocate (and deallocate) +// nodes, and construct and destruct values within those nodes. You may +// instead specify a custom allocator `A` (which in turn requires specifying a +// custom comparator `C`) as in `absl::btree_multimap`. +// +template , + typename Alloc = std::allocator>> +class btree_multimap + : public container_internal::btree_multimap_container< + container_internal::btree>> { + using Base = typename btree_multimap::btree_multimap_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_multimap` supports the same overload set as `std::multimap` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_multimap map1; + // + // * Initializer List constructor + // + // absl::btree_multimap map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::btree_multimap map3(map2); + // + // * Copy assignment operator + // + // absl::btree_multimap map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_multimap map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_multimap map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::btree_multimap map7(v.begin(), v.end()); + btree_multimap() {} + using Base::Base; + + // btree_multimap::begin() + // + // Returns an iterator to the beginning of the `btree_multimap`. + using Base::begin; + + // btree_multimap::cbegin() + // + // Returns a const iterator to the beginning of the `btree_multimap`. + using Base::cbegin; + + // btree_multimap::end() + // + // Returns an iterator to the end of the `btree_multimap`. + using Base::end; + + // btree_multimap::cend() + // + // Returns a const iterator to the end of the `btree_multimap`. + using Base::cend; + + // btree_multimap::empty() + // + // Returns whether or not the `btree_multimap` is empty. + using Base::empty; + + // btree_multimap::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_multimap` under current memory constraints. This value can be + // thought of as the largest value of `std::distance(begin(), end())` for a + // `btree_multimap`. + using Base::max_size; + + // btree_multimap::size() + // + // Returns the number of elements currently within the `btree_multimap`. + using Base::size; + + // btree_multimap::clear() + // + // Removes all elements from the `btree_multimap`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_multimap::erase() + // + // Erases elements within the `btree_multimap`. If an erase occurs, any + // references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_multimap`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template size_type erase(const K& key): + // + // Erases the elements matching the key, if any exist, returning the + // number of elements erased. + using Base::erase; + + // btree_multimap::insert() + // + // Inserts an element of the specified value into the `btree_multimap`, + // returning an iterator pointing to the newly inserted element. + // Any references, pointers, or iterators are invalidated. Overloads are + // listed below. + // + // iterator insert(const value_type& value): + // + // Inserts a value into the `btree_multimap`, returning an iterator to the + // inserted element. + // + // iterator insert(value_type&& value): + // + // Inserts a moveable value into the `btree_multimap`, returning an iterator + // to the inserted element. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_multimap::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multimap`. Any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_multimap::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multimap`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search. + // + // Any references, pointers, or iterators are invalidated. + using Base::emplace_hint; + + // btree_multimap::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template node_type extract(const K& k): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_multimap` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_multimap::merge() + // + // Extracts all elements from a given `source` btree_multimap into this + // `btree_multimap`. + using Base::merge; + + // btree_multimap::swap(btree_multimap& other) + // + // Exchanges the contents of this `btree_multimap` with those of the `other` + // btree_multimap, avoiding invocation of any move, copy, or swap operations + // on individual elements. + // + // All iterators and references on the `btree_multimap` remain valid, + // excepting for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_multimap::contains() + // + // template bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_multimap`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::contains; + + // btree_multimap::count() + // + // template size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::count; + + // btree_multimap::equal_range() + // + // Returns a half-open range [first, last), defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_multimap`. + using Base::equal_range; + + // btree_multimap::find() + // + // template iterator find(const K& key): + // template const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::find; + + // btree_multimap::lower_bound() + // + // template iterator lower_bound(const K& key): + // template const_iterator lower_bound(const K& key) const: + // + // Finds the first element with a key that is not less than `key` within the + // `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_multimap::upper_bound() + // + // template iterator upper_bound(const K& key): + // template const_iterator upper_bound(const K& key) const: + // + // Finds the first element with a key that is greater than `key` within the + // `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::upper_bound; + + // btree_multimap::get_allocator() + // + // Returns the allocator function associated with this `btree_multimap`. + using Base::get_allocator; + + // btree_multimap::key_comp(); + // + // Returns the key comparator associated with this `btree_multimap`. + using Base::key_comp; + + // btree_multimap::value_comp(); + // + // Returns the value comparator associated with this `btree_multimap`. + using Base::value_comp; +}; + +// absl::swap(absl::btree_multimap<>, absl::btree_multimap<>) +// +// Swaps the contents of two `absl::btree_multimap` containers. +template +void swap(btree_multimap &x, btree_multimap &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_multimap<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +// Returns the number of erased elements. +template +typename btree_multimap::size_type erase_if( + btree_multimap &map, Pred pred) { + return container_internal::btree_access::erase_if(map, std::move(pred)); +} + +namespace container_internal { + +// A parameters structure for holding the type parameters for a btree_map. +// Compare and Alloc should be nothrow copy-constructible. +template +struct map_params : common_params> { + using super_type = typename map_params::common_params; + using mapped_type = Data; + // This type allows us to move keys when it is safe to do so. It is safe + // for maps in which value_type and mutable_value_type are layout compatible. + using slot_policy = typename super_type::slot_policy; + using slot_type = typename super_type::slot_type; + using value_type = typename super_type::value_type; + using init_type = typename super_type::init_type; + + template + static auto key(const V &value) -> decltype(value.first) { + return value.first; + } + static const Key &key(const slot_type *s) { return slot_policy::key(s); } + static const Key &key(slot_type *s) { return slot_policy::key(s); } + // For use in node handle. + static auto mutable_key(slot_type *s) + -> decltype(slot_policy::mutable_key(s)) { + return slot_policy::mutable_key(s); + } + static mapped_type &value(value_type *value) { return value->second; } +}; + +} // namespace container_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_BTREE_MAP_H_ diff --git a/src/absl/container/btree_set.h b/src/absl/container/btree_set.h new file mode 100644 index 0000000..695b09f --- /dev/null +++ b/src/absl/container/btree_set.h @@ -0,0 +1,793 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: btree_set.h +// ----------------------------------------------------------------------------- +// +// This header file defines B-tree sets: sorted associative containers of +// values. +// +// * `absl::btree_set<>` +// * `absl::btree_multiset<>` +// +// These B-tree types are similar to the corresponding types in the STL +// (`std::set` and `std::multiset`) and generally conform to the STL interfaces +// of those types. However, because they are implemented using B-trees, they +// are more efficient in most situations. +// +// Unlike `std::set` and `std::multiset`, which are commonly implemented using +// red-black tree nodes, B-tree sets use more generic B-tree nodes able to hold +// multiple values per node. Holding multiple values per node often makes +// B-tree sets perform better than their `std::set` counterparts, because +// multiple entries can be checked within the same cache hit. +// +// However, these types should not be considered drop-in replacements for +// `std::set` and `std::multiset` as there are some API differences, which are +// noted in this header file. The most consequential differences with respect to +// migrating to b-tree from the STL types are listed in the next paragraph. +// Other API differences are minor. +// +// Importantly, insertions and deletions may invalidate outstanding iterators, +// pointers, and references to elements. Such invalidations are typically only +// an issue if insertion and deletion operations are interleaved with the use of +// more than one iterator, pointer, or reference simultaneously. For this +// reason, `insert()` and `erase()` return a valid iterator at the current +// position. + +#ifndef ABSL_CONTAINER_BTREE_SET_H_ +#define ABSL_CONTAINER_BTREE_SET_H_ + +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/btree_container.h" // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace container_internal { + +template +struct set_slot_policy; + +template +struct set_params; + +} // namespace container_internal + +// absl::btree_set<> +// +// An `absl::btree_set` is an ordered associative container of unique key +// values designed to be a more efficient replacement for `std::set` (in most +// cases). +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less`. +// +// An `absl::btree_set` uses a default allocator of `std::allocator` to +// allocate (and deallocate) nodes, and construct and destruct values within +// those nodes. You may instead specify a custom allocator `A` (which in turn +// requires specifying a custom comparator `C`) as in +// `absl::btree_set`. +// +template , + typename Alloc = std::allocator> +class btree_set + : public container_internal::btree_set_container< + container_internal::btree>> { + using Base = typename btree_set::btree_set_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_set` supports the same overload set as `std::set` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_set set1; + // + // * Initializer List constructor + // + // absl::btree_set set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::btree_set set3(set2); + // + // * Copy assignment operator + // + // absl::btree_set set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_set set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_set set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::btree_set set7(v.begin(), v.end()); + btree_set() {} + using Base::Base; + + // btree_set::begin() + // + // Returns an iterator to the beginning of the `btree_set`. + using Base::begin; + + // btree_set::cbegin() + // + // Returns a const iterator to the beginning of the `btree_set`. + using Base::cbegin; + + // btree_set::end() + // + // Returns an iterator to the end of the `btree_set`. + using Base::end; + + // btree_set::cend() + // + // Returns a const iterator to the end of the `btree_set`. + using Base::cend; + + // btree_set::empty() + // + // Returns whether or not the `btree_set` is empty. + using Base::empty; + + // btree_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_set` under current memory constraints. This value can be thought + // of as the largest value of `std::distance(begin(), end())` for a + // `btree_set`. + using Base::max_size; + + // btree_set::size() + // + // Returns the number of elements currently within the `btree_set`. + using Base::size; + + // btree_set::clear() + // + // Removes all elements from the `btree_set`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_set::erase() + // + // Erases elements within the `btree_set`. Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_set`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template size_type erase(const K& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). + using Base::erase; + + // btree_set::insert() + // + // Inserts an element of the specified value into the `btree_set`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If an insertion + // occurs, any references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // std::pair insert(const value_type& value): + // + // Inserts a value into the `btree_set`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(value_type&& value): + // + // Inserts a moveable value into the `btree_set`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_set::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_set`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_set::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_set`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace_hint; + + // btree_set::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template node_type extract(const K& k): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_set` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_set::merge() + // + // Extracts elements from a given `source` btree_set into this + // `btree_set`. If the destination `btree_set` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_set::swap(btree_set& other) + // + // Exchanges the contents of this `btree_set` with those of the `other` + // btree_set, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `btree_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_set::contains() + // + // template bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_set`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::contains; + + // btree_set::count() + // + // template size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_set`. Note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `btree_set`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::count; + + // btree_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_set`. + using Base::equal_range; + + // btree_set::find() + // + // template iterator find(const K& key): + // template const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::find; + + // btree_set::lower_bound() + // + // template iterator lower_bound(const K& key): + // template const_iterator lower_bound(const K& key) const: + // + // Finds the first element that is not less than `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_set::upper_bound() + // + // template iterator upper_bound(const K& key): + // template const_iterator upper_bound(const K& key) const: + // + // Finds the first element that is greater than `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::upper_bound; + + // btree_set::get_allocator() + // + // Returns the allocator function associated with this `btree_set`. + using Base::get_allocator; + + // btree_set::key_comp(); + // + // Returns the key comparator associated with this `btree_set`. + using Base::key_comp; + + // btree_set::value_comp(); + // + // Returns the value comparator associated with this `btree_set`. The keys to + // sort the elements are the values themselves, therefore `value_comp` and its + // sibling member function `key_comp` are equivalent. + using Base::value_comp; +}; + +// absl::swap(absl::btree_set<>, absl::btree_set<>) +// +// Swaps the contents of two `absl::btree_set` containers. +template +void swap(btree_set &x, btree_set &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_set<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +// Returns the number of erased elements. +template +typename btree_set::size_type erase_if(btree_set &set, + Pred pred) { + return container_internal::btree_access::erase_if(set, std::move(pred)); +} + +// absl::btree_multiset<> +// +// An `absl::btree_multiset` is an ordered associative container of +// keys and associated values designed to be a more efficient replacement +// for `std::multiset` (in most cases). Unlike `absl::btree_set`, a B-tree +// multiset allows equivalent elements. +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less`. +// +// An `absl::btree_multiset` uses a default allocator of `std::allocator` +// to allocate (and deallocate) nodes, and construct and destruct values within +// those nodes. You may instead specify a custom allocator `A` (which in turn +// requires specifying a custom comparator `C`) as in +// `absl::btree_multiset`. +// +template , + typename Alloc = std::allocator> +class btree_multiset + : public container_internal::btree_multiset_container< + container_internal::btree>> { + using Base = typename btree_multiset::btree_multiset_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_multiset` supports the same overload set as `std::set` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_multiset set1; + // + // * Initializer List constructor + // + // absl::btree_multiset set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::btree_multiset set3(set2); + // + // * Copy assignment operator + // + // absl::btree_multiset set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_multiset set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_multiset set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::btree_multiset set7(v.begin(), v.end()); + btree_multiset() {} + using Base::Base; + + // btree_multiset::begin() + // + // Returns an iterator to the beginning of the `btree_multiset`. + using Base::begin; + + // btree_multiset::cbegin() + // + // Returns a const iterator to the beginning of the `btree_multiset`. + using Base::cbegin; + + // btree_multiset::end() + // + // Returns an iterator to the end of the `btree_multiset`. + using Base::end; + + // btree_multiset::cend() + // + // Returns a const iterator to the end of the `btree_multiset`. + using Base::cend; + + // btree_multiset::empty() + // + // Returns whether or not the `btree_multiset` is empty. + using Base::empty; + + // btree_multiset::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_multiset` under current memory constraints. This value can be + // thought of as the largest value of `std::distance(begin(), end())` for a + // `btree_multiset`. + using Base::max_size; + + // btree_multiset::size() + // + // Returns the number of elements currently within the `btree_multiset`. + using Base::size; + + // btree_multiset::clear() + // + // Removes all elements from the `btree_multiset`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_multiset::erase() + // + // Erases elements within the `btree_multiset`. Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_multiset`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template size_type erase(const K& key): + // + // Erases the elements matching the key, if any exist, returning the + // number of elements erased. + using Base::erase; + + // btree_multiset::insert() + // + // Inserts an element of the specified value into the `btree_multiset`, + // returning an iterator pointing to the newly inserted element. + // Any references, pointers, or iterators are invalidated. Overloads are + // listed below. + // + // iterator insert(const value_type& value): + // + // Inserts a value into the `btree_multiset`, returning an iterator to the + // inserted element. + // + // iterator insert(value_type&& value): + // + // Inserts a moveable value into the `btree_multiset`, returning an iterator + // to the inserted element. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_multiset::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multiset`. Any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_multiset::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multiset`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search. + // + // Any references, pointers, or iterators are invalidated. + using Base::emplace_hint; + + // btree_multiset::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template node_type extract(const K& k): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_multiset` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_multiset::merge() + // + // Extracts all elements from a given `source` btree_multiset into this + // `btree_multiset`. + using Base::merge; + + // btree_multiset::swap(btree_multiset& other) + // + // Exchanges the contents of this `btree_multiset` with those of the `other` + // btree_multiset, avoiding invocation of any move, copy, or swap operations + // on individual elements. + // + // All iterators and references on the `btree_multiset` remain valid, + // excepting for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_multiset::contains() + // + // template bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_multiset`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::contains; + + // btree_multiset::count() + // + // template size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::count; + + // btree_multiset::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_multiset`. + using Base::equal_range; + + // btree_multiset::find() + // + // template iterator find(const K& key): + // template const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::find; + + // btree_multiset::lower_bound() + // + // template iterator lower_bound(const K& key): + // template const_iterator lower_bound(const K& key) const: + // + // Finds the first element that is not less than `key` within the + // `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_multiset::upper_bound() + // + // template iterator upper_bound(const K& key): + // template const_iterator upper_bound(const K& key) const: + // + // Finds the first element that is greater than `key` within the + // `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::upper_bound; + + // btree_multiset::get_allocator() + // + // Returns the allocator function associated with this `btree_multiset`. + using Base::get_allocator; + + // btree_multiset::key_comp(); + // + // Returns the key comparator associated with this `btree_multiset`. + using Base::key_comp; + + // btree_multiset::value_comp(); + // + // Returns the value comparator associated with this `btree_multiset`. The + // keys to sort the elements are the values themselves, therefore `value_comp` + // and its sibling member function `key_comp` are equivalent. + using Base::value_comp; +}; + +// absl::swap(absl::btree_multiset<>, absl::btree_multiset<>) +// +// Swaps the contents of two `absl::btree_multiset` containers. +template +void swap(btree_multiset &x, btree_multiset &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_multiset<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +// Returns the number of erased elements. +template +typename btree_multiset::size_type erase_if( + btree_multiset & set, Pred pred) { + return container_internal::btree_access::erase_if(set, std::move(pred)); +} + +namespace container_internal { + +// This type implements the necessary functions from the +// absl::container_internal::slot_type interface for btree_(multi)set. +template +struct set_slot_policy { + using slot_type = Key; + using value_type = Key; + using mutable_value_type = Key; + + static value_type &element(slot_type *slot) { return *slot; } + static const value_type &element(const slot_type *slot) { return *slot; } + + template + static void construct(Alloc *alloc, slot_type *slot, Args &&...args) { + absl::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + template + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + absl::allocator_traits::construct(*alloc, slot, std::move(*other)); + } + + template + static void construct(Alloc *alloc, slot_type *slot, const slot_type *other) { + absl::allocator_traits::construct(*alloc, slot, *other); + } + + template + static void destroy(Alloc *alloc, slot_type *slot) { + absl::allocator_traits::destroy(*alloc, slot); + } + + template + static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) { + construct(alloc, new_slot, old_slot); + destroy(alloc, old_slot); + } +}; + +// A parameters structure for holding the type parameters for a btree_set. +// Compare and Alloc should be nothrow copy-constructible. +template +struct set_params : common_params> { + using value_type = Key; + using slot_type = typename set_params::common_params::slot_type; + + template + static const V &key(const V &value) { + return value; + } + static const Key &key(const slot_type *slot) { return *slot; } + static const Key &key(slot_type *slot) { return *slot; } +}; + +} // namespace container_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_BTREE_SET_H_ diff --git a/src/absl/container/fixed_array.h b/src/absl/container/fixed_array.h new file mode 100644 index 0000000..2aefae3 --- /dev/null +++ b/src/absl/container/fixed_array.h @@ -0,0 +1,529 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: fixed_array.h +// ----------------------------------------------------------------------------- +// +// A `FixedArray` represents a non-resizable array of `T` where the length of +// the array can be determined at run-time. It is a good replacement for +// non-standard and deprecated uses of `alloca()` and variable length arrays +// within the GCC extension. (See +// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html). +// +// `FixedArray` allocates small arrays inline, keeping performance fast by +// avoiding heap operations. It also helps reduce the chances of +// accidentally overflowing your stack if large input is passed to +// your function. + +#ifndef ABSL_CONTAINER_FIXED_ARRAY_H_ +#define ABSL_CONTAINER_FIXED_ARRAY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/config.h" +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +constexpr static auto kFixedArrayUseDefault = static_cast(-1); + +// ----------------------------------------------------------------------------- +// FixedArray +// ----------------------------------------------------------------------------- +// +// A `FixedArray` provides a run-time fixed-size array, allocating a small array +// inline for efficiency. +// +// Most users should not specify an `inline_elements` argument and let +// `FixedArray` automatically determine the number of elements +// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the +// `FixedArray` implementation will use inline storage for arrays with a +// length <= `inline_elements`. +// +// Note that a `FixedArray` constructed with a `size_type` argument will +// default-initialize its values by leaving trivially constructible types +// uninitialized (e.g. int, int[4], double), and others default-constructed. +// This matches the behavior of c-style arrays and `std::array`, but not +// `std::vector`. +template > +class FixedArray { + static_assert(!std::is_array::value || std::extent::value > 0, + "Arrays with unknown bounds cannot be used with FixedArray."); + + static constexpr size_t kInlineBytesDefault = 256; + + using AllocatorTraits = std::allocator_traits; + // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, + // but this seems to be mostly pedantic. + template + using EnableIfForwardIterator = absl::enable_if_t::iterator_category, + std::forward_iterator_tag>::value>; + static constexpr bool NoexceptCopyable() { + return std::is_nothrow_copy_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool NoexceptMovable() { + return std::is_nothrow_move_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool DefaultConstructorIsNonTrivial() { + return !absl::is_trivially_default_constructible::value; + } + + public: + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static constexpr size_type inline_elements = + (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) + : static_cast(N)); + + FixedArray( + const FixedArray& other, + const allocator_type& a = allocator_type()) noexcept(NoexceptCopyable()) + : FixedArray(other.begin(), other.end(), a) {} + + FixedArray( + FixedArray&& other, + const allocator_type& a = allocator_type()) noexcept(NoexceptMovable()) + : FixedArray(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end()), a) {} + + // Creates an array object that can store `n` elements. + // Note that trivially constructible elements will be uninitialized. + explicit FixedArray(size_type n, const allocator_type& a = allocator_type()) + : storage_(n, a) { + if (DefaultConstructorIsNonTrivial()) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end()); + } + } + + // Creates an array initialized with `n` copies of `val`. + FixedArray(size_type n, const value_type& val, + const allocator_type& a = allocator_type()) + : storage_(n, a) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end(), val); + } + + // Creates an array initialized with the size and contents of `init_list`. + FixedArray(std::initializer_list init_list, + const allocator_type& a = allocator_type()) + : FixedArray(init_list.begin(), init_list.end(), a) {} + + // Creates an array initialized with the elements from the input + // range. The array's size will always be `std::distance(first, last)`. + // REQUIRES: Iterator must be a forward_iterator or better. + template * = nullptr> + FixedArray(Iterator first, Iterator last, + const allocator_type& a = allocator_type()) + : storage_(std::distance(first, last), a) { + memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last); + } + + ~FixedArray() noexcept { + for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) { + AllocatorTraits::destroy(storage_.alloc(), cur); + } + } + + // Assignments are deleted because they break the invariant that the size of a + // `FixedArray` never changes. + void operator=(FixedArray&&) = delete; + void operator=(const FixedArray&) = delete; + + // FixedArray::size() + // + // Returns the length of the fixed array. + size_type size() const { return storage_.size(); } + + // FixedArray::max_size() + // + // Returns the largest possible value of `std::distance(begin(), end())` for a + // `FixedArray`. 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::max)() / sizeof(value_type); + } + + // FixedArray::empty() + // + // Returns whether or not the fixed array is empty. + bool empty() const { return size() == 0; } + + // FixedArray::memsize() + // + // Returns the memory size of the fixed array in bytes. + size_t memsize() const { return size() * sizeof(value_type); } + + // FixedArray::data() + // + // Returns a const T* pointer to elements of the `FixedArray`. This pointer + // can be used to access (but not modify) the contained elements. + const_pointer data() const { return AsValueType(storage_.begin()); } + + // Overload of FixedArray::data() to return a T* pointer to elements of the + // fixed array. This pointer can be used to access and modify the contained + // elements. + pointer data() { return AsValueType(storage_.begin()); } + + // FixedArray::operator[] + // + // Returns a reference the ith element of the fixed array. + // REQUIRES: 0 <= i < size() + reference operator[](size_type i) { + ABSL_HARDENING_ASSERT(i < size()); + return data()[i]; + } + + // Overload of FixedArray::operator()[] to return a const reference to the + // ith element of the fixed array. + // REQUIRES: 0 <= i < size() + const_reference operator[](size_type i) const { + ABSL_HARDENING_ASSERT(i < size()); + return data()[i]; + } + + // FixedArray::at + // + // Bounds-checked access. Returns a reference to the ith element of the fixed + // array, or throws std::out_of_range + reference at(size_type i) { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + } + return data()[i]; + } + + // Overload of FixedArray::at() to return a const reference to the ith element + // of the fixed array. + const_reference at(size_type i) const { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + } + return data()[i]; + } + + // FixedArray::front() + // + // Returns a reference to the first element of the fixed array. + reference front() { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } + + // Overload of FixedArray::front() to return a reference to the first element + // of a fixed array of const values. + const_reference front() const { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } + + // FixedArray::back() + // + // Returns a reference to the last element of the fixed array. + reference back() { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } + + // Overload of FixedArray::back() to return a reference to the last element + // of a fixed array of const values. + const_reference back() const { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } + + // FixedArray::begin() + // + // Returns an iterator to the beginning of the fixed array. + iterator begin() { return data(); } + + // Overload of FixedArray::begin() to return a const iterator to the + // beginning of the fixed array. + const_iterator begin() const { return data(); } + + // FixedArray::cbegin() + // + // Returns a const iterator to the beginning of the fixed array. + const_iterator cbegin() const { return begin(); } + + // FixedArray::end() + // + // Returns an iterator to the end of the fixed array. + iterator end() { return data() + size(); } + + // Overload of FixedArray::end() to return a const iterator to the end of the + // fixed array. + const_iterator end() const { return data() + size(); } + + // FixedArray::cend() + // + // Returns a const iterator to the end of the fixed array. + const_iterator cend() const { return end(); } + + // FixedArray::rbegin() + // + // Returns a reverse iterator from the end of the fixed array. + reverse_iterator rbegin() { return reverse_iterator(end()); } + + // Overload of FixedArray::rbegin() to return a const reverse iterator from + // the end of the fixed array. + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + // FixedArray::crbegin() + // + // Returns a const reverse iterator from the end of the fixed array. + const_reverse_iterator crbegin() const { return rbegin(); } + + // FixedArray::rend() + // + // Returns a reverse iterator from the beginning of the fixed array. + reverse_iterator rend() { return reverse_iterator(begin()); } + + // Overload of FixedArray::rend() for returning a const reverse iterator + // from the beginning of the fixed array. + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // FixedArray::crend() + // + // Returns a reverse iterator from the beginning of the fixed array. + const_reverse_iterator crend() const { return rend(); } + + // FixedArray::fill() + // + // Assigns the given `value` to all elements in the fixed array. + void fill(const value_type& val) { std::fill(begin(), end(), val); } + + // Relational operators. Equality operators are elementwise using + // `operator==`, while order operators order FixedArrays lexicographically. + friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) { + return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), + rhs.end()); + } + + friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) { + return rhs < lhs; + } + + friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) { + return !(rhs < lhs); + } + + friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) { + return !(lhs < rhs); + } + + template + friend H AbslHashValue(H h, const FixedArray& v) { + return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()), + v.size()); + } + + private: + // StorageElement + // + // For FixedArrays with a C-style-array value_type, StorageElement is a POD + // wrapper struct called StorageElementWrapper that holds the value_type + // instance inside. This is needed for construction and destruction of the + // entire array regardless of how many dimensions it has. For all other cases, + // StorageElement is just an alias of value_type. + // + // Maintainer's Note: The simpler solution would be to simply wrap value_type + // in a struct whether it's an array or not. That causes some paranoid + // diagnostics to misfire, believing that 'data()' returns a pointer to a + // single element, rather than the packed array that it really is. + // e.g.: + // + // FixedArray buf(1); + // sprintf(buf.data(), "foo"); + // + // error: call to int __builtin___sprintf_chk(etc...) + // will always overflow destination buffer [-Werror] + // + template , + size_t InnerN = std::extent::value> + struct StorageElementWrapper { + InnerT array[InnerN]; + }; + + using StorageElement = + absl::conditional_t::value, + StorageElementWrapper, value_type>; + + static pointer AsValueType(pointer ptr) { return ptr; } + static pointer AsValueType(StorageElementWrapper* ptr) { + return std::addressof(ptr->array); + } + + static_assert(sizeof(StorageElement) == sizeof(value_type), ""); + static_assert(alignof(StorageElement) == alignof(value_type), ""); + + class NonEmptyInlinedStorage { + public: + StorageElement* data() { return reinterpret_cast(buff_); } + void AnnotateConstruct(size_type n); + void AnnotateDestruct(size_type n); + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + void* RedzoneBegin() { return &redzone_begin_; } + void* RedzoneEnd() { return &redzone_end_ + 1; } +#endif // ABSL_HAVE_ADDRESS_SANITIZER + + private: + ABSL_ADDRESS_SANITIZER_REDZONE(redzone_begin_); + alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])]; + ABSL_ADDRESS_SANITIZER_REDZONE(redzone_end_); + }; + + class EmptyInlinedStorage { + public: + StorageElement* data() { return nullptr; } + void AnnotateConstruct(size_type) {} + void AnnotateDestruct(size_type) {} + }; + + using InlinedStorage = + absl::conditional_t; + + // Storage + // + // An instance of Storage manages the inline and out-of-line memory for + // instances of FixedArray. This guarantees that even when construction of + // individual elements fails in the FixedArray constructor body, the + // destructor for Storage will still be called and out-of-line memory will be + // properly deallocated. + // + class Storage : public InlinedStorage { + public: + Storage(size_type n, const allocator_type& a) + : size_alloc_(n, a), data_(InitializeData()) {} + + ~Storage() noexcept { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateDestruct(size()); + } else { + AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size()); + } + } + + size_type size() const { return size_alloc_.template get<0>(); } + StorageElement* begin() const { return data_; } + StorageElement* end() const { return begin() + size(); } + allocator_type& alloc() { return size_alloc_.template get<1>(); } + + private: + static bool UsingInlinedStorage(size_type n) { + return n <= inline_elements; + } + + StorageElement* InitializeData() { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateConstruct(size()); + return InlinedStorage::data(); + } else { + return reinterpret_cast( + AllocatorTraits::allocate(alloc(), size())); + } + } + + // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s + container_internal::CompressedTuple size_alloc_; + StorageElement* data_; + }; + + Storage storage_; +}; + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +template +constexpr size_t FixedArray::kInlineBytesDefault; + +template +constexpr typename FixedArray::size_type + FixedArray::inline_elements; +#endif + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateConstruct( + typename FixedArray::size_type n) { +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + if (!n) return; + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), + data() + n); + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), + RedzoneBegin()); +#endif // ABSL_HAVE_ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateDestruct( + typename FixedArray::size_type n) { +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + if (!n) return; + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, + RedzoneEnd()); + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), + data()); +#endif // ABSL_HAVE_ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/src/absl/container/flat_hash_map.h b/src/absl/container/flat_hash_map.h new file mode 100644 index 0000000..e6bdbd9 --- /dev/null +++ b/src/absl/container/flat_hash_map.h @@ -0,0 +1,613 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: flat_hash_map.h +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_map` 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 +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/base/macros.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +struct FlatHashMapPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::flat_hash_map +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_map` 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` with +// the following notable differences: +// +// * Requires keys that are CopyConstructible +// * Requires values that are MoveConstructible +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Invalidates any references and pointers to elements within the table after +// `rehash()`. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash map. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `flat_hash_map` uses the `absl::Hash` hashing framework. +// All fundamental and Abseil types that support the `absl::Hash` framework have +// a compatible equality operator for comparing insertions into `flat_hash_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// Using `absl::flat_hash_map` at interface boundaries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// +// NOTE: A `flat_hash_map` stores its value types directly inside its +// implementation array to avoid memory indirection. Because a `flat_hash_map` +// is designed to move data when rehashed, map values will not retain pointer +// stability. If you require pointer stability, or if your values are large, +// consider using `absl::flat_hash_map>` 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 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 Eq = absl::container_internal::hash_default_eq, + class Allocator = std::allocator>> +class flat_hash_map : public absl::container_internal::raw_hash_map< + absl::container_internal::FlatHashMapPolicy, + 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 map1; + // + // * Initializer List constructor + // + // absl::flat_hash_map map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::flat_hash_map map3(map2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::flat_hash_map map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::flat_hash_map map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::flat_hash_map map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::flat_hash_map 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`. + using Base::max_size; + + // flat_hash_map::size() + // + // Returns the number of elements currently within the `flat_hash_map`. + using Base::size; + + // flat_hash_map::clear() + // + // Removes all elements from the `flat_hash_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // flat_hash_map::erase() + // + // Erases elements within the `flat_hash_map`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `flat_hash_map`, returning + // `void`. + // + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_map` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // map.erase(it++); + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). + using Base::erase; + + // flat_hash_map::insert() + // + // Inserts an element of the specified value into the `flat_hash_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair 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 insert(T&& value): + // std::pair 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 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 insert_or_assign(const init_type& k, T&& obj): + // pair 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 try_emplace(const key_type& k, Args&&... args): + // pair try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map`. + // + // iterator try_emplace(const_iterator hint, + // const key_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + // + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. + using Base::try_emplace; + + // flat_hash_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the key,value pair of the element at the indicated position and + // returns a node handle owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the key,value pair of the element with a key matching the passed + // key value and returns a node handle owning that extracted data. If the + // `flat_hash_map` does not contain an element with a matching key, this + // function returns an empty node handle. + // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). + using Base::extract; + + // flat_hash_map::merge() + // + // Extracts elements from a given `source` flat hash map into this + // `flat_hash_map`. If the destination `flat_hash_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // flat_hash_map::swap(flat_hash_map& other) + // + // Exchanges the contents of this `flat_hash_map` with those of the `other` + // flat hash map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `flat_hash_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash map's hashing and key equivalence + // functions be Swappable, and are exchanged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_map::rehash(count) + // + // Rehashes the `flat_hash_map`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_map`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_map::reserve(count) + // + // Sets the number of slots in the `flat_hash_map` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // flat_hash_map::contains() + // + // Determines whether an element with a key comparing equal to the given `key` + // exists within the `flat_hash_map`, returning `true` if so or `false` + // otherwise. + using Base::contains; + + // flat_hash_map::count(const Key& key) const + // + // Returns the number of elements with a key comparing equal to the given + // `key` within the `flat_hash_map`. note that this function will return + // either `1` or `0` since duplicate keys are not allowed within a + // `flat_hash_map`. + using Base::count; + + // flat_hash_map::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_map`. + using Base::equal_range; + + // flat_hash_map::find() + // + // Finds an element with the passed `key` within the `flat_hash_map`. + using Base::find; + + // flat_hash_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `flat_hash_map`, performing an `insert()` if the key does not already + // exist. + // + // If an insertion occurs and results in a rehashing of the container, all + // iterators are invalidated. Otherwise iterators are not affected and + // references are not invalidated. Overloads are listed below. + // + // T& operator[](const Key& key): + // + // Inserts an init_type object constructed in-place if the element with the + // given key does not exist. + // + // T& operator[](Key&& key): + // + // Inserts an init_type object constructed in-place provided that an element + // with the given key does not exist. + using Base::operator[]; + + // flat_hash_map::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_map`. Note that + // because a flat hash map contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_map`. + using Base::bucket_count; + + // flat_hash_map::load_factor() + // + // Returns the current load factor of the `flat_hash_map` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // flat_hash_map::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_map`. Overloads are + // listed below. + // + // float flat_hash_map::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_map`. + // + // void flat_hash_map::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_map` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_map` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_map::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_map`. + using Base::get_allocator; + + // flat_hash_map::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_map`. + using Base::hash_function; + + // flat_hash_map::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +// erase_if(flat_hash_map<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. +template +typename flat_hash_map::size_type erase_if( + flat_hash_map& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +struct FlatHashMapPolicy { + using slot_policy = container_internal::map_slot_policy; + using slot_type = typename slot_policy::slot_type; + using key_type = K; + using mapped_type = V; + using init_type = std::pair; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + slot_policy::construct(alloc, slot, std::forward(args)...); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + slot_policy::destroy(alloc, slot); + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + slot_policy::transfer(alloc, new_slot, old_slot); + } + + template + static decltype(absl::container_internal::DecomposePair( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposePair(std::forward(f), + std::forward(args)...); + } + + static size_t space_used(const slot_type*) { return 0; } + + static std::pair& element(slot_type* slot) { return slot->value; } + + static V& value(std::pair* kv) { return kv->second; } + static const V& value(const std::pair* kv) { return kv->second; } +}; + +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer< + absl::flat_hash_map> : std::true_type {}; + +} // namespace container_algorithm_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_ diff --git a/src/absl/container/flat_hash_set.h b/src/absl/container/flat_hash_set.h new file mode 100644 index 0000000..4938c70 --- /dev/null +++ b/src/absl/container/flat_hash_set.h @@ -0,0 +1,510 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: flat_hash_set.h +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_set` 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 +#include + +#include "absl/algorithm/container.h" +#include "absl/base/macros.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +struct FlatHashSetPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::flat_hash_set +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_set` 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` with the +// following notable differences: +// +// * Requires keys that are CopyConstructible +// * Supports heterogeneous lookup, through `find()` and `insert()`, provided +// that the set is provided a compatible heterogeneous hashing function and +// equality operator. +// * Invalidates any references and pointers to elements within the table after +// `rehash()`. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash set. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All +// fundamental and Abseil types that support the `absl::Hash` framework have a +// compatible equality operator for comparing insertions into `flat_hash_set`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// Using `absl::flat_hash_set` at interface boundaries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// +// NOTE: A `flat_hash_set` stores its keys directly inside its implementation +// array to avoid memory indirection. Because a `flat_hash_set` is designed to +// move data when rehashed, set keys will not retain pointer stability. If you +// require pointer stability, consider using +// `absl::flat_hash_set>`. 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 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 Eq = absl::container_internal::hash_default_eq, + class Allocator = std::allocator> +class flat_hash_set + : public absl::container_internal::raw_hash_set< + absl::container_internal::FlatHashSetPolicy, Hash, Eq, Allocator> { + using Base = typename flat_hash_set::raw_hash_set; + + public: + // Constructors and Assignment Operators + // + // A flat_hash_set supports the same overload set as `std::unordered_set` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::flat_hash_set set1; + // + // * Initializer List constructor + // + // absl::flat_hash_set set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::flat_hash_set set3(set2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::flat_hash_set set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::flat_hash_set set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::flat_hash_set set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::flat_hash_set set7(v.begin(), v.end()); + flat_hash_set() {} + using Base::Base; + + // flat_hash_set::begin() + // + // Returns an iterator to the beginning of the `flat_hash_set`. + using Base::begin; + + // flat_hash_set::cbegin() + // + // Returns a const iterator to the beginning of the `flat_hash_set`. + using Base::cbegin; + + // flat_hash_set::cend() + // + // Returns a const iterator to the end of the `flat_hash_set`. + using Base::cend; + + // flat_hash_set::end() + // + // Returns an iterator to the end of the `flat_hash_set`. + using Base::end; + + // flat_hash_set::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `flat_hash_set`. + // + // NOTE: this member function is particular to `absl::flat_hash_set` and is + // not provided in the `std::unordered_set` API. + using Base::capacity; + + // flat_hash_set::empty() + // + // Returns whether or not the `flat_hash_set` is empty. + using Base::empty; + + // flat_hash_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `flat_hash_set` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `flat_hash_set`. + using Base::max_size; + + // flat_hash_set::size() + // + // Returns the number of elements currently within the `flat_hash_set`. + using Base::size; + + // flat_hash_set::clear() + // + // Removes all elements from the `flat_hash_set`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // flat_hash_set::erase() + // + // Erases elements within the `flat_hash_set`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `flat_hash_set`, returning + // `void`. + // + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_set` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // set.erase(it++); + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). + using Base::erase; + + // flat_hash_set::insert() + // + // Inserts an element of the specified value into the `flat_hash_set`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair 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 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 ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `flat_hash_set` we guarantee the first match is inserted. + using Base::insert; + + // flat_hash_set::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_set`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // flat_hash_set::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_set`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // flat_hash_set::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `flat_hash_set` + // does not contain an element with a matching key, this function returns an + // empty node handle. + using Base::extract; + + // flat_hash_set::merge() + // + // Extracts elements from a given `source` flat hash set into this + // `flat_hash_set`. If the destination `flat_hash_set` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // flat_hash_set::swap(flat_hash_set& other) + // + // Exchanges the contents of this `flat_hash_set` with those of the `other` + // flat hash set, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `flat_hash_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash set's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the set's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_set::rehash(count) + // + // Rehashes the `flat_hash_set`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_set`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_set::reserve(count) + // + // Sets the number of slots in the `flat_hash_set` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_set::contains() + // + // Determines whether an element comparing equal to the given `key` exists + // within the `flat_hash_set`, returning `true` if so or `false` otherwise. + using Base::contains; + + // flat_hash_set::count(const Key& key) const + // + // Returns the number of elements comparing equal to the given `key` within + // the `flat_hash_set`. note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `flat_hash_set`. + using Base::count; + + // flat_hash_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_set`. + using Base::equal_range; + + // flat_hash_set::find() + // + // Finds an element with the passed `key` within the `flat_hash_set`. + using Base::find; + + // flat_hash_set::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_set`. Note that + // because a flat hash set contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_set`. + using Base::bucket_count; + + // flat_hash_set::load_factor() + // + // Returns the current load factor of the `flat_hash_set` (the average number + // of slots occupied with a value within the hash set). + using Base::load_factor; + + // flat_hash_set::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_set`. Overloads are + // listed below. + // + // float flat_hash_set::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_set`. + // + // void flat_hash_set::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_set` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_set` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_set::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_set`. + using Base::get_allocator; + + // flat_hash_set::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_set`. + using Base::hash_function; + + // flat_hash_set::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +// erase_if(flat_hash_set<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. +template +typename flat_hash_set::size_type erase_if( + flat_hash_set& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +struct FlatHashSetPolicy { + using slot_type = T; + using key_type = T; + using init_type = T; + using constant_iterators = std::true_type; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + absl::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + absl::allocator_traits::destroy(*alloc, slot); + } + + template + 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 + static decltype(absl::container_internal::DecomposeValue( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposeValue( + std::forward(f), std::forward(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 +struct IsUnorderedContainer> + : std::true_type {}; + +} // namespace container_algorithm_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_ diff --git a/src/absl/container/inlined_vector.h b/src/absl/container/inlined_vector.h new file mode 100644 index 0000000..e8d1019 --- /dev/null +++ b/src/absl/container/inlined_vector.h @@ -0,0 +1,866 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: inlined_vector.h +// ----------------------------------------------------------------------------- +// +// This header file contains the declaration and definition of an "inlined +// vector" which behaves in an equivalent fashion to a `std::vector`, except +// that storage for small sequences of the vector are provided inline without +// requiring any heap allocation. +// +// An `absl::InlinedVector` specifies the default capacity `N` as one of +// its template parameters. Instances where `size() <= N` hold contained +// elements in inline space. Typically `N` is very small so that sequences that +// are expected to be short do not require allocations. +// +// An `absl::InlinedVector` does not usually require a specific allocator. If +// the inlined vector grows beyond its initial constraints, it will need to +// allocate (as any normal `std::vector` would). This is usually performed with +// the default allocator (defined as `std::allocator`). Optionally, a custom +// allocator type may be specified as `A` in `absl::InlinedVector`. + +#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_ +#define ABSL_CONTAINER_INLINED_VECTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" +#include "absl/container/internal/inlined_vector.h" +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +// ----------------------------------------------------------------------------- +// InlinedVector +// ----------------------------------------------------------------------------- +// +// An `absl::InlinedVector` is designed to be a drop-in replacement for +// `std::vector` for use cases where the vector's size is sufficiently small +// that it can be inlined. If the inlined vector does grow beyond its estimated +// capacity, it will trigger an initial allocation on the heap, and will behave +// as a `std::vector`. The API of the `absl::InlinedVector` within this file is +// designed to cover the same API footprint as covered by `std::vector`. +template > +class InlinedVector { + static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity."); + + using Storage = inlined_vector_internal::Storage; + + template + using AllocatorTraits = inlined_vector_internal::AllocatorTraits; + template + using MoveIterator = inlined_vector_internal::MoveIterator; + template + using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk; + + template + using IteratorValueAdapter = + inlined_vector_internal::IteratorValueAdapter; + template + using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter; + template + using DefaultValueAdapter = + inlined_vector_internal::DefaultValueAdapter; + + template + using EnableIfAtLeastForwardIterator = absl::enable_if_t< + inlined_vector_internal::IsAtLeastForwardIterator::value, int>; + template + using DisableIfAtLeastForwardIterator = absl::enable_if_t< + !inlined_vector_internal::IsAtLeastForwardIterator::value, int>; + + public: + using allocator_type = A; + using value_type = inlined_vector_internal::ValueType; + using pointer = inlined_vector_internal::Pointer; + using const_pointer = inlined_vector_internal::ConstPointer; + using size_type = inlined_vector_internal::SizeType; + using difference_type = inlined_vector_internal::DifferenceType; + using reference = inlined_vector_internal::Reference; + using const_reference = inlined_vector_internal::ConstReference; + using iterator = inlined_vector_internal::Iterator; + using const_iterator = inlined_vector_internal::ConstIterator; + using reverse_iterator = inlined_vector_internal::ReverseIterator; + using const_reverse_iterator = + inlined_vector_internal::ConstReverseIterator; + + // --------------------------------------------------------------------------- + // InlinedVector Constructors and Destructor + // --------------------------------------------------------------------------- + + // Creates an empty inlined vector with a value-initialized allocator. + InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {} + + // Creates an empty inlined vector with a copy of `allocator`. + explicit InlinedVector(const allocator_type& allocator) noexcept + : storage_(allocator) {} + + // Creates an inlined vector with `n` copies of `value_type()`. + explicit InlinedVector(size_type n, + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(DefaultValueAdapter(), n); + } + + // Creates an inlined vector with `n` copies of `v`. + InlinedVector(size_type n, const_reference v, + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(CopyValueAdapter(std::addressof(v)), n); + } + + // Creates an inlined vector with copies of the elements of `list`. + InlinedVector(std::initializer_list list, + const allocator_type& allocator = allocator_type()) + : InlinedVector(list.begin(), list.end(), allocator) {} + + // Creates an inlined vector with elements constructed from the provided + // forward iterator range [`first`, `last`). + // + // NOTE: the `enable_if` prevents ambiguous interpretation between a call to + // this constructor with two integral arguments and a call to the above + // `InlinedVector(size_type, const_reference)` constructor. + template = 0> + InlinedVector(ForwardIterator first, ForwardIterator last, + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(IteratorValueAdapter(first), + static_cast(std::distance(first, last))); + } + + // Creates an inlined vector with elements constructed from the provided input + // iterator range [`first`, `last`). + template = 0> + InlinedVector(InputIterator first, InputIterator last, + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + std::copy(first, last, std::back_inserter(*this)); + } + + // Creates an inlined vector by copying the contents of `other` using + // `other`'s allocator. + InlinedVector(const InlinedVector& other) + : InlinedVector(other, other.storage_.GetAllocator()) {} + + // Creates an inlined vector by copying the contents of `other` using the + // provided `allocator`. + InlinedVector(const InlinedVector& other, const allocator_type& allocator) + : storage_(allocator) { + if (other.empty()) { + // Empty; nothing to do. + } else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) { + // Memcpy-able and do not need allocation. + storage_.MemcpyFrom(other.storage_); + } else { + storage_.InitFrom(other.storage_); + } + } + + // Creates an inlined vector by moving in the contents of `other` without + // allocating. If `other` contains allocated memory, the newly-created inlined + // vector will take ownership of that memory. However, if `other` does not + // contain allocated memory, the newly-created inlined vector will perform + // element-wise move construction of the contents of `other`. + // + // NOTE: since no allocation is performed for the inlined vector in either + // case, the `noexcept(...)` specification depends on whether moving the + // underlying objects can throw. It is assumed assumed that... + // a) move constructors should only throw due to allocation failure. + // b) if `value_type`'s move constructor allocates, it uses the same + // allocation function as the inlined vector's allocator. + // Thus, the move constructor is non-throwing if the allocator is non-throwing + // or `value_type`'s move constructor is specified as `noexcept`. + InlinedVector(InlinedVector&& other) noexcept( + absl::allocator_is_nothrow::value || + std::is_nothrow_move_constructible::value) + : storage_(other.storage_.GetAllocator()) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if (other.storage_.GetIsAllocated()) { + storage_.SetAllocation({other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()}); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + } else { + IteratorValueAdapter> other_values( + MoveIterator(other.storage_.GetInlinedData())); + + inlined_vector_internal::ConstructElements( + storage_.GetAllocator(), storage_.GetInlinedData(), other_values, + other.storage_.GetSize()); + + storage_.SetInlinedSize(other.storage_.GetSize()); + } + } + + // Creates an inlined vector by moving in the contents of `other` with a copy + // of `allocator`. + // + // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other` + // contains allocated memory, this move constructor will still allocate. Since + // allocation is performed, this constructor can only be `noexcept` if the + // specified allocator is also `noexcept`. + InlinedVector( + InlinedVector&& other, + const allocator_type& + allocator) noexcept(absl::allocator_is_nothrow::value) + : storage_(allocator) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) && + other.storage_.GetIsAllocated()) { + storage_.SetAllocation({other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()}); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + } else { + storage_.Initialize(IteratorValueAdapter>( + MoveIterator(other.data())), + other.size()); + } + } + + ~InlinedVector() {} + + // --------------------------------------------------------------------------- + // InlinedVector Member Accessors + // --------------------------------------------------------------------------- + + // `InlinedVector::empty()` + // + // Returns whether the inlined vector contains no elements. + bool empty() const noexcept { return !size(); } + + // `InlinedVector::size()` + // + // Returns the number of elements in the inlined vector. + size_type size() const noexcept { return storage_.GetSize(); } + + // `InlinedVector::max_size()` + // + // Returns the maximum number of elements the inlined vector can hold. + size_type max_size() const noexcept { + // One bit of the size storage is used to indicate whether the inlined + // vector contains allocated memory. As a result, the maximum size that the + // inlined vector can express is half of the max for `size_type`. + return (std::numeric_limits::max)() / 2; + } + + // `InlinedVector::capacity()` + // + // Returns the number of elements that could be stored in the inlined vector + // without requiring a reallocation. + // + // NOTE: for most inlined vectors, `capacity()` should be equal to the + // template parameter `N`. For inlined vectors which exceed this capacity, + // they will no longer be inlined and `capacity()` will equal the capactity of + // the allocated memory. + size_type capacity() const noexcept { + return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity() + : storage_.GetInlinedCapacity(); + } + + // `InlinedVector::data()` + // + // Returns a `pointer` to the elements of the inlined vector. This pointer + // can be used to access and modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. + pointer data() noexcept { + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); + } + + // Overload of `InlinedVector::data()` that returns a `const_pointer` to the + // elements of the inlined vector. This pointer can be used to access but not + // modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. + const_pointer data() const noexcept { + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); + } + + // `InlinedVector::operator[](...)` + // + // Returns a `reference` to the `i`th element of the inlined vector. + reference operator[](size_type i) { + ABSL_HARDENING_ASSERT(i < size()); + return data()[i]; + } + + // Overload of `InlinedVector::operator[](...)` that returns a + // `const_reference` to the `i`th element of the inlined vector. + const_reference operator[](size_type i) const { + ABSL_HARDENING_ASSERT(i < size()); + return data()[i]; + } + + // `InlinedVector::at(...)` + // + // Returns a `reference` to the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. + reference at(size_type i) { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "`InlinedVector::at(size_type)` failed bounds check"); + } + return data()[i]; + } + + // Overload of `InlinedVector::at(...)` that returns a `const_reference` to + // the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. + const_reference at(size_type i) const { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "`InlinedVector::at(size_type) const` failed bounds check"); + } + return data()[i]; + } + + // `InlinedVector::front()` + // + // Returns a `reference` to the first element of the inlined vector. + reference front() { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } + + // Overload of `InlinedVector::front()` that returns a `const_reference` to + // the first element of the inlined vector. + const_reference front() const { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } + + // `InlinedVector::back()` + // + // Returns a `reference` to the last element of the inlined vector. + reference back() { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } + + // Overload of `InlinedVector::back()` that returns a `const_reference` to the + // last element of the inlined vector. + const_reference back() const { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } + + // `InlinedVector::begin()` + // + // Returns an `iterator` to the beginning of the inlined vector. + iterator begin() noexcept { return data(); } + + // Overload of `InlinedVector::begin()` that returns a `const_iterator` to + // the beginning of the inlined vector. + const_iterator begin() const noexcept { return data(); } + + // `InlinedVector::end()` + // + // Returns an `iterator` to the end of the inlined vector. + iterator end() noexcept { return data() + size(); } + + // Overload of `InlinedVector::end()` that returns a `const_iterator` to the + // end of the inlined vector. + const_iterator end() const noexcept { return data() + size(); } + + // `InlinedVector::cbegin()` + // + // Returns a `const_iterator` to the beginning of the inlined vector. + const_iterator cbegin() const noexcept { return begin(); } + + // `InlinedVector::cend()` + // + // Returns a `const_iterator` to the end of the inlined vector. + const_iterator cend() const noexcept { return end(); } + + // `InlinedVector::rbegin()` + // + // Returns a `reverse_iterator` from the end of the inlined vector. + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + + // Overload of `InlinedVector::rbegin()` that returns a + // `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + // `InlinedVector::rend()` + // + // Returns a `reverse_iterator` from the beginning of the inlined vector. + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + + // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator` + // from the beginning of the inlined vector. + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + // `InlinedVector::crbegin()` + // + // Returns a `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + + // `InlinedVector::crend()` + // + // Returns a `const_reverse_iterator` from the beginning of the inlined + // vector. + const_reverse_iterator crend() const noexcept { return rend(); } + + // `InlinedVector::get_allocator()` + // + // Returns a copy of the inlined vector's allocator. + allocator_type get_allocator() const { return storage_.GetAllocator(); } + + // --------------------------------------------------------------------------- + // InlinedVector Member Mutators + // --------------------------------------------------------------------------- + + // `InlinedVector::operator=(...)` + // + // Replaces the elements of the inlined vector with copies of the elements of + // `list`. + InlinedVector& operator=(std::initializer_list list) { + assign(list.begin(), list.end()); + + return *this; + } + + // Overload of `InlinedVector::operator=(...)` that replaces the elements of + // the inlined vector with copies of the elements of `other`. + InlinedVector& operator=(const InlinedVector& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + const_pointer other_data = other.data(); + assign(other_data, other_data + other.size()); + } + + return *this; + } + + // Overload of `InlinedVector::operator=(...)` that moves the elements of + // `other` into the inlined vector. + // + // NOTE: as a result of calling this overload, `other` is left in a valid but + // unspecified state. + InlinedVector& operator=(InlinedVector&& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { + inlined_vector_internal::DestroyAdapter::DestroyElements( + storage_.GetAllocator(), data(), size()); + storage_.DeallocateIfAllocated(); + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else { + storage_.Assign(IteratorValueAdapter>( + MoveIterator(other.storage_.GetInlinedData())), + other.size()); + } + } + + return *this; + } + + // `InlinedVector::assign(...)` + // + // Replaces the contents of the inlined vector with `n` copies of `v`. + void assign(size_type n, const_reference v) { + storage_.Assign(CopyValueAdapter(std::addressof(v)), n); + } + + // Overload of `InlinedVector::assign(...)` that replaces the contents of the + // inlined vector with copies of the elements of `list`. + void assign(std::initializer_list list) { + assign(list.begin(), list.end()); + } + + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "forward" category or better. + template = 0> + void assign(ForwardIterator first, ForwardIterator last) { + storage_.Assign(IteratorValueAdapter(first), + static_cast(std::distance(first, last))); + } + + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "input" category. + template = 0> + void assign(InputIterator first, InputIterator last) { + size_type i = 0; + for (; i < size() && first != last; ++i, static_cast(++first)) { + data()[i] = *first; + } + + erase(data() + i, data() + size()); + std::copy(first, last, std::back_inserter(*this)); + } + + // `InlinedVector::resize(...)` + // + // Resizes the inlined vector to contain `n` elements. + // + // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are value-initialized. + void resize(size_type n) { + ABSL_HARDENING_ASSERT(n <= max_size()); + storage_.Resize(DefaultValueAdapter(), n); + } + + // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to + // contain `n` elements. + // + // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are copied-constructed from `v`. + void resize(size_type n, const_reference v) { + ABSL_HARDENING_ASSERT(n <= max_size()); + storage_.Resize(CopyValueAdapter(std::addressof(v)), n); + } + + // `InlinedVector::insert(...)` + // + // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly + // inserted element. + iterator insert(const_iterator pos, const_reference v) { + return emplace(pos, v); + } + + // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using + // move semantics, returning an `iterator` to the newly inserted element. + iterator insert(const_iterator pos, value_type&& v) { + return emplace(pos, std::move(v)); + } + + // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies + // of `v` starting at `pos`, returning an `iterator` pointing to the first of + // the newly inserted elements. + iterator insert(const_iterator pos, size_type n, const_reference v) { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); + + if (ABSL_PREDICT_TRUE(n != 0)) { + value_type dealias = v; + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 + // It appears that GCC thinks that since `pos` is a const pointer and may + // point to uninitialized memory at this point, a warning should be + // issued. But `pos` is actually only used to compute an array index to + // write to. +#if !defined(__clang__) && defined(__GNUC__) +// #pragma GCC diagnostic push +// #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + return storage_.Insert(pos, CopyValueAdapter(std::addressof(dealias)), + n); +#if !defined(__clang__) && defined(__GNUC__) +// #pragma GCC diagnostic pop +#endif + } else { + return const_cast(pos); + } + } + + // Overload of `InlinedVector::insert(...)` that inserts copies of the + // elements of `list` starting at `pos`, returning an `iterator` pointing to + // the first of the newly inserted elements. + iterator insert(const_iterator pos, std::initializer_list list) { + return insert(pos, list.begin(), list.end()); + } + + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. + // + // NOTE: this overload is for iterators that are "forward" category or better. + template = 0> + iterator insert(const_iterator pos, ForwardIterator first, + ForwardIterator last) { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); + + if (ABSL_PREDICT_TRUE(first != last)) { + return storage_.Insert(pos, + IteratorValueAdapter(first), + std::distance(first, last)); + } else { + return const_cast(pos); + } + } + + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. + // + // NOTE: this overload is for iterators that are "input" category. + template = 0> + iterator insert(const_iterator pos, InputIterator first, InputIterator last) { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); + + size_type index = std::distance(cbegin(), pos); + for (size_type i = index; first != last; ++i, static_cast(++first)) { + insert(data() + i, *first); + } + + return iterator(data() + index); + } + + // `InlinedVector::emplace(...)` + // + // Constructs and inserts an element using `args...` in the inlined vector at + // `pos`, returning an `iterator` pointing to the newly emplaced element. + template + iterator emplace(const_iterator pos, Args&&... args) { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); + + value_type dealias(std::forward(args)...); + return storage_.Insert(pos, + IteratorValueAdapter>( + MoveIterator(std::addressof(dealias))), + 1); + } + + // `InlinedVector::emplace_back(...)` + // + // Constructs and inserts an element using `args...` in the inlined vector at + // `end()`, returning a `reference` to the newly emplaced element. + template + reference emplace_back(Args&&... args) { + return storage_.EmplaceBack(std::forward(args)...); + } + + // `InlinedVector::push_back(...)` + // + // Inserts a copy of `v` in the inlined vector at `end()`. + void push_back(const_reference v) { static_cast(emplace_back(v)); } + + // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` + // using move semantics. + void push_back(value_type&& v) { + static_cast(emplace_back(std::move(v))); + } + + // `InlinedVector::pop_back()` + // + // Destroys the element at `back()`, reducing the size by `1`. + void pop_back() noexcept { + ABSL_HARDENING_ASSERT(!empty()); + + AllocatorTraits::destroy(storage_.GetAllocator(), data() + (size() - 1)); + storage_.SubtractSize(1); + } + + // `InlinedVector::erase(...)` + // + // Erases the element at `pos`, returning an `iterator` pointing to where the + // erased element was located. + // + // NOTE: may return `end()`, which is not dereferencable. + iterator erase(const_iterator pos) { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos < end()); + + return storage_.Erase(pos, pos + 1); + } + + // Overload of `InlinedVector::erase(...)` that erases every element in the + // range [`from`, `to`), returning an `iterator` pointing to where the first + // erased element was located. + // + // NOTE: may return `end()`, which is not dereferencable. + iterator erase(const_iterator from, const_iterator to) { + ABSL_HARDENING_ASSERT(from >= begin()); + ABSL_HARDENING_ASSERT(from <= to); + ABSL_HARDENING_ASSERT(to <= end()); + + if (ABSL_PREDICT_TRUE(from != to)) { + return storage_.Erase(from, to); + } else { + return const_cast(from); + } + } + + // `InlinedVector::clear()` + // + // Destroys all elements in the inlined vector, setting the size to `0` and + // deallocating any held memory. + void clear() noexcept { + inlined_vector_internal::DestroyAdapter::DestroyElements( + storage_.GetAllocator(), data(), size()); + storage_.DeallocateIfAllocated(); + + storage_.SetInlinedSize(0); + } + + // `InlinedVector::reserve(...)` + // + // Ensures that there is enough room for at least `n` elements. + void reserve(size_type n) { storage_.Reserve(n); } + + // `InlinedVector::shrink_to_fit()` + // + // Attempts to reduce memory usage by moving elements to (or keeping elements + // in) the smallest available buffer sufficient for containing `size()` + // elements. + // + // If `size()` is sufficiently small, the elements will be moved into (or kept + // in) the inlined space. + void shrink_to_fit() { + if (storage_.GetIsAllocated()) { + storage_.ShrinkToFit(); + } + } + + // `InlinedVector::swap(...)` + // + // Swaps the contents of the inlined vector with `other`. + void swap(InlinedVector& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + storage_.Swap(std::addressof(other.storage_)); + } + } + + private: + template + friend H AbslHashValue(H h, const absl::InlinedVector& a); + + Storage storage_; +}; + +// ----------------------------------------------------------------------------- +// InlinedVector Non-Member Functions +// ----------------------------------------------------------------------------- + +// `swap(...)` +// +// Swaps the contents of two inlined vectors. +template +void swap(absl::InlinedVector& a, + absl::InlinedVector& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +// `operator==(...)` +// +// Tests for value-equality of two inlined vectors. +template +bool operator==(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return absl::equal(a_data, a_data + a.size(), b_data, b_data + b.size()); +} + +// `operator!=(...)` +// +// Tests for value-inequality of two inlined vectors. +template +bool operator!=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(a == b); +} + +// `operator<(...)` +// +// Tests whether the value of an inlined vector is less than the value of +// another inlined vector using a lexicographical comparison algorithm. +template +bool operator<(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return std::lexicographical_compare(a_data, a_data + a.size(), b_data, + b_data + b.size()); +} + +// `operator>(...)` +// +// Tests whether the value of an inlined vector is greater than the value of +// another inlined vector using a lexicographical comparison algorithm. +template +bool operator>(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return b < a; +} + +// `operator<=(...)` +// +// Tests whether the value of an inlined vector is less than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. +template +bool operator<=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(b < a); +} + +// `operator>=(...)` +// +// Tests whether the value of an inlined vector is greater than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. +template +bool operator>=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(a < b); +} + +// `AbslHashValue(...)` +// +// Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to +// call this directly. +template +H AbslHashValue(H h, const absl::InlinedVector& a) { + auto size = a.size(); + return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INLINED_VECTOR_H_ diff --git a/src/absl/container/internal/btree.h b/src/absl/container/internal/btree.h new file mode 100644 index 0000000..01f4e74 --- /dev/null +++ b/src/absl/container/internal/btree.h @@ -0,0 +1,2854 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A btree implementation of the STL set and map interfaces. A btree is smaller +// and generally also faster than STL set/map (refer to the benchmarks below). +// The red-black tree implementation of STL set/map has an overhead of 3 +// pointers (left, right and parent) plus the node color information for each +// stored value. So a set consumes 40 bytes for each value stored in +// 64-bit mode. This btree implementation stores multiple values on fixed +// size nodes (usually 256 bytes) and doesn't store child pointers for leaf +// nodes. The result is that a btree_set may use much less memory per +// stored value. For the random insertion benchmark in btree_bench.cc, a +// btree_set with node-size of 256 uses 5.1 bytes per stored value. +// +// The packing of multiple values on to each node of a btree has another effect +// besides better space utilization: better cache locality due to fewer cache +// lines being accessed. Better cache locality translates into faster +// operations. +// +// CAVEATS +// +// Insertions and deletions on a btree can cause splitting, merging or +// rebalancing of btree nodes. And even without these operations, insertions +// and deletions on a btree will move values around within a node. In both +// cases, the result is that insertions and deletions can invalidate iterators +// pointing to values other than the one being inserted/deleted. Therefore, this +// container does not provide pointer stability. This is notably different from +// STL set/map which takes care to not invalidate iterators on insert/erase +// except, of course, for iterators pointing to the value being erased. A +// partial workaround when erasing is available: erase() returns an iterator +// pointing to the item just after the one that was erased (or end() if none +// exists). + +#ifndef ABSL_CONTAINER_INTERNAL_BTREE_H_ +#define ABSL_CONTAINER_INTERNAL_BTREE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/container/internal/common.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/layout.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/cord.h" +#include "absl/strings/string_view.h" +#include "absl/types/compare.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +#ifdef ABSL_BTREE_ENABLE_GENERATIONS +#error ABSL_BTREE_ENABLE_GENERATIONS cannot be directly set +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER) +// When compiled in sanitizer mode, we add generation integers to the nodes and +// iterators. When iterators are used, we validate that the container has not +// been mutated since the iterator was constructed. +#define ABSL_BTREE_ENABLE_GENERATIONS +#endif + +template +using compare_result_t = absl::result_of_t; + +// A helper class that indicates if the Compare parameter is a key-compare-to +// comparator. +template +using btree_is_key_compare_to = + std::is_convertible, absl::weak_ordering>; + +struct StringBtreeDefaultLess { + using is_transparent = void; + + StringBtreeDefaultLess() = default; + + // Compatibility constructor. + StringBtreeDefaultLess(std::less) {} // NOLINT + StringBtreeDefaultLess(std::less) {} // NOLINT + + // Allow converting to std::less for use in key_comp()/value_comp(). + explicit operator std::less() const { return {}; } + explicit operator std::less() const { return {}; } + explicit operator std::less() const { return {}; } + + absl::weak_ordering operator()(absl::string_view lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(lhs.compare(rhs)); + } + StringBtreeDefaultLess(std::less) {} // NOLINT + absl::weak_ordering operator()(const absl::Cord &lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(lhs.Compare(rhs)); + } + absl::weak_ordering operator()(const absl::Cord &lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(lhs.Compare(rhs)); + } + absl::weak_ordering operator()(absl::string_view lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(-rhs.Compare(lhs)); + } +}; + +struct StringBtreeDefaultGreater { + using is_transparent = void; + + StringBtreeDefaultGreater() = default; + + StringBtreeDefaultGreater(std::greater) {} // NOLINT + StringBtreeDefaultGreater(std::greater) {} // NOLINT + + // Allow converting to std::greater for use in key_comp()/value_comp(). + explicit operator std::greater() const { return {}; } + explicit operator std::greater() const { return {}; } + explicit operator std::greater() const { return {}; } + + absl::weak_ordering operator()(absl::string_view lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(rhs.compare(lhs)); + } + StringBtreeDefaultGreater(std::greater) {} // NOLINT + absl::weak_ordering operator()(const absl::Cord &lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(rhs.Compare(lhs)); + } + absl::weak_ordering operator()(const absl::Cord &lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(-lhs.Compare(rhs)); + } + absl::weak_ordering operator()(absl::string_view lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(rhs.Compare(lhs)); + } +}; + +// See below comments for checked_compare. +template ::value> +struct checked_compare_base : Compare { + using Compare::Compare; + explicit checked_compare_base(Compare c) : Compare(std::move(c)) {} + const Compare &comp() const { return *this; } +}; +template +struct checked_compare_base { + explicit checked_compare_base(Compare c) : compare(std::move(c)) {} + const Compare &comp() const { return compare; } + Compare compare; +}; + +// A mechanism for opting out of checked_compare for use only in btree_test.cc. +struct BtreeTestOnlyCheckedCompareOptOutBase {}; + +// A helper class to adapt the specified comparator for two use cases: +// (1) When using common Abseil string types with common comparison functors, +// convert a boolean comparison into a three-way comparison that returns an +// `absl::weak_ordering`. This helper class is specialized for +// less, greater, less, +// greater, less, and greater. +// (2) Adapt the comparator to diagnose cases of non-strict-weak-ordering (see +// https://en.cppreference.com/w/cpp/named_req/Compare) in debug mode. Whenever +// a comparison is made, we will make assertions to verify that the comparator +// is valid. +template +struct key_compare_adapter { + // Inherit from checked_compare_base to support function pointers and also + // keep empty-base-optimization (EBO) support for classes. + // Note: we can't use CompressedTuple here because that would interfere + // with the EBO for `btree::rightmost_`. `btree::rightmost_` is itself a + // CompressedTuple and nested `CompressedTuple`s don't support EBO. + // TODO(b/214288561): use CompressedTuple instead once it supports EBO for + // nested `CompressedTuple`s. + struct checked_compare : checked_compare_base { + private: + using Base = typename checked_compare::checked_compare_base; + using Base::comp; + + // If possible, returns whether `t` is equivalent to itself. We can only do + // this for `Key`s because we can't be sure that it's safe to call + // `comp()(k, k)` otherwise. Even if SFINAE allows it, there could be a + // compilation failure inside the implementation of the comparison operator. + bool is_self_equivalent(const Key &k) const { + // Note: this works for both boolean and three-way comparators. + return comp()(k, k) == 0; + } + // If we can't compare `t` with itself, returns true unconditionally. + template + bool is_self_equivalent(const T &) const { + return true; + } + + public: + using Base::Base; + checked_compare(Compare comp) : Base(std::move(comp)) {} // NOLINT + + // Allow converting to Compare for use in key_comp()/value_comp(). + explicit operator Compare() const { return comp(); } + + template >::value, + int> = 0> + bool operator()(const T &lhs, const U &rhs) const { + // NOTE: if any of these assertions fail, then the comparator does not + // establish a strict-weak-ordering (see + // https://en.cppreference.com/w/cpp/named_req/Compare). + assert(is_self_equivalent(lhs)); + assert(is_self_equivalent(rhs)); + const bool lhs_comp_rhs = comp()(lhs, rhs); + assert(!lhs_comp_rhs || !comp()(rhs, lhs)); + return lhs_comp_rhs; + } + + template < + typename T, typename U, + absl::enable_if_t, + absl::weak_ordering>::value, + int> = 0> + absl::weak_ordering operator()(const T &lhs, const U &rhs) const { + // NOTE: if any of these assertions fail, then the comparator does not + // establish a strict-weak-ordering (see + // https://en.cppreference.com/w/cpp/named_req/Compare). + assert(is_self_equivalent(lhs)); + assert(is_self_equivalent(rhs)); + const absl::weak_ordering lhs_comp_rhs = comp()(lhs, rhs); +#ifndef NDEBUG + const absl::weak_ordering rhs_comp_lhs = comp()(rhs, lhs); + if (lhs_comp_rhs > 0) { + assert(rhs_comp_lhs < 0 && "lhs_comp_rhs > 0 -> rhs_comp_lhs < 0"); + } else if (lhs_comp_rhs == 0) { + assert(rhs_comp_lhs == 0 && "lhs_comp_rhs == 0 -> rhs_comp_lhs == 0"); + } else { + assert(rhs_comp_lhs > 0 && "lhs_comp_rhs < 0 -> rhs_comp_lhs > 0"); + } +#endif + return lhs_comp_rhs; + } + }; + using type = absl::conditional_t< + std::is_base_of::value, + Compare, checked_compare>; +}; + +template <> +struct key_compare_adapter, std::string> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_adapter, std::string> { + using type = StringBtreeDefaultGreater; +}; + +template <> +struct key_compare_adapter, absl::string_view> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_adapter, absl::string_view> { + using type = StringBtreeDefaultGreater; +}; + +template <> +struct key_compare_adapter, absl::Cord> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_adapter, absl::Cord> { + using type = StringBtreeDefaultGreater; +}; + +// Detects an 'absl_btree_prefer_linear_node_search' member. This is +// a protocol used as an opt-in or opt-out of linear search. +// +// For example, this would be useful for key types that wrap an integer +// and define their own cheap operator<(). For example: +// +// class K { +// public: +// using absl_btree_prefer_linear_node_search = std::true_type; +// ... +// private: +// friend bool operator<(K a, K b) { return a.k_ < b.k_; } +// int k_; +// }; +// +// btree_map m; // Uses linear search +// +// If T has the preference tag, then it has a preference. +// Btree will use the tag's truth value. +template +struct has_linear_node_search_preference : std::false_type {}; +template +struct prefers_linear_node_search : std::false_type {}; +template +struct has_linear_node_search_preference< + T, absl::void_t> + : std::true_type {}; +template +struct prefers_linear_node_search< + T, absl::void_t> + : T::absl_btree_prefer_linear_node_search {}; + +template +constexpr bool compare_has_valid_result_type() { + using compare_result_type = compare_result_t; + return std::is_same::value || + std::is_convertible::value; +} + +template +class map_value_compare { + template + friend class btree; + + // Note: this `protected` is part of the API of std::map::value_compare. See + // https://en.cppreference.com/w/cpp/container/map/value_compare. + protected: + explicit map_value_compare(original_key_compare c) : comp(std::move(c)) {} + + original_key_compare comp; // NOLINT + + public: + auto operator()(const value_type &lhs, const value_type &rhs) const + -> decltype(comp(lhs.first, rhs.first)) { + return comp(lhs.first, rhs.first); + } +}; + +template +struct common_params { + using original_key_compare = Compare; + + // If Compare is a common comparator for a string-like type, then we adapt it + // to use heterogeneous lookup and to be a key-compare-to comparator. + // We also adapt the comparator to diagnose invalid comparators in debug mode. + // We disable this when `Compare` is invalid in a way that will cause + // adaptation to fail (having invalid return type) so that we can give a + // better compilation failure in static_assert_validation. If we don't do + // this, then there will be cascading compilation failures that are confusing + // for users. + using key_compare = + absl::conditional_t(), + Compare, + typename key_compare_adapter::type>; + + static constexpr bool kIsKeyCompareStringAdapted = + std::is_same::value || + std::is_same::value; + static constexpr bool kIsKeyCompareTransparent = + IsTransparent::value || + kIsKeyCompareStringAdapted; + static constexpr bool kEnableGenerations = +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + true; +#else + false; +#endif + + // A type which indicates if we have a key-compare-to functor or a plain old + // key-compare functor. + using is_key_compare_to = btree_is_key_compare_to; + + using allocator_type = Alloc; + using key_type = Key; + using size_type = size_t; + using difference_type = ptrdiff_t; + + using slot_policy = SlotPolicy; + using slot_type = typename slot_policy::slot_type; + using value_type = typename slot_policy::value_type; + using init_type = typename slot_policy::mutable_value_type; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + + using value_compare = + absl::conditional_t, + original_key_compare>; + using is_map_container = std::integral_constant; + + // For the given lookup key type, returns whether we can have multiple + // equivalent keys in the btree. If this is a multi-container, then we can. + // Otherwise, we can have multiple equivalent keys only if all of the + // following conditions are met: + // - The comparator is transparent. + // - The lookup key type is not the same as key_type. + // - The comparator is not a StringBtreeDefault{Less,Greater} comparator + // that we know has the same equivalence classes for all lookup types. + template + constexpr static bool can_have_multiple_equivalent_keys() { + return IsMulti || (IsTransparent::value && + !std::is_same::value && + !kIsKeyCompareStringAdapted); + } + + enum { + kTargetNodeSize = TargetNodeSize, + + // Upper bound for the available space for slots. This is largest for leaf + // nodes, which have overhead of at least a pointer + 4 bytes (for storing + // 3 field_types and an enum). + kNodeSlotSpace = + TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4), + }; + + // This is an integral type large enough to hold as many slots as will fit a + // node of TargetNodeSize bytes. + using node_count_type = + absl::conditional_t<(kNodeSlotSpace / sizeof(slot_type) > + (std::numeric_limits::max)()), + uint16_t, uint8_t>; // NOLINT + + // The following methods are necessary for passing this struct as PolicyTraits + // for node_handle and/or are used within btree. + static value_type &element(slot_type *slot) { + return slot_policy::element(slot); + } + static const value_type &element(const slot_type *slot) { + return slot_policy::element(slot); + } + template + static void construct(Alloc *alloc, slot_type *slot, Args &&... args) { + slot_policy::construct(alloc, slot, std::forward(args)...); + } + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + slot_policy::construct(alloc, slot, other); + } + static void destroy(Alloc *alloc, slot_type *slot) { + slot_policy::destroy(alloc, slot); + } + static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) { + slot_policy::transfer(alloc, new_slot, old_slot); + } +}; + +// An adapter class that converts a lower-bound compare into an upper-bound +// compare. Note: there is no need to make a version of this adapter specialized +// for key-compare-to functors because the upper-bound (the first value greater +// than the input) is never an exact match. +template +struct upper_bound_adapter { + explicit upper_bound_adapter(const Compare &c) : comp(c) {} + template + bool operator()(const K1 &a, const K2 &b) const { + // Returns true when a is not greater than b. + return !compare_internal::compare_result_as_less_than(comp(b, a)); + } + + private: + Compare comp; +}; + +enum class MatchKind : uint8_t { kEq, kNe }; + +template +struct SearchResult { + V value; + MatchKind match; + + static constexpr bool HasMatch() { return true; } + bool IsEq() const { return match == MatchKind::kEq; } +}; + +// When we don't use CompareTo, `match` is not present. +// This ensures that callers can't use it accidentally when it provides no +// useful information. +template +struct SearchResult { + SearchResult() {} + explicit SearchResult(V v) : value(v) {} + SearchResult(V v, MatchKind /*match*/) : value(v) {} + + V value; + + static constexpr bool HasMatch() { return false; } + static constexpr bool IsEq() { return false; } +}; + +// A node in the btree holding. The same node type is used for both internal +// and leaf nodes in the btree, though the nodes are allocated in such a way +// that the children array is only valid in internal nodes. +template +class btree_node { + using is_key_compare_to = typename Params::is_key_compare_to; + using field_type = typename Params::node_count_type; + using allocator_type = typename Params::allocator_type; + using slot_type = typename Params::slot_type; + using original_key_compare = typename Params::original_key_compare; + + public: + using params_type = Params; + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using key_compare = typename Params::key_compare; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + + // Btree decides whether to use linear node search as follows: + // - If the comparator expresses a preference, use that. + // - If the key expresses a preference, use that. + // - If the key is arithmetic and the comparator is std::less or + // std::greater, choose linear. + // - Otherwise, choose binary. + // TODO(ezb): Might make sense to add condition(s) based on node-size. + using use_linear_search = std::integral_constant< + bool, has_linear_node_search_preference::value + ? prefers_linear_node_search::value + : has_linear_node_search_preference::value + ? prefers_linear_node_search::value + : std::is_arithmetic::value && + (std::is_same, + original_key_compare>::value || + std::is_same, + original_key_compare>::value)>; + + // This class is organized by absl::container_internal::Layout as if it had + // the following structure: + // // A pointer to the node's parent. + // btree_node *parent; + // + // // When ABSL_BTREE_ENABLE_GENERATIONS is defined, we also have a + // // generation integer in order to check that when iterators are + // // used, they haven't been invalidated already. Only the generation on + // // the root is used, but we have one on each node because whether a node + // // is root or not can change. + // uint32_t generation; + // + // // The position of the node in the node's parent. + // field_type position; + // // The index of the first populated value in `values`. + // // TODO(ezb): right now, `start` is always 0. Update insertion/merge + // // logic to allow for floating storage within nodes. + // field_type start; + // // The index after the last populated value in `values`. Currently, this + // // is the same as the count of values. + // field_type finish; + // // The maximum number of values the node can hold. This is an integer in + // // [1, kNodeSlots] for root leaf nodes, kNodeSlots for non-root leaf + // // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal + // // nodes (even though there are still kNodeSlots values in the node). + // // TODO(ezb): make max_count use only 4 bits and record log2(capacity) + // // to free extra bits for is_root, etc. + // field_type max_count; + // + // // The array of values. The capacity is `max_count` for leaf nodes and + // // kNodeSlots for internal nodes. Only the values in + // // [start, finish) have been initialized and are valid. + // slot_type values[max_count]; + // + // // The array of child pointers. The keys in children[i] are all less + // // than key(i). The keys in children[i + 1] are all greater than key(i). + // // There are 0 children for leaf nodes and kNodeSlots + 1 children for + // // internal nodes. + // btree_node *children[kNodeSlots + 1]; + // + // This class is only constructed by EmptyNodeType. Normally, pointers to the + // layout above are allocated, cast to btree_node*, and de-allocated within + // the btree implementation. + ~btree_node() = default; + btree_node(btree_node const &) = delete; + btree_node &operator=(btree_node const &) = delete; + + // Public for EmptyNodeType. + constexpr static size_type Alignment() { + static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(), + "Alignment of all nodes must be equal."); + return InternalLayout().Alignment(); + } + + protected: + btree_node() = default; + + private: + using layout_type = + absl::container_internal::Layout; + constexpr static size_type SizeWithNSlots(size_type n) { + return layout_type( + /*parent*/ 1, + /*generation*/ params_type::kEnableGenerations ? 1 : 0, + /*position, start, finish, max_count*/ 4, + /*slots*/ n, + /*children*/ 0) + .AllocSize(); + } + // A lower bound for the overhead of fields other than slots in a leaf node. + constexpr static size_type MinimumOverhead() { + return SizeWithNSlots(1) - sizeof(slot_type); + } + + // Compute how many values we can fit onto a leaf node taking into account + // padding. + constexpr static size_type NodeTargetSlots(const size_type begin, + const size_type end) { + return begin == end ? begin + : SizeWithNSlots((begin + end) / 2 + 1) > + params_type::kTargetNodeSize + ? NodeTargetSlots(begin, (begin + end) / 2) + : NodeTargetSlots((begin + end) / 2 + 1, end); + } + + enum { + kTargetNodeSize = params_type::kTargetNodeSize, + kNodeTargetSlots = NodeTargetSlots(0, params_type::kTargetNodeSize), + + // We need a minimum of 3 slots per internal node in order to perform + // splitting (1 value for the two nodes involved in the split and 1 value + // propagated to the parent as the delimiter for the split). For performance + // reasons, we don't allow 3 slots-per-node due to bad worst case occupancy + // of 1/3 (for a node, not a b-tree). + kMinNodeSlots = 4, + + kNodeSlots = + kNodeTargetSlots >= kMinNodeSlots ? kNodeTargetSlots : kMinNodeSlots, + + // The node is internal (i.e. is not a leaf node) if and only if `max_count` + // has this value. + kInternalNodeMaxCount = 0, + }; + + // Leaves can have less than kNodeSlots values. + constexpr static layout_type LeafLayout(const int slot_count = kNodeSlots) { + return layout_type( + /*parent*/ 1, + /*generation*/ params_type::kEnableGenerations ? 1 : 0, + /*position, start, finish, max_count*/ 4, + /*slots*/ slot_count, + /*children*/ 0); + } + constexpr static layout_type InternalLayout() { + return layout_type( + /*parent*/ 1, + /*generation*/ params_type::kEnableGenerations ? 1 : 0, + /*position, start, finish, max_count*/ 4, + /*slots*/ kNodeSlots, + /*children*/ kNodeSlots + 1); + } + constexpr static size_type LeafSize(const int slot_count = kNodeSlots) { + return LeafLayout(slot_count).AllocSize(); + } + constexpr static size_type InternalSize() { + return InternalLayout().AllocSize(); + } + + // N is the index of the type in the Layout definition. + // ElementType is the Nth type in the Layout definition. + template + inline typename layout_type::template ElementType *GetField() { + // We assert that we don't read from values that aren't there. + assert(N < 4 || is_internal()); + return InternalLayout().template Pointer(reinterpret_cast(this)); + } + template + inline const typename layout_type::template ElementType *GetField() const { + assert(N < 4 || is_internal()); + return InternalLayout().template Pointer( + reinterpret_cast(this)); + } + void set_parent(btree_node *p) { *GetField<0>() = p; } + field_type &mutable_finish() { return GetField<2>()[2]; } + slot_type *slot(int i) { return &GetField<3>()[i]; } + slot_type *start_slot() { return slot(start()); } + slot_type *finish_slot() { return slot(finish()); } + const slot_type *slot(int i) const { return &GetField<3>()[i]; } + void set_position(field_type v) { GetField<2>()[0] = v; } + void set_start(field_type v) { GetField<2>()[1] = v; } + void set_finish(field_type v) { GetField<2>()[2] = v; } + // This method is only called by the node init methods. + void set_max_count(field_type v) { GetField<2>()[3] = v; } + + public: + // Whether this is a leaf node or not. This value doesn't change after the + // node is created. + bool is_leaf() const { return GetField<2>()[3] != kInternalNodeMaxCount; } + // Whether this is an internal node or not. This value doesn't change after + // the node is created. + bool is_internal() const { return !is_leaf(); } + + // Getter for the position of this node in its parent. + field_type position() const { return GetField<2>()[0]; } + + // Getter for the offset of the first value in the `values` array. + field_type start() const { + // TODO(ezb): when floating storage is implemented, return GetField<2>()[1]; + assert(GetField<2>()[1] == 0); + return 0; + } + + // Getter for the offset after the last value in the `values` array. + field_type finish() const { return GetField<2>()[2]; } + + // Getters for the number of values stored in this node. + field_type count() const { + assert(finish() >= start()); + return finish() - start(); + } + field_type max_count() const { + // Internal nodes have max_count==kInternalNodeMaxCount. + // Leaf nodes have max_count in [1, kNodeSlots]. + const field_type max_count = GetField<2>()[3]; + return max_count == field_type{kInternalNodeMaxCount} + ? field_type{kNodeSlots} + : max_count; + } + + // Getter for the parent of this node. + btree_node *parent() const { return *GetField<0>(); } + // Getter for whether the node is the root of the tree. The parent of the + // root of the tree is the leftmost node in the tree which is guaranteed to + // be a leaf. + bool is_root() const { return parent()->is_leaf(); } + void make_root() { + assert(parent()->is_root()); + set_generation(parent()->generation()); + set_parent(parent()->parent()); + } + + // Gets the root node's generation integer, which is the one used by the tree. + uint32_t *get_root_generation() const { + assert(params_type::kEnableGenerations); + const btree_node *curr = this; + for (; !curr->is_root(); curr = curr->parent()) continue; + return const_cast(&curr->GetField<1>()[0]); + } + + // Returns the generation for iterator validation. + uint32_t generation() const { + return params_type::kEnableGenerations ? *get_root_generation() : 0; + } + // Updates generation. Should only be called on a root node or during node + // initialization. + void set_generation(uint32_t generation) { + if (params_type::kEnableGenerations) GetField<1>()[0] = generation; + } + // Updates the generation. We do this whenever the node is mutated. + void next_generation() { + if (params_type::kEnableGenerations) ++*get_root_generation(); + } + + // Getters for the key/value at position i in the node. + const key_type &key(int i) const { return params_type::key(slot(i)); } + reference value(int i) { return params_type::element(slot(i)); } + const_reference value(int i) const { return params_type::element(slot(i)); } + + // Getters/setter for the child at position i in the node. + btree_node *child(int i) const { return GetField<4>()[i]; } + btree_node *start_child() const { return child(start()); } + btree_node *&mutable_child(int i) { return GetField<4>()[i]; } + void clear_child(int i) { + absl::container_internal::SanitizerPoisonObject(&mutable_child(i)); + } + void set_child(int i, btree_node *c) { + absl::container_internal::SanitizerUnpoisonObject(&mutable_child(i)); + mutable_child(i) = c; + c->set_position(i); + } + void init_child(int i, btree_node *c) { + set_child(i, c); + c->set_parent(this); + } + + // Returns the position of the first value whose key is not less than k. + template + SearchResult lower_bound( + const K &k, const key_compare &comp) const { + return use_linear_search::value ? linear_search(k, comp) + : binary_search(k, comp); + } + // Returns the position of the first value whose key is greater than k. + template + int upper_bound(const K &k, const key_compare &comp) const { + auto upper_compare = upper_bound_adapter(comp); + return use_linear_search::value ? linear_search(k, upper_compare).value + : binary_search(k, upper_compare).value; + } + + template + SearchResult::value> + linear_search(const K &k, const Compare &comp) const { + return linear_search_impl(k, start(), finish(), comp, + btree_is_key_compare_to()); + } + + template + SearchResult::value> + binary_search(const K &k, const Compare &comp) const { + return binary_search_impl(k, start(), finish(), comp, + btree_is_key_compare_to()); + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using plain compare. + template + SearchResult linear_search_impl( + const K &k, int s, const int e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s < e) { + if (!comp(key(s), k)) { + break; + } + ++s; + } + return SearchResult{s}; + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using compare-to. + template + SearchResult linear_search_impl( + const K &k, int s, const int e, const Compare &comp, + std::true_type /* IsCompareTo */) const { + while (s < e) { + const absl::weak_ordering c = comp(key(s), k); + if (c == 0) { + return {s, MatchKind::kEq}; + } else if (c > 0) { + break; + } + ++s; + } + return {s, MatchKind::kNe}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using plain compare. + template + SearchResult binary_search_impl( + const K &k, int s, int e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s != e) { + const int mid = (s + e) >> 1; + if (comp(key(mid), k)) { + s = mid + 1; + } else { + e = mid; + } + } + return SearchResult{s}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using compare-to. + template + SearchResult binary_search_impl( + const K &k, int s, int e, const CompareTo &comp, + std::true_type /* IsCompareTo */) const { + if (params_type::template can_have_multiple_equivalent_keys()) { + MatchKind exact_match = MatchKind::kNe; + while (s != e) { + const int mid = (s + e) >> 1; + const absl::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else { + e = mid; + if (c == 0) { + // Need to return the first value whose key is not less than k, + // which requires continuing the binary search if there could be + // multiple equivalent keys. + exact_match = MatchKind::kEq; + } + } + } + return {s, exact_match}; + } else { // Can't have multiple equivalent keys. + while (s != e) { + const int mid = (s + e) >> 1; + const absl::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else if (c > 0) { + e = mid; + } else { + return {mid, MatchKind::kEq}; + } + } + return {s, MatchKind::kNe}; + } + } + + // Emplaces a value at position i, shifting all existing values and + // children at positions >= i to the right by 1. + template + void emplace_value(size_type i, allocator_type *alloc, Args &&... args); + + // Removes the values at positions [i, i + to_erase), shifting all existing + // values and children after that range to the left by to_erase. Clears all + // children between [i, i + to_erase). + void remove_values(field_type i, field_type to_erase, allocator_type *alloc); + + // Rebalances a node with its right sibling. + void rebalance_right_to_left(int to_move, btree_node *right, + allocator_type *alloc); + void rebalance_left_to_right(int to_move, btree_node *right, + allocator_type *alloc); + + // Splits a node, moving a portion of the node's values to its right sibling. + void split(int insert_position, btree_node *dest, allocator_type *alloc); + + // Merges a node with its right sibling, moving all of the values and the + // delimiting key in the parent node onto itself, and deleting the src node. + void merge(btree_node *src, allocator_type *alloc); + + // Node allocation/deletion routines. + void init_leaf(int max_count, btree_node *parent) { + set_generation(0); + set_parent(parent); + set_position(0); + set_start(0); + set_finish(0); + set_max_count(max_count); + absl::container_internal::SanitizerPoisonMemoryRegion( + start_slot(), max_count * sizeof(slot_type)); + } + void init_internal(btree_node *parent) { + init_leaf(kNodeSlots, parent); + // Set `max_count` to a sentinel value to indicate that this node is + // internal. + set_max_count(kInternalNodeMaxCount); + absl::container_internal::SanitizerPoisonMemoryRegion( + &mutable_child(start()), (kNodeSlots + 1) * sizeof(btree_node *)); + } + + static void deallocate(const size_type size, btree_node *node, + allocator_type *alloc) { + absl::container_internal::Deallocate(alloc, node, size); + } + + // Deletes a node and all of its children. + static void clear_and_delete(btree_node *node, allocator_type *alloc); + + private: + template + void value_init(const field_type i, allocator_type *alloc, Args &&... args) { + next_generation(); + absl::container_internal::SanitizerUnpoisonObject(slot(i)); + params_type::construct(alloc, slot(i), std::forward(args)...); + } + void value_destroy(const field_type i, allocator_type *alloc) { + next_generation(); + params_type::destroy(alloc, slot(i)); + absl::container_internal::SanitizerPoisonObject(slot(i)); + } + void value_destroy_n(const field_type i, const field_type n, + allocator_type *alloc) { + next_generation(); + for (slot_type *s = slot(i), *end = slot(i + n); s != end; ++s) { + params_type::destroy(alloc, s); + absl::container_internal::SanitizerPoisonObject(s); + } + } + + static void transfer(slot_type *dest, slot_type *src, allocator_type *alloc) { + absl::container_internal::SanitizerUnpoisonObject(dest); + params_type::transfer(alloc, dest, src); + absl::container_internal::SanitizerPoisonObject(src); + } + + // Transfers value from slot `src_i` in `src_node` to slot `dest_i` in `this`. + void transfer(const size_type dest_i, const size_type src_i, + btree_node *src_node, allocator_type *alloc) { + next_generation(); + transfer(slot(dest_i), src_node->slot(src_i), alloc); + } + + // Transfers `n` values starting at value `src_i` in `src_node` into the + // values starting at value `dest_i` in `this`. + void transfer_n(const size_type n, const size_type dest_i, + const size_type src_i, btree_node *src_node, + allocator_type *alloc) { + next_generation(); + for (slot_type *src = src_node->slot(src_i), *end = src + n, + *dest = slot(dest_i); + src != end; ++src, ++dest) { + transfer(dest, src, alloc); + } + } + + // Same as above, except that we start at the end and work our way to the + // beginning. + void transfer_n_backward(const size_type n, const size_type dest_i, + const size_type src_i, btree_node *src_node, + allocator_type *alloc) { + next_generation(); + for (slot_type *src = src_node->slot(src_i + n - 1), *end = src - n, + *dest = slot(dest_i + n - 1); + src != end; --src, --dest) { + transfer(dest, src, alloc); + } + } + + template + friend class btree; + template + friend class btree_iterator; + friend class BtreeNodePeer; + friend struct btree_access; +}; + +template +class btree_iterator { + using key_type = typename Node::key_type; + using size_type = typename Node::size_type; + using params_type = typename Node::params_type; + using is_map_container = typename params_type::is_map_container; + + using node_type = Node; + using normal_node = typename std::remove_const::type; + using const_node = const Node; + using normal_pointer = typename params_type::pointer; + using normal_reference = typename params_type::reference; + using const_pointer = typename params_type::const_pointer; + using const_reference = typename params_type::const_reference; + using slot_type = typename params_type::slot_type; + + using iterator = + btree_iterator; + using const_iterator = + btree_iterator; + + public: + // These aliases are public for std::iterator_traits. + using difference_type = typename Node::difference_type; + using value_type = typename params_type::value_type; + using pointer = Pointer; + using reference = Reference; + using iterator_category = std::bidirectional_iterator_tag; + + btree_iterator() : btree_iterator(nullptr, -1) {} + explicit btree_iterator(Node *n) : btree_iterator(n, n->start()) {} + btree_iterator(Node *n, int p) : node_(n), position_(p) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + // Use `~uint32_t{}` as a sentinel value for iterator generations so it + // doesn't match the initial value for the actual generation. + generation_ = n != nullptr ? n->generation() : ~uint32_t{}; +#endif + } + + // NOTE: this SFINAE allows for implicit conversions from iterator to + // const_iterator, but it specifically avoids hiding the copy constructor so + // that the trivial one will be used when possible. + template , iterator>::value && + std::is_same::value, + int> = 0> + btree_iterator(const btree_iterator other) // NOLINT + : node_(other.node_), position_(other.position_) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + generation_ = other.generation_; +#endif + } + + bool operator==(const iterator &other) const { + return node_ == other.node_ && position_ == other.position_; + } + bool operator==(const const_iterator &other) const { + return node_ == other.node_ && position_ == other.position_; + } + bool operator!=(const iterator &other) const { + return node_ != other.node_ || position_ != other.position_; + } + bool operator!=(const const_iterator &other) const { + return node_ != other.node_ || position_ != other.position_; + } + + // Accessors for the key/value the iterator is pointing at. + reference operator*() const { + ABSL_HARDENING_ASSERT(node_ != nullptr); + ABSL_HARDENING_ASSERT(node_->start() <= position_); + ABSL_HARDENING_ASSERT(node_->finish() > position_); + assert_valid_generation(); + return node_->value(position_); + } + pointer operator->() const { return &operator*(); } + + btree_iterator &operator++() { + increment(); + return *this; + } + btree_iterator &operator--() { + decrement(); + return *this; + } + btree_iterator operator++(int) { + btree_iterator tmp = *this; + ++*this; + return tmp; + } + btree_iterator operator--(int) { + btree_iterator tmp = *this; + --*this; + return tmp; + } + + private: + friend iterator; + friend const_iterator; + template + friend class btree; + template + friend class btree_container; + template + friend class btree_set_container; + template + friend class btree_map_container; + template + friend class btree_multiset_container; + template + friend class base_checker; + friend struct btree_access; + + // This SFINAE allows explicit conversions from const_iterator to + // iterator, but also avoids hiding the copy constructor. + // NOTE: the const_cast is safe because this constructor is only called by + // non-const methods and the container owns the nodes. + template , const_iterator>::value && + std::is_same::value, + int> = 0> + explicit btree_iterator(const btree_iterator other) + : node_(const_cast(other.node_)), + position_(other.position_) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + generation_ = other.generation_; +#endif + } + + // Increment/decrement the iterator. + void increment() { + assert_valid_generation(); + if (node_->is_leaf() && ++position_ < node_->finish()) { + return; + } + increment_slow(); + } + void increment_slow(); + + void decrement() { + assert_valid_generation(); + if (node_->is_leaf() && --position_ >= node_->start()) { + return; + } + decrement_slow(); + } + void decrement_slow(); + + // Updates the generation. For use internally right before we return an + // iterator to the user. + void update_generation() { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + if (node_ != nullptr) generation_ = node_->generation(); +#endif + } + + const key_type &key() const { return node_->key(position_); } + decltype(std::declval()->slot(0)) slot() { + return node_->slot(position_); + } + + void assert_valid_generation() const { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + if (node_ != nullptr && node_->generation() != generation_) { + ABSL_INTERNAL_LOG( + FATAL, + "Attempting to use an invalidated iterator. The corresponding b-tree " + "container has been mutated since this iterator was constructed."); + } +#endif + } + + // The node in the tree the iterator is pointing at. + Node *node_; + // The position within the node of the tree the iterator is pointing at. + // NOTE: this is an int rather than a field_type because iterators can point + // to invalid positions (such as -1) in certain circumstances. + int position_; +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + // Used to check that the iterator hasn't been invalidated. + uint32_t generation_; +#endif +}; + +template +class btree { + using node_type = btree_node; + using is_key_compare_to = typename Params::is_key_compare_to; + using field_type = typename node_type::field_type; + + // We use a static empty node for the root/leftmost/rightmost of empty btrees + // in order to avoid branching in begin()/end(). + struct alignas(node_type::Alignment()) EmptyNodeType : node_type { + using field_type = typename node_type::field_type; + node_type *parent; +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + uint32_t generation = 0; +#endif + field_type position = 0; + field_type start = 0; + field_type finish = 0; + // max_count must be != kInternalNodeMaxCount (so that this node is regarded + // as a leaf node). max_count() is never called when the tree is empty. + field_type max_count = node_type::kInternalNodeMaxCount + 1; + +#ifdef _MSC_VER + // MSVC has constexpr code generations bugs here. + EmptyNodeType() : parent(this) {} +#else + constexpr EmptyNodeType(node_type *p) : parent(p) {} +#endif + }; + + static node_type *EmptyNode() { +#ifdef _MSC_VER + static EmptyNodeType *empty_node = new EmptyNodeType; + // This assert fails on some other construction methods. + assert(empty_node->parent == empty_node); + return empty_node; +#else + static constexpr EmptyNodeType empty_node( + const_cast(&empty_node)); + return const_cast(&empty_node); +#endif + } + + enum : uint32_t { + kNodeSlots = node_type::kNodeSlots, + kMinNodeValues = kNodeSlots / 2, + }; + + struct node_stats { + using size_type = typename Params::size_type; + + node_stats(size_type l, size_type i) : leaf_nodes(l), internal_nodes(i) {} + + node_stats &operator+=(const node_stats &other) { + leaf_nodes += other.leaf_nodes; + internal_nodes += other.internal_nodes; + return *this; + } + + size_type leaf_nodes; + size_type internal_nodes; + }; + + public: + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + using key_compare = typename Params::key_compare; + using original_key_compare = typename Params::original_key_compare; + using value_compare = typename Params::value_compare; + using allocator_type = typename Params::allocator_type; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using iterator = + typename btree_iterator::iterator; + using const_iterator = typename iterator::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using node_handle_type = node_handle; + + // Internal types made public for use by btree_container types. + using params_type = Params; + using slot_type = typename Params::slot_type; + + private: + // Copies or moves (depending on the template parameter) the values in + // other into this btree in their order in other. This btree must be empty + // before this method is called. This method is used in copy construction, + // copy assignment, and move assignment. + template + void copy_or_move_values_in_order(Btree &other); + + // Validates that various assumptions/requirements are true at compile time. + constexpr static bool static_assert_validation(); + + public: + btree(const key_compare &comp, const allocator_type &alloc) + : root_(EmptyNode()), rightmost_(comp, alloc, EmptyNode()), size_(0) {} + + btree(const btree &other) : btree(other, other.allocator()) {} + btree(const btree &other, const allocator_type &alloc) + : btree(other.key_comp(), alloc) { + copy_or_move_values_in_order(other); + } + btree(btree &&other) noexcept + : root_(absl::exchange(other.root_, EmptyNode())), + rightmost_(std::move(other.rightmost_)), + size_(absl::exchange(other.size_, 0)) { + other.mutable_rightmost() = EmptyNode(); + } + btree(btree &&other, const allocator_type &alloc) + : btree(other.key_comp(), alloc) { + if (alloc == other.allocator()) { + swap(other); + } else { + // Move values from `other` one at a time when allocators are different. + copy_or_move_values_in_order(other); + } + } + + ~btree() { + // Put static_asserts in destructor to avoid triggering them before the type + // is complete. + static_assert(static_assert_validation(), "This call must be elided."); + clear(); + } + + // Assign the contents of other to *this. + btree &operator=(const btree &other); + btree &operator=(btree &&other) noexcept; + + iterator begin() { return iterator(leftmost()); } + const_iterator begin() const { return const_iterator(leftmost()); } + iterator end() { return iterator(rightmost(), rightmost()->finish()); } + const_iterator end() const { + return const_iterator(rightmost(), rightmost()->finish()); + } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // Finds the first element whose key is not less than `key`. + template + iterator lower_bound(const K &key) { + return internal_end(internal_lower_bound(key).value); + } + template + const_iterator lower_bound(const K &key) const { + return internal_end(internal_lower_bound(key).value); + } + + // Finds the first element whose key is not less than `key` and also returns + // whether that element is equal to `key`. + template + std::pair lower_bound_equal(const K &key) const; + + // Finds the first element whose key is greater than `key`. + template + iterator upper_bound(const K &key) { + return internal_end(internal_upper_bound(key)); + } + template + const_iterator upper_bound(const K &key) const { + return internal_end(internal_upper_bound(key)); + } + + // Finds the range of values which compare equal to key. The first member of + // the returned pair is equal to lower_bound(key). The second member of the + // pair is equal to upper_bound(key). + template + std::pair equal_range(const K &key); + template + std::pair equal_range(const K &key) const { + return const_cast(this)->equal_range(key); + } + + // Inserts a value into the btree only if it does not already exist. The + // boolean return value indicates whether insertion succeeded or failed. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template + std::pair insert_unique(const K &key, Args &&... args); + + // Inserts with hint. Checks to see if the value should be placed immediately + // before `position` in the tree. If so, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_unique() were made. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template + std::pair insert_hint_unique(iterator position, + const K &key, + Args &&... args); + + // Insert a range of values into the btree. + // Note: the first overload avoids constructing a value_type if the key + // already exists in the btree. + template ()( + params_type::key(*std::declval()), + std::declval()))> + void insert_iterator_unique(InputIterator b, InputIterator e, int); + // We need the second overload for cases in which we need to construct a + // value_type in order to compare it with the keys already in the btree. + template + void insert_iterator_unique(InputIterator b, InputIterator e, char); + + // Inserts a value into the btree. + template + iterator insert_multi(const key_type &key, ValueType &&v); + + // Inserts a value into the btree. + template + iterator insert_multi(ValueType &&v) { + return insert_multi(params_type::key(v), std::forward(v)); + } + + // Insert with hint. Check to see if the value should be placed immediately + // before position in the tree. If it does, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_multi(v) were made. + template + iterator insert_hint_multi(iterator position, ValueType &&v); + + // Insert a range of values into the btree. + template + void insert_iterator_multi(InputIterator b, InputIterator e); + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + // Requirement: does not read the value at `*iter`. + iterator erase(iterator iter); + + // Erases range. Returns the number of keys erased and an iterator pointing + // to the element after the last erased element. + std::pair erase_range(iterator begin, iterator end); + + // Finds an element with key equivalent to `key` or returns `end()` if `key` + // is not present. + template + iterator find(const K &key) { + return internal_end(internal_find(key)); + } + template + const_iterator find(const K &key) const { + return internal_end(internal_find(key)); + } + + // Clear the btree, deleting all of the values it contains. + void clear(); + + // Swaps the contents of `this` and `other`. + void swap(btree &other); + + const key_compare &key_comp() const noexcept { + return rightmost_.template get<0>(); + } + template + bool compare_keys(const K1 &a, const K2 &b) const { + return compare_internal::compare_result_as_less_than(key_comp()(a, b)); + } + + value_compare value_comp() const { + return value_compare(original_key_compare(key_comp())); + } + + // Verifies the structure of the btree. + void verify() const; + + // Size routines. + size_type size() const { return size_; } + size_type max_size() const { return (std::numeric_limits::max)(); } + bool empty() const { return size_ == 0; } + + // The height of the btree. An empty tree will have height 0. + size_type height() const { + size_type h = 0; + if (!empty()) { + // Count the length of the chain from the leftmost node up to the + // root. We actually count from the root back around to the level below + // the root, but the calculation is the same because of the circularity + // of that traversal. + const node_type *n = root(); + do { + ++h; + n = n->parent(); + } while (n != root()); + } + return h; + } + + // The number of internal, leaf and total nodes used by the btree. + size_type leaf_nodes() const { return internal_stats(root()).leaf_nodes; } + size_type internal_nodes() const { + return internal_stats(root()).internal_nodes; + } + size_type nodes() const { + node_stats stats = internal_stats(root()); + return stats.leaf_nodes + stats.internal_nodes; + } + + // The total number of bytes used by the btree. + // TODO(b/169338300): update to support node_btree_*. + size_type bytes_used() const { + node_stats stats = internal_stats(root()); + if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { + return sizeof(*this) + node_type::LeafSize(root()->max_count()); + } else { + return sizeof(*this) + stats.leaf_nodes * node_type::LeafSize() + + stats.internal_nodes * node_type::InternalSize(); + } + } + + // The average number of bytes used per value stored in the btree assuming + // random insertion order. + static double average_bytes_per_value() { + // The expected number of values per node with random insertion order is the + // average of the maximum and minimum numbers of values per node. + const double expected_values_per_node = + (kNodeSlots + kMinNodeValues) / 2.0; + return node_type::LeafSize() / expected_values_per_node; + } + + // The fullness of the btree. Computed as the number of elements in the btree + // divided by the maximum number of elements a tree with the current number + // of nodes could hold. A value of 1 indicates perfect space + // utilization. Smaller values indicate space wastage. + // Returns 0 for empty trees. + double fullness() const { + if (empty()) return 0.0; + return static_cast(size()) / (nodes() * kNodeSlots); + } + // The overhead of the btree structure in bytes per node. Computed as the + // total number of bytes used by the btree minus the number of bytes used for + // storing elements divided by the number of elements. + // Returns 0 for empty trees. + double overhead() const { + if (empty()) return 0.0; + return (bytes_used() - size() * sizeof(value_type)) / + static_cast(size()); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { return allocator(); } + + private: + friend struct btree_access; + + // Internal accessor routines. + node_type *root() { return root_; } + const node_type *root() const { return root_; } + node_type *&mutable_root() noexcept { return root_; } + node_type *rightmost() { return rightmost_.template get<2>(); } + const node_type *rightmost() const { return rightmost_.template get<2>(); } + node_type *&mutable_rightmost() noexcept { + return rightmost_.template get<2>(); + } + key_compare *mutable_key_comp() noexcept { + return &rightmost_.template get<0>(); + } + + // The leftmost node is stored as the parent of the root node. + node_type *leftmost() { return root()->parent(); } + const node_type *leftmost() const { return root()->parent(); } + + // Allocator routines. + allocator_type *mutable_allocator() noexcept { + return &rightmost_.template get<1>(); + } + const allocator_type &allocator() const noexcept { + return rightmost_.template get<1>(); + } + + // Allocates a correctly aligned node of at least size bytes using the + // allocator. + node_type *allocate(const size_type size) { + return reinterpret_cast( + absl::container_internal::Allocate( + mutable_allocator(), size)); + } + + // Node creation/deletion routines. + node_type *new_internal_node(node_type *parent) { + node_type *n = allocate(node_type::InternalSize()); + n->init_internal(parent); + return n; + } + node_type *new_leaf_node(node_type *parent) { + node_type *n = allocate(node_type::LeafSize()); + n->init_leaf(kNodeSlots, parent); + return n; + } + node_type *new_leaf_root_node(const int max_count) { + node_type *n = allocate(node_type::LeafSize(max_count)); + n->init_leaf(max_count, /*parent=*/n); + return n; + } + + // Deletion helper routines. + iterator rebalance_after_delete(iterator iter); + + // Rebalances or splits the node iter points to. + void rebalance_or_split(iterator *iter); + + // Merges the values of left, right and the delimiting key on their parent + // onto left, removing the delimiting key and deleting right. + void merge_nodes(node_type *left, node_type *right); + + // Tries to merge node with its left or right sibling, and failing that, + // rebalance with its left or right sibling. Returns true if a merge + // occurred, at which point it is no longer valid to access node. Returns + // false if no merging took place. + bool try_merge_or_rebalance(iterator *iter); + + // Tries to shrink the height of the tree by 1. + void try_shrink(); + + iterator internal_end(iterator iter) { + return iter.node_ != nullptr ? iter : end(); + } + const_iterator internal_end(const_iterator iter) const { + return iter.node_ != nullptr ? iter : end(); + } + + // Emplaces a value into the btree immediately before iter. Requires that + // key(v) <= iter.key() and (--iter).key() <= key(v). + template + iterator internal_emplace(iterator iter, Args &&... args); + + // Returns an iterator pointing to the first value >= the value "iter" is + // pointing at. Note that "iter" might be pointing to an invalid location such + // as iter.position_ == iter.node_->finish(). This routine simply moves iter + // up in the tree to a valid location. Requires: iter.node_ is non-null. + template + static IterType internal_last(IterType iter); + + // Returns an iterator pointing to the leaf position at which key would + // reside in the tree, unless there is an exact match - in which case, the + // result may not be on a leaf. When there's a three-way comparator, we can + // return whether there was an exact match. This allows the caller to avoid a + // subsequent comparison to determine if an exact match was made, which is + // important for keys with expensive comparison, such as strings. + template + SearchResult internal_locate( + const K &key) const; + + // Internal routine which implements lower_bound(). + template + SearchResult internal_lower_bound( + const K &key) const; + + // Internal routine which implements upper_bound(). + template + iterator internal_upper_bound(const K &key) const; + + // Internal routine which implements find(). + template + iterator internal_find(const K &key) const; + + // Verifies the tree structure of node. + int internal_verify(const node_type *node, const key_type *lo, + const key_type *hi) const; + + node_stats internal_stats(const node_type *node) const { + // The root can be a static empty node. + if (node == nullptr || (node == root() && empty())) { + return node_stats(0, 0); + } + if (node->is_leaf()) { + return node_stats(1, 0); + } + node_stats res(0, 1); + for (int i = node->start(); i <= node->finish(); ++i) { + res += internal_stats(node->child(i)); + } + return res; + } + + node_type *root_; + + // A pointer to the rightmost node. Note that the leftmost node is stored as + // the root's parent. We use compressed tuple in order to save space because + // key_compare and allocator_type are usually empty. + absl::container_internal::CompressedTuple + rightmost_; + + // Number of values. + size_type size_; +}; + +//// +// btree_node methods +template +template +inline void btree_node

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