SERVER-73467 add grpc 1.46.6 to third party

This commit is contained in:
Daniel Moody 2023-03-15 16:02:24 -05:00 committed by Evergreen Agent
parent f5df0445cf
commit f861cf6c9c
6625 changed files with 1695990 additions and 4776 deletions

View File

@ -21,13 +21,15 @@ a notice will be included in
| Name | License | Vendored Version | Emits persisted data | Distributed in Release Binaries |
| ---------------------------| ----------------- | ------------------| :------------------: | :-----------------------------: |
| [abseil-cpp] | Apache-2.0 | 20210324.1 | | ✗ |
| [abseil-cpp] | Apache-2.0 | 20211102.0 | | ✗ |
| [Aladdin MD5] | Zlib | Unknown | ✗ | ✗ |
| [ASIO] | BSL-1.0 | b0926b61b0 | | ✗ |
| [benchmark] | Apache-2.0 | 1.5.2 | | |
| [Boost] | BSL-1.0 | 1.76.0 | | ✗ |
| [c-ares] | MIT | 1.17.2 | | ✗ |
| [fmt] | BSD-2-Clause | 7.1.3 | | ✗ |
| [GPerfTools] | BSD-3-Clause | 2.9.1 | | ✗ |
| [gRPC] | Apache-2.0 | 1.46.6 | | ✗ |
| [ICU4] | ICU | 57.1 | ✗ | ✗ |
| [Intel Decimal FP Library] | BSD-3-Clause | 2.0 Update 1 | | ✗ |
| [JSON-Schema-Test-Suite] | MIT | 728066f9c5 | | |
@ -38,6 +40,8 @@ a notice will be included in
| [ocspbuilder] | MIT | 0.10.2 | | |
| [ocspresponder] | Apache-2.0 | 0.5.0 | | |
| [pcre2] | BSD-3-Clause | 10.40 | | ✗ |
| [protobuf] | BSD-3-Clause | 3.19.5 | | ✗ |
| [re2] | BSD-3-Clause | 2021-09-01 | | ✗ |
| [S2] | Apache-2.0 | Unknown | ✗ | ✗ |
| [SafeInt] | MIT | 3.0.26 | | |
| [schemastore.org] | Apache-2.0 | 6847cfc3a1 | | |

View File

@ -467,11 +467,13 @@ for pack in [
('boost', ),
('fmt', ),
('google-benchmark', 'Google benchmark'),
('grpc', ),
('icu', 'ICU'),
('intel_decimal128', 'intel decimal128'),
('libbson', ),
('libmongocrypt', ),
('pcre2', ),
('protobuf', "Protocol Buffers"),
('snappy', ),
('stemmer', ),
('tcmalloc', ),
@ -836,6 +838,14 @@ def variable_shlex_converter(val):
return shlex.split(val, posix=(parse_mode == 'posix'))
# Setup the command-line variables
def where_is_converter(val):
path = WhereIs(val)
if path:
return os.path.abspath(path)
return val
def variable_arch_converter(val):
arches = {
'x86_64': 'x86_64',
@ -1402,6 +1412,20 @@ env_vars.Add(
help='Path to the strip utility (non-darwin platforms probably use OBJCOPY for this)',
)
env_vars.Add(
'PROTOC',
default="$$PROTOC_VAR_GEN",
help='Path to protobuf compiler.',
converter=where_is_converter,
)
env_vars.Add(
'PROTOC_GRPC_PLUGIN',
default="$$PROTOC_GRPC_PLUGIN_GEN",
help='Path to protobuf compiler grpc plugin.',
converter=where_is_converter,
)
env_vars.Add(
'SPLIT_DWARF',
help=
@ -1410,6 +1434,14 @@ env_vars.Add(
default="auto",
)
env_vars.Add(
'ENABLE_GRPC_BUILD',
help=
'Set the boolean (auto, on/off true/false 1/0) to enable building grpc and protobuf compiler.',
converter=functools.partial(bool_var_converter, var='ENABLE_GRPC_BUILD'),
default="0",
)
env_vars.Add(
'GDB',
help="Configures the path to the 'gdb' debugger binary.",
@ -2183,9 +2215,26 @@ env['BUILDERS']['SharedArchive'] = SCons.Builder.Builder(
src_suffix=env['BUILDERS']['SharedLibrary'].src_suffix,
)
# Teach builders how to build idl files
# Teach object builders how to build underlying generated types
for builder in ['SharedObject', 'StaticObject']:
env['BUILDERS'][builder].add_src_builder("Idlc")
env['BUILDERS'][builder].add_src_builder("Protoc")
# These allow delayed evaluation of the AIB values for the default values of
# the corresponding command line variables
def protoc_var_gen(env, target, source, for_signature):
return env.File("$DESTDIR/$PREFIX_BINDIR/protobuf_compiler$PROGSUFFIX")
env['PROTOC_VAR_GEN'] = protoc_var_gen
def protoc_grpc_plugin_var_gen(env, target, source, for_signature):
return env.File("$DESTDIR/$PREFIX_BINDIR/grpc_cpp_plugin$PROGSUFFIX")
env['PROTOC_GRPC_PLUGIN_GEN'] = protoc_grpc_plugin_var_gen
if link_model.startswith("dynamic"):
@ -5126,6 +5175,16 @@ def doConfigure(myenv):
[boostlib + suffix for suffix in boostSuffixList],
language='C++',
)
if use_system_version_of_library('protobuf'):
conf.FindSysLibDep("protobuf", ["protobuf"])
conf.FindSysLibDep("protoc", ["protoc"])
if use_system_version_of_library('grpc'):
conf.FindSysLibDep("grpc", ["grpc"])
conf.FindSysLibDep("grpcxx", ["grpc++"])
conf.FindSysLibDep("grpcxx_reflection", ["grpc++_reflection"])
if posix_system:
conf.env.SetConfigHeaderDefine("MONGO_CONFIG_HAVE_HEADER_UNISTD_H")
conf.CheckLib('rt')
@ -5534,6 +5593,7 @@ if get_option('ninja') != 'disabled':
env.AppendUnique(CCFLAGS=["-fdiagnostics-color"])
ninja_builder = Tool("ninja")
env["NINJA_BUILDDIR"] = env.Dir("$NINJA_BUILDDIR")
ninja_builder.generate(env)
@ -5787,6 +5847,10 @@ if gdb_index_enabled == True:
elif env.get('GDB_INDEX') != 'auto':
env.FatalError('Could not enable explicit request for gdb index generation.')
if env.get('ENABLE_GRPC_BUILD'):
env.SetConfigHeaderDefine("MONGO_CONFIG_GRPC")
env.Tool('protobuf_compiler')
if get_option('separate-debug') == "on" or env.TargetOSIs("windows"):
separate_debug = Tool('separate_debug')

View File

@ -481,7 +481,7 @@ class InDegreeOne(Analyzer):
in_degree_one_nodes = []
for node, data in self._dependency_graph.nodes(data=True):
if (len(self._dependents_graph[node]) < 2
and data[NodeProps.bin_type.name] == 'SharedLibrary'):
and data.get(NodeProps.bin_type.name) == 'SharedLibrary'):
if len(self._dependents_graph[node]) == 1:
depender = list(self._dependents_graph[node].items())[0][0]

View File

@ -2096,3 +2096,293 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
See the License for the specific language governing permissions and
limitations under the License.
End
29) License Notice for gRPC
------------------------------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
30) License Notice for protobuf
------------------------------
Copyright 2008 Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Code generated by the Protocol Buffer compiler is owned by the owner
of the input file used when generating it. This code is not
standalone and requires a support library to be linked with it. This
support library is itself covered by the above license.
31) License Notice for re2
------------------------------
// Copyright (c) 2009 The RE2 Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32) License Notice for c-ares
------------------------------
Copyright (c) 2007 - 2018, Daniel Stenberg with many contributors, see AUTHORS
file.
Copyright 1998 by the Massachusetts Institute of Technology.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided that
the above copyright notice appear in all copies and that both that copyright
notice and this permission notice appear in supporting documentation, and that
the name of M.I.T. not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
M.I.T. makes no representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied warranty.

View File

@ -203,3 +203,35 @@ buildvariants:
- name: .stitch
- name: crypt_build_debug_and_test
- name: .integration
- <<: *generic_linux_compile_params
name: &linux-x86-dynamic-grpc-suggested linux-x86-dynamic-grpc-suggested
display_name: "* Linux x86 Shared Library with GRPC"
modules:
- enterprise
stepback: false
expansions:
<<: *generic_linux_compile_expansions
compile_flags: >-
--ssl
MONGO_DISTMOD=rhel80
-j$(grep -c ^processor /proc/cpuinfo)
--variables-files=etc/scons/mongodbtoolchain_stable_gcc.vars
--link-model=dynamic
ENABLE_GRPC_BUILD=1
compile_variant: *linux-x86-dynamic-grpc-suggested
clang_tidy_toolchain: v4
tasks:
- name: compile_ninja_quick_TG
- name: compile_test_and_package_parallel_unittest_stream_TG
- name: compile_test_and_package_parallel_core_stream_TG
- name: compile_test_and_package_parallel_dbtest_stream_TG
- name: generate_buildid_to_debug_symbols_mapping
- name: .lint
- name: libdeps_graph_linting_TG
distros:
- rhel80-large
- name: clang_tidy_TG
distros:
- rhel80-xxlarge

View File

@ -2810,3 +2810,26 @@ buildvariants:
- name: push
- name: .crypt
- name: .publish_crypt
- name: enterprise-rhel-82-arm64-grpc
display_name: "Enterprise RHEL 8.2 arm64 GRPC"
cron: "0 4 * * *" # From the ${project_nightly_cron} parameter.
modules:
- enterprise
run_on:
- rhel82-arm64-small
stepback: false
expansions:
compile_flags: >-
--ssl
MONGO_DISTMOD=rhel80
-j$(grep -c ^processor /proc/cpuinfo)
--variables-files=etc/scons/mongodbtoolchain_stable_gcc.vars
--link-model=dynamic
ENABLE_GRPC_BUILD=1
compile_variant: enterprise-rhel-82-arm64-grpc
scons_cache_scope: shared
tasks:
- name: compile_test_and_package_parallel_unittest_stream_TG
- name: compile_test_and_package_parallel_core_stream_TG
- name: compile_test_and_package_parallel_dbtest_stream_TG

View File

@ -88,6 +88,14 @@ components:
is_test_only: true
team_owner: "Wiredtiger"
c-ares:
homepage_url: https://c-ares.org/
open_hub_url: https://www.openhub.net/p/c-ares
release_monitoring_id: 5840
local_directory_path: src/third_party/cares
team_owner: "Service Architecture"
upgrade_suppression: TODO SERVER-74870
"dcleblanc/SafeInt":
homepage_url: https://github.com/dcleblanc/SafeInt
open_hub_url: https://www.openhub.net/p/SafeInt
@ -137,6 +145,13 @@ components:
# TODO - fix the version number in Black Duck
upgrade_suppression: TODO SERVER-67432
grpc:
homepage_url: https://grpc.io/
open_hub_url: https://www.openhub.net/p/grpc
release_monitoring_id: 19117
local_directory_path: src/third_party/grpc
team_owner: "Service Architecture"
"ICU for C/C++ (ICU4C)":
homepage_url: http://site.icu-project.org/
open_hub_url: https://www.openhub.net/p/icu4c
@ -257,6 +272,13 @@ components:
local_directory_path: src/third_party/pcre2
team_owner: "Query"
Protobuf:
homepage_url: https://developers.google.com/protocol-buffers/
open_hub_url: https://www.openhub.net/p/protobuf
release_monitoring_id: 3715
local_directory_path: src/third_party/protobuf
team_owner: "Service Architecture"
python-testscenarios:
homepage_url: https://launchpad.net/testscenarios
open_hub_url: https://www.openhub.net/p/testscenarios
@ -265,6 +287,13 @@ components:
is_test_only: true
team_owner: "Wiredtiger"
google-re2:
homepage_url: https://github.com/google/re2
open_hub_url: https://www.openhub.net/p/google-re2
release_monitoring_id: 10500
local_directory_path: src/third_party/re2
team_owner: "Service Architecture"
extras:
homepage_url: https://github.com/testing-cabal/extras
open_hub_url: https://www.openhub.net/p/550227

View File

@ -852,15 +852,22 @@ class NinjaState:
# cycle.
if (generated_source_files and check_generated_source_deps(build)):
# Make all non-generated source targets depend on
# _generated_sources. We use order_only for generated
# sources so that we don't rebuild the world if one
# generated source was rebuilt. We just need to make
# sure that all of these sources are generated before
# other builds.
order_only = build.get("order_only", [])
order_only.append(generated_sources_alias)
build["order_only"] = order_only
depends_on_gen_source = build['rule'] != 'INSTALL'
if build['outputs']:
if self.env.Entry(
build['outputs'][0]).get_build_env().get('NINJA_GENSOURCE_INDEPENDENT'):
depends_on_gen_source = False
if depends_on_gen_source:
# Make all non-generated source targets depend on
# _generated_sources. We use order_only for generated
# sources so that we don't rebuild the world if one
# generated source was rebuilt. We just need to make
# sure that all of these sources are generated before
# other builds.
order_only = build.get("order_only", [])
order_only.append(generated_sources_alias)
build["order_only"] = order_only
if "order_only" in build:
build["order_only"].sort()
@ -1183,8 +1190,8 @@ def gen_get_response_file_command(env, rule, tool, tool_is_dynamic=False, custom
# Add 1 so we always keep the actual tool inside of cmd
tool_idx = cmd_list.index(tool_command) + 1
except ValueError:
raise Exception("Could not find tool {} in {} generated from {}".format(
tool, cmd_list, get_comstr(env, action, targets, sources)))
raise Exception("Could not find tool {}({}) in {} generated from {}".format(
tool, tool_command, cmd_list, get_comstr(env, action, targets, sources)))
cmd, rsp_content = cmd_list[:tool_idx], cmd_list[tool_idx:]
rsp_content = " ".join(rsp_content)
@ -1419,8 +1426,8 @@ def register_custom_rule_mapping(env, pre_subst_string, rule):
def register_custom_rule(env, rule, command, description="", deps=None, pool=None,
use_depfile=False, use_response_file=False, response_file_content="$rspc",
restat=False):
use_depfile=False, depfile=None, use_response_file=False,
response_file_content="$rspc", restat=False):
"""Allows specification of Ninja rules from inside SCons files."""
rule_obj = {
"command": command,
@ -1428,7 +1435,10 @@ def register_custom_rule(env, rule, command, description="", deps=None, pool=Non
}
if use_depfile:
rule_obj["depfile"] = os.path.join(get_path(env['NINJA_BUILDDIR']), '$out.depfile')
if depfile:
rule_obj["depfile"] = depfile
else:
rule_obj["depfile"] = os.path.join(get_path(env['NINJA_BUILDDIR']), '$out.depfile')
if deps is not None:
rule_obj["deps"] = deps

View File

@ -0,0 +1,416 @@
# Copyright 2022 MongoDB Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
"""Protobuf Compiler Scons Tool."""
import os
import subprocess
import tempfile
import contextlib
import SCons
# context manager copied from
# https://stackoverflow.com/a/57701186/1644736
@contextlib.contextmanager
def temporary_filename(suffix=None):
"""Context that introduces a temporary file.
Creates a temporary file, yields its name, and upon context exit, deletes it.
(In contrast, tempfile.NamedTemporaryFile() provides a 'file' object and
deletes the file as soon as that file object is closed, so the temporary file
cannot be safely re-opened by another library or process.)
Args:
suffix: desired filename extension (e.g. '.mp4').
Yields:
The name of the temporary file.
"""
try:
f = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
tmp_name = f.name
f.close()
yield tmp_name
finally:
os.unlink(tmp_name)
def get_gen_type_and_dir(env, gen_type):
# Utility function for parsing out the gen type and desired gen dir
if SCons.Util.is_String(gen_type):
gen_out_dir = None
elif SCons.Util.is_List(gen_type) and len(gen_type) == 1:
gen_type = gen_type[0]
gen_out_dir = None
elif SCons.Util.is_List(gen_type) and len(gen_type) == 2:
gen_out_dir = gen_type[1]
gen_type = gen_type[0]
else:
raise ValueError(
f"Invalid generation type {gen_type}, must be string of gen type, or list of gen type and gen out dir."
)
return (gen_type, gen_out_dir)
def protoc_emitter(target, source, env):
new_targets = []
gen_types = env.subst_list('$PROTOC_GEN_TYPES', target=target, source=source)
base_file_name = os.path.splitext(target[0].get_path())[0]
for gen_type in gen_types:
# Check for valid requested gen type.
gen_type, gen_out_dir = get_gen_type_and_dir(env, gen_type)
if gen_type not in env['_PROTOC_SUPPORTED_GEN_TYPES']:
raise ValueError(
f"Requested protoc gen output of {gen_type}, but only {env['_PROTOC_SUPPORTED_GEN_TYPES']} are currenlty supported."
)
if gen_out_dir:
base_file_name = os.path.join(
env.Dir(gen_out_dir).get_path(),
os.path.split(SCons.Util.splitext(target[0].get_path())[0])[1])
# Create the targets by extensions list for this type in the desired gen dir.
exts = env['_PROTOC_SUPPORTED_GEN_TYPES'][gen_type]
new_targets += [env.File(f"{base_file_name}{ext}") for ext in exts]
if gen_types:
# Setup the dependency file.
# This is little weird currently, because of the limitation of ninja and multiple
# outputs. The base file name can change for each gen type, so in this case we are
# taking the last one. This works if all gen outs are in the same dir and makes ninja
# happy, but if there are multiple gen_out dirs, then in a scons only build the deps
# is gened to the last in the list, which is awkward, but because this is only refernced
# as a target throughout the rest of tool, it works fine in scons build.
dep_file = env.File(f"{base_file_name}.protodeps")
new_targets += [dep_file]
# Create targets for any listed plugins.
plugins = env.get('PROTOC_PLUGINS', [])
for name in plugins:
out_dir = plugins[name].get('gen_out')
exts = plugins[name].get('exts', [])
if out_dir:
base_file_name = os.path.join(
env.Dir(out_dir).get_path(),
os.path.split(SCons.Util.splitext(target[0].get_path())[0])[1])
new_targets += [env.File(f"{base_file_name}{ext}") for ext in exts]
env.Alias("generated-sources", new_targets)
return new_targets, source
def protoc_scanner(node, env, path):
deps = []
# Need to depend on the compiler and any plugins.
plugins = env.get('PROTOC_PLUGINS', {})
for name in plugins:
deps.append(env.File(env.subst(plugins[name].get('plugin'))))
deps.append(env.File("$PROTOC"))
# For scanning the proto dependencies from within the proto files themselves,
# there are two ways (with out writing a custom reader) to do it. One is with the
# output depends file and other other is with a tool the protobuf project supplies.
# The problem with the depends files, is you must first run the command before you can
# get the dependencies, which has some downsides:
# https://scons.org/doc/4.4.0/HTML/scons-user.html#idp105548894482512
#
# Using the reader provided by protobuf project works, but you must have access to the
# proto which gives this functionality.
#
# Scanners will run multiple times during the building phase, revisiting as new dependencies
# from the original scan are completed. Here we will use both methods, because in the case
# you have an existing dep file you can get more dependency information on the first scan.
if str(node).endswith('.protodeps'):
if os.path.exists(node.get_path()):
# This code was mostly ripped from SCons ParseDepends function
try:
with open(node.get_path(), 'r') as fp:
lines = SCons.Util.LogicalLines(fp).readlines()
except IOError:
pass
else:
lines = [l for l in lines if l[0] != '#']
for line in lines:
try:
target, depends = line.split(':', 1)
except (AttributeError, ValueError):
# Throws AttributeError if line isn't a string. Can throw
# ValueError if line doesn't split into two or more elements.
pass
else:
deps += [env.File(d) for d in depends.split()]
if os.path.exists(env.File("$PROTOC").abspath) and os.path.exists(
env.File('$PROTOC_DESCRIPTOR_PROTO').abspath):
# First we generate a the command line so we can extract the proto_paths as they
# used for finding imported protos. Then we run the command and output the
# descriptor set to a file for use later. The descriptor set is output as binary data
# intended to be read in by other protos. In this case the second command does that
# and extracts the dependencies.
source = node.sources[0]
with temporary_filename() as temp_filename:
cmd_list, _, _ = env['BUILDERS']['Protoc'].action.process([node], [source], env,
executor=None)
paths = [
str(proto_path) for proto_path in cmd_list[0]
if str(proto_path).startswith('--proto_path=')
]
cmd = [env.File("$PROTOC").path] + paths + [
'--include_imports', f'--descriptor_set_out={temp_filename}',
source.srcnode().path
]
subprocess.run(cmd)
with open(temp_filename) as f:
cmd = [env.File("$PROTOC").path] + paths + [
'--decode=google.protobuf.FileDescriptorSet',
str(env.File('$PROTOC_DESCRIPTOR_PROTO'))
]
p = subprocess.run(cmd, stdin=f, capture_output=True)
for line in p.stdout.decode().splitlines():
if line.startswith(" name: \""):
file = line[len(" name: \""):-1]
for path in paths:
proto_file = os.path.join(path.replace('--proto_path=', ''), file)
if os.path.exists(proto_file) and proto_file != str(
source.srcnode()):
dep_node = env.File(proto_file)
if dep_node not in deps:
deps += [env.File(proto_file)]
break
return sorted(deps, key=lambda dep: dep.path)
protoc_scanner = SCons.Scanner.Scanner(function=protoc_scanner)
def get_cmd_line_dirs(env, target, source):
source_dir = os.path.dirname(source[0].srcnode().path)
target_dir = os.path.dirname(target[0].get_path())
return target_dir, source_dir
def gen_types(source, target, env, for_signature):
# This subst function is for generating the command line --proto_path and desired
# --TYPE_out options.
cmd_flags = ""
gen_types = env.subst_list('$PROTOC_GEN_TYPES', target=target, source=source)
if gen_types:
for gen_type in gen_types:
gen_type, gen_out_dir = get_gen_type_and_dir(env, gen_type)
exts = tuple(env['_PROTOC_SUPPORTED_GEN_TYPES'][gen_type])
gen_targets = [t for t in target if str(t).endswith(exts)]
if gen_targets:
out_dir, proto_path = get_cmd_line_dirs(env, gen_targets, source)
cmd_flags += f" --proto_path={proto_path} --{gen_type}_out={out_dir}"
# This depends out only works if there is at least one gen out
for t in target:
if str(t).endswith('.protodeps'):
cmd_flags = f'--dependency_out={t} ' + cmd_flags
return cmd_flags
def gen_types_str(source, target, env, for_signature):
# This generates the types from the list of types requested by the user
# for the pretty build output message. Any invalid types are caught in the emitter.
gen_types = []
for gen_type in env.subst_list('$PROTOC_GEN_TYPES', target=target, source=source):
gen_type, gen_out_dir = get_gen_type_and_dir(env, gen_type)
gen_types += [str(gen_type)]
return ', '.join(gen_types)
def gen_plugins(source, target, env, for_signature):
# Plugins are user customizable ways to modify the generation and generate
# additional files if desired. This extracts the desired plugins from the environment
# and formats them to be suitable for the command line.
plugins_cmds = []
plugins = env.get('PROTOC_PLUGINS', [])
for name in plugins:
plugin = plugins[name].get('plugin')
exts = plugins[name].get('exts')
if plugin and exts:
out_dir = plugins[name].get('gen_out', '.')
options = plugins[name].get('options', [])
# A custom out command for this plugin, options to the plugin can
# be passed here with colon separating
cmd_line = f'--{name}_out='
for opt in options:
cmd_line += f'{opt}:'
gen_targets = [t for t in target if str(t).endswith(tuple(exts))]
if gen_targets:
out_dir, proto_path = get_cmd_line_dirs(env, gen_targets, source)
cmd_line += out_dir
# specify the plugin binary
cmd_line += f' --proto_path={proto_path} --plugin=protoc-gen-{name}={env.File(plugin).path}'
plugins_cmds += [cmd_line]
else:
print(
f"Failed to process PROTOC plugin, need valid plugin and extensions {name}: {plugins[name]}"
)
gen_types = env.subst_list('$PROTOC_GEN_TYPES', target=target, source=source)
# In the case the command did not include any standard gen types, we add a command line
# entry so the depends file is still written
if not gen_types:
for t in target:
if str(t).endswith(".protodeps"):
plugins_cmds += [f'--dependency_out={t}']
return ' '.join(plugins_cmds)
def generate(env):
ProtocBuilder = SCons.Builder.Builder(
action=SCons.Action.Action("$PROTOCCOM", "$PROTOCCOMSTR"),
emitter=protoc_emitter,
src_suffix=".proto",
suffix=".cc",
target_scanner=protoc_scanner,
)
env.Append(SCANNERS=protoc_scanner)
env["BUILDERS"]["Protoc"] = ProtocBuilder
env["PROTOC"] = env.get('PROTOC', env.WhereIs('protoc'))
env['PROTOCCOM'] = "$PROTOC $_PROTOCPATHS $_PROTOC_GEN_TYPES $_PROTOC_PLUGINS $PROTOCFLAGS $SOURCE"
env['PROTOCCOMSTR'] = ("Generating $_PROTOC_GEN_TYPES_STR Protocol Buffers from ${SOURCE}"
if not env.Verbose() else "")
# Internal subst function vars
env["_PROTOC_GEN_TYPES"] = gen_types
env['_PROTOC_GEN_TYPES_STR'] = gen_types_str
env['_PROTOC_PLUGINS'] = gen_plugins
env['_PROTOCPATHS'] = '${_concat(PROTOPATH_PREFIX, PROTOCPATHS, PROTOPATH_SUFFIX, __env__)}'
env['PROTOPATH_PREFIX'] = '--proto_path='
env['PROTOPATH_SUFFIX'] = ''
# Somewhat safe cross tool dependency
if hasattr(env, 'NinjaGenResponseFileProvider'):
env.NinjaRule(
rule="PROTO",
command='$env$cmd',
description="Generating protocol buffers $out",
deps="gcc",
use_depfile=True,
depfile="$protodep",
)
def gen_protobuf_provider(env, rule, tool):
def protobuf_provider(env, node, action, targets, sources, executor=None):
provided_rule, variables, tool_command = env.NinjaGetGenericShellCommand(
node, action, targets, sources, executor)
t_dirs = [os.path.dirname(t.get_path()) for t in targets]
if len(set(t_dirs)) > 1:
raise SCons.Errors.BuildError(
node=node, errstr=
"Due to limitations with ninja tool and using phonies for multiple targets, protoc must generate all generated output for a single command to the same directory."
)
for t in targets:
if str(t).endswith('.protodeps'):
variables['protodep'] = str(t)
return "PROTO", variables, tool_command
return protobuf_provider
def robust_rule_mapping(var, rule, tool):
provider = gen_protobuf_provider(env, rule, tool)
env.NinjaRuleMapping("${" + var + "}", provider)
env.NinjaRuleMapping(env[var], provider)
robust_rule_mapping("PROTOCCOM", "PROTO", "$PROTOC")
# TODO create variables to support other generation types, might require a more flexible
# builder setup
env["_PROTOC_SUPPORTED_GEN_TYPES"] = {'cpp': ['.pb.cc', '.pb.h']}
# User facing customizable variables
# PROTOC_GEN_TYPES can be a list of strings, where
# each string is the gen type desired, or it could
# a list of lists, where each list contains first
# the type, the the desired output dir, if no
# dir is specified the scons will build it at the location
# of the source proto file, accounting for variant
# dirs. e.g.
# env["PROTOC_GEN_TYPES"] = [
# 'cpp',
# ['java', "$BUILD_DIR/java_gen_source"]
# ]
env["PROTOC_GEN_TYPES"] = []
# PROTOC_PLUGINS allows customization of the plugins
# for the command lines. It should be a dict of dicts where
# the keys are the names of the plugins, and the plugin must
# specify the plugin binary file path and a list of extensions
# to use on the output files. Optionally you can specify a list
# of options to pass the plugin and a gen out directory. e.g:
# env['PROTOC_PLUGINS']={
# 'grpc': {
# 'plugin': '$PROTOC_GRPC_PLUGIN',
# 'options': ['generate_mock_code=true'],
# 'gen_out': "$BUILD_DIR/grpc_gen"
# 'exts': ['.grpc.pb.cc', '.grpc.pb.h'],
# },
# 'my_plugin': {
# 'plugin': '/usr/bin/my_custom_plugin',
# 'exts': ['.pb.txt'],
# }
# },
env['PROTOC_PLUGINS'] = {}
# This is a proto which allows dependent protos to be extracted
# generally this is in protobuf src tree at google/protobuf/descriptor.proto
env['PROTOC_DESCRIPTOR_PROTO'] = 'google/protobuf/descriptor.proto'
env['PROTOCFLAGS'] = SCons.Util.CLVar('')
env['PROTOCPATHS'] = SCons.Util.CLVar('')
def exists(env):
return True

View File

@ -75,6 +75,7 @@ config_header_substs = (
('@mongo_config_use_libunwind@', 'MONGO_CONFIG_USE_LIBUNWIND'),
('@mongo_config_use_raw_latches@', 'MONGO_CONFIG_USE_RAW_LATCHES'),
('@mongo_config_wiredtiger_enabled@', 'MONGO_CONFIG_WIREDTIGER_ENABLED'),
('@mongo_config_grpc@', 'MONGO_CONFIG_GRPC'),
)

View File

@ -103,3 +103,6 @@
// Defined if WiredTiger storage engine is enabled
@mongo_config_wiredtiger_enabled@
// Defined if grpc support is enabled
@mongo_config_grpc@

View File

@ -20,6 +20,12 @@ variantSuffix = '-1.4.0'
thirdPartyEnvironmentModifications = {
'abseil-cpp': {'CPPPATH': ['#/src/third_party/abseil-cpp/dist'], },
'cares': {
'CPPPATH': [
'#src/third_party/cares/dist/include',
'#src/third_party/cares/platform/${TARGET_OS}_${TARGET_ARCH}/install/include'
],
},
'fmt': {'CPPPATH': ['#src/third_party/fmt/dist/include'], },
's2': {'CPPPATH': ['#src/third_party/s2'], },
'safeint': {
@ -219,6 +225,22 @@ if not use_system_version_of_library('libmongocrypt'):
],
}
if not use_system_version_of_library('protobuf'):
thirdPartyEnvironmentModifications['protobuf'] = {
'CPPPATH': ['#src/third_party/protobuf/dist/src'],
}
if not use_system_version_of_library('grpc'):
thirdPartyEnvironmentModifications['re2'] = {
'CPPPATH': ['#src/third_party/re2/dist'],
}
thirdPartyEnvironmentModifications['cares'] = {
'CPPPATH': [
'#src/third_party/cares/dist/include',
'#src/third_party/cares/platform/${TARGET_OS}_${TARGET_ARCH}/install/include'
],
}
if use_system_libunwind:
thirdPartyEnvironmentModifications['unwind'] = {
'SYSLIBDEPS_PRIVATE': [env['LIBDEPS_UNWIND_SYSLIBDEP'], env['LIBDEPS_LZMA_SYSLIBDEP']],
@ -258,10 +280,8 @@ empty_source = env.Textfile(
)
env.Alias('generated-sources', empty_source)
empty_object = env.LibraryObject(
target='third_party_shim',
source=empty_source,
)
empty_object = env.LibraryObject(target='third_party_shim', source=empty_source,
NINJA_GENSOURCE_INDEPENDENT=True)
def shim_library(env, name, **kwargs):
@ -330,7 +350,7 @@ if use_libunwind:
'unwind/unwind',
])
unwindEnv.ShimLibrary(name="unwind", )
unwindEnv.ShimLibrary(name="unwind", NINJA_GENSOURCE_INDEPENDENT=True)
fmtEnv = env.Clone()
if use_system_version_of_library("fmt"):
@ -399,9 +419,8 @@ abseilEnv = abseilEnv.Clone(LIBDEPS_INTERFACE=[
abseilDirectory + '/absl_city',
abseilDirectory + '/absl_hash',
abseilDirectory + '/absl_int128',
abseilDirectory + '/absl_low_level_hash',
abseilDirectory + '/absl_raw_hash_set',
abseilDirectory + '/absl_throw_delegate',
abseilDirectory + '/absl_wyhash',
])
abseilEnv.ShimLibrary(name="abseil")
@ -435,7 +454,7 @@ else:
'zlib/zlib',
])
zlibEnv.ShimLibrary(name="zlib", )
zlibEnv.ShimLibrary(name="zlib", NINJA_GENSOURCE_INDEPENDENT=True)
zstdEnv = env.Clone()
if use_system_version_of_library("zstd"):
@ -522,6 +541,7 @@ gperftoolsEnv.ShimLibrary(
# allowed to have public dependencies.
'lint-public-dep-allowed'
],
NINJA_GENSOURCE_INDEPENDENT=True,
)
stemmerEnv = env.Clone()
@ -659,3 +679,39 @@ else:
])
mcEnv.ShimLibrary(name="libmongocrypt", )
if env.TargetOSIs('linux') and env['ENABLE_GRPC_BUILD']:
protobufEnv = env.Clone(NINJA_GENSOURCE_INDEPENDENT=True)
if use_system_version_of_library("protobuf"):
protobufEnv = protobufEnv.Clone(
SYSLIBDEPS=[
env['LIBDEPS_PROTOBUF_SYSLIBDEP'],
env['LIBDEPS_PROTOC_SYSLIBDEP'],
], )
else:
protobufEnv.SConscript(dirs=[
'protobuf',
], duplicate=False, exports={'env': env})
protobufEnv = protobufEnv.Clone(LIBDEPS_INTERFACE=[
'protobuf/protoc',
])
protobufEnv.ShimLibrary(name="protobuf", )
grpcEnv = env.Clone()
if use_system_version_of_library("grpc"):
grpcEnv = grpcEnv.Clone(SYSLIBDEPS=[
env['LIBDEPS_GRPC_SYSLIBDEP'],
env['LIBDEPS_GRPCXX_SYSLIBDEP'],
env['LIBDEPS_GRPCXX_REFLECTION_SYSLIBDEP'],
])
else:
grpcEnv.SConscript(dirs=[
'cares',
're2',
'grpc',
], duplicate=False, exports={'env': env})
grpcEnv = grpcEnv.Clone(LIBDEPS_INTERFACE=[
'grpc/grpc++_reflection',
])
grpcEnv.ShimLibrary(name="grpc", )

