SERVER-73467 add grpc 1.46.6 to third party
This commit is contained in:
parent
f5df0445cf
commit
f861cf6c9c
@ -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 | | |
|
||||
|
||||
66
SConstruct
66
SConstruct
@ -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')
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
416
site_scons/site_tools/protobuf_compiler.py
Normal file
416
site_scons/site_tools/protobuf_compiler.py
Normal 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
|
||||
@ -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'),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -103,3 +103,6 @@
|
||||
|
||||
// Defined if WiredTiger storage engine is enabled
|
||||
@mongo_config_wiredtiger_enabled@
|
||||
|
||||
// Defined if grpc support is enabled
|
||||
@mongo_config_grpc@
|
||||
|
||||
72
src/third_party/SConscript
vendored
72
src/third_party/SConscript
vendored
@ -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", )
|
||||
|
||||
575
src/third_party/abseil-cpp/SConscript
vendored
575
src/third_party/abseil-cpp/SConscript
vendored
@ -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',
|
||||
],
|
||||
)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
90
src/third_party/abseil-cpp/dist/CMake/README.md
vendored
90
src/third_party/abseil-cpp/dist/CMake/README.md
vendored
@ -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`
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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}"
|
||||
|
||||
53
src/third_party/abseil-cpp/dist/CMakeLists.txt
vendored
53
src/third_party/abseil-cpp/dist/CMakeLists.txt
vendored
@ -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)
|
||||
|
||||
5
src/third_party/abseil-cpp/dist/FAQ.md
vendored
5
src/third_party/abseil-cpp/dist/FAQ.md
vendored
@ -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.
|
||||
|
||||
3
src/third_party/abseil-cpp/dist/README.md
vendored
3
src/third_party/abseil-cpp/dist/README.md
vendored
@ -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>`.
|
||||
|
||||
29
src/third_party/abseil-cpp/dist/WORKSPACE
vendored
29
src/third_party/abseil-cpp/dist/WORKSPACE
vendored
@ -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"],
|
||||
)
|
||||
|
||||
12
src/third_party/abseil-cpp/dist/absl/BUILD.bazel
vendored
12
src/third_party/abseil-cpp/dist/absl/BUILD.bazel
vendored
@ -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__"],
|
||||
)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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_
|
||||
|
||||
104
src/third_party/abseil-cpp/dist/absl/base/config.h
vendored
104
src/third_party/abseil-cpp/dist/absl/base/config.h
vendored
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
//
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -51,5 +51,5 @@ absl_cc_test(
|
||||
absl::cleanup
|
||||
absl::config
|
||||
absl::utility
|
||||
gmock_main
|
||||
GTest::gmock_main
|
||||
)
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = [
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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`.
|
||||
|
||||
@ -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`.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 <>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
@ -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));
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {};
|
||||
|
||||
@ -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
|
||||
|
||||
114
src/third_party/abseil-cpp/dist/absl/container/sample_element_size_test.cc
vendored
Normal file
114
src/third_party/abseil-cpp/dist/absl/container/sample_element_size_test.cc
vendored
Normal 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
|
||||
@ -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"
|
||||
)
|
||||
|
||||
|
||||
@ -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",
|
||||
]
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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",
|
||||
],
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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.
|
||||
//
|
||||
|
||||
@ -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, '_')) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
110
src/third_party/abseil-cpp/dist/absl/debugging/internal/stacktrace_emscripten-inl.inc
vendored
Normal file
110
src/third_party/abseil-cpp/dist/absl/debugging/internal/stacktrace_emscripten-inl.inc
vendored
Normal 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_
|
||||
234
src/third_party/abseil-cpp/dist/absl/debugging/internal/stacktrace_riscv-inl.inc
vendored
Normal file
234
src/third_party/abseil-cpp/dist/absl/debugging/internal/stacktrace_riscv-inl.inc
vendored
Normal 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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
72
src/third_party/abseil-cpp/dist/absl/debugging/symbolize_emscripten.inc
vendored
Normal file
72
src/third_party/abseil-cpp/dist/absl/debugging/symbolize_emscripten.inc
vendored
Normal 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
|
||||
@ -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();
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user