From 86837299c38d888bd2efc76b6ee6d436940a9371 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=89tienne=20Mollier?= Date: Mon, 4 Oct 2021 20:37:06 +0100 Subject: [PATCH] Import r-cran-s2_1.0.7.orig.tar.gz [dgit import orig r-cran-s2_1.0.7.orig.tar.gz] --- DESCRIPTION | 45 + MD5 | 630 ++++ NAMESPACE | 178 ++ NEWS.md | 80 + R/RcppExports.R | 423 +++ R/data.R | 72 + R/s2-accessors.R | 169 + R/s2-bounds.R | 35 + R/s2-cell.R | 315 ++ R/s2-constructors-formatters.R | 139 + R/s2-earth.R | 23 + R/s2-geography.R | 151 + R/s2-lnglat.R | 113 + R/s2-matrix.R | 187 ++ R/s2-options.R | 147 + R/s2-package.R | 10 + R/s2-point.R | 89 + R/s2-predicates.R | 171 + R/s2-transformers.R | 269 ++ R/s2-xptr.R | 116 + R/utils.R | 78 + R/vctrs.R | 48 + R/wk-utils.R | 78 + R/zzz.R | 72 + README.md | 184 ++ build/partial.rdb | Bin 0 -> 2929 bytes cleanup | 3 + configure | 117 + configure.win | 0 data/s2_data_tbl_cities.rda | Bin 0 -> 7074 bytes data/s2_data_tbl_countries.rda | Bin 0 -> 132764 bytes data/s2_data_tbl_timezones.rda | Bin 0 -> 488748 bytes inst/extdata/emptyfile | 0 man/as_s2_geography.Rd | 77 + man/figures/rc300.png | Bin 0 -> 15702 bytes man/s2-package.Rd | 41 + man/s2_boundary.Rd | 196 ++ man/s2_bounds_cap.Rd | 41 + man/s2_cell.Rd | 77 + man/s2_cell_is_valid.Rd | 74 + man/s2_closest_feature.Rd | 122 + man/s2_contains.Rd | 170 + man/s2_data_tbl_countries.Rd | 59 + man/s2_earth_radius_meters.Rd | 25 + man/s2_geog_point.Rd | 117 + man/s2_interpolate.Rd | 53 + man/s2_is_collection.Rd | 112 + man/s2_lnglat.Rd | 55 + man/s2_options.Rd | 109 + man/s2_point.Rd | 48 + man/s2_unprojection_filter.Rd | 79 + src/Makevars.in | 203 ++ src/Makevars.ucrt | 2 + src/Makevars.win | 206 ++ src/RcppExports.cpp | 1396 +++++++++ src/absl/algorithm/algorithm.h | 159 + src/absl/algorithm/container.h | 1764 +++++++++++ src/absl/base/attributes.h | 702 +++++ src/absl/base/call_once.h | 219 ++ src/absl/base/casts.h | 187 ++ src/absl/base/config.h | 746 +++++ src/absl/base/const_init.h | 76 + src/absl/base/dynamic_annotations.h | 496 +++ src/absl/base/internal/atomic_hook.h | 200 ++ src/absl/base/internal/cycleclock.cc | 107 + src/absl/base/internal/cycleclock.h | 94 + src/absl/base/internal/direct_mmap.h | 169 + src/absl/base/internal/dynamic_annotations.h | 398 +++ src/absl/base/internal/endian.h | 327 ++ src/absl/base/internal/errno_saver.h | 43 + src/absl/base/internal/exponential_biased.cc | 93 + src/absl/base/internal/exponential_biased.h | 130 + src/absl/base/internal/fast_type_id.h | 48 + 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 | 187 ++ src/absl/base/internal/low_level_alloc.cc | 620 ++++ src/absl/base/internal/low_level_alloc.h | 126 + src/absl/base/internal/low_level_scheduling.h | 134 + src/absl/base/internal/per_thread_tls.h | 52 + src/absl/base/internal/periodic_sampler.cc | 53 + src/absl/base/internal/periodic_sampler.h | 211 ++ src/absl/base/internal/pretty_function.h | 33 + src/absl/base/internal/raw_logging.cc | 245 ++ src/absl/base/internal/raw_logging.h | 205 ++ 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 | 229 ++ src/absl/base/internal/spinlock.h | 246 ++ src/absl/base/internal/spinlock_akaros.inc | 35 + src/absl/base/internal/spinlock_linux.inc | 74 + src/absl/base/internal/spinlock_posix.inc | 46 + src/absl/base/internal/spinlock_wait.cc | 81 + src/absl/base/internal/spinlock_wait.h | 93 + 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 | 444 +++ src/absl/base/internal/sysinfo.h | 74 + src/absl/base/internal/thread_annotations.h | 271 ++ src/absl/base/internal/thread_identity.cc | 155 + 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 | 138 + src/absl/base/internal/unscaledcycleclock.h | 124 + src/absl/base/log_severity.cc | 27 + src/absl/base/log_severity.h | 121 + src/absl/base/macros.h | 158 + src/absl/base/optimization.h | 244 ++ 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 | 768 +++++ src/absl/container/btree_set.h | 683 ++++ src/absl/container/fixed_array.h | 532 ++++ src/absl/container/flat_hash_map.h | 606 ++++ src/absl/container/flat_hash_set.h | 504 +++ src/absl/container/inlined_vector.h | 847 +++++ src/absl/container/internal/btree.h | 2620 ++++++++++++++++ src/absl/container/internal/btree_container.h | 682 ++++ src/absl/container/internal/common.h | 206 ++ .../container/internal/compressed_tuple.h | 290 ++ .../container/internal/container_memory.h | 460 +++ .../container/internal/counting_allocator.h | 114 + .../internal/hash_function_defaults.h | 161 + .../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 | 274 ++ .../container/internal/hashtablez_sampler.h | 322 ++ ...ashtablez_sampler_force_weak_definition.cc | 31 + src/absl/container/internal/have_sse.h | 50 + src/absl/container/internal/inlined_vector.h | 967 ++++++ src/absl/container/internal/layout.h | 743 +++++ .../container/internal/node_hash_policy.h | 92 + src/absl/container/internal/raw_hash_map.h | 197 ++ src/absl/container/internal/raw_hash_set.cc | 61 + src/absl/container/internal/raw_hash_set.h | 1903 ++++++++++++ src/absl/container/internal/tracked.h | 83 + src/absl/container/node_hash_map.h | 597 ++++ src/absl/container/node_hash_set.h | 493 +++ src/absl/debugging/failure_signal_handler.cc | 387 +++ src/absl/debugging/failure_signal_handler.h | 121 + .../debugging/internal/address_is_readable.cc | 139 + .../debugging/internal/address_is_readable.h | 32 + src/absl/debugging/internal/demangle.cc | 1949 ++++++++++++ src/absl/debugging/internal/demangle.h | 71 + src/absl/debugging/internal/elf_mem_image.cc | 382 +++ src/absl/debugging/internal/elf_mem_image.h | 134 + src/absl/debugging/internal/examine_stack.cc | 203 ++ src/absl/debugging/internal/examine_stack.h | 42 + .../debugging/internal/stack_consumption.cc | 185 ++ .../debugging/internal/stack_consumption.h | 50 + .../internal/stacktrace_aarch64-inl.inc | 199 ++ .../debugging/internal/stacktrace_arm-inl.inc | 134 + .../debugging/internal/stacktrace_config.h | 80 + .../internal/stacktrace_generic-inl.inc | 108 + .../internal/stacktrace_powerpc-inl.inc | 253 ++ .../internal/stacktrace_unimplemented-inl.inc | 24 + .../internal/stacktrace_win32-inl.inc | 93 + .../debugging/internal/stacktrace_x86-inl.inc | 346 +++ src/absl/debugging/internal/symbolize.h | 147 + src/absl/debugging/internal/vdso_support.cc | 173 ++ src/absl/debugging/internal/vdso_support.h | 158 + src/absl/debugging/leak_check.cc | 69 + src/absl/debugging/leak_check.h | 133 + src/absl/debugging/leak_check_disable.cc | 20 + src/absl/debugging/stacktrace.cc | 140 + src/absl/debugging/stacktrace.h | 231 ++ src/absl/debugging/symbolize.cc | 37 + src/absl/debugging/symbolize.h | 99 + src/absl/debugging/symbolize_darwin.inc | 101 + src/absl/debugging/symbolize_elf.inc | 1560 ++++++++++ .../debugging/symbolize_unimplemented.inc | 40 + src/absl/debugging/symbolize_win32.inc | 82 + src/absl/functional/bind_front.h | 184 ++ src/absl/functional/function_ref.h | 139 + 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 | 767 +++++ src/absl/numeric/bits.h | 177 ++ src/absl/numeric/int128.cc | 390 +++ src/absl/numeric/int128.h | 1092 +++++++ src/absl/numeric/int128_have_intrinsic.inc | 302 ++ src/absl/numeric/int128_no_intrinsic.inc | 308 ++ src/absl/numeric/internal/bits.h | 358 +++ src/absl/numeric/internal/representation.h | 55 + src/absl/strings/ascii.cc | 200 ++ src/absl/strings/ascii.h | 242 ++ src/absl/strings/charconv.cc | 984 ++++++ src/absl/strings/charconv.h | 119 + src/absl/strings/cord.cc | 1953 ++++++++++++ src/absl/strings/cord.h | 1394 +++++++++ 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_internal.cc | 83 + src/absl/strings/internal/cord_internal.h | 543 ++++ src/absl/strings/internal/cord_rep_flat.h | 146 + src/absl/strings/internal/cord_rep_ring.cc | 897 ++++++ src/absl/strings/internal/cord_rep_ring.h | 589 ++++ .../strings/internal/cord_rep_ring_reader.h | 114 + src/absl/strings/internal/escaping.cc | 180 ++ 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 | 73 + 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 | 518 ++++ src/absl/strings/internal/str_format/bind.cc | 259 ++ src/absl/strings/internal/str_format/bind.h | 217 ++ .../strings/internal/str_format/checker.h | 333 ++ .../strings/internal/str_format/extension.cc | 75 + .../strings/internal/str_format/extension.h | 427 +++ .../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 | 96 + .../strings/internal/str_format/parser.cc | 350 +++ src/absl/strings/internal/str_format/parser.h | 349 +++ src/absl/strings/internal/str_join_internal.h | 314 ++ .../strings/internal/str_split_internal.h | 430 +++ src/absl/strings/internal/string_constant.h | 64 + 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 | 266 ++ src/absl/strings/str_cat.cc | 246 ++ src/absl/strings/str_cat.h | 408 +++ src/absl/strings/str_format.h | 813 +++++ src/absl/strings/str_join.h | 293 ++ 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 | 548 ++++ src/absl/strings/string_view.cc | 235 ++ src/absl/strings/string_view.h | 629 ++++ src/absl/strings/strip.h | 91 + src/absl/strings/substitute.cc | 171 + src/absl/strings/substitute.h | 696 +++++ src/absl/synchronization/barrier.cc | 52 + src/absl/synchronization/barrier.h | 79 + src/absl/synchronization/blocking_counter.cc | 57 + src/absl/synchronization/blocking_counter.h | 99 + .../internal/create_thread_identity.cc | 140 + .../internal/create_thread_identity.h | 60 + 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 | 106 + .../synchronization/internal/per_thread_sem.h | 115 + .../synchronization/internal/thread_pool.h | 93 + src/absl/synchronization/internal/waiter.cc | 428 +++ src/absl/synchronization/internal/waiter.h | 155 + src/absl/synchronization/mutex.cc | 2751 +++++++++++++++++ src/absl/synchronization/mutex.h | 1082 +++++++ src/absl/synchronization/notification.cc | 78 + src/absl/synchronization/notification.h | 123 + src/absl/time/civil_time.cc | 175 ++ src/absl/time/civil_time.h | 538 ++++ src/absl/time/clock.cc | 585 ++++ src/absl/time/clock.h | 74 + src/absl/time/duration.cc | 954 ++++++ src/absl/time/format.cc | 160 + .../internal/cctz/include/cctz/civil_time.h | 332 ++ .../cctz/include/cctz/civil_time_detail.h | 628 ++++ .../internal/cctz/include/cctz/time_zone.h | 386 +++ .../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 | 1029 ++++++ .../time/internal/cctz/src/time_zone_if.cc | 45 + .../time/internal/cctz/src/time_zone_if.h | 76 + .../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 | 965 ++++++ .../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 | 187 ++ .../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 | 116 + .../time/internal/get_current_time_chrono.inc | 31 + .../time/internal/get_current_time_posix.inc | 24 + src/absl/time/internal/zoneinfo.inc | 729 +++++ src/absl/time/time.cc | 500 +++ src/absl/time/time.h | 1585 ++++++++++ src/absl/types/any.h | 528 ++++ 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 | 931 ++++++ .../internal/conformance_testing_helpers.h | 391 +++ src/absl/types/internal/optional.h | 396 +++ 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 | 776 +++++ src/absl/types/span.h | 726 +++++ src/absl/types/variant.h | 866 ++++++ src/absl/utility/utility.h | 350 +++ src/cpp-compat.cpp | 40 + src/cpp-compat.h | 16 + src/geography-collection.h | 241 ++ src/geography-operator.h | 108 + src/geography.h | 116 + src/init.cpp | 14 + src/point-geography.h | 181 ++ src/polygon-geography.h | 334 ++ src/polyline-geography.h | 215 ++ src/s2-accessors.cpp | 237 ++ src/s2-bounds.cpp | 68 + src/s2-c-api.cpp | 170 + src/s2-cell.cpp | 744 +++++ src/s2-constructors-formatters.cpp | 82 + src/s2-geography.cpp | 135 + src/s2-lnglat.cpp | 62 + src/s2-matrix.cpp | 656 ++++ src/s2-options.h | 347 +++ src/s2-point.cpp | 60 + src/s2-predicates.cpp | 231 ++ src/s2-transformers.cpp | 673 ++++ src/s2-xptr.cpp | 50 + 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 | 61 + 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 | 419 +++ 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 | 356 +++ 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 | 832 +++++ 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/tests/soname.h | 9 + src/wk-c-utils.c | 371 +++ src/wk-geography.h | 168 + src/wk-impl.c | 2 + tests/area.R | 23 + tests/area.Rout | 29 + tests/testthat.R | 4 + tests/testthat/test-data.R | 30 + tests/testthat/test-s2-accessors.R | 219 ++ tests/testthat/test-s2-bounds.R | 44 + tests/testthat/test-s2-cell.R | 371 +++ .../test-s2-constructors-formatters.R | 171 + tests/testthat/test-s2-earth.R | 5 + tests/testthat/test-s2-geography.R | 236 ++ tests/testthat/test-s2-lnglat.R | 92 + tests/testthat/test-s2-matrix.R | 249 ++ tests/testthat/test-s2-options.R | 11 + tests/testthat/test-s2-point.R | 57 + tests/testthat/test-s2-predicates.R | 165 + tests/testthat/test-s2-transformers.R | 622 ++++ tests/testthat/test-s2-xptr.R | 36 + tests/testthat/test-utils.R | 42 + tests/testthat/test-vctrs.R | 32 + tests/testthat/test-wk-utils.R | 178 ++ tools/version.c | 4 + tools/winlibs.R | 8 + 631 files changed, 177886 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/s2-accessors.R create mode 100644 R/s2-bounds.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/s2-xptr.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_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_closest_feature.Rd create mode 100644 man/s2_contains.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_point.Rd create mode 100644 man/s2_unprojection_filter.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/exponential_biased.cc create mode 100644 src/absl/base/internal/exponential_biased.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/periodic_sampler.cc create mode 100644 src/absl/base/internal/periodic_sampler.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/have_sse.h 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_hash_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_generic-inl.inc create mode 100644 src/absl/debugging/internal/stacktrace_powerpc-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/leak_check_disable.cc 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_unimplemented.inc create mode 100644 src/absl/debugging/symbolize_win32.inc 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/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/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/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_internal.cc create mode 100644 src/absl/strings/internal/cord_internal.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/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-collection.h create mode 100644 src/geography-operator.h create mode 100644 src/geography.h create mode 100644 src/init.cpp create mode 100644 src/point-geography.h create mode 100644 src/polygon-geography.h create mode 100644 src/polyline-geography.h create mode 100644 src/s2-accessors.cpp create mode 100644 src/s2-bounds.cpp create mode 100644 src/s2-c-api.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-point.cpp create mode 100644 src/s2-predicates.cpp create mode 100644 src/s2-transformers.cpp create mode 100644 src/s2-xptr.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/tests/soname.h create mode 100644 src/wk-c-utils.c create mode 100644 src/wk-geography.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-s2-accessors.R create mode 100644 tests/testthat/test-s2-bounds.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-s2-xptr.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..29f09da --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,45 @@ +Package: s2 +Title: Spherical Geometry Operators Using the S2 Geometry Library +Version: 1.0.7 +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.1.2 +SystemRequirements: OpenSSL >= 1.0.1 +LinkingTo: Rcpp, wk +Imports: Rcpp, wk (>= 0.5.0) +Suggests: testthat, 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) +NeedsCompilation: yes +Packaged: 2021-09-22 15:42:08 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: 2021-09-28 09:40:02 UTC diff --git a/MD5 b/MD5 new file mode 100644 index 0000000..2249f56 --- /dev/null +++ b/MD5 @@ -0,0 +1,630 @@ +81b168cfcec3ee13f46dcdb577c17cf5 *DESCRIPTION +c2cf7412597f5a006b397f17534e4926 *NAMESPACE +00d4b6d276793ebb734e49773c6cf8ee *NEWS.md +c2684d1f0a404bc345a324f7da698415 *R/RcppExports.R +8567a6b7a3c75cad01a581303546fced *R/data.R +b783a368a460bcae5d174c575d863e59 *R/s2-accessors.R +4ba8ec7d16efeadbc21c3ab5ac3dc6a6 *R/s2-bounds.R +e4b9443bce55208a66db5e2270e3b39e *R/s2-cell.R +a9b85fd1af5d8ffba7f46f9d16b333ee *R/s2-constructors-formatters.R +0968515e318a2c7a7372bddad1be6c23 *R/s2-earth.R +9b49bdbca96d37c94db186cac76c12f4 *R/s2-geography.R +f7370a030a2faf0fb9a79166fc012ea2 *R/s2-lnglat.R +98df8e5c809782b58b54ec16b7e1e47f *R/s2-matrix.R +e960477e2e4f889c85831e3d438bfe0f *R/s2-options.R +1bab90d590a0e66114c86739ba0d6ce4 *R/s2-package.R +e78c2f5f3bc790e3fa91d3f5e037754e *R/s2-point.R +447d319c0cbec576de0695a518292089 *R/s2-predicates.R +57f21ccb0ebe8ce32a1a8c2677e28766 *R/s2-transformers.R +3b4c3f3354c7dc58491cf0ec7b21a869 *R/s2-xptr.R +4dc8dfdd1b91963c25b41c106a439978 *R/utils.R +f47605a30d14896490f2b587097b7d64 *R/vctrs.R +9c82a07787347c391042dbb9b1c7fabe *R/wk-utils.R +f73f271153285cff50aca1aea7d273aa *R/zzz.R +e9fb7b9570945530c4833c91310084f6 *README.md +c5b12c2075a2b01b74abcbcd51301950 *build/partial.rdb +cc6eebf14a3f516ec74168f3ea416fb2 *cleanup +9401515addc6f00f403443fd0715fcf9 *configure +d41d8cd98f00b204e9800998ecf8427e *configure.win +6d0a8453b0e12b4654d468d1e7248961 *data/s2_data_tbl_cities.rda +b0b89f3499ca649d9a78f77096e4f5da *data/s2_data_tbl_countries.rda +5bbeed937d4c3e766c02ce396f77cefc *data/s2_data_tbl_timezones.rda +d41d8cd98f00b204e9800998ecf8427e *inst/extdata/emptyfile +229cd1481362f4fa2c4c3769886f6cfd *man/as_s2_geography.Rd +ed01c59b91c1a9c3382d1f2ab67e60cc *man/figures/rc300.png +0d32af4cce70305a4ad68adb26e7c89f *man/s2-package.Rd +d5798affb398838bd20ed59853a68a41 *man/s2_boundary.Rd +a906d7f9956940ceebe0bd879c14e5f2 *man/s2_bounds_cap.Rd +198a89e9bd9be11ca5d34e0d79aaabba *man/s2_cell.Rd +11547e36b46a12a3adbcfcb3a6d0104c *man/s2_cell_is_valid.Rd +8f98d4539e0aa509666786e0e12bdda8 *man/s2_closest_feature.Rd +d96650f409fa8eb02fe4d5f54d00eb7d *man/s2_contains.Rd +addd614791ada510339317f68202d348 *man/s2_data_tbl_countries.Rd +d7a1ee72a69458a577921e02c2f4142f *man/s2_earth_radius_meters.Rd +b7d61946f176fbfc08dcb89df81b8bed *man/s2_geog_point.Rd +9676c31cb07674f1d6935a5b0462f3ca *man/s2_interpolate.Rd +32c6a0bb1280c25f7da7dc9781315d92 *man/s2_is_collection.Rd +e1a47506a896f0144af3c34265fef2b1 *man/s2_lnglat.Rd +43d061044e95b05b97b7cdb01d392708 *man/s2_options.Rd +a86f2581e1af6031167a29956341993c *man/s2_point.Rd +e2e7f612cb4705ee22e059404c24be46 *man/s2_unprojection_filter.Rd +f684c84e3ef6a090c5560ff816af3977 *src/Makevars.in +2cec33791efd347cbcb051402d7cf9c4 *src/Makevars.ucrt +75eba20be555de5ca4e072f20f6ddf07 *src/Makevars.win +7a2ec09d8e7950ea34ca0cd01a31f10e *src/RcppExports.cpp +642c7be2ce45690aea290338fec0675b *src/absl/algorithm/algorithm.h +abbb3beafcfd12ec8de50f69f3a47b28 *src/absl/algorithm/container.h +64d93e215a141d272b707e7c3e3d686c *src/absl/base/attributes.h +daad8899b1d374f22c26521512c3a8bb *src/absl/base/call_once.h +e8dea424b99773eaa10eb115f38a8020 *src/absl/base/casts.h +ac9b1e3f0c06e773e4ba0f3992e12990 *src/absl/base/config.h +f787189f34e273f4314344b53f5ccae9 *src/absl/base/const_init.h +1f5620fada3335961e01206ff444b0ae *src/absl/base/dynamic_annotations.h +376abfbcc035fb5ddd76fb513589a1e9 *src/absl/base/internal/atomic_hook.h +5c0507fc9e5c3bc17ce202b48782021c *src/absl/base/internal/cycleclock.cc +b116912ccd85e3a9430818e4c3036ea3 *src/absl/base/internal/cycleclock.h +23a9c55200d0af9d9c8cef12c90dd2a6 *src/absl/base/internal/direct_mmap.h +3c6051b3b1895802b42fd070e0739173 *src/absl/base/internal/dynamic_annotations.h +7db02f9ac5f988a004333c9f3d87b41d *src/absl/base/internal/endian.h +1ad6b95fe99ebc367d31ff7a9a8dd1fa *src/absl/base/internal/errno_saver.h +8ee24334738ffd8e7b3e8fbcc05cf6d2 *src/absl/base/internal/exponential_biased.cc +99bdbfd7a69988307f49350c499dcd6d *src/absl/base/internal/exponential_biased.h +05a1a4ed552e4e9950477ddccf4e60f2 *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 +3d66e2273f9898834248c42c49792f1f *src/absl/base/internal/invoke.h +f75b6817d994658a3311ae68b3f695f4 *src/absl/base/internal/low_level_alloc.cc +b193e771e15968a65b708776c1952cd8 *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 +da0b534ac124fa816d87a32283635e41 *src/absl/base/internal/periodic_sampler.cc +fa58280b181168b79b2fa73da55d7210 *src/absl/base/internal/periodic_sampler.h +cff9e9f5e90b5e4455317dddbda47753 *src/absl/base/internal/pretty_function.h +127a6bca2c255766410d509ee66a1727 *src/absl/base/internal/raw_logging.cc +3fd701b86b88ea265e9081cf813dfa63 *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 +38f62404cbcc87c6c59dac883fa17efe *src/absl/base/internal/spinlock.cc +8c633f51e341ada2cfcec5dcb284306e *src/absl/base/internal/spinlock.h +5e1d7bbdbd633f5f9e26bc6000d7bdc5 *src/absl/base/internal/spinlock_akaros.inc +19fd194fffce5af31f4b04948bc0bbce *src/absl/base/internal/spinlock_linux.inc +63f56ff5eab205a2ba5a852dbd6276f2 *src/absl/base/internal/spinlock_posix.inc +3917a541890f75bcb371b26c11491bec *src/absl/base/internal/spinlock_wait.cc +fcd0670efcd90e57a5d8636e18c12466 *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 +6cecc85fd4065b9bb247be733215a66e *src/absl/base/internal/sysinfo.cc +89920866f6792bd96338cb6f9587f3a9 *src/absl/base/internal/sysinfo.h +af1f8d88b67b54f7302728488ddb98a0 *src/absl/base/internal/thread_annotations.h +1daa68c6e9939a0c80dfa0be8d0156b7 *src/absl/base/internal/thread_identity.cc +efc69770bbf3e706d68f5c03291e7fcd *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 +523d89add712826c5dcdba48080a18dc *src/absl/base/internal/unscaledcycleclock.cc +277e4d3d077af7616e5f168c11a06333 *src/absl/base/internal/unscaledcycleclock.h +fdf34f719b5db2e3779db336a9f4aeb5 *src/absl/base/log_severity.cc +44df01285eed64cac786953b0bbe0584 *src/absl/base/log_severity.h +9a7add8647ed73b494f794f18c11270d *src/absl/base/macros.h +a17b980f549b27ac31509baa0c32a1d9 *src/absl/base/optimization.h +5366ef14082b53cb6d2ee8cb34df7b9c *src/absl/base/options.h +30a55e8729c05984884801d5f5fa1422 *src/absl/base/policy_checks.h +c942a5d8341ce35b666c8a2e3625aed9 *src/absl/base/port.h +1ae44e59aef073449d4de7f6db67f9ef *src/absl/base/thread_annotations.h +8be292b5a266737b84d1d06a2dcffd6d *src/absl/container/btree_map.h +6f70ed3485af0133d0d7f416c47cb791 *src/absl/container/btree_set.h +4f04d47ba2a8c9d6dbddb09d47bb7715 *src/absl/container/fixed_array.h +3fee50e5fc0e01a674590d0badfb7c1a *src/absl/container/flat_hash_map.h +a635f4eb9fb36a55aa12db2a30cb1e78 *src/absl/container/flat_hash_set.h +e958494ec88a99a23c51ad906c0f0b4f *src/absl/container/inlined_vector.h +91da9f3b5057f01bd875d0068f18dc51 *src/absl/container/internal/btree.h +d6fc5b4500396f8ce82c2bc2d3e40503 *src/absl/container/internal/btree_container.h +7558fa6e6027b296784a64a46bc6d788 *src/absl/container/internal/common.h +013341274845cbce0b85a94bc2984b38 *src/absl/container/internal/compressed_tuple.h +b2adaced46957e3053d08bce85129f7a *src/absl/container/internal/container_memory.h +6e0d77985136113b115286f526da43d9 *src/absl/container/internal/counting_allocator.h +99dd086eeb57d74fa2e74ea720122b6e *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 +6e68e5b0ad7c4699102c22804ff1b02a *src/absl/container/internal/hashtablez_sampler.cc +71181538daeffa65c94b97bf5c93cd3d *src/absl/container/internal/hashtablez_sampler.h +fcc5f2abdf1fd0edb18d122a846e84f0 *src/absl/container/internal/hashtablez_sampler_force_weak_definition.cc +67787367372189cf47eecccc554522bd *src/absl/container/internal/have_sse.h +b4a83c63277eb5904e8d8ef68378d138 *src/absl/container/internal/inlined_vector.h +dde28d12969cb6df933b96f90b287506 *src/absl/container/internal/layout.h +08ec3770b73818067921e384d882113b *src/absl/container/internal/node_hash_policy.h +6bebca16955352c224454bad2947a1af *src/absl/container/internal/raw_hash_map.h +330143d7b11b49fb5cc4790e16e23b25 *src/absl/container/internal/raw_hash_set.cc +6cc10694c755f45efb3d940d24e5bf65 *src/absl/container/internal/raw_hash_set.h +b2c91b1fd5a05ea12fd76abd5e7401bc *src/absl/container/internal/tracked.h +45e358ab0306337b76b37d58cefff3fc *src/absl/container/node_hash_map.h +4587399b24c39753dc9c0597dc88c86c *src/absl/container/node_hash_set.h +fd227e1bc37c98bf705ff468ddccbbc4 *src/absl/debugging/failure_signal_handler.cc +c527b83e449ee568a4c8f837fb743831 *src/absl/debugging/failure_signal_handler.h +030a9a4b376aef8d3e17a5b7653e79c1 *src/absl/debugging/internal/address_is_readable.cc +91ad1bd868606c791022ba6aa6ef4860 *src/absl/debugging/internal/address_is_readable.h +79749a7ac0197c336804adacba36e3b9 *src/absl/debugging/internal/demangle.cc +de5e466a575d551be06c658446b26ca9 *src/absl/debugging/internal/demangle.h +377661945fc750fd86c51d19fd5687e2 *src/absl/debugging/internal/elf_mem_image.cc +7b5efa6e38540db0e97555ab96be25cd *src/absl/debugging/internal/elf_mem_image.h +ae3b4548cb22eed77938f806c6a7f595 *src/absl/debugging/internal/examine_stack.cc +bf6d2ad74f838b382b9b1ac74a3a9859 *src/absl/debugging/internal/examine_stack.h +4e46a9f2039e409ad9bff7a60cdb2812 *src/absl/debugging/internal/stack_consumption.cc +612671a5e770431c91f673f764547f9d *src/absl/debugging/internal/stack_consumption.h +915b1fdf07f8dd394c197b1df232e707 *src/absl/debugging/internal/stacktrace_aarch64-inl.inc +4f46976f36e6cee9a1891e5e1f3a9194 *src/absl/debugging/internal/stacktrace_arm-inl.inc +c8798257ab865349a90f7d399601c70f *src/absl/debugging/internal/stacktrace_config.h +31136485c4ae4719146fe310d09794b3 *src/absl/debugging/internal/stacktrace_generic-inl.inc +0e9dc2311a8c9adc0566d9e6ea2be586 *src/absl/debugging/internal/stacktrace_powerpc-inl.inc +e7ced4e67444a66063312dc27d7bb17d *src/absl/debugging/internal/stacktrace_unimplemented-inl.inc +36cbcb6faf6816b2fd3db94ed61e077d *src/absl/debugging/internal/stacktrace_win32-inl.inc +1a7b1161b715ea1e760779d3eea3e519 *src/absl/debugging/internal/stacktrace_x86-inl.inc +a89779a76eed6b7d48929f476c38ee00 *src/absl/debugging/internal/symbolize.h +1e154851ddc7945372bcc284a8562d63 *src/absl/debugging/internal/vdso_support.cc +32da4040ebc4e635623be36e861380d4 *src/absl/debugging/internal/vdso_support.h +65dc6fd0275e5453f5de91e1914adf47 *src/absl/debugging/leak_check.cc +cb4faa082b9cb79436905b6e28cec041 *src/absl/debugging/leak_check.h +112d7181da9d011fc1dc76cadf5682af *src/absl/debugging/leak_check_disable.cc +1183382775768ec8cdc4d12afc5cd5a6 *src/absl/debugging/stacktrace.cc +54920d74c95e9623f986ca3bf9a49c14 *src/absl/debugging/stacktrace.h +7618dc5ae1e244e987c2b4cf75699b11 *src/absl/debugging/symbolize.cc +2a4f1e90fdc9a56d8812afd0f0291876 *src/absl/debugging/symbolize.h +e1ff8c65c67e769e4d929d70b4c315a4 *src/absl/debugging/symbolize_darwin.inc +9febce3cf87a802ec2cced1cf1f043bd *src/absl/debugging/symbolize_elf.inc +37d1355403219402fa4d888e9be56fca *src/absl/debugging/symbolize_unimplemented.inc +479e5ee9044294e4a9f41d617bea785a *src/absl/debugging/symbolize_win32.inc +ebe446f8b54b238875a33dbc70ca9f4c *src/absl/functional/bind_front.h +5d7da8b88dbb9d2f99382941fd131c4c *src/absl/functional/function_ref.h +ca707a4761ddef188eefa1c47a49305d *src/absl/functional/internal/front_binder.h +acc1da8f94634fb69d04d64af189f7d9 *src/absl/functional/internal/function_ref.h +b4ffb4f58d0c4a4f17aa860ee04aad53 *src/absl/memory/memory.h +7af9293692a48df008af9d0611d9c31c *src/absl/meta/type_traits.h +858e460c788d7de02ff13b0b233126db *src/absl/numeric/bits.h +145175b518d1adbb5fb0dce36a5f8899 *src/absl/numeric/int128.cc +8644673633a9dfc079968b5176ea2f7f *src/absl/numeric/int128.h +d47ee4c02e33ef2807f31e1d0dd583d8 *src/absl/numeric/int128_have_intrinsic.inc +73c8313fb617cfb81256ddf3435068e1 *src/absl/numeric/int128_no_intrinsic.inc +cda33272dc55c7d4e85c050048ac3b68 *src/absl/numeric/internal/bits.h +b71517976c0230c0f0be9e102124708b *src/absl/numeric/internal/representation.h +d8fbe682d7f971ad41cf3585695e5b8c *src/absl/strings/ascii.cc +3df34b80366fc927b32ca23aa02e84a7 *src/absl/strings/ascii.h +8116da12e911ccc43c42e9625d66649d *src/absl/strings/charconv.cc +952b5d3677d49520668071e34ce45dec *src/absl/strings/charconv.h +31bbaeb6b8a559bebb814a464a12636c *src/absl/strings/cord.cc +398bbd3109f1a19fbba1bf1e6580d451 *src/absl/strings/cord.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 +aed0b804c0b6fafc249045d6e813d977 *src/absl/strings/internal/charconv_parse.cc +bbb89ffd31b13599454ac66c7fca1021 *src/absl/strings/internal/charconv_parse.h +eba3000b493bf919ab4f1ea0fcc893b3 *src/absl/strings/internal/cord_internal.cc +3265151e70bb7085b8d90f73d12e7cb7 *src/absl/strings/internal/cord_internal.h +f86d2d94553fd7178bb408aa16114558 *src/absl/strings/internal/cord_rep_flat.h +fbef4ad540f3c3695543431c759598f3 *src/absl/strings/internal/cord_rep_ring.cc +5b638ed5240f87c07804ed0d8bab607e *src/absl/strings/internal/cord_rep_ring.h +1ffe84c3144080125bbe543e185b47cc *src/absl/strings/internal/cord_rep_ring_reader.h +dabafab078d1bac10b0098727d403546 *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 +3708fdf5b90a9c3172473ce0d9604d17 *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 +fc6ff54a84bb558a5a7c777e23e5a920 *src/absl/strings/internal/resize_uninitialized.h +0b127a362be3ed6ce297a9bd6704b6f5 *src/absl/strings/internal/stl_type_traits.h +b9c8cf14b3a279effbaa20556c88fb4a *src/absl/strings/internal/str_format/arg.cc +4da0477be5eb4f92ad99365211d18e3b *src/absl/strings/internal/str_format/arg.h +56f76b7caadb708b9acc64a806c51a9c *src/absl/strings/internal/str_format/bind.cc +124a202275e971bc9a66fd7bd2a9b2cd *src/absl/strings/internal/str_format/bind.h +d840ba11bac79cfb5949e0e2f8e747f8 *src/absl/strings/internal/str_format/checker.h +82b6f25110cfaf6029f695d476d2df40 *src/absl/strings/internal/str_format/extension.cc +53cf33dd9171df9b28885c98684f1ac9 *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 +e11e12d5a8451faafdfa9055ee790517 *src/absl/strings/internal/str_format/output.h +a230208d6294cd6023b8f3a4a2e29ded *src/absl/strings/internal/str_format/parser.cc +42cf8fd14b9709ce8bd291d3e85c47cb *src/absl/strings/internal/str_format/parser.h +0da27066225bcdc3b6f03bc778f189ad *src/absl/strings/internal/str_join_internal.h +b1a532cd4b0d5ad6de67b268208a44d4 *src/absl/strings/internal/str_split_internal.h +471b6b7154083c7597fedb2ebcb4f7f9 *src/absl/strings/internal/string_constant.h +3f62ae65e3b560df6dce5b898e18a77e *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 +be764738922372e43aaab2abb84522e9 *src/absl/strings/numbers.cc +1e14203552b13db98e369a5f52567d7b *src/absl/strings/numbers.h +36af19ea6cb1761e464e0539e0a55e96 *src/absl/strings/str_cat.cc +65cd83cd3f60b592cefb41074e3372a8 *src/absl/strings/str_cat.h +578172c22291b2e3f8a32d4717e3f362 *src/absl/strings/str_format.h +1dce8f996516f3a9600a23d7b3c4953d *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 +adac09a2c5742680794a6b7401ddd125 *src/absl/strings/str_split.h +c13fbe924649e26b18213d3baff68cf8 *src/absl/strings/string_view.cc +dcdc37362ca77fda83f4d7ecfcf46021 *src/absl/strings/string_view.h +7839fb4d9bc896e90a45cd792c871cad *src/absl/strings/strip.h +9c0638dd36d893e583935a2380cde1be *src/absl/strings/substitute.cc +7f3fb4b82a9281066f1424b8efc030bb *src/absl/strings/substitute.h +e8a0e5ed8343c44e8d414a72596dbde9 *src/absl/synchronization/barrier.cc +104730dc624a422f9e0610e357d1d319 *src/absl/synchronization/barrier.h +735eaedd4692093fc3174a3103dd1b0e *src/absl/synchronization/blocking_counter.cc +d1b19aa573dbe35fe8a42f94c7de6026 *src/absl/synchronization/blocking_counter.h +f3983cd18ac57c01888f64fc5684a526 *src/absl/synchronization/internal/create_thread_identity.cc +d73963f1c926ad5c88a684bd9dda5908 *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 +40c3dece09e0b615dd79dcd7840f846f *src/absl/synchronization/internal/per_thread_sem.cc +22f2bf8ff660c4439e8f668dc29537cf *src/absl/synchronization/internal/per_thread_sem.h +dcd3d301b476895c2de035ae9f4a9c2f *src/absl/synchronization/internal/thread_pool.h +a216ef1d1164cc1b11d8220f2dc72821 *src/absl/synchronization/internal/waiter.cc +69e148f20aea7defb0ce5aa05d10afa3 *src/absl/synchronization/internal/waiter.h +53f2cd2a12bdffe2a12e402926cf98ac *src/absl/synchronization/mutex.cc +7a9e707ce21026cd9e2ede33571a75c5 *src/absl/synchronization/mutex.h +18f40d74c189e4cb3bade5bbfac62eac *src/absl/synchronization/notification.cc +f1503caa5d24b585b51e7e3f43b73bf7 *src/absl/synchronization/notification.h +4ca503c31ce61fd3d3c0e838cba8618c *src/absl/time/civil_time.cc +21f77d9a8b12daee08647a79fa2f1c24 *src/absl/time/civil_time.h +f21e0ee98ace2abfa28852ce87a60fff *src/absl/time/clock.cc +7669a9d4fb695b4bdf63f2199778b7da *src/absl/time/clock.h +c80757466714981bbd771a5d1683f939 *src/absl/time/duration.cc +1029367259d746995cfcc86300cb550e *src/absl/time/format.cc +153fe848b8d395acdc4d7188b6b1f7bb *src/absl/time/internal/cctz/include/cctz/civil_time.h +27de12f307456658f389c758397935e7 *src/absl/time/internal/cctz/include/cctz/civil_time_detail.h +37eda67bdf4f83d1f892815fb45cfb81 *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 +59c65df1b08bc63ffa7a35c7837a89e2 *src/absl/time/internal/cctz/src/time_zone_fixed.cc +3afdc610576df58fccc41a7029f1591b *src/absl/time/internal/cctz/src/time_zone_fixed.h +ea69205e8e9f1e129590f2174e3807d8 *src/absl/time/internal/cctz/src/time_zone_format.cc +977f9a0fc17a72093bc4c8a87784f632 *src/absl/time/internal/cctz/src/time_zone_if.cc +8dd51e1e69ed86f3530b53f3eb63045b *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 +9efbeccc7ce7a4bd7edbf051a9bcc66b *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 +907aa719095f45114e565d7cd766126c *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 +b8aa420258080dab2f2c04570842ba10 *src/absl/time/internal/cctz/src/tzfile.h +b5acdb5d367ee97af0bea4a6f8f98cd4 *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 +ea2464a434c72678c0874bb09ed1ac22 *src/absl/time/internal/zoneinfo.inc +8893c1ef798bfa8c6a3421eafd82d0aa *src/absl/time/time.cc +f37a53793ed7fb319028b7d326e1056e *src/absl/time/time.h +a445019a19bf877dc01db45c01557a67 *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 +21cac14882c183c5c65cad134cd8d21b *src/absl/types/bad_optional_access.h +339cb16d4f7c125ef6f9975e9c70e2fd *src/absl/types/bad_variant_access.cc +b95c41fee3ab3f3b45b6b4202327652a *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 +e1e8f52038f942c2bad3abc5475cd692 *src/absl/types/internal/conformance_profile.h +a87cad8cf13eaebb402020f8d7fe4119 *src/absl/types/internal/conformance_testing_helpers.h +ca3dcfcfd2f2f870064028fe7498b1df *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 +229db9d6e5909ee0117e64404f72f433 *src/absl/types/internal/variant.h +cc6e8fbfb65176747af2e03bb8538497 *src/absl/types/optional.h +2848d44a5e03fa22209585de1315c79b *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 +2d84b8be2c1d5dd660edc6e0e90617df *src/geography-collection.h +5ee693f87c151f5f94ec383914992136 *src/geography-operator.h +95002ec40eccb735a1705fec9b04128e *src/geography.h +0365ec85c22784d83cce5b84d71d794a *src/init.cpp +d9466b6083ab42ccd4d48fdfb54c3725 *src/point-geography.h +be0819cf0b1ce418b7fd58f72ca8ef57 *src/polygon-geography.h +ae93138b66e63d703a72e48d47ca7b18 *src/polyline-geography.h +429b72a0714edd2015799bcc067ee748 *src/s2-accessors.cpp +40989d3a6148de2a42117013f098d21c *src/s2-bounds.cpp +10857401a812bd1e9f7c2bdbd626d3ab *src/s2-c-api.cpp +fa945115915fc66fa695861fd0475f47 *src/s2-cell.cpp +f5c6f18bef0fa2647fb6870d5c72177b *src/s2-constructors-formatters.cpp +148db936b49095d6352bfae506bba472 *src/s2-geography.cpp +2a18a33f6a16832c371549cee3ef1829 *src/s2-lnglat.cpp +34c555d3c53b6304835a25d6dc53b452 *src/s2-matrix.cpp +093210195ecdda6a7435575128bc5e85 *src/s2-options.h +26da724d2fb9ccb3b7579e9a7f49001e *src/s2-point.cpp +980e286c3b8ad05def8cb5729e83a98f *src/s2-predicates.cpp +22dc6e98f2e9d0afc941164bac1ef411 *src/s2-transformers.cpp +2a3ac9aacf9ff1513a19f70b91902c83 *src/s2-xptr.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 +84003e3ea7ed2316b94891afd7d425f0 *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 +2afc2b3b5d5c5eee69d662c490aeac3a *src/s2/s2edge_distances.cc +525ad6e536828fd0f783a532962bde59 *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 +fc15e353fdbb3c76de73bf5fa5a713d8 *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 +a3e2ab5873a9c7890d0e6e23fbbc3e72 *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 +4495750656420f7cd041bf0bf27ec020 *src/tests/soname.h +4b45b27837b35f28530c827cd798931f *src/wk-c-utils.c +c4146b7691c20e506a2cd4a013ca6218 *src/wk-geography.h +3ff04996080488941707e8fd75d50d02 *src/wk-impl.c +a9ffa55303a6dcba9c5c9c9d322e2009 *tests/area.R +960ec65d25d1018d2084d81ed3324fdd *tests/area.Rout +6a0d9b8264490c375aab2554285f41ec *tests/testthat.R +2852576d235eb67783d7b1118d8d9800 *tests/testthat/test-data.R +81045befe336a5386a8634dc25bb7230 *tests/testthat/test-s2-accessors.R +1047ccf796c487587c03286eb8ad3eb2 *tests/testthat/test-s2-bounds.R +ca886c0f51abb2c6a3783edfef8e9a95 *tests/testthat/test-s2-cell.R +cfb96b2f1b55571dd58adcbeb254950f *tests/testthat/test-s2-constructors-formatters.R +75ad7009cf6227ca8239c31198787956 *tests/testthat/test-s2-earth.R +76193fff2215eaa3c484556c88b490f6 *tests/testthat/test-s2-geography.R +adfd5e8008ceeea58ba3ee5e71ca040b *tests/testthat/test-s2-lnglat.R +37c763db9f669b79c2deb559f2e59b76 *tests/testthat/test-s2-matrix.R +589616aabb1c291a3984f521274815bf *tests/testthat/test-s2-options.R +9be8d6955daa3c211af562f4f41c639a *tests/testthat/test-s2-point.R +0688d88be5a5f999507d227ee30c99dc *tests/testthat/test-s2-predicates.R +b560db419b2b933fa6f9ed51859266c7 *tests/testthat/test-s2-transformers.R +e0de43269c8f8eb622ef3aa6c6d6f01a *tests/testthat/test-s2-xptr.R +a7de1183f03382626ca2b46bf1c4e781 *tests/testthat/test-utils.R +940f3bf944538a6dbfc2d786d12f2d67 *tests/testthat/test-vctrs.R +2203aa8a7e91a47ba75217c156e3b352 *tests/testthat/test-wk-utils.R +0ec27c181a66ce5b72bc8c3940e9e00e *tools/version.c +1f90833745efcd8c2e13769eef3fb57a *tools/winlibs.R diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 0000000..87ee9ea --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,178 @@ +# Generated by roxygen2: do not edit by hand + +S3method("[",s2_xptr) +S3method("[<-",s2_cell) +S3method("[<-",s2_geography) +S3method("[<-",s2_lnglat) +S3method("[<-",s2_point) +S3method("[[",s2_xptr) +S3method("[[<-",s2_cell) +S3method("[[<-",s2_geography) +S3method("[[<-",s2_lnglat) +S3method("[[<-",s2_point) +S3method(Math,s2_cell) +S3method(Ops,s2_cell) +S3method(Summary,s2_cell) +S3method(as.character,s2_cell) +S3method(as.character,s2_geography) +S3method(as.data.frame,s2_lnglat) +S3method(as.data.frame,s2_point) +S3method(as.data.frame,s2_xptr) +S3method(as.matrix,s2_lnglat) +S3method(as.matrix,s2_point) +S3method(as_s2_cell,character) +S3method(as_s2_cell,s2_cell) +S3method(as_s2_cell,s2_geography) +S3method(as_s2_cell,s2_lnglat) +S3method(as_s2_cell,s2_point) +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_geography) +S3method(as_s2_geography,s2_lnglat) +S3method(as_s2_geography,s2_point) +S3method(as_s2_geography,wk_wkb) +S3method(as_s2_geography,wk_wkt) +S3method(as_s2_lnglat,character) +S3method(as_s2_lnglat,matrix) +S3method(as_s2_lnglat,s2_geography) +S3method(as_s2_lnglat,s2_lnglat) +S3method(as_s2_lnglat,s2_point) +S3method(as_s2_lnglat,wk_wkb) +S3method(as_s2_lnglat,wk_wkt) +S3method(as_s2_point,matrix) +S3method(as_s2_point,s2_geography) +S3method(as_s2_point,s2_lnglat) +S3method(as_s2_point,s2_point) +S3method(as_wkb,s2_geography) +S3method(as_wkb,s2_lnglat) +S3method(as_wkt,s2_geography) +S3method(as_wkt,s2_lnglat) +S3method(c,s2_xptr) +S3method(format,s2_cell) +S3method(format,s2_geography) +S3method(format,s2_lnglat) +S3method(format,s2_point) +S3method(is.na,s2_cell) +S3method(is.numeric,s2_cell) +S3method(print,s2_xptr) +S3method(rep,s2_xptr) +S3method(rep_len,s2_xptr) +S3method(sort,s2_cell) +S3method(str,s2_xptr) +S3method(unique,s2_cell) +export(as_s2_cell) +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_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_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_coverage_union_agg) +export(s2_covered_by) +export(s2_covered_by_matrix) +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_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_point) +export(s2_project) +export(s2_project_normalized) +export(s2_projection_filter) +export(s2_projection_mercator) +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_touches) +export(s2_touches_matrix) +export(s2_union) +export(s2_union_agg) +export(s2_unprojection_filter) +export(s2_within) +export(s2_within_matrix) +export(s2_x) +export(s2_y) +importFrom(Rcpp,sourceCpp) +importFrom(wk,as_wkb) +importFrom(wk,as_wkt) +useDynLib(s2, .registration = TRUE) diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..347e8c0 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,80 @@ +# 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..22d8884 --- /dev/null +++ b/R/RcppExports.R @@ -0,0 +1,423 @@ +# 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_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_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_geog_point <- function(x, y) { + .Call(`_s2_cpp_s2_geog_point`, x, y) +} + +cpp_s2_make_line <- function(x, y, featureId) { + .Call(`_s2_cpp_s2_make_line`, x, y, featureId) +} + +cpp_s2_make_polygon <- function(x, y, featureId, ringId, oriented, check) { + .Call(`_s2_cpp_s2_make_polygon`, x, y, featureId, ringId, oriented, check) +} + +s2_geography_from_wkb <- function(wkb, oriented, check) { + .Call(`_s2_s2_geography_from_wkb`, wkb, oriented, check) +} + +s2_geography_from_wkt <- function(wkt, oriented, check) { + .Call(`_s2_s2_geography_from_wkt`, wkt, oriented, check) +} + +s2_geography_full <- function(x) { + .Call(`_s2_s2_geography_full`, x) +} + +s2_geography_to_wkt <- function(s2_geography, precision, trim) { + .Call(`_s2_s2_geography_to_wkt`, s2_geography, precision, trim) +} + +s2_geography_to_wkb <- function(s2_geography, endian) { + .Call(`_s2_s2_geography_to_wkb`, s2_geography, endian) +} + +s2_geography_format <- function(s2_geography, maxCoords, precision, trim) { + .Call(`_s2_s2_geography_format`, s2_geography, maxCoords, precision, trim) +} + +s2_lnglat_from_numeric <- function(lng, lat) { + .Call(`_s2_s2_lnglat_from_numeric`, lng, lat) +} + +s2_lnglat_from_s2_point <- function(s2_point) { + .Call(`_s2_s2_lnglat_from_s2_point`, s2_point) +} + +data_frame_from_s2_lnglat <- function(xptr) { + .Call(`_s2_data_frame_from_s2_lnglat`, xptr) +} + +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) { + .Call(`_s2_cpp_s2_closest_edges`, geog1, geog2, n, min_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) +} + +s2_point_from_numeric <- function(x, y, z) { + .Call(`_s2_s2_point_from_numeric`, x, y, z) +} + +s2_point_from_s2_lnglat <- function(s2_lnglat) { + .Call(`_s2_s2_point_from_s2_lnglat`, s2_lnglat) +} + +data_frame_from_s2_point <- function(s2_point) { + .Call(`_s2_data_frame_from_s2_point`, s2_point) +} + +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_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_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) +} + +s2_xptr_test <- function(size) { + .Call(`_s2_s2_xptr_test`, size) +} + +s2_xptr_test_op <- function(s2_xptr_test) { + invisible(.Call(`_s2_s2_xptr_test_op`, s2_xptr_test)) +} + diff --git a/R/data.R b/R/data.R new file mode 100644 index 0000000..80afa35 --- /dev/null +++ b/R/data.R @@ -0,0 +1,72 @@ + +#' 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 <- structure(df$geometry[(df$name %in% name) | (df$continent %in% name)], class = "wk_wkb") + } + + 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 <- structure(df$geometry[matches], class = "wk_wkb") + } + + 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 <- structure(df$geometry[(df$name %in% name)], class = "wk_wkb") + } + + as_s2_geography(wkb) +} 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..6c4f792 --- /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. +#' +#' @inheritParams s2_is_collection +#' @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.R b/R/s2-cell.R new file mode 100644 index 0000000..553f77b --- /dev/null +++ b/R/s2-cell.R @@ -0,0 +1,315 @@ + +#' 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.s2_lnglat <- function(x, ...) { + cpp_s2_cell_from_lnglat(as.data.frame(x)) +} + +#' @rdname s2_cell +#' @export +as_s2_cell.s2_point <- function(x, ...) { + as_s2_cell(as_s2_lnglat(x)) +} + +#' @rdname s2_cell +#' @export +new_s2_cell <- function(x) { + structure(x, class = c("s2_cell", "wk_vctr")) +} + +#' @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 +`[<-.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 1 and 4 +#' @param radius The radius to use (e.g., [s2_earth_radius_meters()]) +#' @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) +} diff --git a/R/s2-constructors-formatters.R b/R/s2-constructors-formatters.R new file mode 100644 index 0000000..46c1496 --- /dev/null +++ b/R/s2-constructors-formatters.R @@ -0,0 +1,139 @@ + +#' 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 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) +#' +s2_geog_point <- function(longitude, latitude) { + recycled <- recycle_common(longitude, latitude) + new_s2_xptr(cpp_s2_geog_point(recycled[[1]], recycled[[2]]), "s2_geography") +} + +#' @rdname s2_geog_point +#' @export +s2_make_line <- function(longitude, latitude, feature_id = 1L) { + recycled <- recycle_common(longitude, latitude, feature_id) + new_s2_xptr(cpp_s2_make_line(recycled[[1]], recycled[[2]], featureId = recycled[[3]]), "s2_geography") +} + +#' @rdname s2_geog_point +#' @export +s2_make_polygon <- function(longitude, latitude, feature_id = 1L, ring_id = 1L, + oriented = FALSE, check = TRUE) { + recycled <- recycle_common(longitude, latitude, feature_id, ring_id) + new_s2_xptr( + cpp_s2_make_polygon( + recycled[[1]], recycled[[2]], + featureId = recycled[[3]], + ringId = recycled[[4]], + oriented = oriented, + check = check + ), + "s2_geography" + ) +} + +#' @rdname s2_geog_point +#' @export +s2_geog_from_text <- function(wkt_string, oriented = FALSE, check = TRUE) { + wk::validate_wk_wkt(wkt_string) + new_s2_xptr( + s2_geography_from_wkt( + wkt_string, + oriented = oriented, + check = check + ), + "s2_geography" + ) +} + +#' @rdname s2_geog_point +#' @export +s2_geog_from_wkb <- function(wkb_bytes, oriented = FALSE, check = TRUE) { + wk::validate_wk_wkb(wkb_bytes) + new_s2_xptr( + s2_geography_from_wkb( + wkb_bytes, + oriented = oriented, + check = check + ), + "s2_geography" + ) +} + +#' @rdname s2_geog_point +#' @export +s2_as_text <- function(x, precision = 16, trim = TRUE) { + s2_geography_to_wkt(as_s2_geography(x), precision = precision, trim = trim) +} + +#' @rdname s2_geog_point +#' @export +s2_as_binary <- function(x, endian = wk::wk_platform_endian()) { + structure(s2_geography_to_wkb(as_s2_geography(x), endian = endian), class = "blob") +} 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..ac8dfe9 --- /dev/null +++ b/R/s2-geography.R @@ -0,0 +1,151 @@ + +#' 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_xptr(list(), "s2_geography") +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.s2_geography <- function(x, ...) { + x +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.s2_lnglat <- function(x, ...) { + df <- data_frame_from_s2_lnglat(x) + new_s2_xptr(cpp_s2_geog_point(df[[1]], df[[2]]), "s2_geography") +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.s2_point <- function(x, ...) { + as_s2_geography(as_s2_lnglat(x)) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.wk_wkb <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkb(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.WKB <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkb(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.blob <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkb(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.wk_wkt <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkt(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.character <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkt(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.logical <- function(x, ...) { + stopifnot(isTRUE(x)) + new_s2_xptr(s2_geography_full(TRUE), "s2_geography") +} + +#' @importFrom wk as_wkb +#' @rdname as_s2_geography +#' @export +as_wkb.s2_geography <- function(x, ...) { + wk::new_wk_wkb(s2_geography_to_wkb(x, wk::wk_platform_endian())) +} + +#' @importFrom wk as_wkt +#' @rdname as_s2_geography +#' @export +as_wkt.s2_geography <- function(x, ...) { + wk::new_wk_wkt(s2_geography_to_wkt(x, precision = 16, trim = TRUE)) +} + + +#' @export +`[<-.s2_geography` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_geography(value) + new_s2_xptr(x, "s2_geography") +} + +#' @export +`[[<-.s2_geography` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_geography(value) + new_s2_xptr(x, "s2_geography") +} + +#' @export +format.s2_geography <- function(x, ..., max_coords = 5, precision = 9, trim = TRUE) { + paste0("<", s2_geography_format(x, max_coords, precision, 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..59ceaad --- /dev/null +++ b/R/s2-lnglat.R @@ -0,0 +1,113 @@ + +#' 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) { + recycled <- recycle_common(as.double(lng), as.double(lat)) + new_s2_xptr(s2_lnglat_from_numeric(recycled[[1]], recycled[[2]]), "s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat <- function(x, ...) { + UseMethod("as_s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.s2_lnglat <- function(x, ...) { + x +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.s2_point <- function(x, ...) { + new_s2_xptr(s2_lnglat_from_s2_point(x), "s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.s2_geography <- function(x, ...) { + new_s2_xptr(s2_lnglat_from_numeric(cpp_s2_x(x), cpp_s2_y(x)), "s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.matrix <- function(x, ...) { + s2_lnglat(x[, 1, drop = TRUE], x[, 2, drop = TRUE]) +} + +#' @export +as_s2_lnglat.character <- function(x, ...) { + as_s2_lnglat.wk_wkt(x) +} + +#' @export +as_s2_lnglat.wk_wkt <- function(x, ...) { + as_s2_lnglat(as_s2_geography(x), ...) +} + +#' @export +as_s2_lnglat.wk_wkb <- function(x, ...) { + as_s2_lnglat(as_s2_geography(x), ...) +} + +#' @rdname s2_lnglat +#' @export +as.data.frame.s2_lnglat <- function(x, ...) { + as.data.frame(data_frame_from_s2_lnglat(x)) +} + +#' @rdname s2_lnglat +#' @export +as.matrix.s2_lnglat <- function(x, ...) { + as.matrix(as.data.frame(data_frame_from_s2_lnglat(x))) +} + +#' @rdname s2_lnglat +#' @importFrom wk as_wkb +#' @export +as_wkb.s2_lnglat <- function(x, ...) { + as_wkb(as_s2_geography(x), ...) +} + +#' @rdname s2_lnglat +#' @importFrom wk as_wkt +#' @export +as_wkt.s2_lnglat <- function(x, ...) { + as_wkt(as_s2_geography(x), ...) +} + +#' @export +`[<-.s2_lnglat` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_lnglat(value) + new_s2_xptr(x, "s2_lnglat") +} + +#' @export +`[[<-.s2_lnglat` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_lnglat(value) + new_s2_xptr(x, "s2_lnglat") +} + +#' @export +format.s2_lnglat <- function(x, ...) { + df <- as.data.frame(x) + sprintf("(%s, %s)", format(df$lng, trim = TRUE), format(df$lat, trim = TRUE)) +} diff --git a/R/s2-matrix.R b/R/s2-matrix.R new file mode 100644 index 0000000..efa7a6a --- /dev/null +++ b/R/s2-matrix.R @@ -0,0 +1,187 @@ + +#' 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_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, radius = s2_earth_radius_meters()) { + stopifnot(k >= 1) + cpp_s2_closest_edges(as_s2_geography(x), as_s2_geography(y), k, min_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) +} 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..681f3ba --- /dev/null +++ b/R/s2-point.R @@ -0,0 +1,89 @@ + +#' Create an S2 Point Vector +#' +#' In S2 terminology, a "point" is a 3-dimensional unit vector representation +#' of an [s2_lnglat()]. 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 +#' lnglat <- s2_lnglat(-64, 45) # Halifax, Nova Scotia! +#' as_s2_point(lnglat) +#' as.data.frame(as_s2_point(lnglat)) +#' +s2_point <- function(x, y, z) { + recycled <- recycle_common(as.double(x), as.double(y), as.double(z)) + new_s2_xptr(s2_point_from_numeric(recycled[[1]], recycled[[2]], recycled[[3]]), "s2_point") +} + +#' @rdname s2_point +#' @export +as_s2_point <- function(x, ...) { + UseMethod("as_s2_point") +} + +#' @rdname s2_point +#' @export +as_s2_point.s2_point <- function(x, ...) { + x +} + +#' @rdname s2_point +#' @export +as_s2_point.s2_lnglat <- function(x, ...) { + new_s2_xptr(s2_point_from_s2_lnglat(x), "s2_point") +} + +#' @rdname s2_point +#' @export +as_s2_point.s2_geography <- function(x, ...) { + as_s2_point(as_s2_lnglat(x)) +} + +#' @rdname s2_point +#' @export +as_s2_point.matrix <- function(x, ...) { + s2_point(x[, 1, drop = TRUE], x[, 2, drop = TRUE], x[, 3, drop = TRUE]) +} + +#' @rdname s2_point +#' @export +as.data.frame.s2_point <- function(x, ...) { + as.data.frame(data_frame_from_s2_point(x)) +} + +#' @rdname s2_point +#' @export +as.matrix.s2_point <- function(x, ...) { + as.matrix(as.data.frame(data_frame_from_s2_point(x))) +} + +#' @export +`[<-.s2_point` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_point(value) + new_s2_xptr(x, "s2_point") +} + +#' @export +`[[<-.s2_point` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_point(value) + new_s2_xptr(x, "s2_point") +} + +#' @export +format.s2_point <- function(x, ...) { + df <- as.data.frame(x) + sprintf( + "[%s %s %s]", + format(df$x, trim = TRUE), + format(df$y, trim = TRUE), + format(df$z, trim = TRUE) + ) +} diff --git a/R/s2-predicates.R b/R/s2-predicates.R new file mode 100644 index 0000000..45701f0 --- /dev/null +++ b/R/s2-predicates.R @@ -0,0 +1,171 @@ + +#' 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]]) +} diff --git a/R/s2-transformers.R b/R/s2-transformers.R new file mode 100644 index 0000000..ca97620 --- /dev/null +++ b/R/s2-transformers.R @@ -0,0 +1,269 @@ + +#' 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)") +#' +#' # 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)) +#' ) +#' +#' # 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_xptr(cpp_s2_boundary(as_s2_geography(x)), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_centroid <- function(x) { + new_s2_xptr(cpp_s2_centroid(as_s2_geography(x)), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_closest_point <- function(x, y) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_xptr(cpp_s2_closest_point(recycled[[1]], recycled[[2]]), "s2_geography") +} + +#' @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_xptr(cpp_s2_minimum_clearance_line_between(recycled[[1]], recycled[[2]]), "s2_geography") +} + +#' @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_xptr(cpp_s2_difference(recycled[[1]], recycled[[2]], options), "s2_geography") +} + +#' @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_xptr(cpp_s2_sym_difference(recycled[[1]], recycled[[2]], options), "s2_geography") +} + +#' @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_xptr(cpp_s2_intersection(recycled[[1]], recycled[[2]], options), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_union <- function(x, y = NULL, options = s2_options()) { + x <- as_s2_geography(x) + + if (is.null(y)) { + new_s2_xptr(cpp_s2_unary_union(x, options), "s2_geography") + } else { + recycled <- recycle_common(x, as_s2_geography(y)) + new_s2_xptr(cpp_s2_union(recycled[[1]], recycled[[2]], options), "s2_geography") + } +} + +#' @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_xptr(cpp_s2_rebuild(as_s2_geography(x), options), "s2_geography") +} + +#' @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_xptr(cpp_s2_buffer_cells(recycled[[1]], recycled[[2]], max_cells, min_level), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_centroid_agg <- function(x, na.rm = FALSE) { + new_s2_xptr(cpp_s2_centroid_agg(as_s2_geography(x), naRm = na.rm), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_coverage_union_agg <- function(x, options = s2_options(), na.rm = FALSE) { + new_s2_xptr(cpp_s2_coverage_union_agg(as_s2_geography(x), options, na.rm), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_rebuild_agg <- function(x, options = s2_options(), na.rm = FALSE) { + new_s2_xptr(cpp_s2_rebuild_agg(as_s2_geography(x), options, na.rm), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_union_agg <- function(x, options = s2_options(), na.rm = FALSE) { + new_s2_xptr(cpp_s2_union_agg(s2_union(x, options = options), options, na.rm), "s2_geography") +} + + +#' 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_xptr( + cpp_s2_interpolate_normalized(recycled[[1]], distance / radius / length), + "s2_geography" + ) +} + +#' @rdname s2_interpolate +#' @export +s2_interpolate_normalized <- function(x, distance_normalized) { + recycled <- recycle_common(as_s2_geography(x), distance_normalized) + new_s2_xptr( + cpp_s2_interpolate_normalized(recycled[[1]], distance_normalized), + "s2_geography" + ) +} diff --git a/R/s2-xptr.R b/R/s2-xptr.R new file mode 100644 index 0000000..78cf56a --- /dev/null +++ b/R/s2-xptr.R @@ -0,0 +1,116 @@ + +#' Create vectors of XPtr objects +#' +#' @param x A bare `list()` of external pointers +#' @param class A character vector subclass +#' @param ... Unused +#' +#' @return An object of class s2_xptr +#' @noRd +#' +new_s2_xptr <- function(x = list(), class = character()) { + if (!is.list(x) || is.object(x)) { + stop("x must be a bare list of 'externalptr' objects") + } + + class(x) <- union(class, "s2_xptr") + x +} + +validate_s2_xptr <- function(x) { + type <- vapply(unclass(x), typeof, character(1)) + valid_items <- type %in% c("externalptr", "NULL") + if (any(!valid_items)) { + stop("Items must be externalptr objects or NULL") + } + + invisible(x) +} + +#' @export +`[.s2_xptr` <- function(x, i) { + new_s2_xptr(NextMethod(), class(x)) +} + +# makes lapply() along these vectors possible +#' @export +`[[.s2_xptr` <- function(x, i) { + x[i] +} + +#' @export +`c.s2_xptr` <- function(...) { + # make sure all items inherit the same top-level class + dots <- list(...) + inherits_first <- vapply(dots, inherits, class(dots[[1]])[1], FUN.VALUE = logical(1)) + if (!all(inherits_first)) { + stop(sprintf("All items must inherit from '%s'", class(dots[[1]])[1])) + } + + xptr <- new_s2_xptr(NextMethod(), class(dots[[1]])) + validate_s2_xptr(xptr) + xptr +} + +#' @export +rep.s2_xptr <- function(x, ...) { + if (length(x) == 0) { + new_s2_xptr(list(), class(x)) + } else { + new_s2_xptr(NextMethod(), class(x)) + } +} + +#' @method rep_len s2_xptr +#' @export +rep_len.s2_xptr <- function(x, length.out) { + rep(x, length.out = length.out) +} + +# data.frame() will call as.data.frame() with optional = TRUE +#' @export +as.data.frame.s2_xptr <- function(x, ..., optional = FALSE) { + if (!optional) { + NextMethod() + } else { + new_data_frame(list(x)) + } +} + +# lifted from vctrs::obj_leaf() +#' @export +str.s2_xptr <- function(object, ..., indent.str = "", width = getOption("width")) { + if (length(object) == 0) { + cat(paste0(" ", class(object)[1], "[0]\n")) + return(invisible(object)) + } + + # estimate possible number of elements that could be displayed + # to avoid formatting too many + width <- width - nchar(indent.str) - 2 + length <- min(length(object), ceiling(width / 5)) + formatted <- format(object[seq_len(length)], trim = TRUE) + + title <- paste0(" ", class(object)[1], "[1:", length(object), "]") + cat( + paste0( + title, + " ", + strtrim(paste0(formatted, collapse = ", "), width - nchar(title)), + "\n" + ) + ) + invisible(object) +} + +#' @export +print.s2_xptr <- function(x, ...) { + cat(sprintf("<%s[%s]>\n", class(x)[1], length(x))) + if (length(x) == 0) { + return(invisible(x)) + } + + out <- stats::setNames(format(x, ...), names(x)) + print(out, quote = FALSE) + invisible(x) +} diff --git a/R/utils.R b/R/utils.R new file mode 100644 index 0000000..8d6d38e --- /dev/null +++ b/R/utils.R @@ -0,0 +1,78 @@ + +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( + s2_geography_to_wkt(as_s2_geography(x), precision = precision, trim = TRUE), + s2_geography_to_wkt(as_s2_geography(y), precision = precision, trim = TRUE) + ) +} + +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..d8aeaf2 --- /dev/null +++ b/R/vctrs.R @@ -0,0 +1,48 @@ + +vec_proxy.s2_geography <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_geography <- function(x, ...) { + new_s2_xptr(x, "s2_geography") +} + +vec_ptype_abbr.s2_geography <- function(x, ...) { + "s2_geography" +} + +vec_proxy.s2_point <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_point <- function(x, ...) { + new_s2_xptr(x, "s2_point") +} + +vec_ptype_abbr.s2_point <- function(x, ...) { + "s2_point" +} + +vec_proxy.s2_lnglat <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_lnglat <- function(x, ...) { + new_s2_xptr(x, "s2_lnglat") +} + +vec_ptype_abbr.s2_lnglat <- function(x, ...) { + "s2_lnglat" +} + +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" +} diff --git a/R/wk-utils.R b/R/wk-utils.R new file mode 100644 index 0000000..1372a36 --- /dev/null +++ b/R/wk-utils.R @@ -0,0 +1,78 @@ + +#' Low-level wk filters and handlers +#' +#' @inheritParams wk::wk_handle +#' @param projection One of [s2_projection_plate_carree()] or +#' [s2_projection_mercator()] +#' @param tessellate_tol An angle in radians. Points will not be added +#' if a line segment is within this distance of a point. +#' +#' @return +#' - `s2_unprojection_filter()`, `s2_projection_filter()`: A `new_wk_handler()` +#' - `s2_projection_plate_carree()`, `s2_projection_mercator()`: An external pointer +#' to an S2 projection. +#' @export +#' +#' @examples +#' library(wk) +#' +#' # simple conversion of individual coordinates *to* unit sphere +#' # space +#' wk_handle( +#' wkt("LINESTRING (0 0, 0 45, -60 45)"), +#' s2_unprojection_filter(wkt_format_handler(5)) +#' ) +#' +#' # simple conversion of individual coordinates *from* unit sphere +#' # space +#' wk_handle( +#' wkt("LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)"), +#' s2_projection_filter(wkt_format_handler(5)) +#' ) +#' +#' # use tessellate_tol to force points to be added to an edge +#' # unprojection will ensure an edge maintains its cartesian +#' # assumption when transformed to the unit sphere +#' # (i.e., what you probably want when importing a geography) +#' wk_handle( +#' wkt("LINESTRING (0 0, 0 45, -60 45)"), +#' s2_unprojection_filter(wkt_format_handler(5), tessellate_tol = 0.001) +#' ) +#' +#' # projection will ensure an edge maintains its geodesic +#' # assumption when transformed to projected space +#' # (i.e., what you probably want when exporting a geography) +#' wk_handle( +#' wkt("LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)"), +#' s2_projection_filter(wkt_format_handler(5), tessellate_tol = 0.001) +#' ) +#' +s2_unprojection_filter <- function(handler, projection = s2_projection_plate_carree(), + tessellate_tol = Inf) { + wk::new_wk_handler( + .Call(c_s2_coord_filter_new, handler, projection, TRUE, tessellate_tol), + subclass = "s2_coord_filter" + ) +} + +#' @rdname s2_unprojection_filter +#' @export +s2_projection_filter <- function(handler, projection = s2_projection_plate_carree(), + tessellate_tol = Inf) { + wk::new_wk_handler( + .Call(c_s2_coord_filter_new, handler, projection, FALSE, tessellate_tol), + subclass = "s2_coord_filter" + ) +} + +#' @rdname s2_unprojection_filter +#' @export +s2_projection_plate_carree <- function() { + .Call(c_s2_projection_plate_carree) +} + +#' @rdname s2_unprojection_filter +#' @export +s2_projection_mercator <- function() { + .Call(c_s2_projection_mercator) +} diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..83a7cd4 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,72 @@ + +# nocov start +.onLoad <- function(...) { + # call c++ init + cpp_s2_init() + + # dynamically register vctrs dependencies + for (cls in c("s2_geography", "s2_point", "s2_lnglat", "s2_cell")) { + s3_register("vctrs::vec_proxy", cls) + s3_register("vctrs::vec_restore", cls) + s3_register("vctrs::vec_ptype_abbr", cls) + } +} + +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..4d203fc --- /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://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 2 Alleghany 3 Surry 4 Currituck 5 Northampton 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … 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 2 Alleghany 3 Surry 4 Currituck 5 Northampton 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … 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 % + mutate(geometry = s2_boundary(geometry)) +#> # A tibble: 100 × 2 +#> NAME geometry +#> +#> 1 Ashe 2 Alleghany 3 Surry 4 Currituck 5 Northampton 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … 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..c663fde46ff3e9303b3288553621ea164bcd134f GIT binary patch literal 2929 zcmV-%3y$<3iwFP!000001La)pQ`^WH7Y1YVo=b8`xXFtKCO4!ZejyhwVZz;UKoU9- zs3Dy+(H&h&Ypg?-TuBb04E^4}sQ*O!we6qLxzC-ZyV4`GTCE)0QbMj}Mm&$vqut;0 zvd=!dTJv&LlBA3@G%z4#1}S@q{2Gv?K}s)@&lkMUL(&ML^S9-v*u*w+u_jlKW!rjt ze1Oe`C0Vm%({g33=`OudN2Vr8LmB4T;fl8Jq65OQE#_9Yx3<=w@A1Npt=hOoU`)_Y zKBV?Fe?~)3<;YVt-N1w$lKh{aIg!D2Q{^CJU+g`(_mGpzV^g&>-8|qdAQaA;sDT}> z+5CFD1?fNB10$|Z9&k~kNf1ra8!P|#_up>(<)=UMEvp7{f|fIZnmgOO8!uHiu$A}G z6?BuQ#-P|ZXlq5c(JWTTucKG)?#{1^m1;{jwDMwYL0$YBJzQKwi$!Ol*gz&B`xx17 zecwi!-g5RE*u}O}sPOL19BEuHUv}4vk2}CV@{SLXGoQwZw1XvRDCDt$QU9?4{;{FG z^_LKLnRi^I-D4eDxwZsBArtJ`bq0zAXuf`G!6&@9v9VLW%IXM8zu6{XX_Pp23zEe9Ql z!X=o;^HQjC{Mg}=_nq9PaP=83qeE1^{uS5(!qX{9T>mi#+Q!?rwEe)QvEw~mJb^Rd9RL1c~)zgDlN(! z8QEBFIb@|n)N-&(;rEJq43JV<0=|?O^?eQ3P|I*V(>ij^vKiEF9g!Q@ZP})bWOARR zd9`QY;{cY@`@r8%4E!cRic}RhT?U$r98v%`TW+ryCjb|v>CM-%*#E3nFIio zGFdOdtSy?oCrD-v>^66&4~R2B3&0mrtb7gKJe0VFE?M+jO&XY-BIC=9CVl8}8R#wG z`znxxEeBWejHYHGl_bZQZu3A(z?TvSa#lvB)=wChfj$8KLF!>VPMv?GyLH{{D~h*) zz5@Q$2Z`eGcO2}o3NA7Qs63Vw?kO#_Q|J1#(Xm(DkGVIAD2$s#wG6b`K=CKw7=LaRLURz>sj zYT-#irc0xOJhS4swnYNi)>Ut!eu{~VOFrz_RukKn)HBTh{0o!J1M~78sv1~c=y|eY zwywO1dcdcBY7o2-dZg^)w@v%CjUBQobe3&xo>=$*FMBUsemHS0%W#&KmPj#9<@Pm8 zpPQL2&)hkM1}>IMg>q@(QL*x*R4O-1LoR;n_UIrAdzw*Ux*rw1ON3~J0AG@$#+K(^R5q97y6ZNb zrDBmt6)gLp7!~ZbSWaJ^P6p2!h?x9bA@Zfm5ViPuArf!EE2dSoF*+z)P&V7})~GxPoda9-9D23HR7eNyq2R~!R5_5ZuTV(*T=KVUwzZ(>%{ zbx-I6$7zPo%igqeu2K3+9r8Th?klDL)d&-In^W`}HXPAL(J#B>P&GPS(cmZ&D?k2N zwM-LxuV222X){C5OC?_{vnoxWT`AdeOtFJikZ8l;vOgT!`z3vC7=-LOrX8E8p{q{} z?_6N;K4&-$Q&E%|hW5R$5bw`OpTW-;porA3g{Izx%@K}5Z%srG1qO#4dHS<-mcV|` zJ)Kn?@7Q6`{J?lv)wbZEqS}nuq(Edy!QN@gK;{y6pWT0I3uQT>ac{?c_p9KfV<0ys z?C`Lvg|Tz@y()v(EhLjuYWhLT#*Ely-on8#l(xiB#ESm5cv~PJ5il*cQwt-ov0P<^ zK0EV8bSbRhMzbC@kmw3MII2{%H`sQ(4ea*Qd{m7oPOIwpS*(Lg$=bVSMnQyRNCk>9 zG8`+cek{O@Qh86HjVR8sX*C_)32P9|D2j=XRIAbO&L6i$uSSh;nXZc+HduHUPAE+> zC@s5zh-9az%P9@))-6`#k^tMV@&*|#*4@?KnK&AU3CrMGT2r#E^EhPtK5Vg0EvJka6z$?mdlV6`0Zm!-`L4 zX9v3DMs-f1ySgEfm|@BprezyqmqtbwKSFa|v_GsEn&q+kW+5g$Qij z#SQ(BoJVSQ09-86+}jgPfx@C8xBTdL}=}bw%rURsL`mUuuI~-n+P!JK{cskZo1!PZVDEu zZLup61>qEzT&x6CEBb53qT{`@ zZGU@fEc*0$eP>Is{@khb6*?L(@~cRoL|=*SROV*O!f;?;ItyOe;_mko#nbO5+QTmy zMX&X*=xxX|C*FIw(-XQHJ~{DwI6XZ*@fm@R3!oRJOTaIsrsD`%NLdF4^1e)&f!LjEB-{v ze{Y){GTJgx^n~VfPj7+B!Ra1Kw}9VDMP*XAcyBP%3EeM5KK&E%q%6`Z*n*_A1bit; zyb2aEVBtM~CnycCe$)GeIY|0<~+x+?0X0{d*3n(h_UX;|pH-Yum2 z{m0vg`Lek5Bk(__mfOw>p7q_;%Er###`g1cY|%$~!(fQF#~j#zQbBpsy*)y9m<;T& z0y{WqnBd#qP2g`J#cMwU8dItOucVR>;Z#x@dM7x~71g*1T2i_R{AOZ*Ukv=cg>;iF zs7#!a&sTf*d7}*>-U_!tb4oSfwbU~uw>0|L7u8@gl1DzkKYPeGZQLX)3u;Mk74z-C bq~Hf|r|SaheQAX?e?lL 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..bf20a71 --- /dev/null +++ b/configure @@ -0,0 +1,117 @@ +# 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 + brew --version 2>/dev/null + if [ $? -eq 0 ]; then + BREWDIR=`brew --prefix` + PKG_CFLAGS="-I$BREWDIR/opt/openssl/include" + PKG_LIBS="-L$BREWDIR/opt/openssl/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 + +# 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 + +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_tbl_cities.rda b/data/s2_data_tbl_cities.rda new file mode 100644 index 0000000000000000000000000000000000000000..317ea50460e058aa54ed4c376c73dbaf6f94a86e GIT binary patch literal 7074 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 M$rRy2LfrE5#EXDphX4Qo 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..a94b448af10f4dada259311bbdd058c46359abc9 GIT binary patch literal 132764 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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..1d96a0beefe1f22d5c8f1c8a9986a6b73798fa60 GIT binary patch literal 488748 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