View File

@ -31,6 +31,49 @@ if env.GetOption('sanitize') and 'undefined' in env.GetOption('sanitize').split(
],
)
# absl_spinlock_wait is an explicit dependency to the server build
env.Library(
target='absl_spinlock_wait',
source=[
'dist/absl/base/internal/spinlock_wait.cc',
],
LIBDEPS=[],
)
# absl_log_severity is an explicit dependency to the server build
env.Library(
target='absl_log_severity',
source=[
'dist/absl/base/log_severity.cc',
],
LIBDEPS=[],
)
# absl_raw_logging_internal is an explicit dependency to the server build
env.Library(
target='absl_raw_logging_internal',
source=[
'dist/absl/base/internal/raw_logging.cc',
],
LIBDEPS=[
'absl_log_severity',
],
)
# absl_malloc_internal is an explicit dependency to the server build
env.Library(
target='absl_malloc_internal',
source=[
'dist/absl/base/internal/low_level_alloc.cc',
],
LIBDEPS=[
'absl_base',
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
],
)
# absl_throw_delegate is an explicit dependency to the server build
env.Library(
target='absl_throw_delegate',
@ -43,6 +86,23 @@ env.Library(
],
)
# absl_base is an explicit dependency to the server build
env.Library(
target='absl_base',
source=[
'dist/absl/base/internal/cycleclock.cc',
'dist/absl/base/internal/spinlock.cc',
'dist/absl/base/internal/sysinfo.cc',
'dist/absl/base/internal/thread_identity.cc',
'dist/absl/base/internal/unscaledcycleclock.cc',
],
LIBDEPS=[
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
],
)
# absl_raw_hash_set is an explicit dependency to the server build
env.Library(
target='absl_raw_hash_set',
@ -74,7 +134,7 @@ env.Library(
],
)
# absl_hashtablez_sampler added as a dependency of other abseil libraries
# absl_hashtablez_sampler is an explicit dependency to the server build
env.Library(
target='absl_hashtablez_sampler',
source=[
@ -104,7 +164,7 @@ env.Library(
],
)
# absl_stacktrace added as a dependency of other abseil libraries
# absl_stacktrace is an explicit dependency to the server build
env.Library(
target='absl_stacktrace',
source=[
@ -117,7 +177,7 @@ env.Library(
],
)
# absl_symbolize added as a dependency of other abseil libraries
# absl_symbolize is an explicit dependency to the server build
env.Library(
target='absl_symbolize',
source=[
@ -138,7 +198,7 @@ env.Library(
],
)
# absl_demangle_internal added as a dependency of other abseil libraries
# absl_demangle_internal is an explicit dependency to the server build
env.Library(
target='absl_demangle_internal',
source=[
@ -152,7 +212,7 @@ env.Library(
],
)
# absl_debugging_internal added as a dependency of other abseil libraries
# absl_debugging_internal is an explicit dependency to the server build
env.Library(
target='absl_debugging_internal',
source=[
@ -166,11 +226,11 @@ env.Library(
],
)
# absl_wyhash is an explicit dependency to the server build
# absl_low_level_hash is an explicit dependency to the server build
env.Library(
target='absl_wyhash',
target='absl_low_level_hash',
source=[
'dist/absl/hash/internal/wyhash.cc',
'dist/absl/hash/internal/low_level_hash.cc',
],
LIBDEPS=[
'absl_base',
@ -208,12 +268,12 @@ env.Library(
'absl_city',
'absl_int128',
'absl_log_severity',
'absl_low_level_hash',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_strings',
'absl_strings_internal',
'absl_throw_delegate',
'absl_wyhash',
],
)
@ -226,23 +286,248 @@ env.Library(
LIBDEPS=[],
)
# absl_strings_internal added as a dependency of other abseil libraries
# absl_exponential_biased is an explicit dependency to the server build
env.Library(
target='absl_strings_internal',
target='absl_exponential_biased',
source=[
'dist/absl/strings/internal/ostringstream.cc',
'dist/absl/strings/internal/utf8.cc',
'dist/absl/strings/internal/escaping.cc',
'dist/absl/profiling/internal/exponential_biased.cc',
],
LIBDEPS=[],
)
# absl_random_internal_randen_hwaes is an explicit dependency to the server build
env.Library(
target='absl_random_internal_randen_hwaes',
source=[
'dist/absl/random/internal/randen_detect.cc',
],
LIBDEPS=[
'absl_base',
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_random_internal_platform',
'absl_random_internal_randen_hwaes_impl',
],
)
# absl_strings added as a dependency of other abseil libraries
# absl_random_internal_seed_material is an explicit dependency to the server build
env.Library(
target='absl_random_internal_seed_material',
source=[
'dist/absl/random/internal/seed_material.cc',
],
LIBDEPS=[
'absl_bad_optional_access',
'absl_base',
'absl_int128',
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_strings',
'absl_strings_internal',
'absl_throw_delegate',
],
)
# absl_random_internal_randen_slow is an explicit dependency to the server build
env.Library(
target='absl_random_internal_randen_slow',
source=[
'dist/absl/random/internal/randen_slow.cc',
],
LIBDEPS=[
'absl_random_internal_platform',
],
)
# absl_random_seed_gen_exception is an explicit dependency to the server build
env.Library(
target='absl_random_seed_gen_exception',
source=[
'dist/absl/random/seed_gen_exception.cc',
],
LIBDEPS=[],
)
# absl_random_internal_pool_urbg is an explicit dependency to the server build
env.Library(
target='absl_random_internal_pool_urbg',
source=[
'dist/absl/random/internal/pool_urbg.cc',
],
LIBDEPS=[
'absl_bad_optional_access',
'absl_base',
'absl_int128',
'absl_log_severity',
'absl_random_internal_platform',
'absl_random_internal_randen',
'absl_random_internal_randen_hwaes',
'absl_random_internal_randen_hwaes_impl',
'absl_random_internal_randen_slow',
'absl_random_internal_seed_material',
'absl_random_seed_gen_exception',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_strings',
'absl_strings_internal',
'absl_throw_delegate',
],
)
# absl_random_distributions is an explicit dependency to the server build
env.Library(
target='absl_random_distributions',
source=[
'dist/absl/random/discrete_distribution.cc',
'dist/absl/random/gaussian_distribution.cc',
],
LIBDEPS=[
'absl_base',
'absl_int128',
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_strings',
'absl_strings_internal',
'absl_throw_delegate',
],
)
# absl_random_internal_randen_hwaes_impl is an explicit dependency to the server build
env.Library(
target='absl_random_internal_randen_hwaes_impl',
source=[
'dist/absl/random/internal/randen_hwaes.cc',
],
LIBDEPS=[
'absl_random_internal_platform',
],
)
# absl_random_seed_sequences is an explicit dependency to the server build
env.Library(
target='absl_random_seed_sequences',
source=[
'dist/absl/random/seed_sequences.cc',
],
LIBDEPS=[
'absl_bad_optional_access',
'absl_base',
'absl_int128',
'absl_log_severity',
'absl_random_internal_platform',
'absl_random_internal_pool_urbg',
'absl_random_internal_randen',
'absl_random_internal_randen_hwaes',
'absl_random_internal_randen_hwaes_impl',
'absl_random_internal_randen_slow',
'absl_random_internal_seed_material',
'absl_random_seed_gen_exception',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_strings',
'absl_strings_internal',
'absl_throw_delegate',
],
)
# absl_random_internal_randen is an explicit dependency to the server build
env.Library(
target='absl_random_internal_randen',
source=[
'dist/absl/random/internal/randen.cc',
],
LIBDEPS=[
'absl_random_internal_platform',
'absl_random_internal_randen_hwaes',
'absl_random_internal_randen_hwaes_impl',
'absl_random_internal_randen_slow',
],
)
# absl_random_internal_platform is an explicit dependency to the server build
env.Library(
target='absl_random_internal_platform',
source=[
'dist/absl/random/internal/randen_round_keys.cc',
],
LIBDEPS=[],
)
# absl_statusor is an explicit dependency to the server build
env.Library(
target='absl_statusor',
source=[
'dist/absl/status/statusor.cc',
],
LIBDEPS=[
'absl_bad_optional_access',
'absl_bad_variant_access',
'absl_base',
'absl_civil_time',
'absl_cord',
'absl_cord_internal',
'absl_cordz_functions',
'absl_cordz_handle',
'absl_cordz_info',
'absl_debugging_internal',
'absl_demangle_internal',
'absl_exponential_biased',
'absl_graphcycles_internal',
'absl_int128',
'absl_log_severity',
'absl_malloc_internal',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_stacktrace',
'absl_status',
'absl_str_format_internal',
'absl_strings',
'absl_strings_internal',
'absl_symbolize',
'absl_synchronization',
'absl_throw_delegate',
'absl_time',
'absl_time_zone',
],
)
# absl_status is an explicit dependency to the server build
env.Library(
target='absl_status',
source=[
'dist/absl/status/status.cc',
'dist/absl/status/status_payload_printer.cc',
],
LIBDEPS=[
'absl_bad_optional_access',
'absl_base',
'absl_civil_time',
'absl_cord',
'absl_cord_internal',
'absl_cordz_functions',
'absl_cordz_handle',
'absl_cordz_info',
'absl_debugging_internal',
'absl_demangle_internal',
'absl_exponential_biased',
'absl_graphcycles_internal',
'absl_int128',
'absl_log_severity',
'absl_malloc_internal',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_stacktrace',
'absl_str_format_internal',
'absl_strings',
'absl_strings_internal',
'absl_symbolize',
'absl_synchronization',
'absl_throw_delegate',
'absl_time',
'absl_time_zone',
],
)
# absl_strings is an explicit dependency to the server build
env.Library(
target='absl_strings',
source=[
@ -271,7 +556,176 @@ env.Library(
],
)
# absl_synchronization added as a dependency of other abseil libraries
# absl_strings_internal is an explicit dependency to the server build
env.Library(
target='absl_strings_internal',
source=[
'dist/absl/strings/internal/ostringstream.cc',
'dist/absl/strings/internal/utf8.cc',
'dist/absl/strings/internal/escaping.cc',
],
LIBDEPS=[
'absl_base',
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
],
)
# absl_cordz_functions is an explicit dependency to the server build
env.Library(
target='absl_cordz_functions',
source=[
'dist/absl/strings/internal/cordz_functions.cc',
],
LIBDEPS=[
'absl_exponential_biased',
'absl_log_severity',
'absl_raw_logging_internal',
],
)
# absl_cordz_info is an explicit dependency to the server build
env.Library(
target='absl_cordz_info',
source=[
'dist/absl/strings/internal/cordz_info.cc',
],
LIBDEPS=[
'absl_base',
'absl_civil_time',
'absl_cord_internal',
'absl_cordz_functions',
'absl_cordz_handle',
'absl_debugging_internal',
'absl_demangle_internal',
'absl_exponential_biased',
'absl_graphcycles_internal',
'absl_int128',
'absl_log_severity',
'absl_malloc_internal',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_stacktrace',
'absl_strings',
'absl_strings_internal',
'absl_symbolize',
'absl_synchronization',
'absl_throw_delegate',
'absl_time',
'absl_time_zone',
],
)
# absl_cord_internal is an explicit dependency to the server build
env.Library(
target='absl_cord_internal',
source=[
'dist/absl/strings/internal/cord_internal.cc',
'dist/absl/strings/internal/cord_rep_btree.cc',
'dist/absl/strings/internal/cord_rep_btree_navigator.cc',
'dist/absl/strings/internal/cord_rep_btree_reader.cc',
'dist/absl/strings/internal/cord_rep_consume.cc',
'dist/absl/strings/internal/cord_rep_ring.cc',
],
LIBDEPS=[
'absl_base',
'absl_int128',
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_strings',
'absl_strings_internal',
'absl_throw_delegate',
],
)
# absl_str_format_internal is an explicit dependency to the server build
env.Library(
target='absl_str_format_internal',
source=[
'dist/absl/strings/internal/str_format/arg.cc',
'dist/absl/strings/internal/str_format/bind.cc',
'dist/absl/strings/internal/str_format/extension.cc',
'dist/absl/strings/internal/str_format/float_conversion.cc',
'dist/absl/strings/internal/str_format/output.cc',
'dist/absl/strings/internal/str_format/parser.cc',
],
LIBDEPS=[
'absl_base',
'absl_int128',
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_strings',
'absl_strings_internal',
'absl_throw_delegate',
],
)
# absl_cordz_handle is an explicit dependency to the server build
env.Library(
target='absl_cordz_handle',
source=[
'dist/absl/strings/internal/cordz_handle.cc',
],
LIBDEPS=[
'absl_base',
'absl_civil_time',
'absl_debugging_internal',
'absl_demangle_internal',
'absl_graphcycles_internal',
'absl_int128',
'absl_log_severity',
'absl_malloc_internal',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_stacktrace',
'absl_strings',
'absl_strings_internal',
'absl_symbolize',
'absl_synchronization',
'absl_throw_delegate',
'absl_time',
'absl_time_zone',
],
)
# absl_cord is an explicit dependency to the server build
env.Library(
target='absl_cord',
source=[
'dist/absl/strings/cord.cc',
],
LIBDEPS=[
'absl_bad_optional_access',
'absl_base',
'absl_civil_time',
'absl_cord_internal',
'absl_cordz_functions',
'absl_cordz_handle',
'absl_cordz_info',
'absl_debugging_internal',
'absl_demangle_internal',
'absl_exponential_biased',
'absl_graphcycles_internal',
'absl_int128',
'absl_log_severity',
'absl_malloc_internal',
'absl_raw_logging_internal',
'absl_spinlock_wait',
'absl_stacktrace',
'absl_strings',
'absl_strings_internal',
'absl_symbolize',
'absl_synchronization',
'absl_throw_delegate',
'absl_time',
'absl_time_zone',
],
)
# absl_synchronization is an explicit dependency to the server build
env.Library(
target='absl_synchronization',
source=[
@ -304,7 +758,7 @@ env.Library(
],
)
# absl_graphcycles_internal added as a dependency of other abseil libraries
# absl_graphcycles_internal is an explicit dependency to the server build
env.Library(
target='absl_graphcycles_internal',
source=[
@ -319,7 +773,7 @@ env.Library(
],
)
# absl_time_zone added as a dependency of other abseil libraries
# absl_time_zone is an explicit dependency to the server build
env.Library(
target='absl_time_zone',
source=[
@ -336,7 +790,7 @@ env.Library(
LIBDEPS=[],
)
# absl_civil_time added as a dependency of other abseil libraries
# absl_civil_time is an explicit dependency to the server build
env.Library(
target='absl_civil_time',
source=[
@ -345,7 +799,7 @@ env.Library(
LIBDEPS=[],
)
# absl_time added as a dependency of other abseil libraries
# absl_time is an explicit dependency to the server build
env.Library(
target='absl_time',
source=[
@ -369,7 +823,7 @@ env.Library(
],
)
# absl_bad_optional_access added as a dependency of other abseil libraries
# absl_bad_optional_access is an explicit dependency to the server build
env.Library(
target='absl_bad_optional_access',
source=[
@ -381,7 +835,7 @@ env.Library(
],
)
# absl_bad_variant_access added as a dependency of other abseil libraries
# absl_bad_variant_access is an explicit dependency to the server build
env.Library(
target='absl_bad_variant_access',
source=[
@ -392,72 +846,3 @@ env.Library(
'absl_raw_logging_internal',
],
)
# absl_spinlock_wait added as a dependency of other abseil libraries
env.Library(
target='absl_spinlock_wait',
source=[
'dist/absl/base/internal/spinlock_wait.cc',
],
LIBDEPS=[],
)
# absl_log_severity added as a dependency of other abseil libraries
env.Library(
target='absl_log_severity',
source=[
'dist/absl/base/log_severity.cc',
],
LIBDEPS=[],
)
# absl_raw_logging_internal added as a dependency of other abseil libraries
env.Library(
target='absl_raw_logging_internal',
source=[
'dist/absl/base/internal/raw_logging.cc',
],
LIBDEPS=[
'absl_log_severity',
],
)
# absl_exponential_biased added as a dependency of other abseil libraries
env.Library(
target='absl_exponential_biased',
source=[
'dist/absl/base/internal/exponential_biased.cc',
],
LIBDEPS=[],
)
# absl_malloc_internal added as a dependency of other abseil libraries
env.Library(
target='absl_malloc_internal',
source=[
'dist/absl/base/internal/low_level_alloc.cc',
],
LIBDEPS=[
'absl_base',
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
],
)
# absl_base added as a dependency of other abseil libraries
env.Library(
target='absl_base',
source=[
'dist/absl/base/internal/cycleclock.cc',
'dist/absl/base/internal/spinlock.cc',
'dist/absl/base/internal/sysinfo.cc',
'dist/absl/base/internal/thread_identity.cc',
'dist/absl/base/internal/unscaledcycleclock.cc',
],
LIBDEPS=[
'absl_log_severity',
'absl_raw_logging_internal',
'absl_spinlock_wait',
],
)

View File

@ -17,8 +17,6 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/dynamic_annotations.h"
"base/internal/endian.h"
"base/internal/errno_saver.h"
"base/internal/exponential_biased.cc"
"base/internal/exponential_biased.h"
"base/internal/fast_type_id.h"
"base/internal/hide_ptr.h"
"base/internal/identity.h"
@ -28,8 +26,6 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/low_level_alloc.h"
"base/internal/low_level_scheduling.h"
"base/internal/per_thread_tls.h"
"base/internal/periodic_sampler.cc"
"base/internal/periodic_sampler.h"
"base/internal/pretty_function.h"
"base/internal/raw_logging.cc"
"base/internal/raw_logging.h"
@ -124,8 +120,8 @@ set(ABSL_INTERNAL_DLL_FILES
"hash/internal/hash.h"
"hash/internal/hash.cc"
"hash/internal/spy_hash_state.h"
"hash/internal/wyhash.h"
"hash/internal/wyhash.cc"
"hash/internal/low_level_hash.h"
"hash/internal/low_level_hash.cc"
"memory/memory.h"
"meta/type_traits.h"
"numeric/bits.h"
@ -133,6 +129,11 @@ set(ABSL_INTERNAL_DLL_FILES
"numeric/int128.h"
"numeric/internal/bits.h"
"numeric/internal/representation.h"
"profiling/internal/exponential_biased.cc"
"profiling/internal/exponential_biased.h"
"profiling/internal/periodic_sampler.cc"
"profiling/internal/periodic_sampler.h"
"profiling/internal/sample_recorder.h"
"random/bernoulli_distribution.h"
"random/beta_distribution.h"
"random/bit_gen_ref.h"
@ -197,16 +198,35 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/cord.h"
"strings/escaping.cc"
"strings/escaping.h"
"strings/internal/cord_internal.cc"
"strings/internal/cord_internal.h"
"strings/internal/cord_rep_flat.h"
"strings/internal/cord_rep_ring.cc"
"strings/internal/cord_rep_ring.h"
"strings/internal/cord_rep_ring_reader.h"
"strings/internal/charconv_bigint.cc"
"strings/internal/charconv_bigint.h"
"strings/internal/charconv_parse.cc"
"strings/internal/charconv_parse.h"
"strings/internal/cord_internal.cc"
"strings/internal/cord_internal.h"
"strings/internal/cord_rep_consume.h"
"strings/internal/cord_rep_consume.cc"
"strings/internal/cord_rep_btree.cc"
"strings/internal/cord_rep_btree.h"
"strings/internal/cord_rep_btree_navigator.cc"
"strings/internal/cord_rep_btree_navigator.h"
"strings/internal/cord_rep_btree_reader.cc"
"strings/internal/cord_rep_btree_reader.h"
"strings/internal/cord_rep_flat.h"
"strings/internal/cord_rep_ring.cc"
"strings/internal/cord_rep_ring.h"
"strings/internal/cord_rep_ring_reader.h"
"strings/internal/cordz_functions.cc"
"strings/internal/cordz_functions.h"
"strings/internal/cordz_handle.cc"
"strings/internal/cordz_handle.h"
"strings/internal/cordz_info.cc"
"strings/internal/cordz_info.h"
"strings/internal/cordz_sample_token.cc"
"strings/internal/cordz_sample_token.h"
"strings/internal/cordz_statistics.h"
"strings/internal/cordz_update_scope.h"
"strings/internal/cordz_update_tracker.h"
"strings/internal/stl_type_traits.h"
"strings/internal/string_constant.h"
"strings/match.cc"
@ -438,6 +458,7 @@ set(ABSL_INTERNAL_DLL_TARGETS
"raw_hash_set"
"layout"
"tracked"
"sample_recorder"
)
function(absl_internal_dll_contains)

View File

@ -141,7 +141,8 @@ function(absl_cc_library)
endif()
# Generate a pkg-config file for every library:
if(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
if((_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
AND ABSL_ENABLE_INSTALL)
if(NOT ABSL_CC_LIB_TESTONLY)
if(absl_VERSION)
set(PC_VERSION "${absl_VERSION}")
@ -170,8 +171,8 @@ function(absl_cc_library)
FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\
prefix=${CMAKE_INSTALL_PREFIX}\n\
exec_prefix=\${prefix}\n\
libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n\
includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}\n\
libdir=${CMAKE_INSTALL_FULL_LIBDIR}\n\
includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}\n\
\n\
Name: absl_${_NAME}\n\
Description: Abseil ${_NAME} library\n\
@ -253,9 +254,23 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/internal)
endif()
# INTERFACE libraries can't have the CXX_STANDARD property set
set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
if(ABSL_PROPAGATE_CXX_STD)
# Abseil libraries require C++11 as the current minimum standard.
# Top-level application CMake projects should ensure a consistent C++
# standard for all compiled sources by setting CMAKE_CXX_STANDARD.
target_compile_features(${_NAME} PUBLIC cxx_std_11)
else()
# Note: This is legacy (before CMake 3.8) behavior. Setting the
# target-level CXX_STANDARD property to ABSL_CXX_STANDARD (which is
# initialized by CMAKE_CXX_STANDARD) should have no real effect, since
# that is the default value anyway.
#
# CXX_STANDARD_REQUIRED does guard against the top-level CMake project
# not having enabled CMAKE_CXX_STANDARD_REQUIRED (which prevents
# "decaying" to an older standard if the requested one isn't available).
set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
endif()
# When being installed, we lose the absl_ prefix. We want to put it back
# to have properly named lib files. This is a no-op when we are not being
@ -263,7 +278,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
if(ABSL_ENABLE_INSTALL)
set_target_properties(${_NAME} PROPERTIES
OUTPUT_NAME "absl_${_NAME}"
SOVERSION "2103.0.1"
SOVERSION "2111.0.0"
)
endif()
else()
@ -286,6 +301,16 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
${ABSL_DEFAULT_LINKOPTS}
)
target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES})
if(ABSL_PROPAGATE_CXX_STD)
# Abseil libraries require C++11 as the current minimum standard.
# Top-level application CMake projects should ensure a consistent C++
# standard for all compiled sources by setting CMAKE_CXX_STANDARD.
target_compile_features(${_NAME} INTERFACE cxx_std_11)
# (INTERFACE libraries can't have the CXX_STANDARD property set, so there
# is no legacy behavior else case).
endif()
endif()
# TODO currently we don't install googletest alongside abseil sources, so
@ -335,8 +360,8 @@ endfunction()
# "awesome_test.cc"
# DEPS
# absl::awesome
# gmock
# gtest_main
# GTest::gmock
# GTest::gtest_main
# )
function(absl_cc_test)
if(NOT BUILD_TESTING)
@ -389,8 +414,23 @@ function(absl_cc_test)
# Add all Abseil targets to a folder in the IDE for organization.
set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test)
set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
if(ABSL_PROPAGATE_CXX_STD)
# Abseil libraries require C++11 as the current minimum standard.
# Top-level application CMake projects should ensure a consistent C++
# standard for all compiled sources by setting CMAKE_CXX_STANDARD.
target_compile_features(${_NAME} PUBLIC cxx_std_11)
else()
# Note: This is legacy (before CMake 3.8) behavior. Setting the
# target-level CXX_STANDARD property to ABSL_CXX_STANDARD (which is
# initialized by CMAKE_CXX_STANDARD) should have no real effect, since
# that is the default value anyway.
#
# CXX_STANDARD_REQUIRED does guard against the top-level CMake project
# not having enabled CMAKE_CXX_STANDARD_REQUIRED (which prevents
# "decaying" to an older standard if the requested one isn't available).
set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
endif()
add_test(NAME ${_NAME} COMMAND ${_NAME})
endfunction()

View File

@ -34,15 +34,16 @@ to include Abseil directly in your CMake project.
4. Add the **absl::** target you wish to use to the
[`target_link_libraries()`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html)
section of your executable or of your library.<br>
Here is a short CMakeLists.txt example of a project file using Abseil.
Here is a short CMakeLists.txt example of an application project using Abseil.
```cmake
cmake_minimum_required(VERSION 3.5)
project(my_project)
cmake_minimum_required(VERSION 3.8.2)
project(my_app_project)
# Pick the C++ standard to compile with.
# Abseil currently supports C++11, C++14, and C++17.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(abseil-cpp)
@ -50,6 +51,44 @@ add_executable(my_exe source.cpp)
target_link_libraries(my_exe absl::base absl::synchronization absl::strings)
```
Note that if you are developing a library designed for use by other clients, you
should instead leave `CMAKE_CXX_STANDARD` unset (or only set if being built as
the current top-level CMake project) and configure the minimum required C++
standard at the target level. If you require a later minimum C++ standard than
Abseil does, it's a good idea to also enforce that `CMAKE_CXX_STANDARD` (which
will control Abseil library targets) is set to at least that minimum. For
example:
```cmake
cmake_minimum_required(VERSION 3.8.2)
project(my_lib_project)
# Leave C++ standard up to the root application, so set it only if this is the
# current top-level CMake project.
if(CMAKE_SOURCE_DIR STREQUAL my_lib_project_SOURCE_DIR)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
add_subdirectory(abseil-cpp)
add_library(my_lib source.cpp)
target_link_libraries(my_lib absl::base absl::synchronization absl::strings)
# Enforce that my_lib requires C++17. Important to document for clients that they
# must set CMAKE_CXX_STANDARD to 17 or higher for proper Abseil ABI compatibility
# (since otherwise, Abseil library targets could be compiled with a lower C++
# standard than my_lib).
target_compile_features(my_lib PUBLIC cxx_std_17)
if(CMAKE_CXX_STANDARD LESS 17)
message(FATAL_ERROR
"my_lib_project requires CMAKE_CXX_STANDARD >= 17 (got: ${CMAKE_CXX_STANDARD})")
endif()
```
Then the top-level application project that uses your library is responsible for
setting a consistent `CMAKE_CXX_STANDARD` that is sufficiently high.
### Running Abseil Tests with CMake
Use the `-DBUILD_TESTING=ON` flag to run Abseil tests.
@ -99,3 +138,48 @@ absl::synchronization
absl::time
absl::utility
```
## Traditional CMake Set-Up
For larger projects, it may make sense to use the traditional CMake set-up where you build and install projects separately.
First, you'd need to build and install Google Test:
```
cmake -S /source/googletest -B /build/googletest -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/installation/dir -DBUILD_GMOCK=ON
cmake --build /build/googletest --target install
```
Then you need to configure and build Abseil. Make sure you enable `ABSL_USE_EXTERNAL_GOOGLETEST` and `ABSL_FIND_GOOGLETEST`. You also need to enable `ABSL_ENABLE_INSTALL` so that you can install Abseil itself.
```
cmake -S /source/abseil-cpp -B /build/abseil-cpp -DCMAKE_PREFIX_PATH=/installation/dir -DCMAKE_INSTALL_PREFIX=/installation/dir -DABSL_ENABLE_INSTALL=ON -DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON
cmake --build /temporary/build/abseil-cpp
```
(`CMAKE_PREFIX_PATH` is where you already have Google Test installed; `CMAKE_INSTALL_PREFIX` is where you want to have Abseil installed; they can be different.)
Run the tests:
```
ctest --test-dir /temporary/build/abseil-cpp
```
And finally install:
```
cmake --build /temporary/build/abseil-cpp --target install
```
# CMake Option Synposis
## Enable Standard CMake Installation
`-DABSL_ENABLE_INSTALL=ON`
## Google Test Options
`-DBUILD_TESTING=ON` must be set to enable testing
- Have Abseil download and build Google Test for you: `-DABSL_USE_EXTERNAL_GOOGLETEST=OFF` (default)
- Download and build latest Google Test: `-DABSL_USE_GOOGLETEST_HEAD=ON`
- Download specific Google Test version (ZIP archive): `-DABSL_GOOGLETEST_DOWNLOAD_URL=https://.../version.zip`
- Use Google Test from specific local directory: `-DABSL_LOCAL_GOOGLETEST_DIR=/path/to/googletest`
- Use Google Test included elsewhere in your project: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON`
- Use standard CMake `find_package(CTest)` to find installed Google Test: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON`

View File

@ -18,10 +18,8 @@
cmake_minimum_required(VERSION 3.5)
project(absl_cmake_testing CXX)
set(CMAKE_CXX_STANDARD 11)
add_executable(simple simple.cc)
find_package(absl REQUIRED)
target_link_libraries(simple absl::strings)
target_link_libraries(simple absl::strings absl::config)

View File

@ -14,8 +14,17 @@
// limitations under the License.
#include <iostream>
#include "absl/base/config.h"
#include "absl/strings/substitute.h"
#if !defined(ABSL_LTS_RELEASE_VERSION) || ABSL_LTS_RELEASE_VERSION != 99998877
#error ABSL_LTS_RELEASE_VERSION is not set correctly.
#endif
#if !defined(ABSL_LTS_RELEASE_PATCH_LEVEL) || ABSL_LTS_RELEASE_PATCH_LEVEL != 0
#error ABSL_LTS_RELEASE_PATCH_LEVEL is not set correctly.
#endif
int main(int argc, char** argv) {
for (int i = 0; i < argc; ++i) {
std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]);

View File

@ -19,10 +19,9 @@
# Fail on any error. Treat unset variables an error. Print commands as executed.
set -euox pipefail
source ci/cmake_common.sh
absl_dir=/abseil-cpp
absl_build_dir=/buildfs
googletest_builddir=/googletest_builddir
project_dir="${absl_dir}"/CMake/install_test_project
project_build_dir=/buildfs/project-build
@ -31,13 +30,30 @@ if [ "${LINK_TYPE:-}" = "DYNAMIC" ]; then
build_shared_libs="ON"
fi
# Build and install GoogleTest
mkdir "${googletest_builddir}"
pushd "${googletest_builddir}"
curl -L "${ABSL_GOOGLETEST_DOWNLOAD_URL}" --output "${ABSL_GOOGLETEST_COMMIT}".zip
unzip "${ABSL_GOOGLETEST_COMMIT}".zip
pushd "googletest-${ABSL_GOOGLETEST_COMMIT}"
mkdir build
pushd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS="${build_shared_libs}" ..
make -j $(nproc)
make install
ldconfig
popd
popd
popd
# Run the LTS transformations
./create_lts.py 99998877
# Install Abseil
# Build and install Abseil
pushd "${absl_build_dir}"
cmake "${absl_dir}" \
-DABSL_GOOGLETEST_DOWNLOAD_URL="${ABSL_GOOGLETEST_DOWNLOAD_URL}" \
-DABSL_USE_EXTERNAL_GOOGLETEST=ON \
-DABSL_FIND_GOOGLETEST=ON \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=ON \
-DBUILD_SHARED_LIBS="${build_shared_libs}"

View File

@ -41,11 +41,16 @@ if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif (POLICY CMP0077)
# Allow the user to specify the MSVC runtime
if (POLICY CMP0091)
cmake_policy(SET CMP0091 NEW)
endif (POLICY CMP0091)
# Set BUILD_TESTING to OFF by default.
# This must come before the project() and include(CTest) lines.
OPTION(BUILD_TESTING "Build tests" OFF)
project(absl LANGUAGES CXX VERSION 20210324)
project(absl LANGUAGES CXX VERSION 20211102)
include(CTest)
# Output directory is correct by default for most build setups. However, when
@ -62,6 +67,13 @@ else()
option(ABSL_ENABLE_INSTALL "Enable install rule" ON)
endif()
option(ABSL_PROPAGATE_CXX_STD
"Use CMake C++ standard meta features (e.g. cxx_std_11) that propagate to targets that link to Abseil"
OFF) # TODO: Default to ON for CMake 3.8 and greater.
if((${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.8) AND (NOT ABSL_PROPAGATE_CXX_STD))
message(WARNING "A future Abseil release will default ABSL_PROPAGATE_CXX_STD to ON for CMake 3.8 and up. We recommend enabling this option to ensure your project still builds correctly.")
endif()
list(APPEND CMAKE_MODULE_PATH
${CMAKE_CURRENT_LIST_DIR}/CMake
${CMAKE_CURRENT_LIST_DIR}/absl/copts
@ -97,9 +109,18 @@ endif()
## pthread
find_package(Threads REQUIRED)
include(CMakeDependentOption)
option(ABSL_USE_EXTERNAL_GOOGLETEST
"If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subproject." OFF)
cmake_dependent_option(ABSL_FIND_GOOGLETEST
"If ON, Abseil will use find_package(GTest) rather than assuming that GoogleTest is already provided by the including project."
ON
"ABSL_USE_EXTERNAL_GOOGLETEST"
OFF)
option(ABSL_USE_GOOGLETEST_HEAD
"If ON, abseil will download HEAD from GoogleTest at config time." OFF)
@ -111,7 +132,15 @@ set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH
if(BUILD_TESTING)
## check targets
if (NOT ABSL_USE_EXTERNAL_GOOGLETEST)
if (ABSL_USE_EXTERNAL_GOOGLETEST)
if (ABSL_FIND_GOOGLETEST)
find_package(GTest REQUIRED)
else()
if (NOT TARGET gtest AND NOT TARGET GTest::gtest)
message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.")
endif()
endif()
else()
set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build)
if(ABSL_USE_GOOGLETEST_HEAD AND ABSL_GOOGLETEST_DOWNLOAD_URL)
message(FATAL_ERROR "Do not set both ABSL_USE_GOOGLETEST_HEAD and ABSL_GOOGLETEST_DOWNLOAD_URL")
@ -129,16 +158,18 @@ if(BUILD_TESTING)
include(CMake/Googletest/DownloadGTest.cmake)
endif()
check_target(gtest)
check_target(gtest_main)
check_target(gmock)
if (NOT ABSL_FIND_GOOGLETEST)
# When Google Test is included directly rather than through find_package, the aliases are missing.
add_library(GTest::gtest ALIAS gtest)
add_library(GTest::gtest_main ALIAS gtest_main)
add_library(GTest::gmock ALIAS gmock)
add_library(GTest::gmock_main ALIAS gmock_main)
endif()
list(APPEND ABSL_TEST_COMMON_LIBRARIES
gtest_main
gtest
gmock
${CMAKE_THREAD_LIBS_INIT}
)
check_target(GTest::gtest)
check_target(GTest::gtest_main)
check_target(GTest::gmock)
check_target(GTest::gmock_main)
endif()
add_subdirectory(absl)

View File

@ -27,7 +27,10 @@ compiler, there several ways to do this:
file](https://docs.bazel.build/versions/master/guide.html#bazelrc)
If you are using CMake as the build system, you'll need to add a line like
`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. See the
`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you
are developing a library designed to be used by other clients, you should
instead leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard
required by each of your library targets via `target_compile_features`. See the
[CMake build
instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md)
for more information.

View File

@ -92,6 +92,9 @@ Abseil contains the following C++ library components:
available within C++14 and C++17 versions of the C++ `<type_traits>` library.
* [`numeric`](absl/numeric/)
<br /> The `numeric` library contains C++11-compatible 128-bit integers.
* [`profiling`](absl/profiling/)
<br /> The `profiling` library contains utility code for profiling C++
entities. It is currently a private dependency of other Abseil libraries.
* [`status`](absl/status/)
<br /> The `status` contains abstractions for error handling, specifically
`absl::Status` and `absl::StatusOr<T>`.

View File

@ -15,31 +15,30 @@
#
workspace(name = "com_google_absl")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# GoogleTest/GoogleMock framework. Used by most unit-tests.
http_archive(
name = "com_google_googletest",
name = "com_google_googletest", # 2021-07-09T13:28:13Z
sha256 = "12ef65654dc01ab40f6f33f9d02c04f2097d2cd9fbe48dc6001b29543583b0ad",
strip_prefix = "googletest-8d51ffdfab10b3fba636ae69bc03da4b54f8c235",
# Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh.
urls = ["https://github.com/google/googletest/archive/8567b09290fe402cf01923e2131c5635b8ed851b.zip"], # 2020-06-12T22:24:28Z
strip_prefix = "googletest-8567b09290fe402cf01923e2131c5635b8ed851b",
sha256 = "9a8a166eb6a56c7b3d7b19dc2c946fe4778fd6f21c7a12368ad3b836d8f1be48",
urls = ["https://github.com/google/googletest/archive/8d51ffdfab10b3fba636ae69bc03da4b54f8c235.zip"],
)
# Google benchmark.
http_archive(
name = "com_github_google_benchmark",
urls = ["https://github.com/google/benchmark/archive/bf585a2789e30585b4e3ce6baf11ef2750b54677.zip"], # 2020-11-26T11:14:03Z
strip_prefix = "benchmark-bf585a2789e30585b4e3ce6baf11ef2750b54677",
sha256 = "2a778d821997df7d8646c9c59b8edb9a573a6e04c534c01892a40aa524a7b68c",
name = "com_github_google_benchmark", # 2021-09-20T09:19:51Z
sha256 = "62e2f2e6d8a744d67e4bbc212fcfd06647080de4253c97ad5c6749e09faf2cb0",
strip_prefix = "benchmark-0baacde3618ca617da95375e0af13ce1baadea47",
urls = ["https://github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.zip"],
)
# C++ rules for Bazel.
# Bazel platform rules.
http_archive(
name = "rules_cc",
sha256 = "9a446e9dd9c1bb180c86977a8dc1e9e659550ae732ae58bd2e8fd51e15b2c91d",
strip_prefix = "rules_cc-262ebec3c2296296526740db4aefce68c80de7fa",
urls = [
"https://github.com/bazelbuild/rules_cc/archive/262ebec3c2296296526740db4aefce68c80de7fa.zip",
],
name = "platforms",
sha256 = "b601beaf841244de5c5a50d2b2eddd34839788000fa1be4260ce6603ca0d8eb7",
strip_prefix = "platforms-98939346da932eef0b54cf808622f5bb0928f00b",
urls = ["https://github.com/bazelbuild/platforms/archive/98939346da932eef0b54cf808622f5bb0928f00b.zip"],
)

View File

@ -44,14 +44,14 @@ config_setting(
config_setting(
name = "osx",
constraint_values = [
"@bazel_tools//platforms:osx",
"@platforms//os:osx",
],
)
config_setting(
name = "ios",
constraint_values = [
"@bazel_tools//platforms:ios",
"@platforms//os:ios",
],
)
@ -70,3 +70,11 @@ config_setting(
},
visibility = [":__subpackages__"],
)
config_setting(
name = "fuchsia",
values = {
"cpu": "fuchsia",
},
visibility = [":__subpackages__"],
)

View File

@ -25,6 +25,7 @@ add_subdirectory(hash)
add_subdirectory(memory)
add_subdirectory(meta)
add_subdirectory(numeric)
add_subdirectory(profiling)
add_subdirectory(random)
add_subdirectory(status)
add_subdirectory(strings)

View File

@ -14,7 +14,6 @@
# limitations under the License.
#
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",

View File

@ -35,7 +35,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::algorithm
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -65,5 +65,5 @@ absl_cc_test(
absl::core_headers
absl::memory
absl::span
gmock_main
GTest::gmock_main
)

View File

@ -905,11 +905,11 @@ void c_sort(C& c) {
// Overload of c_sort() for performing a `comp` comparison other than the
// default `operator<`.
template <typename C, typename Compare>
void c_sort(C& c, Compare&& comp) {
template <typename C, typename LessThan>
void c_sort(C& c, LessThan&& comp) {
std::sort(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_stable_sort()
@ -925,11 +925,11 @@ void c_stable_sort(C& c) {
// Overload of c_stable_sort() for performing a `comp` comparison other than the
// default `operator<`.
template <typename C, typename Compare>
void c_stable_sort(C& c, Compare&& comp) {
template <typename C, typename LessThan>
void c_stable_sort(C& c, LessThan&& comp) {
std::stable_sort(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_is_sorted()
@ -944,11 +944,11 @@ bool c_is_sorted(const C& c) {
// c_is_sorted() overload for performing a `comp` comparison other than the
// default `operator<`.
template <typename C, typename Compare>
bool c_is_sorted(const C& c, Compare&& comp) {
template <typename C, typename LessThan>
bool c_is_sorted(const C& c, LessThan&& comp) {
return std::is_sorted(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_partial_sort()
@ -966,14 +966,14 @@ void c_partial_sort(
// Overload of c_partial_sort() for performing a `comp` comparison other than
// the default `operator<`.
template <typename RandomAccessContainer, typename Compare>
template <typename RandomAccessContainer, typename LessThan>
void c_partial_sort(
RandomAccessContainer& sequence,
container_algorithm_internal::ContainerIter<RandomAccessContainer> middle,
Compare&& comp) {
LessThan&& comp) {
std::partial_sort(container_algorithm_internal::c_begin(sequence), middle,
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_partial_sort_copy()
@ -994,15 +994,15 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) {
// Overload of c_partial_sort_copy() for performing a `comp` comparison other
// than the default `operator<`.
template <typename C, typename RandomAccessContainer, typename Compare>
template <typename C, typename RandomAccessContainer, typename LessThan>
container_algorithm_internal::ContainerIter<RandomAccessContainer>
c_partial_sort_copy(const C& sequence, RandomAccessContainer& result,
Compare&& comp) {
LessThan&& comp) {
return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
container_algorithm_internal::c_begin(result),
container_algorithm_internal::c_end(result),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_is_sorted_until()
@ -1018,12 +1018,12 @@ container_algorithm_internal::ContainerIter<C> c_is_sorted_until(C& c) {
// Overload of c_is_sorted_until() for performing a `comp` comparison other than
// the default `operator<`.
template <typename C, typename Compare>
template <typename C, typename LessThan>
container_algorithm_internal::ContainerIter<C> c_is_sorted_until(
C& c, Compare&& comp) {
C& c, LessThan&& comp) {
return std::is_sorted_until(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_nth_element()
@ -1043,14 +1043,14 @@ void c_nth_element(
// Overload of c_nth_element() for performing a `comp` comparison other than
// the default `operator<`.
template <typename RandomAccessContainer, typename Compare>
template <typename RandomAccessContainer, typename LessThan>
void c_nth_element(
RandomAccessContainer& sequence,
container_algorithm_internal::ContainerIter<RandomAccessContainer> nth,
Compare&& comp) {
LessThan&& comp) {
std::nth_element(container_algorithm_internal::c_begin(sequence), nth,
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
//------------------------------------------------------------------------------
@ -1072,12 +1072,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
// Overload of c_lower_bound() for performing a `comp` comparison other than
// the default `operator<`.
template <typename Sequence, typename T, typename Compare>
template <typename Sequence, typename T, typename LessThan>
container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
Sequence& sequence, T&& value, Compare&& comp) {
Sequence& sequence, T&& value, LessThan&& comp) {
return std::lower_bound(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<T>(value), std::forward<Compare>(comp));
std::forward<T>(value), std::forward<LessThan>(comp));
}
// c_upper_bound()
@ -1095,12 +1095,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
// Overload of c_upper_bound() for performing a `comp` comparison other than
// the default `operator<`.
template <typename Sequence, typename T, typename Compare>
template <typename Sequence, typename T, typename LessThan>
container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
Sequence& sequence, T&& value, Compare&& comp) {
Sequence& sequence, T&& value, LessThan&& comp) {
return std::upper_bound(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<T>(value), std::forward<Compare>(comp));
std::forward<T>(value), std::forward<LessThan>(comp));
}
// c_equal_range()
@ -1118,12 +1118,12 @@ c_equal_range(Sequence& sequence, T&& value) {
// Overload of c_equal_range() for performing a `comp` comparison other than
// the default `operator<`.
template <typename Sequence, typename T, typename Compare>
template <typename Sequence, typename T, typename LessThan>
container_algorithm_internal::ContainerIterPairType<Sequence, Sequence>
c_equal_range(Sequence& sequence, T&& value, Compare&& comp) {
c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) {
return std::equal_range(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<T>(value), std::forward<Compare>(comp));
std::forward<T>(value), std::forward<LessThan>(comp));
}
// c_binary_search()
@ -1140,12 +1140,12 @@ bool c_binary_search(Sequence&& sequence, T&& value) {
// Overload of c_binary_search() for performing a `comp` comparison other than
// the default `operator<`.
template <typename Sequence, typename T, typename Compare>
bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) {
template <typename Sequence, typename T, typename LessThan>
bool c_binary_search(Sequence&& sequence, T&& value, LessThan&& comp) {
return std::binary_search(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<T>(value),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
//------------------------------------------------------------------------------
@ -1166,14 +1166,14 @@ OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) {
// Overload of c_merge() for performing a `comp` comparison other than
// the default `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare>
template <typename C1, typename C2, typename OutputIterator, typename LessThan>
OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result,
Compare&& comp) {
LessThan&& comp) {
return std::merge(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), result,
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_inplace_merge()
@ -1189,13 +1189,13 @@ void c_inplace_merge(C& c,
// Overload of c_inplace_merge() for performing a merge using a `comp` other
// than `operator<`.
template <typename C, typename Compare>
template <typename C, typename LessThan>
void c_inplace_merge(C& c,
container_algorithm_internal::ContainerIter<C> middle,
Compare&& comp) {
LessThan&& comp) {
std::inplace_merge(container_algorithm_internal::c_begin(c), middle,
container_algorithm_internal::c_end(c),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_includes()
@ -1213,13 +1213,13 @@ bool c_includes(const C1& c1, const C2& c2) {
// Overload of c_includes() for performing a merge using a `comp` other than
// `operator<`.
template <typename C1, typename C2, typename Compare>
bool c_includes(const C1& c1, const C2& c2, Compare&& comp) {
template <typename C1, typename C2, typename LessThan>
bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) {
return std::includes(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_set_union()
@ -1243,7 +1243,7 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
// Overload of c_set_union() for performing a merge using a `comp` other than
// `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare,
template <typename C1, typename C2, typename OutputIterator, typename LessThan,
typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value,
void>::type,
@ -1251,18 +1251,18 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare,
!container_algorithm_internal::IsUnorderedContainer<C2>::value,
void>::type>
OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
Compare&& comp) {
LessThan&& comp) {
return std::set_union(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), output,
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_set_intersection()
//
// Container-based version of the <algorithm> `std::set_intersection()` function
// to return an iterator containing the intersection of two containers.
// to return an iterator containing the intersection of two sorted containers.
template <typename C1, typename C2, typename OutputIterator,
typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value,
@ -1272,6 +1272,11 @@ template <typename C1, typename C2, typename OutputIterator,
void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output) {
// In debug builds, ensure that both containers are sorted with respect to the
// default comparator. std::set_intersection requires the containers be sorted
// using operator<.
assert(absl::c_is_sorted(c1));
assert(absl::c_is_sorted(c2));
return std::set_intersection(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2),
@ -1280,7 +1285,7 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2,
// Overload of c_set_intersection() for performing a merge using a `comp` other
// than `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare,
template <typename C1, typename C2, typename OutputIterator, typename LessThan,
typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value,
void>::type,
@ -1288,12 +1293,17 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare,
!container_algorithm_internal::IsUnorderedContainer<C2>::value,
void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) {
OutputIterator output, LessThan&& comp) {
// In debug builds, ensure that both containers are sorted with respect to the
// default comparator. std::set_intersection requires the containers be sorted
// using the same comparator.
assert(absl::c_is_sorted(c1, comp));
assert(absl::c_is_sorted(c2, comp));
return std::set_intersection(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), output,
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_set_difference()
@ -1318,7 +1328,7 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2,
// Overload of c_set_difference() for performing a merge using a `comp` other
// than `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare,
template <typename C1, typename C2, typename OutputIterator, typename LessThan,
typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value,
void>::type,
@ -1326,12 +1336,12 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare,
!container_algorithm_internal::IsUnorderedContainer<C2>::value,
void>::type>
OutputIterator c_set_difference(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) {
OutputIterator output, LessThan&& comp) {
return std::set_difference(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), output,
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_set_symmetric_difference()
@ -1357,7 +1367,7 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
// Overload of c_set_symmetric_difference() for performing a merge using a
// `comp` other than `operator<`.
template <typename C1, typename C2, typename OutputIterator, typename Compare,
template <typename C1, typename C2, typename OutputIterator, typename LessThan,
typename = typename std::enable_if<
!container_algorithm_internal::IsUnorderedContainer<C1>::value,
void>::type,
@ -1366,13 +1376,13 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare,
void>::type>
OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output,
Compare&& comp) {
LessThan&& comp) {
return std::set_symmetric_difference(
container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
container_algorithm_internal::c_begin(c2),
container_algorithm_internal::c_end(c2), output,
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
//------------------------------------------------------------------------------
@ -1391,11 +1401,11 @@ void c_push_heap(RandomAccessContainer& sequence) {
// Overload of c_push_heap() for performing a push operation on a heap using a
// `comp` other than `operator<`.
template <typename RandomAccessContainer, typename Compare>
void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) {
template <typename RandomAccessContainer, typename LessThan>
void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) {
std::push_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_pop_heap()
@ -1410,11 +1420,11 @@ void c_pop_heap(RandomAccessContainer& sequence) {
// Overload of c_pop_heap() for performing a pop operation on a heap using a
// `comp` other than `operator<`.
template <typename RandomAccessContainer, typename Compare>
void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) {
template <typename RandomAccessContainer, typename LessThan>
void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) {
std::pop_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_make_heap()
@ -1429,11 +1439,11 @@ void c_make_heap(RandomAccessContainer& sequence) {
// Overload of c_make_heap() for performing heap comparisons using a
// `comp` other than `operator<`
template <typename RandomAccessContainer, typename Compare>
void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) {
template <typename RandomAccessContainer, typename LessThan>
void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) {
std::make_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_sort_heap()
@ -1448,11 +1458,11 @@ void c_sort_heap(RandomAccessContainer& sequence) {
// Overload of c_sort_heap() for performing heap comparisons using a
// `comp` other than `operator<`
template <typename RandomAccessContainer, typename Compare>
void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) {
template <typename RandomAccessContainer, typename LessThan>
void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) {
std::sort_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_is_heap()
@ -1467,11 +1477,11 @@ bool c_is_heap(const RandomAccessContainer& sequence) {
// Overload of c_is_heap() for performing heap comparisons using a
// `comp` other than `operator<`
template <typename RandomAccessContainer, typename Compare>
bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) {
template <typename RandomAccessContainer, typename LessThan>
bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) {
return std::is_heap(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_is_heap_until()
@ -1487,12 +1497,12 @@ c_is_heap_until(RandomAccessContainer& sequence) {
// Overload of c_is_heap_until() for performing heap comparisons using a
// `comp` other than `operator<`
template <typename RandomAccessContainer, typename Compare>
template <typename RandomAccessContainer, typename LessThan>
container_algorithm_internal::ContainerIter<RandomAccessContainer>
c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) {
c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) {
return std::is_heap_until(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
//------------------------------------------------------------------------------
@ -1513,12 +1523,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_min_element(
// Overload of c_min_element() for performing a `comp` comparison other than
// `operator<`.
template <typename Sequence, typename Compare>
template <typename Sequence, typename LessThan>
container_algorithm_internal::ContainerIter<Sequence> c_min_element(
Sequence& sequence, Compare&& comp) {
Sequence& sequence, LessThan&& comp) {
return std::min_element(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_max_element()
@ -1535,12 +1545,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_max_element(
// Overload of c_max_element() for performing a `comp` comparison other than
// `operator<`.
template <typename Sequence, typename Compare>
template <typename Sequence, typename LessThan>
container_algorithm_internal::ContainerIter<Sequence> c_max_element(
Sequence& sequence, Compare&& comp) {
Sequence& sequence, LessThan&& comp) {
return std::max_element(container_algorithm_internal::c_begin(sequence),
container_algorithm_internal::c_end(sequence),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_minmax_element()
@ -1558,12 +1568,12 @@ c_minmax_element(C& c) {
// Overload of c_minmax_element() for performing `comp` comparisons other than
// `operator<`.
template <typename C, typename Compare>
template <typename C, typename LessThan>
container_algorithm_internal::ContainerIterPairType<C, C>
c_minmax_element(C& c, Compare&& comp) {
c_minmax_element(C& c, LessThan&& comp) {
return std::minmax_element(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
//------------------------------------------------------------------------------
@ -1588,15 +1598,15 @@ bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) {
// Overload of c_lexicographical_compare() for performing a lexicographical
// comparison using a `comp` operator instead of `operator<`.
template <typename Sequence1, typename Sequence2, typename Compare>
template <typename Sequence1, typename Sequence2, typename LessThan>
bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2,
Compare&& comp) {
LessThan&& comp) {
return std::lexicographical_compare(
container_algorithm_internal::c_begin(sequence1),
container_algorithm_internal::c_end(sequence1),
container_algorithm_internal::c_begin(sequence2),
container_algorithm_internal::c_end(sequence2),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_next_permutation()
@ -1612,11 +1622,11 @@ bool c_next_permutation(C& c) {
// Overload of c_next_permutation() for performing a lexicographical
// comparison using a `comp` operator instead of `operator<`.
template <typename C, typename Compare>
bool c_next_permutation(C& c, Compare&& comp) {
template <typename C, typename LessThan>
bool c_next_permutation(C& c, LessThan&& comp) {
return std::next_permutation(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
// c_prev_permutation()
@ -1632,11 +1642,11 @@ bool c_prev_permutation(C& c) {
// Overload of c_prev_permutation() for performing a lexicographical
// comparison using a `comp` operator instead of `operator<`.
template <typename C, typename Compare>
bool c_prev_permutation(C& c, Compare&& comp) {
template <typename C, typename LessThan>
bool c_prev_permutation(C& c, LessThan&& comp) {
return std::prev_permutation(container_algorithm_internal::c_begin(c),
container_algorithm_internal::c_end(c),
std::forward<Compare>(comp));
std::forward<LessThan>(comp));
}
//------------------------------------------------------------------------------

View File

@ -14,7 +14,6 @@
# limitations under the License.
#
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@ -593,75 +592,6 @@ cc_test(
],
)
cc_library(
name = "exponential_biased",
srcs = ["internal/exponential_biased.cc"],
hdrs = ["internal/exponential_biased.h"],
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
"//absl:__subpackages__",
],
deps = [
":config",
":core_headers",
],
)
cc_test(
name = "exponential_biased_test",
size = "small",
srcs = ["internal/exponential_biased_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
":exponential_biased",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "periodic_sampler",
srcs = ["internal/periodic_sampler.cc"],
hdrs = ["internal/periodic_sampler.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":core_headers",
":exponential_biased",
],
)
cc_test(
name = "periodic_sampler_test",
size = "small",
srcs = ["internal/periodic_sampler_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
":core_headers",
":periodic_sampler",
"@com_google_googletest//:gtest_main",
],
)
cc_binary(
name = "periodic_sampler_benchmark",
testonly = 1,
srcs = ["internal/periodic_sampler_benchmark.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
tags = ["benchmark"],
visibility = ["//visibility:private"],
deps = [
":core_headers",
":periodic_sampler",
"@com_github_google_benchmark//:benchmark_main",
],
)
cc_library(
name = "scoped_set_env",
testonly = 1,

View File

@ -230,7 +230,7 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::config
gtest
GTest::gtest
TESTONLY
)
@ -259,7 +259,7 @@ absl_cc_library(
absl::meta
absl::strings
absl::utility
gtest
GTest::gtest
TESTONLY
)
@ -273,7 +273,7 @@ absl_cc_test(
DEPS
absl::exception_safety_testing
absl::memory
gtest_main
GTest::gtest_main
)
absl_cc_library(
@ -300,8 +300,8 @@ absl_cc_test(
absl::atomic_hook_test_helper
absl::atomic_hook
absl::core_headers
gmock
gtest_main
GTest::gmock
GTest::gtest_main
)
absl_cc_test(
@ -314,7 +314,7 @@ absl_cc_test(
DEPS
absl::base
absl::core_headers
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -327,8 +327,8 @@ absl_cc_test(
DEPS
absl::errno_saver
absl::strerror
gmock
gtest_main
GTest::gmock
GTest::gtest_main
)
absl_cc_test(
@ -342,7 +342,7 @@ absl_cc_test(
absl::base
absl::config
absl::throw_delegate
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -357,7 +357,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::base_internal
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -371,8 +371,8 @@ absl_cc_test(
absl::base_internal
absl::memory
absl::strings
gmock
gtest_main
GTest::gmock
GTest::gtest_main
)
absl_cc_library(
@ -388,7 +388,7 @@ absl_cc_library(
absl::base_internal
absl::core_headers
absl::synchronization
gtest
GTest::gtest
TESTONLY
)
@ -406,7 +406,7 @@ absl_cc_test(
absl::config
absl::core_headers
absl::synchronization
gtest_main
GTest::gtest_main
)
absl_cc_library(
@ -435,7 +435,7 @@ absl_cc_test(
absl::base
absl::config
absl::endian
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -448,7 +448,7 @@ absl_cc_test(
DEPS
absl::config
absl::synchronization
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -462,7 +462,7 @@ absl_cc_test(
absl::base
absl::core_headers
absl::synchronization
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -475,7 +475,7 @@ absl_cc_test(
DEPS
absl::raw_logging_internal
absl::strings
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -488,7 +488,7 @@ absl_cc_test(
DEPS
absl::base
absl::synchronization
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -516,61 +516,7 @@ absl_cc_test(
absl::core_headers
absl::synchronization
Threads::Threads
gtest_main
)
absl_cc_library(
NAME
exponential_biased
SRCS
"internal/exponential_biased.cc"
HDRS
"internal/exponential_biased.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::config
absl::core_headers
)
absl_cc_test(
NAME
exponential_biased_test
SRCS
"internal/exponential_biased_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::exponential_biased
absl::strings
gmock_main
)
absl_cc_library(
NAME
periodic_sampler
SRCS
"internal/periodic_sampler.cc"
HDRS
"internal/periodic_sampler.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::core_headers
absl::exponential_biased
)
absl_cc_test(
NAME
periodic_sampler_test
SRCS
"internal/periodic_sampler_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::core_headers
absl::periodic_sampler
gmock_main
GTest::gtest_main
)
absl_cc_library(
@ -596,7 +542,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::scoped_set_env
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -620,8 +566,8 @@ absl_cc_test(
absl::flags_marshalling
absl::log_severity
absl::strings
gmock
gtest_main
GTest::gmock
GTest::gtest_main
)
absl_cc_library(
@ -651,8 +597,8 @@ absl_cc_test(
DEPS
absl::strerror
absl::strings
gmock
gtest_main
GTest::gmock
GTest::gtest_main
)
absl_cc_library(
@ -677,7 +623,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::fast_type_id
gtest_main
GTest::gtest_main
)
absl_cc_test(
@ -690,5 +636,5 @@ absl_cc_test(
DEPS
absl::core_headers
absl::optional
gtest_main
GTest::gtest_main
)

View File

@ -131,14 +131,14 @@
// 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
// Weak attributes did not work properly in LLVM's Windows backend before
// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
// for further information.
// The MinGW compiler doesn't complain about the weak attribute until the link
// step, presumably because Windows doesn't use ELF binaries.
#if (ABSL_HAVE_ATTRIBUTE(weak) || \
(defined(__GNUC__) && !defined(__clang__))) && \
!(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__)
(!defined(_WIN32) || __clang_major__ < 9) && !defined(__MINGW32__)
#undef ABSL_ATTRIBUTE_WEAK
#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
#define ABSL_HAVE_ATTRIBUTE_WEAK 1
@ -281,10 +281,7 @@
// 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__))
#if ABSL_HAVE_ATTRIBUTE(returns_nonnull)
#define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
#else
#define ABSL_ATTRIBUTE_RETURNS_NONNULL
@ -321,8 +318,16 @@
// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section.
// This functionality is supported by GNU linker.
#ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE
#ifdef _AIX
// __attribute__((section(#name))) on AIX is achived by using the `.csect` psudo
// op which includes an additional integer as part of its syntax indcating
// alignment. If data fall under different alignments then you might get a
// compilation error indicating a `Section type conflict`.
#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name)
#else
#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
#endif
#endif
// ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
//
@ -524,6 +529,13 @@
// ABSL_ATTRIBUTE_UNUSED
//
// Prevents the compiler from complaining about variables that appear unused.
//
// For code or headers that are assured to only build with C++17 and up, prefer
// just using the standard '[[maybe_unused]]' directly over this macro.
//
// Due to differences in positioning requirements between the old, compiler
// specific __attribute__ syntax and the now standard [[maybe_unused]], this
// macro does not attempt to take advantage of '[[maybe_unused]]'.
#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
#undef ABSL_ATTRIBUTE_UNUSED
#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__))
@ -544,13 +556,19 @@
// 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.
// structure, but instead to reduce its alignment to 1.
//
// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing
// so can cause atomic variables to be mis-aligned and silently violate
// atomicity on x86.
//
// This attribute can either be applied to members of a structure or to a
// structure in its entirety. Applying this attribute (judiciously) to a
// structure in its entirety to optimize the memory footprint of very
// commonly-used structs is fine. Do not apply this attribute to a structure in
// its entirety if the purpose is to control the offsets of the members in the
// structure. Instead, apply this attribute only to structure members that need
// it.
//
// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the
// natural alignment of structure members not annotated is preserved. Aligned
@ -595,31 +613,24 @@
// 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:
// Notes: When supported, GCC and Clang can issue a warning on switch labels
// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See
// clang documentation on language extensions for details:
// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
//
// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro
// has no effect on diagnostics. In any case this macro has no effect on runtime
// 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")
#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough)
#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]]
#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough)
#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]]
#endif
#elif defined(__GNUC__) && __GNUC__ >= 7
#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough)
#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
#endif
#ifndef ABSL_FALLTHROUGH_INTENDED
#else
#define ABSL_FALLTHROUGH_INTENDED \
do { \
} while (0)
@ -699,4 +710,26 @@
#define ABSL_ATTRIBUTE_PURE_FUNCTION
#endif
// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function
// parameter or implicit object parameter is retained by the return value of the
// annotated function (or, for a parameter of a constructor, in the value of the
// constructed object). This attribute causes warnings to be produced if a
// temporary object does not live long enough.
//
// When applied to a reference parameter, the referenced object is assumed to be
// retained by the return value of the function. When applied to a non-reference
// parameter (for example, a pointer or a class type), all temporaries
// referenced by the parameter are assumed to be retained by the return value of
// the function.
//
// See also the upstream documentation:
// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound
#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound)
#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]]
#elif ABSL_HAVE_ATTRIBUTE(lifetimebound)
#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound))
#else
#define ABSL_ATTRIBUTE_LIFETIME_BOUND
#endif
#endif // ABSL_BASE_ATTRIBUTES_H_

View File

@ -66,6 +66,35 @@
#include "absl/base/options.h"
#include "absl/base/policy_checks.h"
// Abseil long-term support (LTS) releases will define
// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the
// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the
// integer representing the patch-level for that release.
//
// For example, for LTS release version "20300401.2", this would give us
// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2
//
// These symbols will not be defined in non-LTS code.
//
// Abseil recommends that clients live-at-head. Therefore, if you are using
// these symbols to assert a minimum version requirement, we recommend you do it
// as
//
// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401
// #error Project foo requires Abseil LTS version >= 20300401
// #endif
//
// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes
// live-at-head clients from the minimum version assertion.
//
// See https://abseil.io/about/releases for more information on Abseil release
// management.
//
// LTS releases can be obtained from
// https://github.com/abseil/abseil-cpp/releases.
#define ABSL_LTS_RELEASE_VERSION 20211102
#define ABSL_LTS_RELEASE_PATCH_LEVEL 0
// 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)
@ -166,6 +195,22 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#define ABSL_HAVE_FEATURE(f) 0
#endif
// Portable check for GCC minimum version:
// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \
(__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
#else
#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0
#endif
#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__)
#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \
(__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y))
#else
#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0
#endif
// ABSL_HAVE_TLS is defined to 1 when __thread should be supported.
// We assume __thread is supported on Linux when compiled with Clang or compiled
// against libstdc++ with _GLIBCXX_HAVE_TLS defined.
@ -183,10 +228,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// 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)
#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \
(!defined(__clang__) && defined(__GLIBCXX__) && \
ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8))
#define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
#endif
@ -199,16 +243,17 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
//
// Checks whether `std::is_trivially_copy_assignable<T>` 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).
// Notes: Clang with libc++ supports these features, as does gcc >= 7.4 with
// libstdc++, or gcc >= 8.2 with libc++, and Visual Studio (but not NVCC).
#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
(!defined(__clang__) && defined(__GNUC__) && \
(__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
(!defined(__clang__) && \
((ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \
(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) && \
defined(_LIBCPP_VERSION)))) || \
(defined(_MSC_VER) && !defined(__NVCC__))
#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
@ -222,7 +267,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#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
#elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0)
#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1
#endif
#endif
@ -319,25 +364,21 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// 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)
#elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6)
// Clang >= 3.6
#if ABSL_HAVE_FEATURE(cxx_exceptions)
#define ABSL_HAVE_EXCEPTIONS 1
#endif // ABSL_HAVE_FEATURE(cxx_exceptions)
#else
#elif defined(__clang__)
// Clang < 3.6
// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro
#if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions)
#define ABSL_HAVE_EXCEPTIONS 1
#endif // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions)
#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)) && \
#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \
!(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) && \
!defined(__cpp_exceptions)) && \
!(defined(_MSC_VER) && !defined(_CPPUNWIND))
#define ABSL_HAVE_EXCEPTIONS 1
#endif
@ -369,10 +410,11 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// 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__)
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(_AIX) || defined(__ros__) || defined(__native_client__) || \
defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \
defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \
defined(__HAIKU__)
#define ABSL_HAVE_MMAP 1
#endif
@ -383,7 +425,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
#error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__ros__)
defined(_AIX) || defined(__ros__)
#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
#endif
@ -690,10 +732,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// 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)
@ -705,10 +743,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// 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)
@ -720,10 +754,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// 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)

View File

@ -433,31 +433,6 @@ ABSL_NAMESPACE_END
#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
@ -471,7 +446,7 @@ using absl::base_internal::ValgrindSlowdown;
__sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)
#define ABSL_ADDRESS_SANITIZER_REDZONE(name) \
struct { \
char x[8] __attribute__((aligned(8))); \
alignas(8) char x[8]; \
} name
#else

View File

@ -536,7 +536,22 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
}
// Memory management operators
// Args.. allows us to overload regular and placement new in one shot
static void* operator new(size_t s) noexcept(
IsSpecified(TypeSpec::kNoThrowNew)) {
if (!IsSpecified(TypeSpec::kNoThrowNew)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
}
return ::operator new(s);
}
static void* operator new[](size_t s) noexcept(
IsSpecified(TypeSpec::kNoThrowNew)) {
if (!IsSpecified(TypeSpec::kNoThrowNew)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
}
return ::operator new[](s);
}
template <typename... Args>
static void* operator new(size_t s, Args&&... args) noexcept(
IsSpecified(TypeSpec::kNoThrowNew)) {
@ -557,12 +572,6 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
// Abseil doesn't support throwing overloaded operator delete. These are
// provided so a throwing operator-new can clean up after itself.
//
// We provide both regular and templated operator delete because if only the
// templated version is provided as we did with operator new, the compiler has
// no way of knowing which overload of operator delete to call. See
// https://en.cppreference.com/w/cpp/memory/new/operator_delete and
// https://en.cppreference.com/w/cpp/language/delete for the gory details.
void operator delete(void* p) noexcept { ::operator delete(p); }
template <typename... Args>
@ -726,9 +735,8 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
ThrowingAllocator select_on_container_copy_construction() noexcept(
IsSpecified(AllocSpec::kNoThrowAllocate)) {
auto& out = *this;
ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
return out;
return *this;
}
template <typename U>

View File

@ -16,13 +16,15 @@
// 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 use by Abseil internal code that Mutex itself depends on
// - for async signal safety (see below)
// SpinLock is async signal safe. If a spinlock is used within a signal
// handler, all code that acquires the lock must ensure that the signal cannot
// arrive while they are holding the lock. Typically, this is done by blocking
// the signal.
//
// Threads waiting on a SpinLock may be woken in an arbitrary order.
#ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_
#define ABSL_BASE_INTERNAL_SPINLOCK_H_

View File

@ -39,6 +39,8 @@ struct SpinLockWaitTransition {
// satisfying 0<=i<n && trans[i].done, atomically make the transition,
// then return the old value of *w. Make any other atomic transitions
// where !trans[i].done, but continue waiting.
//
// Wakeups for threads blocked on SpinLockWait do not respect priorities.
uint32_t SpinLockWait(std::atomic<uint32_t> *w, int n,
const SpinLockWaitTransition trans[],
SchedulingMode scheduling_mode);

View File

@ -61,9 +61,78 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
namespace {
#if defined(_WIN32)
// Returns number of bits set in `bitMask`
DWORD Win32CountSetBits(ULONG_PTR bitMask) {
for (DWORD bitSetCount = 0; ; ++bitSetCount) {
if (bitMask == 0) return bitSetCount;
bitMask &= bitMask - 1;
}
}
// Returns the number of logical CPUs using GetLogicalProcessorInformation(), or
// 0 if the number of processors is not available or can not be computed.
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation
int Win32NumCPUs() {
#pragma comment(lib, "kernel32.lib")
using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
DWORD info_size = sizeof(Info);
Info* info(static_cast<Info*>(malloc(info_size)));
if (info == nullptr) return 0;
bool success = GetLogicalProcessorInformation(info, &info_size);
if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
free(info);
info = static_cast<Info*>(malloc(info_size));
if (info == nullptr) return 0;
success = GetLogicalProcessorInformation(info, &info_size);
}
DWORD logicalProcessorCount = 0;
if (success) {
Info* ptr = info;
DWORD byteOffset = 0;
while (byteOffset + sizeof(Info) <= info_size) {
switch (ptr->Relationship) {
case RelationProcessorCore:
logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask);
break;
case RelationNumaNode:
case RelationCache:
case RelationProcessorPackage:
// Ignore other entries
break;
default:
// Ignore unknown entries
break;
}
byteOffset += sizeof(Info);
ptr++;
}
}
free(info);
return logicalProcessorCount;
}
#endif
} // namespace
static int GetNumCPUs() {
#if defined(__myriad2__)
return 1;
#elif defined(_WIN32)
const unsigned hardware_concurrency = Win32NumCPUs();
return hardware_concurrency ? hardware_concurrency : 1;
#elif defined(_AIX)
return sysconf(_SC_NPROCESSORS_ONLN);
#else
// Other possibilities:
// - Read /sys/devices/system/cpu/online and use cpumask_parse()

View File

@ -120,10 +120,10 @@ void SetCurrentThreadIdentity(
ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
// Please see the comment on `CurrentThreadIdentityIfPresent` in
// thread_identity.h. Because DLLs cannot expose thread_local variables in
// headers, we opt for the correct-but-slower option of placing the definition
// of this function only in a translation unit inside DLL.
#if defined(ABSL_BUILD_DLL) || defined(ABSL_CONSUME_DLL)
// 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

View File

@ -188,25 +188,25 @@ void ClearCurrentThreadIdentity();
// May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE=<mode
// index>
#ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be direcly set
#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be directly set
#else
#define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0
#endif
#ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS
#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be direcly set
#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be directly set
#else
#define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1
#endif
#ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11
#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be direcly set
#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be directly set
#else
#define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2
#endif
#ifdef ABSL_THREAD_IDENTITY_MODE
#error ABSL_THREAD_IDENTITY_MODE cannot be direcly set
#error ABSL_THREAD_IDENTITY_MODE cannot be directly set
#elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE)
#define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE
#elif defined(_WIN32) && !defined(__MINGW32__)
@ -236,13 +236,18 @@ ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr;
#error Thread-local storage not detected on this platform
#endif
// thread_local variables cannot be in headers exposed by DLLs. However, it is
// important for performance reasons in general that
// `CurrentThreadIdentityIfPresent` be inlined. This is not possible across a
// DLL boundary so, with DLLs, we opt to have the function not be inlined. Note
// 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 when compiling as a DLL.
#if !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL)
// 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;
}

View File

@ -87,6 +87,10 @@ int64_t UnscaledCycleClock::Now() {
double UnscaledCycleClock::Frequency() {
#ifdef __GLIBC__
return __ppc_get_timebase_freq();
#elif defined(_AIX)
// This is the same constant value as returned by
// __ppc_get_timebase_freq().
return static_cast<double>(512000000);
#elif defined(__FreeBSD__)
static once_flag init_timebase_frequency_once;
static double timebase_frequency = 0.0;
@ -119,6 +123,18 @@ double UnscaledCycleClock::Frequency() {
return aarch64_timer_frequency;
}
#elif defined(__riscv)
int64_t UnscaledCycleClock::Now() {
int64_t virtual_timer_value;
asm volatile("rdcycle %0" : "=r"(virtual_timer_value));
return virtual_timer_value;
}
double UnscaledCycleClock::Frequency() {
return base_internal::NominalCPUFrequency();
}
#elif defined(_M_IX86) || defined(_M_X64)
#pragma intrinsic(__rdtsc)

View File

@ -46,8 +46,8 @@
// 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)
defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \
defined(_M_IX86) || defined(_M_X64)
#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1
#else
#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0
@ -80,8 +80,8 @@
// 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))
#if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || \
defined(_M_IX86) || defined(_M_X64))
#define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
#endif

View File

@ -52,9 +52,9 @@ TEST(StreamTest, Works) {
Eq("absl::LogSeverity(4)"));
}
static_assert(
absl::flags_internal::FlagUseOneWordStorage<absl::LogSeverity>::value,
"Flags of type absl::LogSeverity ought to be lock-free.");
static_assert(absl::flags_internal::FlagUseValueAndInitBitStorage<
absl::LogSeverity>::value,
"Flags of type absl::LogSeverity ought to be lock-free.");
using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>;
INSTANTIATE_TEST_SUITE_P(

View File

@ -206,7 +206,7 @@
// allowed.
#define ABSL_OPTION_USE_INLINE_NAMESPACE 1
#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20210324
#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20211102
// ABSL_OPTION_HARDENED
//

View File

@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",

View File

@ -51,5 +51,5 @@ absl_cc_test(
absl::cleanup
absl::config
absl::utility
gmock_main
GTest::gmock_main
)

View File

@ -86,25 +86,25 @@ class ABSL_MUST_USE_RESULT Cleanup final {
"Callbacks that return values are not supported.");
public:
Cleanup(Callback callback) // NOLINT
: storage_(std::move(callback), /* is_callback_engaged = */ true) {}
Cleanup(Callback callback) : storage_(std::move(callback)) {} // NOLINT
Cleanup(Cleanup&& other) = default;
void Cancel() && {
ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
storage_.DisengageCallback();
storage_.DestroyCallback();
}
void Invoke() && {
ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
storage_.DisengageCallback();
storage_.InvokeCallback();
storage_.DestroyCallback();
}
~Cleanup() {
if (storage_.IsCallbackEngaged()) {
storage_.InvokeCallback();
storage_.DestroyCallback();
}
}

View File

@ -264,4 +264,48 @@ TYPED_TEST(CleanupTest, Move) {
EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
}
int DestructionCount = 0;
struct DestructionCounter {
void operator()() {}
~DestructionCounter() { ++DestructionCount; }
};
TYPED_TEST(CleanupTest, DestructorDestroys) {
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
DestructionCount = 0;
}
EXPECT_EQ(DestructionCount, 1); // Engaged cleanup destroys
}
TYPED_TEST(CleanupTest, CancelDestroys) {
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
DestructionCount = 0;
std::move(cleanup).Cancel();
EXPECT_EQ(DestructionCount, 1); // Cancel destroys
}
EXPECT_EQ(DestructionCount, 1); // Canceled cleanup does not double destroy
}
TYPED_TEST(CleanupTest, InvokeDestroys) {
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
DestructionCount = 0;
std::move(cleanup).Invoke();
EXPECT_EQ(DestructionCount, 1); // Invoke destroys
}
EXPECT_EQ(DestructionCount, 1); // Invoked cleanup does not double destroy
}
} // namespace

View File

@ -15,10 +15,12 @@
#ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_
#define ABSL_CLEANUP_INTERNAL_CLEANUP_H_
#include <new>
#include <type_traits>
#include <utility>
#include "absl/base/internal/invoke.h"
#include "absl/base/macros.h"
#include "absl/base/thread_annotations.h"
#include "absl/utility/utility.h"
@ -45,14 +47,22 @@ class Storage {
public:
Storage() = delete;
Storage(Callback callback, bool is_callback_engaged)
: callback_(std::move(callback)),
is_callback_engaged_(is_callback_engaged) {}
explicit Storage(Callback callback) {
// Placement-new into a character buffer is used for eager destruction when
// the cleanup is invoked or cancelled. To ensure this optimizes well, the
// behavior is implemented locally instead of using an absl::optional.
::new (GetCallbackBuffer()) Callback(std::move(callback));
is_callback_engaged_ = true;
}
Storage(Storage&& other)
: callback_(std::move(other.callback_)),
is_callback_engaged_(
absl::exchange(other.is_callback_engaged_, false)) {}
Storage(Storage&& other) {
ABSL_HARDENING_ASSERT(other.IsCallbackEngaged());
::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback()));
is_callback_engaged_ = true;
other.DestroyCallback();
}
Storage(const Storage& other) = delete;
@ -60,17 +70,26 @@ class Storage {
Storage& operator=(const Storage& other) = delete;
void* GetCallbackBuffer() { return static_cast<void*>(+callback_buffer_); }
Callback& GetCallback() {
return *reinterpret_cast<Callback*>(GetCallbackBuffer());
}
bool IsCallbackEngaged() const { return is_callback_engaged_; }
void DisengageCallback() { is_callback_engaged_ = false; }
void DestroyCallback() {
is_callback_engaged_ = false;
GetCallback().~Callback();
}
void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS {
std::move(callback_)();
std::move(GetCallback())();
}
private:
Callback callback_;
bool is_callback_engaged_;
alignas(Callback) char callback_buffer_[sizeof(Callback)];
};
} // namespace cleanup_internal

View File

@ -14,7 +14,6 @@
# limitations under the License.
#
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@ -510,9 +509,10 @@ cc_library(
":have_sse",
"//absl/base",
"//absl/base:core_headers",
"//absl/base:exponential_biased",
"//absl/debugging:stacktrace",
"//absl/memory",
"//absl/profiling:exponential_biased",
"//absl/profiling:sample_recorder",
"//absl/synchronization",
"//absl/utility",
],
@ -526,6 +526,7 @@ cc_test(
":hashtablez_sampler",
":have_sse",
"//absl/base:core_headers",
"//absl/profiling:sample_recorder",
"//absl/synchronization",
"//absl/synchronization:thread_pool",
"//absl/time",
@ -598,7 +599,6 @@ cc_library(
":hashtable_debug_hooks",
":hashtablez_sampler",
":have_sse",
":layout",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
@ -876,6 +876,22 @@ cc_test(
],
)
cc_test(
name = "sample_element_size_test",
srcs = ["sample_element_size_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
tags = NOTEST_TAGS_NONMOBILE,
visibility = ["//visibility:private"],
deps = [
":flat_hash_map",
":flat_hash_set",
":node_hash_map",
":node_hash_set",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "btree",
srcs = [

View File

@ -80,7 +80,7 @@ absl_cc_test(
absl::strings
absl::test_instance_tracker
absl::type_traits
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -109,7 +109,7 @@ absl_cc_test(
absl::optional
absl::test_instance_tracker
absl::utility
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -144,7 +144,7 @@ absl_cc_test(
absl::exception_testing
absl::hash_testing
absl::memory
gmock_main
GTest::gmock_main
)
absl_cc_test(
@ -158,7 +158,7 @@ absl_cc_test(
absl::fixed_array
absl::config
absl::exception_safety_testing
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -222,7 +222,7 @@ absl_cc_test(
absl::memory
absl::raw_logging_internal
absl::strings
gmock_main
GTest::gmock_main
)
absl_cc_test(
@ -236,7 +236,7 @@ absl_cc_test(
absl::inlined_vector
absl::config
absl::exception_safety_testing
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -262,7 +262,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::test_instance_tracker
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -297,7 +297,7 @@ absl_cc_test(
absl::unordered_map_modifiers_test
absl::any
absl::raw_logging_internal
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -335,7 +335,7 @@ absl_cc_test(
absl::memory
absl::raw_logging_internal
absl::strings
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -370,7 +370,7 @@ absl_cc_test(
absl::unordered_map_lookup_test
absl::unordered_map_members_test
absl::unordered_map_modifiers_test
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -404,7 +404,7 @@ absl_cc_test(
absl::unordered_set_lookup_test
absl::unordered_set_members_test
absl::unordered_set_modifiers_test
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -433,7 +433,7 @@ absl_cc_test(
absl::container_memory
absl::strings
absl::test_instance_tracker
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -465,7 +465,7 @@ absl_cc_test(
absl::hash
absl::random_random
absl::strings
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -507,7 +507,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::hash_policy_testing
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -531,7 +531,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::hash_policy_traits
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -548,6 +548,7 @@ absl_cc_library(
absl::base
absl::exponential_biased
absl::have_sse
absl::sample_recorder
absl::synchronization
)
@ -561,7 +562,7 @@ absl_cc_test(
DEPS
absl::hashtablez_sampler
absl::have_sse
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -618,7 +619,7 @@ absl_cc_test(
DEPS
absl::hash_policy_traits
absl::node_hash_policy
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -666,7 +667,6 @@ absl_cc_library(
absl::hash_policy_traits
absl::hashtable_debug_hooks
absl::have_sse
absl::layout
absl::memory
absl::meta
absl::optional
@ -693,7 +693,7 @@ absl_cc_test(
absl::core_headers
absl::raw_logging_internal
absl::strings
gmock_main
GTest::gmock_main
)
absl_cc_test(
@ -707,7 +707,7 @@ absl_cc_test(
absl::raw_hash_set
absl::tracked
absl::core_headers
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -740,7 +740,7 @@ absl_cc_test(
absl::core_headers
absl::raw_logging_internal
absl::span
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -765,7 +765,7 @@ absl_cc_library(
DEPS
absl::hash_generator_testing
absl::hash_policy_testing
gmock
GTest::gmock
TESTONLY
)
@ -779,7 +779,7 @@ absl_cc_library(
DEPS
absl::hash_generator_testing
absl::hash_policy_testing
gmock
GTest::gmock
TESTONLY
)
@ -792,7 +792,7 @@ absl_cc_library(
${ABSL_TEST_COPTS}
DEPS
absl::type_traits
gmock
GTest::gmock
TESTONLY
)
@ -806,7 +806,7 @@ absl_cc_library(
DEPS
absl::hash_generator_testing
absl::hash_policy_testing
gmock
GTest::gmock
TESTONLY
)
@ -820,7 +820,7 @@ absl_cc_library(
DEPS
absl::hash_generator_testing
absl::hash_policy_testing
gmock
GTest::gmock
TESTONLY
)
@ -834,7 +834,7 @@ absl_cc_library(
DEPS
absl::hash_generator_testing
absl::hash_policy_testing
gmock
GTest::gmock
TESTONLY
)
@ -847,7 +847,7 @@ absl_cc_library(
${ABSL_TEST_COPTS}
DEPS
absl::type_traits
gmock
GTest::gmock
TESTONLY
)
@ -861,7 +861,7 @@ absl_cc_library(
DEPS
absl::hash_generator_testing
absl::hash_policy_testing
gmock
GTest::gmock
TESTONLY
)
@ -877,7 +877,7 @@ absl_cc_test(
absl::unordered_set_lookup_test
absl::unordered_set_members_test
absl::unordered_set_modifiers_test
gmock_main
GTest::gmock_main
)
absl_cc_test(
@ -892,5 +892,20 @@ absl_cc_test(
absl::unordered_map_lookup_test
absl::unordered_map_members_test
absl::unordered_map_modifiers_test
gmock_main
GTest::gmock_main
)
absl_cc_test(
NAME
sample_element_size_test
SRCS
"sample_element_size_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::flat_hash_map
absl::flat_hash_set
absl::node_hash_map
absl::node_hash_set
GTest::gmock_main
)

View File

@ -366,8 +366,8 @@ class btree_map
// 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.
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::contains;
// btree_map::count()
@ -378,8 +378,8 @@ class btree_map
// 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.
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::count;
// btree_map::equal_range()
@ -395,10 +395,34 @@ class btree_map
//
// Finds an element with the passed `key` within the `btree_map`.
//
// Supports heterogeneous lookup, provided that the map is provided a
// compatible heterogeneous comparator.
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::find;
// btree_map::lower_bound()
//
// template <typename K> iterator lower_bound(const K& key):
// template <typename K> const_iterator lower_bound(const K& key) const:
//
// Finds the first element with a key that is not less than `key` within the
// `btree_map`.
//
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::lower_bound;
// btree_map::upper_bound()
//
// template <typename K> iterator upper_bound(const K& key):
// template <typename K> const_iterator upper_bound(const K& key) const:
//
// Finds the first element with a key that is greater than `key` within the
// `btree_map`.
//
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::upper_bound;
// btree_map::operator[]()
//
// Returns a reference to the value mapped to the passed key within the
@ -669,9 +693,8 @@ class btree_multimap
// 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.
// Extracts all elements from a given `source` btree_multimap into this
// `btree_multimap`.
using Base::merge;
// btree_multimap::swap(btree_multimap& other)
@ -691,8 +714,8 @@ class btree_multimap
// 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.
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::contains;
// btree_multimap::count()
@ -702,8 +725,8 @@ class btree_multimap
// 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.
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::count;
// btree_multimap::equal_range()
@ -720,10 +743,34 @@ class btree_multimap
//
// Finds an element with the passed `key` within the `btree_multimap`.
//
// Supports heterogeneous lookup, provided that the map is provided a
// compatible heterogeneous comparator.
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::find;
// btree_multimap::lower_bound()
//
// template <typename K> iterator lower_bound(const K& key):
// template <typename K> const_iterator lower_bound(const K& key) const:
//
// Finds the first element with a key that is not less than `key` within the
// `btree_multimap`.
//
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::lower_bound;
// btree_multimap::upper_bound()
//
// template <typename K> iterator upper_bound(const K& key):
// template <typename K> const_iterator upper_bound(const K& key) const:
//
// Finds the first element with a key that is greater than `key` within the
// `btree_multimap`.
//
// Supports heterogeneous lookup, provided that the map has a compatible
// heterogeneous comparator.
using Base::upper_bound;
// btree_multimap::get_allocator()
//
// Returns the allocator function associated with this `btree_multimap`.

View File

@ -300,8 +300,8 @@ class btree_set
// 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.
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::contains;
// btree_set::count()
@ -312,8 +312,8 @@ class btree_set
// 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.
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::count;
// btree_set::equal_range()
@ -330,10 +330,32 @@ class btree_set
//
// Finds an element with the passed `key` within the `btree_set`.
//
// Supports heterogeneous lookup, provided that the set is provided a
// compatible heterogeneous comparator.
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::find;
// btree_set::lower_bound()
//
// template <typename K> iterator lower_bound(const K& key):
// template <typename K> const_iterator lower_bound(const K& key) const:
//
// Finds the first element that is not less than `key` within the `btree_set`.
//
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::lower_bound;
// btree_set::upper_bound()
//
// template <typename K> iterator upper_bound(const K& key):
// template <typename K> const_iterator upper_bound(const K& key) const:
//
// Finds the first element that is greater than `key` within the `btree_set`.
//
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::upper_bound;
// btree_set::get_allocator()
//
// Returns the allocator function associated with this `btree_set`.
@ -582,9 +604,8 @@ class btree_multiset
// 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.
// Extracts all elements from a given `source` btree_multiset into this
// `btree_multiset`.
using Base::merge;
// btree_multiset::swap(btree_multiset& other)
@ -604,8 +625,8 @@ class btree_multiset
// 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.
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::contains;
// btree_multiset::count()
@ -615,8 +636,8 @@ class btree_multiset
// 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.
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::count;
// btree_multiset::equal_range()
@ -633,10 +654,34 @@ class btree_multiset
//
// Finds an element with the passed `key` within the `btree_multiset`.
//
// Supports heterogeneous lookup, provided that the set is provided a
// compatible heterogeneous comparator.
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::find;
// btree_multiset::lower_bound()
//
// template <typename K> iterator lower_bound(const K& key):
// template <typename K> const_iterator lower_bound(const K& key) const:
//
// Finds the first element that is not less than `key` within the
// `btree_multiset`.
//
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::lower_bound;
// btree_multiset::upper_bound()
//
// template <typename K> iterator upper_bound(const K& key):
// template <typename K> const_iterator upper_bound(const K& key) const:
//
// Finds the first element that is greater than `key` within the
// `btree_multiset`.
//
// Supports heterogeneous lookup, provided that the set has a compatible
// heterogeneous comparator.
using Base::upper_bound;
// btree_multiset::get_allocator()
//
// Returns the allocator function associated with this `btree_multiset`.

View File

@ -595,7 +595,7 @@ void BtreeTest() {
using V = typename remove_pair_const<typename T::value_type>::type;
const std::vector<V> random_values = GenerateValuesWithSeed<V>(
absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values),
testing::GTEST_FLAG(random_seed));
GTEST_FLAG_GET(random_seed));
unique_checker<T, C> container;
@ -619,7 +619,7 @@ void BtreeMultiTest() {
using V = typename remove_pair_const<typename T::value_type>::type;
const std::vector<V> random_values = GenerateValuesWithSeed<V>(
absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values),
testing::GTEST_FLAG(random_seed));
GTEST_FLAG_GET(random_seed));
multi_checker<T, C> container;
@ -1708,10 +1708,25 @@ TEST(Btree, StrSplitCompatible) {
EXPECT_EQ(split_set, expected_set);
}
// We can't use EXPECT_EQ/etc. to compare absl::weak_ordering because they
// convert literal 0 to int and absl::weak_ordering can only be compared with
// literal 0. Defining this function allows for avoiding ClangTidy warnings.
bool Identity(const bool b) { return b; }
TEST(Btree, KeyComp) {
absl::btree_set<int> s;
EXPECT_TRUE(s.key_comp()(1, 2));
EXPECT_FALSE(s.key_comp()(2, 2));
EXPECT_FALSE(s.key_comp()(2, 1));
absl::btree_map<int, int> m1;
EXPECT_TRUE(m1.key_comp()(1, 2));
EXPECT_FALSE(m1.key_comp()(2, 2));
EXPECT_FALSE(m1.key_comp()(2, 1));
// Even though we internally adapt the comparator of `m2` to be three-way and
// heterogeneous, the comparator we expose through key_comp() is the original
// unadapted comparator.
absl::btree_map<std::string, int> m2;
EXPECT_TRUE(m2.key_comp()("a", "b"));
EXPECT_FALSE(m2.key_comp()("b", "b"));
EXPECT_FALSE(m2.key_comp()("b", "a"));
}
TEST(Btree, ValueComp) {
absl::btree_set<int> s;
@ -1724,13 +1739,13 @@ TEST(Btree, ValueComp) {
EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(2, 0)));
EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(1, 0)));
// Even though we internally adapt the comparator of `m2` to be three-way and
// heterogeneous, the comparator we expose through value_comp() is based on
// the original unadapted comparator.
absl::btree_map<std::string, int> m2;
EXPECT_TRUE(Identity(
m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)) < 0));
EXPECT_TRUE(Identity(
m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)) == 0));
EXPECT_TRUE(Identity(
m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)) > 0));
EXPECT_TRUE(m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)));
EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)));
EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)));
}
TEST(Btree, DefaultConstruction) {
@ -2893,6 +2908,46 @@ TEST(Btree, AllocMoveConstructor_DifferentAlloc) {
EXPECT_EQ(bytes_used2, original_bytes_used);
}
bool IntCmp(const int a, const int b) { return a < b; }
TEST(Btree, SupportsFunctionPtrComparator) {
absl::btree_set<int, decltype(IntCmp) *> set(IntCmp);
set.insert({1, 2, 3});
EXPECT_THAT(set, ElementsAre(1, 2, 3));
EXPECT_TRUE(set.key_comp()(1, 2));
EXPECT_TRUE(set.value_comp()(1, 2));
absl::btree_map<int, int, decltype(IntCmp) *> map(&IntCmp);
map[1] = 1;
EXPECT_THAT(map, ElementsAre(Pair(1, 1)));
EXPECT_TRUE(map.key_comp()(1, 2));
EXPECT_TRUE(map.value_comp()(std::make_pair(1, 1), std::make_pair(2, 2)));
}
template <typename Compare>
struct TransparentPassThroughComp {
using is_transparent = void;
// This will fail compilation if we attempt a comparison that Compare does not
// support, and the failure will happen inside the function implementation so
// it can't be avoided by using SFINAE on this comparator.
template <typename T, typename U>
bool operator()(const T &lhs, const U &rhs) const {
return Compare()(lhs, rhs);
}
};
TEST(Btree,
SupportsTransparentComparatorThatDoesNotImplementAllVisibleOperators) {
absl::btree_set<MultiKey, TransparentPassThroughComp<MultiKeyComp>> set;
set.insert(MultiKey{1, 2});
EXPECT_TRUE(set.contains(1));
}
TEST(Btree, ConstructImplicitlyWithUnadaptedComparator) {
absl::btree_set<MultiKey, MultiKeyComp> set = {{}, MultiKeyComp{}};
}
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END

View File

@ -73,11 +73,6 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
// 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 <typename T, size_t N = kFixedArrayUseDefault,
typename A = std::allocator<T>>
class FixedArray {

View File

@ -282,6 +282,32 @@ TEST(FlatHashMap, NodeHandleMutableKeyAccess) {
}
#endif
TEST(FlatHashMap, Reserve) {
// Verify that if we reserve(size() + n) then we can perform n insertions
// without a rehash, i.e., without invalidating any references.
for (size_t trial = 0; trial < 20; ++trial) {
for (size_t initial = 3; initial < 100; ++initial) {
// Fill in `initial` entries, then erase 2 of them, then reserve space for
// two inserts and check for reference stability while doing the inserts.
flat_hash_map<size_t, size_t> map;
for (size_t i = 0; i < initial; ++i) {
map[i] = i;
}
map.erase(0);
map.erase(1);
map.reserve(map.size() + 2);
size_t& a2 = map[2];
// In the event of a failure, asan will complain in one of these two
// assignments.
map[initial] = a2;
map[initial + 1] = a2;
// Fail even when not under asan:
size_t& a2new = map[2];
EXPECT_EQ(&a2, &a2new);
}
}
}
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END

View File

@ -72,37 +72,43 @@ class InlinedVector {
using Storage = inlined_vector_internal::Storage<T, N, A>;
using AllocatorTraits = typename Storage::AllocatorTraits;
using RValueReference = typename Storage::RValueReference;
using MoveIterator = typename Storage::MoveIterator;
using IsMemcpyOk = typename Storage::IsMemcpyOk;
template <typename TheA>
using AllocatorTraits = inlined_vector_internal::AllocatorTraits<TheA>;
template <typename TheA>
using MoveIterator = inlined_vector_internal::MoveIterator<TheA>;
template <typename TheA>
using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<TheA>;
template <typename Iterator>
template <typename TheA, typename Iterator>
using IteratorValueAdapter =
typename Storage::template IteratorValueAdapter<Iterator>;
using CopyValueAdapter = typename Storage::CopyValueAdapter;
using DefaultValueAdapter = typename Storage::DefaultValueAdapter;
inlined_vector_internal::IteratorValueAdapter<TheA, Iterator>;
template <typename TheA>
using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter<TheA>;
template <typename TheA>
using DefaultValueAdapter =
inlined_vector_internal::DefaultValueAdapter<TheA>;
template <typename Iterator>
using EnableIfAtLeastForwardIterator = absl::enable_if_t<
inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>;
inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
template <typename Iterator>
using DisableIfAtLeastForwardIterator = absl::enable_if_t<
!inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>;
!inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
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;
using allocator_type = A;
using value_type = inlined_vector_internal::ValueType<A>;
using pointer = inlined_vector_internal::Pointer<A>;
using const_pointer = inlined_vector_internal::ConstPointer<A>;
using size_type = inlined_vector_internal::SizeType<A>;
using difference_type = inlined_vector_internal::DifferenceType<A>;
using reference = inlined_vector_internal::Reference<A>;
using const_reference = inlined_vector_internal::ConstReference<A>;
using iterator = inlined_vector_internal::Iterator<A>;
using const_iterator = inlined_vector_internal::ConstIterator<A>;
using reverse_iterator = inlined_vector_internal::ReverseIterator<A>;
using const_reverse_iterator =
inlined_vector_internal::ConstReverseIterator<A>;
// ---------------------------------------------------------------------------
// InlinedVector Constructors and Destructor
@ -111,28 +117,28 @@ class InlinedVector {
// 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 empty inlined vector with a copy of `allocator`.
explicit InlinedVector(const allocator_type& allocator) noexcept
: storage_(allocator) {}
// Creates an inlined vector with `n` copies of `value_type()`.
explicit InlinedVector(size_type n,
const allocator_type& alloc = allocator_type())
: storage_(alloc) {
storage_.Initialize(DefaultValueAdapter(), n);
const allocator_type& allocator = allocator_type())
: storage_(allocator) {
storage_.Initialize(DefaultValueAdapter<A>(), n);
}
// Creates an inlined vector with `n` copies of `v`.
InlinedVector(size_type n, const_reference v,
const allocator_type& alloc = allocator_type())
: storage_(alloc) {
storage_.Initialize(CopyValueAdapter(v), n);
const allocator_type& allocator = allocator_type())
: storage_(allocator) {
storage_.Initialize(CopyValueAdapter<A>(std::addressof(v)), n);
}
// Creates an inlined vector with copies of the elements of `list`.
InlinedVector(std::initializer_list<value_type> list,
const allocator_type& alloc = allocator_type())
: InlinedVector(list.begin(), list.end(), alloc) {}
const allocator_type& allocator = allocator_type())
: InlinedVector(list.begin(), list.end(), allocator) {}
// Creates an inlined vector with elements constructed from the provided
// forward iterator range [`first`, `last`).
@ -141,35 +147,36 @@ class InlinedVector {
// this constructor with two integral arguments and a call to the above
// `InlinedVector(size_type, const_reference)` constructor.
template <typename ForwardIterator,
EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
InlinedVector(ForwardIterator first, ForwardIterator last,
const allocator_type& alloc = allocator_type())
: storage_(alloc) {
storage_.Initialize(IteratorValueAdapter<ForwardIterator>(first),
const allocator_type& allocator = allocator_type())
: storage_(allocator) {
storage_.Initialize(IteratorValueAdapter<A, ForwardIterator>(first),
std::distance(first, last));
}
// Creates an inlined vector with elements constructed from the provided input
// iterator range [`first`, `last`).
template <typename InputIterator,
DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
DisableIfAtLeastForwardIterator<InputIterator> = 0>
InlinedVector(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type())
: storage_(alloc) {
const allocator_type& allocator = allocator_type())
: storage_(allocator) {
std::copy(first, last, std::back_inserter(*this));
}
// Creates an inlined vector by copying the contents of `other` using
// `other`'s allocator.
InlinedVector(const InlinedVector& other)
: InlinedVector(other, *other.storage_.GetAllocPtr()) {}
: InlinedVector(other, other.storage_.GetAllocator()) {}
// Creates an inlined vector by copying the contents of `other` using `alloc`.
InlinedVector(const InlinedVector& other, const allocator_type& alloc)
: storage_(alloc) {
// Creates an inlined vector by copying the contents of `other` using the
// provided `allocator`.
InlinedVector(const InlinedVector& other, const allocator_type& allocator)
: storage_(allocator) {
if (other.empty()) {
// Empty; nothing to do.
} else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
} else if (IsMemcpyOk<A>::value && !other.storage_.GetIsAllocated()) {
// Memcpy-able and do not need allocation.
storage_.MemcpyFrom(other.storage_);
} else {
@ -194,23 +201,23 @@ class InlinedVector {
InlinedVector(InlinedVector&& other) noexcept(
absl::allocator_is_nothrow<allocator_type>::value ||
std::is_nothrow_move_constructible<value_type>::value)
: storage_(*other.storage_.GetAllocPtr()) {
if (IsMemcpyOk::value) {
: storage_(other.storage_.GetAllocator()) {
if (IsMemcpyOk<A>::value) {
storage_.MemcpyFrom(other.storage_);
other.storage_.SetInlinedSize(0);
} else if (other.storage_.GetIsAllocated()) {
storage_.SetAllocatedData(other.storage_.GetAllocatedData(),
other.storage_.GetAllocatedCapacity());
storage_.SetAllocation({other.storage_.GetAllocatedData(),
other.storage_.GetAllocatedCapacity()});
storage_.SetAllocatedSize(other.storage_.GetSize());
other.storage_.SetInlinedSize(0);
} else {
IteratorValueAdapter<MoveIterator> other_values(
MoveIterator(other.storage_.GetInlinedData()));
IteratorValueAdapter<A, MoveIterator<A>> other_values(
MoveIterator<A>(other.storage_.GetInlinedData()));
inlined_vector_internal::ConstructElements(
storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values,
inlined_vector_internal::ConstructElements<A>(
storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
other.storage_.GetSize());
storage_.SetInlinedSize(other.storage_.GetSize());
@ -218,30 +225,32 @@ class InlinedVector {
}
// Creates an inlined vector by moving in the contents of `other` with a copy
// of `alloc`.
// of `allocator`.
//
// NOTE: if `other`'s allocator is not equal to `alloc`, even if `other`
// NOTE: if `other`'s allocator is not equal to `allocator`, even if `other`
// contains allocated memory, this move constructor will still allocate. Since
// allocation is performed, this constructor can only be `noexcept` if the
// specified allocator is also `noexcept`.
InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept(
absl::allocator_is_nothrow<allocator_type>::value)
: storage_(alloc) {
if (IsMemcpyOk::value) {
InlinedVector(
InlinedVector&& other,
const allocator_type& allocator)
noexcept(absl::allocator_is_nothrow<allocator_type>::value)
: storage_(allocator) {
if (IsMemcpyOk<A>::value) {
storage_.MemcpyFrom(other.storage_);
other.storage_.SetInlinedSize(0);
} else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) &&
} else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) &&
other.storage_.GetIsAllocated()) {
storage_.SetAllocatedData(other.storage_.GetAllocatedData(),
other.storage_.GetAllocatedCapacity());
storage_.SetAllocation({other.storage_.GetAllocatedData(),
other.storage_.GetAllocatedCapacity()});
storage_.SetAllocatedSize(other.storage_.GetSize());
other.storage_.SetInlinedSize(0);
} else {
storage_.Initialize(
IteratorValueAdapter<MoveIterator>(MoveIterator(other.data())),
other.size());
storage_.Initialize(IteratorValueAdapter<A, MoveIterator<A>>(
MoveIterator<A>(other.data())),
other.size());
}
}
@ -442,7 +451,7 @@ class InlinedVector {
// `InlinedVector::get_allocator()`
//
// Returns a copy of the inlined vector's allocator.
allocator_type get_allocator() const { return *storage_.GetAllocPtr(); }
allocator_type get_allocator() const { return storage_.GetAllocator(); }
// ---------------------------------------------------------------------------
// InlinedVector Member Mutators
@ -476,16 +485,16 @@ class InlinedVector {
// 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());
if (IsMemcpyOk<A>::value || other.storage_.GetIsAllocated()) {
inlined_vector_internal::DestroyElements<A>(storage_.GetAllocator(),
data(), size());
storage_.DeallocateIfAllocated();
storage_.MemcpyFrom(other.storage_);
other.storage_.SetInlinedSize(0);
} else {
storage_.Assign(IteratorValueAdapter<MoveIterator>(
MoveIterator(other.storage_.GetInlinedData())),
storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>(
MoveIterator<A>(other.storage_.GetInlinedData())),
other.size());
}
}
@ -497,7 +506,7 @@ class InlinedVector {
//
// 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);
storage_.Assign(CopyValueAdapter<A>(std::addressof(v)), n);
}
// Overload of `InlinedVector::assign(...)` that replaces the contents of the
@ -511,9 +520,9 @@ class InlinedVector {
//
// NOTE: this overload is for iterators that are "forward" category or better.
template <typename ForwardIterator,
EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
void assign(ForwardIterator first, ForwardIterator last) {
storage_.Assign(IteratorValueAdapter<ForwardIterator>(first),
storage_.Assign(IteratorValueAdapter<A, ForwardIterator>(first),
std::distance(first, last));
}
@ -522,7 +531,7 @@ class InlinedVector {
//
// NOTE: this overload is for iterators that are "input" category.
template <typename InputIterator,
DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
DisableIfAtLeastForwardIterator<InputIterator> = 0>
void assign(InputIterator first, InputIterator last) {
size_type i = 0;
for (; i < size() && first != last; ++i, static_cast<void>(++first)) {
@ -541,7 +550,7 @@ class InlinedVector {
// is larger than `size()`, new elements are value-initialized.
void resize(size_type n) {
ABSL_HARDENING_ASSERT(n <= max_size());
storage_.Resize(DefaultValueAdapter(), n);
storage_.Resize(DefaultValueAdapter<A>(), n);
}
// Overload of `InlinedVector::resize(...)` that resizes the inlined vector to
@ -551,7 +560,7 @@ class InlinedVector {
// 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);
storage_.Resize(CopyValueAdapter<A>(std::addressof(v)), n);
}
// `InlinedVector::insert(...)`
@ -564,7 +573,7 @@ class InlinedVector {
// 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) {
iterator insert(const_iterator pos, value_type&& v) {
return emplace(pos, std::move(v));
}
@ -577,7 +586,8 @@ class InlinedVector {
if (ABSL_PREDICT_TRUE(n != 0)) {
value_type dealias = v;
return storage_.Insert(pos, CopyValueAdapter(dealias), n);
return storage_.Insert(pos, CopyValueAdapter<A>(std::addressof(dealias)),
n);
} else {
return const_cast<iterator>(pos);
}
@ -596,14 +606,15 @@ class InlinedVector {
//
// NOTE: this overload is for iterators that are "forward" category or better.
template <typename ForwardIterator,
EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
iterator insert(const_iterator pos, ForwardIterator first,
ForwardIterator last) {
ABSL_HARDENING_ASSERT(pos >= begin());
ABSL_HARDENING_ASSERT(pos <= end());
if (ABSL_PREDICT_TRUE(first != last)) {
return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first),
return storage_.Insert(pos,
IteratorValueAdapter<A, ForwardIterator>(first),
std::distance(first, last));
} else {
return const_cast<iterator>(pos);
@ -616,7 +627,7 @@ class InlinedVector {
//
// NOTE: this overload is for iterators that are "input" category.
template <typename InputIterator,
DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
DisableIfAtLeastForwardIterator<InputIterator> = 0>
iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
ABSL_HARDENING_ASSERT(pos >= begin());
ABSL_HARDENING_ASSERT(pos <= end());
@ -640,8 +651,8 @@ class InlinedVector {
value_type dealias(std::forward<Args>(args)...);
return storage_.Insert(pos,
IteratorValueAdapter<MoveIterator>(
MoveIterator(std::addressof(dealias))),
IteratorValueAdapter<A, MoveIterator<A>>(
MoveIterator<A>(std::addressof(dealias))),
1);
}
@ -661,7 +672,7 @@ class InlinedVector {
// Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()`
// using move semantics.
void push_back(RValueReference v) {
void push_back(value_type&& v) {
static_cast<void>(emplace_back(std::move(v)));
}
@ -671,7 +682,7 @@ class InlinedVector {
void pop_back() noexcept {
ABSL_HARDENING_ASSERT(!empty());
AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1));
AllocatorTraits<A>::destroy(storage_.GetAllocator(), data() + (size() - 1));
storage_.SubtractSize(1);
}
@ -710,8 +721,8 @@ class InlinedVector {
// 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());
inlined_vector_internal::DestroyElements<A>(storage_.GetAllocator(), data(),
size());
storage_.DeallocateIfAllocated();
storage_.SetInlinedSize(0);
@ -724,15 +735,12 @@ class InlinedVector {
// `InlinedVector::shrink_to_fit()`
//
// Reduces memory usage by freeing unused memory. After being called, calls to
// `capacity()` will be equal to `max(N, size())`.
// Attempts to reduce memory usage by moving elements to (or keeping elements
// in) the smallest available buffer sufficient for containing `size()`
// elements.
//
// 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.
// If `size()` is sufficiently small, the elements will be moved into (or kept
// in) the inlined space.
void shrink_to_fit() {
if (storage_.GetIsAllocated()) {
storage_.ShrinkToFit();

View File

@ -88,7 +88,12 @@ struct StringBtreeDefaultLess {
// Compatibility constructor.
StringBtreeDefaultLess(std::less<std::string>) {} // NOLINT
StringBtreeDefaultLess(std::less<string_view>) {} // NOLINT
StringBtreeDefaultLess(std::less<absl::string_view>) {} // NOLINT
// Allow converting to std::less for use in key_comp()/value_comp().
explicit operator std::less<std::string>() const { return {}; }
explicit operator std::less<absl::string_view>() const { return {}; }
explicit operator std::less<absl::Cord>() const { return {}; }
absl::weak_ordering operator()(absl::string_view lhs,
absl::string_view rhs) const {
@ -115,7 +120,12 @@ struct StringBtreeDefaultGreater {
StringBtreeDefaultGreater() = default;
StringBtreeDefaultGreater(std::greater<std::string>) {} // NOLINT
StringBtreeDefaultGreater(std::greater<string_view>) {} // NOLINT
StringBtreeDefaultGreater(std::greater<absl::string_view>) {} // NOLINT
// Allow converting to std::greater for use in key_comp()/value_comp().
explicit operator std::greater<std::string>() const { return {}; }
explicit operator std::greater<absl::string_view>() const { return {}; }
explicit operator std::greater<absl::Cord>() const { return {}; }
absl::weak_ordering operator()(absl::string_view lhs,
absl::string_view rhs) const {
@ -217,6 +227,8 @@ struct prefers_linear_node_search<
template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
bool Multi, typename SlotPolicy>
struct common_params {
using original_key_compare = Compare;
// If Compare is a common comparator for a string-like type, then we adapt it
// to use heterogeneous lookup and to be a key-compare-to comparator.
using key_compare = typename key_compare_to_adapter<Compare>::type;
@ -317,16 +329,21 @@ struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi,
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) {}
using original_key_compare = typename super_type::original_key_compare;
// Reference: https://en.cppreference.com/w/cpp/container/map/value_compare
class value_compare {
template <typename Params>
friend class btree;
template <typename T, typename U>
auto operator()(const T &left, const U &right) const
-> decltype(std::declval<key_compare>()(left.first, right.first)) {
return key_compare::operator()(left.first, right.first);
protected:
explicit value_compare(original_key_compare c) : comp(std::move(c)) {}
original_key_compare comp; // NOLINT
public:
auto operator()(const value_type &lhs, const value_type &rhs) const
-> decltype(comp(lhs.first, rhs.first)) {
return comp(lhs.first, rhs.first);
}
};
using is_map_container = std::true_type;
@ -392,7 +409,8 @@ struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi,
set_slot_policy<Key>> {
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 value_compare =
typename set_params::common_params::original_key_compare;
using is_map_container = std::false_type;
template <typename V>
@ -484,8 +502,8 @@ class btree_node {
std::is_same<std::greater<key_type>,
key_compare>::value)>;
// This class is organized by gtl::Layout as if it had the following
// structure:
// This class is organized by absl::container_internal::Layout as if it had
// the following structure:
// // A pointer to the node's parent.
// btree_node *parent;
//
@ -1129,6 +1147,7 @@ class btree {
using size_type = typename Params::size_type;
using difference_type = typename Params::difference_type;
using key_compare = typename Params::key_compare;
using original_key_compare = typename Params::original_key_compare;
using value_compare = typename Params::value_compare;
using allocator_type = typename Params::allocator_type;
using reference = typename Params::reference;
@ -1338,7 +1357,9 @@ class btree {
return compare_internal::compare_result_as_less_than(key_comp()(a, b));
}
value_compare value_comp() const { return value_compare(key_comp()); }
value_compare value_comp() const {
return value_compare(original_key_compare(key_comp()));
}
// Verifies the structure of the btree.
void verify() const;

View File

@ -20,6 +20,7 @@
#include <iterator>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/container/internal/btree.h" // IWYU pragma: export
#include "absl/container/internal/common.h"
@ -51,7 +52,7 @@ class btree_container {
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 key_compare = typename Tree::original_key_compare;
using value_compare = typename Tree::value_compare;
using allocator_type = typename Tree::allocator_type;
using reference = typename Tree::reference;
@ -176,7 +177,7 @@ class btree_container {
}
// Utility routines.
void clear() { tree_.clear(); }
ABSL_ATTRIBUTE_REINITIALIZES void clear() { tree_.clear(); }
void swap(btree_container &other) { tree_.swap(other.tree_); }
void verify() const { tree_.verify(); }
@ -214,7 +215,7 @@ class btree_container {
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(); }
key_compare key_comp() const { return key_compare(tree_.key_comp()); }
value_compare value_comp() const { return tree_.value_comp(); }
// Support absl::Hash.
@ -247,7 +248,7 @@ class btree_set_container : public btree_container<Tree> {
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 key_compare = typename Tree::original_key_compare;
using allocator_type = typename Tree::allocator_type;
using iterator = typename Tree::iterator;
using const_iterator = typename Tree::const_iterator;
@ -398,7 +399,7 @@ class btree_map_container : public btree_set_container<Tree> {
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 key_compare = typename Tree::original_key_compare;
using allocator_type = typename Tree::allocator_type;
using iterator = typename Tree::iterator;
using const_iterator = typename Tree::const_iterator;
@ -543,7 +544,7 @@ class btree_multiset_container : public btree_container<Tree> {
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 key_compare = typename Tree::original_key_compare;
using allocator_type = typename Tree::allocator_type;
using iterator = typename Tree::iterator;
using const_iterator = typename Tree::const_iterator;

View File

@ -78,24 +78,26 @@ struct StringHash {
}
};
struct StringEq {
using is_transparent = void;
bool operator()(absl::string_view lhs, absl::string_view rhs) const {
return lhs == rhs;
}
bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const {
return lhs == rhs;
}
bool operator()(const absl::Cord& lhs, absl::string_view rhs) const {
return lhs == rhs;
}
bool operator()(absl::string_view lhs, const absl::Cord& rhs) const {
return lhs == rhs;
}
};
// Supports heterogeneous lookup for string-like elements.
struct StringHashEq {
using Hash = StringHash;
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;
}
};
using Eq = StringEq;
};
template <>

View File

@ -21,11 +21,13 @@
#include <stdint.h>
#include <algorithm>
#include <cassert>
#include <iosfwd>
#include <random>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "absl/container/internal/hash_policy_testing.h"
#include "absl/memory/memory.h"
@ -153,6 +155,25 @@ using GeneratedType = decltype(
typename Container::value_type,
typename Container::key_type>::type>&>()());
// Naive wrapper that performs a linear search of previous values.
// Beware this is O(SQR), which is reasonable for smaller kMaxValues.
template <class T, size_t kMaxValues = 64, class E = void>
struct UniqueGenerator {
Generator<T, E> gen;
std::vector<T> values;
T operator()() {
assert(values.size() < kMaxValues);
for (;;) {
T value = gen();
if (std::find(values.begin(), values.end(), value) == values.end()) {
values.push_back(value);
return value;
}
}
}
};
} // namespace hash_internal
} // namespace container_internal
ABSL_NAMESPACE_END

View File

@ -21,10 +21,11 @@
#include <limits>
#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/profiling/internal/exponential_biased.h"
#include "absl/profiling/internal/sample_recorder.h"
#include "absl/synchronization/mutex.h"
namespace absl {
@ -37,10 +38,9 @@ ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{
false
};
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10};
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20};
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased
ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased
g_exponential_biased_generator;
#endif
@ -50,16 +50,14 @@ ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased
ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0;
#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
HashtablezSampler& HashtablezSampler::Global() {
HashtablezSampler& GlobalHashtablezSampler() {
static auto* sampler = new HashtablezSampler();
return *sampler;
}
HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback(
DisposeCallback f) {
return dispose_.exchange(f, std::memory_order_relaxed);
}
// TODO(bradleybear): The comments at this constructors declaration say that the
// fields are not initialized, but this definition does initialize the fields.
// Something needs to be cleaned up.
HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
HashtablezInfo::~HashtablezInfo() = default;
@ -73,6 +71,7 @@ void HashtablezInfo::PrepareForSampling() {
hashes_bitwise_or.store(0, std::memory_order_relaxed);
hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed);
hashes_bitwise_xor.store(0, std::memory_order_relaxed);
max_reserve.store(0, std::memory_order_relaxed);
create_time = absl::Now();
// The inliner makes hardcoded skip_count difficult (especially when combined
@ -80,93 +79,6 @@ void HashtablezInfo::PrepareForSampling() {
// 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<void(const HashtablezInfo& stack)>& 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() {
@ -189,10 +101,12 @@ static bool ShouldForceSampling() {
return state == kForce;
}
HashtablezInfo* SampleSlow(int64_t* next_sample) {
HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) {
if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
*next_sample = 1;
return HashtablezSampler::Global().Register();
HashtablezInfo* result = GlobalHashtablezSampler().Register();
result->inline_element_size = inline_element_size;
return result;
}
#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@ -214,15 +128,17 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) {
// that case.
if (first) {
if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
return SampleSlow(next_sample);
return SampleSlow(next_sample, inline_element_size);
}
return HashtablezSampler::Global().Register();
HashtablezInfo* result = GlobalHashtablezSampler().Register();
result->inline_element_size = inline_element_size;
return result;
#endif
}
void UnsampleSlow(HashtablezInfo* info) {
HashtablezSampler::Global().Unregister(info);
GlobalHashtablezSampler().Unregister(info);
}
void RecordInsertSlow(HashtablezInfo* info, size_t hash,
@ -262,7 +178,7 @@ void SetHashtablezSampleParameter(int32_t rate) {
void SetHashtablezMaxSamples(int32_t max) {
if (max > 0) {
g_hashtablez_max_samples.store(max, std::memory_order_release);
GlobalHashtablezSampler().SetMaxSamples(max);
} else {
ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld",
static_cast<long long>(max)); // NOLINT(runtime/int)

View File

@ -47,6 +47,7 @@
#include "absl/base/internal/per_thread_tls.h"
#include "absl/base/optimization.h"
#include "absl/container/internal/have_sse.h"
#include "absl/profiling/internal/sample_recorder.h"
#include "absl/synchronization/mutex.h"
#include "absl/utility/utility.h"
@ -57,7 +58,7 @@ 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 {
struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> {
// Constructs the object but does not fill in any fields.
HashtablezInfo();
~HashtablezInfo();
@ -79,14 +80,7 @@ struct HashtablezInfo {
std::atomic<size_t> hashes_bitwise_or;
std::atomic<size_t> hashes_bitwise_and;
std::atomic<size_t> 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);
std::atomic<size_t> max_reserve;
// All of the fields below are set by `PrepareForSampling`, they must not be
// mutated in `Record*` functions. They are logically `const` in that sense.
@ -97,6 +91,7 @@ struct HashtablezInfo {
absl::Time create_time;
int32_t depth;
void* stack[kMaxStackDepth];
size_t inline_element_size;
};
inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
@ -114,6 +109,18 @@ inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
std::memory_order_relaxed);
}
inline void RecordReservationSlow(HashtablezInfo* info,
size_t target_capacity) {
info->max_reserve.store(
(std::max)(info->max_reserve.load(std::memory_order_relaxed),
target_capacity),
std::memory_order_relaxed);
}
inline void RecordClearedReservationSlow(HashtablezInfo* info) {
info->max_reserve.store(0, std::memory_order_relaxed);
}
inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
size_t capacity) {
info->size.store(size, std::memory_order_relaxed);
@ -137,7 +144,7 @@ inline void RecordEraseSlow(HashtablezInfo* info) {
std::memory_order_relaxed);
}
HashtablezInfo* SampleSlow(int64_t* next_sample);
HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size);
void UnsampleSlow(HashtablezInfo* info);
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@ -177,6 +184,16 @@ class HashtablezInfoHandle {
RecordRehashSlow(info_, total_probe_length);
}
inline void RecordReservation(size_t target_capacity) {
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
RecordReservationSlow(info_, target_capacity);
}
inline void RecordClearedReservation() {
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
RecordClearedReservationSlow(info_);
}
inline void RecordInsert(size_t hash, size_t distance_from_desired) {
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
RecordInsertSlow(info_, hash, distance_from_desired);
@ -206,6 +223,8 @@ class HashtablezInfoHandle {
inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {}
inline void RecordRehash(size_t /*total_probe_length*/) {}
inline void RecordReservation(size_t /*target_capacity*/) {}
inline void RecordClearedReservation() {}
inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) {}
inline void RecordErase() {}
@ -220,84 +239,24 @@ extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample;
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
inline HashtablezInfoHandle Sample() {
inline HashtablezInfoHandle Sample(
size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
return HashtablezInfoHandle(nullptr);
}
return HashtablezInfoHandle(SampleSlow(&global_next_sample));
return HashtablezInfoHandle(
SampleSlow(&global_next_sample, inline_element_size));
#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();
using HashtablezSampler =
::absl::profiling_internal::SampleRecorder<HashtablezInfo>;
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<void(const HashtablezInfo& stack)>& f);
private:
void PushNew(HashtablezInfo* sample);
void PushDead(HashtablezInfo* sample);
HashtablezInfo* PopDead();
std::atomic<size_t> dropped_samples_;
std::atomic<size_t> 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<HashtablezInfo*> all_;
HashtablezInfo graveyard_;
std::atomic<DisposeCallback> dispose_;
};
// Returns a global Sampler.
HashtablezSampler& GlobalHashtablezSampler();
// Enables or disables sampling for Swiss tables.
void SetHashtablezEnabled(bool enabled);

View File

@ -22,6 +22,7 @@
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
#include "absl/container/internal/have_sse.h"
#include "absl/profiling/internal/sample_recorder.h"
#include "absl/synchronization/blocking_counter.h"
#include "absl/synchronization/internal/thread_pool.h"
#include "absl/synchronization/mutex.h"
@ -77,10 +78,12 @@ HashtablezInfo* Register(HashtablezSampler* s, size_t size) {
TEST(HashtablezInfoTest, PrepareForSampling) {
absl::Time test_start = absl::Now();
const size_t test_element_size = 17;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
info.inline_element_size = test_element_size;
EXPECT_EQ(info.capacity.load(), 0);
EXPECT_EQ(info.size.load(), 0);
EXPECT_EQ(info.num_erases.load(), 0);
@ -90,7 +93,9 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
EXPECT_EQ(info.max_reserve.load(), 0);
EXPECT_GE(info.create_time, test_start);
EXPECT_EQ(info.inline_element_size, test_element_size);
info.capacity.store(1, std::memory_order_relaxed);
info.size.store(1, std::memory_order_relaxed);
@ -100,6 +105,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
info.hashes_bitwise_or.store(1, std::memory_order_relaxed);
info.hashes_bitwise_and.store(1, std::memory_order_relaxed);
info.hashes_bitwise_xor.store(1, std::memory_order_relaxed);
info.max_reserve.store(1, std::memory_order_relaxed);
info.create_time = test_start - absl::Hours(20);
info.PrepareForSampling();
@ -112,6 +118,8 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
EXPECT_EQ(info.max_reserve.load(), 0);
EXPECT_EQ(info.inline_element_size, test_element_size);
EXPECT_GE(info.create_time, test_start);
}
@ -150,9 +158,11 @@ TEST(HashtablezInfoTest, RecordInsert) {
}
TEST(HashtablezInfoTest, RecordErase) {
const size_t test_element_size = 29;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
info.inline_element_size = test_element_size;
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.size.load(), 0);
RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
@ -160,12 +170,15 @@ TEST(HashtablezInfoTest, RecordErase) {
RecordEraseSlow(&info);
EXPECT_EQ(info.size.load(), 0);
EXPECT_EQ(info.num_erases.load(), 1);
EXPECT_EQ(info.inline_element_size, test_element_size);
}
TEST(HashtablezInfoTest, RecordRehash) {
const size_t test_element_size = 31;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
info.inline_element_size = test_element_size;
RecordInsertSlow(&info, 0x1, 0);
RecordInsertSlow(&info, 0x2, kProbeLength);
RecordInsertSlow(&info, 0x4, kProbeLength);
@ -184,16 +197,34 @@ TEST(HashtablezInfoTest, RecordRehash) {
EXPECT_EQ(info.total_probe_length.load(), 3);
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.num_rehashes.load(), 1);
EXPECT_EQ(info.inline_element_size, test_element_size);
}
TEST(HashtablezInfoTest, RecordReservation) {
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
RecordReservationSlow(&info, 3);
EXPECT_EQ(info.max_reserve.load(), 3);
RecordReservationSlow(&info, 2);
// High watermark does not change
EXPECT_EQ(info.max_reserve.load(), 3);
RecordReservationSlow(&info, 10);
// High watermark does change
EXPECT_EQ(info.max_reserve.load(), 10);
}
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
TEST(HashtablezSamplerTest, SmallSampleParameter) {
const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
for (int i = 0; i < 1000; ++i) {
int64_t next_sample = 0;
HashtablezInfo* sample = SampleSlow(&next_sample);
HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
EXPECT_GT(next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
@ -201,12 +232,13 @@ TEST(HashtablezSamplerTest, SmallSampleParameter) {
}
TEST(HashtablezSamplerTest, LargeSampleParameter) {
const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
for (int i = 0; i < 1000; ++i) {
int64_t next_sample = 0;
HashtablezInfo* sample = SampleSlow(&next_sample);
HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
EXPECT_GT(next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
@ -214,13 +246,14 @@ TEST(HashtablezSamplerTest, LargeSampleParameter) {
}
TEST(HashtablezSamplerTest, Sample) {
const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
int64_t num_sampled = 0;
int64_t total = 0;
double sample_rate = 0.0;
for (int i = 0; i < 1000000; ++i) {
HashtablezInfoHandle h = Sample();
HashtablezInfoHandle h = Sample(test_element_size);
++total;
if (HashtablezInfoHandlePeer::IsSampled(h)) {
++num_sampled;
@ -232,7 +265,7 @@ TEST(HashtablezSamplerTest, Sample) {
}
TEST(HashtablezSamplerTest, Handle) {
auto& sampler = HashtablezSampler::Global();
auto& sampler = GlobalHashtablezSampler();
HashtablezInfoHandle h(sampler.Register());
auto* info = HashtablezInfoHandlePeer::GetInfo(&h);
info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed);

File diff suppressed because it is too large Load Diff

View File

@ -1350,7 +1350,13 @@ TEST(Layout, CustomAlignment) {
TEST(Layout, OverAligned) {
constexpr size_t M = alignof(max_align_t);
constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3);
#ifdef __GNUC__
// Using __attribute__ ((aligned ())) instead of alignas to bypass a gcc bug:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89357
__attribute__((aligned(2 * M))) unsigned char p[x.AllocSize()];
#else
alignas(2 * M) unsigned char p[x.AllocSize()];
#endif
EXPECT_EQ(2 * M + 3, x.AllocSize());
EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M));
}

View File

@ -51,8 +51,9 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> {
using key_arg = typename KeyArgImpl::template type<K, key_type>;
static_assert(!std::is_reference<key_type>::value, "");
// TODO(alkis): remove this assertion and verify that reference mapped_type is
// supported.
// TODO(b/187807849): Evaluate whether to support reference mapped_type and
// remove this assertion if/when it is supported.
static_assert(!std::is_reference<mapped_type>::value, "");
using iterator = typename raw_hash_map::raw_hash_set::iterator;

View File

@ -23,6 +23,12 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace container_internal {
alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[16] = {
ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty};
constexpr size_t Group::kWidth;
// Returns "random" seed.
@ -37,24 +43,24 @@ inline size_t RandomSeed() {
return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
}
bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) {
bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl) {
// To avoid problems with weak hashes and single bit tests, we use % 13.
// TODO(kfm,sbenza): revisit after we do unconditional mixing
return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6;
}
void ConvertDeletedToEmptyAndFullToDeleted(
ctrl_t* ctrl, size_t capacity) {
assert(ctrl[capacity] == kSentinel);
void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) {
assert(ctrl[capacity] == ctrl_t::kSentinel);
assert(IsValidCapacity(capacity));
for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) {
Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
}
// Copy the cloned ctrl bytes.
std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
ctrl[capacity] = kSentinel;
std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes());
ctrl[capacity] = ctrl_t::kSentinel;
}
// Extern template instantiotion for inline function.
template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t);
} // namespace container_internal
ABSL_NAMESPACE_END

View File

@ -87,6 +87,17 @@
//
// This probing function guarantees that after N probes, all the groups of the
// table will be probed exactly once.
//
// The control state and slot array are stored contiguously in a shared heap
// allocation. The layout of this allocation is: `capacity()` control bytes,
// one sentinel control byte, `Group::kWidth - 1` cloned control bytes,
// <possible padding>, `capacity()` slots. The sentinel control byte is used in
// iteration so we know when we reach the end of the table. The cloned control
// bytes at the end of the table are cloned from the beginning of the table so
// groups that begin near the end of the table can see a full group. In cases in
// which there are more than `capacity()` cloned control bytes, the extra bytes
// are `kEmpty`, and these ensure that we always see at least one empty slot and
// can stop an unsuccessful search.
#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
@ -112,7 +123,6 @@
#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"
@ -252,48 +262,53 @@ class BitMask {
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 {
// below for details. We use an enum class so that when strict aliasing is
// enabled, the compiler knows ctrl_t doesn't alias other types.
enum class ctrl_t : int8_t {
kEmpty = -128, // 0b10000000
kDeleted = -2, // 0b11111110
kSentinel = -1, // 0b11111111
};
static_assert(
kEmpty & kDeleted & kSentinel & 0x80,
(static_cast<int8_t>(ctrl_t::kEmpty) &
static_cast<int8_t>(ctrl_t::kDeleted) &
static_cast<int8_t>(ctrl_t::kSentinel) & 0x80) != 0,
"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 "
static_assert(
ctrl_t::kEmpty < ctrl_t::kSentinel && ctrl_t::kDeleted < ctrl_t::kSentinel,
"ctrl_t::kEmpty and ctrl_t::kDeleted must be smaller than "
"ctrl_t::kSentinel to make the SIMD test of IsEmptyOrDeleted() efficient");
static_assert(
ctrl_t::kSentinel == static_cast<ctrl_t>(-1),
"ctrl_t::kSentinel must be -1 to elide loading it from memory into SIMD "
"registers (pcmpeqd xmm, xmm)");
static_assert(ctrl_t::kEmpty == static_cast<ctrl_t>(-128),
"ctrl_t::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 "
static_assert(
(~static_cast<int8_t>(ctrl_t::kEmpty) &
~static_cast<int8_t>(ctrl_t::kDeleted) &
static_cast<int8_t>(ctrl_t::kSentinel) & 0x7F) != 0,
"ctrl_t::kEmpty and ctrl_t::kDeleted must share an unset bit that is not "
"shared by ctrl_t::kSentinel to make the scalar test for "
"MatchEmptyOrDeleted() efficient");
static_assert(ctrl_t::kDeleted == static_cast<ctrl_t>(-2),
"ctrl_t::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().
ABSL_DLL extern const ctrl_t kEmptyGroup[16];
inline ctrl_t* EmptyGroup() {
alignas(16) static constexpr ctrl_t empty_group[] = {
kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty};
return const_cast<ctrl_t*>(empty_group);
return const_cast<ctrl_t*>(kEmptyGroup);
}
// 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);
bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl);
// Returns a hash seed.
//
@ -309,12 +324,12 @@ inline size_t HashSeed(const ctrl_t* ctrl) {
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 h2_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; }
inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; }
inline bool IsFull(ctrl_t c) { return c >= static_cast<ctrl_t>(0); }
inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; }
inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; }
#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
@ -351,24 +366,24 @@ struct GroupSse2Impl {
// Returns a bitmask representing the positions of empty slots.
BitMask<uint32_t, kWidth> MatchEmpty() const {
#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3
// This only works because kEmpty is -128.
// This only works because ctrl_t::kEmpty is -128.
return BitMask<uint32_t, kWidth>(
_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)));
#else
return Match(static_cast<h2_t>(kEmpty));
return Match(static_cast<h2_t>(ctrl_t::kEmpty));
#endif
}
// Returns a bitmask representing the positions of empty or deleted slots.
BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const {
auto special = _mm_set1_epi8(kSentinel);
auto special = _mm_set1_epi8(static_cast<int8_t>(ctrl_t::kSentinel));
return BitMask<uint32_t, kWidth>(
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)));
}
// Returns the number of trailing empty or deleted elements in the group.
uint32_t CountLeadingEmptyOrDeleted() const {
auto special = _mm_set1_epi8(kSentinel);
auto special = _mm_set1_epi8(static_cast<int8_t>(ctrl_t::kSentinel));
return TrailingZeros(static_cast<uint32_t>(
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1));
}
@ -403,7 +418,7 @@ struct GroupPortableImpl {
//
// Caveat: there are false positives but:
// - they only occur if there is a real match
// - they never occur on kEmpty, kDeleted, kSentinel
// - they never occur on ctrl_t::kEmpty, ctrl_t::kDeleted, ctrl_t::kSentinel
// - they will be handled gracefully by subsequent checks in code
//
// Example:
@ -448,6 +463,10 @@ using Group = GroupSse2Impl;
using Group = GroupPortableImpl;
#endif
// The number of cloned control bytes that we copy from the beginning to the
// end of the control bytes array.
constexpr size_t NumClonedBytes() { return Group::kWidth - 1; }
template <class Policy, class Hash, class Eq, class Alloc>
class raw_hash_set;
@ -455,8 +474,8 @@ 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
// ctrl[capacity] == ctrl_t::kSentinel
// ctrl[i] != ctrl_t::kSentinel for all i < capacity
// Applies mapping for every byte in ctrl:
// DELETED -> EMPTY
// EMPTY -> EMPTY
@ -498,6 +517,22 @@ inline size_t GrowthToLowerboundCapacity(size_t growth) {
return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7);
}
template <class InputIter>
size_t SelectBucketCountForIterRange(InputIter first, InputIter last,
size_t bucket_count) {
if (bucket_count != 0) {
return bucket_count;
}
using InputIterCategory =
typename std::iterator_traits<InputIter>::iterator_category;
if (std::is_base_of<std::random_access_iterator_tag,
InputIterCategory>::value) {
return GrowthToLowerboundCapacity(
static_cast<size_t>(std::distance(first, last)));
}
return 0;
}
inline void AssertIsFull(ctrl_t* ctrl) {
ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) &&
"Invalid operation on iterator. The element might have "
@ -525,27 +560,29 @@ struct FindInfo {
// 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
// sentinel are valid. The rest contain dummy ctrl_t::kEmpty values that do not
// represent a real slot. This is important to take into account on
// find_first_non_full(), where we never try ShouldInsertBackwards() for
// small tables.
inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
inline probe_seq<Group::kWidth> probe(ctrl_t* ctrl, size_t hash,
inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, size_t hash,
size_t capacity) {
return probe_seq<Group::kWidth>(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.
// NOTE: this function must work with tables having both ctrl_t::kEmpty and
// ctrl_t::kDeleted 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,
template <typename = void>
inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash,
size_t capacity) {
auto seq = probe(ctrl, hash, capacity);
while (true) {
@ -564,10 +601,60 @@ inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash,
return {seq.offset(mask.LowestBitSet()), seq.index()};
}
seq.next();
assert(seq.index() < capacity && "full table!");
assert(seq.index() <= capacity && "full table!");
}
}
// Extern template for inline function keep possibility of inlining.
// When compiler decided to not inline, no symbols will be added to the
// corresponding translation unit.
extern template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t);
// Reset all ctrl bytes back to ctrl_t::kEmpty, except the sentinel.
inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot,
size_t slot_size) {
std::memset(ctrl, static_cast<int8_t>(ctrl_t::kEmpty),
capacity + 1 + NumClonedBytes());
ctrl[capacity] = ctrl_t::kSentinel;
SanitizerPoisonMemoryRegion(slot, slot_size * capacity);
}
// Sets the control byte, and if `i < NumClonedBytes()`, set the cloned byte
// at the end too.
inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl,
const void* slot, size_t slot_size) {
assert(i < capacity);
auto* slot_i = static_cast<const char*>(slot) + i * slot_size;
if (IsFull(h)) {
SanitizerUnpoisonMemoryRegion(slot_i, slot_size);
} else {
SanitizerPoisonMemoryRegion(slot_i, slot_size);
}
ctrl[i] = h;
ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h;
}
inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl,
const void* slot, size_t slot_size) {
SetCtrl(i, static_cast<ctrl_t>(h), capacity, ctrl, slot, slot_size);
}
// The allocated block consists of `capacity + 1 + NumClonedBytes()` control
// bytes followed by `capacity` slots, which must be aligned to `slot_align`.
// SlotOffset returns the offset of the slots into the allocated block.
inline size_t SlotOffset(size_t capacity, size_t slot_align) {
assert(IsValidCapacity(capacity));
const size_t num_control_bytes = capacity + 1 + NumClonedBytes();
return (num_control_bytes + slot_align - 1) & (~slot_align + 1);
}
// Returns the size of the allocated block. See also above comment.
inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) {
return SlotOffset(capacity, slot_align) + capacity * slot_size;
}
// 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).
@ -624,13 +711,6 @@ class raw_hash_set {
auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k));
auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k));
using Layout = absl::container_internal::Layout<ctrl_t, slot_type>;
static Layout MakeLayout(size_t capacity) {
assert(IsValidCapacity(capacity));
return Layout(capacity + Group::kWidth + 1, capacity);
}
using AllocTraits = absl::allocator_traits<allocator_type>;
using SlotAlloc = typename absl::allocator_traits<
allocator_type>::template rebind_alloc<slot_type>;
@ -733,7 +813,7 @@ class raw_hash_set {
ctrl_ += shift;
slot_ += shift;
}
if (ABSL_PREDICT_FALSE(*ctrl_ == kSentinel)) ctrl_ = nullptr;
if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr;
}
ctrl_t* ctrl_ = nullptr;
@ -814,7 +894,8 @@ class raw_hash_set {
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) {
: raw_hash_set(SelectBucketCountForIterRange(first, last, bucket_count),
hash, eq, alloc) {
insert(first, last);
}
@ -902,7 +983,8 @@ class raw_hash_set {
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));
SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_,
sizeof(slot_type));
emplace_at(target.offset, v);
infoz().RecordInsert(hash, target.probe_length);
}
@ -998,6 +1080,8 @@ class raw_hash_set {
// past that we simply deallocate the array.
if (capacity_ > 127) {
destroy_slots();
infoz().RecordClearedReservation();
} else if (capacity_) {
for (size_t i = 0; i != capacity_; ++i) {
if (IsFull(ctrl_[i])) {
@ -1005,7 +1089,7 @@ class raw_hash_set {
}
}
size_ = 0;
reset_ctrl();
ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type));
reset_growth_left();
}
assert(empty());
@ -1311,21 +1395,31 @@ class raw_hash_set {
if (n == 0 && size_ == 0) {
destroy_slots();
infoz().RecordStorageChanged(0, 0);
infoz().RecordClearedReservation();
return;
}
// bitor is a faster way of doing `max` here. We will round up to the next
// power-of-2-minus-1, so bitor is good enough.
auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size()));
// n == 0 unconditionally rehashes as per the standard.
if (n == 0 || m > capacity_) {
resize(m);
// This is after resize, to ensure that we have completed the allocation
// and have potentially sampled the hashtable.
infoz().RecordReservation(n);
}
}
void reserve(size_t n) {
size_t m = GrowthToLowerboundCapacity(n);
if (m > capacity_) {
if (n > size() + growth_left()) {
size_t m = GrowthToLowerboundCapacity(n);
resize(NormalizeCapacity(m));
// This is after resize, to ensure that we have completed the allocation
// and have potentially sampled the hashtable.
infoz().RecordReservation(n);
}
}
@ -1352,6 +1446,7 @@ class raw_hash_set {
void prefetch(const key_arg<K>& key) const {
(void)key;
#if defined(__GNUC__)
prefetch_heap_block();
auto seq = probe(ctrl_, hash_ref()(key), capacity_);
__builtin_prefetch(static_cast<const void*>(ctrl_ + seq.offset()));
__builtin_prefetch(static_cast<const void*>(slots_ + seq.offset()));
@ -1378,11 +1473,12 @@ class raw_hash_set {
}
if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end();
seq.next();
assert(seq.index() < capacity_ && "full table!");
assert(seq.index() <= capacity_ && "full table!");
}
}
template <class K = key_type>
iterator find(const key_arg<K>& key) {
prefetch_heap_block();
return find(key, hash_ref()(key));
}
@ -1392,6 +1488,7 @@ class raw_hash_set {
}
template <class K = key_type>
const_iterator find(const key_arg<K>& key) const {
prefetch_heap_block();
return find(key, hash_ref()(key));
}
@ -1526,7 +1623,8 @@ class raw_hash_set {
static_cast<size_t>(empty_after.TrailingZeros() +
empty_before.LeadingZeros()) < Group::kWidth;
set_ctrl(index, was_never_full ? kEmpty : kDeleted);
SetCtrl(index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted,
capacity_, ctrl_, slots_, sizeof(slot_type));
growth_left() += was_never_full;
infoz().RecordErase();
}
@ -1545,15 +1643,16 @@ class raw_hash_set {
// bound more carefully.
if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
slots_ == nullptr) {
infoz() = Sample();
infoz() = Sample(sizeof(slot_type));
}
auto layout = MakeLayout(capacity_);
char* mem = static_cast<char*>(
Allocate<Layout::Alignment()>(&alloc_ref(), layout.AllocSize()));
ctrl_ = reinterpret_cast<ctrl_t*>(layout.template Pointer<0>(mem));
slots_ = layout.template Pointer<1>(mem);
reset_ctrl();
char* mem = static_cast<char*>(Allocate<alignof(slot_type)>(
&alloc_ref(),
AllocSize(capacity_, sizeof(slot_type), alignof(slot_type))));
ctrl_ = reinterpret_cast<ctrl_t*>(mem);
slots_ = reinterpret_cast<slot_type*>(
mem + SlotOffset(capacity_, alignof(slot_type)));
ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type));
reset_growth_left();
infoz().RecordStorageChanged(size_, capacity_);
}
@ -1565,10 +1664,12 @@ class raw_hash_set {
PolicyTraits::destroy(&alloc_ref(), slots_ + i);
}
}
auto layout = MakeLayout(capacity_);
// Unpoison before returning the memory to the allocator.
SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
Deallocate<Layout::Alignment()>(&alloc_ref(), ctrl_, layout.AllocSize());
Deallocate<alignof(slot_type)>(
&alloc_ref(), ctrl_,
AllocSize(capacity_, sizeof(slot_type), alignof(slot_type)));
ctrl_ = EmptyGroup();
slots_ = nullptr;
size_ = 0;
@ -1592,16 +1693,16 @@ class raw_hash_set {
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));
SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i);
}
}
if (old_capacity) {
SanitizerUnpoisonMemoryRegion(old_slots,
sizeof(slot_type) * old_capacity);
auto layout = MakeLayout(old_capacity);
Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl,
layout.AllocSize());
Deallocate<alignof(slot_type)>(
&alloc_ref(), old_ctrl,
AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type)));
}
infoz().RecordRehash(total_probe_length);
}
@ -1631,35 +1732,35 @@ class raw_hash_set {
slot_type* slot = reinterpret_cast<slot_type*>(&raw);
for (size_t i = 0; i != capacity_; ++i) {
if (!IsDeleted(ctrl_[i])) continue;
size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
PolicyTraits::element(slots_ + i));
auto target = find_first_non_full(ctrl_, hash, capacity_);
size_t new_i = target.offset;
const size_t hash = PolicyTraits::apply(
HashElement{hash_ref()}, PolicyTraits::element(slots_ + i));
const FindInfo target = find_first_non_full(ctrl_, hash, capacity_);
const size_t new_i = target.offset;
total_probe_length += target.probe_length;
// Verify if the old and new i fall within the same group wrt the hash.
// If they do, we don't need to move the object as it falls already in the
// best probe we can.
const auto probe_index = [&](size_t pos) {
return ((pos - probe(ctrl_, hash, capacity_).offset()) & capacity_) /
Group::kWidth;
const size_t probe_offset = probe(ctrl_, hash, capacity_).offset();
const auto probe_index = [probe_offset, this](size_t pos) {
return ((pos - probe_offset) & capacity_) / Group::kWidth;
};
// Element doesn't move.
if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
set_ctrl(i, H2(hash));
SetCtrl(i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
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
// SetCtrl poisons/unpoisons the slots so we have to call it at the
// right time.
set_ctrl(new_i, H2(hash));
SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i);
set_ctrl(i, kEmpty);
SetCtrl(i, ctrl_t::kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type));
} else {
assert(IsDeleted(ctrl_[new_i]));
set_ctrl(new_i, H2(hash));
SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
// Until we are done rehashing, DELETED marks previously FULL slots.
// Swap i and new_i elements.
PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i);
@ -1675,8 +1776,50 @@ class raw_hash_set {
void rehash_and_grow_if_necessary() {
if (capacity_ == 0) {
resize(1);
} else if (size() <= CapacityToGrowth(capacity()) / 2) {
} else if (capacity_ > Group::kWidth &&
// Do these calcuations in 64-bit to avoid overflow.
size() * uint64_t{32} <= capacity_ * uint64_t{25}) {
// Squash DELETED without growing if there is enough capacity.
//
// Rehash in place if the current size is <= 25/32 of capacity_.
// Rationale for such a high factor: 1) drop_deletes_without_resize() is
// faster than resize, and 2) it takes quite a bit of work to add
// tombstones. In the worst case, seems to take approximately 4
// insert/erase pairs to create a single tombstone and so if we are
// rehashing because of tombstones, we can afford to rehash-in-place as
// long as we are reclaiming at least 1/8 the capacity without doing more
// than 2X the work. (Where "work" is defined to be size() for rehashing
// or rehashing in place, and 1 for an insert or erase.) But rehashing in
// place is faster per operation than inserting or even doubling the size
// of the table, so we actually afford to reclaim even less space from a
// resize-in-place. The decision is to rehash in place if we can reclaim
// at about 1/8th of the usable capacity (specifically 3/28 of the
// capacity) which means that the total cost of rehashing will be a small
// fraction of the total work.
//
// Here is output of an experiment using the BM_CacheInSteadyState
// benchmark running the old case (where we rehash-in-place only if we can
// reclaim at least 7/16*capacity_) vs. this code (which rehashes in place
// if we can recover 3/32*capacity_).
//
// Note that although in the worst-case number of rehashes jumped up from
// 15 to 190, but the number of operations per second is almost the same.
//
// Abridged output of running BM_CacheInSteadyState benchmark from
// raw_hash_set_benchmark. N is the number of insert/erase operations.
//
// | OLD (recover >= 7/16 | NEW (recover >= 3/32)
// size | N/s LoadFactor NRehashes | N/s LoadFactor NRehashes
// 448 | 145284 0.44 18 | 140118 0.44 19
// 493 | 152546 0.24 11 | 151417 0.48 28
// 538 | 151439 0.26 11 | 151152 0.53 38
// 583 | 151765 0.28 11 | 150572 0.57 50
// 628 | 150241 0.31 11 | 150853 0.61 66
// 672 | 149602 0.33 12 | 150110 0.66 90
// 717 | 149998 0.35 12 | 149531 0.70 129
// 762 | 149836 0.37 13 | 148559 0.74 190
// 807 | 149736 0.39 14 | 151107 0.39 14
// 852 | 150204 0.42 15 | 151019 0.42 15
drop_deletes_without_resize();
} else {
// Otherwise grow the container.
@ -1696,7 +1839,7 @@ class raw_hash_set {
}
if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false;
seq.next();
assert(seq.index() < capacity_ && "full table!");
assert(seq.index() <= capacity_ && "full table!");
}
return false;
}
@ -1716,6 +1859,7 @@ class raw_hash_set {
protected:
template <class K>
std::pair<size_t, bool> find_or_prepare_insert(const K& key) {
prefetch_heap_block();
auto hash = hash_ref()(key);
auto seq = probe(ctrl_, hash, capacity_);
while (true) {
@ -1728,7 +1872,7 @@ class raw_hash_set {
}
if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break;
seq.next();
assert(seq.index() < capacity_ && "full table!");
assert(seq.index() <= capacity_ && "full table!");
}
return {prepare_insert(hash), true};
}
@ -1742,7 +1886,8 @@ class raw_hash_set {
}
++size_;
growth_left() -= IsEmpty(ctrl_[target.offset]);
set_ctrl(target.offset, H2(hash));
SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_,
sizeof(slot_type));
infoz().RecordInsert(hash, target.probe_length);
return target.offset;
}
@ -1771,35 +1916,21 @@ class raw_hash_set {
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>(); }
void prefetch_heap_block() const {
// Prefetch the heap-allocated memory region to resolve potential TLB
// misses. This is intended to overlap with execution of calculating the
// hash for a key.
#if defined(__GNUC__)
__builtin_prefetch(static_cast<const void*>(ctrl_), 0, 1);
#endif // __GNUC__
}
HashtablezInfoHandle& infoz() { return settings_.template get<1>(); }
hasher& hash_ref() { return settings_.template get<2>(); }
@ -1814,10 +1945,10 @@ class raw_hash_set {
// 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
ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1 + NumClonedBytes()) * ctrl_t]
slot_type* slots_ = nullptr; // [capacity * slot_type]
size_t size_ = 0; // number of full slots
size_t capacity_ = 0; // total number of slots
absl::container_internal::CompressedTuple<size_t /* growth_left */,
HashtablezInfoHandle, hasher,
key_equal, allocator_type>
@ -1827,11 +1958,12 @@ class raw_hash_set {
// Erases all elements that satisfy the predicate `pred` from the container `c`.
template <typename P, typename H, typename E, typename A, typename Predicate>
void EraseIf(Predicate pred, raw_hash_set<P, H, E, A>* c) {
void EraseIf(Predicate& pred, raw_hash_set<P, H, E, A>* c) {
for (auto it = c->begin(), last = c->end(); it != last;) {
auto copy_it = it++;
if (pred(*copy_it)) {
c->erase(copy_it);
if (pred(*it)) {
c->erase(it++);
} else {
++it;
}
}
}
@ -1866,8 +1998,7 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
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 m = AllocSize(capacity, sizeof(Slot), alignof(Slot));
size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
if (per_slot != ~size_t{}) {
@ -1885,8 +2016,8 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
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 m =
AllocSize(NormalizeCapacity(capacity), sizeof(Slot), alignof(Slot));
size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
if (per_slot != ~size_t{}) {
m += per_slot * size;

View File

@ -254,6 +254,23 @@ void BM_CopyAssign(benchmark::State& state) {
}
BENCHMARK(BM_CopyAssign)->Range(128, 4096);
void BM_RangeCtor(benchmark::State& state) {
std::random_device rd;
std::mt19937 rng(rd());
std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{});
std::vector<int> values;
const size_t desired_size = state.range(0);
while (values.size() < desired_size) {
values.emplace_back(dist(rng));
}
for (auto unused : state) {
IntTable t{values.begin(), values.end()};
benchmark::DoNotOptimize(t);
}
}
BENCHMARK(BM_RangeCtor)->Range(128, 65536);
void BM_NoOpReserveIntTable(benchmark::State& state) {
IntTable t;
t.reserve(100000);
@ -298,9 +315,17 @@ void BM_ReserveStringTable(benchmark::State& state) {
}
BENCHMARK(BM_ReserveStringTable)->Range(128, 4096);
// Like std::iota, except that ctrl_t doesn't support operator++.
template <typename CtrlIter>
void Iota(CtrlIter begin, CtrlIter end, int value) {
for (; begin != end; ++begin, ++value) {
*begin = static_cast<ctrl_t>(value);
}
}
void BM_Group_Match(benchmark::State& state) {
std::array<ctrl_t, Group::kWidth> group;
std::iota(group.begin(), group.end(), -4);
Iota(group.begin(), group.end(), -4);
Group g{group.data()};
h2_t h = 1;
for (auto _ : state) {
@ -312,7 +337,7 @@ BENCHMARK(BM_Group_Match);
void BM_Group_MatchEmpty(benchmark::State& state) {
std::array<ctrl_t, Group::kWidth> group;
std::iota(group.begin(), group.end(), -4);
Iota(group.begin(), group.end(), -4);
Group g{group.data()};
for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmpty());
}
@ -320,7 +345,7 @@ BENCHMARK(BM_Group_MatchEmpty);
void BM_Group_MatchEmptyOrDeleted(benchmark::State& state) {
std::array<ctrl_t, Group::kWidth> group;
std::iota(group.begin(), group.end(), -4);
Iota(group.begin(), group.end(), -4);
Group g{group.data()};
for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmptyOrDeleted());
}
@ -328,7 +353,7 @@ BENCHMARK(BM_Group_MatchEmptyOrDeleted);
void BM_Group_CountLeadingEmptyOrDeleted(benchmark::State& state) {
std::array<ctrl_t, Group::kWidth> group;
std::iota(group.begin(), group.end(), -2);
Iota(group.begin(), group.end(), -2);
Group g{group.data()};
for (auto _ : state)
::benchmark::DoNotOptimize(g.CountLeadingEmptyOrDeleted());
@ -337,7 +362,7 @@ BENCHMARK(BM_Group_CountLeadingEmptyOrDeleted);
void BM_Group_MatchFirstEmptyOrDeleted(benchmark::State& state) {
std::array<ctrl_t, Group::kWidth> group;
std::iota(group.begin(), group.end(), -2);
Iota(group.begin(), group.end(), -2);
Group g{group.data()};
for (auto _ : state) ::benchmark::DoNotOptimize(*g.MatchEmptyOrDeleted());
}
@ -346,8 +371,11 @@ BENCHMARK(BM_Group_MatchFirstEmptyOrDeleted);
void BM_DropDeletes(benchmark::State& state) {
constexpr size_t capacity = (1 << 20) - 1;
std::vector<ctrl_t> ctrl(capacity + 1 + Group::kWidth);
ctrl[capacity] = kSentinel;
std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted};
ctrl[capacity] = ctrl_t::kSentinel;
std::vector<ctrl_t> pattern = {ctrl_t::kEmpty, static_cast<ctrl_t>(2),
ctrl_t::kDeleted, static_cast<ctrl_t>(2),
ctrl_t::kEmpty, static_cast<ctrl_t>(1),
ctrl_t::kDeleted};
for (size_t i = 0; i != capacity; ++i) {
ctrl[i] = pattern[i % pattern.size()];
}
@ -378,6 +406,12 @@ bool CodegenAbslRawHashSetInt64FindNeEnd(
return table->find(key) != table->end();
}
auto CodegenAbslRawHashSetInt64Insert(absl::container_internal::IntTable* table,
int64_t key)
-> decltype(table->insert(key)) {
return table->insert(key);
}
bool CodegenAbslRawHashSetInt64Contains(
absl::container_internal::IntTable* table, int64_t key) {
return table->contains(key);
@ -391,6 +425,7 @@ void CodegenAbslRawHashSetInt64Iterate(
int odr =
(::benchmark::DoNotOptimize(std::make_tuple(
&CodegenAbslRawHashSetInt64Find, &CodegenAbslRawHashSetInt64FindNeEnd,
&CodegenAbslRawHashSetInt64Insert,
&CodegenAbslRawHashSetInt64Contains,
&CodegenAbslRawHashSetInt64Iterate)),
1);

View File

@ -58,6 +58,9 @@ using ::testing::Lt;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
// Convenience function to static cast to ctrl_t.
ctrl_t CtrlT(int i) { return static_cast<ctrl_t>(i); }
TEST(Util, NormalizeCapacity) {
EXPECT_EQ(1, NormalizeCapacity(0));
EXPECT_EQ(1, NormalizeCapacity(1));
@ -170,15 +173,19 @@ TEST(Group, EmptyGroup) {
TEST(Group, Match) {
if (Group::kWidth == 16) {
ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
7, 5, 3, 1, 1, 1, 1, 1};
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
EXPECT_THAT(Group{group}.Match(0), ElementsAre());
EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
} else if (Group::kWidth == 8) {
ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
ctrl_t::kSentinel, CtrlT(1)};
EXPECT_THAT(Group{group}.Match(0), ElementsAre());
EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
@ -189,11 +196,15 @@ TEST(Group, Match) {
TEST(Group, MatchEmpty) {
if (Group::kWidth == 16) {
ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
7, 5, 3, 1, 1, 1, 1, 1};
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4));
} else if (Group::kWidth == 8) {
ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
ctrl_t::kSentinel, CtrlT(1)};
EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0));
} else {
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
@ -202,11 +213,15 @@ TEST(Group, MatchEmpty) {
TEST(Group, MatchEmptyOrDeleted) {
if (Group::kWidth == 16) {
ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
7, 5, 3, 1, 1, 1, 1, 1};
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4));
} else if (Group::kWidth == 8) {
ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
ctrl_t::kSentinel, CtrlT(1)};
EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3));
} else {
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
@ -217,28 +232,32 @@ TEST(Batch, DropDeletes) {
constexpr size_t kCapacity = 63;
constexpr size_t kGroupWidth = container_internal::Group::kWidth;
std::vector<ctrl_t> ctrl(kCapacity + 1 + kGroupWidth);
ctrl[kCapacity] = kSentinel;
std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted};
ctrl[kCapacity] = ctrl_t::kSentinel;
std::vector<ctrl_t> pattern = {
ctrl_t::kEmpty, CtrlT(2), ctrl_t::kDeleted, CtrlT(2),
ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted};
for (size_t i = 0; i != kCapacity; ++i) {
ctrl[i] = pattern[i % pattern.size()];
if (i < kGroupWidth - 1)
ctrl[i + kCapacity + 1] = pattern[i % pattern.size()];
}
ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity);
ASSERT_EQ(ctrl[kCapacity], kSentinel);
for (size_t i = 0; i < kCapacity + 1 + kGroupWidth; ++i) {
ASSERT_EQ(ctrl[kCapacity], ctrl_t::kSentinel);
for (size_t i = 0; i < kCapacity + kGroupWidth; ++i) {
ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()];
if (i == kCapacity) expected = kSentinel;
if (expected == kDeleted) expected = kEmpty;
if (IsFull(expected)) expected = kDeleted;
if (i == kCapacity) expected = ctrl_t::kSentinel;
if (expected == ctrl_t::kDeleted) expected = ctrl_t::kEmpty;
if (IsFull(expected)) expected = ctrl_t::kDeleted;
EXPECT_EQ(ctrl[i], expected)
<< i << " " << int{pattern[i % pattern.size()]};
<< i << " " << static_cast<int>(pattern[i % pattern.size()]);
}
}
TEST(Group, CountLeadingEmptyOrDeleted) {
const std::vector<ctrl_t> empty_examples = {kEmpty, kDeleted};
const std::vector<ctrl_t> full_examples = {0, 1, 2, 3, 5, 9, 127, kSentinel};
const std::vector<ctrl_t> empty_examples = {ctrl_t::kEmpty, ctrl_t::kDeleted};
const std::vector<ctrl_t> full_examples = {
CtrlT(0), CtrlT(1), CtrlT(2), CtrlT(3),
CtrlT(5), CtrlT(9), CtrlT(127), ctrl_t::kSentinel};
for (ctrl_t empty : empty_examples) {
std::vector<ctrl_t> e(Group::kWidth, empty);
@ -294,6 +313,7 @@ struct ValuePolicy {
};
using IntPolicy = ValuePolicy<int64_t>;
using Uint8Policy = ValuePolicy<uint8_t>;
class StringPolicy {
template <class F, class K, class V,
@ -374,6 +394,13 @@ struct IntTable
using Base::Base;
};
struct Uint8Table
: raw_hash_set<Uint8Policy, container_internal::hash_default_hash<uint8_t>,
std::equal_to<uint8_t>, std::allocator<uint8_t>> {
using Base = typename Uint8Table::raw_hash_set;
using Base::Base;
};
template <typename T>
struct CustomAlloc : std::allocator<T> {
CustomAlloc() {}
@ -541,6 +568,37 @@ TEST(Table, InsertCollisionAndFindAfterDelete) {
EXPECT_TRUE(t.empty());
}
TEST(Table, InsertWithinCapacity) {
IntTable t;
t.reserve(10);
const size_t original_capacity = t.capacity();
const auto addr = [&](int i) {
return reinterpret_cast<uintptr_t>(&*t.find(i));
};
// Inserting an element does not change capacity.
t.insert(0);
EXPECT_THAT(t.capacity(), original_capacity);
const uintptr_t original_addr_0 = addr(0);
// Inserting another element does not rehash.
t.insert(1);
EXPECT_THAT(t.capacity(), original_capacity);
EXPECT_THAT(addr(0), original_addr_0);
// Inserting lots of duplicate elements does not rehash.
for (int i = 0; i < 100; ++i) {
t.insert(i % 10);
}
EXPECT_THAT(t.capacity(), original_capacity);
EXPECT_THAT(addr(0), original_addr_0);
// Inserting a range of duplicate elements does not rehash.
std::vector<int> dup_range;
for (int i = 0; i < 100; ++i) {
dup_range.push_back(i % 10);
}
t.insert(dup_range.begin(), dup_range.end());
EXPECT_THAT(t.capacity(), original_capacity);
EXPECT_THAT(addr(0), original_addr_0);
}
TEST(Table, LazyEmplace) {
StringTable t;
bool called = false;
@ -588,28 +646,53 @@ TEST(Table, Contains2) {
}
int decompose_constructed;
int decompose_copy_constructed;
int decompose_copy_assigned;
int decompose_move_constructed;
int decompose_move_assigned;
struct DecomposeType {
DecomposeType(int i) : i(i) { // NOLINT
DecomposeType(int i = 0) : i(i) { // NOLINT
++decompose_constructed;
}
explicit DecomposeType(const char* d) : DecomposeType(*d) {}
DecomposeType(const DecomposeType& other) : i(other.i) {
++decompose_copy_constructed;
}
DecomposeType& operator=(const DecomposeType& other) {
++decompose_copy_assigned;
i = other.i;
return *this;
}
DecomposeType(DecomposeType&& other) : i(other.i) {
++decompose_move_constructed;
}
DecomposeType& operator=(DecomposeType&& other) {
++decompose_move_assigned;
i = other.i;
return *this;
}
int i;
};
struct DecomposeHash {
using is_transparent = void;
size_t operator()(DecomposeType a) const { return a.i; }
size_t operator()(const DecomposeType& a) const { return a.i; }
size_t operator()(int a) const { return a; }
size_t operator()(const char* a) const { return *a; }
};
struct DecomposeEq {
using is_transparent = void;
bool operator()(DecomposeType a, DecomposeType b) const { return a.i == b.i; }
bool operator()(DecomposeType a, int b) const { return a.i == b; }
bool operator()(DecomposeType a, const char* b) const { return a.i == *b; }
bool operator()(const DecomposeType& a, const DecomposeType& b) const {
return a.i == b.i;
}
bool operator()(const DecomposeType& a, int b) const { return a.i == b; }
bool operator()(const DecomposeType& a, const char* b) const {
return a.i == *b;
}
};
struct DecomposePolicy {
@ -619,9 +702,9 @@ struct DecomposePolicy {
template <typename T>
static void construct(void*, DecomposeType* slot, T&& v) {
*slot = DecomposeType(std::forward<T>(v));
::new (slot) DecomposeType(std::forward<T>(v));
}
static void destroy(void*, DecomposeType*) {}
static void destroy(void*, DecomposeType* slot) { slot->~DecomposeType(); }
static DecomposeType& element(slot_type* slot) { return *slot; }
template <class F, class T>
@ -636,8 +719,13 @@ void TestDecompose(bool construct_three) {
const int one = 1;
const char* three_p = "3";
const auto& three = three_p;
const int elem_vector_count = 256;
std::vector<DecomposeType> elem_vector(elem_vector_count, DecomposeType{0});
std::iota(elem_vector.begin(), elem_vector.end(), 0);
raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>> set1;
using DecomposeSet =
raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>>;
DecomposeSet set1;
decompose_constructed = 0;
int expected_constructed = 0;
@ -695,20 +783,72 @@ void TestDecompose(bool construct_three) {
expected_constructed += construct_three;
EXPECT_EQ(expected_constructed, decompose_constructed);
}
decompose_copy_constructed = 0;
decompose_copy_assigned = 0;
decompose_move_constructed = 0;
decompose_move_assigned = 0;
int expected_copy_constructed = 0;
int expected_move_constructed = 0;
{ // raw_hash_set(first, last) with random-access iterators
DecomposeSet set2(elem_vector.begin(), elem_vector.end());
// Expect exactly one copy-constructor call for each element if no
// rehashing is done.
expected_copy_constructed += elem_vector_count;
EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
EXPECT_EQ(expected_move_constructed, decompose_move_constructed);
EXPECT_EQ(0, decompose_move_assigned);
EXPECT_EQ(0, decompose_copy_assigned);
}
{ // raw_hash_set(first, last) with forward iterators
std::list<DecomposeType> elem_list(elem_vector.begin(), elem_vector.end());
expected_copy_constructed = decompose_copy_constructed;
DecomposeSet set2(elem_list.begin(), elem_list.end());
// Expect exactly N elements copied into set, expect at most 2*N elements
// moving internally for all resizing needed (for a growth factor of 2).
expected_copy_constructed += elem_vector_count;
EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
expected_move_constructed += elem_vector_count;
EXPECT_LT(expected_move_constructed, decompose_move_constructed);
expected_move_constructed += elem_vector_count;
EXPECT_GE(expected_move_constructed, decompose_move_constructed);
EXPECT_EQ(0, decompose_move_assigned);
EXPECT_EQ(0, decompose_copy_assigned);
expected_copy_constructed = decompose_copy_constructed;
expected_move_constructed = decompose_move_constructed;
}
{ // insert(first, last)
DecomposeSet set2;
set2.insert(elem_vector.begin(), elem_vector.end());
// Expect exactly N elements copied into set, expect at most 2*N elements
// moving internally for all resizing needed (for a growth factor of 2).
const int expected_new_elements = elem_vector_count;
const int expected_max_element_moves = 2 * elem_vector_count;
expected_copy_constructed += expected_new_elements;
EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
expected_move_constructed += expected_max_element_moves;
EXPECT_GE(expected_move_constructed, decompose_move_constructed);
EXPECT_EQ(0, decompose_move_assigned);
EXPECT_EQ(0, decompose_copy_assigned);
expected_copy_constructed = decompose_copy_constructed;
expected_move_constructed = decompose_move_constructed;
}
}
TEST(Table, Decompose) {
TestDecompose<DecomposeHash, DecomposeEq>(false);
struct TransparentHashIntOverload {
size_t operator()(DecomposeType a) const { return a.i; }
size_t operator()(const DecomposeType& a) const { return a.i; }
size_t operator()(int a) const { return a; }
};
struct TransparentEqIntOverload {
bool operator()(DecomposeType a, DecomposeType b) const {
bool operator()(const DecomposeType& a, const DecomposeType& b) const {
return a.i == b.i;
}
bool operator()(DecomposeType a, int b) const { return a.i == b; }
bool operator()(const DecomposeType& a, int b) const { return a.i == b; }
};
TestDecompose<TransparentHashIntOverload, DecomposeEq>(true);
TestDecompose<TransparentHashIntOverload, TransparentEqIntOverload>(true);
@ -750,7 +890,7 @@ TEST(Table, RehashWithNoResize) {
const size_t capacity = t.capacity();
// Remove elements from all groups except the first and the last one.
// All elements removed from full groups will be marked as kDeleted.
// All elements removed from full groups will be marked as ctrl_t::kDeleted.
const size_t erase_begin = Group::kWidth / 2;
const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth;
for (size_t i = erase_begin; i < erase_end; ++i) {
@ -1898,7 +2038,7 @@ TEST(RawHashSamplerTest, Sample) {
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
auto& sampler = HashtablezSampler::Global();
auto& sampler = GlobalHashtablezSampler();
size_t start_size = 0;
std::unordered_set<const HashtablezInfo*> preexisting_info;
start_size += sampler.Iterate([&](const HashtablezInfo& info) {
@ -1909,16 +2049,33 @@ TEST(RawHashSamplerTest, Sample) {
std::vector<IntTable> tables;
for (int i = 0; i < 1000000; ++i) {
tables.emplace_back();
const bool do_reserve = (i % 10 > 5);
const bool do_rehash = !do_reserve && (i % 10 > 0);
if (do_reserve) {
// Don't reserve on all tables.
tables.back().reserve(10 * (i % 10));
}
tables.back().insert(1);
tables.back().insert(i % 5);
if (do_rehash) {
// Rehash some other tables.
tables.back().rehash(10 * (i % 10));
}
}
size_t end_size = 0;
std::unordered_map<size_t, int> observed_checksums;
std::unordered_map<ssize_t, int> reservations;
end_size += sampler.Iterate([&](const HashtablezInfo& info) {
if (preexisting_info.count(&info) == 0) {
observed_checksums[info.hashes_bitwise_xor.load(
std::memory_order_relaxed)]++;
reservations[info.max_reserve.load(std::memory_order_relaxed)]++;
}
EXPECT_EQ(info.inline_element_size, sizeof(int64_t));
++end_size;
});
@ -1928,6 +2085,15 @@ TEST(RawHashSamplerTest, Sample) {
for (const auto& [_, count] : observed_checksums) {
EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.2, 0.05);
}
EXPECT_EQ(reservations.size(), 10);
for (const auto& [reservation, count] : reservations) {
EXPECT_GE(reservation, 0);
EXPECT_LT(reservation, 100);
EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.1, 0.05)
<< reservation;
}
}
#endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE
@ -1936,7 +2102,7 @@ TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
auto& sampler = HashtablezSampler::Global();
auto& sampler = GlobalHashtablezSampler();
size_t start_size = 0;
start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
@ -1978,6 +2144,36 @@ TEST(Sanitizer, PoisoningOnErase) {
}
#endif // ABSL_HAVE_ADDRESS_SANITIZER
TEST(Table, AlignOne) {
// We previously had a bug in which we were copying a control byte over the
// first slot when alignof(value_type) is 1. We test repeated
// insertions/erases and verify that the behavior is correct.
Uint8Table t;
std::unordered_set<uint8_t> verifier; // NOLINT
// Do repeated insertions/erases from the table.
for (int64_t i = 0; i < 100000; ++i) {
SCOPED_TRACE(i);
const uint8_t u = (i * -i) & 0xFF;
auto it = t.find(u);
auto verifier_it = verifier.find(u);
if (it == t.end()) {
ASSERT_EQ(verifier_it, verifier.end());
t.insert(u);
verifier.insert(u);
} else {
ASSERT_NE(verifier_it, verifier.end());
t.erase(it);
verifier.erase(verifier_it);
}
}
EXPECT_EQ(t.size(), verifier.size());
for (uint8_t u : t) {
EXPECT_EQ(verifier.count(u), 1);
}
}
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END

View File

@ -179,7 +179,7 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
A alloc(0);
std::vector<T> values;
std::generate_n(std::back_inserter(values), 10,
hash_internal::Generator<T>());
hash_internal::UniqueGenerator<T>());
TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc);
EXPECT_EQ(m.hash_function(), hasher);
EXPECT_EQ(m.key_eq(), equal);
@ -198,7 +198,7 @@ void InputIteratorBucketAllocTest(std::true_type) {
A alloc(0);
std::vector<T> values;
std::generate_n(std::back_inserter(values), 10,
hash_internal::Generator<T>());
hash_internal::UniqueGenerator<T>());
TypeParam m(values.begin(), values.end(), 123, alloc);
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
@ -221,7 +221,7 @@ void InputIteratorBucketHashAllocTest(std::true_type) {
A alloc(0);
std::vector<T> values;
std::generate_n(std::back_inserter(values), 10,
hash_internal::Generator<T>());
hash_internal::UniqueGenerator<T>());
TypeParam m(values.begin(), values.end(), 123, hasher, alloc);
EXPECT_EQ(m.hash_function(), hasher);
EXPECT_EQ(m.get_allocator(), alloc);
@ -241,8 +241,9 @@ TYPED_TEST_P(ConstructorTest, CopyConstructor) {
H hasher;
E equal;
A alloc(0);
hash_internal::UniqueGenerator<T> gen;
TypeParam m(123, hasher, equal, alloc);
for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
for (size_t i = 0; i != 10; ++i) m.insert(gen());
TypeParam n(m);
EXPECT_EQ(m.hash_function(), n.hash_function());
EXPECT_EQ(m.key_eq(), n.key_eq());
@ -262,8 +263,9 @@ void CopyConstructorAllocTest(std::true_type) {
H hasher;
E equal;
A alloc(0);
hash_internal::UniqueGenerator<T> gen;
TypeParam m(123, hasher, equal, alloc);
for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
for (size_t i = 0; i != 10; ++i) m.insert(gen());
TypeParam n(m, A(11));
EXPECT_EQ(m.hash_function(), n.hash_function());
EXPECT_EQ(m.key_eq(), n.key_eq());
@ -285,8 +287,9 @@ TYPED_TEST_P(ConstructorTest, MoveConstructor) {
H hasher;
E equal;
A alloc(0);
hash_internal::UniqueGenerator<T> gen;
TypeParam m(123, hasher, equal, alloc);
for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
for (size_t i = 0; i != 10; ++i) m.insert(gen());
TypeParam t(m);
TypeParam n(std::move(t));
EXPECT_EQ(m.hash_function(), n.hash_function());
@ -307,8 +310,9 @@ void MoveConstructorAllocTest(std::true_type) {
H hasher;
E equal;
A alloc(0);
hash_internal::UniqueGenerator<T> gen;
TypeParam m(123, hasher, equal, alloc);
for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
for (size_t i = 0; i != 10; ++i) m.insert(gen());
TypeParam t(m);
TypeParam n(std::move(t), A(1));
EXPECT_EQ(m.hash_function(), n.hash_function());
@ -325,7 +329,7 @@ TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) {
using T = hash_internal::GeneratedType<TypeParam>;
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
using H = typename TypeParam::hasher;
using E = typename TypeParam::key_equal;
@ -348,7 +352,7 @@ template <typename TypeParam>
void InitializerListBucketAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using A = typename TypeParam::allocator_type;
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
A alloc(0);
TypeParam m(values, 123, alloc);
@ -371,7 +375,7 @@ void InitializerListBucketHashAllocTest(std::true_type) {
using A = typename TypeParam::allocator_type;
H hasher;
A alloc(0);
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
TypeParam m(values, 123, hasher, alloc);
EXPECT_EQ(m.hash_function(), hasher);
@ -392,7 +396,7 @@ TYPED_TEST_P(ConstructorTest, Assignment) {
H hasher;
E equal;
A alloc(0);
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
TypeParam n;
n = m;
@ -412,7 +416,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignment) {
H hasher;
E equal;
A alloc(0);
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
TypeParam t(m);
TypeParam n;
@ -424,7 +428,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignment) {
TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
using T = hash_internal::GeneratedType<TypeParam>;
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
TypeParam m;
m = values;
@ -433,7 +437,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
using T = hash_internal::GeneratedType<TypeParam>;
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
TypeParam m({gen(), gen(), gen()});
TypeParam n({gen()});
n = m;
@ -442,7 +446,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
using T = hash_internal::GeneratedType<TypeParam>;
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
TypeParam m({gen(), gen(), gen()});
TypeParam t(m);
TypeParam n({gen()});
@ -452,7 +456,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
using T = hash_internal::GeneratedType<TypeParam>;
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
TypeParam m;
m = values;
@ -461,7 +465,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) {
using T = hash_internal::GeneratedType<TypeParam>;
hash_internal::Generator<T> gen;
hash_internal::UniqueGenerator<T> gen;
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
TypeParam m(values);
m = *&m; // Avoid -Wself-assign

View File

@ -81,6 +81,38 @@ TYPED_TEST_P(ModifiersTest, InsertRange) {
ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
}
TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) {
using T = hash_internal::GeneratedType<TypeParam>;
using V = typename TypeParam::mapped_type;
T val = hash_internal::Generator<T>()();
TypeParam m;
m.reserve(10);
const size_t original_capacity = m.bucket_count();
m.insert(val);
EXPECT_EQ(m.bucket_count(), original_capacity);
T val2 = {val.first, hash_internal::Generator<V>()()};
m.insert(val2);
EXPECT_EQ(m.bucket_count(), original_capacity);
}
TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) {
#if !defined(__GLIBCXX__)
using T = hash_internal::GeneratedType<TypeParam>;
std::vector<T> base_values;
std::generate_n(std::back_inserter(base_values), 10,
hash_internal::Generator<T>());
std::vector<T> values;
while (values.size() != 100) {
std::copy_n(base_values.begin(), 10, std::back_inserter(values));
}
TypeParam m;
m.reserve(10);
const size_t original_capacity = m.bucket_count();
m.insert(values.begin(), values.end());
EXPECT_EQ(m.bucket_count(), original_capacity);
#endif
}
TYPED_TEST_P(ModifiersTest, InsertOrAssign) {
#ifdef UNORDERED_MAP_CXX17
using std::get;
@ -266,9 +298,10 @@ TYPED_TEST_P(ModifiersTest, Swap) {
// TODO(alkis): Write tests for merge.
REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
InsertRange, InsertOrAssign, InsertOrAssignHint,
Emplace, EmplaceHint, TryEmplace, TryEmplaceHint,
Erase, EraseRange, EraseKey, Swap);
InsertRange, InsertWithinCapacity,
InsertRangeWithinCapacity, InsertOrAssign,
InsertOrAssignHint, Emplace, EmplaceHint, TryEmplace,
TryEmplaceHint, Erase, EraseRange, EraseKey, Swap);
template <typename Type>
struct is_unique_ptr : std::false_type {};

View File

@ -74,6 +74,36 @@ TYPED_TEST_P(ModifiersTest, InsertRange) {
ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
}
TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) {
using T = hash_internal::GeneratedType<TypeParam>;
T val = hash_internal::Generator<T>()();
TypeParam m;
m.reserve(10);
const size_t original_capacity = m.bucket_count();
m.insert(val);
EXPECT_EQ(m.bucket_count(), original_capacity);
m.insert(val);
EXPECT_EQ(m.bucket_count(), original_capacity);
}
TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) {
#if !defined(__GLIBCXX__)
using T = hash_internal::GeneratedType<TypeParam>;
std::vector<T> base_values;
std::generate_n(std::back_inserter(base_values), 10,
hash_internal::Generator<T>());
std::vector<T> values;
while (values.size() != 100) {
values.insert(values.end(), base_values.begin(), base_values.end());
}
TypeParam m;
m.reserve(10);
const size_t original_capacity = m.bucket_count();
m.insert(values.begin(), values.end());
EXPECT_EQ(m.bucket_count(), original_capacity);
#endif
}
TYPED_TEST_P(ModifiersTest, Emplace) {
using T = hash_internal::GeneratedType<TypeParam>;
T val = hash_internal::Generator<T>()();
@ -180,8 +210,9 @@ TYPED_TEST_P(ModifiersTest, Swap) {
// TODO(alkis): Write tests for merge.
REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
InsertRange, Emplace, EmplaceHint, Erase, EraseRange,
EraseKey, Swap);
InsertRange, InsertWithinCapacity,
InsertRangeWithinCapacity, Emplace, EmplaceHint,
Erase, EraseRange, EraseKey, Swap);
} // namespace container_internal
ABSL_NAMESPACE_END

View File

@ -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.
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/container/node_hash_map.h"
#include "absl/container/node_hash_set.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
// Create some tables of type `Table`, then look at all the new
// `HashtablezInfo`s to make sure that the `inline_element_size ==
// expected_element_size`. The `inline_element_size` is the amount of memory
// allocated for each slot of a hash table, that is `sizeof(slot_type)`. Add
// the new `HashtablezInfo`s to `preexisting_info`. Store all the new tables
// into `tables`.
template <class Table>
void TestInlineElementSize(
HashtablezSampler& sampler,
// clang-tidy gives a false positive on this declaration. This unordered
// set cannot be flat_hash_set, however, since that would introduce a mutex
// deadlock.
std::unordered_set<const HashtablezInfo*>& preexisting_info, // NOLINT
std::vector<Table>& tables, const typename Table::value_type& elt,
size_t expected_element_size) {
for (int i = 0; i < 10; ++i) {
// We create a new table and must store it somewhere so that when we store
// a pointer to the resulting `HashtablezInfo` into `preexisting_info`
// that we aren't storing a dangling pointer.
tables.emplace_back();
// We must insert an element to get a hashtablez to instantiate.
tables.back().insert(elt);
}
size_t new_count = 0;
sampler.Iterate([&](const HashtablezInfo& info) {
if (preexisting_info.insert(&info).second) {
EXPECT_EQ(info.inline_element_size, expected_element_size);
++new_count;
}
});
// Make sure we actually did get a new hashtablez.
EXPECT_GT(new_count, 0);
}
struct bigstruct {
char a[1000];
friend bool operator==(const bigstruct& x, const bigstruct& y) {
return memcmp(x.a, y.a, sizeof(x.a)) == 0;
}
template <typename H>
friend H AbslHashValue(H h, const bigstruct& c) {
return H::combine_contiguous(std::move(h), c.a, sizeof(c.a));
}
};
#endif
TEST(FlatHashMap, SampleElementSize) {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
// Enable sampling even if the prod default is off.
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(1);
auto& sampler = GlobalHashtablezSampler();
std::vector<flat_hash_map<int, bigstruct>> flat_map_tables;
std::vector<flat_hash_set<bigstruct>> flat_set_tables;
std::vector<node_hash_map<int, bigstruct>> node_map_tables;
std::vector<node_hash_set<bigstruct>> node_set_tables;
// It takes thousands of new tables after changing the sampling parameters
// before you actually get some instrumentation. And if you must actually
// put something into those tables.
for (int i = 0; i < 10000; ++i) {
flat_map_tables.emplace_back();
flat_map_tables.back()[i] = bigstruct{};
}
// clang-tidy gives a false positive on this declaration. This unordered set
// cannot be a flat_hash_set, however, since that would introduce a mutex
// deadlock.
std::unordered_set<const HashtablezInfo*> preexisting_info; // NOLINT
sampler.Iterate(
[&](const HashtablezInfo& info) { preexisting_info.insert(&info); });
TestInlineElementSize(sampler, preexisting_info, flat_map_tables,
{0, bigstruct{}}, sizeof(int) + sizeof(bigstruct));
TestInlineElementSize(sampler, preexisting_info, node_map_tables,
{0, bigstruct{}}, sizeof(void*));
TestInlineElementSize(sampler, preexisting_info, flat_set_tables, //
bigstruct{}, sizeof(bigstruct));
TestInlineElementSize(sampler, preexisting_info, node_set_tables, //
bigstruct{}, sizeof(void*));
#endif
}
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END
} // namespace absl

View File

@ -71,12 +71,13 @@ list(APPEND ABSL_LLVM_FLAGS
"-Wformat-security"
"-Wgnu-redeclared-enum"
"-Winfinite-recursion"
"-Winvalid-constexpr"
"-Wliteral-conversion"
"-Wmissing-declarations"
"-Woverlength-strings"
"-Wpointer-arith"
"-Wself-assign"
"-Wshadow"
"-Wshadow-all"
"-Wstring-conversion"
"-Wtautological-overlap-compare"
"-Wundef"
@ -93,6 +94,7 @@ list(APPEND ABSL_LLVM_FLAGS
"-Wno-implicit-int-conversion"
"-Wno-shorten-64-to-32"
"-Wno-sign-conversion"
"-Wno-unknown-warning-option"
"-DNOMINMAX"
)

View File

@ -72,12 +72,13 @@ ABSL_LLVM_FLAGS = [
"-Wformat-security",
"-Wgnu-redeclared-enum",
"-Winfinite-recursion",
"-Winvalid-constexpr",
"-Wliteral-conversion",
"-Wmissing-declarations",
"-Woverlength-strings",
"-Wpointer-arith",
"-Wself-assign",
"-Wshadow",
"-Wshadow-all",
"-Wstring-conversion",
"-Wtautological-overlap-compare",
"-Wundef",
@ -94,6 +95,7 @@ ABSL_LLVM_FLAGS = [
"-Wno-implicit-int-conversion",
"-Wno-shorten-64-to-32",
"-Wno-sign-conversion",
"-Wno-unknown-warning-option",
"-DNOMINMAX",
]

View File

@ -50,6 +50,7 @@ ABSL_RANDOM_RANDEN_COPTS = select({
":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,
":cpu_k8": ABSL_RANDOM_HWAES_X64_FLAGS,
":cpu_ppc": ["-mcrypto"],
":cpu_aarch64": ABSL_RANDOM_HWAES_ARM64_FLAGS,
# Supported by default or unsupported.
"//conditions:default": [],
@ -70,6 +71,7 @@ def absl_random_randen_copts_init():
"darwin",
"x64_windows_msvc",
"x64_windows",
"aarch64",
]
for cpu in cpu_configs:
native.config_setting(

View File

@ -87,12 +87,13 @@ COPT_VARS = {
"-Wformat-security",
"-Wgnu-redeclared-enum",
"-Winfinite-recursion",
"-Winvalid-constexpr",
"-Wliteral-conversion",
"-Wmissing-declarations",
"-Woverlength-strings",
"-Wpointer-arith",
"-Wself-assign",
"-Wshadow",
"-Wshadow-all",
"-Wstring-conversion",
"-Wtautological-overlap-compare",
"-Wundef",
@ -111,6 +112,9 @@ COPT_VARS = {
"-Wno-implicit-int-conversion",
"-Wno-shorten-64-to-32",
"-Wno-sign-conversion",
# Disable warnings on unknown warning flags (when warning flags are
# unknown on older compiler versions)
"-Wno-unknown-warning-option",
# Don't define min and max macros (Build on Windows using clang)
"-DNOMINMAX",
],

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python3
"""Generate Abseil compile compile option configs.
Usage: <path_to_absl>/copts/generate_copts.py

View File

@ -14,7 +14,6 @@
# limitations under the License.
#
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@ -34,8 +33,10 @@ cc_library(
"internal/stacktrace_aarch64-inl.inc",
"internal/stacktrace_arm-inl.inc",
"internal/stacktrace_config.h",
"internal/stacktrace_emscripten-inl.inc",
"internal/stacktrace_generic-inl.inc",
"internal/stacktrace_powerpc-inl.inc",
"internal/stacktrace_riscv-inl.inc",
"internal/stacktrace_unimplemented-inl.inc",
"internal/stacktrace_win32-inl.inc",
"internal/stacktrace_x86-inl.inc",
@ -57,6 +58,7 @@ cc_library(
"symbolize.cc",
"symbolize_darwin.inc",
"symbolize_elf.inc",
"symbolize_emscripten.inc",
"symbolize_unimplemented.inc",
"symbolize_win32.inc",
],
@ -180,6 +182,7 @@ cc_library(
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
"//absl/base:config",
"//absl/base:core_headers",
@ -194,6 +197,7 @@ cc_library(
srcs = ["internal/demangle.cc"],
hdrs = ["internal/demangle.h"],
copts = ABSL_DEFAULT_COPTS,
visibility = ["//visibility:private"],
deps = [
"//absl/base",
"//absl/base:config",
@ -344,6 +348,7 @@ cc_test(
srcs = ["internal/stack_consumption_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
tags = ["notsan"],
deps = [
":stack_consumption",
"//absl/base:core_headers",

View File

@ -22,8 +22,10 @@ absl_cc_library(
"internal/stacktrace_aarch64-inl.inc"
"internal/stacktrace_arm-inl.inc"
"internal/stacktrace_config.h"
"internal/stacktrace_emscripten-inl.inc"
"internal/stacktrace_generic-inl.inc"
"internal/stacktrace_powerpc-inl.inc"
"internal/stacktrace_riscv-inl.inc"
"internal/stacktrace_unimplemented-inl.inc"
"internal/stacktrace_win32-inl.inc"
"internal/stacktrace_x86-inl.inc"
@ -48,6 +50,7 @@ absl_cc_library(
"symbolize.cc"
"symbolize_darwin.inc"
"symbolize_elf.inc"
"symbolize_emscripten.inc"
"symbolize_unimplemented.inc"
"symbolize_win32.inc"
COPTS
@ -87,7 +90,7 @@ absl_cc_test(
absl::memory
absl::raw_logging_internal
absl::strings
gmock
GTest::gmock
)
absl_cc_library(
@ -141,7 +144,7 @@ absl_cc_test(
absl::strings
absl::raw_logging_internal
Threads::Threads
gmock
GTest::gmock
)
absl_cc_library(
@ -194,7 +197,7 @@ absl_cc_test(
absl::core_headers
absl::memory
absl::raw_logging_internal
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -261,7 +264,7 @@ absl_cc_test(
DEPS
absl::leak_check_api_enabled_for_testing
absl::base
gmock_main
GTest::gmock_main
)
absl_cc_test(
@ -275,7 +278,7 @@ absl_cc_test(
DEPS
absl::leak_check_api_disabled_for_testing
absl::base
gmock_main
GTest::gmock_main
)
absl_cc_test(
@ -292,7 +295,7 @@ absl_cc_test(
absl::leak_check_disable
absl::base
absl::raw_logging_internal
gmock_main
GTest::gmock_main
)
absl_cc_library(
@ -322,7 +325,7 @@ absl_cc_test(
absl::stack_consumption
absl::core_headers
absl::raw_logging_internal
gmock_main
GTest::gmock_main
)
# component target

View File

@ -136,7 +136,8 @@ static bool SetupAlternateStackOnce() {
#else
const size_t page_mask = sysconf(_SC_PAGESIZE) - 1;
#endif
size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
size_t stack_size =
(std::max<size_t>(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.
@ -366,6 +367,7 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) {
// goes after this point.
if (fsh_options.writerfn != nullptr) {
WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn);
fsh_options.writerfn(nullptr);
}
if (fsh_options.call_previous_handler) {

View File

@ -90,7 +90,7 @@ struct FailureSignalHandlerOptions {
// 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
// as a log file. This function will also be called with null data, as a hint
// to flush any buffered data before the program may be terminated. Consider
// flushing any buffered data in all calls to this function.
//

View File

@ -1617,6 +1617,7 @@ static bool ParseUnresolvedName(State *state) {
// ::= <2-ary operator-name> <expression> <expression>
// ::= <3-ary operator-name> <expression> <expression> <expression>
// ::= cl <expression>+ E
// ::= cp <simple-id> <expression>* E # Clang-specific.
// ::= cv <type> <expression> # type (expression)
// ::= cv <type> _ <expression>* E # type (expr-list)
// ::= st <type>
@ -1639,14 +1640,23 @@ static bool ParseExpression(State *state) {
return true;
}
// Object/function call expression.
ParseState copy = state->parse_state;
// Object/function call expression.
if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) &&
ParseOneCharToken(state, 'E')) {
return true;
}
state->parse_state = copy;
// Clang-specific "cp <simple-id> <expression>* E"
// https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338
if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) &&
ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) {
return true;
}
state->parse_state = copy;
// Function-param expression (level 0).
if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) &&
Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {

View File

@ -22,6 +22,7 @@
#include <string.h>
#include <cassert>
#include <cstddef>
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
// From binutils/include/elf/common.h (this doesn't appear to be documented
@ -43,11 +44,11 @@ namespace debugging_internal {
namespace {
#if __WORDSIZE == 32
#if __SIZEOF_POINTER__ == 4
const int kElfClass = ELFCLASS32;
int ElfBind(const ElfW(Sym) *symbol) { return ELF32_ST_BIND(symbol->st_info); }
int ElfType(const ElfW(Sym) *symbol) { return ELF32_ST_TYPE(symbol->st_info); }
#elif __WORDSIZE == 64
#elif __SIZEOF_POINTER__ == 8
const int kElfClass = ELFCLASS64;
int ElfBind(const ElfW(Sym) *symbol) { return ELF64_ST_BIND(symbol->st_info); }
int ElfType(const ElfW(Sym) *symbol) { return ELF64_ST_TYPE(symbol->st_info); }
@ -175,17 +176,17 @@ void ElfMemImage::Init(const void *base) {
}
switch (base_as_char[EI_DATA]) {
case ELFDATA2LSB: {
if (__LITTLE_ENDIAN != __BYTE_ORDER) {
assert(false);
return;
}
#ifndef ABSL_IS_LITTLE_ENDIAN
assert(false);
return;
#endif
break;
}
case ELFDATA2MSB: {
if (__BIG_ENDIAN != __BYTE_ORDER) {
assert(false);
return;
}
#ifndef ABSL_IS_BIG_ENDIAN
assert(false);
return;
#endif
break;
}
default: {
@ -221,7 +222,7 @@ void ElfMemImage::Init(const void *base) {
reinterpret_cast<ElfW(Dyn) *>(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;
const auto value = dynamic_entry->d_un.d_val + relocation;
switch (dynamic_entry->d_tag) {
case DT_HASH:
hash_ = reinterpret_cast<ElfW(Word) *>(value);

View File

@ -31,8 +31,8 @@
#error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set
#endif
#if defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \
!defined(__asmjs__) && !defined(__wasm__)
#if defined(__ELF__) && !defined(__native_client__) && !defined(__asmjs__) && \
!defined(__wasm__)
#define ABSL_HAVE_ELF_MEM_IMAGE 1
#endif
@ -40,6 +40,10 @@
#include <link.h> // for ElfW
#if defined(__FreeBSD__) && !defined(ElfW)
#define ElfW(x) __ElfN(x)
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {

View File

@ -43,7 +43,7 @@ namespace {
// unspecified. Therefore, instead we hardcode the direction of the
// stack on platforms we know about.
#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
defined(__aarch64__)
defined(__aarch64__) || defined(__riscv)
constexpr bool kStackGrowsDown = true;
#else
#error Need to define kStackGrowsDown

View File

@ -26,7 +26,7 @@
#error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly
#elif !defined(__APPLE__) && !defined(_WIN32) && \
(defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
defined(__aarch64__))
defined(__aarch64__) || defined(__riscv))
#define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1
namespace absl {

View File

@ -35,7 +35,11 @@
// Thread local support required for UnwindImpl.
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"
#endif
#endif // defined(ABSL_HAVE_THREAD_LOCAL)
#elif defined(__EMSCRIPTEN__)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_emscripten-inl.inc"
#elif defined(__linux__) && !defined(__ANDROID__)
@ -51,7 +55,7 @@
// 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 // __has_include(<execinfo.h>)
#elif defined(__i386__) || defined(__x86_64__)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_x86-inl.inc"
@ -61,15 +65,18 @@
#elif defined(__aarch64__)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_aarch64-inl.inc"
#elif defined(__riscv)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_riscv-inl.inc"
#elif defined(__has_include)
#if __has_include(<execinfo.h>)
// 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 // __has_include(<execinfo.h>)
#endif // defined(__has_include)
#endif
#endif // defined(__linux__) && !defined(__ANDROID__)
// Fallback to the empty implementation.
#if !defined(ABSL_STACKTRACE_INL_HEADER)

View File

@ -0,0 +1,110 @@
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Portable implementation - just use glibc
//
// Note: The glibc implementation may cause a call to malloc.
// This can cause a deadlock in HeapProfiler.
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
#include <emscripten.h>
#include <atomic>
#include <cstring>
#include "absl/base/attributes.h"
#include "absl/debugging/stacktrace.h"
extern "C" {
uintptr_t emscripten_stack_snapshot();
uint32_t emscripten_stack_unwind_buffer(uintptr_t pc, void *buffer,
uint32_t depth);
}
// Sometimes, we can try to get a stack trace from within a stack
// trace, which can cause a self-deadlock.
// Protect against such reentrant call by failing to get a stack trace.
//
// We use __thread here because the code here is extremely low level -- it is
// called while collecting stack traces from within malloc and mmap, and thus
// can not call anything which might call malloc or mmap itself.
static __thread int recursive = 0;
// The stack trace function might be invoked very early in the program's
// execution (e.g. from the very first malloc).
// As such, we suppress usage of backtrace during this early stage of execution.
static std::atomic<bool> disable_stacktraces(true); // Disabled until healthy.
// Waiting until static initializers run seems to be late enough.
// This file is included into stacktrace.cc so this will only run once.
ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() {
// Check if we can even create stacktraces. If not, bail early and leave
// disable_stacktraces set as-is.
// clang-format off
if (!EM_ASM_INT({ return (typeof wasmOffsetConverter !== 'undefined'); })) {
return 0;
}
// clang-format on
disable_stacktraces.store(false, std::memory_order_relaxed);
return 0;
}();
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
const void *ucp, int *min_dropped_frames) {
if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) {
return 0;
}
++recursive;
static_cast<void>(ucp); // Unused.
constexpr int kStackLength = 64;
void *stack[kStackLength];
int size;
uintptr_t pc = emscripten_stack_snapshot();
size = emscripten_stack_unwind_buffer(pc, stack, kStackLength);
int result_count = size - skip_count;
if (result_count < 0) result_count = 0;
if (result_count > max_depth) result_count = max_depth;
for (int i = 0; i < result_count; i++) result[i] = stack[i + skip_count];
if (IS_STACK_FRAMES) {
// No implementation for finding out the stack frame sizes yet.
memset(sizes, 0, sizeof(*sizes) * result_count);
}
if (min_dropped_frames != nullptr) {
if (size - skip_count - max_depth > 0) {
*min_dropped_frames = size - skip_count - max_depth;
} else {
*min_dropped_frames = 0;
}
}
--recursive;
return result_count;
}
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
bool StackTraceWorksForTest() { return true; }
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_

View File

@ -0,0 +1,234 @@
// Copyright 2021 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
// Generate stack trace for riscv
#include <sys/ucontext.h>
#include "absl/base/config.h"
#if defined(__linux__)
#include <sys/mman.h>
#include <ucontext.h>
#include <unistd.h>
#endif
#include <atomic>
#include <cassert>
#include <cstdint>
#include <iostream>
#include "absl/base/attributes.h"
#include "absl/debugging/internal/address_is_readable.h"
#include "absl/debugging/internal/vdso_support.h"
#include "absl/debugging/stacktrace.h"
static const uintptr_t kUnknownFrameSize = 0;
#if defined(__linux__)
// Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
static const unsigned char *GetKernelRtSigreturnAddress() {
constexpr uintptr_t kImpossibleAddress = 0;
ABSL_CONST_INIT static std::atomic<uintptr_t> memoized(kImpossibleAddress);
uintptr_t address = memoized.load(std::memory_order_relaxed);
if (address != kImpossibleAddress) {
return reinterpret_cast<const unsigned char *>(address);
}
address = reinterpret_cast<uintptr_t>(nullptr);
#if ABSL_HAVE_VDSO_SUPPORT
absl::debugging_internal::VDSOSupport vdso;
if (vdso.IsPresent()) {
absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info;
// Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10.
auto lookup = [&](int type) {
return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_4.15", type,
&symbol_info);
};
if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) ||
symbol_info.address == nullptr) {
// Unexpected: VDSO is present, yet the expected symbol is missing or
// null.
assert(false && "VDSO is present, but doesn't have expected symbol");
} else {
if (reinterpret_cast<uintptr_t>(symbol_info.address) !=
kImpossibleAddress) {
address = reinterpret_cast<uintptr_t>(symbol_info.address);
} else {
assert(false && "VDSO returned invalid address");
}
}
}
#endif
memoized.store(address, std::memory_order_relaxed);
return reinterpret_cast<const unsigned char *>(address);
}
#endif // __linux__
// Compute the size of a stack frame in [low..high). We assume that low < high.
// Return size of kUnknownFrameSize.
template <typename T>
static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) {
const char *low_char_ptr = reinterpret_cast<const char *>(low);
const char *high_char_ptr = reinterpret_cast<const char *>(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 <bool STRICT_UNWINDING, bool WITH_CONTEXT>
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
static void ** NextStackFrame(void **old_frame_pointer, const void *uc) {
// .
// .
// .
// +-> +----------------+
// | | return address |
// | | previous fp |
// | | ... |
// | +----------------+ <-+
// | | return address | |
// +---|- previous fp | |
// | ... | |
// $fp ->|----------------+ |
// | return address | |
// | previous fp -|---+
// $sp ->| ... |
// +----------------+
void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]);
bool check_frame_size = true;
#if defined(__linux__)
if (WITH_CONTEXT && uc != nullptr) {
// Check to see if next frame's return address is __kernel_rt_sigreturn.
if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) {
const ucontext_t *ucv = static_cast<const ucontext_t *>(uc);
// old_frame_pointer is not suitable for unwinding, look at ucontext to
// discover frame pointer before signal.
//
// RISCV ELF psABI has the frame pointer at x8/fp/s0.
// -- RISCV psABI Table 18.2
void **const pre_signal_frame_pointer =
reinterpret_cast<void **>(ucv->uc_mcontext.__gregs[8]);
// Check the alleged frame pointer is actually readable. This is to
// prevent "double fault" in case we hit the first fault due to stack
// corruption.
if (!absl::debugging_internal::AddressIsReadable(
pre_signal_frame_pointer))
return nullptr;
// Alleged frame pointer is readable, use it for further unwinding.
new_frame_pointer = pre_signal_frame_pointer;
// Skip frame size check if we return from a signal. We may be using an
// alterate stack for signals.
check_frame_size = false;
}
}
#endif
// The RISCV ELF psABI mandates that the stack pointer is always 16-byte
// aligned.
// FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte
// alignment.
if ((reinterpret_cast<uintptr_t>(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 <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
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) {
#if defined(__GNUC__)
void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0));
#else
#error reading stack pointer not yet supported on this platform
#endif
skip_count++; // Skip the frame for this function.
int n = 0;
// The `frame_pointer` that is computed here points to the top of the frame.
// The two words preceding the address are the return address and the previous
// frame pointer. To find a PC value associated with the current frame, we
// need to go down a level in the call chain. So we remember the return
// 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 si 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<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(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<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(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

View File

@ -27,6 +27,7 @@
#include <cassert>
#include <cstdint>
#include <limits>
#include "absl/base/macros.h"
#include "absl/base/port.h"
@ -132,9 +133,8 @@ static uintptr_t GetFP(const void *vuc) {
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
// Sanity-check that the base pointer is valid. It's possible that some
// code in the process is compiled with --copt=-fomit-frame-pointer or
// --copt=-momit-leaf-frame-pointer.
//
// TODO(bcmills): -momit-leaf-frame-pointer is currently the default
@ -159,7 +159,8 @@ static uintptr_t GetFP(const void *vuc) {
template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
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) {
static void **NextStackFrame(void **old_fp, const void *uc,
size_t stack_low, size_t stack_high) {
void **new_fp = (void **)*old_fp;
#if defined(__linux__) && defined(__i386__)
@ -247,7 +248,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) {
// 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
// ENABLE_COMBINED_UNWINDER is set (because we should be back in the thread's
// stack by this point), but it is empirically still needed (e.g. when the
// stack includes a call to abort). unw_get_reg returns UNW_EBADREG for some
// frames. Figure out why GetValidFrameAddr and/or libunwind isn't doing what
@ -258,6 +259,18 @@ static void **NextStackFrame(void **old_fp, const void *uc) {
// at a greater address that the current one.
if (new_fp_u <= old_fp_u) return nullptr;
if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr;
if (stack_low < old_fp_u && old_fp_u <= stack_high) {
// Old BP was in the expected stack region...
if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
// ... but new BP is outside of expected stack region.
// It is most likely bogus.
return nullptr;
}
} else {
// We may be here if we are executing in a co-routine with a
// separate stack. We can't do safety checks in this case.
}
} else {
if (new_fp == nullptr) return nullptr; // skip AddressIsReadable() below
// In the non-strict mode, allow discontiguous stack frames.
@ -297,13 +310,17 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
int n = 0;
void **fp = reinterpret_cast<void **>(__builtin_frame_address(0));
size_t stack_low = getpagesize(); // Assume that the first page is not stack.
size_t stack_high = std::numeric_limits<size_t>::max() - sizeof(void *);
while (fp && n < max_depth) {
if (*(fp + 1) == reinterpret_cast<void *>(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<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
fp, ucp, stack_low, stack_high);
if (skip_count > 0) {
skip_count--;
} else {
@ -326,7 +343,8 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
const int kMaxUnwind = 1000;
int j = 0;
for (; fp != nullptr && j < kMaxUnwind; j++) {
fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp, stack_low,
stack_high);
}
*min_dropped_frames = j;
}

View File

@ -28,8 +28,8 @@
#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__)
#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) \
&& !defined(__asmjs__) && !defined(__wasm__)
#define ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE 1
#include <elf.h>
@ -68,6 +68,12 @@ ABSL_NAMESPACE_END
#define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1
#endif
#ifdef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE
#error ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE cannot be directly set
#elif defined(__EMSCRIPTEN__)
#define ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE 1
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {

View File

@ -20,12 +20,25 @@
#ifdef ABSL_HAVE_VDSO_SUPPORT // defined in vdso_support.h
#if !defined(__has_include)
#define __has_include(header) 0
#endif
#include <errno.h>
#include <fcntl.h>
#if __has_include(<syscall.h>)
#include <syscall.h>
#elif __has_include(<sys/syscall.h>)
#include <sys/syscall.h>
#endif
#include <unistd.h>
#if __GLIBC_PREREQ(2, 16) // GLIBC-2.16 implements getauxval.
#if defined(__GLIBC__) && \
(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16))
#define ABSL_HAVE_GETAUXVAL
#endif
#ifdef ABSL_HAVE_GETAUXVAL
#include <sys/auxv.h>
#endif
@ -37,6 +50,11 @@
#define AT_SYSINFO_EHDR 33 // for crosstoolv10
#endif
#if defined(__FreeBSD__)
using Elf64_auxv_t = Elf64_Auxinfo;
using Elf32_auxv_t = Elf32_Auxinfo;
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
@ -65,7 +83,7 @@ VDSOSupport::VDSOSupport()
// the operation should be idempotent.
const void *VDSOSupport::Init() {
const auto kInvalidBase = debugging_internal::ElfMemImage::kInvalidBase;
#if __GLIBC_PREREQ(2, 16)
#ifdef ABSL_HAVE_GETAUXVAL
if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
errno = 0;
const void *const sysinfo_ehdr =
@ -74,7 +92,7 @@ const void *VDSOSupport::Init() {
vdso_base_.store(sysinfo_ehdr, std::memory_order_relaxed);
}
}
#endif // __GLIBC_PREREQ(2, 16)
#endif // ABSL_HAVE_GETAUXVAL
if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
int fd = open("/proc/self/auxv", O_RDONLY);
if (fd == -1) {

View File

@ -49,8 +49,10 @@
# include "absl/debugging/internal/stacktrace_aarch64-inl.inc"
# include "absl/debugging/internal/stacktrace_arm-inl.inc"
# include "absl/debugging/internal/stacktrace_emscripten-inl.inc"
# include "absl/debugging/internal/stacktrace_generic-inl.inc"
# include "absl/debugging/internal/stacktrace_powerpc-inl.inc"
# include "absl/debugging/internal/stacktrace_riscv-inl.inc"
# include "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
# include "absl/debugging/internal/stacktrace_win32-inl.inc"
# include "absl/debugging/internal/stacktrace_x86-inl.inc"

View File

@ -31,6 +31,8 @@
#include "absl/debugging/symbolize_win32.inc"
#elif defined(__APPLE__)
#include "absl/debugging/symbolize_darwin.inc"
#elif defined(__EMSCRIPTEN__)
#include "absl/debugging/symbolize_emscripten.inc"
#else
#include "absl/debugging/symbolize_unimplemented.inc"
#endif

View File

@ -77,6 +77,10 @@
#include "absl/debugging/internal/vdso_support.h"
#include "absl/strings/string_view.h"
#if defined(__FreeBSD__) && !defined(ElfW)
#define ElfW(x) __ElfN(x)
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
@ -701,6 +705,16 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
const char *start_address =
ComputeOffset(original_start_address, relocation);
#ifdef __arm__
// ARM functions are always aligned to multiples of two bytes; the
// lowest-order bit in start_address is ignored by the CPU and indicates
// whether the function contains ARM (0) or Thumb (1) code. We don't care
// about what encoding is being used; we just want the real start address
// of the function.
start_address = reinterpret_cast<const char *>(
reinterpret_cast<uintptr_t>(start_address) & ~1);
#endif
if (deref_function_descriptor_pointer &&
InSection(original_start_address, opd)) {
// The opd section is mapped into memory. Just dereference

View File

@ -0,0 +1,72 @@
// Copyright 2020 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cxxabi.h>
#include <emscripten.h>
#include <algorithm>
#include <cstring>
#include "absl/base/internal/raw_logging.h"
#include "absl/debugging/internal/demangle.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
extern "C" {
const char* emscripten_pc_get_function(const void* pc);
}
// clang-format off
EM_JS(bool, HaveOffsetConverter, (),
{ return typeof wasmOffsetConverter !== 'undefined'; });
// clang-format on
namespace absl {
ABSL_NAMESPACE_BEGIN
void InitializeSymbolizer(const char*) {
if (!HaveOffsetConverter()) {
ABSL_RAW_LOG(INFO,
"Symbolization unavailable. Rebuild with -sWASM=1 "
"and -sUSE_OFFSET_CONVERTER=1.");
}
}
bool Symbolize(const void* pc, char* out, int out_size) {
// Check if we have the offset converter necessary for pc_get_function.
// Without it, the program will abort().
if (!HaveOffsetConverter()) {
return false;
}
const char* func_name = emscripten_pc_get_function(pc);
if (func_name == nullptr) {
return false;
}
strncpy(out, func_name, out_size);
if (out[out_size - 1] != '\0') {
// strncpy() does not '\0' terminate when it truncates.
static constexpr char kEllipsis[] = "...";
int ellipsis_size = std::min<int>(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

View File

@ -146,8 +146,22 @@ static const char *TrySymbolize(void *pc) {
return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer));
}
#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)
#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE) || \
defined(ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE)
// Test with a return address.
void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {
#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE)
void *return_address = __builtin_return_address(0);
const char *symbol = TrySymbolize(return_address);
ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed");
ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed");
std::cout << "TestWithReturnAddress passed" << std::endl;
#endif
}
#ifndef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE
TEST(Symbolize, Cached) {
// Compilers should give us pointers to them.
@ -418,6 +432,7 @@ TEST(Symbolize, ForEachSection) {
close(fd);
}
#endif // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
#endif // !ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE
// x86 specific tests. Uses some inline assembler.
extern "C" {
@ -466,17 +481,46 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
}
}
// Test with a return address.
void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {
#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
// Test that we correctly identify bounds of Thumb functions on ARM.
//
// Thumb functions have the lowest-order bit set in their addresses in the ELF
// symbol table. This requires some extra logic to properly compute function
// bounds. To test this logic, nudge a Thumb function right up against an ARM
// function and try to symbolize the ARM function.
//
// A naive implementation will simply use the Thumb function's entry point as
// written in the symbol table and will therefore treat the Thumb function as
// extending one byte further in the instruction stream than it actually does.
// When asked to symbolize the start of the ARM function, it will identify an
// overlap between the Thumb and ARM functions, and it will return the name of
// the Thumb function.
//
// A correct implementation, on the other hand, will null out the lowest-order
// bit in the Thumb function's entry point. It will correctly compute the end of
// the Thumb function, it will find no overlap between the Thumb and ARM
// functions, and it will return the name of the ARM function.
__attribute__((target("thumb"))) int ArmThumbOverlapThumb(int x) {
return x * x * x;
}
__attribute__((target("arm"))) int ArmThumbOverlapArm(int x) {
return x * x * x;
}
void ABSL_ATTRIBUTE_NOINLINE TestArmThumbOverlap() {
#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE)
void *return_address = __builtin_return_address(0);
const char *symbol = TrySymbolize(return_address);
ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed");
ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed");
std::cout << "TestWithReturnAddress passed" << std::endl;
const char *symbol = TrySymbolize((void *)&ArmThumbOverlapArm);
ABSL_RAW_CHECK(symbol != nullptr, "TestArmThumbOverlap failed");
ABSL_RAW_CHECK(strcmp("ArmThumbOverlapArm()", symbol) == 0,
"TestArmThumbOverlap failed");
std::cout << "TestArmThumbOverlap passed" << std::endl;
#endif
}
#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
#elif defined(_WIN32)
#if !defined(ABSL_CONSUME_DLL)
@ -519,7 +563,6 @@ TEST(Symbolize, SymbolizeWithDemangling) {
#endif // !defined(ABSL_CONSUME_DLL)
#else // Symbolizer unimplemented
TEST(Symbolize, Unimplemented) {
char buf[64];
EXPECT_FALSE(absl::Symbolize((void *)(&nonstatic_func), buf, sizeof(buf)));
@ -551,6 +594,9 @@ int main(int argc, char **argv) {
TestWithPCInsideInlineFunction();
TestWithPCInsideNonInlineFunction();
TestWithReturnAddress();
#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
TestArmThumbOverlap();
#endif
#endif
return RUN_ALL_TESTS();

View File

@ -14,7 +14,6 @@
# limitations under the License.
#
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@ -217,6 +216,7 @@ cc_library(
name = "flag",
srcs = [
"flag.cc",
"internal/flag_msvc.inc",
],
hdrs = [
"declare.h",
@ -259,6 +259,7 @@ cc_library(
":reflection",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/container:flat_hash_map",
"//absl/strings",
],
)

Some files were not shown because too many files have changed in this diff Show More