ut>B`V~jRXq|e29qg; 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

1N`0+Jd5ik^rN3oKU|%Tz>i*0u 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

Z9snQ`Y70rp$VXr~J=GdFr|v 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

i#5+J)i!6ARx& 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%0DJ)KKY^})|O%MC@h;B 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

1#>6W$Nn7FwaqvV3X&CwRe-{fi9zM-N#+f zedy$GUB+MEqq3Q-&HJ#yE($7srhGm^ob>N(lizSuY1*RolhrxCby4Y>nt@hX*QGYV z;7y?S+y+I{WPL`}fq2h%gVQ<_DOMGUqz$)Y369h zfir1(i5C%ldP-Y@%<7b+johLa48^bZ8yvPj71crZ8bpdE zL_zpIoRgfpK19_(aF&&w;aQl3Qg+ViQ-%*5{~{?xAY#m89(4qNwV-rlmKH_tL;8sV6OQZ(=P7aV|H@a78RFpOauqcIgpoLt>X% zSGqIrQ&e|({Jtg!=vrK5g+E$TR#`c5-34*TUtL{K(ED6w@l!f4#Mi_z3iHzAuqvcP zv*>!N+N{d1*d9~-Xkt5#YI>$GuBvpy8PYc(2E1D^0!?-evi)8cGr>6Lx6)@gC#Su|&+Vm%&!l{aC8v*T6 zU1zP@O4I+ZWm@?lg4XH3w9QOFzN$_}pWo3niZ0DON4-SJ4f>^>9He8i>pyp@*SSv> zlylbAJJVX!7KJi@QBDKncHK9?*u8CvcF4?+d#JG~5o}-O^e>sUpVb)nxvb)s-_mtk?PxMkbJP9hlWMr+n4CjIqo7&=07W~b`1BT z!e<#rB{0H1^;-8+tIGEiq*bxSE?aGu7Tn$V*M{Uh{@eK0vtL=0VsXrGT#Z@8G#0(a zJhz0ky6#Bhw;lsK*lSwZW{|$vcqIDu;oqvU`QD50YyQ-EGudh^@#Vcu`(KEh0hVd2 z4r#qP*Y9KX@97aaXM**=w^pq`v?~i3;jNxb5Lo{=g_eEFmi=;TJ{1K_??GUN1`{I$ z9oLf}-;X2tU%*(g*ztm?`hbUrG6*r!=Ju;WYI5*e|$sRgBcP1HZCie zu`hE7fNn!_q{s zk?3wJpuww-tJ_wabr~%3)Q4S3c`1h7fN<2ufr0O<{(Hv2J;!M{br&AuYZ%$5EpN)5 zA=}VB71rs_jvzfK<`T` zS{ml&DlDU{`J052MYmd$l~EqK#wSk&d1rMq3Y+3kl~m;>qdz4IZOKSJB)%$HL`w2f zifiF{Zd0qD?>uFtrmwEbv+-RPPU?u!A}dbA#L6Ks4&=?RY#nTNjfY%bN)rAPMR$sv_8eLbhN)Ge$=SFCv|-4sj51b*txcP%j<8F?^R-8 zEDRh)p|D&Q^vN!-CWdjDVwkHY%#vvPR`qFVRaRy4hU28uGz-$>Z``esPyEU{ z@8?AkYipEfk4Z@JSH|fBx~Vg7r42$qWl>#GE?A_S=VuuAJ%s&(q>z}D?c+}y#9A!{79;(CJ{ARJf-Ol|Fe~cJ z@GEU1HCv0!(&L}`Y0q9)wCJ0&82Q#VCIM>RX$yEY^O(b}V!L)lglg-J6}7Mne0=@O zVkUUXc z%JnEwaj7S>YQ6)tn6bPti`^*+He!)?{Wj$f+(xJjvU%`9Cj3$FL!o3=ybPV2nfxyb zOh#%ZD-f7_OXZnu`uFOhI|yIX z_I&9_Zg&xpbFI~;;h45zpYC6}I26{}qpHWchIO_r$3L5Gb#E`Wj;}s)K_z{}z-DBAQQEN-<`P_BC_Dr0lINbYr5%ZzlSRbEwg$ugJuPTG` zw#Pg>_wT($ZL*qM?{qb8ErYm-#k=SIXxyfd^V85-7wPjjgR!4DHChZv7Ks_aw3*2D zMS}pJ!Soes;Asm(kaLZnBW?TCUBrO8G1+~q7!=X+CQGuIkYxV0S!zvdAEMTV!Ffnj zS3O6@SrJhZ2;28T;C?HE#b%ZC=~G30_W7TXgp^g&wCy!bDl4NN^P2a`dsA*~jSae5 zU1XaMfiz%!EjogVbKe3IMW`sLg{>aJ?O6nJn1XiIA$8C+?UEEmg{XOJ!X;mV>hqm; zyop%U=t?WD%6>P7+Umu>dys0Y=8BF*3^E3U#nS|=!>eF6)GK5_lW&ujC)`DT=p$E@ z3|fwywMe#61K(g<`x5M>(%GhctX5X%ocmR!Mpb2C*f;#bB9xTr2$xiE3A!lvnFe_p z+n|WHF33pr70h$_(^YA^Jj8UZ5q?7wnf1W8n4Sg z#Ih-^h@_!-&Z-hsGK6>_eQi8kKh9~^M-y(SaS65kwpKhw* z);cS<%nJ&s!nc&LD~yZMZ_X(z7dC}K^*6~RT4M1H(nQBGH>4++q{573mXF0!;?P`0 zB^@UPQ%2?erX@Dx;ZK;uIvJ2btk!wmFHu$Gf@LShua)@~pIVCg{+6V&Y_`~?8z;8- zkmu+=)C7WpokMckl-XxwQBKoEc#ZkEw!|vMw1+y*ox=W-&TcBuShf|luxoa7Q}=nY z{$6Tzh5@oGy8e}&LdSsp&2g-4OIV%tF9EhR29DY7X%NjgcP(6dLtST$34?aa?NNHV zjQx!#w$AFSh)VfY>kiGC+jnNm#oBD>@(*#YE1S1xj`tc1)m);*`6_t5rKMSkY+Qxr zXyPjmT8e^zr6cG_6NJq&W#5AEi=-grD18&0f7YP>tGESSQ(;SuXG@=B_&J0s{2k8E zkYSdg9n|%DNTrW;BBLWw9+CzTcV%`l-r_B@W7I@6L<%)~IKnf9^T96-$?-9-;;oFU zzfQvctqEM+4^-`wdlthho3fwcWXQ#|Y6=o|y0mPvC3r!AYB(zEbc)T|ypH0eijiOt zo=Z^J_VEPfZCjs^{TVSZtU%SY8#5oU0P_Y`M5mC^Iy9+p2jHOv%$%t5==a!B7w?QjDmS!W}mBw zr6Qiv@{?6~s)GXB_7Gq4Xvaf@k64)}zM7~hlK%6g-1fEODaq-1R-g02?5%DM_nA25 zE?NZb8q!gy`Xw13m|vLp^AYR)3Pp8Xr1x4k{pK#pisIcVPi0PgZYnMOrpX48y+tg_ z!)C@Y2IZ%FbrqqXMX#&sITZfU-vS$O(e>J9SD=(eFb{^3kZ!W5$Exn99CS+Vv(nzn z&vo~ZX%M>%wvnQDorSq;ULLxRqNq=SHgZ&*y|KQ8zN43R4MS$$t64!_UKHtYPSVP1 zxW+l{13cbN9t5K{<|fa{#$8dZBVOG^w!DP#B-OC(4Ivr-k;{X0>$xk-b%lFOi>sSW zzD$%7tu#L(e@cQBD?b*Y1$_&|N-)kUm%TPLbRM#2I#uU4#*p@7VP|t$-CJ9a=9m^q zyT6Z;j5`;jBN~KiSz*#mvHQh>98_X)qWR$+8u+zShn6B z=CrRgpQL1ee zCWrIMtLXMIM{r+L-+L+B*@*5qs;ltb^ce@5^_$kC_0BTy5o%Ij`Mg#U*?bGIup^wG zH@Tg7???Q9NY*X>zK*=@U`4rX3aX2pEl)w7O<(PCAj>)m0;-8unAFjrMLbtY@TAjZ zd(K=Mr_!7~&s? zPa4jf#%NmB32IlD)U?_7wvU-FVW*4*QAGRW6J`rV#=ci?4w%Na&@Xi zzJ=*Tehw2(&bjM@bh2^|@^@`T@#S8VD%Qp>j!Ty0qf@uqm2TEF4MW^)RmZH9UOSK7 z&wFa6(Vh1bMiE0)J@4&mW8d@ZW*%khSmUmbkJGCy3v654y54^^1@EeLH_tM4)MZgZ zPgEpI;d*0k9!s~`vQ+;`C;ZMmbs^-btSaia%rNo`6R@f$&(VRMs|!m4&0B7{7PTmH znMTQ&{S=S2giE?;p5qGYKRQ}jPMtWZnq4IL5I*Ee?(!I9-G@`(3n-wYnC5YYU6e=0 zm%f!1`{!LzSCtLLP~QX0t0f-Z3SC2OUG#cMeQ;A{l`?qy6iRNRBlxTW=CMDuElW#6 zTa?D(JW%TLksBv1)kUISL+y6gC$6B>)>d7XPB0!yw2f)f{FJjE&96wtgQ#Bb?!#XD z%+i?CTN4V4!r3~{;kre49|QeOU*gWfL?=&S)p!@=Ok4S1p5#kvm0ytGs&ddMYYH_Q z&^9YwDjb8ZsUUy+!#r+w~Phv9n$l*YML?o@&6JCQVnL1+k-7nMFDM zx~=l`#XC#$?PL~n>va~#%Kn>as@B@BuE*N=Q$}^E6eUeHRGOH4X46>lA5#dYFM6wM zR_iruI+*qu@3GWV)m;)^8-3w?DJEt)LQI)=hl7ws*xHn{X2hmwxuS%XLVd1cDlubB zDT?D#u73XtMaFu~%j)?4?EZPII1kL5d3-DMT=XPCSDm?gG=$F$K) z+w=UM>?k&p(Z7=5{vHAa?0x2T5GJUN6+Kn)M6p(H4O7Svs6x!(iz(&C7W}N3Lk5J&et?Pr>3?-^-lZR#gR_#_TpW#`BeGdCITI z8y!j^>3w=aPvjYTIY=J|hmsRXh)2^gm(KD<2s6hAdnSY8L(`j+SV ztvc-b;LxlpgKD6;ZEEiJ-Xn&uDvGnsdT-|3kBscO+IKnPjN{Eu+BbFKO4nwO**;A> zcBZ}FSYMi3y2-7{B~iF)Xn6L0Fqc~>H@1yI`BE?0{l66*+omU_LF}jR*|%;Lg$?9L zHt`8|b)!wV>{5cCw{4nsue{Y?B*i_{HnCGkCQ%-0+@7k5W@&v)D{8TSbp3Z_{JM;a z$cvH6rIjuUH_}9gaS$0%4dTPTm3KE>3Gf&Ng zeUR0su-7A4*M%2xUd_sq?Xr!#R;VZ`EW2Lyl4_T(xG!l$VmYOHkM4H&W6`Mx3Hv5T zqS3Q9UHnsswlyT>PgENTSBjh_@*Lk?6_b4_>*xC0b|d3h_6)NbI&IvV_6h5?kD+2> zo;uvwH~NN|J;uemlx1~LR#1vVAh4*)dbGT!lJ26as4a?Ga`+RKUHZ}!#FG<;byY`< z-LA|!C9-6i)cJN$otDw{x5>*~q^sIhsp4*OX&Y ztTm4kn)}(7i0rhcrOFC-I}Er)3R6^qkDaf)+G$K9T5F6JDJ-Bn$WME&;Bc1(F}Gcq zoImK3V!Vd66tga@a{Qye)`d!0(qGdwj(Coh#bIU8`)?tlcT*pQ9g%+zN5p8CG-|TQ z#6)78m3?nmkR{VKrB+sz#l^K+A*idTuboEC5`IdQgups0BT%k1=DGip%{$LcFsPK_ z?DtuBvidcO56SxGp1O9ut%&7XWWpg}oQ)O1NNnEVAw|qhGs2S@BBWyjvxmP9xT8FVvGRNq^#q~CJ_N9y# zE>U>-o}oVBmbSPEUMxc}_dErF&Mn1gsU9&Ab~amJ)EWIW8|Fs<{1CoH?i2A%-&&JJ zlKvQa$|fc&^`?KHQ<(}4*5$B$7su(ES8cl%U-iz}u3$NoO+DFc3>Jot4e)mwt3l#k zn?r0439Wu3^P64% zROe>d2~1Ob3X^)zD2of$c@mrzZF!|K^G^VKK!m>~FxNfDRn%kN%ivcPC3*BPN~70U z8404mo2V%lGm@gC7njntBmED_Am%HK`(lfH3^Nr_w7Dzd-1QT;+N@0Qv>9dLfMQ%_ zL1s=9*W2bj-As!v#I%pCSe&HW6y3j%c9cc&0x`Ty@^7b6O>X+G)-JZPqF3~aX$qU7 zVAiL~#U~RMsw-ad_3kpGNYVQKjjS)o%*aAq}42| zQ<<4%TQ&I>$0gG@>cOebV*L8o)(JlO*ZZYaj7u(w%4Eu<)5XRj_Yv~6h_|MqCROIa zJ!e=na^~-{XiXy&zoRh?R5!Qlq}b%ct&3D^Djk9Os7eaV^42wBP*YVE-5C_wqMkzP zlhwW_byHH(5A8~9A42T--^FX~Xw~Hb>AH?XE{b=RPyC=wm4E5DSy4*5kerqesj|mUev%DVFX2^{#4JDYDLx)bTxvte^9dcYMt5slHvQ$|} zbE#>zwDq(#sPXhYB1?MHtO{jI2gR6AGIpRqMTYOPA5oiFS%pRLBy~7R|45z z-Ex+};Wyf~Wt64`3iHKZP}A4s^`CkrcuZo@);u(&K@#AgL}V5xSr${%Cg|vwl(h&& zD!_l0X^2JQ<6u{7IvbjkP+^#L)_{WfhMA*5erEBbt=Q0|R~O2+BsI-lCnSZ$ArJLQ z#Uh%s>~dx;c8A1;#~ha0)}PgC!VYmNN*Veojz_%|&fb$C+}zVPT*R-^(3^w$HRX$2 zqXe&Q6!=!9ltZY4Eaj5sw-#Mj;EGc9vnPyeE@f4yLKMX!#MJK zN=m3T-ijlR)!yrfem6$PB&hkR6mv-??GY?81VvRjiryk`)W!uAMA-AooJ-%nAfX|~ zFepwErwR173Su*WmvE7ZRKnVZy`xiYLd1r-rRyrWMeFjhYaL?7k-85aTWRa z%O=X;T4nzt)IObmq|U#3Z1ygHOS}5Y-hL+@{_F~y8?sCMqtn6v=!ven{LgPf;y+_Z zU3kxT@@Efm!P#W%i?s64Xzgjk^Hj?xOt|t~V+N}S{o{MtYZLP`c7o}btJ8!}0*b%$ zU`zF~&&;c@=lW(qUAc5jbumaTXt{#t6A(a+qU32Rw*O#JC8k7RGYIk>LD=3Uj!tN} zdKzRZL)S+mQXgF!X%2~3|6@_48LQ%yjO@5*wb3{vE&(GhE+ri7uZ5S$m2zg{qlQ!cM4(aiCK7`MdITO!85hJcD~h-1*cMu?Afzu?vF z7)=FL`W-u$mHU2oFD1j;we4r>^-DD7;?`V3;+nKMBz`xFy{a5r(p2LzEm2mIh6xMD z4w+vet13-Xb%=YGtVY7nrHfdUH)d5eM(~CO3RM*N85cmZ&(Dxb`4qx1Nu$kO(G{ax zU|x^dv74cGKGG0E2w~hqm4CIxwBb_5IvOlJw}T@e6uFK~DJ^v@Q)a|1V-_tdNToK1 zET5$mr&zSOtA%iGuEcteg>dp+pW9kH<4D6*?4ZUY281AlKi3rH7^Q}YhLY4~lxN)} zubT7uHPw{iQB2_nmgN$>Mza*}9BVvVVa+p?(YP~dYmU)%RXIlRtMaUHs-}!G{oc1_ zy5mz_?5vZIZ;!!Ndf%57M~syvn4A+%SgDs_pQ&35=GxuWT&9qdwnFM1N@-7-Qd&YO ztTgqCuTlLWMOAwWszXmhEY$sqzSL7ay3?1*Ys-9Z(%lLzXe$z{Wr`ANQl!3DOsj)p68pX9oUP?M1h7bOto1at z4;x?p&3@wM1@W|H4OUaF4>oO)d232#5r5`=sk@;qN@%1ao4O?wq!6TUXq%`qh;0@(P6dcs+E%y4RQ^vPVzh>A zXiQqR7$i~cp(@f;hI-d^rIc8PMh8I!TP&G&yqaK;`B2QRr35!eMI*jLc|o$n^dXW$ z{oEziF>XbHSr2J6NeB0B4mnnAxX>4Dr6FnAe)YhwEQIOK;q6Qx=Et zagL>?*kN(&^*p7Ca7k(i&yi#)_9Lr>Duy`s3pro;eL-jHyc=v7`F3^|!UOD%umbA4@x~ZmB4PAj)s}U6M zP^afbAE63ttP44AXi5-6C^g6U#Q6oe3ks6%2{!Y~Zk z4V|(-68iaSn?tqy8Y0cn2I9gYB;Ca!4e_t&NxVd|6I#|g4y`vj-?ppE2%{q(OQ5l& z+jrugIf&Vkx`ADOt+aL}(Qw%3o3qWPirZ@c%BupPw5~4oGItcE5mHWGjFRI6Lf%=D zX%`JfOe7Rlb#_KSb-hzx9$OV=ZC+bA_ZftGWMb81yH@b3?@2)Dzoc!!Lr5m^&jZSC zYqV7scHer>)s0VBCFanXEeIJ$kfr}tHN8jwnKv<*+nYz+u%W1|Lx}M_#M2$RS8EK# z3;jLMEeE`@sVbFe1#7(D)t##PS#kdn^kr#7xRu_O0!b7nHm0KhzwTeL;Vz_Wugjvia71PwUF=ksWKoaNDbLz2)L(-+65vx4L%l z+1Vs{Z^bde{Caw4`!i=F?Khk5SC-%>hfUwH-eTt zd`e6>JB&Kys1Jzm9WeupW&v$gU34A1;mIJ^2pu5msLcZUxPvYpmKZnTLtu!lQ?!hcf zddBivm$6-{4Y@!)h%snvZp+^_^&&U zfhwE0D7V*0X`f^1jeqT;mjB4Lq1j*j>s#)@*sUrr1#oZGUzUcQEHMxT6|)VHfP`k^8nZ1r-FcJuWk^NxA38K zl;q7oTURDN)AS-;l_?Dglu_0P9VnSC3;j_8O8xKXpXIkv-!okBxvEp)PikqS44$p& z)m4n*CT|r3;Z_{=U2<74%tXG|Qy@Ur}5n{bZ9^6j$4F zSyS#a-hR9KMio0w6V$5~%`Yw7o+&gxXCkyn^?nSpMQ)@R2o_7-=-xVMUX z*nDeZ6Ut5!4zgx%$pTaGaYgIJvtk6G}fnZ$Khrq2*5WFeMj`r23NwazB`s}5s z4^NP%D-NnDuUnl4sYCu=(w6br7j;Hc6V`!KPanFK8+KDyUW+E9zC^}%%JGnRtzv+x zzD!gQo_qOdltuMIuegr-==9cX>IHq;SnNwOb8%Q-3x4`&uCjFNHx9Ko(y>tZ6!r?i zrs8v-dTgUTlm}`{>lI$uB zx8AxTU67fV%a3ncO_B=Y^q%t&e%|Bt{oViR2fp`FQ)UTzsWkDOg+_}J4dd;doiAN%TASo*XP`etG?dgm^zT5ziFGoGsoiRJpsGtq1PzfX9$f92`f z{c71o!I<+FXMF=s^Gu_{@mi}Nf2WOk`y3O@UKo`n=r}Bj_~dDu2GT9ve9T8>4Ua zQljpv4Z8sI(T}m=Ro>#V*Do;><;@z5X;&r9f=0QS?6Q`cH!rb8YEjEi-`B7vYV?b^ z@tubLzQG|U&sE8Nh?ea#eG3DKrY+1%)9AG>^Y?QVmnBf6U)awJ)S}J$Q0y%>55
F%gyrm7nZC(ZiyJFb%OZIcrWOW&n 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%0DZ|X6pop9%Opc!eTl=O`jFG))o^2$1aqdesw+eY zIc(bQ)hP@!w%aYQAwqCdEF=(r6Xf|L%$d|^U1#NUK@)0n95+3DMASS8K3m`;;x0uv z&#weN_k(zI9*O?31QQx9hL}NLiTWQuUKmP(U6-{!p@SaLSI}2#|Mj{mc7le%E2`7} zSru=xWwSvSbywsN@oP%5j*0RRcM-qy(e)5aJshUa(#j%X;{SaIs-CloDs_)p_z*8> zAz&)M1K`f1(;rR<%0EV#)mz@1Wb#}0 zWjm&+YbrS^w!H>Xefj-{+4C;w*VgGKj`~sZw+*YN;VW(vp{8!6QcvNVX4?jV{4=%n zu9o!I1wlhcy7zxcQ_a3LiG@Lkb>EB5eb4-?t7!zLH9bpGUlOjPGj>;$qQxwU`YEJw zk*d4?a9atJKEu4n%S}~WD2sdHdT)i|cdc(x-+3()2K80g_Pv5jrIaVg!>UVDsP$B3 zQh1^y(W(FTtfbl$Y7?7Gx}sW!O50s=)o&RzRo;Rrq|lb`G9@6x)?d27t*-RHH`Vp$ zxGx=p;YloVKN4SO<|d3a<7T&|o28`|wlCRMeHfxFEdyTGK1;4qwth+rSfRb%+BLdJ zGz=5YRhjlR?>(mFI^MbpnrO8@g`s;?7o;R|O?P6GmeC2+QL$ZxZWBuT-1WhrY10a0 zKAOJ9SK4+O)DO*ic)f&EM27j1jI9MYTNTIH_uopW_m}2*J$I0u#{o!uFWKy)*@dyy zO;Qc*CyMuSRoVJ*=&-Ge?o=HY%$u(-IXPBUTvAy)kfkR#2d(m9m=hE9fRmo9T860Y?xv$Im zHQjXHt4hPFjM~1Xz7|;&{$JA=p}Vb`i0ayUtRq;kGbKsSVr^@~7>s)8)Y+mZ?GhzH zeTz?Eihoc*}Dzt3UFH z$Ulryl9*H9^J2d(8%f)+vNn&Y5ldaF=;tNImhK?EL|t7*6*@Z)i5BuqG!--#1P-%Q zup#Z#t#Sp4yG1f9a}cOK1Zz%=Uf}gxMysMS%X8#WnZ9Nn%N+O_!tKOi+xr~x-|=hP zZ7n5-v7)c#=po&;rQ`2ut503HI7NQvSd%w#%o4cdX{cv!A@{0wj@FqK#f{tmqjsbwx#AH(M!^1{H5BO5-sD@Zl%;j0`uCtQtk*6$KPCff`FIB<&ukj1zMyXYu>mZ49RhCgZP;43obrRj^ z;!*Dv0wTPnXQ0l~;QJO=%U}*Gw?*?|V$-G4IcHq-G`4oo>m2g}^LHLiGs)|&3rKSk ztOx7bK?`z@JZ7O!W#7IP2K_XWi2NDa%Y=m!a9OU1YBT;fW^B_{bU(@IKxW8x)jkvg^+Qu=YuDOQ7+}E#>Zg#u-cb9^iD_Tr5$(8%yD~(i zRUa{UQ=2wfH1(5>Y|RlP%0cBjiyM6*vp)B@>s{j&d|l8s&Z$>z*|k;INQOlVh
_ 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

oqtMXxCR})0DviTi(RcY^6ljSMzHLNA=1V#hz zN~KtIipbWdiDa7>V@^>mEB<1a{{jjmc}$ag2$Xv8nW*Y3pq@VzjiJg>Bwq72&^VXY z#WG4Y>Knvh((*@=&n4XsI&6zo5F?!Q`Px<+#@%jjUz_Ax>TKf{#HMX>b2xpAyQ=b# zk_DBkUR8xT>pjM4XJ+#sraHP&lIkj9c1O&bh8bK!Y3gelIoMqlXCJdupSsZO=8AcMyK5SDpMnqPF8d)b)dbM{O1r z4WDki2Qr!rfYHQc|kOxmMyXrIz_x~-0cN6NCO zw$;%b!??F@-_qSonYih~ zlHw(sbzOFF?T(5S{I_jY{Bqw7`@d%Kdw%!CntY%C+c;36Vmw3=OHzFD-fB&CanmP( zQBzT=>i)bv#r2SBT%^gIV5n@+7Y3rPr5>x=m3=5#^^a)CIEU@^ep#88S6yljrL^apQ*W5A=E7#r{(a8A&s{t- zC$l`4+117k3!H?-sWBNh@YnO2w@-5f9nW9!QCxN_*WO)Fp~I%Tc05bKwJcAiQBI$x zep&j%gBFReC@M3}Kh_PyHcrylhscwc;`CBWXHlS~!icT+>GZR&n*cD2|#r!cD(~ zTYuMs@N3`NEyYgv^==Y?)>yI?jY6-b@gyFK;q8BoOC%QBiiXBJsxm>9ULNX>ovR9SGKqUx_aRsLpX%49RjMdHq|Jy?WN|6o(QkBj zO)a<8Lsl7tf;xctU(S6_n>-4{yu**Cf2@o4qSwe0-=kEU#d!+YXtx(kuXWT&BKZgnLJS*?Smup^|*=_Y{o8nW{1(tnoO(hjY zEh??Df4kLuPoc4A+UMHu*FN^W-e}jgb&XpaH_pi_PhIw{sseKQ*mQzw%j{m$BJ&

A7eW+hg^bHc~A@Nxwwzl4$gLY?Vl(SLp_6 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 LrwS4R=j$=Rc8_&l literal 0 HcmV?d00001 diff --git a/inst/extdata/emptyfile b/inst/extdata/emptyfile new file mode 100644 index 0000000..e69de29 diff --git a/man/as_s2_geography.Rd b/man/as_s2_geography.Rd new file mode 100644 index 0000000..729da5b --- /dev/null +++ b/man/as_s2_geography.Rd @@ -0,0 +1,77 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-geography.R +\name{as_s2_geography} +\alias{as_s2_geography} +\alias{s2_geography} +\alias{as_s2_geography.s2_geography} +\alias{as_s2_geography.s2_lnglat} +\alias{as_s2_geography.s2_point} +\alias{as_s2_geography.wk_wkb} +\alias{as_s2_geography.WKB} +\alias{as_s2_geography.blob} +\alias{as_s2_geography.wk_wkt} +\alias{as_s2_geography.character} +\alias{as_s2_geography.logical} +\alias{as_wkb.s2_geography} +\alias{as_wkt.s2_geography} +\title{Create an S2 Geography Vector} +\usage{ +as_s2_geography(x, ...) + +s2_geography() + +\method{as_s2_geography}{s2_geography}(x, ...) + +\method{as_s2_geography}{s2_lnglat}(x, ...) + +\method{as_s2_geography}{s2_point}(x, ...) + +\method{as_s2_geography}{wk_wkb}(x, ..., oriented = FALSE, check = TRUE) + +\method{as_s2_geography}{WKB}(x, ..., oriented = FALSE, check = TRUE) + +\method{as_s2_geography}{blob}(x, ..., oriented = FALSE, check = TRUE) + +\method{as_s2_geography}{wk_wkt}(x, ..., oriented = FALSE, check = TRUE) + +\method{as_s2_geography}{character}(x, ..., oriented = FALSE, check = TRUE) + +\method{as_s2_geography}{logical}(x, ...) + +\method{as_wkb}{s2_geography}(x, ...) + +\method{as_wkt}{s2_geography}(x, ...) +} +\arguments{ +\item{x}{An object that can be converted to an s2_geography vector} + +\item{...}{Unused} + +\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} +} +\value{ +An object with class s2_geography +} +\description{ +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. +} +\details{ +The coercion function \code{\link[=as_s2_geography]{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 \link[vctrs:vctrs-package]{vctrs} implementation, so you can +use these objects in tibble, dplyr, and other packages that use the vctrs +framework. +} +\seealso{ +\code{\link[=s2_geog_from_wkb]{s2_geog_from_wkb()}}, \code{\link[=s2_geog_from_text]{s2_geog_from_text()}}, \code{\link[=s2_geog_point]{s2_geog_point()}}, +\code{\link[=s2_make_line]{s2_make_line()}}, \code{\link[=s2_make_polygon]{s2_make_polygon()}} for other ways to +create geography vectors, and \code{\link[=s2_as_binary]{s2_as_binary()}} and \code{\link[=s2_as_text]{s2_as_text()}} +for other ways to export them. +} diff --git a/man/figures/rc300.png b/man/figures/rc300.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb066a3628469cfb955f2b00e3ba877d5be7d5f GIT binary patch literal 15702 zcmZvD19TO z)8|ZA{lb(KBoX0o;J$qMf+#H|rt;+r7~S8t7!1_kD{m#t!`~a2lZvFsm#Rs;WJ~y7fO=aTu`ZpIFb z%nVG7|Gz=bmS+EJdjH3$f8PApuz$7sFJXLtO~WJV2rzcGcT}^tw-FHj$96>0w#McF z6@am+J0Ii!P4T~G%J&aT9z{n>z+YYe(MW)qkMaNM`wu-I<3H;DZ|eV-ZU624Yg_@i zzmopjQ37z(3={l+-A^VhCamTLcAg3AhcDjv*#5f7YfXU*fnb_S93gT=9NAK%fOaPe zi9+Nq0)xa3ksW?|+d6hNuejcG?@b04O9ulRLljPO*(qoGtcS$``89})73^z}X`CF( zT4(d?`^SR5tH;IqN}W+av)RipmxWT*Vuef|Z!6oSXU`VxG1STMm6fnV1|P$1_bn&A zM=VMhodBYz<05sZZh|agBmqu9DWCL@iaUEuu)nUca>Moz}HQ>|gkqnMG!~Wn| z;GP@Pr>B~fr>FR6NvG{qxD^KYWUurtnR~PZS}NQMF~?`y%*2I>wp%B>cDpxWtoGbG zG83_mr(-Kzy$egP3L92N(78;neVPc2|IM+mCW#RV<^Fz-RYhYgSx~ELRw4W1W<7Z@ zP(--3<2nPk3~#6!mPKDirHoK7fYt$ZR(T~KU1^P{sb-L(yewBBpJA^cH3Eru_pF^J zBjos?)xtK+NnCTk=QJKX7`=~Zw9V`JX5eh4z8>Dr?GB9-<^x=xb;M=8(jWEB`J)5X zhd_td`;YLbj5E?i9uOOP3}DqE{>N!-~^N?G6_{Ci0y^abU4ixp< z`HI0Fn9d7`)=H(Ke3e6pq+QV_hL$@oJG7A-E2s7k%eV7r|B0u-6!6YA8}LNPLQ_3y z^N}j&n(_&kPGp3*e7z2cQMsR8m*)JOxD7|f6KPNzHyL!42^t&fVMXZmoZm~~B~y}E zu?EpX?w%Hmx_~7C<+`Ju(hB1=b zbjvlX6?~3F+3}rW?ajvF6jJ={5VKqn34Nqk(xpGI>&C2b2T9hv6S|8N=U?US-U=39Y%DqWGj>WQ=BUTyBD%Ud+rA#LoP^laj2`mx~R z-|!mnmhy6Nc4GsHQODOwafJx}+9rH@yV4OuEzCdn>zFG_fip-|1~ zUahuYG+Rd;72ky@*Ph)LZyDS^9=NA)Kudpi*P8FpYSI5$Ai$>lpLEKD;R(4dXYwQu z8CyPbAJgsd=MRyZF8jZN63&nhKOEnJoG`QbjSqHJ9iJM{a*x9Q2{^)7@SSZpL-OR= za9RfVX2Qh8iGb_sujSj&N*b#IYQGI48r=T<(gS0`+1@NV)k?77s6(|{8+N^W1431H z+R8r?_2^bMJ=tNZUG#JFMzsx|e-{ev$*8S`LN#786DnV~4*Qcpe`ES~2PnKl_wkF( zR*X(4MQlGvgB}9_Ru1zT0o=+RnA{Ey4mri@B!vS;_bs={^3qR}$y$OA4Tn&SwWd2m z@~-3B=P3Y48#V!{jD&@=6}yKvT*A~omIy+n9z{SuT3=v z-z_jLJr}SEgCR&Hc?Z}{G z+x6RMG7GC4JtfrmsXcMdHJ8PPhyl1KeRxWI%B-}lpm==0yHI#|3^Nwwv0A)AK2U?u z)`4#EycuE6WqDYyR#T56s4Z_Sq)(ry38l=mTzPQR6d%08UZQ^)OVw#KG~9|gQ<&3-da*;yixvT@4zO(qZGg#JG9U5z*)#0D!h#{Jo0$s%LE*R*uHs;aE9uQGp_u?4Tl+ZnhN z3S6psd;^TofcHq`?YVukPl&RZO%*Af)_voEZrOzB7P7UOSuozX8q&Q&nz7opMFHPK za^C=9s_3f$$UVk86FQD1LP3+2?%BN`IlS9ykecW-#e05$CHM?BE4yOc7Tu#XyWMe+ zdAbfz5SL(5QiYnqX@9(DPrf)i3pE5+r8KUdhv!!^3DqiaPg6}T@qDw>B>m920!7$* z@eTzC67%nx;tBNUS(Oir7e4mla|oO=nmCPe*IJxPqKF?6qe#~tzs)`-pr~o_psLVI zIqXPIwnBj{mL|bx=!xxEL9o4XqpRkuHthbW4uZWTGTasF`leSMdhBq!0%tu_EA?88L3cw4M)ScFm{8R5{?(T&^ zq>QGf#R+XnUh9vJ_@~z4z8d?54A7-&Sr8D0hBz}3t>#BF= z+UUKc*z7Z_m?UT0_TDG?VDx8`W3pwoRsEcb{@b_}Xx96?W>KKbPyWe~wB4j{uPK-0 zg!sOjaoJd1k`3R~pntXYfs*k*=>=m`*Sp&9ccYs&SOY2qpA8Q)pM<#LE!x7tb7_E> z+)Sq5a-4tV=Ok320up5jwe?yLTZ(gYd&!74HuTy%f{JTR8|NSvQxPJe)0Wr;NAyq5 z2uIuUI&kK~xbtDOv__T{=VZ*WncGA-vBpOGR(UBZlf)rb*plRh3>JM@zHG2t?6hAZ z8*RrYmCM$InXZ&5Iy5-4kI_y_w3hvVTp}b0RHnUR?7pj%XVq4?)8Jyi55IPBJ+Q1rX&-zq*?Wmv*gp5+__{ZL*V5~u|2buYLkdlS4B=LT;_ z9(6MUM+CDty5+99_AX0n&Ge)Rt-#GWH&HrebCx_=^uG>aLVI3P>ck5qWfyW9a9lHFLq(KT^RlR?N8XGmc2nN1dA-P%^Qq2o%^2-#vQT?*uZ$nc zqFst}4bqlJ(rsFG9-MRgD6s;ukOrj?yjkzko`A)xiUUn4j;aH{#oDh-^)yKa(leuJsvEnVc@&qWch%w{Knd~>hFj}Hict=8GCH5TEgqiWRCw*Mg(WEE} zn}deGtRg?3bZvco9Zrj33&yoNoZ)weLk?Qi?Bgj(o8$8Xv>r@TTU*;%jOe!!l5Le9 zZq1U8*5NFI8S3YW8KHg#TYG!Lkhq_}&Q}*6-fpMT@9C6TZb^CtjaEpW-qQD#p6?S4 zeoBPfiX;)g`K5MF<=HgED34jqI$x`3NegaVt%uOkts885f_SC8zzZXWhTc1;C0Um8 z2|U4qJ5Z78MN3R5yDNXkcwfF~XlYOCD?%JchoboGg8Nt&^^MiRQaSqYoN|-*4ilZq z3;)#bbNhS^w4p!=uuyH3Eu^GX^(i`!JK#N_>mAJ#MzR5{@IBn~8)&n$vxf$L%9({I z{CLZQ`jism3&ik2zkfyJaYKx{^ZgOkUV@g2_^WymWN{!BRUF*>RKI=+ z`AL5wvz>g38J5bbxF!zo=8r$%7;SvbinCu)#L>=FjJ6f~0q!$z0 z$bZfwmNFQ>5gLAZ_}M4DEzARW?$gNsPyoRN~wSsapo1%;wsXQJl&_}Q zCHUpig7^$ok&hViqGv3AXQzFDkpPMG(s6sO=bF!w42hPsT%}>mQtz1E#R7kF%_-vn zvAbrm01jV9)0#h75W<=Q?nq~=xy3{k;0zovKZs>C-Z3mk;_ehOtsZ3{zcR$z? zr;Z%%ZFrRt>D@v+_p+MZc09uYRzh(ul+Re1hnMZ@QM(1)?^8vw7g+Jp9EBdp@@LSoeB1$h zz#3UmD6h-MxJ?y_#Ai2fTdvVS?CEZ0bHeyY5yR4x-h_}K2|K()n`s}g)VAz~!oK0SP(5}o^is1qN8Bw^^#fD-EKgfb%@1mj&@ zbRI)yo68Kk+d}dt)oJf_Jicar#qyWoVQ;luua57NWtpM&UBG+^5FJ|~b_V?V>NDOL z-cOD@T@Ivg;7Ua#)>c*wLM7R~^Pg14{YG^~RW5*Yad7||R5mMHo)yHk4$T#DlzfZ? z9Qns?Ow7?}o|nL%O95)ySzSEstEwZD^6P1C^ zDk}PVmL(H@wW$8Dkk6SoJL8G{Ny0TSE3q(_l~kLGfuW4fk2!|g!`J{-{?eEDtAodc zS9uCT3v$Al>~=W=ZNt0dFRze64YAf=r^=2p=yb$cf8GqZ=2a}twj6w)s*~x4_rhQ_ z*b}lo5%PNM^<=m&0N(g$w*)_ZWyo(JZZAR`Mvn&X*_b}P7l&B`u}%#*yYsw<5~>)cCp=&XlSHz*U0r*xeG zWLZApFdWS(zuGGEj0DU!o~E{iyB6AdDARkaDmzF3l{MlvsITtL3#i%ezmK*hdehcp zpPZm=FA}jX^~8F{*6Q4#W;no377&jY>}cO8Nq2gu^RzRQkVsisWsj@T9#c)Pvqcp3 zp13V{UAr!2bG!0qoJZtVxpIV1(r`^xr!?F4jg!kCm7AnetJxmwmxcG9Mze)!jI%Ua zyahfphtJ&OVy@n_DKu|&5CnA?QW__-{QV6rMDZc=C=v4pLX%rC%oM<%hkJ!EDtZ@) zMj=gCplQ;7T?x{DYdXyGj27oL28V7|4%)6NR;h+aGW9zTC9<{PyV9yunG71nbDgF@pE9S;Af&lKMX~A1!j+`cZ(~K#o7B!&`_6ICqQFj#k ztK)kf{Vbq0_%z#4EKmqWYW)wZzEUIgbIv-)Nf>IsxX8;-v|fQUHNa4koRAgO7P+!G zzkfj8sFKz>v6(>aW#5FrH_*}I)sn9epTWV!{GQ9r!YGYd!5S-T~c_?tt>gKikfo*Cq}VEh})$N5$u zxqBaFxj!a#N{WC?ln*}HEw@c{Ig*bi3e*{Ep0H6kJj;5zRQ=iXOhluh4*6@-t80!O zk-QDN(xJb@osw+@6-nqyhZmX7e|rd>^~ZC%M8V-l3Qpi3_k6Gy%lf3`1~cAngosHZ z4*185X;+jTRQHGK2Alqh($Z9N0OT!VeQ!RHh!Ri`J~e}>9z-J@E*UH>9&jybpt6<# zQ@kRZ4@0`Hc{7RJaaHnT5D7LIndXg~5@k5BS0GFF5172gquB`TF~grpFBF7&&AHkY`iMq!CchDnN#KlHbO z-ZQTDp22V!CmA~&GngT}M1m~XM%|CJyr5luY^FM~r_JT@E7j%&7df} zD}|ZY0RJs-@$<2(fK(}t`A*f1;nFj`I9}E6b#m64!3NCW(zhak7#LOsF|30+EeL=9z9uXMt_YTp9?m^XCdb%0{HU{bo!_~d_=2h(@^YQQ<-w;;JMXI4)U+<2zs%Ye$z&uAk11pn%}}=4x^Uk6!&s9y z*Q1Dv(b@w=vWr7Tw$CgaI~5W|0uy3%au0d2vJ_aop@6=ry(_7Dn6tw^BFovitD4)zfYq@|}_LhP%FgAUS#Ceaa8GC>@tk!usi9 zJI;(e4e%=aWUGFO@@2X&@ro=9I6$9%g1mY|(BY7HirR-ER6tZ&TsOUG5du>t2r;0` zy@u#yPnc5DqHaf9fU@X{Jk4}&R@nCW_Pj2v2d9U!+={zoYcsuVW8)}tMBXKMV9Sew zw^*lA0&-_%^A+s##&+u(Yb3~^X&xmH8QmfmP|1RP0(lvr8PmdNzrLu~L4cmY@l&c} zmYT0V(JORQ{A%`UPdM~b9_B7dz!Ax|iYi*xJZ7}M%dhGW-kSm8w+6|8bi3l&dR4Ri zL*O@@*_t{eWY1H5@u#tmj1Gok_?m;&3LCY1>i9E$?)avoqanGs9rGzm?U+cCk;3NR~OA}`_ zkMX!17RBeWZ7FgiyjT0?m{FlqLt`#kt+ELn{U)_tnR7;Tt1@=}%lvN=_ES5SD@4R5 z;q;*{pEFq0YP-@WwK|)9-euZSpSD`Py3|QOQWu5{hmVPFo34#c+V%Pr)H*yFoPw3t z!R<4zm&JVJhDJPyz48E~7Z=<``==$nylE$RnJNMGIGu4)T;_UAwo3aKdY^0IJM&6* zVR7HD;!YL_%})$i(8euj(DC0kTzz>gKmtEzk$M-K>~YS8Tx;WiB>=jorlGTYuI=pM@B zQ`jTEaBD+tFA}-)HH2TH`2ScTLPc#6438gMKC`Qcu~u5nDDOcJAV6s zXDd<@vF6s@1SVeHcDOyq21d9U0LfF6&w5yvB21s@S zd+vkH_l^~Gr_G@b!Ng`V%c>;;nIJLPZMctwmtN(f=G;!6EVoBp*o* zjLc5Iq!nstD;pP%A7Eu!a)hoRySz{4?%+qOH>QTzE@`H6#@O1ZO^O#e{uqT^Ps4A6 z05n42nbl?%()Vr1`QT9b2HeHxOt2|jtQ(kgB75nv)f35-c028yZNHQ|2NJjxO)P z))`h7!Evyo7`_ovp3W6d(}sFJG(tDFNw>>H(-^+3@TIL!yyawyBrC6|sF16(F`E|S z{FxnaA@g*FY{9aj;G&VEFTaZ-beXHW8G+r0%&przMgjoEDat(mp0s$I={vc&cBF|~ z^h=D>oB!;gK&WO31Uqm8{+){?T$2iD66 zil8L3lwOLcVE>NA8^;G{Oz)V6miC?51o+f(W_JK`qhW0N^ELm0LUyxh&LnO>y!FSB z-S22_PHX7%lOMUBAQu-ED49KnBs#AzsnnuR^7$bUsq%?I_>=FI4FOJCq3WYX?cS_3 zCd^?I%kAJ0SFUh1;Zh&HT;F>jNUQn)nb&i8ks>E1r9nYFTsQKP1buN!rA*3}Q6$A* z3o4p=_y5o`sYv9qXb2q*+(jlNCV-)vB>X}v=JZN}6G$87Urk_9(=s-Jee<+%lO-9; zU-}`d&h>RxxnBkRDew3d#`faD&PyjiX?O*QqhlKF6zJE`w?^}g+VIL%MN+os6F(ts zARA=`y81@70XbTC%4dZF zOHth%c@RafcN4FQazr)uid#G?E@$fX2CCWaZ?r3#vFGQAk>w9)LDSwU5GIwaOvaD) zDM+j)+tVuW3zfh*x{gsB9Aj6Mp`pA9ixn`n7nQo_Z#y~eq;1p5WN$$x;8+^FCN|WN>P4=g@dIQXLFmGnt9#fDx?SDbFZ~1u5|4b>->r3z`RO3S?8~%d+%w83nYfKf3{my z=%@|a#)rr*G{j66%;s43|8SGK1CHyIgAgUJ&CO~Ioy@6Hh#psxpEPu67P}t&^}+6lrZbKXb$agQv?!9kJll!ov zuA+iPS(`a;-T6`9TwqVrcIPJKrHe+5E9AtDb}nM7ffYK*wKkY?t&z6BcHGh^q1cjd zoj8qM#K01plK<>xMDvdwodfzAcAcYg31DJHox~M3VFt3m2Eu=YFMhbWYbD4yv_+ElcvtoT?vFM@ zos{(|$egnya8msf9lrmPN+?p=4EdlExR?T%;3ge<2BZD_HhEx7k1JDy1t{2NV@qvd zGm@7%3btOv{HYnpC=8Yv^0ggt>{LNJGU$1`_P&~&u3rs9FIr+00QC183x*}SaBSes z@+bf6l7dJX3%q=KzA4CO+O%Gic5;jkx65qsB86>B`_$9=CXt{9<`J<=X9$nmgM})m z!1-EUSG!jQNy)X1cGpY2fwRHu4AOgAdh2CD>)Oa3DXiNnB57?hte=HK5hnAMrd$}B z=H-Mfmp(hu5!sv(3ZZx0FBf)~Me3MSBhF2FzvEsYM}I0eIW2jnU$#7r?Vsk! zl%7=N*NEiA7@y4JU;CnBj5%-H1XJ_OKu+0=@QceyuzCmQhcnKxijN-|Y42V&_=(kn zWhXMr&1uIP%bZqXG6kjqIrK+XokNOd)own6-5oWDuh1qk)`gz%sYh`7EoZ_a++@A` z=hkhAkwbFdNnRz8V!xdy-%HZl?9_(M=S;Mb~8#OoN1zF$xFuL#5lGp56n9BKW1;Yu6X4#gb3U3Ta8w^wulNkGEwm3hZ zA%nRi(3fG#(Hg-by%S`D2bXZ0QQLNJ?!L-}_8hwlJ9#6<2>$F!r4&81{z{3k^19_6 zUBe{ZB*$+u0b??i^AtadJEO_vjra8BIz2ca3;p0IQ90l0Jt;}Te!k?stLv~i+d@nR zyH_fSI44Q*gxm~E_>{Rv!6rt&fF=%)tfj+pVUUpJ?(E*|P)S(kIJMV>q7XORc)YC3 zhiL;}!Bqa@N1H}eG2gBt6^8#UL00!nNj}TVFRKbG_Fda=42vaceH;m=j%I^29K_jR zZ~VGVHpc+KcZOmI+wn{}^Ve)82RKy&Gq0!`Gsu#NLS_om65~Dfs}DyFVW>0PH(2m8 zcrNK31R%p?K`h>w3k3Eb(J%x|uxZnsC=KE_a0ppr@|t-AK;$WU@7H2XF$;Gn1^jCq zdsM+hh9TA9u|c59Ci)-_SDYRn4!uIZjgub&+p*{( zy?(efKkr2tu?MmOx?+`&!s56&nnUNNbp3Y8W2a3D4Kb_e{;Vb$n6jL`wcbZSoXKgS zU>_fVa!tn06U|k}HTD%s9g=usi$aPxlFviJLQK2$mpk0~eW;T}{5;+BT6+chfwnrk zi~<&)eTAx#2ZHR$5M6aG$%089Uvm*s>=`>x%M3pvwaVrxm!gx$a8Kn+`#7XXgGi1? zd=quNni@^JqN;YF1RIm5dQIP%GP%l=lckEmgeM)g0*#LaHoFOgiIVKFgKLO-(Bm7X zNrg+Mr(d+Vf&NxN`HsAsDSUr#oCEoM2Q)^xkwzjb|6tIN2z!x;XuR2=!g1L$@HB6l z%%OMD8-Hi}qC71+yqw+`lv-74MK*8@X~alP;oW5UZLULb$bNEiRDO?yN5#mDV+{5p z%0c#6>Z)P*C)eeOdfL~;9uI`-#Y$5)+z=z50B0TR0=_{}>6|282d9f!BoXT)NCt+? zpWQtVVXE#z1(v5jryp9eR4PePy%|6obw2MehJ91nN)rCdK-y23_k^Un1nEgvJJ!bw z!li@>7mU1tErM&kWBRFjxWGeuiERwY^BJ`?|6a*1+F-lKi;Y+DtZgUHECQG%`qtjH zMEeQ`&*9izsJ*i^)(~Wl;7d*udFs;9)LyRS&(N3Jx7d!O07k;VLvtAeB@LAf=wMci zMQa)A5b$w!6Xf`*wsvPdPxpt^GJWz1*USx;kGFKeU2+v#?egH5gH>b)atb+FFM3P+-!{A_ZeV>5)b8bZ;w5IcrT~1A;}4a;TaR0o9-dlW7Zoa4=& zslZwD#fg{pg1wz1l}}K9(}^UvF-pZmcvaJl*Io;he=k}MIfWoNPcj73^q9rZBq9HOiW3x0_MI(T)ynsD$!`k0mOVW&K;UIaTpuk2bstA7f}U(iO=Q z6l0`eQgU*4i8I1-H&_e&wmd)6sw5R3FmIWZS*Y9{aEbv5>|o=QBo(d7qd z-vW4#A=00?GD|0Wj~ZGlqi2#Jn%Do0oJsC_smggtHbEAE@eO27=WAwpy1gbUHM=k% zsKE8*Bh>$iq^>vLzwz$)+3(lJ*EmHA6TVefOoGlIPy@Hb4_DZmHM0g`OP%jimfCWR zn=Zd_C0U9&HhgBvh&F!ZYpRq2(@r0@8I8HYvvFy=P2a`=gw9g=h~s_CTE!r29v;bL zGePL|Drme#^eGjh&0muZ>)%(#3kyS7|qH$+f`tfED(3tR{~+5mMR~RUMd>9 zr3t}<(AQK)?;d%>FQRyy*9$& z8?KI2j2T7(oR#g5$fJIo+loesy-z&89Poa{GjV@HJMxp>`}vE94QuU`7qt6&j1RB$ zmX^9*W}SWzx8Xs#6=E(QuxL8-(gO?8j?lvz5x|kSp#FXB>(ofKLPp4eGCML&?mhpxV zt;(;j?6I#=YK}Duo>B2M4-vvF_CSDE#U66mjpGoww~n0pCpRNaYn?h6&AbcKlq~_7 z4##&AQIfCr$FHSr3pl-YKG9bamvc~&9qGD_dg`D1D@n8nAZdXXTS}&`Nl=X1PkB(9 zP)1YPFpZLEJc%VG-(XfR0Olk$NZO&G01-(^hfuVZKl}?^bDJLp!J9UzUzV=)OPF4$ z^cBf^nd}ayjC$V~*Yx0vjKe%6s@>pWyNK~KlcJj3)_cH9!M+mY!9 zpQ_td1quZ?aX$3-He3&jY0kZ+l_#B`5*G5MYQsm%(B?ve~RR3|Fh;;boe z?#Y4Fh^=69XTH2UNAvXV8xVTE>t(Nb*N+zHtGZU5MZLW~Bp0S0ALjZLS0hmQx1h}gzf5-ugqBV z`ELvJf(oHxDyho3GE=M7VfM9Q{M*_zs4X$ZsIsv%5(fuJvq1x=WMfmg85iVwp!hE% z*y}KN&T@}X4uqjZaLJYsFgab;m~f?FoR+rWi!8rU7|!`SBKiHBWwb3LX(8yJtlcSb zD!Pe`e2AX0+}didokKWwzoFxEQR{1u(O?c1nI`C4{`|@$gaJjKRnWq7`{qv-1&VOn z&V}ewQ&m+3Tc;Fl+IPUEm5}m}M>Pnh&a6Ul(}tUBe7r)*dTg%mX#L8gSU6c8i+nBY zyy8?oK)Vp7aKab(%2<;{|Nfxhkgd?t(4*n<=6Jt<5^;=CO(OSt$)hYoLHHJjv{Tgr{3R(%V80Y{NsvWxROWO7j^aX43B5XMK%Xt= z@tS;VO6i&&rZ8HHS#p>vOZM(`mN<+@12{n1@@LLQk@TE>0K~haFnhi(@Uw5AIp9?y zJ*nM6y2lxNNA$>6Gbxo*8?88G-(e!VEsNnk2e^Anh=j?%=j^Si1?@$&1R+suEe zw^yAJvF*ns8$8&{50HC;IoT!&1ia?WSJzwtYpcJCv`6fL!CVYZGkM}slGdFo1kskI zMiN&?1Rpi!_119VyY-Qo6+2z(cQM^R-EuiCBrNPnFT5~R2_*Yl8BfZjb8HG}QZw9) zlKA~z`v<6Z|Ri$tD zKGsyRwO8C=>*SCs$PT~u(9ebCLuQObuxy5eWQiE?z`D{qoWza6R!7@@;QllW&1y!@ zGbtKbZ9pGPE_>_n^S}lxV0{M9$Bf9>|1)&859adi#QrP)i!D)IU0rn3=`sNwer@rP=Po!!U zyvgmcH&TFdSRHcoR%=8mp3?J50SBY`n!!u}$9kgP{mnqe7}}>5&goqwW{Xo`k1vF5 zxyWl>u0W6|Y+I=e~&VSf6E)}DC%#%3{ z$Z>RvMF)7x?XF5oh=QxIn|@!vG}rF4KpSO)upybsS4z&5T9SRrmQcz*u+tN4LF`QH z9LNme!YZ_zJKkJeECMnn?~&U(SC9WInb4o5U;!|5u1vNq-Gl>Uc(us<0wl*hj4`X9&)YmVD zRq{kbk5|M?JSRMrd+A{Zfg0Kii5t{0Q<(7>>sw?SQ5^aSp>%v|(o=kHY#?Evp&aya ze7+Y%lGEt-;tsAQP;dTd=4el3^>-qmIKw>Jkz_qhtIG*%)tu2D+y;G6;n~%^>0dr%Sf^!xt-U zH(A7$UcR3nioVj-Vpv zaTzdFtmf?L`?b$WPNa4j!ENrSLZ5kH6 z;dJD7Ubf&(;*NT#SN*HlLJW0cBB@IIn(pV+8&WqY>;0-1F>%5mPZHgjk^iz3Rl(bz z`;lcv2QNsC4~OPMj8IHVFE}r;dltKQ6nwZd>bCDZKUZIdx|uyF1?xSq#5|&GsguYq zA<*GLGs{a1O3}yYj!)_NMM{H-E9*~XvsV_-_$C%131g=w*31O7|+s-pxs2 z=h>y2sqp+yR_=c67~LrChs|4T@B+TVpNB2(T>wupshZKCk{%RXd|~uf*R3_KMqb6= zoL8g?;sJfhySqUb$iy)%tMsd_CN7%>dho~ZeTrHjRbCs#q#q*Wj|MR3G zmUIYjX%R@Eqorq{3_4r>82zD8&;orb8=~opz$msNWURpi6@Y8if&2|;w=2h0(Sho- zciubK5qnj+DtPyS!^Uuj!|fp{MYw=aP9V@9n~HX4dLfy%Amt?hJ&V8eHFqC*sy)X` zM)Vi2H6XM74(skhU92P4no_*0%6(x__F6Y;HS^%h!wLXi@O9ycU&CmOJ~D5rD=s;oEhCZ5%Xi$2 z(&BrNZ{8iBKy^ttKMXeiaQg=g(vgJMBSLK&iw?K8WNJxAzl9Dr7D_FE`465$c+9^K zV=*T@|3b|oS8h5b`7}eQXR+w~^uooNqAg)DCYp&rtq4QP8Cr6(!_sP2I-}3^&)Pb} zVb;OcxaG>!^Xgl~<3A9WCtin~sq0KIixKMbzqxt+J&JfmhqykJ=7P5i*nA2>OSEMJ z+r48-*9pG73W{;9m%;+wn}+nV7UEc%&q7rc8!X?Cy1yG054k=!^b-GKO|~+@>(oNd z&SdUj59vEelsR68D!7J*jTLdXuuKs(sjXYGZ0)#jQXJN0;X**L-dwg3VH#N0o{k4bnDBWzs&JJ)QOr^)st?3a-8iWXpP=8S& z-<^#9(9yk)&?WJDrP%VK8IH<$27XOgm}J|+%V7CAQ1tklv9ZzQRVQZ{>)FbwsDuAO ze6(z9V%Ve!3jmD1L#mySe6C|uGE9S2(#U)ChSo?V8MASVAtJ;vRA{wOVH+(Sy^o*rF{vn`WN`Z^X)7Zz0c1hFo_%DW6rBl zJ9rVO^y@5`*7zP0uOW10N&Sd+RL6|S+Nw7XI92cKXyUF7Ixj4}@%)3u`Qsdslg~@f zEkNYb#PFmqKXzo)cX{s);ey8yh92ZdgzcJCQP2O$7=->Wz^LOJca1q5?GK2i`6VtN z;LcMjT+@}qg)~hn72!l}#W5tV#-#UnBq_*S-2MJ!K^iT|cOx&95li4L=JKQ_VKSP7 z$NAh+K`ABSK68uC;7zMALE|HDDx{6aM1wqY`^4rjdLnE#7M&p!|0nepib8Np`SGOe z1%`X8-RWt~Y`8ehUqn$NxMxvK9<~rvGvT+6fiSAd&s^)Lygl=*ktW+$7Q^1>662$A zZHeB2)UoFKzMoW6tr_-aR@RY|{0Awhl&zkw*5=}%E7G<0H>X6ivt^)O^3?Y9hiFCn zp@zYY3{bIyf30>;WAf9YFVB->t?T^cK<#+^y8`(8JHjSz&aUFWNGlt><$h+maBS*I zd0I2Ufw^|1?Dm!$5P7l%gn;A5X@kx03m!#%*XSUAvh;=B=bmXHUzO0x+?;}`w|+>~ zY4PW-mLCrkyMiTul>hDyV6XqK0rxaBUfEq~)X?JB&_2ea z&2u_!peyOLHdFFRm|Klgaf-?322+#NFa{j&%c&(jctc&afbJ%YQK9zyMi z&Hv`qWb1icHFmg=6}U?x6$W*(iY=?)_kKX@_a7KtI=H8~@jX?$HXcvTY>jz|;VYDls#fgigYf8C@DfUxrgT?=>99&r3SKZ2c 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..db9b662 --- /dev/null +++ b/man/s2-package.Rd @@ -0,0 +1,41 @@ +% 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..3222577 --- /dev/null +++ b/man/s2_boundary.Rd @@ -0,0 +1,196 @@ +% 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_centroid_agg} +\alias{s2_coverage_union_agg} +\alias{s2_rebuild_agg} +\alias{s2_union_agg} +\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_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) +} +\arguments{ +\item{x}{\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{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)") + +# 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)) +) + +# 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..e4bd419 --- /dev/null +++ b/man/s2_bounds_cap.Rd @@ -0,0 +1,41 @@ +% 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}{\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.} +} +\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..986389f --- /dev/null +++ b/man/s2_cell.Rd @@ -0,0 +1,77 @@ +% 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.s2_lnglat} +\alias{as_s2_cell.s2_point} +\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}{s2_lnglat}(x, ...) + +\method{as_s2_cell}{s2_point}(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..93e3fc8 --- /dev/null +++ b/man/s2_cell_is_valid.Rd @@ -0,0 +1,74 @@ +% 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} +\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) +} +\arguments{ +\item{x, y}{An \code{\link[=s2_cell]{s2_cell()}} vector} + +\item{k}{An integer between 1 and 4} + +\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.} +} +\description{ +S2 cell operators +} diff --git a/man/s2_closest_feature.Rd b/man/s2_closest_feature.Rd new file mode 100644 index 0000000..9d613d1 --- /dev/null +++ b/man/s2_closest_feature.Rd @@ -0,0 +1,122 @@ +% 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, 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{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..8bfc9bd --- /dev/null +++ b/man/s2_contains.Rd @@ -0,0 +1,170 @@ +% 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} +\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()) +} +\arguments{ +\item{x}{\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{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_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..ef89748 --- /dev/null +++ b/man/s2_geog_point.Rd @@ -0,0 +1,117 @@ +% 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} +\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) + +s2_geog_from_wkb(wkb_bytes, oriented = FALSE, check = TRUE) + +s2_as_text(x, precision = 16, trim = TRUE) + +s2_as_binary(x, endian = wk::wk_platform_endian()) +} +\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{wkb_bytes}{A \code{list()} of \code{raw()}} + +\item{x}{\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{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) + +} +\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..5ba9cca --- /dev/null +++ b/man/s2_lnglat.Rd @@ -0,0 +1,55 @@ +% 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.s2_lnglat} +\alias{as_s2_lnglat.s2_point} +\alias{as_s2_lnglat.s2_geography} +\alias{as_s2_lnglat.matrix} +\alias{as.data.frame.s2_lnglat} +\alias{as.matrix.s2_lnglat} +\alias{as_wkb.s2_lnglat} +\alias{as_wkt.s2_lnglat} +\title{Create an S2 LngLat Vector} +\usage{ +s2_lnglat(lng, lat) + +as_s2_lnglat(x, ...) + +\method{as_s2_lnglat}{s2_lnglat}(x, ...) + +\method{as_s2_lnglat}{s2_point}(x, ...) + +\method{as_s2_lnglat}{s2_geography}(x, ...) + +\method{as_s2_lnglat}{matrix}(x, ...) + +\method{as.data.frame}{s2_lnglat}(x, ...) + +\method{as.matrix}{s2_lnglat}(x, ...) + +\method{as_wkb}{s2_lnglat}(x, ...) + +\method{as_wkt}{s2_lnglat}(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_point.Rd b/man/s2_point.Rd new file mode 100644 index 0000000..05f83bf --- /dev/null +++ b/man/s2_point.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s2-point.R +\name{s2_point} +\alias{s2_point} +\alias{as_s2_point} +\alias{as_s2_point.s2_point} +\alias{as_s2_point.s2_lnglat} +\alias{as_s2_point.s2_geography} +\alias{as_s2_point.matrix} +\alias{as.data.frame.s2_point} +\alias{as.matrix.s2_point} +\title{Create an S2 Point Vector} +\usage{ +s2_point(x, y, z) + +as_s2_point(x, ...) + +\method{as_s2_point}{s2_point}(x, ...) + +\method{as_s2_point}{s2_lnglat}(x, ...) + +\method{as_s2_point}{s2_geography}(x, ...) + +\method{as_s2_point}{matrix}(x, ...) + +\method{as.data.frame}{s2_point}(x, ...) + +\method{as.matrix}{s2_point}(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_lnglat]{s2_lnglat()}}. Internally, all s2 objects are stored as +3-dimensional unit vectors. +} +\examples{ +lnglat <- s2_lnglat(-64, 45) # Halifax, Nova Scotia! +as_s2_point(lnglat) +as.data.frame(as_s2_point(lnglat)) + +} diff --git a/man/s2_unprojection_filter.Rd b/man/s2_unprojection_filter.Rd new file mode 100644 index 0000000..0844d40 --- /dev/null +++ b/man/s2_unprojection_filter.Rd @@ -0,0 +1,79 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/wk-utils.R +\name{s2_unprojection_filter} +\alias{s2_unprojection_filter} +\alias{s2_projection_filter} +\alias{s2_projection_plate_carree} +\alias{s2_projection_mercator} +\title{Low-level wk filters and handlers} +\usage{ +s2_unprojection_filter( + handler, + projection = s2_projection_plate_carree(), + tessellate_tol = Inf +) + +s2_projection_filter( + handler, + projection = s2_projection_plate_carree(), + tessellate_tol = Inf +) + +s2_projection_plate_carree() + +s2_projection_mercator() +} +\arguments{ +\item{handler}{A \link[wk:wk_handle]{wk_handler} object.} + +\item{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}{An angle in radians. Points will not be added +if a line segment is within this distance of a point.} +} +\value{ +\itemize{ +\item \code{s2_unprojection_filter()}, \code{s2_projection_filter()}: A \code{new_wk_handler()} +\item \code{s2_projection_plate_carree()}, \code{s2_projection_mercator()}: An external pointer +to an S2 projection. +} +} +\description{ +Low-level wk filters and handlers +} +\examples{ +library(wk) + +# simple conversion of individual coordinates *to* unit sphere +# space +wk_handle( + wkt("LINESTRING (0 0, 0 45, -60 45)"), + s2_unprojection_filter(wkt_format_handler(5)) +) + +# simple conversion of individual coordinates *from* unit sphere +# space +wk_handle( + wkt("LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)"), + s2_projection_filter(wkt_format_handler(5)) +) + +# use tessellate_tol to force points to be added to an edge +# unprojection will ensure an edge maintains its cartesian +# assumption when transformed to the unit sphere +# (i.e., what you probably want when importing a geography) +wk_handle( + wkt("LINESTRING (0 0, 0 45, -60 45)"), + s2_unprojection_filter(wkt_format_handler(5), tessellate_tol = 0.001) +) + +# projection will ensure an edge maintains its geodesic +# assumption when transformed to projected space +# (i.e., what you probably want when exporting a geography) +wk_handle( + wkt("LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)"), + s2_projection_filter(wkt_format_handler(5), tessellate_tol = 0.001) +) + +} diff --git a/src/Makevars.in b/src/Makevars.in new file mode 100644 index 0000000..e1df096 --- /dev/null +++ b/src/Makevars.in @@ -0,0 +1,203 @@ +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/exponential_biased.o \ + absl/base/internal/low_level_alloc.o \ + absl/base/internal/periodic_sampler.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_disable.o \ + absl/debugging/leak_check.o \ + absl/debugging/stacktrace.o \ + absl/debugging/symbolize.o \ + absl/numeric/int128.o \ + absl/strings/ascii.o \ + absl/strings/charconv.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_ring.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-constructors-formatters.o \ + s2-predicates.o \ + s2-transformers.o \ + init.o \ + RcppExports.o \ + s2-geography.o \ + s2-lnglat.o \ + s2-matrix.o \ + s2-point.o \ + s2-xptr.o \ + wk-impl.o \ + wk-c-utils.o \ + s2-c-api.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..1a2e101 --- /dev/null +++ b/src/Makevars.ucrt @@ -0,0 +1,2 @@ +CRT=-ucrt +include Makevars.win diff --git a/src/Makevars.win b/src/Makevars.win new file mode 100644 index 0000000..a3c91ab --- /dev/null +++ b/src/Makevars.win @@ -0,0 +1,206 @@ +PKG_CPPFLAGS = -DS2_USE_EXACTFLOAT -D_USE_MATH_DEFINES -DNDEBUG -DIS_LITTLE_ENDIAN -I../windows/openssl-1.1.1k/include -I../src +PKG_LIBS = -Ls2 -ls2static -L../windows/openssl-1.1.1k/lib${R_ARCH}${CRT} -lssl -lcrypto -lcrypt32 -lws2_32 + +CXX_STD = CXX11 + +STATLIB = s2/libs2static.a + +ABSL_LIBS = absl/base/internal/cycleclock.o \ + absl/base/internal/exponential_biased.o \ + absl/base/internal/low_level_alloc.o \ + absl/base/internal/periodic_sampler.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_disable.o \ + absl/debugging/leak_check.o \ + absl/debugging/stacktrace.o \ + absl/debugging/symbolize.o \ + absl/numeric/int128.o \ + absl/strings/ascii.o \ + absl/strings/charconv.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_ring.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) \ + s2-c-api.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" --vanilla "../tools/winlibs.R" + +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..e7ff9bb --- /dev/null +++ b/src/RcppExports.cpp @@ -0,0 +1,1396 @@ +// 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_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_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_geog_point +List cpp_s2_geog_point(NumericVector x, NumericVector y); +RcppExport SEXP _s2_cpp_s2_geog_point(SEXP xSEXP, SEXP ySEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); + Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_geog_point(x, y)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_make_line +List cpp_s2_make_line(NumericVector x, NumericVector y, IntegerVector featureId); +RcppExport SEXP _s2_cpp_s2_make_line(SEXP xSEXP, SEXP ySEXP, SEXP featureIdSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); + Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP); + Rcpp::traits::input_parameter< IntegerVector >::type featureId(featureIdSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_make_line(x, y, featureId)); + return rcpp_result_gen; +END_RCPP +} +// cpp_s2_make_polygon +List cpp_s2_make_polygon(NumericVector x, NumericVector y, IntegerVector featureId, IntegerVector ringId, bool oriented, bool check); +RcppExport SEXP _s2_cpp_s2_make_polygon(SEXP xSEXP, SEXP ySEXP, SEXP featureIdSEXP, SEXP ringIdSEXP, SEXP orientedSEXP, SEXP checkSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); + Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP); + Rcpp::traits::input_parameter< IntegerVector >::type featureId(featureIdSEXP); + Rcpp::traits::input_parameter< IntegerVector >::type ringId(ringIdSEXP); + Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP); + Rcpp::traits::input_parameter< bool >::type check(checkSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_s2_make_polygon(x, y, featureId, ringId, oriented, check)); + return rcpp_result_gen; +END_RCPP +} +// s2_geography_from_wkb +List s2_geography_from_wkb(List wkb, bool oriented, bool check); +RcppExport SEXP _s2_s2_geography_from_wkb(SEXP wkbSEXP, SEXP orientedSEXP, SEXP checkSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type wkb(wkbSEXP); + Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP); + Rcpp::traits::input_parameter< bool >::type check(checkSEXP); + rcpp_result_gen = Rcpp::wrap(s2_geography_from_wkb(wkb, oriented, check)); + return rcpp_result_gen; +END_RCPP +} +// s2_geography_from_wkt +List s2_geography_from_wkt(CharacterVector wkt, bool oriented, bool check); +RcppExport SEXP _s2_s2_geography_from_wkt(SEXP wktSEXP, SEXP orientedSEXP, SEXP checkSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< CharacterVector >::type wkt(wktSEXP); + Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP); + Rcpp::traits::input_parameter< bool >::type check(checkSEXP); + rcpp_result_gen = Rcpp::wrap(s2_geography_from_wkt(wkt, oriented, check)); + 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 +} +// s2_geography_to_wkt +CharacterVector s2_geography_to_wkt(List s2_geography, int precision, bool trim); +RcppExport SEXP _s2_s2_geography_to_wkt(SEXP s2_geographySEXP, SEXP precisionSEXP, SEXP trimSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP); + Rcpp::traits::input_parameter< int >::type precision(precisionSEXP); + Rcpp::traits::input_parameter< bool >::type trim(trimSEXP); + rcpp_result_gen = Rcpp::wrap(s2_geography_to_wkt(s2_geography, precision, trim)); + return rcpp_result_gen; +END_RCPP +} +// s2_geography_to_wkb +List s2_geography_to_wkb(List s2_geography, int endian); +RcppExport SEXP _s2_s2_geography_to_wkb(SEXP s2_geographySEXP, SEXP endianSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP); + Rcpp::traits::input_parameter< int >::type endian(endianSEXP); + rcpp_result_gen = Rcpp::wrap(s2_geography_to_wkb(s2_geography, endian)); + return rcpp_result_gen; +END_RCPP +} +// s2_geography_format +CharacterVector s2_geography_format(List s2_geography, int maxCoords, int precision, bool trim); +RcppExport SEXP _s2_s2_geography_format(SEXP s2_geographySEXP, SEXP maxCoordsSEXP, SEXP precisionSEXP, SEXP trimSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP); + Rcpp::traits::input_parameter< int >::type maxCoords(maxCoordsSEXP); + Rcpp::traits::input_parameter< int >::type precision(precisionSEXP); + Rcpp::traits::input_parameter< bool >::type trim(trimSEXP); + rcpp_result_gen = Rcpp::wrap(s2_geography_format(s2_geography, maxCoords, precision, trim)); + return rcpp_result_gen; +END_RCPP +} +// s2_lnglat_from_numeric +List s2_lnglat_from_numeric(NumericVector lng, NumericVector lat); +RcppExport SEXP _s2_s2_lnglat_from_numeric(SEXP lngSEXP, SEXP latSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type lng(lngSEXP); + Rcpp::traits::input_parameter< NumericVector >::type lat(latSEXP); + rcpp_result_gen = Rcpp::wrap(s2_lnglat_from_numeric(lng, lat)); + 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 +} +// data_frame_from_s2_lnglat +List data_frame_from_s2_lnglat(List xptr); +RcppExport SEXP _s2_data_frame_from_s2_lnglat(SEXP xptrSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type xptr(xptrSEXP); + rcpp_result_gen = Rcpp::wrap(data_frame_from_s2_lnglat(xptr)); + 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); +RcppExport SEXP _s2_cpp_s2_closest_edges(SEXP geog1SEXP, SEXP geog2SEXP, SEXP nSEXP, SEXP min_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_result_gen = Rcpp::wrap(cpp_s2_closest_edges(geog1, geog2, n, min_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 +} +// s2_point_from_numeric +List s2_point_from_numeric(NumericVector x, NumericVector y, NumericVector z); +RcppExport SEXP _s2_s2_point_from_numeric(SEXP xSEXP, SEXP ySEXP, SEXP zSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); + Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP); + Rcpp::traits::input_parameter< NumericVector >::type z(zSEXP); + rcpp_result_gen = Rcpp::wrap(s2_point_from_numeric(x, y, z)); + 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 +} +// data_frame_from_s2_point +List data_frame_from_s2_point(List s2_point); +RcppExport SEXP _s2_data_frame_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(data_frame_from_s2_point(s2_point)); + 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_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_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 +} +// s2_xptr_test +List s2_xptr_test(R_xlen_t size); +RcppExport SEXP _s2_s2_xptr_test(SEXP sizeSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< R_xlen_t >::type size(sizeSEXP); + rcpp_result_gen = Rcpp::wrap(s2_xptr_test(size)); + return rcpp_result_gen; +END_RCPP +} +// s2_xptr_test_op +void s2_xptr_test_op(List s2_xptr_test); +RcppExport SEXP _s2_s2_xptr_test_op(SEXP s2_xptr_testSEXP) { +BEGIN_RCPP + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< List >::type s2_xptr_test(s2_xptr_testSEXP); + s2_xptr_test_op(s2_xptr_test); + return R_NilValue; +END_RCPP +} + +RcppExport SEXP c_s2_coord_filter_new(SEXP, SEXP, SEXP, SEXP); +RcppExport SEXP c_s2_projection_mercator(); +RcppExport SEXP c_s2_projection_plate_carree(); + +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_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_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_geog_point", (DL_FUNC) &_s2_cpp_s2_geog_point, 2}, + {"_s2_cpp_s2_make_line", (DL_FUNC) &_s2_cpp_s2_make_line, 3}, + {"_s2_cpp_s2_make_polygon", (DL_FUNC) &_s2_cpp_s2_make_polygon, 6}, + {"_s2_s2_geography_from_wkb", (DL_FUNC) &_s2_s2_geography_from_wkb, 3}, + {"_s2_s2_geography_from_wkt", (DL_FUNC) &_s2_s2_geography_from_wkt, 3}, + {"_s2_s2_geography_full", (DL_FUNC) &_s2_s2_geography_full, 1}, + {"_s2_s2_geography_to_wkt", (DL_FUNC) &_s2_s2_geography_to_wkt, 3}, + {"_s2_s2_geography_to_wkb", (DL_FUNC) &_s2_s2_geography_to_wkb, 2}, + {"_s2_s2_geography_format", (DL_FUNC) &_s2_s2_geography_format, 4}, + {"_s2_s2_lnglat_from_numeric", (DL_FUNC) &_s2_s2_lnglat_from_numeric, 2}, + {"_s2_s2_lnglat_from_s2_point", (DL_FUNC) &_s2_s2_lnglat_from_s2_point, 1}, + {"_s2_data_frame_from_s2_lnglat", (DL_FUNC) &_s2_data_frame_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, 4}, + {"_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_s2_point_from_numeric", (DL_FUNC) &_s2_s2_point_from_numeric, 3}, + {"_s2_s2_point_from_s2_lnglat", (DL_FUNC) &_s2_s2_point_from_s2_lnglat, 1}, + {"_s2_data_frame_from_s2_point", (DL_FUNC) &_s2_data_frame_from_s2_point, 1}, + {"_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_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_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_s2_xptr_test", (DL_FUNC) &_s2_s2_xptr_test, 1}, + {"_s2_s2_xptr_test_op", (DL_FUNC) &_s2_s2_xptr_test_op, 1}, + {"c_s2_coord_filter_new", (DL_FUNC) &c_s2_coord_filter_new, 4}, + {"c_s2_projection_mercator", (DL_FUNC) &c_s2_projection_mercator, 0}, + {"c_s2_projection_plate_carree", (DL_FUNC) &c_s2_projection_plate_carree, 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..6398438 --- /dev/null +++ b/src/absl/algorithm/container.h @@ -0,0 +1,1764 @@ +// 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 a condition on all elements within a container. +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, Compare&& 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, Compare&& 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, Compare&& 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, + Compare&& 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, + Compare&& 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, Compare&& 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, + Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, + Compare&& 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, + Compare&& 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, Compare&& 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, + Compare&& 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 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) { + 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, Compare&& 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, Compare&& 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, + Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, Compare&& 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, + Compare&& 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, Compare&& 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, Compare&& 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..cf2cb55 --- /dev/null +++ b/src/absl/base/attributes.h @@ -0,0 +1,702 @@ +// 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 currently do not work properly in LLVM's Windows backend, +// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// for 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(__llvm__) && defined(_WIN32)) && !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)) +#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) || \ + (defined(__GNUC__) && \ + (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ + !defined(__clang__)) +#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 +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) +#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. +// +// 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. +#if ABSL_HAVE_ATTRIBUTE(nodiscard) +#define ABSL_MUST_USE_RESULT [[nodiscard]] +#elif 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. +#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. 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 compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED +// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed +// when performing switch labels fall-through diagnostic +// (`-Wimplicit-fallthrough`). See clang documentation on language extensions +// for details: +// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough +// +// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro +// has no effect on diagnostics. In any case this macro has no effect on runtime +// behavior and performance of code. + +#ifdef ABSL_FALLTHROUGH_INTENDED +#error "ABSL_FALLTHROUGH_INTENDED should not be defined." +#endif + +// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported. +#if defined(__clang__) && defined(__has_warning) +#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] +#endif +#elif defined(__GNUC__) && __GNUC__ >= 7 +#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] +#endif + +#ifndef ABSL_FALLTHROUGH_INTENDED +#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). +// +// Examples: +// +// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; +// +// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} +// +// template +// ABSL_DEPRECATED("Use DoThat() instead") +// void DoThis(); +// +// Every usage of a deprecated entity will trigger a warning when compiled with +// clang's `-Wdeprecated-declarations` option. This option is turned off by +// default, but the warnings will be reported by clang-tidy. +#if defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L +#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) +#endif + +#ifndef ABSL_DEPRECATED +#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". Prefer to put this attribute on the most visible +// declaration of the variable, if there's more than one, because code that +// accesses the variable can then use the attribute for optimization. +// +// Example: +// +// class MyClass { +// public: +// ABSL_CONST_INIT static MyType my_var; +// }; +// +// MyType MyClass::my_var = MakeMyType(...); +// +// Note that this attribute is redundant if the variable is declared constexpr. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define ABSL_CONST_INIT [[clang::require_constant_initialization]] +#else +#define ABSL_CONST_INIT +#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) + +// 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 + +#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..83c6912 --- /dev/null +++ b/src/absl/base/casts.h @@ -0,0 +1,187 @@ +// +// 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 + +#include "absl/base/internal/identity.h" +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace internal_casts { + +template +struct is_bitcastable + : std::integral_constant< + bool, + sizeof(Dest) == sizeof(Source) && + type_traits_internal::is_trivially_copyable::value && + type_traits_internal::is_trivially_copyable::value && + std::is_default_constructible::value> {}; + +} // namespace internal_casts + +// 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() +// +// Performs a bitwise cast on a type without changing the underlying bit +// representation of that type's value. The two 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 temporarily treat a +// type as some other type, such as in the following cases: +// +// * Serialization (casting temporarily to `char *` for those purposes is +// always allowed by the C++ standard) +// * Managing the individual bits of a type within mathematical operations +// that are not normally accessible through that type +// * Casting non-pointer types to pointer types (casting the other way is +// allowed by `reinterpret_cast()` but round-trips cannot occur the other +// way). +// +// Example: +// +// float f = 3.14159265358979; +// int i = bit_cast(f); +// // i = 0x40490fdb +// +// Casting non-pointer types to pointer types and then dereferencing them +// traditionally produces undefined behavior. +// +// Example: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast(&f); // WRONG +// +// The address-casting method produces 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 values of "different type". +// +// 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 implementing its casts using `memcpy()`, which avoids introducing +// this undefined behavior. +// +// NOTE: The requirements here are more strict than the bit_cast of standard +// proposal p0476 due to the need for workarounds and lack of intrinsics. +// Specifically, this implementation also requires `Dest` to be +// default-constructible. +template < + typename Dest, typename Source, + typename std::enable_if::value, + int>::type = 0> +inline Dest bit_cast(const Source& source) { + Dest dest; + memcpy(static_cast(std::addressof(dest)), + static_cast(std::addressof(source)), sizeof(dest)); + return dest; +} + +// NOTE: This overload is only picked if the requirements of bit_cast are +// not met. It is therefore UB, but is provided temporarily as previous +// versions of this function template were unchecked. Do not use this in +// new code. +template < + typename Dest, typename Source, + typename std::enable_if< + !internal_casts::is_bitcastable::value, + int>::type = 0> +ABSL_DEPRECATED( + "absl::bit_cast type requirements were violated. Update the types " + "being used such that they are the same size and are both " + "TriviallyCopyable.") +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), + "Source and destination types should have equal sizes."); + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +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..a3a778d --- /dev/null +++ b/src/absl/base/config.h @@ -0,0 +1,746 @@ +// +// 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 + +#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" + +// 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 + +#if defined(__is_identifier) +#define ABSL_INTERNAL_HAS_KEYWORD(x) !(__is_identifier(x)) +#else +#define ABSL_INTERNAL_HAS_KEYWORD(x) 0 +#endif + +#ifdef __has_feature +#define ABSL_HAVE_FEATURE(f) __has_feature(f) +#else +#define ABSL_HAVE_FEATURE(f) 0 +#endif + +// ABSL_HAVE_TLS is defined to 1 when __thread should be supported. +// We assume __thread is supported on Linux 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(__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(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \ + defined(_MSC_VER) +#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 >= 5.1 with +// either libc++ or libstdc++, 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__) && defined(__GNUC__) && \ + (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ + (defined(_MSC_VER) && !defined(__NVCC__)) +#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 +#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 +#endif + +// ABSL_HAVE_SOURCE_LOCATION_CURRENT +// +// Indicates whether `absl::SourceLocation::current()` will return useful +// information in some contexts. +#ifndef ABSL_HAVE_SOURCE_LOCATION_CURRENT +#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \ + ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE) +#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 +#elif defined(__GNUC__) && __GNUC__ >= 5 +#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 +#endif +#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 packages can't use intrinsic int128 because -Wpedantic gives an +// R CMD check warning. Disable it 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 defined(__clang__) + +#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) +// Clang >= 3.6 +#if ABSL_HAVE_FEATURE(cxx_exceptions) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif // ABSL_HAVE_FEATURE(cxx_exceptions) +#else +// 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) +#endif // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) + +// Handle remaining special cases and default to exceptions being supported. +#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ + !(defined(__GNUC__) && (__GNUC__ >= 5) && !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(__ros__) || defined(__native_client__) || defined(__asmjs__) || \ + defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \ + defined(__ASYLO__) || defined(__myriad2__) +#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(__ros__) +#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 10.11 don't let you use , , or +// even though the headers exist and are publicly noted to work. See +// https://github.com/abseil/abseil-cpp/issues/207 and +// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +// libc++ spells out the availability requirements in the file +// llvm-project/libcxx/include/__config via the #define +// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. +#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ + ((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 + +#undef ABSL_INTERNAL_HAS_KEYWORD + +// 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(MEMORY_SANITIZER) +// The MEMORY_SANITIZER macro is deprecated but we will continue to honor it +// for now. +#define ABSL_HAVE_MEMORY_SANITIZER 1 +#elif defined(__SANITIZE_MEMORY__) +#define ABSL_HAVE_MEMORY_SANITIZER 1 +#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(THREAD_SANITIZER) +// The THREAD_SANITIZER macro is deprecated but we will continue to honor it +// for now. +#define ABSL_HAVE_THREAD_SANITIZER 1 +#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(ADDRESS_SANITIZER) +// The ADDRESS_SANITIZER macro is deprecated but we will continue to honor it +// for now. +#define ABSL_HAVE_ADDRESS_SANITIZER 1 +#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_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 + +#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..880cbf6 --- /dev/null +++ b/src/absl/base/dynamic_annotations.h @@ -0,0 +1,496 @@ +// 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 + +#ifdef __cplusplus +#ifdef ABSL_HAVE_THREAD_SANITIZER +ABSL_INTERNAL_BEGIN_EXTERN_C +int RunningOnValgrind(); +double ValgrindSlowdown(); +ABSL_INTERNAL_END_EXTERN_C +#else +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { +ABSL_DEPRECATED( + "Don't use this interface. It is misleading and is being deleted.") +ABSL_ATTRIBUTE_ALWAYS_INLINE inline int RunningOnValgrind() { return 0; } +ABSL_DEPRECATED( + "Don't use this interface. It is misleading and is being deleted.") +ABSL_ATTRIBUTE_ALWAYS_INLINE inline double ValgrindSlowdown() { return 1.0; } +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +using absl::base_internal::RunningOnValgrind; +using absl::base_internal::ValgrindSlowdown; +#endif +#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 { \ + char x[8] __attribute__((aligned(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..0e65005 --- /dev/null +++ b/src/absl/base/internal/cycleclock.cc @@ -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. + +// 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/internal/unscaledcycleclock.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +#if ABSL_USE_UNSCALED_CYCLECLOCK + +namespace { + +#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 +// In debug mode use a different shift to discourage depending on a +// particular shift value. +static constexpr int32_t kShift = 2; +#endif + +static constexpr double kFrequencyScale = 1.0 / (1 << kShift); +static std::atomic cycle_clock_source; + +CycleClockSourceFunc LoadCycleClockSource() { + // 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; + } + // 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); +} + +} // namespace + +int64_t CycleClock::Now() { + auto fn = LoadCycleClockSource(); + if (fn == nullptr) { + return base_internal::UnscaledCycleClock::Now() >> kShift; + } + return fn() >> kShift; +} + +double CycleClock::Frequency() { + return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency(); +} + +void CycleClockSource::Register(CycleClockSourceFunc source) { + // Corresponds to the load(std::memory_order_acquire) in LoadCycleClockSource. + cycle_clock_source.store(source, std::memory_order_release); +} + +#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..a18b584 --- /dev/null +++ b/src/absl/base/internal/cycleclock.h @@ -0,0 +1,94 @@ +// +// 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 "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// ----------------------------------------------------------------------------- +// 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: + CycleClock() = delete; // no instances + CycleClock(const CycleClock&) = delete; + CycleClock& operator=(const CycleClock&) = delete; +}; + +using CycleClockSourceFunc = int64_t (*)(); + +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); +}; + +} // 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..274054c --- /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" + +#if 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. +#ifdef __BIONIC__ +// Android doesn't have sgidefs.h, but does have asm/sgidefs.h, which has the +// definitions we need. +#include +#else +#include +#endif // __BIONIC__ +#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..dad0e9a --- /dev/null +++ b/src/absl/base/internal/endian.h @@ -0,0 +1,327 @@ +// 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_ + +// The following guarantees declaration of the byte swap functions +#ifdef _MSC_VER +#include // NOLINT(build/include) +#elif defined(__FreeBSD__) +#include +#elif defined(__GLIBC__) +#include // IWYU pragma: export +#endif + +#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 + +// Use compiler byte-swapping intrinsics if they are available. 32-bit +// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0. +// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0. +// For simplicity, we enable them all only for GCC 4.8.0 or later. +#if defined(__clang__) || \ + (defined(__GNUC__) && \ + ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5)) +inline uint64_t gbswap_64(uint64_t host_int) { + return __builtin_bswap64(host_int); +} +inline uint32_t gbswap_32(uint32_t host_int) { + return __builtin_bswap32(host_int); +} +inline uint16_t gbswap_16(uint16_t host_int) { + return __builtin_bswap16(host_int); +} + +#elif defined(_MSC_VER) +inline uint64_t gbswap_64(uint64_t host_int) { + return _byteswap_uint64(host_int); +} +inline uint32_t gbswap_32(uint32_t host_int) { + return _byteswap_ulong(host_int); +} +inline uint16_t gbswap_16(uint16_t host_int) { + return _byteswap_ushort(host_int); +} + +#else +inline uint64_t gbswap_64(uint64_t host_int) { +#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) + // Adapted from /usr/include/byteswap.h. Not available on Mac. + if (__builtin_constant_p(host_int)) { + return __bswap_constant_64(host_int); + } else { + uint64_t result; + __asm__("bswap %0" : "=r"(result) : "0"(host_int)); + return result; + } +#elif defined(__GLIBC__) + return bswap_64(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 // bswap_64 +} + +inline uint32_t gbswap_32(uint32_t host_int) { +#if defined(__GLIBC__) + return bswap_32(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 defined(__GLIBC__) + return bswap_16(host_int); +#else + return (((host_int & uint16_t{0xFF}) << 8) | + ((host_int & uint16_t{0xFF00}) >> 8)); +#endif +} + +#endif // intrinsics available + +#ifdef ABSL_IS_LITTLE_ENDIAN + +// Definitions for ntohl etc. that don't require us to include +// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather +// than just #defining them because in debug mode, gcc doesn't +// correctly handle the (rather involved) definitions of bswap_32. +// gcc guarantees that inline functions are as fast as macros, so +// this isn't a performance hit. +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 + +// These definitions are simpler on big-endian machines +// These are functions instead of macros to avoid self-assignment warnings +// on calls such as "i = ghtnol(i);". This also provides type checking. +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/exponential_biased.cc b/src/absl/base/internal/exponential_biased.cc new file mode 100644 index 0000000..1b30c06 --- /dev/null +++ b/src/absl/base/internal/exponential_biased.cc @@ -0,0 +1,93 @@ +// 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/exponential_biased.h" + +#include + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// The algorithm generates a random number between 0 and 1 and applies the +// inverse cumulative distribution function for an exponential. Specifically: +// Let m be the inverse of the sample period, then the probability +// distribution function is m*exp(-mx) so the CDF is +// p = 1 - exp(-mx), so +// q = 1 - p = exp(-mx) +// log_e(q) = -mx +// -log_e(q)/m = x +// log_2(q) * (-log_e(2) * 1/m) = x +// In the code, q is actually in the range 1 to 2**26, hence the -26 below +int64_t ExponentialBiased::GetSkipCount(int64_t mean) { + if (ABSL_PREDICT_FALSE(!initialized_)) { + Initialize(); + } + + uint64_t rng = NextRandom(rng_); + rng_ = rng; + + // Take the top 26 bits as the random number + // (This plus the 1<<58 sampling bound give a max possible step of + // 5194297183973780480 bytes.) + // The uint32_t cast is to prevent a (hard-to-reproduce) NAN + // under piii debug for some binaries. + double q = static_cast(rng >> (kPrngNumBits - 26)) + 1.0; + // Put the computed p-value through the CDF of a geometric. + double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean); + // Very large values of interval overflow int64_t. To avoid that, we will + // cheat and clamp any huge values to (int64_t max)/2. This is a potential + // source of bias, but the mean would need to be such a large value that it's + // not likely to come up. For example, with a mean of 1e18, the probability of + // hitting this condition is about 1/1000. For a mean of 1e17, standard + // calculators claim that this event won't happen. + if (interval > static_cast(std::numeric_limits::max() / 2)) { + // Assume huge values are bias neutral, retain bias for next call. + return std::numeric_limits::max() / 2; + } + double value = std::round(interval); + bias_ = interval - value; + return value; +} + +int64_t ExponentialBiased::GetStride(int64_t mean) { + return GetSkipCount(mean - 1) + 1; +} + +void ExponentialBiased::Initialize() { + // We don't get well distributed numbers from `this` so we call NextRandom() a + // bunch to mush the bits around. We use a global_rand to handle the case + // where the same thread (by memory address) gets created and destroyed + // repeatedly. + ABSL_CONST_INIT static std::atomic global_rand(0); + uint64_t r = reinterpret_cast(this) + + global_rand.fetch_add(1, std::memory_order_relaxed); + for (int i = 0; i < 20; ++i) { + r = NextRandom(r); + } + rng_ = r; + initialized_ = true; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/exponential_biased.h b/src/absl/base/internal/exponential_biased.h new file mode 100644 index 0000000..94f79a3 --- /dev/null +++ b/src/absl/base/internal/exponential_biased.h @@ -0,0 +1,130 @@ +// 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_EXPONENTIAL_BIASED_H_ +#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ + +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// ExponentialBiased provides a small and fast random number generator for a +// rounded exponential distribution. This generator manages very little state, +// and imposes no synchronization overhead. This makes it useful in specialized +// scenarios requiring minimum overhead, such as stride based periodic sampling. +// +// ExponentialBiased provides two closely related functions, GetSkipCount() and +// GetStride(), both returning a rounded integer defining a number of events +// required before some event with a given mean probability occurs. +// +// The distribution is useful to generate a random wait time or some periodic +// event with a given mean probability. For example, if an action is supposed to +// happen on average once every 'N' events, then we can get a random 'stride' +// counting down how long before the event to happen. For example, if we'd want +// to sample one in every 1000 'Frobber' calls, our code could look like this: +// +// Frobber::Frobber() { +// stride_ = exponential_biased_.GetStride(1000); +// } +// +// void Frobber::Frob(int arg) { +// if (--stride == 0) { +// SampleFrob(arg); +// stride_ = exponential_biased_.GetStride(1000); +// } +// ... +// } +// +// The rounding of the return value creates a bias, especially for smaller means +// where the distribution of the fraction is not evenly distributed. We correct +// this bias by tracking the fraction we rounded up or down on each iteration, +// effectively tracking the distance between the cumulative value, and the +// rounded cumulative value. For example, given a mean of 2: +// +// raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923 +// raw = 0.14624, cumulative = 1.77701, rounded = 2, bias = 0.14624 +// raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805 +// raw = 0.24206, cumulative = 6.95101, rounded = 7, bias = 0.24206 +// etc... +// +// Adjusting with rounding bias is relatively trivial: +// +// double value = bias_ + exponential_distribution(mean)(); +// double rounded_value = std::round(value); +// bias_ = value - rounded_value; +// return rounded_value; +// +// This class is thread-compatible. +class ExponentialBiased { + public: + // The number of bits set by NextRandom. + static constexpr int kPrngNumBits = 48; + + // `GetSkipCount()` returns the number of events to skip before some chosen + // event happens. For example, randomly tossing a coin, we will on average + // throw heads once before we get tails. We can simulate random coin tosses + // using GetSkipCount() as: + // + // ExponentialBiased eb; + // for (...) { + // int number_of_heads_before_tail = eb.GetSkipCount(1); + // for (int flips = 0; flips < number_of_heads_before_tail; ++flips) { + // printf("head..."); + // } + // printf("tail\n"); + // } + // + int64_t GetSkipCount(int64_t mean); + + // GetStride() returns the number of events required for a specific event to + // happen. See the class comments for a usage example. `GetStride()` is + // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or + // `GetSkipCount()` depends mostly on what best fits the use case. + int64_t GetStride(int64_t mean); + + // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] + // + // This is public to enable testing. + static uint64_t NextRandom(uint64_t rnd); + + private: + void Initialize(); + + uint64_t rng_{0}; + double bias_{0}; + bool initialized_{false}; +}; + +// Returns the next prng value. +// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 +// This is the lrand64 generator. +inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { + const uint64_t prng_mult = uint64_t{0x5DEECE66D}; + const uint64_t prng_add = 0xB; + const uint64_t prng_mod_power = 48; + const uint64_t prng_mod_mask = + ~((~static_cast(0)) << prng_mod_power); + return (prng_mult * rnd + prng_add) & prng_mod_mask; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_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..3db59e8 --- /dev/null +++ b/src/absl/base/internal/fast_type_id.h @@ -0,0 +1,48 @@ +// +// 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; +}; + +template +constexpr char FastTypeTag::dummy_var; + +// 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..5c71f32 --- /dev/null +++ b/src/absl/base/internal/invoke.h @@ -0,0 +1,187 @@ +// 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. +// +// [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 +#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) { + return (std::forward(obj).* + std::forward(mem_fun))(std::forward(args)...); + } +}; + +// ((*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)...); +} +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#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..db91951 --- /dev/null +++ b/src/absl/base/internal/low_level_alloc.h @@ -0,0 +1,126 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ +#define ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ + +// A simple thread-safe memory allocator that does not depend on +// mutexes or thread-specific data. It is intended to be used +// sparingly, and only when malloc() would introduce an unwanted +// dependency, such as inside the heap-checker, or the Mutex +// implementation. + +// IWYU pragma: private, include "base/low_level_alloc.h" + +#include + +#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/periodic_sampler.cc b/src/absl/base/internal/periodic_sampler.cc new file mode 100644 index 0000000..520dabb --- /dev/null +++ b/src/absl/base/internal/periodic_sampler.cc @@ -0,0 +1,53 @@ +// 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/periodic_sampler.h" + +#include + +#include "absl/base/internal/exponential_biased.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept { + return rng_.GetStride(period); +} + +bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { + int current_period = period(); + + // Deal with period case 0 (always off) and 1 (always on) + if (ABSL_PREDICT_FALSE(current_period < 2)) { + stride_ = 0; + return current_period == 1; + } + + // Check if this is the first call to Sample() + if (ABSL_PREDICT_FALSE(stride_ == 1)) { + stride_ = static_cast(-GetExponentialBiased(current_period)); + if (static_cast(stride_) < -1) { + ++stride_; + return false; + } + } + + stride_ = static_cast(-GetExponentialBiased(current_period)); + return true; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/base/internal/periodic_sampler.h b/src/absl/base/internal/periodic_sampler.h new file mode 100644 index 0000000..f8a8679 --- /dev/null +++ b/src/absl/base/internal/periodic_sampler.h @@ -0,0 +1,211 @@ +// 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_PERIODIC_SAMPLER_H_ +#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ + +#include + +#include + +#include "absl/base/internal/exponential_biased.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// PeriodicSamplerBase provides the basic period sampler implementation. +// +// This is the base class for the templated PeriodicSampler class, which holds +// a global std::atomic value identified by a user defined tag, such that +// each specific PeriodSampler implementation holds its own global period. +// +// PeriodicSamplerBase is thread-compatible except where stated otherwise. +class PeriodicSamplerBase { + public: + // PeriodicSamplerBase is trivial / copyable / movable / destructible. + PeriodicSamplerBase() = default; + PeriodicSamplerBase(PeriodicSamplerBase&&) = default; + PeriodicSamplerBase(const PeriodicSamplerBase&) = default; + + // Returns true roughly once every `period` calls. This is established by a + // randomly picked `stride` that is counted down on each call to `Sample`. + // This stride is picked such that the probability of `Sample()` returning + // true is 1 in `period`. + inline bool Sample() noexcept; + + // The below methods are intended for optimized use cases where the + // size of the inlined fast path code is highly important. Applications + // should use the `Sample()` method unless they have proof that their + // specific use case requires the optimizations offered by these methods. + // + // An example of such a use case is SwissTable sampling. All sampling checks + // are in inlined SwissTable methods, and the number of call sites is huge. + // In this case, the inlined code size added to each translation unit calling + // SwissTable methods is non-trivial. + // + // The `SubtleMaybeSample()` function spuriously returns true even if the + // function should not be sampled, applications MUST match each call to + // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call, + // and use the result of the latter as the sampling decision. + // In other words: the code should logically be equivalent to: + // + // if (SubtleMaybeSample() && SubtleConfirmSample()) { + // // Sample this call + // } + // + // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can + // be placed out of line, for example, the typical use case looks as follows: + // + // // --- frobber.h ----------- + // void FrobberSampled(); + // + // inline void FrobberImpl() { + // // ... + // } + // + // inline void Frobber() { + // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) { + // FrobberSampled(); + // } else { + // FrobberImpl(); + // } + // } + // + // // --- frobber.cc ----------- + // void FrobberSampled() { + // if (!sampler.SubtleConfirmSample())) { + // // Spurious false positive + // FrobberImpl(); + // return; + // } + // + // // Sampled execution + // // ... + // } + inline bool SubtleMaybeSample() noexcept; + bool SubtleConfirmSample() noexcept; + + protected: + // We explicitly don't use a virtual destructor as this class is never + // virtually destroyed, and it keeps the class trivial, which avoids TLS + // prologue and epilogue code for our TLS instances. + ~PeriodicSamplerBase() = default; + + // Returns the next stride for our sampler. + // This function is virtual for testing purposes only. + virtual int64_t GetExponentialBiased(int period) noexcept; + + private: + // Returns the current period of this sampler. Thread-safe. + virtual int period() const noexcept = 0; + + // Keep and decrement stride_ as an unsigned integer, but compare the value + // to zero casted as a signed int. clang and msvc do not create optimum code + // if we use signed for the combined decrement and sign comparison. + // + // Below 3 alternative options, all compiles generate the best code + // using the unsigned increment <---> signed int comparison option. + // + // Option 1: + // int64_t stride_; + // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA + // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt + // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd + // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W + // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS + // + // Option 2: + // int64_t stride_ = 0; + // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK + // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA + // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX + // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd + // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE + // + // Option 3: + // uint64_t stride_; + // if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy + // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE + // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4 + // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD + // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5 + uint64_t stride_ = 0; + ExponentialBiased rng_; +}; + +inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { + // See comments on `stride_` for the unsigned increment / signed compare. + if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { + return false; + } + return true; +} + +inline bool PeriodicSamplerBase::Sample() noexcept { + return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample() + : false; +} + +// PeriodicSampler is a concreted periodic sampler implementation. +// The user provided Tag identifies the implementation, and is required to +// isolate the global state of this instance from other instances. +// +// Typical use case: +// +// struct HashTablezTag {}; +// thread_local PeriodicSampler sampler; +// +// void HashTableSamplingLogic(...) { +// if (sampler.Sample()) { +// HashTableSlowSamplePath(...); +// } +// } +// +template +class PeriodicSampler final : public PeriodicSamplerBase { + public: + ~PeriodicSampler() = default; + + int period() const noexcept final { + return period_.load(std::memory_order_relaxed); + } + + // Sets the global period for this sampler. Thread-safe. + // Setting a period of 0 disables the sampler, i.e., every call to Sample() + // will return false. Setting a period of 1 puts the sampler in 'always on' + // mode, i.e., every call to Sample() returns true. + static void SetGlobalPeriod(int period) { + period_.store(period, std::memory_order_relaxed); + } + + private: + static std::atomic period_; +}; + +template +std::atomic PeriodicSampler::period_(default_period); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_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..985efec --- /dev/null +++ b/src/absl/base/internal/raw_logging.cc @@ -0,0 +1,245 @@ +#include "cpp-compat.h" +// 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 "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/atomic_hook.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(__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(__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. + +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES + absl::base_internal::AtomicHook + log_prefix_hook; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES + absl::base_internal::AtomicHook + abort_hook; + +#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; +} + +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 + + auto log_prefix_hook_ptr = log_prefix_hook.Load(); + if (log_prefix_hook_ptr) { + enabled = log_prefix_hook_ptr(severity, file, line, &buf, &size); + } else { + if (enabled) { + DoRawLog(&buf, &size, "[%s : %d] RAW: ", file, line); + } + } + 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); + } + SafeWriteToStderr(buffer, strlen(buffer)); + } +#else + static_cast(format); + static_cast(ap); +#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); + // dd: R CMD check will fail if abort() is used + // abort(); + cpp_compat_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 SafeWriteToStderr(const char *s, size_t len) { +#if defined(ABSL_HAVE_SYSCALL_WRITE) + 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 RegisterLogPrefixHook(LogPrefixHook func) { log_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..47d7af8 --- /dev/null +++ b/src/absl/base/internal/raw_logging.h @@ -0,0 +1,205 @@ +// 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 + +// __VA_ARGS__ isn't passed along properly on Windows and we don't ever log +// to stderr because R CMD check won't let us; so ignore log messages. +#ifdef _WIN32 + +#define ABSL_RAW_LOG(severity, ...) + +#else + +#define ABSL_RAW_LOG(severity, ...) \ + do { \ + constexpr const char* absl_raw_logging_internal_basename = \ + ::absl::raw_logging_internal::Basename(__FILE__, \ + sizeof(__FILE__) - 1); \ + ::absl::raw_logging_internal::RawLog(ABSL_RAW_LOGGING_INTERNAL_##severity, \ + absl_raw_logging_internal_basename, \ + __LINE__, __VA_ARGS__); \ + } while (0) \ + +#endif + +// 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 safe, low-level manner. +// +// In POSIX this means calling write(), which is async-signal safe and does +// not malloc. If the platform supports the SYS_write syscall, we invoke that +// directly to side-step any libc interception. +void SafeWriteToStderr(const char *s, size_t len); + +// compile-time function to get the "base" filename, that is, the part of +// a filename after the last "/" or "\" path separator. The search starts at +// the end of the 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. +// 'buffer' and 'buf_size' are pointers to the buffer and buffer size. If the +// hook writes a prefix, it must increment *buffer and decrement *buf_size +// accordingly. +using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, + int line, char** buffer, 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 LogPrefixHook.) +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 RegisterLogPrefixHook(LogPrefixHook 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..35c0696 --- /dev/null +++ b/src/absl/base/internal/spinlock.cc @@ -0,0 +1,229 @@ +// 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/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); +} + +// 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; + +// 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..c73b5e0 --- /dev/null +++ b/src/absl/base/internal/spinlock.h @@ -0,0 +1,246 @@ +// +// 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 in 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. + +#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; + } + + 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..202f7cd --- /dev/null +++ b/src/absl/base/internal/spinlock_linux.inc @@ -0,0 +1,74 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 loop, + absl::base_internal::SchedulingMode) { + absl::base_internal::ErrnoSaver errno_saver; + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); + syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); +} + +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..579bd09 --- /dev/null +++ b/src/absl/base/internal/spinlock_wait.h @@ -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. + +#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..7d264d8 --- /dev/null +++ b/src/absl/base/internal/sysinfo.cc @@ -0,0 +1,444 @@ +#include "cpp-compat.h" +// 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 { + +static int GetNumCPUs() { +#if defined(__myriad2__) + return 1; +#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"); + // dd: R CMD check will fail if abort() is used + // abort(); + cpp_compat_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"); + // dd: R CMD check will fail if abort() is used + // abort(); + cpp_compat_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"); + cpp_compat_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..9950e63 --- /dev/null +++ b/src/absl/base/internal/thread_identity.cc @@ -0,0 +1,155 @@ +// 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" + +#ifndef _WIN32 +#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. +#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..6e25b92 --- /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 direcly 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 direcly 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 direcly set +#else +#define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2 +#endif + +#ifdef ABSL_THREAD_IDENTITY_MODE +#error ABSL_THREAD_IDENTITY_MODE cannot be direcly 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..1545288 --- /dev/null +++ b/src/absl/base/internal/unscaledcycleclock.cc @@ -0,0 +1,138 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#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__) +#include +#include +#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__) + +int64_t UnscaledCycleClock::Now() { + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + return (high << 32) | low; +} + +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(__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(_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..82f2c87 --- /dev/null +++ b/src/absl/base/internal/unscaledcycleclock.h @@ -0,0 +1,124 @@ +// 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(_M_IX86) || defined(_M_X64) +#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(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ + (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(_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; +}; + +} // 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..72312af --- /dev/null +++ b/src/absl/base/log_severity.cc @@ -0,0 +1,27 @@ +// 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 + +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) << ")"; +} +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..2236422 --- /dev/null +++ b/src/absl/base/log_severity.h @@ -0,0 +1,121 @@ +// 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); + +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..d090be1 --- /dev/null +++ b/src/absl/base/optimization.h @@ -0,0 +1,244 @@ +// +// 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_INTERNAL_ASSUME(cond) +// Informs the compiler that a condition is always true and that it can assume +// it to be true for optimization purposes. The call has undefined behavior if +// the condition is false. +// In !NDEBUG mode, the condition is checked with an assert(). +// NOTE: The expression must not have side effects, as it will only be evaluated +// in some compilation modes and not others. +// +// Example: +// +// int x = ...; +// ABSL_INTERNAL_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_INTERNAL_ASSUME(cond) assert(cond) +#elif ABSL_HAVE_BUILTIN(__builtin_assume) +#define ABSL_INTERNAL_ASSUME(cond) __builtin_assume(cond) +#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_INTERNAL_ASSUME(cond) \ + do { \ + if (!(cond)) __builtin_unreachable(); \ + } while (0) +#elif defined(_MSC_VER) +#define ABSL_INTERNAL_ASSUME(cond) __assume(cond) +#else +#define ABSL_INTERNAL_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..eca879a --- /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_20210324 + +// 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..9695f6d --- /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 acquired in the body of the function. These locks +// cannot be held when calling this function (as 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..ea49d44 --- /dev/null +++ b/src/absl/container/btree_map.h @@ -0,0 +1,768 @@ +// 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. +// +// 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_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 + +// 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 is provided 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 is provided 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 is provided a + // compatible heterogeneous comparator. + using Base::find; + + // 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. +template +void erase_if(btree_map &map, Pred pred) { + for (auto it = map.begin(); it != map.end();) { + if (pred(*it)) { + it = map.erase(it); + } else { + ++it; + } + } +} + +// 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 elements from a given `source` btree_multimap into this + // `btree_multimap`. If the destination `btree_multimap` already contains an + // element with an equivalent key, that element is not extracted. + 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 is provided 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 is provided 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 is provided a + // compatible heterogeneous comparator. + using Base::find; + + // 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. +template +void erase_if(btree_multimap &map, Pred pred) { + for (auto it = map.begin(); it != map.end();) { + if (pred(*it)) { + it = map.erase(it); + } else { + ++it; + } + } +} + +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..21ef0a0 --- /dev/null +++ b/src/absl/container/btree_set.h @@ -0,0 +1,683 @@ +// 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. +// +// 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 + +// 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 is provided 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 is provided 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 is provided a + // compatible heterogeneous comparator. + using Base::find; + + // 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. +template +void erase_if(btree_set &set, Pred pred) { + for (auto it = set.begin(); it != set.end();) { + if (pred(*it)) { + it = set.erase(it); + } else { + ++it; + } + } +} + +// 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 elements from a given `source` btree_multiset into this + // `btree_multiset`. If the destination `btree_multiset` already contains an + // element with an equivalent key, that element is not extracted. + 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 is provided 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 is provided 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 is provided a + // compatible heterogeneous comparator. + using Base::find; + + // 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. +template +void erase_if(btree_multiset &set, Pred pred) { + for (auto it = set.begin(); it != set.end();) { + if (pred(*it)) { + it = set.erase(it); + } else { + ++it; + } + } +} + +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..fcb3e54 --- /dev/null +++ b/src/absl/container/fixed_array.h @@ -0,0 +1,532 @@ +// 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`. +// +// Note that `FixedArray` does not provide a public allocator; if it requires a +// heap allocation, it will do so with global `::operator new[]()` and +// `::operator delete[]()`, even if T provides class-scope overrides for these +// operators. +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_; +}; + +template +constexpr size_t FixedArray::kInlineBytesDefault; + +template +constexpr typename FixedArray::size_type + FixedArray::inline_elements; + +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..74def0d --- /dev/null +++ b/src/absl/container/flat_hash_map.h @@ -0,0 +1,606 @@ +// 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/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. +// +// 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 init_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + // + // 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`. +template +void erase_if(flat_hash_map& c, Predicate pred) { + 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..6b89da6 --- /dev/null +++ b/src/absl/container/flat_hash_set.h @@ -0,0 +1,504 @@ +// 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_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// NOTE: A `flat_hash_set` stores its keys directly inside its implementation +// array to avoid memory indirection. Because a `flat_hash_set` is designed to +// move data when rehashed, set keys will not retain pointer stability. If you +// require pointer stability, consider using +// `absl::flat_hash_set>`. 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_map` + // 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_map` API. + using Base::capacity; + + // flat_hash_set::empty() + // + // Returns whether or not the `flat_hash_set` is empty. + using Base::empty; + + // flat_hash_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `flat_hash_set` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `flat_hash_set`. + 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 map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `flat_hash_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash set's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_set::rehash(count) + // + // Rehashes the `flat_hash_set`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_set`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_set::reserve(count) + // + // Sets the number of slots in the `flat_hash_set` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_set::contains() + // + // Determines whether an element comparing equal to the given `key` exists + // within the `flat_hash_set`, returning `true` if so or `false` otherwise. + using Base::contains; + + // flat_hash_set::count(const Key& key) const + // + // Returns the number of elements comparing equal to the given `key` within + // the `flat_hash_set`. note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `flat_hash_set`. + using Base::count; + + // flat_hash_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_set`. + using Base::equal_range; + + // flat_hash_set::find() + // + // Finds an element with the passed `key` within the `flat_hash_set`. + using Base::find; + + // flat_hash_set::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_set`. Note that + // because a flat hash map contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_set`. + using Base::bucket_count; + + // flat_hash_set::load_factor() + // + // Returns the current load factor of the `flat_hash_set` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // flat_hash_set::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_set`. Overloads are + // listed below. + // + // float flat_hash_set::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_set`. + // + // void flat_hash_set::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_set` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_set` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_set::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_set`. + using Base::get_allocator; + + // flat_hash_set::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_set`. + using Base::hash_function; + + // flat_hash_set::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +// erase_if(flat_hash_set<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +void erase_if(flat_hash_set& c, Predicate pred) { + 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..7c18234 --- /dev/null +++ b/src/absl/container/inlined_vector.h @@ -0,0 +1,847 @@ +// 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 + +#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; + + using AllocatorTraits = typename Storage::AllocatorTraits; + using RValueReference = typename Storage::RValueReference; + using MoveIterator = typename Storage::MoveIterator; + using IsMemcpyOk = typename Storage::IsMemcpyOk; + + template + using IteratorValueAdapter = + typename Storage::template IteratorValueAdapter; + using CopyValueAdapter = typename Storage::CopyValueAdapter; + using DefaultValueAdapter = typename Storage::DefaultValueAdapter; + + template + using EnableIfAtLeastForwardIterator = absl::enable_if_t< + inlined_vector_internal::IsAtLeastForwardIterator::value>; + template + using DisableIfAtLeastForwardIterator = absl::enable_if_t< + !inlined_vector_internal::IsAtLeastForwardIterator::value>; + + public: + using allocator_type = typename Storage::allocator_type; + using value_type = typename Storage::value_type; + using pointer = typename Storage::pointer; + using const_pointer = typename Storage::const_pointer; + using size_type = typename Storage::size_type; + using difference_type = typename Storage::difference_type; + using reference = typename Storage::reference; + using const_reference = typename Storage::const_reference; + using iterator = typename Storage::iterator; + using const_iterator = typename Storage::const_iterator; + using reverse_iterator = typename Storage::reverse_iterator; + using const_reverse_iterator = typename Storage::const_reverse_iterator; + + // --------------------------------------------------------------------------- + // 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 `alloc`. + explicit InlinedVector(const allocator_type& alloc) noexcept + : storage_(alloc) {} + + // Creates an inlined vector with `n` copies of `value_type()`. + explicit InlinedVector(size_type n, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + storage_.Initialize(DefaultValueAdapter(), n); + } + + // Creates an inlined vector with `n` copies of `v`. + InlinedVector(size_type n, const_reference v, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + storage_.Initialize(CopyValueAdapter(v), n); + } + + // Creates an inlined vector with copies of the elements of `list`. + InlinedVector(std::initializer_list list, + const allocator_type& alloc = allocator_type()) + : InlinedVector(list.begin(), list.end(), alloc) {} + + // 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 * = nullptr> + InlinedVector(ForwardIterator first, ForwardIterator last, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + storage_.Initialize(IteratorValueAdapter(first), + std::distance(first, last)); + } + + // Creates an inlined vector with elements constructed from the provided input + // iterator range [`first`, `last`). + template * = nullptr> + InlinedVector(InputIterator first, InputIterator last, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + 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_.GetAllocPtr()) {} + + // Creates an inlined vector by copying the contents of `other` using `alloc`. + InlinedVector(const InlinedVector& other, const allocator_type& alloc) + : storage_(alloc) { + 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_.GetAllocPtr()) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if (other.storage_.GetIsAllocated()) { + storage_.SetAllocatedData(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_.GetAllocPtr(), 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 `alloc`. + // + // NOTE: if `other`'s allocator is not equal to `alloc`, 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& alloc) noexcept( + absl::allocator_is_nothrow::value) + : storage_(alloc) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) && + other.storage_.GetIsAllocated()) { + storage_.SetAllocatedData(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_.GetAllocPtr(); } + + // --------------------------------------------------------------------------- + // 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::DestroyElements(storage_.GetAllocPtr(), 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(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 * = nullptr> + void assign(ForwardIterator first, ForwardIterator last) { + storage_.Assign(IteratorValueAdapter(first), + 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 * = nullptr> + 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(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, RValueReference 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; + return storage_.Insert(pos, CopyValueAdapter(dealias), n); + } 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 * = nullptr> + 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 * = nullptr> + 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(RValueReference 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_.GetAllocPtr(), 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::DestroyElements(storage_.GetAllocPtr(), 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()` + // + // Reduces memory usage by freeing unused memory. After being called, calls to + // `capacity()` will be equal to `max(N, size())`. + // + // If `size() <= N` and the inlined vector contains allocated memory, the + // elements will all be moved to the inlined space and the allocated memory + // will be deallocated. + // + // If `size() > N` and `size() < capacity()`, the elements will be moved to a + // smaller allocation. + 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..0bb3836 --- /dev/null +++ b/src/absl/container/internal/btree.h @@ -0,0 +1,2620 @@ +// 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/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 { + +// 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 + + 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 + + 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)); + } +}; + +// A helper class to convert a boolean comparison into a three-way "compare-to" +// comparison that returns an `absl::weak_ordering`. This helper +// class is specialized for less, greater, +// less, greater, less, and +// greater. +// +// key_compare_to_adapter is provided so that btree users +// automatically get the more efficient compare-to code when using common +// Abseil string types with common comparison functors. +// These string-like specializations also turn on heterogeneous lookup by +// default. +template +struct key_compare_to_adapter { + using type = Compare; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultGreater; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultGreater; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_to_adapter> { + 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 +struct common_params { + // 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. + using key_compare = typename key_compare_to_adapter::type; + // 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 = std::make_signed::type; + 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 &; + + // 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 Multi || + (IsTransparent::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value); + } + + enum { + kTargetNodeSize = TargetNodeSize, + + // Upper bound for the available space for values. This is largest for leaf + // nodes, which have overhead of at least a pointer + 4 bytes (for storing + // 3 field_types and an enum). + kNodeValueSpace = + TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4), + }; + + // This is an integral type large enough to hold as many + // ValueSize-values as will fit a node of TargetNodeSize bytes. + using node_count_type = + absl::conditional_t<(kNodeValueSpace / sizeof(value_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) { + construct(alloc, new_slot, old_slot); + destroy(alloc, old_slot); + } + static void swap(Alloc *alloc, slot_type *a, slot_type *b) { + slot_policy::swap(alloc, a, b); + } + static void move(Alloc *alloc, slot_type *src, slot_type *dest) { + slot_policy::move(alloc, src, dest); + } +}; + +// 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; + + using key_compare = typename super_type::key_compare; + // Inherit from key_compare for empty base class optimization. + struct value_compare : private key_compare { + value_compare() = default; + explicit value_compare(const key_compare &cmp) : key_compare(cmp) {} + + template + auto operator()(const T &left, const U &right) const + -> decltype(std::declval()(left.first, right.first)) { + return key_compare::operator()(left.first, right.first); + } + }; + using is_map_container = std::true_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; } +}; + +// This type implements the necessary functions from the +// absl::container_internal::slot_type interface. +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 destroy(Alloc *alloc, slot_type *slot) { + absl::allocator_traits::destroy(*alloc, slot); + } + + template + static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) { + using std::swap; + swap(*a, *b); + } + + template + static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) { + *dest = std::move(*src); + } +}; + +// 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; + using value_compare = typename set_params::common_params::key_compare; + using is_map_container = std::false_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; } +}; + +// 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 value) : value(value) {} + SearchResult(V value, MatchKind /*match*/) : value(value) {} + + 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; + + 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, key_compare>::value || + std::is_same, + key_compare>::value)>; + + // This class is organized by gtl::Layout as if it had the following + // structure: + // // A pointer to the node's parent. + // btree_node *parent; + // + // // 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, + /*position, start, finish, max_count*/ 4, + /*slots*/ n, + /*children*/ 0) + .AllocSize(); + } + // A lower bound for the overhead of fields other than values in a leaf node. + constexpr static size_type MinimumOverhead() { + return SizeWithNSlots(1) - sizeof(value_type); + } + + // Compute how many values we can fit onto a leaf node taking into account + // padding. + constexpr static size_type NodeTargetSlots(const int begin, const int 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, + /*position, start, finish, max_count*/ 4, + /*slots*/ slot_count, + /*children*/ 0); + } + constexpr static layout_type InternalLayout() { + return layout_type(/*parent*/ 1, + /*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 < 3 || !leaf()); + return InternalLayout().template Pointer(reinterpret_cast(this)); + } + template + inline const typename layout_type::template ElementType *GetField() const { + assert(N < 3 || !leaf()); + return InternalLayout().template Pointer( + reinterpret_cast(this)); + } + void set_parent(btree_node *p) { *GetField<0>() = p; } + field_type &mutable_finish() { return GetField<1>()[2]; } + slot_type *slot(int i) { return &GetField<2>()[i]; } + slot_type *start_slot() { return slot(start()); } + slot_type *finish_slot() { return slot(finish()); } + const slot_type *slot(int i) const { return &GetField<2>()[i]; } + void set_position(field_type v) { GetField<1>()[0] = v; } + void set_start(field_type v) { GetField<1>()[1] = v; } + void set_finish(field_type v) { GetField<1>()[2] = v; } + // This method is only called by the node init methods. + void set_max_count(field_type v) { GetField<1>()[3] = v; } + + public: + // Whether this is a leaf node or not. This value doesn't change after the + // node is created. + bool leaf() const { return GetField<1>()[3] != kInternalNodeMaxCount; } + + // Getter for the position of this node in its parent. + field_type position() const { return GetField<1>()[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<1>()[1]; + assert(GetField<1>()[1] == 0); + return 0; + } + + // Getter for the offset after the last value in the `values` array. + field_type finish() const { return GetField<1>()[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<1>()[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()->leaf(); } + void make_root() { + assert(parent()->is_root()); + set_parent(parent()->parent()); + } + + // 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<3>()[i]; } + btree_node *start_child() const { return child(start()); } + btree_node *&mutable_child(int i) { return GetField<3>()[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(btree_node *parent, int max_count) { + 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(parent, kNodeSlots); + // 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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 struct btree_iterator; + friend class BtreeNodePeer; +}; + +template +struct btree_iterator { + private: + 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() : node(nullptr), position(-1) {} + explicit btree_iterator(Node *n) : node(n), position(n->start()) {} + btree_iterator(Node *n, int p) : node(n), position(p) {} + + // 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) {} + + private: + // 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) {} + + // Increment/decrement the iterator. + void increment() { + if (node->leaf() && ++position < node->finish()) { + return; + } + increment_slow(); + } + void increment_slow(); + + void decrement() { + if (node->leaf() && --position >= node->start()) { + return; + } + decrement_slow(); + } + void decrement_slow(); + + public: + 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); + 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; + + const key_type &key() const { return node->key(position); } + slot_type *slot() { return node->slot(position); } + + // 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; +}; + +template +class btree { + using node_type = btree_node; + using is_key_compare_to = typename Params::is_key_compare_to; + using init_type = typename Params::init_type; + 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; + 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 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: + // For use in copy_or_move_values_in_order. + const value_type &maybe_move_from_iterator(const_iterator it) { return *it; } + value_type &&maybe_move_from_iterator(iterator it) { + // This is a destructive operation on the other container so it's safe for + // us to const_cast and move from the keys here even if it's a set. + return std::move(const_cast(*it)); + } + + // 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_(comp, alloc, EmptyNode()), rightmost_(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_(std::move(other.root_)), + rightmost_(absl::exchange(other.rightmost_, EmptyNode())), + size_(absl::exchange(other.size_, 0)) { + other.mutable_root() = 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 root_.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(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. + 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: + // Internal accessor routines. + node_type *root() { return root_.template get<2>(); } + const node_type *root() const { return root_.template get<2>(); } + node_type *&mutable_root() noexcept { return root_.template get<2>(); } + key_compare *mutable_key_comp() noexcept { return &root_.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 &root_.template get<1>(); + } + const allocator_type &allocator() const noexcept { + return root_.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(parent, kNodeSlots); + return n; + } + node_type *new_leaf_root_node(const int max_count) { + node_type *n = allocate(node_type::LeafSize(max_count)); + n->init_leaf(/*parent=*/n, max_count); + 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->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; + } + + // We use compressed tuple in order to save space because key_compare and + // allocator_type are usually empty. + absl::container_internal::CompressedTuple + root_; + + // A pointer to the rightmost node. Note that the leftmost node is stored as + // the root's parent. + node_type *rightmost_; + + // Number of values. + size_type size_; +}; + +//// +// btree_node methods +template +template +inline void btree_node

::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 (!leaf() && finish() > i + 1) { + for (int 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 (!leaf()) { + // 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 (!leaf()) { + // 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 (!leaf()) { + // 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 (!leaf()) { + 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 (!leaf()) { + // 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->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->leaf()) node = node->start_child(); + // 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->leaf()) { + // Navigate to the leftmost leaf under node. + while (!node->leaf()) node = node->start_child(); + pos = node->position(); + parent = node->parent(); + } + node->value_destroy_n(node->start(), node->count(), alloc); + 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) return; + ++pos; + } while (pos > parent->finish()); + } +} + +//// +// btree_iterator methods +template +void btree_iterator::increment_slow() { + if (node->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->leaf()) { + node = node->start_child(); + } + position = node->start(); + } +} + +template +void btree_iterator::decrement_slow() { + if (node->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->leaf()) { + 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(maybe_move_from_iterator(iter)); + ++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(), maybe_move_from_iterator(iter)); + } +} + +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. + using compare_result_type = + absl::result_of_t; + static_assert( + std::is_same::value || + std::is_convertible::value, + "key comparison function must return absl::{weak,strong}_ordering or " + "bool."); + + // Test the assumption made in setting kNodeValueSpace. + 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() = 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) { + init_type value(*b); + insert_hint_unique(end(), params_type::key(value), std::move(value)); + } +} + +template +template +auto btree

::insert_multi(const key_type &key, ValueType &&v) -> iterator { + if (empty()) { + mutable_root() = 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) { + // Note: `root_` also contains the allocator and the key comparator. + swap(root_, other.root_); + 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(rightmost_, other.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 { + bool internal_delete = false; + if (!iter.node->leaf()) { + // Deletion of a value on an internal node. First, move the largest value + // from our left child here, then delete that position (in remove_values() + // below). We can get to the largest value from our left child by + // decrementing iter. + iterator internal_iter(iter); + --iter; + assert(iter.node->leaf()); + params_type::move(mutable_allocator(), iter.node->slot(iter.position), + internal_iter.node->slot(internal_iter.position)); + internal_delete = true; + } + + // Delete the key from the leaf. + iter.node->remove_values(iter.position, /*to_erase=*/1, mutable_allocator()); + --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(); + } + + // 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 (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->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); + } + } + return {count, begin}; +} + +template +void btree

::clear() { + if (!empty()) { + node_type::clear_and_delete(root(), mutable_allocator()); + } + mutable_root() = EmptyNode(); + 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: `root_` also contains the allocator and the key comparator. + swap(root_, other.root_); + } else { + // It's undefined behavior if the allocators are unequal here. + assert(allocator() == other.allocator()); + swap(mutable_root(), other.mutable_root()); + swap(*mutable_key_comp(), *other.mutable_key_comp()); + } + swap(rightmost_, other.rightmost_); + 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()->leaf()); + assert(rightmost_->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->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()->leaf() || + parent->start_child() == rightmost_); + } + + // Split the node. + node_type *split_node; + if (node->leaf()) { + split_node = new_leaf_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + if (rightmost_ == node) 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) 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->leaf()) { + assert(size() == 0); + mutable_root() = 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->leaf()) { + iter.node = nullptr; + break; + } + } + return iter; +} + +template +template +inline auto btree

::internal_emplace(iterator iter, Args &&... args) + -> iterator { + if (!iter.node->leaf()) { + // 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()); + node_type::clear_and_delete(old_root, alloc); + mutable_root() = rightmost_ = new_root; + } else { + rebalance_or_split(&iter); + } + } + iter.node->emplace_value(iter.position, alloc, std::forward(args)...); + ++size_; + 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->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->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->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->leaf()) { + 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; +} + +} // 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..03be708 --- /dev/null +++ b/src/absl/container/internal/btree_container.h @@ -0,0 +1,682 @@ +// 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/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::value>:: + template 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::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 Move instead of Transfer, because the rebalancing code expects to + // have a valid object to scribble metadata bits on top of. + auto node = CommonAccess::Move(get_allocator(), position.slot()); + erase(position); + return node; + } + node_type extract(const_iterator position) { + return extract(iterator(position)); + } + + // Utility routines. + 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 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: + 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::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) { + init_type v(std::forward(args)...); + return this->tree_.insert_unique(params_type::key(v), std::move(v)); + } + 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) { + init_type v(std::forward(args)...); + return this->tree_ + .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v)) + .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::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; + + 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::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) { + return this->tree_.insert_multi(init_type(std::forward(args)...)); + } + template + iterator emplace_hint(const_iterator hint, Args &&... args) { + return this->tree_.insert_hint_multi( + iterator(hint), init_type(std::forward(args)...)); + } + 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; + + 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..030e9d4 --- /dev/null +++ b/src/absl/container/internal/common.h @@ -0,0 +1,206 @@ +// 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 move_tag_t {}; + node_handle_base(move_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::construct(alloc(), slot(), s); + } + + void destroy() { + if (!empty()) { + PolicyTraits::destroy(alloc(), slot()); + reset(); + } + } + + void reset() { + assert(alloc_.has_value()); + alloc_ = absl::nullopt; + } + + slot_type* slot() const { + assert(!empty()); + return reinterpret_cast(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 Move(Args&&... args) { + return T(typename T::move_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..e67529e --- /dev/null +++ b/src/absl/container/internal/container_memory.h @@ -0,0 +1,460 @@ +// 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(p.first), std::move(p.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)); + } + } + + 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); + } + + template + static void swap(Allocator* alloc, slot_type* a, slot_type* b) { + if (kMutableKeys::value) { + using std::swap; + swap(a->mutable_value, b->mutable_value); + } else { + value_type tmp = std::move(a->value); + absl::allocator_traits::destroy(*alloc, &a->value); + absl::allocator_traits::construct(*alloc, &a->value, + std::move(b->value)); + absl::allocator_traits::destroy(*alloc, &b->value); + absl::allocator_traits::construct(*alloc, &b->value, + std::move(tmp)); + } + } + + template + static void move(Allocator* alloc, slot_type* src, slot_type* dest) { + if (kMutableKeys::value) { + dest->mutable_value = std::move(src->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &dest->value); + absl::allocator_traits::construct(*alloc, &dest->value, + std::move(src->value)); + } + } +}; + +} // 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..927cf08 --- /dev/null +++ b/src/absl/container/internal/counting_allocator.h @@ -0,0 +1,114 @@ +// 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; + AllocatorTraits::destroy(allocator, p); + 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..0683422 --- /dev/null +++ b/src/absl/container/internal/hash_function_defaults.h @@ -0,0 +1,161 @@ +// 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); + } +}; + +// Supports heterogeneous lookup for string-like elements. +struct StringHashEq { + using Hash = StringHash; + struct Eq { + using is_transparent = void; + bool operator()(absl::string_view lhs, absl::string_view rhs) const { + return lhs == rhs; + } + 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; + } + }; +}; + +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..5a29bed --- /dev/null +++ b/src/absl/container/internal/hashtablez_sampler.cc @@ -0,0 +1,274 @@ +// 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/internal/exponential_biased.h" +#include "absl/container/internal/have_sse.h" +#include "absl/debugging/stacktrace.h" +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +constexpr int HashtablezInfo::kMaxStackDepth; + +namespace { +ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ + false +}; +ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; +ABSL_CONST_INIT std::atomic g_hashtablez_max_samples{1 << 20}; + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased + g_exponential_biased_generator; +#endif + +} // namespace + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +HashtablezSampler& HashtablezSampler::Global() { + static auto* sampler = new HashtablezSampler(); + return *sampler; +} + +HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback( + DisposeCallback f) { + return dispose_.exchange(f, std::memory_order_relaxed); +} + +HashtablezInfo::HashtablezInfo() { PrepareForSampling(); } +HashtablezInfo::~HashtablezInfo() = default; + +void HashtablezInfo::PrepareForSampling() { + 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); + + create_time = absl::Now(); + // 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); + dead = nullptr; +} + +HashtablezSampler::HashtablezSampler() + : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) { + absl::MutexLock l(&graveyard_.init_mu); + graveyard_.dead = &graveyard_; +} + +HashtablezSampler::~HashtablezSampler() { + HashtablezInfo* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + HashtablezInfo* next = s->next; + delete s; + s = next; + } +} + +void HashtablezSampler::PushNew(HashtablezInfo* sample) { + sample->next = all_.load(std::memory_order_relaxed); + while (!all_.compare_exchange_weak(sample->next, sample, + std::memory_order_release, + std::memory_order_relaxed)) { + } +} + +void HashtablezSampler::PushDead(HashtablezInfo* sample) { + if (auto* dispose = dispose_.load(std::memory_order_relaxed)) { + dispose(*sample); + } + + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + absl::MutexLock sample_lock(&sample->init_mu); + sample->dead = graveyard_.dead; + graveyard_.dead = sample; +} + +HashtablezInfo* HashtablezSampler::PopDead() { + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + + // The list is circular, so eventually it collapses down to + // graveyard_.dead == &graveyard_ + // when it is empty. + HashtablezInfo* sample = graveyard_.dead; + if (sample == &graveyard_) return nullptr; + + absl::MutexLock sample_lock(&sample->init_mu); + graveyard_.dead = sample->dead; + sample->PrepareForSampling(); + return sample; +} + +HashtablezInfo* HashtablezSampler::Register() { + int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); + if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) { + size_estimate_.fetch_sub(1, std::memory_order_relaxed); + dropped_samples_.fetch_add(1, std::memory_order_relaxed); + return nullptr; + } + + HashtablezInfo* sample = PopDead(); + if (sample == nullptr) { + // Resurrection failed. Hire a new warlock. + sample = new HashtablezInfo(); + PushNew(sample); + } + + return sample; +} + +void HashtablezSampler::Unregister(HashtablezInfo* sample) { + PushDead(sample); + size_estimate_.fetch_sub(1, std::memory_order_relaxed); +} + +int64_t HashtablezSampler::Iterate( + const std::function& f) { + HashtablezInfo* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + absl::MutexLock l(&s->init_mu); + if (s->dead == nullptr) { + f(*s); + } + s = s->next; + } + + return dropped_samples_.load(std::memory_order_relaxed); +} + +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(int64_t* next_sample) { + if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { + *next_sample = 1; + return HashtablezSampler::Global().Register(); + } + +#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + *next_sample = std::numeric_limits::max(); + return nullptr; +#else + bool first = *next_sample < 0; + *next_sample = g_exponential_biased_generator.GetStride( + g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); + // Small values of interval are equivalent to just sampling next time. + ABSL_ASSERT(*next_sample >= 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 > 0)) return nullptr; + return SampleSlow(next_sample); + } + + return HashtablezSampler::Global().Register(); +#endif +} + +void UnsampleSlow(HashtablezInfo* info) { + HashtablezSampler::Global().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; +#if ABSL_INTERNAL_RAW_HASH_SET_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 SetHashtablezEnabled(bool enabled) { + g_hashtablez_enabled.store(enabled, std::memory_order_release); +} + +void SetHashtablezSampleParameter(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) + } +} + +void SetHashtablezMaxSamples(int32_t max) { + if (max > 0) { + g_hashtablez_max_samples.store(max, std::memory_order_release); + } 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..85685f7 --- /dev/null +++ b/src/absl/container/internal/hashtablez_sampler.h @@ -0,0 +1,322 @@ +// 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/internal/per_thread_tls.h" +#include "absl/base/optimization.h" +#include "absl/container/internal/have_sse.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 { + // 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() 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; + + // `HashtablezSampler` maintains intrusive linked lists for all samples. See + // comments on `HashtablezSampler::all_` for details on these. `init_mu` + // guards the ability to restore the sample to a pristine state. This + // prevents races with sampling and resurrecting an object. + absl::Mutex init_mu; + HashtablezInfo* next; + HashtablezInfo* dead ABSL_GUARDED_BY(init_mu); + + // 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, who + // can only read them during `HashtablezSampler::Iterate` which will hold the + // lock. + static constexpr int kMaxStackDepth = 64; + absl::Time create_time; + int32_t depth; + void* stack[kMaxStackDepth]; +}; + +inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { +#if ABSL_INTERNAL_RAW_HASH_SET_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 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); +} + +HashtablezInfo* SampleSlow(int64_t* next_sample); +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 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 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 int64_t 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() { +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { + return HashtablezInfoHandle(nullptr); + } + return HashtablezInfoHandle(SampleSlow(&global_next_sample)); +#else + return HashtablezInfoHandle(nullptr); +#endif // !ABSL_PER_THREAD_TLS +} + +// Holds samples and their associated stack traces with a soft limit of +// `SetHashtablezMaxSamples()`. +// +// Thread safe. +class HashtablezSampler { + public: + // Returns a global Sampler. + static HashtablezSampler& Global(); + + HashtablezSampler(); + ~HashtablezSampler(); + + // Registers for sampling. Returns an opaque registration info. + HashtablezInfo* Register(); + + // Unregisters the sample. + void Unregister(HashtablezInfo* sample); + + // The dispose callback will be called on all samples the moment they are + // being unregistered. Only affects samples that are unregistered after the + // callback has been set. + // Returns the previous callback. + using DisposeCallback = void (*)(const HashtablezInfo&); + DisposeCallback SetDisposeCallback(DisposeCallback f); + + // Iterates over all the registered `StackInfo`s. Returning the number of + // samples that have been dropped. + int64_t Iterate(const std::function& f); + + private: + void PushNew(HashtablezInfo* sample); + void PushDead(HashtablezInfo* sample); + HashtablezInfo* PopDead(); + + std::atomic dropped_samples_; + std::atomic size_estimate_; + + // Intrusive lock free linked lists for tracking samples. + // + // `all_` records all samples (they are never removed from this list) and is + // terminated with a `nullptr`. + // + // `graveyard_.dead` is a circular linked list. When it is empty, + // `graveyard_.dead == &graveyard`. The list is circular so that + // every item on it (even the last) has a non-null dead pointer. This allows + // `Iterate` to determine if a given sample is live or dead using only + // information on the sample itself. + // + // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead + // looks like this (G is the Graveyard): + // + // +---+ +---+ +---+ +---+ +---+ + // all -->| A |--->| B |--->| C |--->| D |--->| E | + // | | | | | | | | | | + // +---+ | | +->| |-+ | | +->| |-+ | | + // | G | +---+ | +---+ | +---+ | +---+ | +---+ + // | | | | | | + // | | --------+ +--------+ | + // +---+ | + // ^ | + // +--------------------------------------+ + // + std::atomic all_; + HashtablezInfo graveyard_; + + std::atomic dispose_; +}; + +// Enables or disables sampling for Swiss tables. +void SetHashtablezEnabled(bool enabled); + +// Sets the rate at which Swiss tables will be sampled. +void SetHashtablezSampleParameter(int32_t rate); + +// Sets a soft max for the number of samples that will be kept. +void SetHashtablezMaxSamples(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/have_sse.h b/src/absl/container/internal/have_sse.h new file mode 100644 index 0000000..e75e1a1 --- /dev/null +++ b/src/absl/container/internal/have_sse.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. +// +// Shared config probing for SSE instructions used in Swiss tables. +#ifndef ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ +#define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ + +#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#if defined(__SSE2__) || \ + (defined(_MSC_VER) && \ + (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) +#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 1 +#else +#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 0 +#endif +#endif + +#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 +#ifdef __SSSE3__ +#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 1 +#else +#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 0 +#endif +#endif + +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 && \ + !ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#error "Bad configuration!" +#endif + +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#include +#endif + +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 +#include +#endif + +#endif // ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ diff --git a/src/absl/container/internal/inlined_vector.h b/src/absl/container/internal/inlined_vector.h new file mode 100644 index 0000000..b1b3649 --- /dev/null +++ b/src/absl/container/internal/inlined_vector.h @@ -0,0 +1,967 @@ +// 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 "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 "-Wmaybe-uninitialized" +// #endif + +template +using IsAtLeastForwardIterator = std::is_convertible< + typename std::iterator_traits::iterator_category, + std::forward_iterator_tag>; + +template ::value_type> +using IsMemcpyOk = + absl::conjunction>, + absl::is_trivially_copy_constructible, + absl::is_trivially_copy_assignable, + absl::is_trivially_destructible>; + +template +void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first, + SizeType destroy_size) { + using AllocatorTraits = absl::allocator_traits; + + if (destroy_first != nullptr) { + for (auto i = destroy_size; i != 0;) { + --i; + AllocatorTraits::destroy(*alloc_ptr, destroy_first + i); + } + +#if !defined(NDEBUG) + { + using ValueType = typename AllocatorTraits::value_type; + + // Overwrite unused memory with `0xab` so we can catch uninitialized + // usage. + // + // Cast to `void*` to tell the compiler that we don't care that we might + // be scribbling on a vtable pointer. + void* memory_ptr = destroy_first; + auto memory_size = destroy_size * sizeof(ValueType); + std::memset(memory_ptr, 0xab, memory_size); + } +#endif // !defined(NDEBUG) + } +} + +// If kUseMemcpy is true, memcpy(dst, src, n); else do nothing. +// Useful to avoid compiler warnings when memcpy() is used for T values +// that are not trivially copyable in non-reachable code. +template +inline void MemcpyIfAllowed(void* dst, const void* src, size_t n); + +// memcpy when allowed. +template <> +inline void MemcpyIfAllowed(void* dst, const void* src, size_t n) { + memcpy(dst, src, n); +} + +// Do nothing for types that are not memcpy-able. This function is only +// called from non-reachable branches. +template <> +inline void MemcpyIfAllowed(void*, const void*, size_t) {} + +template +void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first, + ValueAdapter* values_ptr, SizeType construct_size) { + for (SizeType i = 0; i < construct_size; ++i) { + ABSL_INTERNAL_TRY { + values_ptr->ConstructNext(alloc_ptr, construct_first + i); + } + ABSL_INTERNAL_CATCH_ANY { + inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i); + ABSL_INTERNAL_RETHROW; + } + } +} + +template +void AssignElements(Pointer assign_first, ValueAdapter* values_ptr, + SizeType assign_size) { + for (SizeType i = 0; i < assign_size; ++i) { + values_ptr->AssignNext(assign_first + i); + } +} + +template +struct StorageView { + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; + + Pointer data; + SizeType size; + SizeType capacity; +}; + +template +class IteratorValueAdapter { + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + + public: + explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} + + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); + ++it_; + } + + void AssignNext(Pointer assign_at) { + *assign_at = *it_; + ++it_; + } + + private: + Iterator it_; +}; + +template +class CopyValueAdapter { + using AllocatorTraits = absl::allocator_traits; + using ValueType = typename AllocatorTraits::value_type; + using Pointer = typename AllocatorTraits::pointer; + using ConstPointer = typename AllocatorTraits::const_pointer; + + public: + explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {} + + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); + } + + void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } + + private: + ConstPointer ptr_; +}; + +template +class DefaultValueAdapter { + using AllocatorTraits = absl::allocator_traits; + using ValueType = typename AllocatorTraits::value_type; + using Pointer = typename AllocatorTraits::pointer; + + public: + explicit DefaultValueAdapter() {} + + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at); + } + + void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } +}; + +template +class AllocationTransaction { + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; + + public: + explicit AllocationTransaction(AllocatorType* alloc_ptr) + : alloc_data_(*alloc_ptr, nullptr) {} + + ~AllocationTransaction() { + if (DidAllocate()) { + AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); + } + } + + AllocationTransaction(const AllocationTransaction&) = delete; + void operator=(const AllocationTransaction&) = delete; + + AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } + Pointer& GetData() { return alloc_data_.template get<1>(); } + SizeType& GetCapacity() { return capacity_; } + + bool DidAllocate() { return GetData() != nullptr; } + Pointer Allocate(SizeType capacity) { + GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); + GetCapacity() = capacity; + return GetData(); + } + + void Reset() { + GetData() = nullptr; + GetCapacity() = 0; + } + + private: + container_internal::CompressedTuple alloc_data_; + SizeType capacity_ = 0; +}; + +template +class ConstructionTransaction { + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; + + public: + explicit ConstructionTransaction(AllocatorType* alloc_ptr) + : alloc_data_(*alloc_ptr, nullptr) {} + + ~ConstructionTransaction() { + if (DidConstruct()) { + inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()), + GetData(), GetSize()); + } + } + + ConstructionTransaction(const ConstructionTransaction&) = delete; + void operator=(const ConstructionTransaction&) = delete; + + AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } + Pointer& GetData() { return alloc_data_.template get<1>(); } + SizeType& GetSize() { return size_; } + + bool DidConstruct() { return GetData() != nullptr; } + template + void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) { + inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), + data, values_ptr, size); + GetData() = data; + GetSize() = size; + } + void Commit() { + GetData() = nullptr; + GetSize() = 0; + } + + private: + container_internal::CompressedTuple alloc_data_; + SizeType size_ = 0; +}; + +template +class Storage { + public: + using AllocatorTraits = absl::allocator_traits; + 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 size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + + using reference = value_type&; + using const_reference = const value_type&; + using RValueReference = value_type&&; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using MoveIterator = std::move_iterator; + using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk; + + using StorageView = inlined_vector_internal::StorageView; + + template + using IteratorValueAdapter = + inlined_vector_internal::IteratorValueAdapter; + using CopyValueAdapter = + inlined_vector_internal::CopyValueAdapter; + using DefaultValueAdapter = + inlined_vector_internal::DefaultValueAdapter; + + using AllocationTransaction = + inlined_vector_internal::AllocationTransaction; + using ConstructionTransaction = + inlined_vector_internal::ConstructionTransaction; + + static size_type NextCapacity(size_type current_capacity) { + return current_capacity * 2; + } + + static size_type ComputeCapacity(size_type current_capacity, + size_type requested_capacity) { + return (std::max)(NextCapacity(current_capacity), requested_capacity); + } + + // --------------------------------------------------------------------------- + // Storage Constructors and Destructor + // --------------------------------------------------------------------------- + + Storage() : metadata_(allocator_type(), /* size and is_allocated */ 0) {} + + explicit Storage(const allocator_type& alloc) + : metadata_(alloc, /* size and is_allocated */ 0) {} + + ~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 + // --------------------------------------------------------------------------- + + size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } + + const size_type& GetSizeAndIsAllocated() const { + return metadata_.template get<1>(); + } + + size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; } + + bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } + + pointer GetAllocatedData() { return data_.allocated.allocated_data; } + + const_pointer GetAllocatedData() const { + return data_.allocated.allocated_data; + } + + pointer GetInlinedData() { + return reinterpret_cast( + std::addressof(data_.inlined.inlined_data[0])); + } + + const_pointer GetInlinedData() const { + return reinterpret_cast( + std::addressof(data_.inlined.inlined_data[0])); + } + + size_type GetAllocatedCapacity() const { + return data_.allocated.allocated_capacity; + } + + size_type GetInlinedCapacity() const { return static_cast(N); } + + StorageView MakeStorageView() { + return GetIsAllocated() + ? StorageView{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()} + : StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()}; + } + + allocator_type* GetAllocPtr() { + return std::addressof(metadata_.template get<0>()); + } + + const allocator_type* GetAllocPtr() const { + return std::addressof(metadata_.template get<0>()); + } + + // --------------------------------------------------------------------------- + // Storage Member Mutators + // --------------------------------------------------------------------------- + + ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other); + + template + void Initialize(ValueAdapter values, size_type new_size); + + template + void Assign(ValueAdapter values, size_type new_size); + + template + void Resize(ValueAdapter values, size_type new_size); + + template + iterator Insert(const_iterator pos, ValueAdapter values, + size_type insert_count); + + template + reference EmplaceBack(Args&&... args); + + iterator Erase(const_iterator from, const_iterator to); + + void Reserve(size_type 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(size_type size) { + GetSizeAndIsAllocated() = + (size << 1) | static_cast(GetIsAllocated()); + } + + void SetAllocatedSize(size_type size) { + GetSizeAndIsAllocated() = (size << 1) | static_cast(1); + } + + void SetInlinedSize(size_type size) { + GetSizeAndIsAllocated() = size << static_cast(1); + } + + void AddSize(size_type count) { + GetSizeAndIsAllocated() += count << static_cast(1); + } + + void SubtractSize(size_type count) { + assert(count <= GetSize()); + + GetSizeAndIsAllocated() -= count << static_cast(1); + } + + void SetAllocatedData(pointer data, size_type capacity) { + data_.allocated.allocated_data = data; + data_.allocated.allocated_capacity = capacity; + } + + void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) { + SetAllocatedData(allocation_tx_ptr->GetData(), + allocation_tx_ptr->GetCapacity()); + + allocation_tx_ptr->Reset(); + } + + void MemcpyFrom(const Storage& other_storage) { + assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); + + GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); + data_ = other_storage.data_; + } + + void DeallocateIfAllocated() { + if (GetIsAllocated()) { + AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(), + GetAllocatedCapacity()); + } + } + + private: + ABSL_ATTRIBUTE_NOINLINE void DestroyContents(); + + using Metadata = + container_internal::CompressedTuple; + + struct Allocated { + pointer allocated_data; + size_type allocated_capacity; + }; + + struct Inlined { + alignas(value_type) char inlined_data[sizeof(value_type[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(); + inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize()); + DeallocateIfAllocated(); +} + +template +void Storage::InitFrom(const Storage& other) { + const auto n = other.GetSize(); + assert(n > 0); // Empty sources handled handled in caller. + const_pointer 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()`. + size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), n); + dst = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); + SetAllocatedData(dst, new_capacity); + src = other.GetAllocatedData(); + } + if (IsMemcpyOk::value) { + MemcpyIfAllowed(dst, src, sizeof(dst[0]) * n); + } else { + auto values = IteratorValueAdapter(src); + inlined_vector_internal::ConstructElements(GetAllocPtr(), dst, &values, n); + } + GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated(); +} + +template +template +auto Storage::Initialize(ValueAdapter values, size_type new_size) + -> void { + // Only callable from constructors! + assert(!GetIsAllocated()); + 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()`. + size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); + construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); + SetAllocatedData(construct_data, new_capacity); + SetIsAllocated(); + } else { + construct_data = GetInlinedData(); + } + + inlined_vector_internal::ConstructElements(GetAllocPtr(), 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, size_type new_size) -> void { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + + absl::Span assign_loop; + absl::Span construct_loop; + absl::Span destroy_loop; + + if (new_size > storage_view.capacity) { + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + construct_loop = {allocation_tx.Allocate(new_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}; + } + + inlined_vector_internal::AssignElements(assign_loop.data(), &values, + assign_loop.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), construct_loop.data(), &values, construct_loop.size()); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), + destroy_loop.size()); + + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template +template +auto Storage::Resize(ValueAdapter values, size_type new_size) -> void { + StorageView storage_view = MakeStorageView(); + auto* const base = storage_view.data; + const size_type size = storage_view.size; + auto* alloc = GetAllocPtr(); + if (new_size <= size) { + // Destroy extra old elements. + inlined_vector_internal::DestroyElements(alloc, base + new_size, + size - new_size); + } else if (new_size <= storage_view.capacity) { + // Construct new elements in place. + inlined_vector_internal::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 now. + // 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); + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + + ConstructionTransaction construction_tx(alloc); + construction_tx.Construct(new_data + size, &values, new_size - size); + + IteratorValueAdapter move_values((MoveIterator(base))); + inlined_vector_internal::ConstructElements(alloc, new_data, &move_values, + size); + + inlined_vector_internal::DestroyElements(alloc, base, size); + construction_tx.Commit(); + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + SetSize(new_size); +} + +template +template +auto Storage::Insert(const_iterator pos, ValueAdapter values, + size_type insert_count) -> iterator { + StorageView storage_view = MakeStorageView(); + + size_type insert_index = + std::distance(const_iterator(storage_view.data), pos); + size_type insert_end_index = insert_index + insert_count; + size_type new_size = storage_view.size + insert_count; + + if (new_size > storage_view.capacity) { + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + ConstructionTransaction move_construciton_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + + construction_tx.Construct(new_data + insert_index, &values, insert_count); + + move_construciton_tx.Construct(new_data, &move_values, insert_index); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), new_data + insert_end_index, &move_values, + storage_view.size - insert_index); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + construction_tx.Commit(); + move_construciton_tx.Commit(); + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + + SetAllocatedSize(new_size); + return iterator(new_data + insert_index); + } else { + size_type move_construction_destination_index = + (std::max)(insert_end_index, storage_view.size); + + ConstructionTransaction move_construction_tx(GetAllocPtr()); + + 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); + } + + inlined_vector_internal::AssignElements(insert_assignment.data(), &values, + insert_assignment.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), insert_construction.data(), &values, + insert_construction.size()); + + 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 auto 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(*GetAllocPtr(), 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(GetAllocPtr()); + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + size_type new_capacity = NextCapacity(storage_view.capacity); + pointer construct_data = allocation_tx.Allocate(new_capacity); + pointer last_ptr = construct_data + storage_view.size; + + // Construct new element. + AllocatorTraits::construct(*GetAllocPtr(), last_ptr, + std::forward(args)...); + // Move elements from old backing store to new backing store. + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements( + GetAllocPtr(), allocation_tx.GetData(), &move_values, + storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits::destroy(*GetAllocPtr(), last_ptr); + ABSL_INTERNAL_RETHROW; + } + // Destroy elements in old backing store. + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + AddSize(1); + return *last_ptr; +} + +template +auto Storage::Erase(const_iterator from, const_iterator to) + -> iterator { + StorageView storage_view = MakeStorageView(); + + size_type erase_size = std::distance(from, to); + size_type erase_index = + std::distance(const_iterator(storage_view.data), from); + size_type erase_end_index = erase_index + erase_size; + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data + erase_end_index)); + + inlined_vector_internal::AssignElements(storage_view.data + erase_index, + &move_values, + storage_view.size - erase_end_index); + + inlined_vector_internal::DestroyElements( + GetAllocPtr(), storage_view.data + (storage_view.size - erase_size), + erase_size); + + SubtractSize(erase_size); + return iterator(storage_view.data + erase_index); +} + +template +auto Storage::Reserve(size_type requested_capacity) -> void { + StorageView storage_view = MakeStorageView(); + + if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + size_type new_capacity = + ComputeCapacity(storage_view.capacity, requested_capacity); + pointer new_data = allocation_tx.Allocate(new_capacity); + + inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data, + &move_values, storage_view.size); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); +} + +template +auto Storage::ShrinkToFit() -> void { + // May only be called on allocated instances! + assert(GetIsAllocated()); + + StorageView storage_view{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()}; + + if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + pointer construct_data; + if (storage_view.size > GetInlinedCapacity()) { + size_type new_capacity = storage_view.size; + construct_data = allocation_tx.Allocate(new_capacity); + } else { + construct_data = GetInlinedData(); + } + + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, + &move_values, storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + SetAllocatedData(storage_view.data, storage_view.capacity); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data, + storage_view.capacity); + + if (allocation_tx.DidAllocate()) { + AcquireAllocatedData(&allocation_tx); + } else { + UnsetIsAllocated(); + } +} + +template +auto Storage::Swap(Storage* other_storage_ptr) -> void { + using std::swap; + 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 (size_type 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())); + + inlined_vector_internal::ConstructElements( + large_ptr->GetAllocPtr(), + small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values, + large_ptr->GetSize() - small_ptr->GetSize()); + + inlined_vector_internal::DestroyElements( + large_ptr->GetAllocPtr(), + 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 { + inlined_vector_internal::ConstructElements( + inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(), + &move_values, inlined_ptr->GetSize()); + } + ABSL_INTERNAL_CATCH_ANY { + allocated_ptr->SetAllocatedData(allocated_storage_view.data, + allocated_storage_view.capacity); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(), + inlined_ptr->GetInlinedData(), + inlined_ptr->GetSize()); + + inlined_ptr->SetAllocatedData(allocated_storage_view.data, + allocated_storage_view.capacity); + } + + swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); + swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); +} + +// End ignore "maybe-uninitialized" +// #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_hash_policy.h b/src/absl/container/internal/node_hash_policy.h new file mode 100644 index 0000000..4617162 --- /dev/null +++ b/src/absl/container/internal/node_hash_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_HASH_POLICY_H_ +#define ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct node_hash_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_HASH_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..0a02757 --- /dev/null +++ b/src/absl/container/internal/raw_hash_map.h @@ -0,0 +1,197 @@ +// 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(alkis): remove this assertion and verify that reference mapped_type 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..bfef071 --- /dev/null +++ b/src/absl/container/internal/raw_hash_set.cc @@ -0,0 +1,61 @@ +// 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 { + +constexpr size_t Group::kWidth; + +// 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, 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] == kSentinel); + assert(IsValidCapacity(capacity)); + for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) { + Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); + } + // Copy the cloned ctrl bytes. + std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth); + ctrl[capacity] = kSentinel; +} + + +} // 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..8615de8 --- /dev/null +++ b/src/absl/container/internal/raw_hash_set.h @@ -0,0 +1,1903 @@ +// 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 +// +// The table stores elements inline in a slot array. In addition to the slot +// array the table maintains some control state per slot. The extra state is one +// byte per slot and stores empty or deleted marks, or alternatively 7 bits from +// the hash of an occupied slot. The table is split into logical groups of +// slots, like so: +// +// Group 1 Group 2 Group 3 +// +---------------+---------------+---------------+ +// | | | | | | | | | | | | | | | | | | | | | | | | | +// +---------------+---------------+---------------+ +// +// On lookup the hash is split into two parts: +// - H2: 7 bits (those stored in the control bytes) +// - H1: the rest of the bits +// The groups are probed using H1. For each group the slots are matched to H2 in +// parallel. Because H2 is 7 bits (128 states) and the number of slots per group +// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit. +// +// On insert, once the right group is found (as in lookup), its slots are +// filled in order. +// +// On erase a slot is cleared. In case the group did not have any empty slots +// before the erase, the erased slot is marked as deleted. +// +// Groups without empty slots (but maybe with deleted slots) extend the probe +// sequence. The probing algorithm is quadratic. Given N the number of groups, +// the probing function for the i'th probe is: +// +// P(0) = H1 % N +// +// P(i) = (P(i - 1) + i) % N +// +// This probing function guarantees that after N probes, all the groups of the +// table will be probed exactly once. + +#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/internal/endian.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/container/internal/have_sse.h" +#include "absl/container/internal/layout.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" +#include "absl/utility/utility.h" + +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 */) {} + +template +class probe_seq { + public: + probe_seq(size_t hash, size_t mask) { + assert(((mask + 1) & mask) == 0 && "not a mask"); + mask_ = mask; + offset_ = hash & mask_; + } + size_t offset() const { return offset_; } + size_t offset(size_t i) const { return (offset_ + i) & mask_; } + + void next() { + index_ += Width; + offset_ += index_; + offset_ &= mask_; + } + // 0-based probe index. The i-th probe in the probe sequence. + size_t index() const { return index_; } + + private: + size_t mask_; + size_t offset_; + size_t index_ = 0; +}; + +template +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_INTERNAL_ASSUME(x != 0); + return countr_zero(x); +} + +// An abstraction over a bitmask. It provides an easy way to iterate through the +// indexes of the set bits of a bitmask. When Shift=0 (platforms with SSE), +// this is a true bitmask. On non-SSE, platforms the arithematic used to +// emulate the SSE behavior works in bytes (Shift=3) and leaves each bytes as +// either 0x00 or 0x80. +// +// For example: +// for (int i : BitMask(0x5)) -> yields 0, 2 +// for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 +template +class BitMask { + static_assert(std::is_unsigned::value, ""); + static_assert(Shift == 0 || Shift == 3, ""); + + public: + // These are useful for unit tests (gunit). + using value_type = int; + using iterator = BitMask; + using const_iterator = BitMask; + + explicit BitMask(T mask) : mask_(mask) {} + BitMask& operator++() { + mask_ &= (mask_ - 1); + return *this; + } + explicit operator bool() const { return mask_ != 0; } + int operator*() const { return LowestBitSet(); } + uint32_t LowestBitSet() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + uint32_t HighestBitSet() const { + return static_cast((bit_width(mask_) - 1) >> Shift); + } + + BitMask begin() const { return *this; } + BitMask end() const { return BitMask(0); } + + uint32_t TrailingZeros() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + + uint32_t LeadingZeros() const { + constexpr int total_significant_bits = SignificantBits << Shift; + constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; + return countl_zero(mask_ << extra_bits) >> Shift; + } + + private: + friend bool operator==(const BitMask& a, const BitMask& b) { + return a.mask_ == b.mask_; + } + friend bool operator!=(const BitMask& a, const BitMask& b) { + return a.mask_ != b.mask_; + } + + T mask_; +}; + +using ctrl_t = signed char; +using h2_t = uint8_t; + +// The values here are selected for maximum performance. See the static asserts +// below for details. +enum Ctrl : ctrl_t { + kEmpty = -128, // 0b10000000 + kDeleted = -2, // 0b11111110 + kSentinel = -1, // 0b11111111 +}; +static_assert( + kEmpty & kDeleted & kSentinel & 0x80, + "Special markers need to have the MSB to make checking for them efficient"); +static_assert(kEmpty < kSentinel && kDeleted < kSentinel, + "kEmpty and kDeleted must be smaller than kSentinel to make the " + "SIMD test of IsEmptyOrDeleted() efficient"); +static_assert(kSentinel == -1, + "kSentinel must be -1 to elide loading it from memory into SIMD " + "registers (pcmpeqd xmm, xmm)"); +static_assert(kEmpty == -128, + "kEmpty must be -128 to make the SIMD check for its " + "existence efficient (psignb xmm, xmm)"); +static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F, + "kEmpty and kDeleted must share an unset bit that is not shared " + "by kSentinel to make the scalar test for MatchEmptyOrDeleted() " + "efficient"); +static_assert(kDeleted == -2, + "kDeleted must be -2 to make the implementation of " + "ConvertSpecialToEmptyAndFullToDeleted efficient"); + +// A single block of empty control bytes for tables without any slots allocated. +// This enables removing a branch in the hot path of find(). +inline ctrl_t* EmptyGroup() { + alignas(16) static constexpr ctrl_t empty_group[] = { + kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, + kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty}; + return const_cast(empty_group); +} + +// Mixes a randomly generated per-process seed with `hash` and `ctrl` to +// randomize insertion order within groups. +bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl); + +// Returns a hash seed. +// +// The seed consists of the ctrl_ pointer, which adds enough entropy to ensure +// non-determinism of iteration order in most cases. +inline size_t HashSeed(const ctrl_t* ctrl) { + // The low bits of the pointer have little or no entropy because of + // alignment. We shift the pointer to try to use higher entropy bits. A + // good number seems to be 12 bits, because that aligns with page size. + return reinterpret_cast(ctrl) >> 12; +} + +inline size_t H1(size_t hash, const ctrl_t* ctrl) { + return (hash >> 7) ^ HashSeed(ctrl); +} +inline ctrl_t H2(size_t hash) { return hash & 0x7F; } + +inline bool IsEmpty(ctrl_t c) { return c == kEmpty; } +inline bool IsFull(ctrl_t c) { return c >= 0; } +inline bool IsDeleted(ctrl_t c) { return c == kDeleted; } +inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; } + +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 + +// https://github.com/abseil/abseil-cpp/issues/209 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853 +// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char +// Work around this by using the portable implementation of Group +// when using -funsigned-char under GCC. +inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) { +#if defined(__GNUC__) && !defined(__clang__) + if (std::is_unsigned::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( + _mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))); + } + + // Returns a bitmask representing the positions of empty slots. + BitMask MatchEmpty() const { +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 + // This only works because kEmpty is -128. + return BitMask( + _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); +#else + return Match(static_cast(kEmpty)); +#endif + } + + // Returns a bitmask representing the positions of empty or deleted slots. + BitMask MatchEmptyOrDeleted() const { + auto special = _mm_set1_epi8(kSentinel); + return BitMask( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))); + } + + // Returns the number of trailing empty or deleted elements in the group. + uint32_t CountLeadingEmptyOrDeleted() const { + auto special = _mm_set1_epi8(kSentinel); + return TrailingZeros(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); +#if ABSL_INTERNAL_RAW_HASH_SET_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 + +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 kEmpty, kDeleted, kSentinel + // - they will be handled gracefully by subsequent checks in code + // + // Example: + // v = 0x1716151413121110 + // hash = 0x12 + // retval = (v - lsbs) & ~v & msbs = 0x0000000080800000 + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl ^ (lsbs * hash); + return BitMask((x - lsbs) & ~x & msbs); + } + + BitMask MatchEmpty() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask((ctrl & (~ctrl << 6)) & msbs); + } + + BitMask MatchEmptyOrDeleted() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask((ctrl & (~ctrl << 7)) & msbs); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; + return (TrailingZeros(((~ctrl & (ctrl >> 7)) | gaps) + 1) + 7) >> 3; + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl & msbs; + auto res = (~x + (x >> 7)) & ~lsbs; + little_endian::Store64(dst, res); + } + + uint64_t ctrl; +}; + +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +using Group = GroupSse2Impl; +#else +using Group = GroupPortableImpl; +#endif + +template +class raw_hash_set; + +inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } + +// PRECONDITION: +// IsValidCapacity(capacity) +// ctrl[capacity] == kSentinel +// ctrl[i] != kSentinel for all i < capacity +// Applies mapping for every byte in ctrl: +// DELETED -> EMPTY +// EMPTY -> EMPTY +// FULL -> DELETED +void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity); + +// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1. +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` of the table, returns the size (i.e. number of full slots) +// at which we should grow the capacity. +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; +} +// From desired "growth" to a lowerbound of the necessary capacity. +// Might not be a valid one and requires NormalizeCapacity(). +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); +} + +inline void AssertIsFull(ctrl_t* ctrl) { + ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, or the table might have rehashed."); +} + +inline void AssertIsValid(ctrl_t* ctrl) { + ABSL_HARDENING_ASSERT((ctrl == nullptr || IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, or the table might have rehashed."); +} + +struct FindInfo { + size_t offset; + size_t probe_length; +}; + +// The representation of the object has two modes: +// - small: For capacities < kWidth-1 +// - large: For the rest. +// +// Differences: +// - 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 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; } + +inline probe_seq probe(ctrl_t* ctrl, size_t hash, + size_t capacity) { + return probe_seq(H1(hash, ctrl), capacity); +} + +// Probes the raw_hash_set with the probe sequence for hash and returns the +// pointer to the first empty or deleted slot. +// NOTE: this function must work with tables having both kEmpty and kDelete +// in one group. Such tables appears during drop_deletes_without_resize. +// +// This function is very useful when insertions happen and: +// - the input is already a set +// - there are enough slots +// - the element with the hash is not in the table +inline FindInfo find_first_non_full(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.MatchEmptyOrDeleted(); + 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!"); + } +} + +// 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 Layout = absl::container_internal::Layout; + + static Layout MakeLayout(size_t capacity) { + assert(IsValidCapacity(capacity)); + return Layout(capacity + Group::kWidth + 1, capacity); + } + + 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 { + AssertIsFull(ctrl_); + return PolicyTraits::element(slot_); + } + + // PRECONDITION: not an end() iterator. + pointer operator->() const { return &operator*(); } + + // PRECONDITION: not an end() iterator. + iterator& operator++() { + AssertIsFull(ctrl_); + ++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_INTERNAL_ASSUME(ctrl != nullptr); + } + + void skip_empty_or_deleted() { + while (IsEmptyOrDeleted(*ctrl_)) { + uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); + ctrl_ += shift; + slot_ += shift; + } + if (ABSL_PREDICT_FALSE(*ctrl_ == 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(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_); + set_ctrl(target.offset, H2(hash)); + 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(); + } else if (capacity_) { + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + size_ = 0; + reset_ctrl(); + 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) { + AssertIsFull(it.ctrl_); + 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) { + AssertIsFull(position.inner_.ctrl_); + 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); + 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); + } + } + + void reserve(size_t n) { + size_t m = GrowthToLowerboundCapacity(n); + if (m > capacity_) { + resize(NormalizeCapacity(m)); + } + } + + // 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; +#if defined(__GNUC__) + auto seq = probe(ctrl_, hash_ref()(key), capacity_); + __builtin_prefetch(static_cast(ctrl_ + seq.offset())); + __builtin_prefetch(static_cast(slots_ + seq.offset())); +#endif // __GNUC__ + } + + // The API of find() has two extensions. + // + // 1. The hash can be passed by the user. It must be equal to the hash of the + // key. + // + // 2. The type of the key argument doesn't have to be key_type. This is so + // called heterogeneous key support. + template + iterator find(const key_arg& key, size_t hash) { + auto seq = probe(ctrl_, hash, capacity_); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int 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.MatchEmpty())) return end(); + seq.next(); + assert(seq.index() < capacity_ && "full table!"); + } + } + template + iterator find(const key_arg& key) { + 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 { + 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); + } + + 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" the object from the container, except that it doesn't actually + // destroy the object. It only updates all the metadata of the class. + // This can be used in conjunction with Policy::transfer to move the object to + // another place. + void erase_meta_only(const_iterator it) { + assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator"); + --size_; + const size_t index = it.inner_.ctrl_ - ctrl_; + const size_t index_before = (index - Group::kWidth) & capacity_; + const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty(); + const auto empty_before = Group(ctrl_ + index_before).MatchEmpty(); + + // We count how many consecutive non empties we have to the right and to the + // left of `it`. If the sum is >= kWidth then there is at least one probe + // window that might have seen a full group. + bool was_never_full = + empty_before && empty_after && + static_cast(empty_after.TrailingZeros() + + empty_before.LeadingZeros()) < Group::kWidth; + + set_ctrl(index, was_never_full ? kEmpty : kDeleted); + growth_left() += was_never_full; + infoz().RecordErase(); + } + + 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(); + } + + auto layout = MakeLayout(capacity_); + char* mem = static_cast( + Allocate(&alloc_ref(), layout.AllocSize())); + ctrl_ = reinterpret_cast(layout.template Pointer<0>(mem)); + slots_ = layout.template Pointer<1>(mem); + reset_ctrl(); + reset_growth_left(); + infoz().RecordStorageChanged(size_, capacity_); + } + + void destroy_slots() { + if (!capacity_) return; + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + auto layout = MakeLayout(capacity_); + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + Deallocate(&alloc_ref(), ctrl_, layout.AllocSize()); + ctrl_ = EmptyGroup(); + slots_ = nullptr; + size_ = 0; + capacity_ = 0; + growth_left() = 0; + } + + void resize(size_t new_capacity) { + assert(IsValidCapacity(new_capacity)); + auto* old_ctrl = ctrl_; + auto* old_slots = slots_; + const size_t old_capacity = capacity_; + capacity_ = new_capacity; + initialize_slots(); + + 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; + set_ctrl(new_i, H2(hash)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); + } + } + if (old_capacity) { + SanitizerUnpoisonMemoryRegion(old_slots, + sizeof(slot_type) * old_capacity); + auto layout = MakeLayout(old_capacity); + Deallocate(&alloc_ref(), old_ctrl, + layout.AllocSize()); + } + infoz().RecordRehash(total_probe_length); + } + + 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; + size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, + PolicyTraits::element(slots_ + i)); + auto target = find_first_non_full(ctrl_, hash, capacity_); + 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 auto probe_index = [&](size_t pos) { + return ((pos - probe(ctrl_, hash, capacity_).offset()) & capacity_) / + Group::kWidth; + }; + + // Element doesn't move. + if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { + set_ctrl(i, H2(hash)); + continue; + } + if (IsEmpty(ctrl_[new_i])) { + // Transfer element to the empty spot. + // set_ctrl poisons/unpoisons the slots so we have to call it at the + // right time. + set_ctrl(new_i, H2(hash)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); + set_ctrl(i, kEmpty); + } else { + assert(IsDeleted(ctrl_[new_i])); + set_ctrl(new_i, H2(hash)); + // Until we are done rehashing, DELETED marks previously FULL slots. + // Swap i and new_i elements. + PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i); + PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot); + --i; // repeat + } + } + reset_growth_left(); + infoz().RecordRehash(total_probe_length); + } + + void rehash_and_grow_if_necessary() { + if (capacity_ == 0) { + resize(1); + } else if (size() <= CapacityToGrowth(capacity()) / 2) { + // Squash DELETED without growing if there is enough capacity. + drop_deletes_without_resize(); + } else { + // Otherwise grow the container. + resize(capacity_ * 2 + 1); + } + } + + bool has_element(const value_type& elem) const { + size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem); + auto seq = probe(ctrl_, hash, capacity_); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) == + elem)) + return true; + } + if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false; + seq.next(); + assert(seq.index() < capacity_ && "full table!"); + } + return false; + } + + // TODO(alkis): Optimize this assuming *this and that don't overlap. + raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) { + raw_hash_set tmp(std::move(that)); + swap(tmp); + return *this; + } + raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) { + raw_hash_set tmp(std::move(that), alloc_ref()); + swap(tmp); + return *this; + } + + protected: + template + std::pair find_or_prepare_insert(const K& key) { + auto hash = hash_ref()(key); + auto seq = probe(ctrl_, hash, capacity_); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int 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.MatchEmpty())) break; + seq.next(); + assert(seq.index() < capacity_ && "full table!"); + } + return {prepare_insert(hash), true}; + } + + 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]); + set_ctrl(target.offset, H2(hash)); + 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; + + // Reset all ctrl bytes back to kEmpty, except the sentinel. + void reset_ctrl() { + std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth); + ctrl_[capacity_] = kSentinel; + SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + } + + void reset_growth_left() { + growth_left() = CapacityToGrowth(capacity()) - size_; + } + + // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at + // the end too. + void set_ctrl(size_t i, ctrl_t h) { + assert(i < capacity_); + + if (IsFull(h)) { + SanitizerUnpoisonObject(slots_ + i); + } else { + SanitizerPoisonObject(slots_ + i); + } + + ctrl_[i] = h; + ctrl_[((i - Group::kWidth) & capacity_) + 1 + + ((Group::kWidth - 1) & capacity_)] = h; + } + + size_t& growth_left() { return settings_.template get<0>(); } + + 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 + ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t] + slot_type* slots_ = nullptr; // [capacity * slot_type] + size_t size_ = 0; // number of full slots + size_t capacity_ = 0; // total number of slots + absl::container_internal::CompressedTuple + settings_{0, HashtablezInfoHandle{}, hasher{}, key_equal{}, + allocator_type{}}; +}; + +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +void EraseIf(Predicate pred, raw_hash_set* c) { + for (auto it = c->begin(), last = c->end(); it != last;) { + auto copy_it = it++; + if (pred(*copy_it)) { + c->erase(copy_it); + } + } +} + +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 (int 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.MatchEmpty()) return num_probes; + seq.next(); + ++num_probes; + } + } + + static size_t AllocatedByteSize(const Set& c) { + size_t capacity = c.capacity_; + if (capacity == 0) return 0; + auto layout = Set::MakeLayout(capacity); + size_t m = layout.AllocSize(); + + size_t per_slot = Traits::space_used(static_cast(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; + auto layout = Set::MakeLayout(NormalizeCapacity(capacity)); + size_t m = layout.AllocSize(); + 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 + +#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..7a39f62 --- /dev/null +++ b/src/absl/container/node_hash_map.h @@ -0,0 +1,597 @@ +// 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/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/node_hash_policy.h" +#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +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. +// +// 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 init_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `node_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + // + // 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`. +template +void erase_if(node_hash_map& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +class NodeHashMapPolicy + : public absl::container_internal::node_hash_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..93b15f4 --- /dev/null +++ b/src/absl/container/node_hash_set.h @@ -0,0 +1,493 @@ +// 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/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/node_hash_policy.h" +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +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. +// +// 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`. +template +void erase_if(node_hash_set& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +struct NodeHashSetPolicy + : absl::container_internal::node_hash_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..3ddebd7 --- /dev/null +++ b/src/absl/debugging/failure_signal_handler.cc @@ -0,0 +1,387 @@ +// +// 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/errno_saver.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) +#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::base_internal::ErrnoSaver errno_saver; + absl::raw_logging_internal::SafeWriteToStderr(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); + } + + 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..0c0f585 --- /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 may 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..329c285 --- /dev/null +++ b/src/absl/debugging/internal/address_is_readable.cc @@ -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. + +// 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 + +#include +#include +#include + +#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 { + +// Pack a pid and two file descriptors into a 64-bit word, +// using 16, 24, and 24 bits for each respectively. +static uint64_t Pack(uint64_t pid, uint64_t read_fd, uint64_t write_fd) { + ABSL_RAW_CHECK((read_fd >> 24) == 0 && (write_fd >> 24) == 0, + "fd out of range"); + return (pid << 48) | ((read_fd & 0xffffff) << 24) | (write_fd & 0xffffff); +} + +// Unpack x into a pid and two file descriptors, where x was created with +// Pack(). +static void Unpack(uint64_t x, int *pid, int *read_fd, int *write_fd) { + *pid = x >> 48; + *read_fd = (x >> 24) & 0xffffff; + *write_fd = x & 0xffffff; +} + +// Return whether the byte at *addr is readable, without faulting. +// Save and restores errno. Returns true on systems where +// unimplemented. +// This is a namespace-scoped variable for correct zero-initialization. +static std::atomic pid_and_fds; // initially 0, an invalid pid. + +bool AddressIsReadable(const void *addr) { + absl::base_internal::ErrnoSaver errno_saver; + // We test whether a byte is readable by using write(). Normally, this would + // be done via a cached file descriptor to /dev/null, but linux fails to + // check whether the byte is readable when the destination is /dev/null, so + // we use a cached pipe. We store the pid of the process that created the + // pipe to handle the case where a process forks, and the child closes all + // the file descriptors and then calls this routine. This is not perfect: + // the child could use the routine, then close all file descriptors and then + // use this routine again. But the likely use of this routine is when + // crashing, to test the validity of pages when dumping the stack. Beware + // that we may leak file descriptors, but we're unlikely to leak many. + int bytes_written; + int current_pid = getpid() & 0xffff; // we use only the low order 16 bits + do { // until we do not get EBADF trying to use file descriptors + int pid; + int read_fd; + int write_fd; + uint64_t local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire); + Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd); + while (current_pid != pid) { + int p[2]; + // new pipe + if (pipe(p) != 0) { + ABSL_RAW_LOG(FATAL, "Failed to create pipe, errno=%d", errno); + } + fcntl(p[0], F_SETFD, FD_CLOEXEC); + fcntl(p[1], F_SETFD, FD_CLOEXEC); + uint64_t new_pid_and_fds = Pack(current_pid, p[0], p[1]); + if (pid_and_fds.compare_exchange_strong( + local_pid_and_fds, new_pid_and_fds, std::memory_order_release, + std::memory_order_relaxed)) { + local_pid_and_fds = new_pid_and_fds; // fds exposed to other threads + } else { // fds not exposed to other threads; we can close them. + close(p[0]); + close(p[1]); + local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire); + } + Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd); + } + errno = 0; + // Use syscall(SYS_write, ...) instead of write() to prevent ASAN + // and other checkers from complaining about accesses to arbitrary + // memory. + do { + bytes_written = syscall(SYS_write, write_fd, addr, 1); + } while (bytes_written == -1 && errno == EINTR); + if (bytes_written == 1) { // remove the byte from the pipe + char c; + while (read(read_fd, &c, 1) == -1 && errno == EINTR) { + } + } + if (errno == EBADF) { // Descriptors invalid. + // If pid_and_fds contains the problematic file descriptors we just used, + // this call will forget them, and the loop will try again. + pid_and_fds.compare_exchange_strong(local_pid_and_fds, 0, + std::memory_order_release, + std::memory_order_relaxed); + } + } while (errno == EBADF); + return bytes_written == 1; +} + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif 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..5cd5632 --- /dev/null +++ b/src/absl/debugging/internal/demangle.cc @@ -0,0 +1,1949 @@ +// 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 +// ::= 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; + } + + // Object/function call expression. + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "cl") && OneOrMore(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..24cc013 --- /dev/null +++ b/src/absl/debugging/internal/elf_mem_image.cc @@ -0,0 +1,382 @@ +// 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/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 __WORDSIZE == 32 +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 __WORDSIZE == 64 +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: { + if (__LITTLE_ENDIAN != __BYTE_ORDER) { + assert(false); + return; + } + break; + } + case ELFDATA2MSB: { + if (__BIG_ENDIAN != __BYTE_ORDER) { + assert(false); + return; + } + 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 ElfW(Xword) 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); + const ElfW(Versym) version_index = version_symbol[0] & VERSYM_VERSION; + 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..46bfade --- /dev/null +++ b/src/absl/debugging/internal/elf_mem_image.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. + */ + +// 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(__GLIBC__) && !defined(__native_client__) && \ + !defined(__asmjs__) && !defined(__wasm__) +#define ABSL_HAVE_ELF_MEM_IMAGE 1 +#endif + +#ifdef ABSL_HAVE_ELF_MEM_IMAGE + +#include // for ElfW + +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..589a3ef --- /dev/null +++ b/src/absl/debugging/internal/examine_stack.cc @@ -0,0 +1,203 @@ +// +// 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 + +#ifdef __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 { + +// 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* 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); +#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; +} + +// The %p field width for printf() functions is two characters per byte, +// and two extra for the leading "0x". +static constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*); + +// 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. +static void DumpPCAndFrameSizeAndSymbol(void (*writerfn)(const char*, void*), + void* writerfn_arg, void* pc, + void* 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); + } + writerfn(buf, writerfn_arg); +} + +// Print a program counter and the corresponding stack frame size. +static void DumpPCAndFrameSize(void (*writerfn)(const char*, void*), + void* writerfn_arg, void* 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); + } + writerfn(buf, writerfn_arg); +} + +void DumpPCAndFrameSizesAndStackTrace( + void* pc, void* const stack[], int frame_sizes[], int depth, + int min_dropped_frames, bool symbolize_stacktrace, + void (*writerfn)(const char*, void*), void* writerfn_arg) { + if (pc != nullptr) { + // We don't know the stack frame size for PC, use 0. + if (symbolize_stacktrace) { + DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, pc, pc, 0, "PC: "); + } else { + DumpPCAndFrameSize(writerfn, writerfn_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(writerfn, writerfn_arg, stack[i], + reinterpret_cast(stack[i]) - 1, + frame_sizes[i], " "); + } else { + DumpPCAndFrameSize(writerfn, writerfn_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); + writerfn(buf, writerfn_arg); + } +} + +} // 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..3933691 --- /dev/null +++ b/src/absl/debugging/internal/examine_stack.h @@ -0,0 +1,42 @@ +// +// 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 { + +// 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* vuc); + +// Uses `writerfn` to dump the program counter, stack trace, and stack +// frame sizes. +void DumpPCAndFrameSizesAndStackTrace( + void* pc, void* const stack[], int frame_sizes[], int depth, + int min_dropped_frames, bool symbolize_stacktrace, + void (*writerfn)(const char*, void*), void* writerfn_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..e3dd51c --- /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__) +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..2b5e715 --- /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__)) +#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..f4859d7 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -0,0 +1,199 @@ +#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 j = 0; + for (; frame_pointer != nullptr && j < kMaxUnwind; j++) { + frame_pointer = + NextStackFrame(frame_pointer, ucp); + } + *min_dropped_frames = j; + } + 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..2a1bf2e --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_arm-inl.inc @@ -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. +// +// 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 j = 0; + for (; sp != nullptr && j < kMaxUnwind; j++) { + sp = NextStackFrame(sp); + } + *min_dropped_frames = j; + } + 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..cca410d --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_config.h @@ -0,0 +1,80 @@ +/* + * Copyright 2017 The Abseil Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#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 +#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(__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 +#endif + +#endif + +// 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_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..cf8c051 --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -0,0 +1,253 @@ +// 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 j = 0; + for (; next_sp != nullptr && j < kMaxUnwind; j++) { + next_sp = NextStackFrame(next_sp, ucp); + } + *min_dropped_frames = j; + } + 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_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..bc320ff --- /dev/null +++ b/src/absl/debugging/internal/stacktrace_x86-inl.inc @@ -0,0 +1,346 @@ +// 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 "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 should be as long as + // SHRINK_WRAP_FRAME_POINTER is not set, but 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) { + 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 + // SHRINK_WRAP_FRAME_POINTER 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; + } 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)); + + 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); + 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 j = 0; + for (; fp != nullptr && j < kMaxUnwind; j++) { + fp = NextStackFrame(fp, ucp); + } + *min_dropped_frames = j; + } + 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..4f26130 --- /dev/null +++ b/src/absl/debugging/internal/symbolize.h @@ -0,0 +1,147 @@ +// 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 + +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..6be16d9 --- /dev/null +++ b/src/absl/debugging/internal/vdso_support.cc @@ -0,0 +1,173 @@ +// 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 + +#include +#include +#include +#include + +#if __GLIBC_PREREQ(2, 16) // GLIBC-2.16 implements 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 + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +ABSL_CONST_INIT +std::atomic VDSOSupport::vdso_base_( + debugging_internal::ElfMemImage::kInvalidBase); + +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; +#if __GLIBC_PREREQ(2, 16) + 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 // __GLIBC_PREREQ(2, 16) + 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) { + vdso_base_.store(reinterpret_cast(aux.a_un.a_val), + std::memory_order_relaxed); + 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..764ca0a --- /dev/null +++ b/src/absl/debugging/leak_check.cc @@ -0,0 +1,69 @@ +// 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. +// When lsan is not linked in, these functions are not available, +// therefore Abseil code which depends on these functions is conditioned on the +// definition of LEAK_SANITIZER. +#include "absl/base/attributes.h" +#include "absl/debugging/leak_check.h" + +#ifndef 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 + +#else + +#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 + +#endif // LEAK_SANITIZER diff --git a/src/absl/debugging/leak_check.h b/src/absl/debugging/leak_check.h new file mode 100644 index 0000000..5fc2b05 --- /dev/null +++ b/src/absl/debugging/leak_check.h @@ -0,0 +1,133 @@ +// 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. +// +// See https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// +// ----------------------------------------------------------------------------- +#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/leak_check_disable.cc b/src/absl/debugging/leak_check_disable.cc new file mode 100644 index 0000000..924d6e3 --- /dev/null +++ b/src/absl/debugging/leak_check_disable.cc @@ -0,0 +1,20 @@ +// 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. + +// Disable LeakSanitizer when this file is linked in. +// This function overrides __lsan_is_turned_off from sanitizer/lsan_interface.h +extern "C" int __lsan_is_turned_off(); +extern "C" int __lsan_is_turned_off() { + return 1; +} diff --git a/src/absl/debugging/stacktrace.cc b/src/absl/debugging/stacktrace.cc new file mode 100644 index 0000000..1f7c7d8 --- /dev/null +++ b/src/absl/debugging/stacktrace.cc @@ -0,0 +1,140 @@ +// 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_generic-inl.inc" +# include "absl/debugging/internal/stacktrace_powerpc-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..2d5235c --- /dev/null +++ b/src/absl/debugging/symbolize.cc @@ -0,0 +1,37 @@ +#include "cpp-compat.h" +// 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" + +#ifdef _WIN32 +#include +#if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)) || \ + WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +// UWP doesn't have access to win32 APIs. +#define ABSL_INTERNAL_HAVE_SYMBOLIZE_WIN32 +#endif +#endif + +#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) +#include "absl/debugging/symbolize_elf.inc" +#elif defined(ABSL_INTERNAL_HAVE_SYMBOLIZE_WIN32) +// The Windows Symbolizer only works if PDB files containing the debug info +// are available to the program at runtime. +#include "absl/debugging/symbolize_win32.inc" +#elif defined(__APPLE__) +#include "absl/debugging/symbolize_darwin.inc" +#else +#include "absl/debugging/symbolize_unimplemented.inc" +#endif 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..99f25aa --- /dev/null +++ b/src/absl/debugging/symbolize_elf.inc @@ -0,0 +1,1560 @@ +// 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" + +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); + + 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) : cpp_compat_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); + + 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; + } + } + 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::GetSymbol(const void *const pc) { + const char *entry = FindSymbolInCache(pc); + if (entry != nullptr) { + return entry; + } + symbol_buf_[0] = '\0'; + + 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_); +} + +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_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..6e2650d --- /dev/null +++ b/src/absl/debugging/symbolize_win32.inc @@ -0,0 +1,82 @@ +// 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; + return false; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/src/absl/functional/bind_front.h b/src/absl/functional/bind_front.h new file mode 100644 index 0000000..5b47970 --- /dev/null +++ b/src/absl/functional/bind_front.h @@ -0,0 +1,184 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// 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_ + +#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. +// +// 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"); +// +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)...); +} + +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..6e03ac2 --- /dev/null +++ b/src/absl/functional/function_ref.h @@ -0,0 +1,139 @@ +// 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/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`. +// +// 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 > + FunctionRef(const F& f) // NOLINT(runtime/explicit) + : 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; + + // 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/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..2b5ff62 --- /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 ; +}; + +template +constexpr bool HasRebindAlloc(...) { + return false; +} + +template +constexpr bool HasRebindAlloc(typename T::template rebind::other*) { + return true; +} + +template (nullptr)> +struct RebindAlloc { + using type = typename RebindFirstArg::type; +}; + +template +struct RebindAlloc { + using type = typename T::template rebind::other; +}; + +} // namespace memory_internal + +// ----------------------------------------------------------------------------- +// Class Template: pointer_traits +// ----------------------------------------------------------------------------- +// +// An implementation of C++11's std::pointer_traits. +// +// Provided for portability on toolchains that have a working C++11 compiler, +// but the standard library is lacking in C++11 support. For example, some +// version of the Android NDK. +// + +template +struct pointer_traits { + using pointer = Ptr; + + // element_type: + // Ptr::element_type if present. Otherwise T if Ptr is a template + // instantiation Template + using element_type = typename memory_internal::ElementType::type; + + // difference_type: + // Ptr::difference_type if present, otherwise std::ptrdiff_t + using difference_type = + memory_internal::ExtractOrT; + + // rebind: + // Ptr::rebind if exists, otherwise Template if Ptr is a + // template instantiation Template + template + using rebind = typename memory_internal::RebindPtr::type; + + // pointer_to: + // Calls Ptr::pointer_to(r) + static pointer pointer_to(element_type& r) { // NOLINT(runtime/references) + return Ptr::pointer_to(r); + } +}; + +// Specialization for T*. +template +struct pointer_traits { + using pointer = T*; + using element_type = T; + using difference_type = std::ptrdiff_t; + + template + using rebind = U*; + + // pointer_to: + // Calls std::addressof(r) + static pointer pointer_to( + element_type& r) noexcept { // NOLINT(runtime/references) + return std::addressof(r); + } +}; + +// ----------------------------------------------------------------------------- +// Class Template: allocator_traits +// ----------------------------------------------------------------------------- +// +// A C++11 compatible implementation of C++17's std::allocator_traits. +// +#if __cplusplus >= 201703L +using std::allocator_traits; +#else // __cplusplus >= 201703L +template +struct allocator_traits { + using allocator_type = Alloc; + + // value_type: + // Alloc::value_type + using value_type = typename Alloc::value_type; + + // pointer: + // Alloc::pointer if present, otherwise value_type* + using pointer = memory_internal::ExtractOrT; + + // const_pointer: + // Alloc::const_pointer if present, otherwise + // absl::pointer_traits::rebind + using const_pointer = + memory_internal::ExtractOrT:: + template rebind>; + + // void_pointer: + // Alloc::void_pointer if present, otherwise + // absl::pointer_traits::rebind + using void_pointer = memory_internal::ExtractOrT< + memory_internal::GetVoidPointer, Alloc, + typename absl::pointer_traits::template rebind>; + + // const_void_pointer: + // Alloc::const_void_pointer if present, otherwise + // absl::pointer_traits::rebind + using const_void_pointer = memory_internal::ExtractOrT< + memory_internal::GetConstVoidPointer, Alloc, + typename absl::pointer_traits::template rebind>; + + // difference_type: + // Alloc::difference_type if present, otherwise + // absl::pointer_traits::difference_type + using difference_type = memory_internal::ExtractOrT< + memory_internal::GetDifferenceType, Alloc, + typename absl::pointer_traits::difference_type>; + + // size_type: + // Alloc::size_type if present, otherwise + // std::make_unsigned::type + using size_type = memory_internal::ExtractOrT< + memory_internal::GetSizeType, Alloc, + typename std::make_unsigned::type>; + + // propagate_on_container_copy_assignment: + // Alloc::propagate_on_container_copy_assignment if present, otherwise + // std::false_type + using propagate_on_container_copy_assignment = memory_internal::ExtractOrT< + memory_internal::GetPropagateOnContainerCopyAssignment, Alloc, + std::false_type>; + + // propagate_on_container_move_assignment: + // Alloc::propagate_on_container_move_assignment if present, otherwise + // std::false_type + using propagate_on_container_move_assignment = memory_internal::ExtractOrT< + memory_internal::GetPropagateOnContainerMoveAssignment, Alloc, + std::false_type>; + + // propagate_on_container_swap: + // Alloc::propagate_on_container_swap if present, otherwise std::false_type + using propagate_on_container_swap = + memory_internal::ExtractOrT; + + // is_always_equal: + // Alloc::is_always_equal if present, otherwise std::is_empty::type + using is_always_equal = + memory_internal::ExtractOrT::type>; + + // rebind_alloc: + // Alloc::rebind::other if present, otherwise Alloc if this Alloc + // is Alloc + template + using rebind_alloc = typename memory_internal::RebindAlloc::type; + + // rebind_traits: + // absl::allocator_traits> + template + using rebind_traits = absl::allocator_traits>; + + // allocate(Alloc& a, size_type n): + // Calls a.allocate(n) + static pointer allocate(Alloc& a, // NOLINT(runtime/references) + size_type n) { + return a.allocate(n); + } + + // allocate(Alloc& a, size_type n, const_void_pointer hint): + // Calls a.allocate(n, hint) if possible. + // If not possible, calls a.allocate(n) + static pointer allocate(Alloc& a, size_type n, // NOLINT(runtime/references) + const_void_pointer hint) { + return allocate_impl(0, a, n, hint); + } + + // deallocate(Alloc& a, pointer p, size_type n): + // Calls a.deallocate(p, n) + static void deallocate(Alloc& a, pointer p, // NOLINT(runtime/references) + size_type n) { + a.deallocate(p, n); + } + + // construct(Alloc& a, T* p, Args&&... args): + // Calls a.construct(p, std::forward(args)...) if possible. + // If not possible, calls + // ::new (static_cast(p)) T(std::forward(args)...) + template + static void construct(Alloc& a, T* p, // NOLINT(runtime/references) + Args&&... args) { + construct_impl(0, a, p, std::forward(args)...); + } + + // destroy(Alloc& a, T* p): + // Calls a.destroy(p) if possible. If not possible, calls p->~T(). + template + static void destroy(Alloc& a, T* p) { // NOLINT(runtime/references) + destroy_impl(0, a, p); + } + + // max_size(const Alloc& a): + // Returns a.max_size() if possible. If not possible, returns + // std::numeric_limits::max() / sizeof(value_type) + static size_type max_size(const Alloc& a) { return max_size_impl(0, a); } + + // select_on_container_copy_construction(const Alloc& a): + // Returns a.select_on_container_copy_construction() if possible. + // If not possible, returns a. + static Alloc select_on_container_copy_construction(const Alloc& a) { + return select_on_container_copy_construction_impl(0, a); + } + + private: + template + static auto allocate_impl(int, A& a, // NOLINT(runtime/references) + size_type n, const_void_pointer hint) + -> decltype(a.allocate(n, hint)) { + return a.allocate(n, hint); + } + static pointer allocate_impl(char, Alloc& a, // NOLINT(runtime/references) + size_type n, const_void_pointer) { + return a.allocate(n); + } + + template + static auto construct_impl(int, A& a, // NOLINT(runtime/references) + Args&&... args) + -> decltype(a.construct(std::forward(args)...)) { + a.construct(std::forward(args)...); + } + + template + static void construct_impl(char, Alloc&, T* p, Args&&... args) { + ::new (static_cast(p)) T(std::forward(args)...); + } + + template + static auto destroy_impl(int, A& a, // NOLINT(runtime/references) + T* p) -> decltype(a.destroy(p)) { + a.destroy(p); + } + template + static void destroy_impl(char, Alloc&, T* p) { + p->~T(); + } + + template + static auto max_size_impl(int, const A& a) -> decltype(a.max_size()) { + return a.max_size(); + } + static size_type max_size_impl(char, const Alloc&) { + return (std::numeric_limits::max)() / sizeof(value_type); + } + + template + static auto select_on_container_copy_construction_impl(int, const A& a) + -> decltype(a.select_on_container_copy_construction()) { + return a.select_on_container_copy_construction(); + } + static Alloc select_on_container_copy_construction_impl(char, + const Alloc& a) { + return a; + } +}; +#endif // __cplusplus >= 201703L + +namespace memory_internal { + +// This template alias transforms Alloc::is_nothrow into a metafunction with +// Alloc as a parameter so it can be used with ExtractOrT<>. +template +using GetIsNothrow = typename Alloc::is_nothrow; + +} // namespace memory_internal + +// ABSL_ALLOCATOR_NOTHROW is a build time configuration macro for user to +// specify whether the default allocation function can throw or never throws. +// If the allocation function never throws, user should define it to a non-zero +// value (e.g. via `-DABSL_ALLOCATOR_NOTHROW`). +// If the allocation function can throw, user should leave it undefined or +// define it to zero. +// +// allocator_is_nothrow is a traits class that derives from +// Alloc::is_nothrow if present, otherwise std::false_type. It's specialized +// for Alloc = std::allocator for any type T according to the state of +// ABSL_ALLOCATOR_NOTHROW. +// +// default_allocator_is_nothrow is a class that derives from std::true_type +// when the default allocator (global operator new) never throws, and +// std::false_type when it can throw. It is a convenience shorthand for writing +// allocator_is_nothrow> (T can be any type). +// NOTE: allocator_is_nothrow> is guaranteed to derive from +// the same type for all T, because users should specialize neither +// allocator_is_nothrow nor std::allocator. +template +struct allocator_is_nothrow + : memory_internal::ExtractOrT {}; + +#if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW +template +struct allocator_is_nothrow> : std::true_type {}; +struct default_allocator_is_nothrow : std::true_type {}; +#else +struct default_allocator_is_nothrow : std::false_type {}; +#endif + +namespace memory_internal { +template +void ConstructRange(Allocator& alloc, Iterator first, Iterator last, + const Args&... args) { + for (Iterator cur = first; cur != last; ++cur) { + ABSL_INTERNAL_TRY { + std::allocator_traits::construct(alloc, std::addressof(*cur), + args...); + } + ABSL_INTERNAL_CATCH_ANY { + while (cur != first) { + --cur; + std::allocator_traits::destroy(alloc, std::addressof(*cur)); + } + ABSL_INTERNAL_RETHROW; + } + } +} + +template +void CopyRange(Allocator& alloc, Iterator destination, InputIterator first, + InputIterator last) { + for (Iterator cur = destination; first != last; + static_cast(++cur), static_cast(++first)) { + ABSL_INTERNAL_TRY { + std::allocator_traits::construct(alloc, std::addressof(*cur), + *first); + } + ABSL_INTERNAL_CATCH_ANY { + while (cur != destination) { + --cur; + std::allocator_traits::destroy(alloc, std::addressof(*cur)); + } + ABSL_INTERNAL_RETHROW; + } + } +} +} // namespace memory_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_MEMORY_MEMORY_H_ diff --git a/src/absl/meta/type_traits.h b/src/absl/meta/type_traits.h new file mode 100644 index 0000000..d5cb5f3 --- /dev/null +++ b/src/absl/meta/type_traits.h @@ -0,0 +1,767 @@ +// +// 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. +// +// ----------------------------------------------------------------------------- +// type_traits.h +// ----------------------------------------------------------------------------- +// +// This file contains C++11-compatible versions of standard API +// functions for determining the characteristics of types. Such traits can +// support type inference, classification, and transformation, as well as +// make it easier to write templates based on generic type behavior. +// +// See https://en.cppreference.com/w/cpp/header/type_traits +// +// WARNING: use of many of the constructs in this header will count as "complex +// template metaprogramming", so before proceeding, please carefully consider +// https://google.github.io/styleguide/cppguide.html#Template_metaprogramming +// +// WARNING: using template metaprogramming to detect or depend on API +// features is brittle and not guaranteed. Neither the standard library nor +// Abseil provides any guarantee that APIs are stable in the face of template +// metaprogramming. Use with caution. +#ifndef ABSL_META_TYPE_TRAITS_H_ +#define ABSL_META_TYPE_TRAITS_H_ + +#include +#include +#include + +#include "absl/base/config.h" + +// MSVC constructibility traits do not detect destructor properties and so our +// implementations should not use them as a source-of-truth. +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) +#define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Defined and documented later on in this file. +template +struct is_trivially_destructible; + +// Defined and documented later on in this file. +template +struct is_trivially_move_assignable; + +namespace type_traits_internal { + +// Silence MSVC warnings about the destructor being defined as deleted. +#if defined(_MSC_VER) && !defined(__GNUC__) +#pragma warning(push) +#pragma warning(disable : 4624) +#endif // defined(_MSC_VER) && !defined(__GNUC__) + +template +union SingleMemberUnion { + T t; +}; + +// Restore the state of the destructor warning that was silenced above. +#if defined(_MSC_VER) && !defined(__GNUC__) +#pragma warning(pop) +#endif // defined(_MSC_VER) && !defined(__GNUC__) + +template +struct IsTriviallyMoveConstructibleObject + : std::integral_constant< + bool, std::is_move_constructible< + type_traits_internal::SingleMemberUnion>::value && + absl::is_trivially_destructible::value> {}; + +template +struct IsTriviallyCopyConstructibleObject + : std::integral_constant< + bool, std::is_copy_constructible< + type_traits_internal::SingleMemberUnion>::value && + absl::is_trivially_destructible::value> {}; + +template +struct IsTriviallyMoveAssignableReference : std::false_type {}; + +template +struct IsTriviallyMoveAssignableReference + : absl::is_trivially_move_assignable::type {}; + +template +struct IsTriviallyMoveAssignableReference + : absl::is_trivially_move_assignable::type {}; + +template +struct VoidTImpl { + using type = void; +}; + +// This trick to retrieve a default alignment is necessary for our +// implementation of aligned_storage_t to be consistent with any implementation +// of std::aligned_storage. +template > +struct default_alignment_of_aligned_storage; + +template +struct default_alignment_of_aligned_storage> { + static constexpr size_t value = Align; +}; + +//////////////////////////////// +// Library Fundamentals V2 TS // +//////////////////////////////// + +// NOTE: The `is_detected` family of templates here differ from the library +// fundamentals specification in that for library fundamentals, `Op` is +// evaluated as soon as the type `is_detected` undergoes +// substitution, regardless of whether or not the `::value` is accessed. That +// is inconsistent with all other standard traits and prevents lazy evaluation +// in larger contexts (such as if the `is_detected` check is a trailing argument +// of a `conjunction`. This implementation opts to instead be lazy in the same +// way that the standard traits are (this "defect" of the detection idiom +// specifications has been reported). + +template class Op, class... Args> +struct is_detected_impl { + using type = std::false_type; +}; + +template