master v1.41.0
captainfffsama 2 years ago
parent 8984f4533b
commit 1297acf91b
  1. 2
      BUILD
  2. 19
      README.md
  3. 41
      abseil-cpp/.github/ISSUE_TEMPLATE/00-bug_report.md
  4. 7
      abseil-cpp/.github/ISSUE_TEMPLATE/90-question.md
  5. 1
      abseil-cpp/.github/ISSUE_TEMPLATE/config.yml
  6. 15
      abseil-cpp/.gitignore
  7. 24
      abseil-cpp/CMake/AbseilDll.cmake
  8. 77
      abseil-cpp/CMake/AbseilHelpers.cmake
  9. 20
      abseil-cpp/CMake/AbseilInstallDirs.cmake
  10. 30
      abseil-cpp/CMake/Googletest/CMakeLists.txt.in
  11. 4
      abseil-cpp/CMake/README.md
  12. 146
      abseil-cpp/CMake/install_test_project/test.sh
  13. 48
      abseil-cpp/CMakeLists.txt
  14. 16
      abseil-cpp/LTS.md
  15. 39
      abseil-cpp/README.md
  16. 7
      abseil-cpp/WORKSPACE
  17. 38
      abseil-cpp/absl/BUILD.bazel
  18. 4
      abseil-cpp/absl/abseil.podspec.gen.py
  19. 6
      abseil-cpp/absl/algorithm/container.h
  20. 38
      abseil-cpp/absl/base/BUILD.bazel
  21. 25
      abseil-cpp/absl/base/CMakeLists.txt
  22. 28
      abseil-cpp/absl/base/attributes.h
  23. 11
      abseil-cpp/absl/base/call_once.h
  24. 46
      abseil-cpp/absl/base/config.h
  25. 34
      abseil-cpp/absl/base/dynamic_annotations.h
  26. 219
      abseil-cpp/absl/base/internal/bits.h
  27. 97
      abseil-cpp/absl/base/internal/bits_test.cc
  28. 5
      abseil-cpp/absl/base/internal/direct_mmap.h
  29. 61
      abseil-cpp/absl/base/internal/endian.h
  30. 2
      abseil-cpp/absl/base/internal/exponential_biased_test.cc
  31. 19
      abseil-cpp/absl/base/internal/low_level_alloc_test.cc
  32. 5
      abseil-cpp/absl/base/internal/low_level_scheduling.h
  33. 66
      abseil-cpp/absl/base/internal/raw_logging.cc
  34. 22
      abseil-cpp/absl/base/internal/raw_logging.h
  35. 13
      abseil-cpp/absl/base/internal/spinlock.cc
  36. 19
      abseil-cpp/absl/base/internal/spinlock.h
  37. 4
      abseil-cpp/absl/base/internal/spinlock_akaros.inc
  38. 6
      abseil-cpp/absl/base/internal/spinlock_linux.inc
  39. 4
      abseil-cpp/absl/base/internal/spinlock_posix.inc
  40. 22
      abseil-cpp/absl/base/internal/spinlock_wait.h
  41. 10
      abseil-cpp/absl/base/internal/spinlock_win32.inc
  42. 2
      abseil-cpp/absl/base/internal/strerror.cc
  43. 6
      abseil-cpp/absl/base/internal/strerror_test.cc
  44. 2
      abseil-cpp/absl/base/internal/sysinfo.cc
  45. 23
      abseil-cpp/absl/base/internal/sysinfo_test.cc
  46. 7
      abseil-cpp/absl/base/internal/thread_identity.cc
  47. 85
      abseil-cpp/absl/base/internal/thread_identity.h
  48. 118
      abseil-cpp/absl/base/internal/throw_delegate.cc
  49. 76
      abseil-cpp/absl/base/internal/unaligned_access.h
  50. 4
      abseil-cpp/absl/base/internal/unscaledcycleclock.cc
  51. 8
      abseil-cpp/absl/base/log_severity.h
  52. 11
      abseil-cpp/absl/base/macros.h
  53. 17
      abseil-cpp/absl/base/optimization.h
  54. 2
      abseil-cpp/absl/base/options.h
  55. 1
      abseil-cpp/absl/base/port.h
  56. 1
      abseil-cpp/absl/base/spinlock_test_common.cc
  57. 2
      abseil-cpp/absl/base/thread_annotations.h
  58. 0
      abseil-cpp/absl/cleanup/BUILD.bazel
  59. 0
      abseil-cpp/absl/cleanup/CMakeLists.txt
  60. 0
      abseil-cpp/absl/cleanup/cleanup.h
  61. 0
      abseil-cpp/absl/cleanup/cleanup_test.cc
  62. 0
      abseil-cpp/absl/cleanup/internal/cleanup.h
  63. 38
      abseil-cpp/absl/compiler_config_setting.bzl
  64. 57
      abseil-cpp/absl/container/BUILD.bazel
  65. 9
      abseil-cpp/absl/container/CMakeLists.txt
  66. 36
      abseil-cpp/absl/container/btree_benchmark.cc
  67. 7
      abseil-cpp/absl/container/btree_map.h
  68. 383
      abseil-cpp/absl/container/btree_test.cc
  69. 4
      abseil-cpp/absl/container/fixed_array.h
  70. 2
      abseil-cpp/absl/container/flat_hash_set.h
  71. 8
      abseil-cpp/absl/container/inlined_vector.h
  72. 22
      abseil-cpp/absl/container/inlined_vector_benchmark.cc
  73. 36
      abseil-cpp/absl/container/inlined_vector_test.cc
  74. 472
      abseil-cpp/absl/container/internal/btree.h
  75. 99
      abseil-cpp/absl/container/internal/btree_container.h
  76. 2
      abseil-cpp/absl/container/internal/compressed_tuple.h
  77. 5
      abseil-cpp/absl/container/internal/container_memory_test.cc
  78. 6
      abseil-cpp/absl/container/internal/hashtablez_sampler.cc
  79. 3
      abseil-cpp/absl/container/internal/hashtablez_sampler.h
  80. 3
      abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
  81. 6
      abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc
  82. 207
      abseil-cpp/absl/container/internal/inlined_vector.h
  83. 8
      abseil-cpp/absl/container/internal/layout.h
  84. 0
      abseil-cpp/absl/container/internal/layout_benchmark.cc
  85. 560
      abseil-cpp/absl/container/internal/layout_test.cc
  86. 15
      abseil-cpp/absl/container/internal/raw_hash_set.cc
  87. 272
      abseil-cpp/absl/container/internal/raw_hash_set.h
  88. 4
      abseil-cpp/absl/container/internal/raw_hash_set_allocator_test.cc
  89. 0
      abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc
  90. 0
      abseil-cpp/absl/container/internal/raw_hash_set_probe_benchmark.cc
  91. 172
      abseil-cpp/absl/container/internal/raw_hash_set_test.cc
  92. 1
      abseil-cpp/absl/container/internal/unordered_map_constructor_test.h
  93. 30
      abseil-cpp/absl/container/node_hash_set.h
  94. 17
      abseil-cpp/absl/copts/AbseilConfigureCopts.cmake
  95. 110
      abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake
  96. 110
      abseil-cpp/absl/copts/GENERATED_copts.bzl
  97. 14
      abseil-cpp/absl/copts/configure_copts.bzl
  98. 124
      abseil-cpp/absl/copts/copts.py
  99. 20
      abseil-cpp/absl/debugging/BUILD.bazel
  100. 36
      abseil-cpp/absl/debugging/failure_signal_handler.cc
  101. Some files were not shown because too many files have changed in this diff Show More

@ -12,6 +12,4 @@ exports_files([
"futures.BUILD",
"libuv.BUILD",
"protobuf.patch",
"rules_python.patch",
"protoc-gen-validate.patch",
])

@ -58,26 +58,26 @@ Updating some dependencies requires extra care.
### Updating third_party/boringssl-with-bazel
- Update the `third_party/boringssl-with-bazel` submodule to the latest [`main-with-bazel`](https://github.com/google/boringssl/tree/main-with-bazel) branch
- Update the `third_party/boringssl-with-bazel` submodule to the latest [`master-with-bazel`](https://github.com/google/boringssl/tree/master-with-bazel) branch
```
git submodule update --init # just to start in a clean state
cd third_party/boringssl-with-bazel
git fetch origin # fetch what's new in the boringssl repository
git checkout origin/main-with-bazel # checkout the current state of main-with-bazel branch in the boringssl repo
# Note the latest commit SHA on main-with-bazel branch
git checkout origin/master-with-bazel # checkout the current state of master-with-bazel branch in the boringssl repo
# Note the latest commit SHA on master-with-bazel-branch
cd ../.. # go back to grpc repo root
git status # will show that there are new commits in third_party/boringssl-with-bazel
git add third_party/boringssl-with-bazel # we actually want to update the changes to the submodule
git commit -m "update submodule boringssl-with-bazel with origin/main-with-bazel" # commit
git commit -m "update submodule boringssl-with-bazel with origin/master-with-bazel" # commit
```
- Update boringssl dependency in `bazel/grpc_deps.bzl` to the same commit SHA as main-with-bazel branch
- Update boringssl dependency in `bazel/grpc_deps.bzl` to the same commit SHA as master-with-bazel branch
- Update `http_archive(name = "boringssl",` section by updating the sha in `strip_prefix` and `urls` fields.
- Also, set `sha256` field to "" as the existing value is not valid. This will be added later once we know what that value is.
- Update `tools/run_tests/sanity/check_submodules.sh` with the same commit
- Commit these changes `git commit -m "update boringssl dependency to main-with-bazel commit SHA"`
- Commit these changes `git commit -m "update boringssl dependency to master-with-bazel commit SHA"`
- Run `tools/buildgen/generate_projects.sh` to regenerate the generated files
- Because `sha256` in `bazel/grpc_deps.bzl` was left empty, you will get a DEBUG msg like this one:
@ -118,16 +118,13 @@ Apart from the above steps, please perform the following two steps to generate t
Since upb is vendored in the gRPC repo, you cannot use submodule to update it. Please follow the steps below.
1. Update third_party/upb directory by running
- `export GRPC_ROOT=~/git/grpc`
- `wget https://github.com/protocolbuffers/upb/archive/refs/heads/main.zip`
- `rm -rf $GRPC_ROOT/third_party/upb`
- `unzip main.zip -d $GRPC_ROOT/third_party`
- `mv $GRPC_ROOT/third_party/upb-main $GRPC_ROOT/third_party/upb`
`git subtree pull --squash --prefix=third_party/upb https://github.com/protocolbuffers/upb.git master`
2. Update the dependency in `grpc_deps.bzl` to the same commit
3. Populate the bazel download mirror by running `bazel/update_mirror.sh`
4. Update `src/upb/gen_build_yaml.py` for newly added or removed upb files
5. Run `tools/buildgen/generate_projects.sh` to regenerate the generated files
6. Run `tools/codegen/core/gen_upb_api.sh` to regenerate upb files.
If you see breaking changes here, you may want to import upb into Google3 along with gRPC.
### Updating third_party/xxhash

@ -1,41 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'bug'
assignees: ''
---
**Describe the bug**
Include a clear and concise description of what the problem is, including what
you expected to happen, and what actually happened.
**Steps to reproduce the bug**
It's important that we are able to reproduce the problem that you are
experiencing. Please provide all code and relevant steps to reproduce the
problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links
to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the
problem are also helpful.
**What version of Abseil are you using?**
**What operating system and version are you using**
If you are using a Linux distribution please include the name and version of the
distribution as well.
**What compiler and version are you using?**
Please include the output of `gcc -v` or `clang -v`, or the equivalent for your
compiler.
**What build system are you using?**
Please include the output of `bazel --version` or `cmake --version`, or the
equivalent for your build system.
**Additional context**
Add any other context about the problem here.

@ -1,7 +0,0 @@
---
name: Question
about: Have a question? Ask us anything! :-)
title: ''
labels: 'question'
assignees: ''
---

@ -1 +0,0 @@
blank_issues_enables: true

@ -1,15 +0,0 @@
# Ignore all bazel-* symlinks.
/bazel-*
# Ignore Bazel verbose explanations
--verbose_explanations
# Ignore CMake usual build directory
build
# Ignore Vim files
*.swp
# Ignore QtCreator Project file
CMakeLists.txt.user
# Ignore VS Code files
.vscode/*
# Ignore generated python artifacts
*.pyc
copts/__pycache__/

@ -1,4 +1,5 @@
include(CMakeParseArguments)
include(GNUInstallDirs)
set(ABSL_INTERNAL_DLL_FILES
"algorithm/algorithm.h"
@ -10,7 +11,6 @@ set(ABSL_INTERNAL_DLL_FILES
"base/const_init.h"
"base/dynamic_annotations.h"
"base/internal/atomic_hook.h"
"base/internal/bits.h"
"base/internal/cycleclock.cc"
"base/internal/cycleclock.h"
"base/internal/direct_mmap.h"
@ -61,6 +61,8 @@ set(ABSL_INTERNAL_DLL_FILES
"base/policy_checks.h"
"base/port.h"
"base/thread_annotations.h"
"cleanup/cleanup.h"
"cleanup/internal/cleanup.h"
"container/btree_map.h"
"container/btree_set.h"
"container/fixed_array.h"
@ -122,10 +124,15 @@ 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"
"memory/memory.h"
"meta/type_traits.h"
"numeric/bits.h"
"numeric/int128.cc"
"numeric/int128.h"
"numeric/internal/bits.h"
"numeric/internal/representation.h"
"random/bernoulli_distribution.h"
"random/beta_distribution.h"
"random/bit_gen_ref.h"
@ -190,12 +197,18 @@ 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/stl_type_traits.h"
"strings/internal/string_constant.h"
"strings/match.cc"
"strings/match.h"
"strings/numbers.cc"
@ -250,6 +263,7 @@ set(ABSL_INTERNAL_DLL_FILES
"synchronization/notification.h"
"synchronization/internal/create_thread_identity.cc"
"synchronization/internal/create_thread_identity.h"
"synchronization/internal/futex.h"
"synchronization/internal/graphcycles.cc"
"synchronization/internal/graphcycles.h"
"synchronization/internal/kernel_timeout.h"
@ -487,7 +501,7 @@ function(absl_make_dll)
abseil_dll
PUBLIC
"$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>"
$<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_compile_options(
@ -505,8 +519,8 @@ function(absl_make_dll)
${ABSL_CC_LIB_DEFINES}
)
install(TARGETS abseil_dll EXPORT ${PROJECT_NAME}Targets
RUNTIME DESTINATION ${ABSL_INSTALL_BINDIR}
LIBRARY DESTINATION ${ABSL_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${ABSL_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endfunction()

@ -17,7 +17,6 @@
include(CMakeParseArguments)
include(AbseilConfigureCopts)
include(AbseilDll)
include(AbseilInstallDirs)
# The IDE folder for Abseil that will be used if Abseil is included in a CMake
# project that sets
@ -41,7 +40,7 @@ endif()
# LINKOPTS: List of link options
# PUBLIC: Add this so that this library will be exported under absl::
# Also in IDE, target will appear in Abseil folder while non PUBLIC will be in Abseil/internal.
# TESTONLY: When added, this target will only be built if user passes -DABSL_RUN_TESTS=ON to CMake.
# TESTONLY: When added, this target will only be built if BUILD_TESTING=ON.
#
# Note:
# By default, absl_cc_library will always create a library named absl_${NAME},
@ -83,7 +82,7 @@ function(absl_cc_library)
${ARGN}
)
if(ABSL_CC_LIB_TESTONLY AND NOT ABSL_RUN_TESTS)
if(ABSL_CC_LIB_TESTONLY AND NOT BUILD_TESTING)
return()
endif()
@ -104,7 +103,7 @@ function(absl_cc_library)
endif()
endforeach()
if("${ABSL_CC_SRCS}" STREQUAL "")
if(ABSL_CC_SRCS STREQUAL "")
set(ABSL_CC_LIB_IS_INTERFACE 1)
else()
set(ABSL_CC_LIB_IS_INTERFACE 0)
@ -122,7 +121,11 @@ function(absl_cc_library)
# 4. "static" -- This target does not depend on the DLL and should be built
# statically.
if (${ABSL_BUILD_DLL})
absl_internal_dll_contains(TARGET ${_NAME} OUTPUT _in_dll)
if(ABSL_ENABLE_INSTALL)
absl_internal_dll_contains(TARGET ${_NAME} OUTPUT _in_dll)
else()
absl_internal_dll_contains(TARGET ${ABSL_CC_LIB_NAME} OUTPUT _in_dll)
endif()
if (${_in_dll})
# This target should be replaced by the DLL
set(_build_type "dll")
@ -137,8 +140,53 @@ function(absl_cc_library)
set(_build_type "static")
endif()
# Generate a pkg-config file for every library:
if(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
if(NOT ABSL_CC_LIB_TESTONLY)
if(absl_VERSION)
set(PC_VERSION "${absl_VERSION}")
else()
set(PC_VERSION "head")
endif()
foreach(dep ${ABSL_CC_LIB_DEPS})
if(${dep} MATCHES "^absl::(.*)")
# Join deps with commas.
if(PC_DEPS)
set(PC_DEPS "${PC_DEPS},")
endif()
set(PC_DEPS "${PC_DEPS} absl_${CMAKE_MATCH_1} = ${PC_VERSION}")
endif()
endforeach()
foreach(cflag ${ABSL_CC_LIB_COPTS})
if(${cflag} MATCHES "^(-Wno|/wd)")
# These flags are needed to suppress warnings that might fire in our headers.
set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
elseif(${cflag} MATCHES "^(-W|/w[1234eo])")
# Don't impose our warnings on others.
else()
set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
endif()
endforeach()
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\
\n\
Name: absl_${_NAME}\n\
Description: Abseil ${_NAME} library\n\
URL: https://abseil.io/\n\
Version: ${PC_VERSION}\n\
Requires:${PC_DEPS}\n\
Libs: -L\${libdir} $<JOIN:${ABSL_CC_LIB_LINKOPTS}, > $<$<NOT:$<BOOL:${ABSL_CC_LIB_IS_INTERFACE}>>:-labsl_${_NAME}>\n\
Cflags: -I\${includedir}${PC_CFLAGS}\n")
INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc"
DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
endif()
endif()
if(NOT ABSL_CC_LIB_IS_INTERFACE)
if(${_build_type} STREQUAL "dll_dep")
if(_build_type STREQUAL "dll_dep")
# This target depends on the DLL. When adding dependencies to this target,
# any depended-on-target which is contained inside the DLL is replaced
# with a dependency on the DLL.
@ -167,7 +215,7 @@ function(absl_cc_library)
"${_gtest_link_define}"
)
elseif(${_build_type} STREQUAL "static" OR ${_build_type} STREQUAL "shared")
elseif(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
add_library(${_NAME} "")
target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS})
target_link_libraries(${_NAME}
@ -190,7 +238,7 @@ function(absl_cc_library)
target_include_directories(${_NAME}
PUBLIC
"$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>"
$<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_compile_options(${_NAME}
PRIVATE ${ABSL_CC_LIB_COPTS})
@ -215,6 +263,7 @@ function(absl_cc_library)
if(ABSL_ENABLE_INSTALL)
set_target_properties(${_NAME} PROPERTIES
OUTPUT_NAME "absl_${_NAME}"
SOVERSION "2103.0.0"
)
endif()
else()
@ -223,10 +272,10 @@ function(absl_cc_library)
target_include_directories(${_NAME}
INTERFACE
"$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>"
$<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
if (${_build_type} STREQUAL "dll")
if (_build_type STREQUAL "dll")
set(ABSL_CC_LIB_DEPS abseil_dll)
endif()
@ -243,9 +292,9 @@ function(absl_cc_library)
# installed abseil can't be tested.
if(NOT ABSL_CC_LIB_TESTONLY AND ABSL_ENABLE_INSTALL)
install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets
RUNTIME DESTINATION ${ABSL_INSTALL_BINDIR}
LIBRARY DESTINATION ${ABSL_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${ABSL_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif()
@ -290,7 +339,7 @@ endfunction()
# gtest_main
# )
function(absl_cc_test)
if(NOT ABSL_RUN_TESTS)
if(NOT BUILD_TESTING)
return()
endif()

@ -1,20 +0,0 @@
include(GNUInstallDirs)
# absl_VERSION is only set if we are an LTS release being installed, in which
# case it may be into a system directory and so we need to make subdirectories
# for each installed version of Abseil. This mechanism is implemented in
# Abseil's internal Copybara (https://github.com/google/copybara) workflows and
# isn't visible in the CMake buildsystem itself.
if(absl_VERSION)
set(ABSL_SUBDIR "${PROJECT_NAME}_${PROJECT_VERSION}")
set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}/${ABSL_SUBDIR}")
set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${ABSL_SUBDIR}")
set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/${ABSL_SUBDIR}")
set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/${ABSL_SUBDIR}")
else()
set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}")
set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
endif()

@ -3,24 +3,12 @@ cmake_minimum_required(VERSION 2.8.2)
project(googletest-external NONE)
include(ExternalProject)
if(${ABSL_USE_GOOGLETEST_HEAD})
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${absl_gtest_src_dir}"
BINARY_DIR "${absl_gtest_build_dir}"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
else()
ExternalProject_Add(googletest
SOURCE_DIR "${absl_gtest_src_dir}"
BINARY_DIR "${absl_gtest_build_dir}"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
endif()
ExternalProject_Add(googletest
URL "${absl_gtest_download_url}" # May be empty
SOURCE_DIR "${absl_gtest_src_dir}"
BINARY_DIR "${absl_gtest_build_dir}"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

@ -52,7 +52,7 @@ target_link_libraries(my_exe absl::base absl::synchronization absl::strings)
### Running Abseil Tests with CMake
Use the `-DABSL_RUN_TESTS=ON` flag to run Abseil tests. Note that if the `-DBUILD_TESTING=OFF` flag is passed then Abseil tests will not be run.
Use the `-DBUILD_TESTING=ON` flag to run Abseil tests.
You will need to provide Abseil with a Googletest dependency. There are two
options for how to do this:
@ -70,7 +70,7 @@ For example, to run just the Abseil tests, you could use this script:
cd path/to/abseil-cpp
mkdir build
cd build
cmake -DABSL_USE_GOOGLETEST_HEAD=ON -DABSL_RUN_TESTS=ON ..
cmake -DBUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=ON ..
make -j
ctest
```

@ -13,70 +13,44 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# "Unit" and integration tests for Absl CMake installation
# TODO(absl-team): This script isn't fully hermetic because
# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed
# version of GoogleTest. This means that an upstream change to GoogleTest could
# break this test. Fix this by allowing this script to pin to a known-good
# version of GoogleTest.
#
# Unit and integration tests for Abseil LTS CMake installation
# Fail on any error. Treat unset variables an error. Print commands as executed.
set -euox pipefail
install_absl() {
pushd "${absl_build_dir}"
if [[ "${#}" -eq 1 ]]; then
cmake -DCMAKE_INSTALL_PREFIX="${1}" "${absl_dir}"
else
cmake "${absl_dir}"
fi
cmake --build . --target install -- -j
popd
}
uninstall_absl() {
xargs rm < "${absl_build_dir}"/install_manifest.txt
rm -rf "${absl_build_dir}"
mkdir -p "${absl_build_dir}"
}
lts_install=""
while getopts ":l" lts; do
case "${lts}" in
l )
lts_install="true"
;;
esac
done
source ci/cmake_common.sh
absl_dir=/abseil-cpp
absl_build_dir=/buildfs/absl-build
absl_build_dir=/buildfs
project_dir="${absl_dir}"/CMake/install_test_project
project_build_dir=/buildfs/project-build
mkdir -p "${absl_build_dir}"
mkdir -p "${project_build_dir}"
if [[ "${lts_install}" ]]; then
install_dir="/usr/local"
else
install_dir="${project_build_dir}"/install
build_shared_libs="OFF"
if [ "${LINK_TYPE:-}" = "DYNAMIC" ]; then
build_shared_libs="ON"
fi
mkdir -p "${install_dir}"
# Test build, install, and link against installed abseil
pushd "${project_build_dir}"
if [[ "${lts_install}" ]]; then
install_absl
cmake "${project_dir}"
else
install_absl "${install_dir}"
cmake "${project_dir}" -DCMAKE_PREFIX_PATH="${install_dir}"
fi
# Run the LTS transformations
./create_lts.py 99998877
# Install Abseil
pushd "${absl_build_dir}"
cmake "${absl_dir}" \
-DABSL_GOOGLETEST_DOWNLOAD_URL="${ABSL_GOOGLETEST_DOWNLOAD_URL}" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=ON \
-DBUILD_SHARED_LIBS="${build_shared_libs}"
make -j $(nproc)
ctest -j $(nproc)
make install
ldconfig
popd
# Test the project against the installed Abseil
mkdir -p "${project_build_dir}"
pushd "${project_build_dir}"
cmake "${project_dir}"
cmake --build . --target simple
output="$(${project_build_dir}/simple "printme" 2>&1)"
@ -88,57 +62,35 @@ fi
popd
# Test that we haven't accidentally made absl::abslblah
pushd "${install_dir}"
# Starting in CMake 3.12 the default install dir is lib$bit_width
if [[ -d lib64 ]]; then
libdir="lib64"
elif [[ -d lib ]]; then
libdir="lib"
else
echo "ls *, */*, */*/*:"
ls *
ls */*
ls */*/*
echo "unknown lib dir"
fi
if [[ "${lts_install}" ]]; then
# LTS versions append the date of the release to the subdir.
# 9999/99/99 is the dummy date used in the local_lts workflow.
absl_subdir="absl_99999999"
else
absl_subdir="absl"
fi
if ! grep absl::strings "${libdir}/cmake/${absl_subdir}/abslTargets.cmake"; then
cat "${libdir}"/cmake/absl/abslTargets.cmake
if ! grep absl::strings "/usr/local/lib/cmake/absl/abslTargets.cmake"; then
cat "/usr/local/lib/cmake/absl/abslTargets.cmake"
echo "CMake targets named incorrectly"
exit 1
fi
uninstall_absl
popd
pushd "${HOME}"
cat > hello-abseil.cc << EOF
#include <cstdlib>
#include "absl/strings/str_format.h"
int main(int argc, char **argv) {
absl::PrintF("Hello Abseil!\n");
return EXIT_SUCCESS;
}
EOF
if [[ ! "${lts_install}" ]]; then
# Test that we warn if installed without a prefix or a system prefix
output="$(install_absl 2>&1)"
if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then
echo "Install without prefix didn't warn as expected. Output:"
echo "${output}"
exit 1
fi
uninstall_absl
output="$(install_absl /usr 2>&1)"
if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then
echo "Install with /usr didn't warn as expected. Output:"
echo "${output}"
exit 1
fi
uninstall_absl
if [ "${LINK_TYPE:-}" != "DYNAMIC" ]; then
pc_args=($(pkg-config --cflags --libs --static absl_str_format))
g++ -static -o hello-abseil hello-abseil.cc "${pc_args[@]}"
else
pc_args=($(pkg-config --cflags --libs absl_str_format))
g++ -o hello-abseil hello-abseil.cc "${pc_args[@]}"
fi
hello="$(./hello-abseil)"
[[ "${hello}" == "Hello Abseil!" ]]
popd
echo "Install test complete!"
exit 0

@ -20,7 +20,6 @@
# (https://fedoraproject.org/wiki/EPEL#Extra_Packages_for_Enterprise_Linux_.28EPEL.29)
# and then issuing `yum install cmake3` on the command line.
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 17)
# Compiler id for Apple Clang is now AppleClang.
if (POLICY CMP0025)
@ -42,7 +41,12 @@ if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif (POLICY CMP0077)
project(absl CXX)
# 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)
include(CTest)
# Output directory is correct by default for most build setups. However, when
# building Abseil as a DLL, it is important to have the DLL in the same
@ -52,7 +56,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# when absl is included as subproject (i.e. using add_subdirectory(abseil-cpp))
# in the source tree of a project that uses it, install rules are disabled.
if(NOT "^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$")
if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(ABSL_ENABLE_INSTALL "Enable install rule" OFF)
else()
option(ABSL_ENABLE_INSTALL "Enable install rule" ON)
@ -63,8 +67,8 @@ list(APPEND CMAKE_MODULE_PATH
${CMAKE_CURRENT_LIST_DIR}/absl/copts
)
include(AbseilInstallDirs)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
include(AbseilDll)
include(AbseilHelpers)
@ -96,25 +100,28 @@ find_package(Threads REQUIRED)
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)
option(ABSL_USE_GOOGLETEST_HEAD
"If ON, abseil will download HEAD from googletest at config time." OFF)
"If ON, abseil will download HEAD from GoogleTest at config time." OFF)
set(ABSL_GOOGLETEST_DOWNLOAD_URL "" CACHE STRING "If set, download GoogleTest from this URL")
set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH
"If ABSL_USE_GOOGLETEST_HEAD is OFF, specifies the directory of a local googletest checkout."
"If ABSL_USE_GOOGLETEST_HEAD is OFF and ABSL_GOOGLETEST_URL is not set, specifies the directory of a local GoogleTest checkout."
)
option(ABSL_RUN_TESTS "If ON, Abseil tests will be run." OFF)
if(${ABSL_RUN_TESTS})
# enable CTest. This will set BUILD_TESTING to ON unless otherwise specified
# on the command line
include(CTest)
if(BUILD_TESTING)
## check targets
if (NOT ABSL_USE_EXTERNAL_GOOGLETEST)
set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build)
if(${ABSL_USE_GOOGLETEST_HEAD})
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")
endif()
if(ABSL_USE_GOOGLETEST_HEAD)
set(absl_gtest_download_url "https://github.com/google/googletest/archive/master.zip")
elseif(ABSL_GOOGLETEST_DOWNLOAD_URL)
set(absl_gtest_download_url ${ABSL_GOOGLETEST_DOWNLOAD_URL})
endif()
if(absl_gtest_download_url)
set(absl_gtest_src_dir ${CMAKE_BINARY_DIR}/googletest-src)
else()
set(absl_gtest_src_dir ${ABSL_LOCAL_GOOGLETEST_DIR})
@ -138,19 +145,20 @@ add_subdirectory(absl)
if(ABSL_ENABLE_INSTALL)
# install as a subdirectory only
install(EXPORT ${PROJECT_NAME}Targets
NAMESPACE absl::
DESTINATION "${ABSL_INSTALL_CONFIGDIR}"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
configure_package_config_file(
CMake/abslConfig.cmake.in
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${ABSL_INSTALL_CONFIGDIR}"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION "${ABSL_INSTALL_CONFIGDIR}"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
# Abseil only has a version in LTS releases. This mechanism is accomplished
@ -163,12 +171,12 @@ if(ABSL_ENABLE_INSTALL)
)
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION ${ABSL_INSTALL_CONFIGDIR}
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
endif() # absl_VERSION
install(DIRECTORY absl
DESTINATION ${ABSL_INSTALL_INCLUDEDIR}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.inc"
PATTERN "*.h"

@ -1,16 +0,0 @@
# Long Term Support (LTS) Branches
This repository contains periodic snapshots of the Abseil codebase that are
Long Term Support (LTS) branches. An LTS branch allows you to use a known
version of Abseil without interfering with other projects which may also, in
turn, use Abseil. (For more information about our releases, see the
[Abseil Release Management](https://abseil.io/about/releases) guide.)
## LTS Branches
The following lists LTS branches and the dates on which they have been released:
* [LTS Branch December 18, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_12_18/)
* [LTS Branch June 20, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_06_20/)
* [LTS Branch August 8, 2019](https://github.com/abseil/abseil-cpp/tree/lts_2019_08_08/)
* [LTS Branch February 25, 2020](https://github.com/abseil/abseil-cpp/tree/lts_2020_02_25/)

@ -9,7 +9,9 @@ standard library.
- [About Abseil](#about)
- [Quickstart](#quickstart)
- [Building Abseil](#build)
- [Support](#support)
- [Codemap](#codemap)
- [Releases](#releases)
- [License](#license)
- [Links](#links)
@ -42,14 +44,22 @@ the Abseil code, running tests, and getting a simple binary working.
<a name="build"></a>
## Building Abseil
[Bazel](https://bazel.build) is the official build system for Abseil,
which is supported on most major platforms (Linux, Windows, macOS, for example)
and compilers. See the [quickstart](https://abseil.io/docs/cpp/quickstart) for
more information on building Abseil using the Bazel build system.
[Bazel](https://bazel.build) and [CMake](https://cmake.org/) are the official
build systems for Abseil.
<a name="cmake"></a>
If you require CMake support, please check the
[CMake build instructions](CMake/README.md).
See the [quickstart](https://abseil.io/docs/cpp/quickstart) for more information
on building Abseil using the Bazel build system.
If you require CMake support, please check the [CMake build
instructions](CMake/README.md) and [CMake
Quickstart](https://abseil.io/docs/cpp/quickstart-cmake).
## Support
Abseil is officially supported on many platforms. See the [Abseil
platform support
guide](https://abseil.io/docs/cpp/platforms/platforms) for details on
supported operating systems, compilers, CPUs, etc.
## Codemap
@ -62,6 +72,9 @@ Abseil contains the following C++ library components:
* [`algorithm`](absl/algorithm/)
<br /> The `algorithm` library contains additions to the C++ `<algorithm>`
library and container-based versions of such algorithms.
* [`cleanup`](absl/cleanup/)
<br /> The `cleanup` library contains the control-flow-construct-like type
`absl::Cleanup` which is used for executing a callback on scope exit.
* [`container`](absl/container/)
<br /> The `container` library contains additional STL-style containers,
including Abseil's unordered "Swiss table" containers.
@ -79,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.
* [`status`](absl/status/)
<br /> The `status` contains abstractions for error handling, specifically
`absl::Status` and `absl::StatusOr<T>`.
* [`strings`](absl/strings/)
<br /> The `strings` library contains a variety of strings routines and
utilities, including a C++11-compatible version of the C++17
@ -97,6 +113,15 @@ Abseil contains the following C++ library components:
* [`utility`](absl/utility/)
<br /> The `utility` library contains utility and helper code.
## Releases
Abseil recommends users "live-at-head" (update to the latest commit from the
master branch as often as possible). However, we realize this philosophy doesn't
work for every project, so we also provide [Long Term Support
Releases](https://github.com/abseil/abseil-cpp/releases) to which we backport
fixes for severe bugs. See our [release
management](https://abseil.io/about/releases) document for more details.
## License
The Abseil C++ library is licensed under the terms of the Apache

@ -20,6 +20,7 @@ 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",
# 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",
@ -28,9 +29,9 @@ http_archive(
# Google benchmark.
http_archive(
name = "com_github_google_benchmark",
urls = ["https://github.com/google/benchmark/archive/16703ff83c1ae6d53e5155df3bb3ab0bc96083be.zip"],
strip_prefix = "benchmark-16703ff83c1ae6d53e5155df3bb3ab0bc96083be",
sha256 = "59f918c8ccd4d74b6ac43484467b500f1d64b40cc1010daa055375b322a43ba3",
urls = ["https://github.com/google/benchmark/archive/bf585a2789e30585b4e3ce6baf11ef2750b54677.zip"], # 2020-11-26T11:14:03Z
strip_prefix = "benchmark-bf585a2789e30585b4e3ce6baf11ef2750b54677",
sha256 = "2a778d821997df7d8646c9c59b8edb9a573a6e04c534c01892a40aa524a7b68c",
)
# C++ rules for Bazel.

@ -12,19 +12,32 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
load(
":compiler_config_setting.bzl",
"create_llvm_config",
)
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
create_llvm_config(
name = "llvm_compiler",
config_setting(
name = "clang_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "clang",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "msvc_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "msvc-cl",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "clang-cl_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "clang-cl",
},
visibility = [":__subpackages__"],
)
@ -42,15 +55,6 @@ config_setting(
],
)
config_setting(
name = "windows",
constraint_values = [
"@bazel_tools//platforms:x86_64",
"@bazel_tools//platforms:windows",
],
visibility = [":__subpackages__"],
)
config_setting(
name = "ppc",
values = {

@ -40,8 +40,8 @@ Pod::Spec.new do |s|
'USE_HEADERMAP' => 'NO',
'ALWAYS_SEARCH_USER_PATHS' => 'NO',
}
s.ios.deployment_target = '7.0'
s.osx.deployment_target = '10.9'
s.ios.deployment_target = '9.0'
s.osx.deployment_target = '10.10'
s.tvos.deployment_target = '9.0'
s.watchos.deployment_target = '2.0'
"""

@ -90,10 +90,10 @@ using ContainerPointerType =
// lookup of std::begin and std::end, i.e.
// using std::begin;
// using std::end;
// std::foo(begin(c), end(c);
// std::foo(begin(c), end(c));
// becomes
// std::foo(container_algorithm_internal::begin(c),
// container_algorithm_internal::end(c));
// container_algorithm_internal::end(c));
// These are meant for internal use only.
template <typename C>
@ -188,7 +188,7 @@ bool c_any_of(const C& c, Pred&& pred) {
// c_none_of()
//
// Container-based version of the <algorithm> `std::none_of()` function to
// test if no elements in a container fulfil a condition.
// test if no elements in a container fulfill a condition.
template <typename C, typename Pred>
bool c_none_of(const C& c, Pred&& pred) {
return std::none_of(container_algorithm_internal::c_begin(c),

@ -160,7 +160,8 @@ cc_library(
],
copts = ABSL_DEFAULT_COPTS,
linkopts = select({
"//absl:windows": [],
"//absl:msvc_compiler": [],
"//absl:clang-cl_compiler": [],
"//absl:wasm": [],
"//conditions:default": ["-pthread"],
}) + ABSL_DEFAULT_LINKOPTS,
@ -220,7 +221,10 @@ cc_library(
],
copts = ABSL_DEFAULT_COPTS,
linkopts = select({
"//absl:windows": [
"//absl:msvc_compiler": [
"-DEFAULTLIB:advapi32.lib",
],
"//absl:clang-cl_compiler": [
"-DEFAULTLIB:advapi32.lib",
],
"//absl:wasm": [],
@ -479,6 +483,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":base",
":config",
":core_headers",
],
@ -551,7 +556,9 @@ cc_test(
srcs = ["internal/low_level_alloc_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
tags = ["no_test_ios_x86_64"],
tags = [
"no_test_ios_x86_64",
],
deps = [
":malloc_internal",
"//absl/container:node_hash_map",
@ -586,31 +593,6 @@ cc_test(
],
)
cc_library(
name = "bits",
hdrs = ["internal/bits.h"],
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
"//absl:__subpackages__",
],
deps = [
":config",
":core_headers",
],
)
cc_test(
name = "bits_test",
size = "small",
srcs = ["internal/bits_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":bits",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "exponential_biased",
srcs = ["internal/exponential_biased.cc"],

@ -418,6 +418,7 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::base
absl::config
absl::core_headers
PUBLIC
@ -518,30 +519,6 @@ absl_cc_test(
gtest_main
)
absl_cc_library(
NAME
bits
HDRS
"internal/bits.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::config
absl::core_headers
)
absl_cc_test(
NAME
bits_test
SRCS
"internal/bits_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::bits
gtest_main
)
absl_cc_library(
NAME
exponential_biased

@ -18,8 +18,6 @@
// These macros are used within Abseil and allow the compiler to optimize, where
// applicable, certain function calls.
//
// This file is used for both C and C++!
//
// Most macros here are exposing GCC or Clang features, and are stubbed out for
// other compilers.
//
@ -121,7 +119,7 @@
#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls)
#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
#elif defined(__GNUC__) && !defined(__clang__)
#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__)
#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
#define ABSL_ATTRIBUTE_NO_TAIL_CALL \
__attribute__((optimize("no-optimize-sibling-calls")))
@ -607,6 +605,7 @@
// 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
@ -645,7 +644,7 @@
// Every usage of a deprecated entity will trigger a warning when compiled with
// clang's `-Wdeprecated-declarations` option. This option is turned off by
// default, but the warnings will be reported by clang-tidy.
#if defined(__clang__) && __cplusplus >= 201103L
#if defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L
#define ABSL_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
@ -679,4 +678,25 @@
#define ABSL_CONST_INIT
#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
// ABSL_ATTRIBUTE_PURE_FUNCTION
//
// ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure"
// functions. A function is pure if its return value is only a function of its
// arguments. The pure attribute prohibits a function from modifying the state
// of the program that is observable by means other than inspecting the
// function's return value. Declaring such functions with the pure attribute
// allows the compiler to avoid emitting some calls in repeated invocations of
// the function with the same argument values.
//
// Example:
//
// ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d);
#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure)
#define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]]
#elif ABSL_HAVE_ATTRIBUTE(pure)
#define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure))
#else
#define ABSL_ATTRIBUTE_PURE_FUNCTION
#endif
#endif // ABSL_BASE_ATTRIBUTES_H_

@ -177,15 +177,8 @@ void CallOnceImpl(std::atomic<uint32_t>* control,
scheduling_mode) == kOnceInit) {
base_internal::invoke(std::forward<Callable>(fn),
std::forward<Args>(args)...);
// The call to SpinLockWake below is an optimization, because the waiter
// in SpinLockWait is waiting with a short timeout. The atomic load/store
// sequence is slightly faster than an atomic exchange:
// old_control = control->exchange(base_internal::kOnceDone,
// std::memory_order_release);
// We opt for a slightly faster case when there are no waiters, in spite
// of longer tail latency when there are waiters.
old_control = control->load(std::memory_order_relaxed);
control->store(base_internal::kOnceDone, std::memory_order_release);
old_control =
control->exchange(base_internal::kOnceDone, std::memory_order_release);
if (old_control == base_internal::kOnceWaiter) {
base_internal::SpinLockWake(control, true);
}

@ -121,10 +121,16 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0
#define ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_END
#define ABSL_INTERNAL_C_SYMBOL(x) x
#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1
#define ABSL_NAMESPACE_BEGIN \
inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME {
#define ABSL_NAMESPACE_END }
#define ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) x##_##v
#define ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, v) \
ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v)
#define ABSL_INTERNAL_C_SYMBOL(x) \
ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, ABSL_OPTION_INLINE_NAMESPACE_NAME)
#else
#error options.h is misconfigured.
#endif
@ -216,6 +222,8 @@ 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
#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1
#endif
#endif
@ -364,7 +372,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
defined(__ASYLO__)
defined(__ASYLO__) || defined(__myriad2__)
#define ABSL_HAVE_MMAP 1
#endif
@ -379,6 +387,15 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
#endif
// ABSL_HAVE_SCHED_GETCPU
//
// Checks whether sched_getcpu is available.
#ifdef ABSL_HAVE_SCHED_GETCPU
#error ABSL_HAVE_SCHED_GETCPU cannot be directly set
#elif defined(__linux__)
#define ABSL_HAVE_SCHED_GETCPU 1
#endif
// ABSL_HAVE_SCHED_YIELD
//
// Checks whether the platform implements sched_yield(2) as defined in
@ -490,7 +507,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#endif
#ifdef __has_include
#if __has_include(<any>) && __cplusplus >= 201703L && \
#if __has_include(<any>) && defined(__cplusplus) && __cplusplus >= 201703L && \
!ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_ANY 1
#endif
@ -504,8 +521,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#endif
#ifdef __has_include
#if __has_include(<optional>) && __cplusplus >= 201703L && \
!ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#if __has_include(<optional>) && defined(__cplusplus) && \
__cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_OPTIONAL 1
#endif
#endif
@ -518,8 +535,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#endif
#ifdef __has_include
#if __has_include(<variant>) && __cplusplus >= 201703L && \
!ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#if __has_include(<variant>) && defined(__cplusplus) && \
__cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_VARIANT 1
#endif
#endif
@ -532,7 +549,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#endif
#ifdef __has_include
#if __has_include(<string_view>) && __cplusplus >= 201703L
#if __has_include(<string_view>) && defined(__cplusplus) && \
__cplusplus >= 201703L
#define ABSL_HAVE_STD_STRING_VIEW 1
#endif
#endif
@ -544,8 +562,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language
// version.
// TODO(zhangxy): fix tests before enabling aliasing for `std::any`.
#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402)
#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || \
(defined(__cplusplus) && __cplusplus > 201402))
// #define ABSL_HAVE_STD_ANY 1
#define ABSL_HAVE_STD_OPTIONAL 1
#define ABSL_HAVE_STD_VARIANT 1
@ -711,4 +730,13 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#define ABSL_HAVE_ADDRESS_SANITIZER 1
#endif
// ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
//
// Class template argument deduction is a language feature added in C++17.
#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
#error "ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION cannot be directly set."
#elif defined(__cpp_deduction_guides)
#define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1
#endif
#endif // ABSL_BASE_CONFIG_H_

@ -110,6 +110,9 @@
// Define race annotations.
#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1
// Some of the symbols used in this section (e.g. AnnotateBenignRaceSized) are
// defined by the compiler-based santizer implementation, not by the Abseil
// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL.
// -------------------------------------------------------------
// Annotations that suppress errors. It is usually better to express the
@ -286,17 +289,22 @@ ABSL_INTERNAL_END_EXTERN_C
// Define IGNORE_READS_BEGIN/_END annotations.
#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1
// Some of the symbols used in this section (e.g. AnnotateIgnoreReadsBegin) are
// defined by the compiler-based implementation, not by the Abseil
// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL.
// Request the analysis tool to ignore all reads in the current thread until
// ABSL_ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey
// reads, while still checking other reads and all writes.
// See also ABSL_ANNOTATE_UNPROTECTED_READ.
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__)
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin) \
(__FILE__, __LINE__)
// Stop ignoring reads.
#define ABSL_ANNOTATE_IGNORE_READS_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__)
#define ABSL_ANNOTATE_IGNORE_READS_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd) \
(__FILE__, __LINE__)
// Function prototypes of annotations provided by the compiler-based sanitizer
// implementation.
@ -316,16 +324,22 @@ ABSL_INTERNAL_END_EXTERN_C
// TODO(delesley) -- The exclusive lock here ignores writes as well, but
// allows IGNORE_READS_AND_WRITES to work properly.
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)()
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED( \
ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsBegin)) \
()
#define ABSL_ANNOTATE_IGNORE_READS_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)()
#define ABSL_ANNOTATE_IGNORE_READS_END() \
ABSL_INTERNAL_GLOBAL_SCOPED( \
ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsEnd)) \
()
ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsBegin()
ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL(
AbslInternalAnnotateIgnoreReadsBegin)()
ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE {}
ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsEnd()
ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL(
AbslInternalAnnotateIgnoreReadsEnd)()
ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE {}
#else

@ -1,219 +0,0 @@
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_BASE_INTERNAL_BITS_H_
#define ABSL_BASE_INTERNAL_BITS_H_
// This file contains bitwise ops which are implementation details of various
// absl libraries.
#include <cstdint>
#include "absl/base/config.h"
// Clang on Windows has __builtin_clzll; otherwise we need to use the
// windows intrinsic functions.
#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
#if defined(_M_X64)
#pragma intrinsic(_BitScanReverse64)
#pragma intrinsic(_BitScanForward64)
#endif
#pragma intrinsic(_BitScanReverse)
#pragma intrinsic(_BitScanForward)
#endif
#include "absl/base/attributes.h"
#if defined(_MSC_VER) && !defined(__clang__)
// We can achieve something similar to attribute((always_inline)) with MSVC by
// using the __forceinline keyword, however this is not perfect. MSVC is
// much less aggressive about inlining, and even with the __forceinline keyword.
#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline
#else
// Use default attribute inline.
#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) {
int zeroes = 60;
if (n >> 32) {
zeroes -= 32;
n >>= 32;
}
if (n >> 16) {
zeroes -= 16;
n >>= 16;
}
if (n >> 8) {
zeroes -= 8;
n >>= 8;
}
if (n >> 4) {
zeroes -= 4;
n >>= 4;
}
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
// MSVC does not have __buitin_clzll. Use _BitScanReverse64.
unsigned long result = 0; // NOLINT(runtime/int)
if (_BitScanReverse64(&result, n)) {
return 63 - result;
}
return 64;
#elif defined(_MSC_VER) && !defined(__clang__)
// MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
unsigned long result = 0; // NOLINT(runtime/int)
if ((n >> 32) &&
_BitScanReverse(&result, static_cast<unsigned long>(n >> 32))) {
return 31 - result;
}
if (_BitScanReverse(&result, static_cast<unsigned long>(n))) {
return 63 - result;
}
return 64;
#elif defined(__GNUC__) || defined(__clang__)
// Use __builtin_clzll, which uses the following instructions:
// x86: bsr
// ARM64: clz
// PPC: cntlzd
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
"__builtin_clzll does not take 64-bit arg");
// Handle 0 as a special case because __builtin_clzll(0) is undefined.
if (n == 0) {
return 64;
}
return __builtin_clzll(n);
#else
return CountLeadingZeros64Slow(n);
#endif
}
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
int zeroes = 28;
if (n >> 16) {
zeroes -= 16;
n >>= 16;
}
if (n >> 8) {
zeroes -= 8;
n >>= 8;
}
if (n >> 4) {
zeroes -= 4;
n >>= 4;
}
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
if (_BitScanReverse(&result, n)) {
return 31 - result;
}
return 32;
#elif defined(__GNUC__) || defined(__clang__)
// Use __builtin_clz, which uses the following instructions:
// x86: bsr
// ARM64: clz
// PPC: cntlzd
static_assert(sizeof(int) == sizeof(n),
"__builtin_clz does not take 32-bit arg");
// Handle 0 as a special case because __builtin_clz(0) is undefined.
if (n == 0) {
return 32;
}
return __builtin_clz(n);
#else
return CountLeadingZeros32Slow(n);
#endif
}
ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) {
int c = 63;
n &= ~n + 1;
if (n & 0x00000000FFFFFFFF) c -= 32;
if (n & 0x0000FFFF0000FFFF) c -= 16;
if (n & 0x00FF00FF00FF00FF) c -= 8;
if (n & 0x0F0F0F0F0F0F0F0F) c -= 4;
if (n & 0x3333333333333333) c -= 2;
if (n & 0x5555555555555555) c -= 1;
return c;
}
ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
unsigned long result = 0; // NOLINT(runtime/int)
_BitScanForward64(&result, n);
return result;
#elif defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
if (static_cast<uint32_t>(n) == 0) {
_BitScanForward(&result, static_cast<unsigned long>(n >> 32));
return result + 32;
}
_BitScanForward(&result, static_cast<unsigned long>(n));
return result;
#elif defined(__GNUC__) || defined(__clang__)
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
"__builtin_ctzll does not take 64-bit arg");
return __builtin_ctzll(n);
#else
return CountTrailingZerosNonZero64Slow(n);
#endif
}
ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) {
int c = 31;
n &= ~n + 1;
if (n & 0x0000FFFF) c -= 16;
if (n & 0x00FF00FF) c -= 8;
if (n & 0x0F0F0F0F) c -= 4;
if (n & 0x33333333) c -= 2;
if (n & 0x55555555) c -= 1;
return c;
}
ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
_BitScanForward(&result, n);
return result;
#elif defined(__GNUC__) || defined(__clang__)
static_assert(sizeof(int) == sizeof(n),
"__builtin_ctz does not take 32-bit arg");
return __builtin_ctz(n);
#else
return CountTrailingZerosNonZero32Slow(n);
#endif
}
#undef ABSL_BASE_INTERNAL_FORCEINLINE
} // namespace base_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_BITS_H_

@ -1,97 +0,0 @@
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/base/internal/bits.h"
#include "gtest/gtest.h"
namespace {
int CLZ64(uint64_t n) {
int fast = absl::base_internal::CountLeadingZeros64(n);
int slow = absl::base_internal::CountLeadingZeros64Slow(n);
EXPECT_EQ(fast, slow) << n;
return fast;
}
TEST(BitsTest, CountLeadingZeros64) {
EXPECT_EQ(64, CLZ64(uint64_t{}));
EXPECT_EQ(0, CLZ64(~uint64_t{}));
for (int index = 0; index < 64; index++) {
uint64_t x = static_cast<uint64_t>(1) << index;
const auto cnt = 63 - index;
ASSERT_EQ(cnt, CLZ64(x)) << index;
ASSERT_EQ(cnt, CLZ64(x + x - 1)) << index;
}
}
int CLZ32(uint32_t n) {
int fast = absl::base_internal::CountLeadingZeros32(n);
int slow = absl::base_internal::CountLeadingZeros32Slow(n);
EXPECT_EQ(fast, slow) << n;
return fast;
}
TEST(BitsTest, CountLeadingZeros32) {
EXPECT_EQ(32, CLZ32(uint32_t{}));
EXPECT_EQ(0, CLZ32(~uint32_t{}));
for (int index = 0; index < 32; index++) {
uint32_t x = static_cast<uint32_t>(1) << index;
const auto cnt = 31 - index;
ASSERT_EQ(cnt, CLZ32(x)) << index;
ASSERT_EQ(cnt, CLZ32(x + x - 1)) << index;
ASSERT_EQ(CLZ64(x), CLZ32(x) + 32);
}
}
int CTZ64(uint64_t n) {
int fast = absl::base_internal::CountTrailingZerosNonZero64(n);
int slow = absl::base_internal::CountTrailingZerosNonZero64Slow(n);
EXPECT_EQ(fast, slow) << n;
return fast;
}
TEST(BitsTest, CountTrailingZerosNonZero64) {
EXPECT_EQ(0, CTZ64(~uint64_t{}));
for (int index = 0; index < 64; index++) {
uint64_t x = static_cast<uint64_t>(1) << index;
const auto cnt = index;
ASSERT_EQ(cnt, CTZ64(x)) << index;
ASSERT_EQ(cnt, CTZ64(~(x - 1))) << index;
}
}
int CTZ32(uint32_t n) {
int fast = absl::base_internal::CountTrailingZerosNonZero32(n);
int slow = absl::base_internal::CountTrailingZerosNonZero32Slow(n);
EXPECT_EQ(fast, slow) << n;
return fast;
}
TEST(BitsTest, CountTrailingZerosNonZero32) {
EXPECT_EQ(0, CTZ32(~uint32_t{}));
for (int index = 0; index < 32; index++) {
uint32_t x = static_cast<uint32_t>(1) << index;
const auto cnt = index;
ASSERT_EQ(cnt, CTZ32(x)) << index;
ASSERT_EQ(cnt, CTZ32(~(x - 1))) << index;
}
}
} // namespace

@ -74,10 +74,13 @@ namespace base_internal {
inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
off64_t offset) noexcept {
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \
defined(__m68k__) || defined(__sh__) || \
(defined(__hppa__) && !defined(__LP64__)) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \
(defined(__PPC__) && !defined(__PPC64__)) || \
(defined(__riscv) && __riscv_xlen == 32) || \
(defined(__s390__) && !defined(__s390x__))
(defined(__s390__) && !defined(__s390x__)) || \
(defined(__sparc__) && !defined(__arch64__))
// On these architectures, implement mmap with mmap2.
static int pagesize = 0;
if (pagesize == 0) {

@ -26,6 +26,7 @@
#endif
#include <cstdint>
#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/internal/unaligned_access.h"
#include "absl/base/port.h"
@ -173,6 +174,36 @@ inline constexpr bool IsLittleEndian() { return false; }
#endif /* ENDIAN */
inline uint8_t FromHost(uint8_t x) { return x; }
inline uint16_t FromHost(uint16_t x) { return FromHost16(x); }
inline uint32_t FromHost(uint32_t x) { return FromHost32(x); }
inline uint64_t FromHost(uint64_t x) { return FromHost64(x); }
inline uint8_t ToHost(uint8_t x) { return x; }
inline uint16_t ToHost(uint16_t x) { return ToHost16(x); }
inline uint32_t ToHost(uint32_t x) { return ToHost32(x); }
inline uint64_t ToHost(uint64_t x) { return ToHost64(x); }
inline int8_t FromHost(int8_t x) { return x; }
inline int16_t FromHost(int16_t x) {
return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x)));
}
inline int32_t FromHost(int32_t x) {
return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x)));
}
inline int64_t FromHost(int64_t x) {
return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x)));
}
inline int8_t ToHost(int8_t x) { return x; }
inline int16_t ToHost(int16_t x) {
return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x)));
}
inline int32_t ToHost(int32_t x) {
return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x)));
}
inline int64_t ToHost(int64_t x) {
return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x)));
}
// Functions to do unaligned loads and stores in little-endian order.
inline uint16_t Load16(const void *p) {
return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
@ -233,6 +264,36 @@ inline constexpr bool IsLittleEndian() { return false; }
#endif /* ENDIAN */
inline uint8_t FromHost(uint8_t x) { return x; }
inline uint16_t FromHost(uint16_t x) { return FromHost16(x); }
inline uint32_t FromHost(uint32_t x) { return FromHost32(x); }
inline uint64_t FromHost(uint64_t x) { return FromHost64(x); }
inline uint8_t ToHost(uint8_t x) { return x; }
inline uint16_t ToHost(uint16_t x) { return ToHost16(x); }
inline uint32_t ToHost(uint32_t x) { return ToHost32(x); }
inline uint64_t ToHost(uint64_t x) { return ToHost64(x); }
inline int8_t FromHost(int8_t x) { return x; }
inline int16_t FromHost(int16_t x) {
return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x)));
}
inline int32_t FromHost(int32_t x) {
return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x)));
}
inline int64_t FromHost(int64_t x) {
return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x)));
}
inline int8_t ToHost(int8_t x) { return x; }
inline int16_t ToHost(int16_t x) {
return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x)));
}
inline int32_t ToHost(int32_t x) {
return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x)));
}
inline int64_t ToHost(int64_t x) {
return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x)));
}
// Functions to do unaligned loads and stores in big-endian order.
inline uint16_t Load16(const void *p) {
return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));

@ -185,7 +185,7 @@ TEST(ExponentialBiasedTest, InitializationModes) {
ABSL_CONST_INIT static ExponentialBiased eb_static;
EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0));
#if ABSL_HAVE_THREAD_LOCAL
#ifdef ABSL_HAVE_THREAD_LOCAL
thread_local ExponentialBiased eb_thread;
EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0));
#endif

@ -21,6 +21,10 @@
#include <unordered_map>
#include <utility>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "absl/container/node_hash_map.h"
namespace absl {
@ -158,5 +162,20 @@ ABSL_NAMESPACE_END
int main(int argc, char *argv[]) {
// The actual test runs in the global constructor of `before_main`.
printf("PASS\n");
#ifdef __EMSCRIPTEN__
// clang-format off
// This is JS here. Don't try to format it.
MAIN_THREAD_EM_ASM({
if (ENVIRONMENT_IS_WEB) {
if (typeof TEST_FINISH === 'function') {
TEST_FINISH($0);
} else {
console.error('Attempted to exit with status ' + $0);
console.error('But TEST_FINSIHED is not a function.');
}
}
}, 0);
// clang-format on
#endif
return 0;
}

@ -61,6 +61,8 @@ class SchedulingGuard {
public:
// Returns true iff the calling thread may be cooperatively rescheduled.
static bool ReschedulingIsAllowed();
SchedulingGuard(const SchedulingGuard&) = delete;
SchedulingGuard& operator=(const SchedulingGuard&) = delete;
private:
// Disable cooperative rescheduling of the calling thread. It may still
@ -101,9 +103,6 @@ class SchedulingGuard {
friend class SchedulingHelper;
friend class SpinLock;
friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode);
SchedulingGuard(const SchedulingGuard&) = delete;
SchedulingGuard& operator=(const SchedulingGuard&) = delete;
};
//------------------------------------------------------------------------------

@ -67,28 +67,32 @@
#undef ABSL_HAVE_RAW_IO
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace raw_logging_internal {
namespace {
// TODO(gfalcon): We want raw-logging to work on as many platforms as possible.
// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a
// selected set of platforms for which we expect not to be able to raw log.
// Explicitly `#error` out when not `ABSL_LOW_LEVEL_WRITE_SUPPORTED`, except for
// a selected set of platforms for which we expect not to be able to raw log.
ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
absl::raw_logging_internal::LogPrefixHook>
log_prefix_hook;
ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
absl::raw_logging_internal::AbortHook>
abort_hook;
ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
absl::base_internal::AtomicHook<LogPrefixHook>
log_prefix_hook;
ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
absl::base_internal::AtomicHook<AbortHook>
abort_hook;
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
static const char kTruncated[] = " ... (message truncated)\n";
constexpr char kTruncated[] = " ... (message truncated)\n";
// sprintf the format to the buffer, adjusting *buf and *size to reflect the
// consumed bytes, and return whether the message fit without truncation. If
// truncation occurred, if possible leave room in the buffer for the message
// kTruncated[].
inline static bool VADoRawLog(char** buf, int* size, const char* format,
va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0);
inline static bool VADoRawLog(char** buf, int* size,
const char* format, va_list ap) {
bool VADoRawLog(char** buf, int* size, const char* format, va_list ap)
ABSL_PRINTF_ATTRIBUTE(3, 0);
bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) {
int n = vsnprintf(*buf, *size, format, ap);
bool result = true;
if (n < 0 || n > *size) {
@ -96,7 +100,7 @@ inline static bool VADoRawLog(char** buf, int* size,
if (static_cast<size_t>(*size) > sizeof(kTruncated)) {
n = *size - sizeof(kTruncated); // room for truncation message
} else {
n = 0; // no room for truncation message
n = 0; // no room for truncation message
}
}
*size -= n;
@ -105,9 +109,7 @@ inline static bool VADoRawLog(char** buf, int* size,
}
#endif // ABSL_LOW_LEVEL_WRITE_SUPPORTED
static constexpr int kLogBufSize = 3000;
namespace {
constexpr int kLogBufSize = 3000;
// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths
// that invoke malloc() and getenv() that might acquire some locks.
@ -166,7 +168,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line,
} else {
DoRawLog(&buf, &size, "%s", kTruncated);
}
absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer));
SafeWriteToStderr(buffer, strlen(buffer));
}
#else
static_cast<void>(format);
@ -181,11 +183,18 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line,
}
}
// Non-formatting version of RawLog().
//
// TODO(gfalcon): When string_view no longer depends on base, change this
// interface to take its message as a string_view instead.
void DefaultInternalLog(absl::LogSeverity severity, const char* file, int line,
const std::string& message) {
RawLog(severity, file, line, "%.*s", static_cast<int>(message.size()),
message.data());
}
} // namespace
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace raw_logging_internal {
void SafeWriteToStderr(const char *s, size_t len) {
#if defined(ABSL_HAVE_SYSCALL_WRITE)
syscall(SYS_write, STDERR_FILENO, s, len);
@ -200,8 +209,6 @@ void SafeWriteToStderr(const char *s, size_t len) {
#endif
}
void RawLog(absl::LogSeverity severity, const char* file, int line,
const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
void RawLog(absl::LogSeverity severity, const char* file, int line,
const char* format, ...) {
va_list ap;
@ -210,15 +217,6 @@ void RawLog(absl::LogSeverity severity, const char* file, int line,
va_end(ap);
}
// Non-formatting version of RawLog().
//
// TODO(gfalcon): When string_view no longer depends on base, change this
// interface to take its message as a string_view instead.
static void DefaultInternalLog(absl::LogSeverity severity, const char* file,
int line, const std::string& message) {
RawLog(severity, file, line, "%s", message.c_str());
}
bool RawLoggingFullySupported() {
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
return true;
@ -231,6 +229,10 @@ ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL
absl::base_internal::AtomicHook<InternalLogFunction>
internal_log_function(DefaultInternalLog);
void RegisterLogPrefixHook(LogPrefixHook func) { log_prefix_hook.Store(func); }
void RegisterAbortHook(AbortHook func) { abort_hook.Store(func); }
void RegisterInternalLogFunction(InternalLogFunction func) {
internal_log_function.Store(func);
}

@ -72,12 +72,14 @@
//
// The API is a subset of the above: each macro only takes two arguments. Use
// StrCat if you need to build a richer message.
#define ABSL_INTERNAL_LOG(severity, message) \
do { \
constexpr const char* absl_raw_logging_internal_filename = __FILE__; \
::absl::raw_logging_internal::internal_log_function( \
ABSL_RAW_LOGGING_INTERNAL_##severity, \
absl_raw_logging_internal_filename, __LINE__, message); \
#define ABSL_INTERNAL_LOG(severity, message) \
do { \
constexpr const char* absl_raw_logging_internal_filename = __FILE__; \
::absl::raw_logging_internal::internal_log_function( \
ABSL_RAW_LOGGING_INTERNAL_##severity, \
absl_raw_logging_internal_filename, __LINE__, message); \
if (ABSL_RAW_LOGGING_INTERNAL_##severity == ::absl::LogSeverity::kFatal) \
ABSL_INTERNAL_UNREACHABLE; \
} while (0)
#define ABSL_INTERNAL_CHECK(condition, message) \
@ -176,6 +178,14 @@ ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL extern base_internal::AtomicHook<
InternalLogFunction>
internal_log_function;
// Registers hooks of the above types. Only a single hook of each type may be
// registered. It is an error to call these functions multiple times with
// different input arguments.
//
// These functions are safe to call at any point during initialization; they do
// not block or malloc, and are async-signal safe.
void RegisterLogPrefixHook(LogPrefixHook func);
void RegisterAbortHook(AbortHook func);
void RegisterInternalLogFunction(InternalLogFunction func);
} // namespace raw_logging_internal

@ -125,8 +125,9 @@ void SpinLock::SlowLock() {
// it as having a sleeper.
if ((lock_value & kWaitTimeMask) == 0) {
// Here, just "mark" that the thread is going to sleep. Don't store the
// lock wait time in the lock as that will cause the current lock
// owner to think it experienced contention.
// lock wait time in the lock -- the lock word stores the amount of time
// that the current holder waited before acquiring the lock, not the wait
// time of any thread currently waiting to acquire it.
if (lockword_.compare_exchange_strong(
lock_value, lock_value | kSpinLockSleeper,
std::memory_order_relaxed, std::memory_order_relaxed)) {
@ -140,6 +141,14 @@ void SpinLock::SlowLock() {
// this thread obtains the lock.
lock_value = TryLockInternal(lock_value, wait_cycles);
continue; // Skip the delay at the end of the loop.
} else if ((lock_value & kWaitTimeMask) == 0) {
// The lock is still held, without a waiter being marked, but something
// else about the lock word changed, causing our CAS to fail. For
// example, a new lock holder may have acquired the lock with
// kSpinLockDisabledScheduling set, whereas the previous holder had not
// set that flag. In this case, attempt again to mark ourselves as a
// waiter.
continue;
}
}

@ -15,11 +15,8 @@
//
// Most users requiring mutual exclusion should use Mutex.
// SpinLock is provided for use in three situations:
// SpinLock is provided for use in two situations:
// - for use in code that Mutex itself depends on
// - to get a faster fast-path release under low contention (without an
// atomic read-modify-write) In return, SpinLock has worse behaviour under
// contention, which is why Mutex is preferred in most situations.
// - for async signal safety (see below)
// SpinLock is async signal safe. If a spinlock is used within a signal
@ -140,8 +137,20 @@ class ABSL_LOCKABLE SpinLock {
//
// bit[0] encodes whether a lock is being held.
// bit[1] encodes whether a lock uses cooperative scheduling.
// bit[2] encodes whether a lock disables scheduling.
// bit[2] encodes whether the current lock holder disabled scheduling when
// acquiring the lock. Only set when kSpinLockHeld is also set.
// bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int.
// This is set by the lock holder to indicate how long it waited on
// the lock before eventually acquiring it. The number of cycles is
// encoded as a 29-bit unsigned int, or in the case that the current
// holder did not wait but another waiter is queued, the LSB
// (kSpinLockSleeper) is set. The implementation does not explicitly
// track the number of queued waiters beyond this. It must always be
// assumed that waiters may exist if the current holder was required to
// queue.
//
// Invariant: if the lock is not held, the value is either 0 or
// kSpinLockCooperative.
static constexpr uint32_t kSpinLockHeld = 1;
static constexpr uint32_t kSpinLockCooperative = 2;
static constexpr uint32_t kSpinLockDisabledScheduling = 4;

@ -20,7 +20,7 @@
extern "C" {
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */,
int /* loop */, absl::base_internal::SchedulingMode /* mode */) {
// In Akaros, one must take care not to call anything that could cause a
@ -29,7 +29,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
// arbitrary code.
}
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(
ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(
std::atomic<uint32_t>* /* lock_word */, bool /* all */) {}
} // extern "C"

@ -56,7 +56,7 @@ static_assert(sizeof(std::atomic<uint32_t>) == sizeof(int),
extern "C" {
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t> *w, uint32_t value, int loop,
absl::base_internal::SchedulingMode) {
absl::base_internal::ErrnoSaver errno_saver;
@ -66,8 +66,8 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
}
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w,
bool all) {
ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(
std::atomic<uint32_t> *w, bool all) {
syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0);
}

@ -25,7 +25,7 @@
extern "C" {
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop,
absl::base_internal::SchedulingMode /* mode */) {
absl::base_internal::ErrnoSaver errno_saver;
@ -40,7 +40,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
}
}
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(
ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(
std::atomic<uint32_t>* /* lock_word */, bool /* all */) {}
} // extern "C"

@ -43,18 +43,16 @@ uint32_t SpinLockWait(std::atomic<uint32_t> *w, int n,
const SpinLockWaitTransition trans[],
SchedulingMode scheduling_mode);
// If possible, wake some thread that has called SpinLockDelay(w, ...). If
// "all" is true, wake all such threads. This call is a hint, and on some
// systems it may be a no-op; threads calling SpinLockDelay() will always wake
// eventually even if SpinLockWake() is never called.
// If possible, wake some thread that has called SpinLockDelay(w, ...). If `all`
// is true, wake all such threads. On some systems, this may be a no-op; on
// those systems, threads calling SpinLockDelay() will always wake eventually
// even if SpinLockWake() is never called.
void SpinLockWake(std::atomic<uint32_t> *w, bool all);
// Wait for an appropriate spin delay on iteration "loop" of a
// spin loop on location *w, whose previously observed value was "value".
// SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick,
// or may wait for a delay that can be truncated by a call to SpinLockWake(w).
// In all cases, it must return in bounded time even if SpinLockWake() is not
// called.
// or may wait for a call to SpinLockWake(w).
void SpinLockDelay(std::atomic<uint32_t> *w, uint32_t value, int loop,
base_internal::SchedulingMode scheduling_mode);
@ -73,21 +71,23 @@ ABSL_NAMESPACE_END
// By changing our extension points to be extern "C", we dodge this
// check.
extern "C" {
void AbslInternalSpinLockWake(std::atomic<uint32_t> *w, bool all);
void AbslInternalSpinLockDelay(
void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(std::atomic<uint32_t> *w,
bool all);
void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t> *w, uint32_t value, int loop,
absl::base_internal::SchedulingMode scheduling_mode);
}
inline void absl::base_internal::SpinLockWake(std::atomic<uint32_t> *w,
bool all) {
AbslInternalSpinLockWake(w, all);
ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(w, all);
}
inline void absl::base_internal::SpinLockDelay(
std::atomic<uint32_t> *w, uint32_t value, int loop,
absl::base_internal::SchedulingMode scheduling_mode) {
AbslInternalSpinLockDelay(w, value, loop, scheduling_mode);
ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)
(w, value, loop, scheduling_mode);
}
#endif // ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_

@ -20,9 +20,9 @@
extern "C" {
void AbslInternalSpinLockDelay(std::atomic<uint32_t>* /* lock_word */,
uint32_t /* value */, int loop,
absl::base_internal::SchedulingMode /* mode */) {
void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop,
absl::base_internal::SchedulingMode /* mode */) {
if (loop == 0) {
} else if (loop == 1) {
Sleep(0);
@ -31,7 +31,7 @@ void AbslInternalSpinLockDelay(std::atomic<uint32_t>* /* lock_word */,
}
}
void AbslInternalSpinLockWake(std::atomic<uint32_t>* /* lock_word */,
bool /* all */) {}
void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(
std::atomic<uint32_t>* /* lock_word */, bool /* all */) {}
} // extern "C"

@ -51,7 +51,6 @@ const char* StrErrorAdaptor(int errnum, char* buf, size_t buflen) {
}
std::string StrErrorInternal(int errnum) {
absl::base_internal::ErrnoSaver errno_saver;
char buf[100];
const char* str = StrErrorAdaptor(errnum, buf, sizeof buf);
if (*str == '\0') {
@ -76,6 +75,7 @@ std::array<std::string, kSysNerr>* NewStrErrorTable() {
} // namespace
std::string StrError(int errnum) {
absl::base_internal::ErrnoSaver errno_saver;
static const auto* table = NewStrErrorTable();
if (errnum >= 0 && errnum < static_cast<int>(table->size())) {
return (*table)[errnum];

@ -62,12 +62,14 @@ TEST(StrErrorTest, MultipleThreads) {
++counter;
errno = ERANGE;
const std::string value = absl::base_internal::StrError(i);
// EXPECT_* could change errno. Stash it first.
int check_err = errno;
EXPECT_THAT(check_err, Eq(ERANGE));
// Only the GNU implementation is guaranteed to provide the
// string "Unknown error nnn". POSIX doesn't say anything.
if (!absl::StartsWith(value, "Unknown error ")) {
EXPECT_THAT(absl::base_internal::StrError(i), Eq(expected_strings[i]));
EXPECT_THAT(value, Eq(expected_strings[i]));
}
EXPECT_THAT(errno, Eq(ERANGE));
}
};

@ -426,7 +426,7 @@ pid_t GetTID() {
// userspace construct) to avoid unnecessary system calls. Without this caching,
// it can take roughly 98ns, while it takes roughly 1ns with this caching.
pid_t GetCachedTID() {
#if ABSL_HAVE_THREAD_LOCAL
#ifdef ABSL_HAVE_THREAD_LOCAL
static thread_local pid_t thread_id = GetTID();
return thread_id;
#else

@ -37,17 +37,28 @@ TEST(SysinfoTest, NumCPUs) {
<< "NumCPUs() should not have the default value of 0";
}
// Ensure that NominalCPUFrequency returns a reasonable value, or 1.00 on
// platforms where the CPU frequency is not available through sysfs.
//
// POWER is particularly problematic here; some Linux kernels expose the CPU
// frequency, while others do not. Since we can't predict a priori what a given
// machine is going to do, just disable this test on POWER on Linux.
#if !(defined(__linux) && (defined(__ppc64__) || defined(__PPC64__)))
TEST(SysinfoTest, NominalCPUFrequency) {
#if !(defined(__aarch64__) && defined(__linux__)) && !defined(__EMSCRIPTEN__)
EXPECT_GE(NominalCPUFrequency(), 1000.0)
<< "NominalCPUFrequency() did not return a reasonable value";
#else
// Aarch64 cannot read the CPU frequency from sysfs, so we get back 1.0.
// Emscripten does not have a sysfs to read from at all.
// Linux only exposes the CPU frequency on certain architectures, and
// Emscripten doesn't expose it at all.
#if defined(__linux__) && \
(defined(__aarch64__) || defined(__hppa__) || defined(__mips__) || \
defined(__riscv) || defined(__s390x__)) || \
defined(__EMSCRIPTEN__)
EXPECT_EQ(NominalCPUFrequency(), 1.0)
<< "CPU frequency detection was fixed! Please update unittest.";
#else
EXPECT_GE(NominalCPUFrequency(), 1000.0)
<< "NominalCPUFrequency() did not return a reasonable value";
#endif
}
#endif
TEST(SysinfoTest, GetTID) {
EXPECT_EQ(GetTID(), GetTID()); // Basic compile and equality test.

@ -23,6 +23,7 @@
#include <cassert>
#include <memory>
#include "absl/base/attributes.h"
#include "absl/base/call_once.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/spinlock.h"
@ -53,9 +54,11 @@ void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) {
// exist within a process (via dlopen() or similar), references to
// thread_identity_ptr from each instance of the code will refer to
// *different* instances of this ptr.
#ifdef __GNUC__
// Apple platforms have the visibility attribute, but issue a compile warning
// that protected visibility is unsupported.
#if ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__)
__attribute__((visibility("protected")))
#endif // __GNUC__
#endif // ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__)
#if ABSL_PER_THREAD_TLS
// Prefer __thread to thread_local as benchmarks indicate it is a bit faster.
ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr = nullptr;

@ -32,6 +32,7 @@
#include "absl/base/config.h"
#include "absl/base/internal/per_thread_tls.h"
#include "absl/base/optimization.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@ -69,30 +70,28 @@ struct PerThreadSynch {
// is using this PerThreadSynch as a terminator. Its
// skip field must not be filled in because the loop
// might then skip over the terminator.
// The wait parameters of the current wait. waitp is null if the
// thread is not waiting. Transitions from null to non-null must
// occur before the enqueue commit point (state = kQueued in
// Enqueue() and CondVarEnqueue()). Transitions from non-null to
// null must occur after the wait is finished (state = kAvailable in
// Mutex::Block() and CondVar::WaitCommon()). This field may be
// changed only by the thread that describes this PerThreadSynch. A
// special case is Fer(), which calls Enqueue() on another thread,
// but with an identical SynchWaitParams pointer, thus leaving the
// pointer unchanged.
SynchWaitParams *waitp;
bool suppress_fatal_errors; // If true, try to proceed even in the face of
// broken invariants. This is used within fatal
// signal handlers to improve the chances of
// debug logging information being output
// successfully.
intptr_t readers; // Number of readers in mutex.
int priority; // Priority of thread (updated every so often).
// When priority will next be read (cycles).
int64_t next_priority_read_cycles;
bool wake; // This thread is to be woken from a Mutex.
// If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the
// waiter is waiting on the mutex as part of a CV Wait or Mutex Await.
//
// The value of "x->cond_waiter" is meaningless if "x" is not on a
// Mutex waiter list.
bool cond_waiter;
bool maybe_unlocking; // Valid at head of Mutex waiter queue;
// true if UnlockSlow could be searching
// for a waiter to wake. Used for an optimization
// in Enqueue(). true is always a valid value.
// Can be reset to false when the unlocker or any
// writer releases the lock, or a reader fully
// releases the lock. It may not be set to false
// by a reader that decrements the count to
// non-zero. protected by mutex spinlock
bool suppress_fatal_errors; // If true, try to proceed even in the face
// of broken invariants. This is used within
// fatal signal handlers to improve the
// chances of debug logging information being
// output successfully.
int priority; // Priority of thread (updated every so often).
// State values:
// kAvailable: This PerThreadSynch is available.
@ -111,30 +110,30 @@ struct PerThreadSynch {
};
std::atomic<State> state;
bool maybe_unlocking; // Valid at head of Mutex waiter queue;
// true if UnlockSlow could be searching
// for a waiter to wake. Used for an optimization
// in Enqueue(). true is always a valid value.
// Can be reset to false when the unlocker or any
// writer releases the lock, or a reader fully releases
// the lock. It may not be set to false by a reader
// that decrements the count to non-zero.
// protected by mutex spinlock
// The wait parameters of the current wait. waitp is null if the
// thread is not waiting. Transitions from null to non-null must
// occur before the enqueue commit point (state = kQueued in
// Enqueue() and CondVarEnqueue()). Transitions from non-null to
// null must occur after the wait is finished (state = kAvailable in
// Mutex::Block() and CondVar::WaitCommon()). This field may be
// changed only by the thread that describes this PerThreadSynch. A
// special case is Fer(), which calls Enqueue() on another thread,
// but with an identical SynchWaitParams pointer, thus leaving the
// pointer unchanged.
SynchWaitParams* waitp;
bool wake; // This thread is to be woken from a Mutex.
intptr_t readers; // Number of readers in mutex.
// If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the
// waiter is waiting on the mutex as part of a CV Wait or Mutex Await.
//
// The value of "x->cond_waiter" is meaningless if "x" is not on a
// Mutex waiter list.
bool cond_waiter;
// When priority will next be read (cycles).
int64_t next_priority_read_cycles;
// Locks held; used during deadlock detection.
// Allocated in Synch_GetAllLocks() and freed in ReclaimThreadIdentity().
SynchLocksHeld *all_locks;
};
// The instances of this class are allocated in NewThreadIdentity() with an
// alignment of PerThreadSynch::kAlignment.
struct ThreadIdentity {
// Must be the first member. The Mutex implementation requires that
// the PerThreadSynch object associated with each thread is
@ -144,7 +143,7 @@ struct ThreadIdentity {
// Private: Reserved for absl::synchronization_internal::Waiter.
struct WaiterState {
char data[128];
alignas(void*) char data[128];
} waiter_state;
// Used by PerThreadSem::{Get,Set}ThreadBlockedCounter().
@ -212,7 +211,9 @@ void ClearCurrentThreadIdentity();
#define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE
#elif defined(_WIN32) && !defined(__MINGW32__)
#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11
#elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \
#elif defined(__APPLE__) && defined(ABSL_HAVE_THREAD_LOCAL)
#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11
#elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \
(__GOOGLE_GRTE_VERSION__ >= 20140228L)
// Support for async-safe TLS was specifically added in GRTEv4. It's not
// present in the upstream eglibc.

@ -18,6 +18,7 @@
#include <functional>
#include <new>
#include <stdexcept>
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
@ -25,83 +26,186 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
// NOTE: The various STL exception throwing functions are placed within the
// #ifdef blocks so the symbols aren't exposed on platforms that don't support
// them, such as the Android NDK. For example, ANGLE fails to link when building
// within AOSP without them, since the STL functions don't exist.
namespace {
#ifdef ABSL_HAVE_EXCEPTIONS
template <typename T>
[[noreturn]] void Throw(const T& error) {
#ifdef ABSL_HAVE_EXCEPTIONS
throw error;
#else
ABSL_RAW_LOG(FATAL, "%s", error.what());
std::abort();
#endif
}
#endif
} // namespace
void ThrowStdLogicError(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::logic_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdLogicError(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::logic_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdInvalidArgument(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::invalid_argument(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdInvalidArgument(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::invalid_argument(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdDomainError(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::domain_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdDomainError(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::domain_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdLengthError(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::length_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdLengthError(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::length_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdOutOfRange(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::out_of_range(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdOutOfRange(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::out_of_range(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdRuntimeError(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::runtime_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdRuntimeError(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::runtime_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdRangeError(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::range_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdRangeError(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::range_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdOverflowError(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::overflow_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdOverflowError(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::overflow_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdUnderflowError(const std::string& what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::underflow_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
std::abort();
#endif
}
void ThrowStdUnderflowError(const char* what_arg) {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::underflow_error(what_arg));
#else
ABSL_RAW_LOG(FATAL, "%s", what_arg);
std::abort();
#endif
}
void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); }
void ThrowStdBadFunctionCall() {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::bad_function_call());
#else
std::abort();
#endif
}
void ThrowStdBadAlloc() { Throw(std::bad_alloc()); }
void ThrowStdBadAlloc() {
#ifdef ABSL_HAVE_EXCEPTIONS
Throw(std::bad_alloc());
#else
std::abort();
#endif
}
} // namespace base_internal
ABSL_NAMESPACE_END

@ -31,80 +31,6 @@
// The unaligned API is C++ only. The declarations use C++ features
// (namespaces, inline) which are absent or incompatible in C.
#if defined(__cplusplus)
#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
defined(ABSL_HAVE_THREAD_SANITIZER) || defined(ABSL_HAVE_MEMORY_SANITIZER)
// Consider we have an unaligned load/store of 4 bytes from address 0x...05.
// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and
// will miss a bug if 08 is the first unaddressable byte.
// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will
// miss a race between this access and some other accesses to 08.
// MemorySanitizer will correctly propagate the shadow on unaligned stores
// and correctly report bugs on unaligned loads, but it may not properly
// update and report the origin of the uninitialized memory.
// For all three tools, replacing an unaligned access with a tool-specific
// callback solves the problem.
// Make sure uint16_t/uint32_t/uint64_t are defined.
#include <stdint.h>
extern "C" {
uint16_t __sanitizer_unaligned_load16(const void *p);
uint32_t __sanitizer_unaligned_load32(const void *p);
uint64_t __sanitizer_unaligned_load64(const void *p);
void __sanitizer_unaligned_store16(void *p, uint16_t v);
void __sanitizer_unaligned_store32(void *p, uint32_t v);
void __sanitizer_unaligned_store64(void *p, uint64_t v);
} // extern "C"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
inline uint16_t UnalignedLoad16(const void *p) {
return __sanitizer_unaligned_load16(p);
}
inline uint32_t UnalignedLoad32(const void *p) {
return __sanitizer_unaligned_load32(p);
}
inline uint64_t UnalignedLoad64(const void *p) {
return __sanitizer_unaligned_load64(p);
}
inline void UnalignedStore16(void *p, uint16_t v) {
__sanitizer_unaligned_store16(p, v);
}
inline void UnalignedStore32(void *p, uint32_t v) {
__sanitizer_unaligned_store32(p, v);
}
inline void UnalignedStore64(void *p, uint64_t v) {
__sanitizer_unaligned_store64(p, v);
}
} // namespace base_internal
ABSL_NAMESPACE_END
} // namespace absl
#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
(absl::base_internal::UnalignedLoad16(_p))
#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
(absl::base_internal::UnalignedLoad32(_p))
#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
(absl::base_internal::UnalignedLoad64(_p))
#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
(absl::base_internal::UnalignedStore16(_p, _val))
#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
(absl::base_internal::UnalignedStore32(_p, _val))
#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
(absl::base_internal::UnalignedStore64(_p, _val))
#else
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
@ -151,8 +77,6 @@ ABSL_NAMESPACE_END
#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
(absl::base_internal::UnalignedStore64(_p, _val))
#endif
#endif // defined(__cplusplus), end of unaligned API
#endif // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_

@ -123,9 +123,7 @@ double UnscaledCycleClock::Frequency() {
#pragma intrinsic(__rdtsc)
int64_t UnscaledCycleClock::Now() {
return __rdtsc();
}
int64_t UnscaledCycleClock::Now() { return __rdtsc(); }
double UnscaledCycleClock::Frequency() {
return base_internal::NominalCPUFrequency();

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
#define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
#ifndef ABSL_BASE_LOG_SEVERITY_H_
#define ABSL_BASE_LOG_SEVERITY_H_
#include <array>
#include <ostream>
@ -36,7 +36,7 @@ ABSL_NAMESPACE_BEGIN
// such values to a defined severity level, however in some cases values other
// than the defined levels are useful for comparison.
//
// Exmaple:
// Example:
//
// // Effectively disables all logging:
// SetMinLogLevel(static_cast<absl::LogSeverity>(100));
@ -118,4 +118,4 @@ std::ostream& operator<<(std::ostream& os, absl::LogSeverity s);
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
#endif // ABSL_BASE_LOG_SEVERITY_H_

@ -144,4 +144,15 @@ ABSL_NAMESPACE_END
#define ABSL_INTERNAL_RETHROW do {} while (false)
#endif // ABSL_HAVE_EXCEPTIONS
// `ABSL_INTERNAL_UNREACHABLE` is an unreachable statement. A program which
// reaches one has undefined behavior, and the compiler may optimize
// accordingly.
#if defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable)
#define ABSL_INTERNAL_UNREACHABLE __builtin_unreachable()
#elif defined(_MSC_VER)
#define ABSL_INTERNAL_UNREACHABLE __assume(0)
#else
#define ABSL_INTERNAL_UNREACHABLE
#endif
#endif // ABSL_BASE_MACROS_H_

@ -22,13 +22,15 @@
#ifndef ABSL_BASE_OPTIMIZATION_H_
#define ABSL_BASE_OPTIMIZATION_H_
#include <assert.h>
#include "absl/base/config.h"
// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION
//
// Instructs the compiler to avoid optimizing tail-call recursion. Use of this
// macro is useful when you wish to preserve the existing function order within
// a stack trace for logging, debugging, or profiling purposes.
// Instructs the compiler to avoid optimizing tail-call recursion. This macro is
// useful when you wish to preserve the existing function order within a stack
// trace for logging, debugging, or profiling purposes.
//
// Example:
//
@ -104,9 +106,10 @@
// Cacheline aligning objects properly allows constructive memory sharing and
// prevents destructive (or "false") memory sharing.
//
// NOTE: this macro should be replaced with usage of `alignas()` using
// NOTE: callers should replace uses of this macro with `alignas()` using
// `std::hardware_constructive_interference_size` and/or
// `std::hardware_destructive_interference_size` when available within C++17.
// `std::hardware_destructive_interference_size` when C++17 becomes available to
// them.
//
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html
// for more information.
@ -179,7 +182,7 @@
#endif
// ABSL_INTERNAL_ASSUME(cond)
// Informs the compiler than a condition is always true and that it can assume
// Informs the compiler that a condition is always true and that it can assume
// it to be true for optimization purposes. The call has undefined behavior if
// the condition is false.
// In !NDEBUG mode, the condition is checked with an assert().
@ -216,7 +219,7 @@
// This macro forces small unique name on a static file level symbols like
// static local variables or static functions. This is intended to be used in
// macro definitions to optimize the cost of generated code. Do NOT use it on
// symbols exported from translation unit since it may casue a link time
// symbols exported from translation unit since it may cause a link time
// conflict.
//
// Example:

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

@ -14,7 +14,6 @@
//
// This files is a forwarding header for other headers containing various
// portability macros and functions.
// This file is used for both C and C++!
#ifndef ABSL_BASE_PORT_H_
#define ABSL_BASE_PORT_H_

@ -92,6 +92,7 @@ static void TestFunction(int thread_salt, SpinLock* spinlock) {
static void ThreadedTest(SpinLock* spinlock) {
std::vector<std::thread> threads;
threads.reserve(kNumThreads);
for (int i = 0; i < kNumThreads; ++i) {
threads.push_back(std::thread(TestFunction, i, spinlock));
}

@ -317,7 +317,7 @@ namespace base_internal {
// Takes a reference to a guarded data member, and returns an unguarded
// reference.
// Do not used this function directly, use ABSL_TS_UNCHECKED_READ instead.
// Do not use this function directly, use ABSL_TS_UNCHECKED_READ instead.
template <typename T>
inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS {
return v;

@ -1,38 +0,0 @@
#
# Copyright 2018 The Abseil Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Creates config_setting that allows selecting based on 'compiler' value."""
def create_llvm_config(name, visibility):
# The "do_not_use_tools_cpp_compiler_present" attribute exists to
# distinguish between older versions of Bazel that do not support
# "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do.
# In the future, the only way to select on the compiler will be through
# flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can
# be removed.
if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"):
native.config_setting(
name = name,
flag_values = {
"@bazel_tools//tools/cpp:compiler": "llvm",
},
visibility = visibility,
)
else:
native.config_setting(
name = name,
values = {"compiler": "llvm"},
visibility = visibility,
)

@ -599,12 +599,12 @@ cc_library(
":hashtablez_sampler",
":have_sse",
":layout",
"//absl/base:bits",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/numeric:bits",
"//absl/utility",
],
)
@ -630,6 +630,45 @@ cc_test(
],
)
cc_binary(
name = "raw_hash_set_benchmark",
testonly = 1,
srcs = ["internal/raw_hash_set_benchmark.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
tags = ["benchmark"],
visibility = ["//visibility:private"],
deps = [
":hash_function_defaults",
":raw_hash_set",
"//absl/base:raw_logging_internal",
"//absl/strings:str_format",
"@com_github_google_benchmark//:benchmark_main",
],
)
cc_binary(
name = "raw_hash_set_probe_benchmark",
testonly = 1,
srcs = ["internal/raw_hash_set_probe_benchmark.cc"],
copts = ABSL_TEST_COPTS,
linkopts = select({
"//conditions:default": [],
}) + ABSL_DEFAULT_LINKOPTS,
tags = ["benchmark"],
visibility = ["//visibility:private"],
deps = [
":flat_hash_map",
":hash_function_defaults",
":hashtable_debug",
":raw_hash_set",
"//absl/random",
"//absl/random:distributions",
"//absl/strings",
"//absl/strings:str_format",
],
)
cc_test(
name = "raw_hash_set_allocator_test",
size = "small",
@ -677,6 +716,22 @@ cc_test(
],
)
cc_binary(
name = "layout_benchmark",
testonly = 1,
srcs = ["internal/layout_benchmark.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
tags = ["benchmark"],
visibility = ["//visibility:private"],
deps = [
":layout",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
],
)
cc_library(
name = "tracked",
testonly = 1,

@ -14,15 +14,6 @@
# limitations under the License.
#
# This is deprecated and will be removed in the future. It also doesn't do
# anything anyways. Prefer to use the library associated with the API you are
# using.
absl_cc_library(
NAME
container
PUBLIC
)
absl_cc_library(
NAME
btree

@ -26,6 +26,7 @@
#include <unordered_set>
#include <vector>
#include "benchmark/benchmark.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
@ -39,7 +40,6 @@
#include "absl/strings/cord.h"
#include "absl/strings/str_format.h"
#include "absl/time/time.h"
#include "benchmark/benchmark.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@ -101,39 +101,6 @@ void BM_InsertSorted(benchmark::State& state) {
BM_InsertImpl<T>(state, true);
}
// container::insert sometimes returns a pair<iterator, bool> and sometimes
// returns an iterator (for multi- containers).
template <typename Iter>
Iter GetIterFromInsert(const std::pair<Iter, bool>& pair) {
return pair.first;
}
template <typename Iter>
Iter GetIterFromInsert(const Iter iter) {
return iter;
}
// Benchmark insertion of values into a container at the end.
template <typename T>
void BM_InsertEnd(benchmark::State& state) {
using V = typename remove_pair_const<typename T::value_type>::type;
typename KeyOfValue<typename T::key_type, V>::type key_of_value;
T container;
const int kSize = 10000;
for (int i = 0; i < kSize; ++i) {
container.insert(Generator<V>(kSize)(i));
}
V v = Generator<V>(kSize)(kSize - 1);
typename T::key_type k = key_of_value(v);
auto it = container.find(k);
while (state.KeepRunning()) {
// Repeatedly removing then adding v.
container.erase(it);
it = GetIterFromInsert(container.insert(v));
}
}
// Benchmark inserting the first few elements in a container. In b-tree, this is
// when the root node grows.
template <typename T>
@ -513,7 +480,6 @@ BTREE_TYPES(Time);
#define MY_BENCHMARK3(type) \
MY_BENCHMARK4(type, Insert); \
MY_BENCHMARK4(type, InsertSorted); \
MY_BENCHMARK4(type, InsertEnd); \
MY_BENCHMARK4(type, InsertSmall); \
MY_BENCHMARK4(type, Lookup); \
MY_BENCHMARK4(type, FullLookup); \

@ -384,9 +384,8 @@ class btree_map
// btree_map::equal_range()
//
// Returns a closed range [first, last], defined by a `std::pair` of two
// iterators, containing all elements with the passed key in the
// `btree_map`.
// Returns a half-open range [first, last), defined by a `std::pair` of two
// iterators, containing all elements with the passed key in the `btree_map`.
using Base::equal_range;
// btree_map::find()
@ -709,7 +708,7 @@ class btree_multimap
// btree_multimap::equal_range()
//
// Returns a closed range [first, last], defined by a `std::pair` of two
// Returns a half-open range [first, last), defined by a `std::pair` of two
// iterators, containing all elements with the passed key in the
// `btree_multimap`.
using Base::equal_range;

@ -55,6 +55,7 @@ using ::testing::ElementsAreArray;
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::Pair;
using ::testing::SizeIs;
template <typename T, typename U>
void CheckPairEquals(const T &x, const U &y) {
@ -1182,6 +1183,103 @@ TEST(Btree, RangeCtorSanity) {
EXPECT_EQ(1, tmap.size());
}
} // namespace
class BtreeNodePeer {
public:
// Yields the size of a leaf node with a specific number of values.
template <typename ValueType>
constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) {
return btree_node<
set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>,
/*TargetNodeSize=*/256, // This parameter isn't used here.
/*Multi=*/false>>::SizeWithNSlots(target_values_per_node);
}
// Yields the number of slots in a (non-root) leaf node for this btree.
template <typename Btree>
constexpr static size_t GetNumSlotsPerNode() {
return btree_node<typename Btree::params_type>::kNodeSlots;
}
template <typename Btree>
constexpr static size_t GetMaxFieldType() {
return std::numeric_limits<
typename btree_node<typename Btree::params_type>::field_type>::max();
}
template <typename Btree>
constexpr static bool UsesLinearNodeSearch() {
return btree_node<typename Btree::params_type>::use_linear_search::value;
}
};
namespace {
class BtreeMapTest : public ::testing::Test {
public:
struct Key {};
struct Cmp {
template <typename T>
bool operator()(T, T) const {
return false;
}
};
struct KeyLin {
using absl_btree_prefer_linear_node_search = std::true_type;
};
struct CmpLin : Cmp {
using absl_btree_prefer_linear_node_search = std::true_type;
};
struct KeyBin {
using absl_btree_prefer_linear_node_search = std::false_type;
};
struct CmpBin : Cmp {
using absl_btree_prefer_linear_node_search = std::false_type;
};
template <typename K, typename C>
static bool IsLinear() {
return BtreeNodePeer::UsesLinearNodeSearch<absl::btree_map<K, int, C>>();
}
};
TEST_F(BtreeMapTest, TestLinearSearchPreferredForKeyLinearViaAlias) {
// Test requesting linear search by directly exporting an alias.
EXPECT_FALSE((IsLinear<Key, Cmp>()));
EXPECT_TRUE((IsLinear<KeyLin, Cmp>()));
EXPECT_TRUE((IsLinear<Key, CmpLin>()));
EXPECT_TRUE((IsLinear<KeyLin, CmpLin>()));
}
TEST_F(BtreeMapTest, LinearChoiceTree) {
// Cmp has precedence, and is forcing binary
EXPECT_FALSE((IsLinear<Key, CmpBin>()));
EXPECT_FALSE((IsLinear<KeyLin, CmpBin>()));
EXPECT_FALSE((IsLinear<KeyBin, CmpBin>()));
EXPECT_FALSE((IsLinear<int, CmpBin>()));
EXPECT_FALSE((IsLinear<std::string, CmpBin>()));
// Cmp has precedence, and is forcing linear
EXPECT_TRUE((IsLinear<Key, CmpLin>()));
EXPECT_TRUE((IsLinear<KeyLin, CmpLin>()));
EXPECT_TRUE((IsLinear<KeyBin, CmpLin>()));
EXPECT_TRUE((IsLinear<int, CmpLin>()));
EXPECT_TRUE((IsLinear<std::string, CmpLin>()));
// Cmp has no preference, Key determines linear vs binary.
EXPECT_FALSE((IsLinear<Key, Cmp>()));
EXPECT_TRUE((IsLinear<KeyLin, Cmp>()));
EXPECT_FALSE((IsLinear<KeyBin, Cmp>()));
// arithmetic key w/ std::less or std::greater: linear
EXPECT_TRUE((IsLinear<int, std::less<int>>()));
EXPECT_TRUE((IsLinear<double, std::greater<double>>()));
// arithmetic key w/ custom compare: binary
EXPECT_FALSE((IsLinear<int, Cmp>()));
// non-arithmetic key: binary
EXPECT_FALSE((IsLinear<std::string, std::less<std::string>>()));
}
TEST(Btree, BtreeMapCanHoldMoveOnlyTypes) {
absl::btree_map<std::string, std::unique_ptr<std::string>> m;
@ -1327,34 +1425,6 @@ TEST(Btree, RValueInsert) {
EXPECT_EQ(tracker.swaps(), 0);
}
} // namespace
class BtreeNodePeer {
public:
// Yields the size of a leaf node with a specific number of values.
template <typename ValueType>
constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) {
return btree_node<
set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>,
/*TargetNodeSize=*/256, // This parameter isn't used here.
/*Multi=*/false>>::SizeWithNValues(target_values_per_node);
}
// Yields the number of values in a (non-root) leaf node for this set.
template <typename Set>
constexpr static size_t GetNumValuesPerNode() {
return btree_node<typename Set::params_type>::kNodeValues;
}
template <typename Set>
constexpr static size_t GetMaxFieldType() {
return std::numeric_limits<
typename btree_node<typename Set::params_type>::field_type>::max();
}
};
namespace {
// A btree set with a specific number of values per node.
template <typename Key, int TargetValuesPerNode, typename Cmp = std::less<Key>>
class SizedBtreeSet
@ -1388,7 +1458,7 @@ void ExpectOperationCounts(const int expected_moves,
TEST(Btree, MovesComparisonsCopiesSwapsTracking) {
InstanceTracker tracker;
// Note: this is minimum number of values per node.
SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3> set3;
SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/4> set4;
// Note: this is the default number of values per node for a set of int32s
// (with 64-bit pointers).
SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61> set61;
@ -1399,28 +1469,28 @@ TEST(Btree, MovesComparisonsCopiesSwapsTracking) {
std::vector<int> values =
GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23);
EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3);
EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61);
EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100);
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set4)>(), 4);
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61);
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100);
if (sizeof(void *) == 8) {
EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(),
BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>());
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>());
}
// Test key insertion/deletion in random order.
ExpectOperationCounts(45281, 132551, values, &tracker, &set3);
ExpectOperationCounts(56540, 134212, values, &tracker, &set4);
ExpectOperationCounts(386718, 129807, values, &tracker, &set61);
ExpectOperationCounts(586761, 130310, values, &tracker, &set100);
// Test key insertion/deletion in sorted order.
std::sort(values.begin(), values.end());
ExpectOperationCounts(26638, 92134, values, &tracker, &set3);
ExpectOperationCounts(24972, 85563, values, &tracker, &set4);
ExpectOperationCounts(20208, 87757, values, &tracker, &set61);
ExpectOperationCounts(20124, 96583, values, &tracker, &set100);
// Test key insertion/deletion in reverse sorted order.
std::reverse(values.begin(), values.end());
ExpectOperationCounts(49951, 119325, values, &tracker, &set3);
ExpectOperationCounts(54949, 127531, values, &tracker, &set4);
ExpectOperationCounts(338813, 118266, values, &tracker, &set61);
ExpectOperationCounts(534529, 125279, values, &tracker, &set100);
}
@ -1437,9 +1507,9 @@ struct MovableOnlyInstanceThreeWayCompare {
TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) {
InstanceTracker tracker;
// Note: this is minimum number of values per node.
SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3,
SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/4,
MovableOnlyInstanceThreeWayCompare>
set3;
set4;
// Note: this is the default number of values per node for a set of int32s
// (with 64-bit pointers).
SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61,
@ -1454,28 +1524,28 @@ TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) {
std::vector<int> values =
GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23);
EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3);
EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61);
EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100);
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set4)>(), 4);
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61);
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100);
if (sizeof(void *) == 8) {
EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(),
BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>());
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>());
}
// Test key insertion/deletion in random order.
ExpectOperationCounts(45281, 122560, values, &tracker, &set3);
ExpectOperationCounts(56540, 124221, values, &tracker, &set4);
ExpectOperationCounts(386718, 119816, values, &tracker, &set61);
ExpectOperationCounts(586761, 120319, values, &tracker, &set100);
// Test key insertion/deletion in sorted order.
std::sort(values.begin(), values.end());
ExpectOperationCounts(26638, 92134, values, &tracker, &set3);
ExpectOperationCounts(24972, 85563, values, &tracker, &set4);
ExpectOperationCounts(20208, 87757, values, &tracker, &set61);
ExpectOperationCounts(20124, 96583, values, &tracker, &set100);
// Test key insertion/deletion in reverse sorted order.
std::reverse(values.begin(), values.end());
ExpectOperationCounts(49951, 109326, values, &tracker, &set3);
ExpectOperationCounts(54949, 117532, values, &tracker, &set4);
ExpectOperationCounts(338813, 108267, values, &tracker, &set61);
ExpectOperationCounts(534529, 115280, values, &tracker, &set100);
}
@ -1968,6 +2038,30 @@ TEST(Btree, ExtractAndInsertNodeHandleMultiMap) {
EXPECT_EQ(res, ++other.begin());
}
TEST(Btree, ExtractMultiMapEquivalentKeys) {
// Note: using string keys means a three-way comparator.
absl::btree_multimap<std::string, int> map;
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < 100; ++j) {
map.insert({absl::StrCat(i), j});
}
}
for (int i = 0; i < 100; ++i) {
const std::string key = absl::StrCat(i);
auto node_handle = map.extract(key);
EXPECT_EQ(node_handle.key(), key);
EXPECT_EQ(node_handle.mapped(), 0) << i;
}
for (int i = 0; i < 100; ++i) {
const std::string key = absl::StrCat(i);
auto node_handle = map.extract(key);
EXPECT_EQ(node_handle.key(), key);
EXPECT_EQ(node_handle.mapped(), 1) << i;
}
}
// For multisets, insert with hint also affects correctness because we need to
// insert immediately before the hint if possible.
struct InsertMultiHintData {
@ -2109,6 +2203,31 @@ TEST(Btree, MergeIntoMultiMapsWithDifferentComparators) {
Pair(4, 1), Pair(4, 4), Pair(5, 5)));
}
TEST(Btree, MergeIntoSetMovableOnly) {
absl::btree_set<MovableOnlyInstance> src;
src.insert(MovableOnlyInstance(1));
absl::btree_multiset<MovableOnlyInstance> dst1;
dst1.insert(MovableOnlyInstance(2));
absl::btree_set<MovableOnlyInstance> dst2;
// Test merge into multiset.
dst1.merge(src);
EXPECT_TRUE(src.empty());
// ElementsAre/ElementsAreArray don't work with move-only types.
ASSERT_THAT(dst1, SizeIs(2));
EXPECT_EQ(*dst1.begin(), MovableOnlyInstance(1));
EXPECT_EQ(*std::next(dst1.begin()), MovableOnlyInstance(2));
// Test merge into set.
dst2.merge(dst1);
EXPECT_TRUE(dst1.empty());
ASSERT_THAT(dst2, SizeIs(2));
EXPECT_EQ(*dst2.begin(), MovableOnlyInstance(1));
EXPECT_EQ(*std::next(dst2.begin()), MovableOnlyInstance(2));
}
struct KeyCompareToWeakOrdering {
template <typename T>
absl::weak_ordering operator()(const T &a, const T &b) const {
@ -2585,6 +2704,12 @@ struct MultiKey {
int i2;
};
bool operator==(const MultiKey a, const MultiKey b) {
return a.i1 == b.i1 && a.i2 == b.i2;
}
// A heterogeneous comparator that has different equivalence classes for
// different lookup types.
struct MultiKeyComp {
using is_transparent = void;
bool operator()(const MultiKey a, const MultiKey b) const {
@ -2595,11 +2720,36 @@ struct MultiKeyComp {
bool operator()(const MultiKey a, const int b) const { return a.i1 < b; }
};
// Test that when there's a heterogeneous comparator that behaves differently
// for some heterogeneous operators, we get equal_range() right.
TEST(Btree, MultiKeyEqualRange) {
absl::btree_set<MultiKey, MultiKeyComp> set;
// A heterogeneous, three-way comparator that has different equivalence classes
// for different lookup types.
struct MultiKeyThreeWayComp {
using is_transparent = void;
absl::weak_ordering operator()(const MultiKey a, const MultiKey b) const {
if (a.i1 < b.i1) return absl::weak_ordering::less;
if (a.i1 > b.i1) return absl::weak_ordering::greater;
if (a.i2 < b.i2) return absl::weak_ordering::less;
if (a.i2 > b.i2) return absl::weak_ordering::greater;
return absl::weak_ordering::equivalent;
}
absl::weak_ordering operator()(const int a, const MultiKey b) const {
if (a < b.i1) return absl::weak_ordering::less;
if (a > b.i1) return absl::weak_ordering::greater;
return absl::weak_ordering::equivalent;
}
absl::weak_ordering operator()(const MultiKey a, const int b) const {
if (a.i1 < b) return absl::weak_ordering::less;
if (a.i1 > b) return absl::weak_ordering::greater;
return absl::weak_ordering::equivalent;
}
};
template <typename Compare>
class BtreeMultiKeyTest : public ::testing::Test {};
using MultiKeyComps = ::testing::Types<MultiKeyComp, MultiKeyThreeWayComp>;
TYPED_TEST_SUITE(BtreeMultiKeyTest, MultiKeyComps);
TYPED_TEST(BtreeMultiKeyTest, EqualRange) {
absl::btree_set<MultiKey, TypeParam> set;
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < 100; ++j) {
set.insert({i, j});
@ -2609,11 +2759,140 @@ TEST(Btree, MultiKeyEqualRange) {
for (int i = 0; i < 100; ++i) {
auto equal_range = set.equal_range(i);
EXPECT_EQ(equal_range.first->i1, i);
EXPECT_EQ(equal_range.first->i2, 0);
EXPECT_EQ(equal_range.first->i2, 0) << i;
EXPECT_EQ(std::distance(equal_range.first, equal_range.second), 100) << i;
}
}
TYPED_TEST(BtreeMultiKeyTest, Extract) {
absl::btree_set<MultiKey, TypeParam> set;
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < 100; ++j) {
set.insert({i, j});
}
}
for (int i = 0; i < 100; ++i) {
auto node_handle = set.extract(i);
EXPECT_EQ(node_handle.value().i1, i);
EXPECT_EQ(node_handle.value().i2, 0) << i;
}
for (int i = 0; i < 100; ++i) {
auto node_handle = set.extract(i);
EXPECT_EQ(node_handle.value().i1, i);
EXPECT_EQ(node_handle.value().i2, 1) << i;
}
}
TYPED_TEST(BtreeMultiKeyTest, Erase) {
absl::btree_set<MultiKey, TypeParam> set = {
{1, 1}, {2, 1}, {2, 2}, {3, 1}};
EXPECT_EQ(set.erase(2), 2);
EXPECT_THAT(set, ElementsAre(MultiKey{1, 1}, MultiKey{3, 1}));
}
TYPED_TEST(BtreeMultiKeyTest, Count) {
const absl::btree_set<MultiKey, TypeParam> set = {
{1, 1}, {2, 1}, {2, 2}, {3, 1}};
EXPECT_EQ(set.count(2), 2);
}
TEST(Btree, AllocConstructor) {
using Alloc = CountingAllocator<int>;
using Set = absl::btree_set<int, std::less<int>, Alloc>;
int64_t bytes_used = 0;
Alloc alloc(&bytes_used);
Set set(alloc);
set.insert({1, 2, 3});
EXPECT_THAT(set, ElementsAre(1, 2, 3));
EXPECT_GT(bytes_used, set.size() * sizeof(int));
}
TEST(Btree, AllocInitializerListConstructor) {
using Alloc = CountingAllocator<int>;
using Set = absl::btree_set<int, std::less<int>, Alloc>;
int64_t bytes_used = 0;
Alloc alloc(&bytes_used);
Set set({1, 2, 3}, alloc);
EXPECT_THAT(set, ElementsAre(1, 2, 3));
EXPECT_GT(bytes_used, set.size() * sizeof(int));
}
TEST(Btree, AllocRangeConstructor) {
using Alloc = CountingAllocator<int>;
using Set = absl::btree_set<int, std::less<int>, Alloc>;
int64_t bytes_used = 0;
Alloc alloc(&bytes_used);
std::vector<int> v = {1, 2, 3};
Set set(v.begin(), v.end(), alloc);
EXPECT_THAT(set, ElementsAre(1, 2, 3));
EXPECT_GT(bytes_used, set.size() * sizeof(int));
}
TEST(Btree, AllocCopyConstructor) {
using Alloc = CountingAllocator<int>;
using Set = absl::btree_set<int, std::less<int>, Alloc>;
int64_t bytes_used1 = 0;
Alloc alloc1(&bytes_used1);
Set set1(alloc1);
set1.insert({1, 2, 3});
int64_t bytes_used2 = 0;
Alloc alloc2(&bytes_used2);
Set set2(set1, alloc2);
EXPECT_THAT(set1, ElementsAre(1, 2, 3));
EXPECT_THAT(set2, ElementsAre(1, 2, 3));
EXPECT_GT(bytes_used1, set1.size() * sizeof(int));
EXPECT_EQ(bytes_used1, bytes_used2);
}
TEST(Btree, AllocMoveConstructor_SameAlloc) {
using Alloc = CountingAllocator<int>;
using Set = absl::btree_set<int, std::less<int>, Alloc>;
int64_t bytes_used = 0;
Alloc alloc(&bytes_used);
Set set1(alloc);
set1.insert({1, 2, 3});
const int64_t original_bytes_used = bytes_used;
EXPECT_GT(original_bytes_used, set1.size() * sizeof(int));
Set set2(std::move(set1), alloc);
EXPECT_THAT(set2, ElementsAre(1, 2, 3));
EXPECT_EQ(bytes_used, original_bytes_used);
}
TEST(Btree, AllocMoveConstructor_DifferentAlloc) {
using Alloc = CountingAllocator<int>;
using Set = absl::btree_set<int, std::less<int>, Alloc>;
int64_t bytes_used1 = 0;
Alloc alloc1(&bytes_used1);
Set set1(alloc1);
set1.insert({1, 2, 3});
const int64_t original_bytes_used = bytes_used1;
EXPECT_GT(original_bytes_used, set1.size() * sizeof(int));
int64_t bytes_used2 = 0;
Alloc alloc2(&bytes_used2);
Set set2(std::move(set1), alloc2);
EXPECT_THAT(set2, ElementsAre(1, 2, 3));
// We didn't free these bytes allocated by `set1` yet.
EXPECT_EQ(bytes_used1, original_bytes_used);
EXPECT_EQ(bytes_used2, original_bytes_used);
}
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END

@ -232,8 +232,8 @@ class FixedArray {
// FixedArray::at
//
// Bounds-checked access. Returns a reference to the ith element of the
// fiexed array, or throws std::out_of_range
// Bounds-checked access. Returns a reference to the ith element of the fixed
// array, or throws std::out_of_range
reference at(size_type i) {
if (ABSL_PREDICT_FALSE(i >= size())) {
base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");

@ -324,7 +324,7 @@ class flat_hash_set
// flat_hash_set::merge()
//
// Extracts elements from a given `source` flat hash map into this
// Extracts elements from a given `source` flat hash set into this
// `flat_hash_set`. If the destination `flat_hash_set` already contains an
// element with an equivalent key, that element is not extracted.
using Base::merge;

@ -167,11 +167,13 @@ class InlinedVector {
// Creates an inlined vector by copying the contents of `other` using `alloc`.
InlinedVector(const InlinedVector& other, const allocator_type& alloc)
: storage_(alloc) {
if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
if (other.empty()) {
// Empty; nothing to do.
} else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
// Memcpy-able and do not need allocation.
storage_.MemcpyFrom(other.storage_);
} else {
storage_.Initialize(IteratorValueAdapter<const_pointer>(other.data()),
other.size());
storage_.InitFrom(other.storage_);
}
}

@ -534,6 +534,28 @@ void BM_ConstructFromMove(benchmark::State& state) {
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, TrivialType);
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, NontrivialType);
// Measure cost of copy-constructor+destructor.
void BM_CopyTrivial(benchmark::State& state) {
const int n = state.range(0);
InlVec<int64_t> src(n);
for (auto s : state) {
InlVec<int64_t> copy(src);
benchmark::DoNotOptimize(copy);
}
}
BENCHMARK(BM_CopyTrivial)->Arg(0)->Arg(1)->Arg(kLargeSize);
// Measure cost of copy-constructor+destructor.
void BM_CopyNonTrivial(benchmark::State& state) {
const int n = state.range(0);
InlVec<InlVec<int64_t>> src(n);
for (auto s : state) {
InlVec<InlVec<int64_t>> copy(src);
benchmark::DoNotOptimize(copy);
}
}
BENCHMARK(BM_CopyNonTrivial)->Arg(0)->Arg(1)->Arg(kLargeSize);
template <typename T, size_t FromSize, size_t ToSize>
void BM_AssignSizeRef(benchmark::State& state) {
auto size = ToSize;

@ -736,22 +736,26 @@ TEST(OverheadTest, Storage) {
// In particular, ensure that std::allocator doesn't cost anything to store.
// The union should be absorbing some of the allocation bookkeeping overhead
// in the larger vectors, leaving only the size_ field as overhead.
EXPECT_EQ(2 * sizeof(int*),
sizeof(absl::InlinedVector<int*, 1>) - 1 * sizeof(int*));
EXPECT_EQ(1 * sizeof(int*),
sizeof(absl::InlinedVector<int*, 2>) - 2 * sizeof(int*));
EXPECT_EQ(1 * sizeof(int*),
sizeof(absl::InlinedVector<int*, 3>) - 3 * sizeof(int*));
EXPECT_EQ(1 * sizeof(int*),
sizeof(absl::InlinedVector<int*, 4>) - 4 * sizeof(int*));
EXPECT_EQ(1 * sizeof(int*),
sizeof(absl::InlinedVector<int*, 5>) - 5 * sizeof(int*));
EXPECT_EQ(1 * sizeof(int*),
sizeof(absl::InlinedVector<int*, 6>) - 6 * sizeof(int*));
EXPECT_EQ(1 * sizeof(int*),
sizeof(absl::InlinedVector<int*, 7>) - 7 * sizeof(int*));
EXPECT_EQ(1 * sizeof(int*),
sizeof(absl::InlinedVector<int*, 8>) - 8 * sizeof(int*));
struct T { void* val; };
size_t expected_overhead = sizeof(T);
EXPECT_EQ((2 * expected_overhead),
sizeof(absl::InlinedVector<T, 1>) - sizeof(T[1]));
EXPECT_EQ(expected_overhead,
sizeof(absl::InlinedVector<T, 2>) - sizeof(T[2]));
EXPECT_EQ(expected_overhead,
sizeof(absl::InlinedVector<T, 3>) - sizeof(T[3]));
EXPECT_EQ(expected_overhead,
sizeof(absl::InlinedVector<T, 4>) - sizeof(T[4]));
EXPECT_EQ(expected_overhead,
sizeof(absl::InlinedVector<T, 5>) - sizeof(T[5]));
EXPECT_EQ(expected_overhead,
sizeof(absl::InlinedVector<T, 6>) - sizeof(T[6]));
EXPECT_EQ(expected_overhead,
sizeof(absl::InlinedVector<T, 7>) - sizeof(T[7]));
EXPECT_EQ(expected_overhead,
sizeof(absl::InlinedVector<T, 8>) - sizeof(T[8]));
}
TEST(IntVec, Clear) {

@ -182,15 +182,44 @@ struct key_compare_to_adapter<std::greater<absl::Cord>> {
using type = StringBtreeDefaultGreater;
};
// Detects an 'absl_btree_prefer_linear_node_search' member. This is
// a protocol used as an opt-in or opt-out of linear search.
//
// For example, this would be useful for key types that wrap an integer
// and define their own cheap operator<(). For example:
//
// class K {
// public:
// using absl_btree_prefer_linear_node_search = std::true_type;
// ...
// private:
// friend bool operator<(K a, K b) { return a.k_ < b.k_; }
// int k_;
// };
//
// btree_map<K, V> m; // Uses linear search
//
// If T has the preference tag, then it has a preference.
// Btree will use the tag's truth value.
template <typename T, typename = void>
struct has_linear_node_search_preference : std::false_type {};
template <typename T, typename = void>
struct prefers_linear_node_search : std::false_type {};
template <typename T>
struct has_linear_node_search_preference<
T, absl::void_t<typename T::absl_btree_prefer_linear_node_search>>
: std::true_type {};
template <typename T>
struct prefers_linear_node_search<
T, absl::void_t<typename T::absl_btree_prefer_linear_node_search>>
: T::absl_btree_prefer_linear_node_search {};
template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
bool Multi, typename SlotPolicy>
struct common_params {
// If Compare is a common comparator for a string-like type, then we adapt it
// to use heterogeneous lookup and to be a key-compare-to comparator.
using key_compare = typename key_compare_to_adapter<Compare>::type;
// True when key_compare has been adapted to StringBtreeDefault{Less,Greater}.
using is_key_compare_adapted =
absl::negation<std::is_same<key_compare, Compare>>;
// A type which indicates if we have a key-compare-to functor or a plain old
// key-compare functor.
using is_key_compare_to = btree_is_key_compare_to<key_compare, Key>;
@ -200,9 +229,6 @@ struct common_params {
using size_type = std::make_signed<size_t>::type;
using difference_type = ptrdiff_t;
// True if this is a multiset or multimap.
using is_multi_container = std::integral_constant<bool, Multi>;
using slot_policy = SlotPolicy;
using slot_type = typename slot_policy::slot_type;
using value_type = typename slot_policy::value_type;
@ -212,6 +238,23 @@ struct common_params {
using reference = value_type &;
using const_reference = const value_type &;
// For the given lookup key type, returns whether we can have multiple
// equivalent keys in the btree. If this is a multi-container, then we can.
// Otherwise, we can have multiple equivalent keys only if all of the
// following conditions are met:
// - The comparator is transparent.
// - The lookup key type is not the same as key_type.
// - The comparator is not a StringBtreeDefault{Less,Greater} comparator
// that we know has the same equivalence classes for all lookup types.
template <typename LookupKey>
constexpr static bool can_have_multiple_equivalent_keys() {
return Multi ||
(IsTransparent<key_compare>::value &&
!std::is_same<LookupKey, Key>::value &&
!std::is_same<key_compare, StringBtreeDefaultLess>::value &&
!std::is_same<key_compare, StringBtreeDefaultGreater>::value);
}
enum {
kTargetNodeSize = TargetNodeSize,
@ -391,6 +434,10 @@ struct SearchResult {
// useful information.
template <typename V>
struct SearchResult<V, false> {
SearchResult() {}
explicit SearchResult(V value) : value(value) {}
SearchResult(V value, MatchKind /*match*/) : value(value) {}
V value;
static constexpr bool HasMatch() { return false; }
@ -403,7 +450,6 @@ struct SearchResult<V, false> {
template <typename Params>
class btree_node {
using is_key_compare_to = typename Params::is_key_compare_to;
using is_multi_container = typename Params::is_multi_container;
using field_type = typename Params::node_count_type;
using allocator_type = typename Params::allocator_type;
using slot_type = typename Params::slot_type;
@ -421,15 +467,22 @@ class btree_node {
using difference_type = typename Params::difference_type;
// Btree decides whether to use linear node search as follows:
// - If the comparator expresses a preference, use that.
// - If the key expresses a preference, use that.
// - If the key is arithmetic and the comparator is std::less or
// std::greater, choose linear.
// - Otherwise, choose binary.
// TODO(ezb): Might make sense to add condition(s) based on node-size.
using use_linear_search = std::integral_constant<
bool,
std::is_arithmetic<key_type>::value &&
(std::is_same<std::less<key_type>, key_compare>::value ||
std::is_same<std::greater<key_type>, key_compare>::value)>;
has_linear_node_search_preference<key_compare>::value
? prefers_linear_node_search<key_compare>::value
: has_linear_node_search_preference<key_type>::value
? prefers_linear_node_search<key_type>::value
: std::is_arithmetic<key_type>::value &&
(std::is_same<std::less<key_type>, key_compare>::value ||
std::is_same<std::greater<key_type>,
key_compare>::value)>;
// This class is organized by gtl::Layout as if it had the following
// structure:
@ -446,23 +499,23 @@ class btree_node {
// // is the same as the count of values.
// field_type finish;
// // The maximum number of values the node can hold. This is an integer in
// // [1, kNodeValues] for root leaf nodes, kNodeValues for non-root leaf
// // [1, kNodeSlots] for root leaf nodes, kNodeSlots for non-root leaf
// // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal
// // nodes (even though there are still kNodeValues values in the node).
// // nodes (even though there are still kNodeSlots values in the node).
// // TODO(ezb): make max_count use only 4 bits and record log2(capacity)
// // to free extra bits for is_root, etc.
// field_type max_count;
//
// // The array of values. The capacity is `max_count` for leaf nodes and
// // kNodeValues for internal nodes. Only the values in
// // kNodeSlots for internal nodes. Only the values in
// // [start, finish) have been initialized and are valid.
// slot_type values[max_count];
//
// // The array of child pointers. The keys in children[i] are all less
// // than key(i). The keys in children[i + 1] are all greater than key(i).
// // There are 0 children for leaf nodes and kNodeValues + 1 children for
// // There are 0 children for leaf nodes and kNodeSlots + 1 children for
// // internal nodes.
// btree_node *children[kNodeValues + 1];
// btree_node *children[kNodeSlots + 1];
//
// This class is only constructed by EmptyNodeType. Normally, pointers to the
// layout above are allocated, cast to btree_node*, and de-allocated within
@ -484,57 +537,62 @@ class btree_node {
private:
using layout_type = absl::container_internal::Layout<btree_node *, field_type,
slot_type, btree_node *>;
constexpr static size_type SizeWithNValues(size_type n) {
constexpr static size_type SizeWithNSlots(size_type n) {
return layout_type(/*parent*/ 1,
/*position, start, finish, max_count*/ 4,
/*values*/ n,
/*slots*/ n,
/*children*/ 0)
.AllocSize();
}
// A lower bound for the overhead of fields other than values in a leaf node.
constexpr static size_type MinimumOverhead() {
return SizeWithNValues(1) - sizeof(value_type);
return SizeWithNSlots(1) - sizeof(value_type);
}
// Compute how many values we can fit onto a leaf node taking into account
// padding.
constexpr static size_type NodeTargetValues(const int begin, const int end) {
constexpr static size_type NodeTargetSlots(const int begin, const int end) {
return begin == end ? begin
: SizeWithNValues((begin + end) / 2 + 1) >
: SizeWithNSlots((begin + end) / 2 + 1) >
params_type::kTargetNodeSize
? NodeTargetValues(begin, (begin + end) / 2)
: NodeTargetValues((begin + end) / 2 + 1, end);
? NodeTargetSlots(begin, (begin + end) / 2)
: NodeTargetSlots((begin + end) / 2 + 1, end);
}
enum {
kTargetNodeSize = params_type::kTargetNodeSize,
kNodeTargetValues = NodeTargetValues(0, params_type::kTargetNodeSize),
kNodeTargetSlots = NodeTargetSlots(0, params_type::kTargetNodeSize),
// We need a minimum of 3 values per internal node in order to perform
// We need a minimum of 3 slots per internal node in order to perform
// splitting (1 value for the two nodes involved in the split and 1 value
// propagated to the parent as the delimiter for the split).
kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3,
// propagated to the parent as the delimiter for the split). For performance
// reasons, we don't allow 3 slots-per-node due to bad worst case occupancy
// of 1/3 (for a node, not a b-tree).
kMinNodeSlots = 4,
kNodeSlots =
kNodeTargetSlots >= kMinNodeSlots ? kNodeTargetSlots : kMinNodeSlots,
// The node is internal (i.e. is not a leaf node) if and only if `max_count`
// has this value.
kInternalNodeMaxCount = 0,
};
// Leaves can have less than kNodeValues values.
constexpr static layout_type LeafLayout(const int max_values = kNodeValues) {
// Leaves can have less than kNodeSlots values.
constexpr static layout_type LeafLayout(const int slot_count = kNodeSlots) {
return layout_type(/*parent*/ 1,
/*position, start, finish, max_count*/ 4,
/*values*/ max_values,
/*slots*/ slot_count,
/*children*/ 0);
}
constexpr static layout_type InternalLayout() {
return layout_type(/*parent*/ 1,
/*position, start, finish, max_count*/ 4,
/*values*/ kNodeValues,
/*children*/ kNodeValues + 1);
/*slots*/ kNodeSlots,
/*children*/ kNodeSlots + 1);
}
constexpr static size_type LeafSize(const int max_values = kNodeValues) {
return LeafLayout(max_values).AllocSize();
constexpr static size_type LeafSize(const int slot_count = kNodeSlots) {
return LeafLayout(slot_count).AllocSize();
}
constexpr static size_type InternalSize() {
return InternalLayout().AllocSize();
@ -591,10 +649,10 @@ class btree_node {
}
field_type max_count() const {
// Internal nodes have max_count==kInternalNodeMaxCount.
// Leaf nodes have max_count in [1, kNodeValues].
// Leaf nodes have max_count in [1, kNodeSlots].
const field_type max_count = GetField<1>()[3];
return max_count == field_type{kInternalNodeMaxCount}
? field_type{kNodeValues}
? field_type{kNodeSlots}
: max_count;
}
@ -672,7 +730,7 @@ class btree_node {
}
++s;
}
return {s};
return SearchResult<int, false>{s};
}
// Returns the position of the first value whose key is not less than k using
@ -707,7 +765,7 @@ class btree_node {
e = mid;
}
}
return {s};
return SearchResult<int, false>{s};
}
// Returns the position of the first value whose key is not less than k using
@ -716,7 +774,7 @@ class btree_node {
SearchResult<int, true> binary_search_impl(
const K &k, int s, int e, const CompareTo &comp,
std::true_type /* IsCompareTo */) const {
if (is_multi_container::value) {
if (params_type::template can_have_multiple_equivalent_keys<K>()) {
MatchKind exact_match = MatchKind::kNe;
while (s != e) {
const int mid = (s + e) >> 1;
@ -727,14 +785,14 @@ class btree_node {
e = mid;
if (c == 0) {
// Need to return the first value whose key is not less than k,
// which requires continuing the binary search if this is a
// multi-container.
// which requires continuing the binary search if there could be
// multiple equivalent keys.
exact_match = MatchKind::kEq;
}
}
}
return {s, exact_match};
} else { // Not a multi-container.
} else { // Can't have multiple equivalent keys.
while (s != e) {
const int mid = (s + e) >> 1;
const absl::weak_ordering c = comp(key(mid), k);
@ -784,12 +842,12 @@ class btree_node {
start_slot(), max_count * sizeof(slot_type));
}
void init_internal(btree_node *parent) {
init_leaf(parent, kNodeValues);
init_leaf(parent, kNodeSlots);
// Set `max_count` to a sentinel value to indicate that this node is
// internal.
set_max_count(kInternalNodeMaxCount);
absl::container_internal::SanitizerPoisonMemoryRegion(
&mutable_child(start()), (kNodeValues + 1) * sizeof(btree_node *));
&mutable_child(start()), (kNodeSlots + 1) * sizeof(btree_node *));
}
static void deallocate(const size_type size, btree_node *node,
@ -800,12 +858,6 @@ class btree_node {
// Deletes a node and all of its children.
static void clear_and_delete(btree_node *node, allocator_type *alloc);
public:
// Exposed only for tests.
static bool testonly_uses_linear_node_search() {
return use_linear_search::value;
}
private:
template <typename... Args>
void value_init(const field_type i, allocator_type *alloc, Args &&... args) {
@ -873,6 +925,7 @@ struct btree_iterator {
using key_type = typename Node::key_type;
using size_type = typename Node::size_type;
using params_type = typename Node::params_type;
using is_map_container = typename params_type::is_map_container;
using node_type = Node;
using normal_node = typename std::remove_const<Node>::type;
@ -884,7 +937,7 @@ struct btree_iterator {
using slot_type = typename params_type::slot_type;
using iterator =
btree_iterator<normal_node, normal_reference, normal_pointer>;
btree_iterator<normal_node, normal_reference, normal_pointer>;
using const_iterator =
btree_iterator<const_node, const_reference, const_pointer>;
@ -901,20 +954,19 @@ struct btree_iterator {
btree_iterator(Node *n, int p) : node(n), position(p) {}
// NOTE: this SFINAE allows for implicit conversions from iterator to
// const_iterator, but it specifically avoids defining copy constructors so
// that btree_iterator can be trivially copyable. This is for performance and
// binary size reasons.
// const_iterator, but it specifically avoids hiding the copy constructor so
// that the trivial one will be used when possible.
template <typename N, typename R, typename P,
absl::enable_if_t<
std::is_same<btree_iterator<N, R, P>, iterator>::value &&
std::is_same<btree_iterator, const_iterator>::value,
int> = 0>
btree_iterator(const btree_iterator<N, R, P> &other) // NOLINT
btree_iterator(const btree_iterator<N, R, P> other) // NOLINT
: node(other.node), position(other.position) {}
private:
// This SFINAE allows explicit conversions from const_iterator to
// iterator, but also avoids defining a copy constructor.
// iterator, but also avoids hiding the copy constructor.
// NOTE: the const_cast is safe because this constructor is only called by
// non-const methods and the container owns the nodes.
template <typename N, typename R, typename P,
@ -922,7 +974,7 @@ struct btree_iterator {
std::is_same<btree_iterator<N, R, P>, const_iterator>::value &&
std::is_same<btree_iterator, iterator>::value,
int> = 0>
explicit btree_iterator(const btree_iterator<N, R, P> &other)
explicit btree_iterator(const btree_iterator<N, R, P> other)
: node(const_cast<node_type *>(other.node)), position(other.position) {}
// Increment/decrement the iterator.
@ -985,6 +1037,8 @@ struct btree_iterator {
}
private:
friend iterator;
friend const_iterator;
template <typename Params>
friend class btree;
template <typename Tree>
@ -995,8 +1049,6 @@ struct btree_iterator {
friend class btree_map_container;
template <typename Tree>
friend class btree_multiset_container;
template <typename N, typename R, typename P>
friend struct btree_iterator;
template <typename TreeType, typename CheckerType>
friend class base_checker;
@ -1017,8 +1069,6 @@ class btree {
using is_key_compare_to = typename Params::is_key_compare_to;
using init_type = typename Params::init_type;
using field_type = typename node_type::field_type;
using is_multi_container = typename Params::is_multi_container;
using is_key_compare_adapted = typename Params::is_key_compare_adapted;
// We use a static empty node for the root/leftmost/rightmost of empty btrees
// in order to avoid branching in begin()/end().
@ -1054,8 +1104,8 @@ class btree {
}
enum : uint32_t {
kNodeValues = node_type::kNodeValues,
kMinNodeValues = kNodeValues / 2,
kNodeSlots = node_type::kNodeSlots,
kMinNodeValues = kNodeSlots / 2,
};
struct node_stats {
@ -1085,7 +1135,8 @@ class btree {
using const_reference = typename Params::const_reference;
using pointer = typename Params::pointer;
using const_pointer = typename Params::const_pointer;
using iterator = btree_iterator<node_type, reference, pointer>;
using iterator =
typename btree_iterator<node_type, reference, pointer>::iterator;
using const_iterator = typename iterator::const_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
@ -1098,28 +1149,46 @@ class btree {
private:
// For use in copy_or_move_values_in_order.
const value_type &maybe_move_from_iterator(const_iterator it) { return *it; }
value_type &&maybe_move_from_iterator(iterator it) { return std::move(*it); }
value_type &&maybe_move_from_iterator(iterator it) {
// This is a destructive operation on the other container so it's safe for
// us to const_cast and move from the keys here even if it's a set.
return std::move(const_cast<value_type &>(*it));
}
// Copies or moves (depending on the template parameter) the values in
// other into this btree in their order in other. This btree must be empty
// before this method is called. This method is used in copy construction,
// copy assignment, and move assignment.
template <typename Btree>
void copy_or_move_values_in_order(Btree *other);
void copy_or_move_values_in_order(Btree &other);
// Validates that various assumptions/requirements are true at compile time.
constexpr static bool static_assert_validation();
public:
btree(const key_compare &comp, const allocator_type &alloc);
btree(const key_compare &comp, const allocator_type &alloc)
: root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {}
btree(const btree &other);
btree(const btree &other) : btree(other, other.allocator()) {}
btree(const btree &other, const allocator_type &alloc)
: btree(other.key_comp(), alloc) {
copy_or_move_values_in_order(other);
}
btree(btree &&other) noexcept
: root_(std::move(other.root_)),
rightmost_(absl::exchange(other.rightmost_, EmptyNode())),
size_(absl::exchange(other.size_, 0)) {
other.mutable_root() = EmptyNode();
}
btree(btree &&other, const allocator_type &alloc)
: btree(other.key_comp(), alloc) {
if (alloc == other.allocator()) {
swap(other);
} else {
// Move values from `other` one at a time when allocators are different.
copy_or_move_values_in_order(other);
}
}
~btree() {
// Put static_asserts in destructor to avoid triggering them before the type
@ -1147,17 +1216,22 @@ class btree {
return const_reverse_iterator(begin());
}
// Finds the first element whose key is not less than key.
// Finds the first element whose key is not less than `key`.
template <typename K>
iterator lower_bound(const K &key) {
return internal_end(internal_lower_bound(key));
return internal_end(internal_lower_bound(key).value);
}
template <typename K>
const_iterator lower_bound(const K &key) const {
return internal_end(internal_lower_bound(key));
return internal_end(internal_lower_bound(key).value);
}
// Finds the first element whose key is greater than key.
// Finds the first element whose key is not less than `key` and also returns
// whether that element is equal to `key`.
template <typename K>
std::pair<iterator, bool> lower_bound_equal(const K &key) const;
// Finds the first element whose key is greater than `key`.
template <typename K>
iterator upper_bound(const K &key) {
return internal_end(internal_upper_bound(key));
@ -1239,18 +1313,8 @@ class btree {
// to the element after the last erased element.
std::pair<size_type, iterator> erase_range(iterator begin, iterator end);
// Erases the specified key from the btree. Returns 1 if an element was
// erased and 0 otherwise.
template <typename K>
size_type erase_unique(const K &key);
// Erases all of the entries matching the specified key from the
// btree. Returns the number of elements erased.
template <typename K>
size_type erase_multi(const K &key);
// Finds the iterator corresponding to a key or returns end() if the key is
// not present.
// Finds an element with key equivalent to `key` or returns `end()` if `key`
// is not present.
template <typename K>
iterator find(const K &key) {
return internal_end(internal_find(key));
@ -1260,23 +1324,6 @@ class btree {
return internal_end(internal_find(key));
}
// Returns a count of the number of times the key appears in the btree.
template <typename K>
size_type count_unique(const K &key) const {
const iterator begin = internal_find(key);
if (begin.node == nullptr) {
// The key doesn't exist in the tree.
return 0;
}
return 1;
}
// Returns a count of the number of times the key appears in the btree.
template <typename K>
size_type count_multi(const K &key) const {
const auto range = equal_range(key);
return std::distance(range.first, range.second);
}
// Clear the btree, deleting all of the values it contains.
void clear();
@ -1339,12 +1386,14 @@ class btree {
}
}
// The average number of bytes used per value stored in the btree.
// The average number of bytes used per value stored in the btree assuming
// random insertion order.
static double average_bytes_per_value() {
// Returns the number of bytes per value on a leaf node that is 75%
// full. Experimentally, this matches up nicely with the computed number of
// bytes per value in trees that had their values inserted in random order.
return node_type::LeafSize() / (kNodeValues * 0.75);
// The expected number of values per node with random insertion order is the
// average of the maximum and minimum numbers of values per node.
const double expected_values_per_node =
(kNodeSlots + kMinNodeValues) / 2.0;
return node_type::LeafSize() / expected_values_per_node;
}
// The fullness of the btree. Computed as the number of elements in the btree
@ -1354,7 +1403,7 @@ class btree {
// Returns 0 for empty trees.
double fullness() const {
if (empty()) return 0.0;
return static_cast<double>(size()) / (nodes() * kNodeValues);
return static_cast<double>(size()) / (nodes() * kNodeSlots);
}
// The overhead of the btree structure in bytes per node. Computed as the
// total number of bytes used by the btree minus the number of bytes used for
@ -1404,7 +1453,7 @@ class btree {
}
node_type *new_leaf_node(node_type *parent) {
node_type *n = allocate(node_type::LeafSize());
n->init_leaf(parent, kNodeValues);
n->init_leaf(parent, kNodeSlots);
return n;
}
node_type *new_leaf_root_node(const int max_count) {
@ -1453,28 +1502,19 @@ class btree {
static IterType internal_last(IterType iter);
// Returns an iterator pointing to the leaf position at which key would
// reside in the tree. We provide 2 versions of internal_locate. The first
// version uses a less-than comparator and is incapable of distinguishing when
// there is an exact match. The second version is for the key-compare-to
// specialization and distinguishes exact matches. The key-compare-to
// specialization allows the caller to avoid a subsequent comparison to
// determine if an exact match was made, which is important for keys with
// expensive comparison, such as strings.
// reside in the tree, unless there is an exact match - in which case, the
// result may not be on a leaf. When there's a three-way comparator, we can
// return whether there was an exact match. This allows the caller to avoid a
// subsequent comparison to determine if an exact match was made, which is
// important for keys with expensive comparison, such as strings.
template <typename K>
SearchResult<iterator, is_key_compare_to::value> internal_locate(
const K &key) const;
template <typename K>
SearchResult<iterator, false> internal_locate_impl(
const K &key, std::false_type /* IsCompareTo */) const;
template <typename K>
SearchResult<iterator, true> internal_locate_impl(
const K &key, std::true_type /* IsCompareTo */) const;
// Internal routine which implements lower_bound().
template <typename K>
iterator internal_lower_bound(const K &key) const;
SearchResult<iterator, is_key_compare_to::value> internal_lower_bound(
const K &key) const;
// Internal routine which implements upper_bound().
template <typename K>
@ -1503,13 +1543,6 @@ class btree {
return res;
}
public:
// Exposed only for tests.
static bool testonly_uses_linear_node_search() {
return node_type::testonly_uses_linear_node_search();
}
private:
// We use compressed tuple in order to save space because key_compare and
// allocator_type are usually empty.
absl::container_internal::CompressedTuple<key_compare, allocator_type,
@ -1665,7 +1698,7 @@ template <typename P>
void btree_node<P>::split(const int insert_position, btree_node *dest,
allocator_type *alloc) {
assert(dest->count() == 0);
assert(max_count() == kNodeValues);
assert(max_count() == kNodeSlots);
// We bias the split based on the position being inserted. If we're
// inserting at the beginning of the left node then bias the split to put
@ -1673,7 +1706,7 @@ void btree_node<P>::split(const int insert_position, btree_node *dest,
// right node then bias the split to put more values on the left node.
if (insert_position == start()) {
dest->set_finish(dest->start() + finish() - 1);
} else if (insert_position == kNodeValues) {
} else if (insert_position == kNodeSlots) {
dest->set_finish(dest->start());
} else {
dest->set_finish(dest->start() + count() / 2);
@ -1744,7 +1777,7 @@ void btree_node<P>::clear_and_delete(btree_node *node, allocator_type *alloc) {
// Navigate to the leftmost leaf under node, and then delete upwards.
while (!node->leaf()) node = node->start_child();
// Use `int` because `pos` needs to be able to hold `kNodeValues+1`, which
// Use `int` because `pos` needs to be able to hold `kNodeSlots+1`, which
// isn't guaranteed to be a valid `field_type`.
int pos = node->position();
btree_node *parent = node->parent();
@ -1832,7 +1865,7 @@ void btree_iterator<N, R, P>::decrement_slow() {
// btree methods
template <typename P>
template <typename Btree>
void btree<P>::copy_or_move_values_in_order(Btree *other) {
void btree<P>::copy_or_move_values_in_order(Btree &other) {
static_assert(std::is_same<btree, Btree>::value ||
std::is_same<const btree, Btree>::value,
"Btree type must be same or const.");
@ -1840,11 +1873,11 @@ void btree<P>::copy_or_move_values_in_order(Btree *other) {
// We can avoid key comparisons because we know the order of the
// values is the same order we'll store them in.
auto iter = other->begin();
if (iter == other->end()) return;
auto iter = other.begin();
if (iter == other.end()) return;
insert_multi(maybe_move_from_iterator(iter));
++iter;
for (; iter != other->end(); ++iter) {
for (; iter != other.end(); ++iter) {
// If the btree is not empty, we can just insert the new value at the end
// of the tree.
internal_emplace(end(), maybe_move_from_iterator(iter));
@ -1863,7 +1896,7 @@ constexpr bool btree<P>::static_assert_validation() {
// Note: We assert that kTargetValues, which is computed from
// Params::kTargetNodeSize, must fit the node_type::field_type.
static_assert(
kNodeValues < (1 << (8 * sizeof(typename node_type::field_type))),
kNodeSlots < (1 << (8 * sizeof(typename node_type::field_type))),
"target node size too large");
// Verify that key_compare returns an absl::{weak,strong}_ordering or bool.
@ -1883,31 +1916,29 @@ constexpr bool btree<P>::static_assert_validation() {
}
template <typename P>
btree<P>::btree(const key_compare &comp, const allocator_type &alloc)
: root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {}
template <typename P>
btree<P>::btree(const btree &other)
: btree(other.key_comp(), other.allocator()) {
copy_or_move_values_in_order(&other);
template <typename K>
auto btree<P>::lower_bound_equal(const K &key) const
-> std::pair<iterator, bool> {
const SearchResult<iterator, is_key_compare_to::value> res =
internal_lower_bound(key);
const iterator lower = iterator(internal_end(res.value));
const bool equal = res.HasMatch()
? res.IsEq()
: lower != end() && !compare_keys(key, lower.key());
return {lower, equal};
}
template <typename P>
template <typename K>
auto btree<P>::equal_range(const K &key) -> std::pair<iterator, iterator> {
const iterator lower = lower_bound(key);
// TODO(ezb): we should be able to avoid this comparison when there's a
// three-way comparator.
if (lower == end() || compare_keys(key, lower.key())) return {lower, lower};
const std::pair<iterator, bool> lower_and_equal = lower_bound_equal(key);
const iterator lower = lower_and_equal.first;
if (!lower_and_equal.second) {
return {lower, lower};
}
const iterator next = std::next(lower);
// When the comparator is heterogeneous, we can't assume that comparison with
// non-`key_type` will be equivalent to `key_type` comparisons so there
// could be multiple equivalent keys even in a unique-container. But for
// heterogeneous comparisons from the default string adapted comparators, we
// don't need to worry about this.
if (!is_multi_container::value &&
(std::is_same<K, key_type>::value || is_key_compare_adapted::value)) {
if (!params_type::template can_have_multiple_equivalent_keys<K>()) {
// The next iterator after lower must point to a key greater than `key`.
// Note: if this assert fails, then it may indicate that the comparator does
// not meet the equivalence requirements for Compare
@ -1918,7 +1949,7 @@ auto btree<P>::equal_range(const K &key) -> std::pair<iterator, iterator> {
// Try once more to avoid the call to upper_bound() if there's only one
// equivalent key. This should prevent all calls to upper_bound() in cases of
// unique-containers with heterogeneous comparators in which all comparison
// operators are equivalent.
// operators have the same equivalence classes.
if (next == end() || compare_keys(key, next.key())) return {lower, next};
// In this case, we need to call upper_bound() to avoid worst case O(N)
@ -1934,8 +1965,8 @@ auto btree<P>::insert_unique(const K &key, Args &&... args)
mutable_root() = rightmost_ = new_leaf_root_node(1);
}
auto res = internal_locate(key);
iterator &iter = res.value;
SearchResult<iterator, is_key_compare_to::value> res = internal_locate(key);
iterator iter = res.value;
if (res.HasMatch()) {
if (res.IsEq()) {
@ -2049,7 +2080,7 @@ auto btree<P>::operator=(const btree &other) -> btree & {
*mutable_allocator() = other.allocator();
}
copy_or_move_values_in_order(&other);
copy_or_move_values_in_order(other);
}
return *this;
}
@ -2079,7 +2110,7 @@ auto btree<P>::operator=(btree &&other) noexcept -> btree & {
// comparator while moving the values so we can't swap the key
// comparators.
*mutable_key_comp() = other.key_comp();
copy_or_move_values_in_order(&other);
copy_or_move_values_in_order(other);
}
}
}
@ -2202,31 +2233,6 @@ auto btree<P>::erase_range(iterator begin, iterator end)
return {count, begin};
}
template <typename P>
template <typename K>
auto btree<P>::erase_unique(const K &key) -> size_type {
const iterator iter = internal_find(key);
if (iter.node == nullptr) {
// The key doesn't exist in the tree, return nothing done.
return 0;
}
erase(iter);
return 1;
}
template <typename P>
template <typename K>
auto btree<P>::erase_multi(const K &key) -> size_type {
const iterator begin = internal_lower_bound(key);
if (begin.node == nullptr) {
// The key doesn't exist in the tree, return nothing done.
return 0;
}
// Delete all of the keys between begin and upper_bound(key).
const iterator end = internal_end(internal_upper_bound(key));
return erase_range(begin, end).first;
}
template <typename P>
void btree<P>::clear() {
if (!empty()) {
@ -2271,7 +2277,7 @@ void btree<P>::rebalance_or_split(iterator *iter) {
node_type *&node = iter->node;
int &insert_position = iter->position;
assert(node->count() == node->max_count());
assert(kNodeValues == node->max_count());
assert(kNodeSlots == node->max_count());
// First try to make room on the node by rebalancing.
node_type *parent = node->parent();
@ -2279,17 +2285,17 @@ void btree<P>::rebalance_or_split(iterator *iter) {
if (node->position() > parent->start()) {
// Try rebalancing with our left sibling.
node_type *left = parent->child(node->position() - 1);
assert(left->max_count() == kNodeValues);
if (left->count() < kNodeValues) {
assert(left->max_count() == kNodeSlots);
if (left->count() < kNodeSlots) {
// We bias rebalancing based on the position being inserted. If we're
// inserting at the end of the right node then we bias rebalancing to
// fill up the left node.
int to_move = (kNodeValues - left->count()) /
(1 + (insert_position < kNodeValues));
int to_move = (kNodeSlots - left->count()) /
(1 + (insert_position < static_cast<int>(kNodeSlots)));
to_move = (std::max)(1, to_move);
if (insert_position - to_move >= node->start() ||
left->count() + to_move < kNodeValues) {
left->count() + to_move < static_cast<int>(kNodeSlots)) {
left->rebalance_right_to_left(to_move, node, mutable_allocator());
assert(node->max_count() - node->count() == to_move);
@ -2308,17 +2314,17 @@ void btree<P>::rebalance_or_split(iterator *iter) {
if (node->position() < parent->finish()) {
// Try rebalancing with our right sibling.
node_type *right = parent->child(node->position() + 1);
assert(right->max_count() == kNodeValues);
if (right->count() < kNodeValues) {
assert(right->max_count() == kNodeSlots);
if (right->count() < kNodeSlots) {
// We bias rebalancing based on the position being inserted. If we're
// inserting at the beginning of the left node then we bias rebalancing
// to fill up the right node.
int to_move = (kNodeValues - right->count()) /
int to_move = (static_cast<int>(kNodeSlots) - right->count()) /
(1 + (insert_position > node->start()));
to_move = (std::max)(1, to_move);
if (insert_position <= node->finish() - to_move ||
right->count() + to_move < kNodeValues) {
right->count() + to_move < static_cast<int>(kNodeSlots)) {
node->rebalance_left_to_right(to_move, right, mutable_allocator());
if (insert_position > node->finish()) {
@ -2334,8 +2340,8 @@ void btree<P>::rebalance_or_split(iterator *iter) {
// Rebalancing failed, make sure there is room on the parent node for a new
// value.
assert(parent->max_count() == kNodeValues);
if (parent->count() == kNodeValues) {
assert(parent->max_count() == kNodeSlots);
if (parent->count() == kNodeSlots) {
iterator parent_iter(node->parent(), node->position());
rebalance_or_split(&parent_iter);
}
@ -2380,8 +2386,8 @@ bool btree<P>::try_merge_or_rebalance(iterator *iter) {
if (iter->node->position() > parent->start()) {
// Try merging with our left sibling.
node_type *left = parent->child(iter->node->position() - 1);
assert(left->max_count() == kNodeValues);
if (1 + left->count() + iter->node->count() <= kNodeValues) {
assert(left->max_count() == kNodeSlots);
if (1U + left->count() + iter->node->count() <= kNodeSlots) {
iter->position += 1 + left->count();
merge_nodes(left, iter->node);
iter->node = left;
@ -2391,8 +2397,8 @@ bool btree<P>::try_merge_or_rebalance(iterator *iter) {
if (iter->node->position() < parent->finish()) {
// Try merging with our right sibling.
node_type *right = parent->child(iter->node->position() + 1);
assert(right->max_count() == kNodeValues);
if (1 + iter->node->count() + right->count() <= kNodeValues) {
assert(right->max_count() == kNodeSlots);
if (1U + iter->node->count() + right->count() <= kNodeSlots) {
merge_nodes(iter->node, right);
return true;
}
@ -2473,12 +2479,12 @@ inline auto btree<P>::internal_emplace(iterator iter, Args &&... args)
allocator_type *alloc = mutable_allocator();
if (iter.node->count() == max_count) {
// Make room in the leaf for the new item.
if (max_count < kNodeValues) {
if (max_count < kNodeSlots) {
// Insertion into the root where the root is smaller than the full node
// size. Simply grow the size of the root node.
assert(iter.node == root());
iter.node =
new_leaf_root_node((std::min<int>)(kNodeValues, 2 * max_count));
new_leaf_root_node((std::min<int>)(kNodeSlots, 2 * max_count));
// Transfer the values from the old root to the new root.
node_type *old_root = root();
node_type *new_root = iter.node;
@ -2501,61 +2507,51 @@ template <typename P>
template <typename K>
inline auto btree<P>::internal_locate(const K &key) const
-> SearchResult<iterator, is_key_compare_to::value> {
return internal_locate_impl(key, is_key_compare_to());
}
template <typename P>
template <typename K>
inline auto btree<P>::internal_locate_impl(
const K &key, std::false_type /* IsCompareTo */) const
-> SearchResult<iterator, false> {
iterator iter(const_cast<node_type *>(root()));
for (;;) {
iter.position = iter.node->lower_bound(key, key_comp()).value;
// NOTE: we don't need to walk all the way down the tree if the keys are
// equal, but determining equality would require doing an extra comparison
// on each node on the way down, and we will need to go all the way to the
// leaf node in the expected case.
if (iter.node->leaf()) {
break;
}
iter.node = iter.node->child(iter.position);
}
return {iter};
}
template <typename P>
template <typename K>
inline auto btree<P>::internal_locate_impl(
const K &key, std::true_type /* IsCompareTo */) const
-> SearchResult<iterator, true> {
iterator iter(const_cast<node_type *>(root()));
for (;;) {
SearchResult<int, true> res = iter.node->lower_bound(key, key_comp());
SearchResult<int, is_key_compare_to::value> res =
iter.node->lower_bound(key, key_comp());
iter.position = res.value;
if (res.match == MatchKind::kEq) {
if (res.IsEq()) {
return {iter, MatchKind::kEq};
}
// Note: in the non-key-compare-to case, we don't need to walk all the way
// down the tree if the keys are equal, but determining equality would
// require doing an extra comparison on each node on the way down, and we
// will need to go all the way to the leaf node in the expected case.
if (iter.node->leaf()) {
break;
}
iter.node = iter.node->child(iter.position);
}
// Note: in the non-key-compare-to case, the key may actually be equivalent
// here (and the MatchKind::kNe is ignored).
return {iter, MatchKind::kNe};
}
template <typename P>
template <typename K>
auto btree<P>::internal_lower_bound(const K &key) const -> iterator {
auto btree<P>::internal_lower_bound(const K &key) const
-> SearchResult<iterator, is_key_compare_to::value> {
if (!params_type::template can_have_multiple_equivalent_keys<K>()) {
SearchResult<iterator, is_key_compare_to::value> ret = internal_locate(key);
ret.value = internal_last(ret.value);
return ret;
}
iterator iter(const_cast<node_type *>(root()));
SearchResult<int, is_key_compare_to::value> res;
bool seen_eq = false;
for (;;) {
iter.position = iter.node->lower_bound(key, key_comp()).value;
res = iter.node->lower_bound(key, key_comp());
iter.position = res.value;
if (iter.node->leaf()) {
break;
}
seen_eq = seen_eq || res.IsEq();
iter.node = iter.node->child(iter.position);
}
return internal_last(iter);
if (res.IsEq()) return {iter, MatchKind::kEq};
return {internal_last(iter), seen_eq ? MatchKind::kEq : MatchKind::kNe};
}
template <typename P>
@ -2575,7 +2571,7 @@ auto btree<P>::internal_upper_bound(const K &key) const -> iterator {
template <typename P>
template <typename K>
auto btree<P>::internal_find(const K &key) const -> iterator {
auto res = internal_locate(key);
SearchResult<iterator, is_key_compare_to::value> res = internal_locate(key);
if (res.HasMatch()) {
if (res.IsEq()) {
return res.value;

@ -23,6 +23,7 @@
#include "absl/base/internal/throw_delegate.h"
#include "absl/container/internal/btree.h" // IWYU pragma: export
#include "absl/container/internal/common.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
namespace absl {
@ -68,8 +69,21 @@ class btree_container {
explicit btree_container(const key_compare &comp,
const allocator_type &alloc = allocator_type())
: tree_(comp, alloc) {}
btree_container(const btree_container &other) = default;
btree_container(btree_container &&other) noexcept = default;
explicit btree_container(const allocator_type &alloc)
: tree_(key_compare(), alloc) {}
btree_container(const btree_container &other)
: btree_container(other, absl::allocator_traits<allocator_type>::
select_on_container_copy_construction(
other.get_allocator())) {}
btree_container(const btree_container &other, const allocator_type &alloc)
: tree_(other.tree_, alloc) {}
btree_container(btree_container &&other) noexcept(
std::is_nothrow_move_constructible<Tree>::value) = default;
btree_container(btree_container &&other, const allocator_type &alloc)
: tree_(std::move(other.tree_), alloc) {}
btree_container &operator=(const btree_container &other) = default;
btree_container &operator=(btree_container &&other) noexcept(
std::is_nothrow_move_assignable<Tree>::value) = default;
@ -90,6 +104,11 @@ class btree_container {
// Lookup routines.
template <typename K = key_type>
size_type count(const key_arg<K> &key) const {
auto equal_range = this->equal_range(key);
return std::distance(equal_range.first, equal_range.second);
}
template <typename K = key_type>
iterator find(const key_arg<K> &key) {
return tree_.find(key);
}
@ -138,6 +157,11 @@ class btree_container {
iterator erase(const_iterator first, const_iterator last) {
return tree_.erase_range(iterator(first), iterator(last)).second;
}
template <typename K = key_type>
size_type erase(const key_arg<K> &key) {
auto equal_range = this->equal_range(key);
return tree_.erase_range(equal_range.first, equal_range.second).first;
}
// Extract routines.
node_type extract(iterator position) {
@ -151,7 +175,6 @@ class btree_container {
return extract(iterator(position));
}
public:
// Utility routines.
void clear() { tree_.clear(); }
void swap(btree_container &other) { tree_.swap(other.tree_); }
@ -235,7 +258,7 @@ class btree_set_container : public btree_container<Tree> {
using super_type::super_type;
btree_set_container() {}
// Range constructor.
// Range constructors.
template <class InputIterator>
btree_set_container(InputIterator b, InputIterator e,
const key_compare &comp = key_compare(),
@ -243,18 +266,19 @@ class btree_set_container : public btree_container<Tree> {
: super_type(comp, alloc) {
insert(b, e);
}
template <class InputIterator>
btree_set_container(InputIterator b, InputIterator e,
const allocator_type &alloc)
: btree_set_container(b, e, key_compare(), alloc) {}
// Initializer list constructor.
// Initializer list constructors.
btree_set_container(std::initializer_list<init_type> init,
const key_compare &comp = key_compare(),
const allocator_type &alloc = allocator_type())
: btree_set_container(init.begin(), init.end(), comp, alloc) {}
// Lookup routines.
template <typename K = key_type>
size_type count(const key_arg<K> &key) const {
return this->tree_.count_unique(key);
}
btree_set_container(std::initializer_list<init_type> init,
const allocator_type &alloc)
: btree_set_container(init.begin(), init.end(), alloc) {}
// Insertion routines.
std::pair<iterator, bool> insert(const value_type &v) {
@ -313,20 +337,13 @@ class btree_set_container : public btree_container<Tree> {
return res.first;
}
// Deletion routines.
// TODO(ezb): we should support heterogeneous comparators that have different
// behavior for K!=key_type.
template <typename K = key_type>
size_type erase(const key_arg<K> &key) {
return this->tree_.erase_unique(key);
}
using super_type::erase;
// Node extraction routines.
template <typename K = key_type>
node_type extract(const key_arg<K> &key) {
auto it = this->find(key);
return it == this->end() ? node_type() : extract(it);
const std::pair<iterator, bool> lower_and_equal =
this->tree_.lower_bound_equal(key);
return lower_and_equal.second ? extract(lower_and_equal.first)
: node_type();
}
using super_type::extract;
@ -344,7 +361,7 @@ class btree_set_container : public btree_container<Tree> {
int> = 0>
void merge(btree_container<T> &src) { // NOLINT
for (auto src_it = src.begin(); src_it != src.end();) {
if (insert(std::move(*src_it)).second) {
if (insert(std::move(params_type::element(src_it.slot()))).second) {
src_it = src.erase(src_it);
} else {
++src_it;
@ -371,6 +388,7 @@ template <typename Tree>
class btree_map_container : public btree_set_container<Tree> {
using super_type = btree_set_container<Tree>;
using params_type = typename Tree::params_type;
friend class BtreeNodePeer;
private:
template <class K>
@ -535,7 +553,7 @@ class btree_multiset_container : public btree_container<Tree> {
using super_type::super_type;
btree_multiset_container() {}
// Range constructor.
// Range constructors.
template <class InputIterator>
btree_multiset_container(InputIterator b, InputIterator e,
const key_compare &comp = key_compare(),
@ -543,18 +561,19 @@ class btree_multiset_container : public btree_container<Tree> {
: super_type(comp, alloc) {
insert(b, e);
}
template <class InputIterator>
btree_multiset_container(InputIterator b, InputIterator e,
const allocator_type &alloc)
: btree_multiset_container(b, e, key_compare(), alloc) {}
// Initializer list constructor.
// Initializer list constructors.
btree_multiset_container(std::initializer_list<init_type> init,
const key_compare &comp = key_compare(),
const allocator_type &alloc = allocator_type())
: btree_multiset_container(init.begin(), init.end(), comp, alloc) {}
// Lookup routines.
template <typename K = key_type>
size_type count(const key_arg<K> &key) const {
return this->tree_.count_multi(key);
}
btree_multiset_container(std::initializer_list<init_type> init,
const allocator_type &alloc)
: btree_multiset_container(init.begin(), init.end(), alloc) {}
// Insertion routines.
iterator insert(const value_type &v) { return this->tree_.insert_multi(v); }
@ -600,18 +619,13 @@ class btree_multiset_container : public btree_container<Tree> {
return res;
}
// Deletion routines.
template <typename K = key_type>
size_type erase(const key_arg<K> &key) {
return this->tree_.erase_multi(key);
}
using super_type::erase;
// Node extraction routines.
template <typename K = key_type>
node_type extract(const key_arg<K> &key) {
auto it = this->find(key);
return it == this->end() ? node_type() : extract(it);
const std::pair<iterator, bool> lower_and_equal =
this->tree_.lower_bound_equal(key);
return lower_and_equal.second ? extract(lower_and_equal.first)
: node_type();
}
using super_type::extract;
@ -627,8 +641,9 @@ class btree_multiset_container : public btree_container<Tree> {
typename T::params_type::is_map_container>>::value,
int> = 0>
void merge(btree_container<T> &src) { // NOLINT
insert(std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()));
for (auto src_it = src.begin(), end = src.end(); src_it != end; ++src_it) {
insert(std::move(params_type::element(src_it.slot())));
}
src.clear();
}

@ -257,7 +257,7 @@ class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple
template <int I>
ElemT<I>& get() & {
return internal_compressed_tuple::Storage<ElemT<I>, I>::get();
return StorageT<I>::get();
}
template <int I>

@ -166,7 +166,7 @@ TryDecomposeValue(F&& f, Arg&& arg) {
}
TEST(DecomposeValue, Decomposable) {
auto f = [](const int& x, int&& y) {
auto f = [](const int& x, int&& y) { // NOLINT
EXPECT_EQ(&x, &y);
EXPECT_EQ(42, x);
return 'A';
@ -200,7 +200,8 @@ TryDecomposePair(F&& f, Args&&... args) {
}
TEST(DecomposePair, Decomposable) {
auto f = [](const int& x, std::piecewise_construct_t, std::tuple<int&&> k,
auto f = [](const int& x, // NOLINT
std::piecewise_construct_t, std::tuple<int&&> k,
std::tuple<double>&& v) {
EXPECT_EQ(&x, &std::get<0>(k));
EXPECT_EQ(42, x);

@ -72,6 +72,7 @@ void HashtablezInfo::PrepareForSampling() {
total_probe_length.store(0, std::memory_order_relaxed);
hashes_bitwise_or.store(0, std::memory_order_relaxed);
hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed);
hashes_bitwise_xor.store(0, std::memory_order_relaxed);
create_time = absl::Now();
// The inliner makes hardcoded skip_count difficult (especially when combined
@ -180,7 +181,9 @@ static bool ShouldForceSampling() {
if (ABSL_PREDICT_TRUE(state == kDontForce)) return false;
if (state == kUninitialized) {
state = AbslContainerInternalSampleEverything() ? kForce : kDontForce;
state = ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)()
? kForce
: kDontForce;
global_state.store(state, std::memory_order_relaxed);
}
return state == kForce;
@ -235,6 +238,7 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash,
info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed);
info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed);
info->hashes_bitwise_xor.fetch_xor(hash, std::memory_order_relaxed);
info->max_probe_length.store(
std::max(info->max_probe_length.load(std::memory_order_relaxed),
probe_length),

@ -78,6 +78,7 @@ struct HashtablezInfo {
std::atomic<size_t> total_probe_length;
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`
@ -312,7 +313,7 @@ void SetHashtablezMaxSamples(int32_t max);
// initialization of static storage duration objects.
// The definition of this constant is weak, which allows us to inject a
// different value for it at link time.
extern "C" bool AbslContainerInternalSampleEverything();
extern "C" bool ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)();
} // namespace container_internal
ABSL_NAMESPACE_END

@ -21,7 +21,8 @@ ABSL_NAMESPACE_BEGIN
namespace container_internal {
// See hashtablez_sampler.h for details.
extern "C" ABSL_ATTRIBUTE_WEAK bool AbslContainerInternalSampleEverything() {
extern "C" ABSL_ATTRIBUTE_WEAK bool ABSL_INTERNAL_C_SYMBOL(
AbslContainerInternalSampleEverything)() {
return false;
}

@ -89,6 +89,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
EXPECT_EQ(info.total_probe_length.load(), 0);
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_GE(info.create_time, test_start);
info.capacity.store(1, std::memory_order_relaxed);
@ -98,6 +99,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
info.total_probe_length.store(1, std::memory_order_relaxed);
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.create_time = test_start - absl::Hours(20);
info.PrepareForSampling();
@ -109,6 +111,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
EXPECT_EQ(info.total_probe_length.load(), 0);
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_GE(info.create_time, test_start);
}
@ -133,14 +136,17 @@ TEST(HashtablezInfoTest, RecordInsert) {
EXPECT_EQ(info.max_probe_length.load(), 6);
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00);
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x0000FF00);
RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength);
EXPECT_EQ(info.max_probe_length.load(), 6);
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00);
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x000F0F00);
RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength);
EXPECT_EQ(info.max_probe_length.load(), 12);
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00);
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x00F00F00);
}
TEST(HashtablezInfoTest, RecordErase) {

@ -33,6 +33,12 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace inlined_vector_internal {
// GCC does not deal very well with the below code
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
template <typename Iterator>
using IsAtLeastForwardIterator = std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
@ -75,6 +81,23 @@ void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first,
}
}
// If kUseMemcpy is true, memcpy(dst, src, n); else do nothing.
// Useful to avoid compiler warnings when memcpy() is used for T values
// that are not trivially copyable in non-reachable code.
template <bool kUseMemcpy>
inline void MemcpyIfAllowed(void* dst, const void* src, size_t n);
// memcpy when allowed.
template <>
inline void MemcpyIfAllowed<true>(void* dst, const void* src, size_t n) {
memcpy(dst, src, n);
}
// Do nothing for types that are not memcpy-able. This function is only
// called from non-reachable branches.
template <>
inline void MemcpyIfAllowed<false>(void*, const void*, size_t) {}
template <typename AllocatorType, typename Pointer, typename ValueAdapter,
typename SizeType>
void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first,
@ -298,14 +321,20 @@ class Storage {
// Storage Constructors and Destructor
// ---------------------------------------------------------------------------
Storage() : metadata_() {}
Storage() : metadata_(allocator_type(), /* size and is_allocated */ 0) {}
explicit Storage(const allocator_type& alloc) : metadata_(alloc, {}) {}
explicit Storage(const allocator_type& alloc)
: metadata_(alloc, /* size and is_allocated */ 0) {}
~Storage() {
pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize());
DeallocateIfAllocated();
if (GetSizeAndIsAllocated() == 0) {
// Empty and not allocated; nothing to do.
} else if (IsMemcpyOk::value) {
// No destructors need to be run; just deallocate if necessary.
DeallocateIfAllocated();
} else {
DestroyContents();
}
}
// ---------------------------------------------------------------------------
@ -363,6 +392,8 @@ class Storage {
// Storage Member Mutators
// ---------------------------------------------------------------------------
ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other);
template <typename ValueAdapter>
void Initialize(ValueAdapter values, size_type new_size);
@ -445,6 +476,8 @@ class Storage {
}
private:
ABSL_ATTRIBUTE_NOINLINE void DestroyContents();
using Metadata =
container_internal::CompressedTuple<allocator_type, size_type>;
@ -462,10 +495,47 @@ class Storage {
Inlined inlined;
};
template <typename... Args>
ABSL_ATTRIBUTE_NOINLINE reference EmplaceBackSlow(Args&&... args);
Metadata metadata_;
Data data_;
};
template <typename T, size_t N, typename A>
void Storage<T, N, A>::DestroyContents() {
pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize());
DeallocateIfAllocated();
}
template <typename T, size_t N, typename A>
void Storage<T, N, A>::InitFrom(const Storage& other) {
const auto n = other.GetSize();
assert(n > 0); // Empty sources handled handled in caller.
const_pointer src;
pointer dst;
if (!other.GetIsAllocated()) {
dst = GetInlinedData();
src = other.GetInlinedData();
} else {
// Because this is only called from the `InlinedVector` constructors, it's
// safe to take on the allocation with size `0`. If `ConstructElements(...)`
// throws, deallocation will be automatically handled by `~Storage()`.
size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), n);
dst = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity);
SetAllocatedData(dst, new_capacity);
src = other.GetAllocatedData();
}
if (IsMemcpyOk::value) {
MemcpyIfAllowed<IsMemcpyOk::value>(dst, src, sizeof(dst[0]) * n);
} else {
auto values = IteratorValueAdapter<const_pointer>(src);
inlined_vector_internal::ConstructElements(GetAllocPtr(), dst, &values, n);
}
GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated();
}
template <typename T, size_t N, typename A>
template <typename ValueAdapter>
auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size)
@ -542,48 +612,42 @@ template <typename T, size_t N, typename A>
template <typename ValueAdapter>
auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void {
StorageView storage_view = MakeStorageView();
IteratorValueAdapter<MoveIterator> move_values(
MoveIterator(storage_view.data));
AllocationTransaction allocation_tx(GetAllocPtr());
ConstructionTransaction construction_tx(GetAllocPtr());
absl::Span<value_type> construct_loop;
absl::Span<value_type> move_construct_loop;
absl::Span<value_type> destroy_loop;
if (new_size > storage_view.capacity) {
auto* const base = storage_view.data;
const size_type size = storage_view.size;
auto* alloc = GetAllocPtr();
if (new_size <= size) {
// Destroy extra old elements.
inlined_vector_internal::DestroyElements(alloc, base + new_size,
size - new_size);
} else if (new_size <= storage_view.capacity) {
// Construct new elements in place.
inlined_vector_internal::ConstructElements(alloc, base + size, &values,
new_size - size);
} else {
// Steps:
// a. Allocate new backing store.
// b. Construct new elements in new backing store.
// c. Move existing elements from old backing store to now.
// d. Destroy all elements in old backing store.
// Use transactional wrappers for the first two steps so we can roll
// back if necessary due to exceptions.
AllocationTransaction allocation_tx(alloc);
size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
pointer new_data = allocation_tx.Allocate(new_capacity);
construct_loop = {new_data + storage_view.size,
new_size - storage_view.size};
move_construct_loop = {new_data, storage_view.size};
destroy_loop = {storage_view.data, storage_view.size};
} else if (new_size > storage_view.size) {
construct_loop = {storage_view.data + storage_view.size,
new_size - storage_view.size};
} else {
destroy_loop = {storage_view.data + new_size, storage_view.size - new_size};
}
construction_tx.Construct(construct_loop.data(), &values,
construct_loop.size());
ConstructionTransaction construction_tx(alloc);
construction_tx.Construct(new_data + size, &values, new_size - size);
inlined_vector_internal::ConstructElements(
GetAllocPtr(), move_construct_loop.data(), &move_values,
move_construct_loop.size());
inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(),
destroy_loop.size());
IteratorValueAdapter<MoveIterator> move_values((MoveIterator(base)));
inlined_vector_internal::ConstructElements(alloc, new_data, &move_values,
size);
construction_tx.Commit();
if (allocation_tx.DidAllocate()) {
inlined_vector_internal::DestroyElements(alloc, base, size);
construction_tx.Commit();
DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx);
SetIsAllocated();
}
SetSize(new_size);
}
@ -684,44 +748,50 @@ template <typename T, size_t N, typename A>
template <typename... Args>
auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference {
StorageView storage_view = MakeStorageView();
const auto n = storage_view.size;
if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) {
// Fast path; new element fits.
pointer last_ptr = storage_view.data + n;
AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
std::forward<Args>(args)...);
AddSize(1);
return *last_ptr;
}
// TODO(b/173712035): Annotate with musttail attribute to prevent regression.
return EmplaceBackSlow(std::forward<Args>(args)...);
}
template <typename T, size_t N, typename A>
template <typename... Args>
auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> reference {
StorageView storage_view = MakeStorageView();
AllocationTransaction allocation_tx(GetAllocPtr());
IteratorValueAdapter<MoveIterator> move_values(
MoveIterator(storage_view.data));
pointer construct_data;
if (storage_view.size == storage_view.capacity) {
size_type new_capacity = NextCapacity(storage_view.capacity);
construct_data = allocation_tx.Allocate(new_capacity);
} else {
construct_data = storage_view.data;
}
size_type new_capacity = NextCapacity(storage_view.capacity);
pointer construct_data = allocation_tx.Allocate(new_capacity);
pointer last_ptr = construct_data + storage_view.size;
// Construct new element.
AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
std::forward<Args>(args)...);
if (allocation_tx.DidAllocate()) {
ABSL_INTERNAL_TRY {
inlined_vector_internal::ConstructElements(
GetAllocPtr(), allocation_tx.GetData(), &move_values,
storage_view.size);
}
ABSL_INTERNAL_CATCH_ANY {
AllocatorTraits::destroy(*GetAllocPtr(), last_ptr);
ABSL_INTERNAL_RETHROW;
}
inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
storage_view.size);
DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx);
SetIsAllocated();
// Move elements from old backing store to new backing store.
ABSL_INTERNAL_TRY {
inlined_vector_internal::ConstructElements(
GetAllocPtr(), allocation_tx.GetData(), &move_values,
storage_view.size);
}
ABSL_INTERNAL_CATCH_ANY {
AllocatorTraits::destroy(*GetAllocPtr(), last_ptr);
ABSL_INTERNAL_RETHROW;
}
// Destroy elements in old backing store.
inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
storage_view.size);
DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx);
SetIsAllocated();
AddSize(1);
return *last_ptr;
}
@ -885,6 +955,11 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void {
swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr());
}
// End ignore "maybe-uninitialized"
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
} // namespace inlined_vector_internal
ABSL_NAMESPACE_END
} // namespace absl

@ -404,7 +404,7 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>,
constexpr size_t Offset() const {
static_assert(N < NumOffsets, "Index out of bounds");
return adl_barrier::Align(
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1],
Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1],
ElementAlignment<N>::value);
}
@ -597,7 +597,7 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>,
constexpr size_t AllocSize() const {
static_assert(NumTypes == NumSizes, "You must specify sizes of all fields");
return Offset<NumTypes - 1>() +
SizeOf<ElementType<NumTypes - 1>>() * size_[NumTypes - 1];
SizeOf<ElementType<NumTypes - 1>>::value * size_[NumTypes - 1];
}
// If built with --config=asan, poisons padding bytes (if any) in the
@ -621,7 +621,7 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>,
// The `if` is an optimization. It doesn't affect the observable behaviour.
if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) {
size_t start =
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1];
Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1];
ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start);
}
#endif
@ -645,7 +645,7 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>,
// produce "unsigned*" where another produces "unsigned int *".
std::string DebugString() const {
const auto offsets = Offsets();
const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>()...};
const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>::value...};
const std::string types[] = {
adl_barrier::TypeName<ElementType<OffsetSeq>>()...};
std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")");

@ -128,8 +128,10 @@ TEST(Layout, ElementTypes) {
{
using L = Layout<int32_t, int32_t>;
SameType<std::tuple<int32_t, int32_t>, L::ElementTypes>();
SameType<std::tuple<int32_t, int32_t>, decltype(L::Partial())::ElementTypes>();
SameType<std::tuple<int32_t, int32_t>, decltype(L::Partial(0))::ElementTypes>();
SameType<std::tuple<int32_t, int32_t>,
decltype(L::Partial())::ElementTypes>();
SameType<std::tuple<int32_t, int32_t>,
decltype(L::Partial(0))::ElementTypes>();
}
{
using L = Layout<int8_t, int32_t, Int128>;
@ -368,18 +370,21 @@ TEST(Layout, PointerByIndex) {
{
using L = Layout<int32_t>;
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p))));
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<0>(p))));
}
{
using L = Layout<int32_t, int32_t>;
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p))));
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
EXPECT_EQ(12, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<1>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<0>(p))));
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
EXPECT_EQ(12,
Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<1>(p))));
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<1>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<0>(p))));
EXPECT_EQ(
12, Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<1>(p))));
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<0>(p))));
EXPECT_EQ(12, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<1>(p))));
}
@ -387,39 +392,44 @@ TEST(Layout, PointerByIndex) {
using L = Layout<int8_t, int32_t, Int128>;
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<0>(p))));
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<0>(p))));
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<1>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<1>(p))));
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<0>(p))));
EXPECT_EQ(4, Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<1>(p))));
EXPECT_EQ(4,
Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<1>(p))));
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<0>(p))));
EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<1>(p))));
EXPECT_EQ(8,
Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<1>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<0>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<1>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<1>(p))));
EXPECT_EQ(0,
Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<2>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<0>(p))));
EXPECT_EQ(4,
Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<1>(p))));
EXPECT_EQ(
4, Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<1>(p))));
EXPECT_EQ(8,
Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<2>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<0>(p))));
EXPECT_EQ(8,
Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
EXPECT_EQ(
8, Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
EXPECT_EQ(24,
Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<2>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
0,
Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
EXPECT_EQ(
0, Distance(p, Type<const Int128*>(L::Partial(0, 0, 0).Pointer<2>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p))));
EXPECT_EQ(
4, Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
4,
Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
EXPECT_EQ(
8, Distance(p, Type<const Int128*>(L::Partial(1, 0, 0).Pointer<2>(p))));
EXPECT_EQ(
@ -428,7 +438,8 @@ TEST(Layout, PointerByIndex) {
24,
Distance(p, Type<const Int128*>(L::Partial(5, 3, 1).Pointer<2>(p))));
EXPECT_EQ(
8, Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
8,
Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L(5, 3, 1).Pointer<0>(p))));
EXPECT_EQ(24, Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<2>(p))));
EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<1>(p))));
@ -439,75 +450,78 @@ TEST(Layout, PointerByType) {
alignas(max_align_t) const unsigned char p[100] = {};
{
using L = Layout<int32_t>;
EXPECT_EQ(0,
Distance(p, Type<const int32_t*>(L::Partial().Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<int32_t>(p))));
EXPECT_EQ(
0,
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<int32_t>(p))));
}
{
using L = Layout<int8_t, int32_t, Int128>;
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<int8_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
EXPECT_EQ(4,
Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
EXPECT_EQ(8,
Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<int8_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p))));
0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
EXPECT_EQ(
0,
Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
EXPECT_EQ(
4, Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p))));
0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
EXPECT_EQ(
8,
Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
4,
Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
EXPECT_EQ(
8, Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p))));
8,
Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
EXPECT_EQ(
24,
Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
0,
Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(
L::Partial(0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(
0,
Distance(p, Type<const int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p))));
Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
EXPECT_EQ(
0,
Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<const Int128*>(
L::Partial(0, 0, 0).Pointer<Int128>(p))));
Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
EXPECT_EQ(4, Distance(p, Type<const int32_t*>(
L::Partial(1, 0).Pointer<int32_t>(p))));
EXPECT_EQ(
8,
Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
EXPECT_EQ(
0,
Distance(p, Type<const int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p))));
Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
EXPECT_EQ(8, Distance(p, Type<const int32_t*>(
L::Partial(5, 3).Pointer<int32_t>(p))));
EXPECT_EQ(
4,
Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p))));
24,
Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
L::Partial(0, 0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(
L::Partial(0, 0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<const Int128*>(
L::Partial(0, 0, 0).Pointer<Int128>(p))));
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
L::Partial(1, 0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(4, Distance(p, Type<const int32_t*>(
L::Partial(1, 0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(8, Distance(p, Type<const Int128*>(
L::Partial(1, 0, 0).Pointer<Int128>(p))));
EXPECT_EQ(
0,
Distance(p, Type<const int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p))));
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
L::Partial(5, 3, 1).Pointer<int8_t>(p))));
EXPECT_EQ(24, Distance(p, Type<const Int128*>(
L::Partial(5, 3, 1).Pointer<Int128>(p))));
EXPECT_EQ(
8,
Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p))));
EXPECT_EQ(8, Distance(p, Type<const int32_t*>(
L::Partial(5, 3, 1).Pointer<int32_t>(p))));
EXPECT_EQ(24,
Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<Int128>(p))));
EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
EXPECT_EQ(
8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
}
}
@ -548,15 +562,18 @@ TEST(Layout, MutablePointerByIndex) {
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
EXPECT_EQ(24, Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<2>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p))));
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
EXPECT_EQ(0,
Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<2>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p))));
EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
EXPECT_EQ(4,
Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<2>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<0>(p))));
EXPECT_EQ(24,
Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<2>(p))));
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
EXPECT_EQ(8,
Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<0>(p))));
EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<2>(p))));
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<1>(p))));
@ -568,48 +585,61 @@ TEST(Layout, MutablePointerByType) {
{
using L = Layout<int32_t>;
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3).Pointer<int32_t>(p))));
}
{
using L = Layout<int8_t, int32_t, Int128>;
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial().Pointer<int8_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
EXPECT_EQ(4,
Distance(p, Type<int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(8,
Distance(p, Type<int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
EXPECT_EQ(
4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p))));
EXPECT_EQ(8,
Distance(p, Type<Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
EXPECT_EQ(
8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p))));
EXPECT_EQ(24,
Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
EXPECT_EQ(0,
Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(0,
Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(
0,
Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<Int128>(p))));
EXPECT_EQ(0,
Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(4,
Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p))));
EXPECT_EQ(
4,
Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p))));
EXPECT_EQ(
8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<Int128>(p))));
EXPECT_EQ(0,
Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p))));
EXPECT_EQ(
0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p))));
EXPECT_EQ(
24, Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<Int128>(p))));
EXPECT_EQ(8,
Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p))));
EXPECT_EQ(
8,
Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p))));
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<int8_t>(p))));
EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<Int128>(p))));
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
@ -790,67 +820,72 @@ TEST(Layout, SliceByIndexData) {
{
using L = Layout<int32_t>;
EXPECT_EQ(
0,
Distance(p, Type<Span<const int32_t>>(L::Partial(0).Slice<0>(p)).data()));
0, Distance(
p, Type<Span<const int32_t>>(L::Partial(0).Slice<0>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(L(3).Slice<0>(p)).data()));
0, Distance(
p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
EXPECT_EQ(0,
Distance(p, Type<Span<const int32_t>>(L(3).Slice<0>(p)).data()));
}
{
using L = Layout<int32_t, int32_t>;
EXPECT_EQ(
0,
Distance(p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
0, Distance(
p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
EXPECT_EQ(
0,
Distance(p,
Type<Span<const int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
Distance(
p, Type<Span<const int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
EXPECT_EQ(
12,
Distance(p,
Type<Span<const int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
EXPECT_EQ(0,
Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<0>(p)).data()));
EXPECT_EQ(12,
Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<1>(p)).data()));
Distance(
p, Type<Span<const int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<0>(p)).data()));
EXPECT_EQ(
12, Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<1>(p)).data()));
}
{
using L = Layout<int8_t, int32_t, Int128>;
EXPECT_EQ(
0,
Distance(p, Type<Span<const int8_t>>(L::Partial(0).Slice<0>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<const int8_t>>(L::Partial(1).Slice<0>(p)).data()));
0, Distance(
p, Type<Span<const int8_t>>(L::Partial(0).Slice<0>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<const int8_t>>(L::Partial(5).Slice<0>(p)).data()));
0, Distance(
p, Type<Span<const int8_t>>(L::Partial(1).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
p, Type<Span<const int8_t>>(L::Partial(5).Slice<0>(p)).data()));
EXPECT_EQ(
0,
Distance(p,
Type<Span<const int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
Distance(
p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
0,
Distance(
p, Type<Span<const int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
EXPECT_EQ(
4,
Distance(p,
Type<Span<const int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
Distance(
p, Type<Span<const int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
0,
Distance(
p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
EXPECT_EQ(
8,
Distance(p,
Type<Span<const int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
Distance(
p, Type<Span<const int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
p,
Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
EXPECT_EQ(
0,
Distance(
@ -864,7 +899,8 @@ TEST(Layout, SliceByIndexData) {
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
p,
Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
EXPECT_EQ(
4,
Distance(
@ -878,7 +914,8 @@ TEST(Layout, SliceByIndexData) {
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
p,
Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
EXPECT_EQ(
24,
Distance(
@ -890,12 +927,14 @@ TEST(Layout, SliceByIndexData) {
p,
Type<Span<const int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
0,
Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
EXPECT_EQ(
24,
Distance(p, Type<Span<const Int128>>(L(5, 3, 1).Slice<2>(p)).data()));
EXPECT_EQ(
8, Distance(p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
8,
Distance(p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
}
}
@ -906,98 +945,94 @@ TEST(Layout, SliceByTypeData) {
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
p,
Type<Span<const int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
p,
Type<Span<const int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<const int32_t>>(L(3).Slice<int32_t>(p)).data()));
0,
Distance(p, Type<Span<const int32_t>>(L(3).Slice<int32_t>(p)).data()));
}
{
using L = Layout<int8_t, int32_t, Int128>;
EXPECT_EQ(
0, Distance(
p, Type<Span<const int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<const int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<const int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0,
Distance(
p,
Type<Span<const int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data()));
Type<Span<const int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
4,
Distance(
p,
Type<Span<const int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data()));
Type<Span<const int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0,
Distance(
p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data()));
EXPECT_EQ(
8,
Distance(
p,
Type<Span<const int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data()));
Type<Span<const int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0,
Distance(
p,
Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data()));
Distance(p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<int8_t>(p))
.data()));
EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(
L::Partial(0, 0).Slice<int32_t>(p))
.data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<const int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p))
Distance(p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<int8_t>(p))
.data()));
EXPECT_EQ(0, Distance(p, Type<Span<const Int128>>(
L::Partial(0, 0, 0).Slice<Int128>(p))
EXPECT_EQ(4, Distance(p, Type<Span<const int32_t>>(
L::Partial(1, 0).Slice<int32_t>(p))
.data()));
EXPECT_EQ(
0,
Distance(
p,
Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
4,
Distance(p, Type<Span<const int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p))
Distance(p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<int8_t>(p))
.data()));
EXPECT_EQ(8, Distance(p, Type<Span<const int32_t>>(
L::Partial(5, 3).Slice<int32_t>(p))
.data()));
EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
L::Partial(0, 0, 0).Slice<int8_t>(p))
.data()));
EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(
L::Partial(0, 0, 0).Slice<int32_t>(p))
.data()));
EXPECT_EQ(0, Distance(p, Type<Span<const Int128>>(
L::Partial(0, 0, 0).Slice<Int128>(p))
.data()));
EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
L::Partial(1, 0, 0).Slice<int8_t>(p))
.data()));
EXPECT_EQ(4, Distance(p, Type<Span<const int32_t>>(
L::Partial(1, 0, 0).Slice<int32_t>(p))
.data()));
EXPECT_EQ(8, Distance(p, Type<Span<const Int128>>(
L::Partial(1, 0, 0).Slice<Int128>(p))
.data()));
EXPECT_EQ(
0,
Distance(
p,
Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data()));
EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
L::Partial(5, 3, 1).Slice<int8_t>(p))
.data()));
EXPECT_EQ(24, Distance(p, Type<Span<const Int128>>(
L::Partial(5, 3, 1).Slice<Int128>(p))
.data()));
EXPECT_EQ(
8,
Distance(p, Type<Span<const int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p))
.data()));
EXPECT_EQ(8, Distance(p, Type<Span<const int32_t>>(
L::Partial(5, 3, 1).Slice<int32_t>(p))
.data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
Distance(p,
Type<Span<const int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
EXPECT_EQ(
24,
Distance(p,
Type<Span<const Int128>>(L(5, 3, 1).Slice<Int128>(p)).data()));
EXPECT_EQ(
8, Distance(
p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
8,
Distance(
p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
}
}
@ -1005,18 +1040,19 @@ TEST(Layout, MutableSliceByIndexData) {
alignas(max_align_t) unsigned char p[100];
{
using L = Layout<int32_t>;
EXPECT_EQ(0,
Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<0>(p)).data()));
EXPECT_EQ(0,
Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3).Slice<0>(p)).data()));
}
{
using L = Layout<int32_t, int32_t>;
EXPECT_EQ(0,
Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
0, Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
EXPECT_EQ(
12,
Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
@ -1025,55 +1061,63 @@ TEST(Layout, MutableSliceByIndexData) {
}
{
using L = Layout<int8_t, int32_t, Int128>;
EXPECT_EQ(0,
Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<0>(p)).data()));
EXPECT_EQ(0,
Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<0>(p)).data()));
EXPECT_EQ(0,
Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
0, Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
0, Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<0>(p)).data()));
EXPECT_EQ(
4, Distance(p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
0, Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
0,
Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
EXPECT_EQ(
8, Distance(p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
0,
Distance(p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
EXPECT_EQ(
4,
Distance(p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data()));
Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
EXPECT_EQ(
8,
Distance(p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<2>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
0, Distance(
p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
EXPECT_EQ(
4,
Distance(p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data()));
4, Distance(
p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data()));
EXPECT_EQ(
8, Distance(
p, Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<2>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
0, Distance(
p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
EXPECT_EQ(
24, Distance(
p, Type<Span<Int128>>(L::Partial(5, 3, 1).Slice<2>(p)).data()));
EXPECT_EQ(
8,
Distance(p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
EXPECT_EQ(0, Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
8, Distance(
p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
EXPECT_EQ(0,
Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
EXPECT_EQ(24,
Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<2>(p)).data()));
EXPECT_EQ(8, Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
EXPECT_EQ(8,
Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
}
}
@ -1082,66 +1126,84 @@ TEST(Layout, MutableSliceByTypeData) {
{
using L = Layout<int32_t>;
EXPECT_EQ(
0,
Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
0, Distance(
p, Type<Span<int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3).Slice<int32_t>(p)).data()));
0, Distance(
p, Type<Span<int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
EXPECT_EQ(0,
Distance(p, Type<Span<int32_t>>(L(3).Slice<int32_t>(p)).data()));
}
{
using L = Layout<int8_t, int32_t, Int128>;
EXPECT_EQ(
0, Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
0,
Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
0,
Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
0,
Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data()));
Distance(p,
Type<Span<int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data()));
0,
Distance(
p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data()));
Distance(p,
Type<Span<int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
4, Distance(
p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data()));
4,
Distance(
p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data()));
EXPECT_EQ(
0,
Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data()));
Distance(p,
Type<Span<int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data()));
EXPECT_EQ(
8, Distance(
p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data()));
8,
Distance(
p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data()));
0,
Distance(
p,
Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
0,
Distance(
p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p)).data()));
p,
Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p)).data()));
EXPECT_EQ(
0,
Distance(
p,
Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<Int128>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data()));
0,
Distance(
p,
Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data()));
EXPECT_EQ(
4,
Distance(
p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p)).data()));
p,
Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p)).data()));
EXPECT_EQ(
8,
Distance(
p,
Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<Int128>(p)).data()));
EXPECT_EQ(
0, Distance(
p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data()));
0,
Distance(
p,
Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data()));
EXPECT_EQ(
24,
Distance(
@ -1150,14 +1212,16 @@ TEST(Layout, MutableSliceByTypeData) {
EXPECT_EQ(
8,
Distance(
p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p)).data()));
EXPECT_EQ(0,
Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
p,
Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p)).data()));
EXPECT_EQ(
0, Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
EXPECT_EQ(
24,
Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<Int128>(p)).data()));
EXPECT_EQ(
8, Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
8,
Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
}
}
@ -1256,17 +1320,17 @@ TEST(Layout, MutableSlices) {
}
{
const auto x = L::Partial(1, 2, 3);
EXPECT_THAT(
(Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(x.Slices(p))),
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
IsSameSlice(x.Slice<2>(p))));
EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(
x.Slices(p))),
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
IsSameSlice(x.Slice<2>(p))));
}
{
const L x(1, 2, 3);
EXPECT_THAT(
(Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(x.Slices(p))),
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
IsSameSlice(x.Slice<2>(p))));
EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(
x.Slices(p))),
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
IsSameSlice(x.Slice<2>(p))));
}
}
@ -1398,7 +1462,8 @@ TEST(Layout, DebugString) {
x.DebugString());
}
{
constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3);
constexpr auto x =
Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3);
EXPECT_EQ(
"@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
"@16" +
@ -1406,7 +1471,8 @@ TEST(Layout, DebugString) {
x.DebugString());
}
{
constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3, 4);
constexpr auto x =
Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3, 4);
EXPECT_EQ(
"@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
"@16" +

@ -27,7 +27,7 @@ constexpr size_t Group::kWidth;
// Returns "random" seed.
inline size_t RandomSeed() {
#if ABSL_HAVE_THREAD_LOCAL
#ifdef ABSL_HAVE_THREAD_LOCAL
static thread_local size_t counter = 0;
size_t value = ++counter;
#else // ABSL_HAVE_THREAD_LOCAL
@ -43,6 +43,19 @@ bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) {
return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6;
}
void ConvertDeletedToEmptyAndFullToDeleted(
ctrl_t* ctrl, size_t capacity) {
assert(ctrl[capacity] == kSentinel);
assert(IsValidCapacity(capacity));
for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
}
// Copy the cloned ctrl bytes.
std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
ctrl[capacity] = kSentinel;
}
} // namespace container_internal
ABSL_NAMESPACE_END
} // namespace absl

@ -102,7 +102,6 @@
#include <type_traits>
#include <utility>
#include "absl/base/internal/bits.h"
#include "absl/base/internal/endian.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h"
@ -116,6 +115,7 @@
#include "absl/container/internal/layout.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
#include "absl/numeric/bits.h"
#include "absl/utility/utility.h"
namespace absl {
@ -189,18 +189,9 @@ constexpr bool IsNoThrowSwappable(std::false_type /* is_swappable */) {
}
template <typename T>
int TrailingZeros(T x) {
return sizeof(T) == 8 ? base_internal::CountTrailingZerosNonZero64(
static_cast<uint64_t>(x))
: base_internal::CountTrailingZerosNonZero32(
static_cast<uint32_t>(x));
}
template <typename T>
int LeadingZeros(T x) {
return sizeof(T) == 8
? base_internal::CountLeadingZeros64(static_cast<uint64_t>(x))
: base_internal::CountLeadingZeros32(static_cast<uint32_t>(x));
uint32_t TrailingZeros(T x) {
ABSL_INTERNAL_ASSUME(x != 0);
return countr_zero(x);
}
// An abstraction over a bitmask. It provides an easy way to iterate through the
@ -230,26 +221,24 @@ class BitMask {
}
explicit operator bool() const { return mask_ != 0; }
int operator*() const { return LowestBitSet(); }
int LowestBitSet() const {
uint32_t LowestBitSet() const {
return container_internal::TrailingZeros(mask_) >> Shift;
}
int HighestBitSet() const {
return (sizeof(T) * CHAR_BIT - container_internal::LeadingZeros(mask_) -
1) >>
Shift;
uint32_t HighestBitSet() const {
return static_cast<uint32_t>((bit_width(mask_) - 1) >> Shift);
}
BitMask begin() const { return *this; }
BitMask end() const { return BitMask(0); }
int TrailingZeros() const {
uint32_t TrailingZeros() const {
return container_internal::TrailingZeros(mask_) >> Shift;
}
int LeadingZeros() const {
uint32_t LeadingZeros() const {
constexpr int total_significant_bits = SignificantBits << Shift;
constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
return container_internal::LeadingZeros(mask_ << extra_bits) >> Shift;
return countl_zero(mask_ << extra_bits) >> Shift;
}
private:
@ -380,8 +369,8 @@ struct GroupSse2Impl {
// Returns the number of trailing empty or deleted elements in the group.
uint32_t CountLeadingEmptyOrDeleted() const {
auto special = _mm_set1_epi8(kSentinel);
return TrailingZeros(
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1);
return TrailingZeros(static_cast<uint32_t>(
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1));
}
void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
@ -472,25 +461,23 @@ inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; }
// DELETED -> EMPTY
// EMPTY -> EMPTY
// FULL -> DELETED
inline void ConvertDeletedToEmptyAndFullToDeleted(
ctrl_t* ctrl, size_t capacity) {
assert(ctrl[capacity] == kSentinel);
assert(IsValidCapacity(capacity));
for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
}
// Copy the cloned ctrl bytes.
std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
ctrl[capacity] = kSentinel;
}
void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity);
// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1.
inline size_t NormalizeCapacity(size_t n) {
return n ? ~size_t{} >> LeadingZeros(n) : 1;
return n ? ~size_t{} >> countl_zero(n) : 1;
}
// We use 7/8th as maximum load factor.
// For 16-wide groups, that gives an average of two empty slots per group.
// General notes on capacity/growth methods below:
// - We use 7/8th as maximum load factor. For 16-wide groups, that gives an
// average of two empty slots per group.
// - For (capacity+1) >= Group::kWidth, growth is 7/8*capacity.
// - For (capacity+1) < Group::kWidth, growth == capacity. In this case, we
// never need to probe (the whole table fits in one group) so we don't need a
// load factor less than 1.
// Given `capacity` of the table, returns the size (i.e. number of full slots)
// at which we should grow the capacity.
inline size_t CapacityToGrowth(size_t capacity) {
assert(IsValidCapacity(capacity));
// `capacity*7/8`
@ -501,7 +488,7 @@ inline size_t CapacityToGrowth(size_t capacity) {
return capacity - capacity / 8;
}
// From desired "growth" to a lowerbound of the necessary capacity.
// Might not be a valid one and required NormalizeCapacity().
// Might not be a valid one and requires NormalizeCapacity().
inline size_t GrowthToLowerboundCapacity(size_t growth) {
// `growth*8/7`
if (Group::kWidth == 8 && growth == 7) {
@ -523,6 +510,64 @@ inline void AssertIsValid(ctrl_t* ctrl) {
"been erased, or the table might have rehashed.");
}
struct FindInfo {
size_t offset;
size_t probe_length;
};
// The representation of the object has two modes:
// - small: For capacities < kWidth-1
// - large: For the rest.
//
// Differences:
// - In small mode we are able to use the whole capacity. The extra control
// bytes give us at least one "empty" control byte to stop the iteration.
// This is important to make 1 a valid capacity.
//
// - In small mode only the first `capacity()` control bytes after the
// sentinel are valid. The rest contain dummy kEmpty values that do not
// represent a real slot. This is important to take into account on
// find_first_non_full(), where we never try ShouldInsertBackwards() for
// small tables.
inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
inline probe_seq<Group::kWidth> probe(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.
//
// This function is very useful when insertions happen and:
// - the input is already a set
// - there are enough slots
// - the element with the hash is not in the table
inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash,
size_t capacity) {
auto seq = probe(ctrl, hash, capacity);
while (true) {
Group g{ctrl + seq.offset()};
auto mask = g.MatchEmptyOrDeleted();
if (mask) {
#if !defined(NDEBUG)
// We want to add entropy even when ASLR is not enabled.
// In debug build we will randomly insert in either the front or back of
// the group.
// TODO(kfm,sbenza): revisit after we do unconditional mixing
if (!is_small(capacity) && ShouldInsertBackwards(hash, ctrl)) {
return {seq.offset(mask.HighestBitSet()), seq.index()};
}
#endif
return {seq.offset(mask.LowestBitSet()), seq.index()};
}
seq.next();
assert(seq.index() < capacity && "full table!");
}
}
// Policy: a policy defines how to perform different operations on
// the slots of the hashtable (see hash_policy_traits.h for the full interface
// of policy).
@ -747,10 +792,10 @@ class raw_hash_set {
explicit raw_hash_set(size_t bucket_count, const hasher& hash = hasher(),
const key_equal& eq = key_equal(),
const allocator_type& alloc = allocator_type())
: ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) {
: ctrl_(EmptyGroup()),
settings_(0, HashtablezInfoHandle(), hash, eq, alloc) {
if (bucket_count) {
capacity_ = NormalizeCapacity(bucket_count);
reset_growth_left();
initialize_slots();
}
}
@ -856,10 +901,10 @@ class raw_hash_set {
// than a full `insert`.
for (const auto& v : that) {
const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v);
auto target = find_first_non_full(hash);
auto target = find_first_non_full(ctrl_, hash, capacity_);
set_ctrl(target.offset, H2(hash));
emplace_at(target.offset, v);
infoz_.RecordInsert(hash, target.probe_length);
infoz().RecordInsert(hash, target.probe_length);
}
size_ = that.size();
growth_left() -= that.size();
@ -873,28 +918,27 @@ class raw_hash_set {
slots_(absl::exchange(that.slots_, nullptr)),
size_(absl::exchange(that.size_, 0)),
capacity_(absl::exchange(that.capacity_, 0)),
infoz_(absl::exchange(that.infoz_, HashtablezInfoHandle())),
// Hash, equality and allocator are copied instead of moved because
// `that` must be left valid. If Hash is std::function<Key>, moving it
// would create a nullptr functor that cannot be called.
settings_(that.settings_) {
// growth_left was copied above, reset the one from `that`.
that.growth_left() = 0;
}
settings_(absl::exchange(that.growth_left(), 0),
absl::exchange(that.infoz(), HashtablezInfoHandle()),
that.hash_ref(), that.eq_ref(), that.alloc_ref()) {}
raw_hash_set(raw_hash_set&& that, const allocator_type& a)
: ctrl_(EmptyGroup()),
slots_(nullptr),
size_(0),
capacity_(0),
settings_(0, that.hash_ref(), that.eq_ref(), a) {
settings_(0, HashtablezInfoHandle(), that.hash_ref(), that.eq_ref(),
a) {
if (a == that.alloc_ref()) {
std::swap(ctrl_, that.ctrl_);
std::swap(slots_, that.slots_);
std::swap(size_, that.size_);
std::swap(capacity_, that.capacity_);
std::swap(growth_left(), that.growth_left());
std::swap(infoz_, that.infoz_);
std::swap(infoz(), that.infoz());
} else {
reserve(that.size());
// Note: this will copy elements of dense_set and unordered_set instead of
@ -965,7 +1009,7 @@ class raw_hash_set {
reset_growth_left();
}
assert(empty());
infoz_.RecordStorageChanged(0, capacity_);
infoz().RecordStorageChanged(0, capacity_);
}
// This overload kicks in when the argument is an rvalue of insertable and
@ -1038,7 +1082,7 @@ class raw_hash_set {
template <class InputIt>
void insert(InputIt first, InputIt last) {
for (; first != last; ++first) insert(*first);
for (; first != last; ++first) emplace(*first);
}
template <class T, RequiresNotInit<T> = 0, RequiresInsertable<const T&> = 0>
@ -1065,7 +1109,9 @@ class raw_hash_set {
}
iterator insert(const_iterator, node_type&& node) {
return insert(std::move(node)).first;
auto res = insert(std::move(node));
node = std::move(res.node);
return res.position;
}
// This overload kicks in if we can deduce the key from args. This enables us
@ -1255,7 +1301,7 @@ class raw_hash_set {
swap(growth_left(), that.growth_left());
swap(hash_ref(), that.hash_ref());
swap(eq_ref(), that.eq_ref());
swap(infoz_, that.infoz_);
swap(infoz(), that.infoz());
SwapAlloc(alloc_ref(), that.alloc_ref(),
typename AllocTraits::propagate_on_container_swap{});
}
@ -1264,7 +1310,7 @@ class raw_hash_set {
if (n == 0 && capacity_ == 0) return;
if (n == 0 && size_ == 0) {
destroy_slots();
infoz_.RecordStorageChanged(0, 0);
infoz().RecordStorageChanged(0, 0);
return;
}
// bitor is a faster way of doing `max` here. We will round up to the next
@ -1276,7 +1322,12 @@ class raw_hash_set {
}
}
void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); }
void reserve(size_t n) {
size_t m = GrowthToLowerboundCapacity(n);
if (m > capacity_) {
resize(NormalizeCapacity(m));
}
}
// Extension API: support for heterogeneous keys.
//
@ -1301,7 +1352,7 @@ class raw_hash_set {
void prefetch(const key_arg<K>& key) const {
(void)key;
#if defined(__GNUC__)
auto seq = probe(hash_ref()(key));
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()));
#endif // __GNUC__
@ -1316,7 +1367,7 @@ class raw_hash_set {
// called heterogeneous key support.
template <class K = key_type>
iterator find(const key_arg<K>& key, size_t hash) {
auto seq = probe(hash);
auto seq = probe(ctrl_, hash, capacity_);
while (true) {
Group g{ctrl_ + seq.offset()};
for (int i : g.Match(H2(hash))) {
@ -1477,7 +1528,7 @@ class raw_hash_set {
set_ctrl(index, was_never_full ? kEmpty : kDeleted);
growth_left() += was_never_full;
infoz_.RecordErase();
infoz().RecordErase();
}
void initialize_slots() {
@ -1494,7 +1545,7 @@ class raw_hash_set {
// bound more carefully.
if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
slots_ == nullptr) {
infoz_ = Sample();
infoz() = Sample();
}
auto layout = MakeLayout(capacity_);
@ -1504,7 +1555,7 @@ class raw_hash_set {
slots_ = layout.template Pointer<1>(mem);
reset_ctrl();
reset_growth_left();
infoz_.RecordStorageChanged(size_, capacity_);
infoz().RecordStorageChanged(size_, capacity_);
}
void destroy_slots() {
@ -1538,7 +1589,7 @@ class raw_hash_set {
if (IsFull(old_ctrl[i])) {
size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
PolicyTraits::element(old_slots + i));
auto target = find_first_non_full(hash);
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));
@ -1552,12 +1603,12 @@ class raw_hash_set {
Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl,
layout.AllocSize());
}
infoz_.RecordRehash(total_probe_length);
infoz().RecordRehash(total_probe_length);
}
void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE {
assert(IsValidCapacity(capacity_));
assert(!is_small());
assert(!is_small(capacity_));
// Algorithm:
// - mark all DELETED slots as EMPTY
// - mark all FULL slots as DELETED
@ -1582,7 +1633,7 @@ class raw_hash_set {
if (!IsDeleted(ctrl_[i])) continue;
size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
PolicyTraits::element(slots_ + i));
auto target = find_first_non_full(hash);
auto target = find_first_non_full(ctrl_, hash, capacity_);
size_t new_i = target.offset;
total_probe_length += target.probe_length;
@ -1590,7 +1641,8 @@ class raw_hash_set {
// If they do, we don't need to move the object as it falls already in the
// best probe we can.
const auto probe_index = [&](size_t pos) {
return ((pos - probe(hash).offset()) & capacity_) / Group::kWidth;
return ((pos - probe(ctrl_, hash, capacity_).offset()) & capacity_) /
Group::kWidth;
};
// Element doesn't move.
@ -1617,7 +1669,7 @@ class raw_hash_set {
}
}
reset_growth_left();
infoz_.RecordRehash(total_probe_length);
infoz().RecordRehash(total_probe_length);
}
void rehash_and_grow_if_necessary() {
@ -1634,7 +1686,7 @@ class raw_hash_set {
bool has_element(const value_type& elem) const {
size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem);
auto seq = probe(hash);
auto seq = probe(ctrl_, hash, capacity_);
while (true) {
Group g{ctrl_ + seq.offset()};
for (int i : g.Match(H2(hash))) {
@ -1649,41 +1701,6 @@ class raw_hash_set {
return false;
}
// Probes the raw_hash_set with the probe sequence for hash and returns the
// pointer to the first empty or deleted slot.
// NOTE: this function must work with tables having both kEmpty and kDelete
// in one group. Such tables appears during drop_deletes_without_resize.
//
// This function is very useful when insertions happen and:
// - the input is already a set
// - there are enough slots
// - the element with the hash is not in the table
struct FindInfo {
size_t offset;
size_t probe_length;
};
FindInfo find_first_non_full(size_t hash) {
auto seq = probe(hash);
while (true) {
Group g{ctrl_ + seq.offset()};
auto mask = g.MatchEmptyOrDeleted();
if (mask) {
#if !defined(NDEBUG)
// We want to add entropy even when ASLR is not enabled.
// In debug build we will randomly insert in either the front or back of
// the group.
// TODO(kfm,sbenza): revisit after we do unconditional mixing
if (!is_small() && ShouldInsertBackwards(hash, ctrl_)) {
return {seq.offset(mask.HighestBitSet()), seq.index()};
}
#endif
return {seq.offset(mask.LowestBitSet()), seq.index()};
}
seq.next();
assert(seq.index() < capacity_ && "full table!");
}
}
// TODO(alkis): Optimize this assuming *this and that don't overlap.
raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) {
raw_hash_set tmp(std::move(that));
@ -1700,7 +1717,7 @@ class raw_hash_set {
template <class K>
std::pair<size_t, bool> find_or_prepare_insert(const K& key) {
auto hash = hash_ref()(key);
auto seq = probe(hash);
auto seq = probe(ctrl_, hash, capacity_);
while (true) {
Group g{ctrl_ + seq.offset()};
for (int i : g.Match(H2(hash))) {
@ -1717,16 +1734,16 @@ class raw_hash_set {
}
size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE {
auto target = find_first_non_full(hash);
auto target = find_first_non_full(ctrl_, hash, capacity_);
if (ABSL_PREDICT_FALSE(growth_left() == 0 &&
!IsDeleted(ctrl_[target.offset]))) {
rehash_and_grow_if_necessary();
target = find_first_non_full(hash);
target = find_first_non_full(ctrl_, hash, capacity_);
}
++size_;
growth_left() -= IsEmpty(ctrl_[target.offset]);
set_ctrl(target.offset, H2(hash));
infoz_.RecordInsert(hash, target.probe_length);
infoz().RecordInsert(hash, target.probe_length);
return target.offset;
}
@ -1754,10 +1771,6 @@ class raw_hash_set {
private:
friend struct RawHashSetTestOnlyAccess;
probe_seq<Group::kWidth> probe(size_t hash) const {
return probe_seq<Group::kWidth>(H1(hash, ctrl_), capacity_);
}
// Reset all ctrl bytes back to kEmpty, except the sentinel.
void reset_ctrl() {
std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth);
@ -1787,29 +1800,15 @@ class raw_hash_set {
size_t& growth_left() { return settings_.template get<0>(); }
// The representation of the object has two modes:
// - small: For capacities < kWidth-1
// - large: For the rest.
//
// Differences:
// - In small mode we are able to use the whole capacity. The extra control
// bytes give us at least one "empty" control byte to stop the iteration.
// This is important to make 1 a valid capacity.
//
// - In small mode only the first `capacity()` control bytes after the
// sentinel are valid. The rest contain dummy kEmpty values that do not
// represent a real slot. This is important to take into account on
// find_first_non_full(), where we never try ShouldInsertBackwards() for
// small tables.
bool is_small() const { return capacity_ < Group::kWidth - 1; }
hasher& hash_ref() { return settings_.template get<1>(); }
const hasher& hash_ref() const { return settings_.template get<1>(); }
key_equal& eq_ref() { return settings_.template get<2>(); }
const key_equal& eq_ref() const { return settings_.template get<2>(); }
allocator_type& alloc_ref() { return settings_.template get<3>(); }
HashtablezInfoHandle& infoz() { return settings_.template get<1>(); }
hasher& hash_ref() { return settings_.template get<2>(); }
const hasher& hash_ref() const { return settings_.template get<2>(); }
key_equal& eq_ref() { return settings_.template get<3>(); }
const key_equal& eq_ref() const { return settings_.template get<3>(); }
allocator_type& alloc_ref() { return settings_.template get<4>(); }
const allocator_type& alloc_ref() const {
return settings_.template get<3>();
return settings_.template get<4>();
}
// TODO(alkis): Investigate removing some of these fields:
@ -1819,10 +1818,11 @@ class raw_hash_set {
slot_type* slots_ = nullptr; // [capacity * slot_type]
size_t size_ = 0; // number of full slots
size_t capacity_ = 0; // total number of slots
HashtablezInfoHandle infoz_;
absl::container_internal::CompressedTuple<size_t /* growth_left */, hasher,
absl::container_internal::CompressedTuple<size_t /* growth_left */,
HashtablezInfoHandle, hasher,
key_equal, allocator_type>
settings_{0, hasher{}, key_equal{}, allocator_type{}};
settings_{0, HashtablezInfoHandle{}, hasher{}, key_equal{},
allocator_type{}};
};
// Erases all elements that satisfy the predicate `pred` from the container `c`.
@ -1846,7 +1846,7 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
const typename Set::key_type& key) {
size_t num_probes = 0;
size_t hash = set.hash_ref()(key);
auto seq = set.probe(hash);
auto seq = probe(set.ctrl_, hash, set.capacity_);
while (true) {
container_internal::Group g{set.ctrl_ + seq.offset()};
for (int i : g.Match(container_internal::H2(hash))) {

@ -466,6 +466,9 @@ class PAlloc {
size_t id_ = std::numeric_limits<size_t>::max();
};
// This doesn't compile with GCC 5.4 and 5.5 due to a bug in noexcept handing.
#if !defined(__GNUC__) || __GNUC__ != 5 || (__GNUC_MINOR__ != 4 && \
__GNUC_MINOR__ != 5)
TEST(NoPropagateOn, Swap) {
using PA = PAlloc<char>;
using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
@ -475,6 +478,7 @@ TEST(NoPropagateOn, Swap) {
EXPECT_EQ(t1.get_allocator(), PA(1));
EXPECT_EQ(t2.get_allocator(), PA(2));
}
#endif
TEST(NoPropagateOn, CopyConstruct) {
using PA = PAlloc<char>;

@ -14,6 +14,7 @@
#include "absl/container/internal/raw_hash_set.h"
#include <atomic>
#include <cmath>
#include <cstdint>
#include <deque>
@ -22,6 +23,8 @@
#include <numeric>
#include <random>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@ -48,11 +51,10 @@ struct RawHashSetTestOnlyAccess {
namespace {
using ::testing::DoubleNear;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::Ge;
using ::testing::Lt;
using ::testing::Optional;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
@ -75,8 +77,14 @@ TEST(Util, GrowthAndCapacity) {
for (size_t growth = 0; growth < 10000; ++growth) {
SCOPED_TRACE(growth);
size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth));
// The capacity is large enough for `growth`
// The capacity is large enough for `growth`.
EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth));
// For (capacity+1) < kWidth, growth should equal capacity.
if (capacity + 1 < Group::kWidth) {
EXPECT_THAT(CapacityToGrowth(capacity), Eq(capacity));
} else {
EXPECT_THAT(CapacityToGrowth(capacity), Lt(capacity));
}
if (growth != 0 && capacity > 1) {
// There is no smaller capacity that works.
EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth));
@ -250,25 +258,43 @@ TEST(Group, CountLeadingEmptyOrDeleted) {
}
}
struct IntPolicy {
using slot_type = int64_t;
using key_type = int64_t;
using init_type = int64_t;
template <class T>
struct ValuePolicy {
using slot_type = T;
using key_type = T;
using init_type = T;
static void construct(void*, int64_t* slot, int64_t v) { *slot = v; }
static void destroy(void*, int64_t*) {}
static void transfer(void*, int64_t* new_slot, int64_t* old_slot) {
*new_slot = *old_slot;
template <class Allocator, class... Args>
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
absl::allocator_traits<Allocator>::construct(*alloc, slot,
std::forward<Args>(args)...);
}
static int64_t& element(slot_type* slot) { return *slot; }
template <class Allocator>
static void destroy(Allocator* alloc, slot_type* slot) {
absl::allocator_traits<Allocator>::destroy(*alloc, slot);
}
template <class F>
static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) {
return std::forward<F>(f)(x, x);
template <class Allocator>
static void transfer(Allocator* alloc, slot_type* new_slot,
slot_type* old_slot) {
construct(alloc, new_slot, std::move(*old_slot));
destroy(alloc, old_slot);
}
static T& element(slot_type* slot) { return *slot; }
template <class F, class... Args>
static decltype(absl::container_internal::DecomposeValue(
std::declval<F>(), std::declval<Args>()...))
apply(F&& f, Args&&... args) {
return absl::container_internal::DecomposeValue(
std::forward<F>(f), std::forward<Args>(args)...);
}
};
using IntPolicy = ValuePolicy<int64_t>;
class StringPolicy {
template <class F, class K, class V,
class = typename std::enable_if<
@ -393,6 +419,13 @@ TEST(Table, EmptyFunctorOptimization) {
size_t growth_left;
void* infoz;
};
struct MockTableInfozDisabled {
void* ctrl;
void* slots;
size_t size;
size_t capacity;
size_t growth_left;
};
struct StatelessHash {
size_t operator()(absl::string_view) const { return 0; }
};
@ -400,17 +433,27 @@ TEST(Table, EmptyFunctorOptimization) {
size_t dummy;
};
EXPECT_EQ(
sizeof(MockTable),
sizeof(
raw_hash_set<StringPolicy, StatelessHash,
std::equal_to<absl::string_view>, std::allocator<int>>));
if (std::is_empty<HashtablezInfoHandle>::value) {
EXPECT_EQ(sizeof(MockTableInfozDisabled),
sizeof(raw_hash_set<StringPolicy, StatelessHash,
std::equal_to<absl::string_view>,
std::allocator<int>>));
EXPECT_EQ(
sizeof(MockTable) + sizeof(StatefulHash),
sizeof(
raw_hash_set<StringPolicy, StatefulHash,
std::equal_to<absl::string_view>, std::allocator<int>>));
EXPECT_EQ(sizeof(MockTableInfozDisabled) + sizeof(StatefulHash),
sizeof(raw_hash_set<StringPolicy, StatefulHash,
std::equal_to<absl::string_view>,
std::allocator<int>>));
} else {
EXPECT_EQ(sizeof(MockTable),
sizeof(raw_hash_set<StringPolicy, StatelessHash,
std::equal_to<absl::string_view>,
std::allocator<int>>));
EXPECT_EQ(sizeof(MockTable) + sizeof(StatefulHash),
sizeof(raw_hash_set<StringPolicy, StatefulHash,
std::equal_to<absl::string_view>,
std::allocator<int>>));
}
}
TEST(Table, Empty) {
@ -847,7 +890,8 @@ TEST(Table, EraseMaintainsValidIterator) {
std::vector<int64_t> CollectBadMergeKeys(size_t N) {
static constexpr int kGroupSize = Group::kWidth - 1;
auto topk_range = [](size_t b, size_t e, IntTable* t) -> std::vector<int64_t> {
auto topk_range = [](size_t b, size_t e,
IntTable* t) -> std::vector<int64_t> {
for (size_t i = b; i != e; ++i) {
t->emplace(i);
}
@ -1001,8 +1045,8 @@ using ProbeStatsPerSize = std::map<size_t, ProbeStats>;
// 1. Create new table and reserve it to keys.size() * 2
// 2. Insert all keys xored with seed
// 3. Collect ProbeStats from final table.
ProbeStats CollectProbeStatsOnKeysXoredWithSeed(const std::vector<int64_t>& keys,
size_t num_iters) {
ProbeStats CollectProbeStatsOnKeysXoredWithSeed(
const std::vector<int64_t>& keys, size_t num_iters) {
const size_t reserve_size = keys.size() * 2;
ProbeStats stats;
@ -1656,6 +1700,38 @@ TEST(Table, Merge) {
EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0")));
}
TEST(Table, IteratorEmplaceConstructibleRequirement) {
struct Value {
explicit Value(absl::string_view view) : value(view) {}
std::string value;
bool operator==(const Value& other) const { return value == other.value; }
};
struct H {
size_t operator()(const Value& v) const {
return absl::Hash<std::string>{}(v.value);
}
};
struct Table : raw_hash_set<ValuePolicy<Value>, H, std::equal_to<Value>,
std::allocator<Value>> {
using Base = typename Table::raw_hash_set;
using Base::Base;
};
std::string input[3]{"A", "B", "C"};
Table t(std::begin(input), std::end(input));
EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"}));
input[0] = "D";
input[1] = "E";
input[2] = "F";
t.insert(std::begin(input), std::end(input));
EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"},
Value{"D"}, Value{"E"}, Value{"F"}));
}
TEST(Nodes, EmptyNodeType) {
using node_type = StringTable::node_type;
node_type n;
@ -1710,6 +1786,26 @@ TEST(Nodes, ExtractInsert) {
EXPECT_FALSE(node);
}
TEST(Nodes, HintInsert) {
IntTable t = {1, 2, 3};
auto node = t.extract(1);
EXPECT_THAT(t, UnorderedElementsAre(2, 3));
auto it = t.insert(t.begin(), std::move(node));
EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
EXPECT_EQ(*it, 1);
EXPECT_FALSE(node);
node = t.extract(2);
EXPECT_THAT(t, UnorderedElementsAre(1, 3));
// reinsert 2 to make the next insert fail.
t.insert(2);
EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
it = t.insert(t.begin(), std::move(node));
EXPECT_EQ(*it, 2);
// The node was not emptied by the insert call.
EXPECT_TRUE(node);
}
IntTable MakeSimpleTable(size_t size) {
IntTable t;
while (t.size() < size) t.insert(t.size());
@ -1804,18 +1900,34 @@ TEST(RawHashSamplerTest, Sample) {
auto& sampler = HashtablezSampler::Global();
size_t start_size = 0;
start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
std::unordered_set<const HashtablezInfo*> preexisting_info;
start_size += sampler.Iterate([&](const HashtablezInfo& info) {
preexisting_info.insert(&info);
++start_size;
});
std::vector<IntTable> tables;
for (int i = 0; i < 1000000; ++i) {
tables.emplace_back();
tables.back().insert(1);
tables.back().insert(i % 5);
}
size_t end_size = 0;
end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
std::unordered_map<size_t, int> observed_checksums;
end_size += sampler.Iterate([&](const HashtablezInfo& info) {
if (preexisting_info.count(&info) == 0) {
observed_checksums[info.hashes_bitwise_xor.load(
std::memory_order_relaxed)]++;
}
++end_size;
});
EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
0.01, 0.005);
EXPECT_EQ(observed_checksums.size(), 5);
for (const auto& [_, count] : observed_checksums) {
EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.2, 0.05);
}
}
#endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE

@ -16,6 +16,7 @@
#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
#include <algorithm>
#include <unordered_map>
#include <vector>
#include "gmock/gmock.h"

@ -18,7 +18,7 @@
//
// An `absl::node_hash_set<T>` is an unordered associative container designed to
// be a more efficient replacement for `std::unordered_set`. Like
// `unordered_set`, search, insertion, and deletion of map elements can be done
// `unordered_set`, search, insertion, and deletion of set elements can be done
// as an `O(1)` operation. However, `node_hash_set` (and other unordered
// associative containers known as the collection of Abseil "Swiss tables")
// contain other optimizations that result in both memory and computation
@ -60,7 +60,7 @@ struct NodeHashSetPolicy;
// following notable differences:
//
// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
// `insert()`, provided that the map is provided a compatible heterogeneous
// `insert()`, provided that the set is provided a compatible heterogeneous
// hashing function and equality operator.
// * Contains a `capacity()` member function indicating the number of element
// slots (open, deleted, and empty) within the hash set.
@ -76,13 +76,13 @@ struct NodeHashSetPolicy;
// Example:
//
// // Create a node hash set of three strings
// absl::node_hash_map<std::string, std::string> ducks =
// absl::node_hash_set<std::string> ducks =
// {"huey", "dewey", "louie"};
//
// // Insert a new element into the node hash map
// ducks.insert("donald"};
// // Insert a new element into the node hash set
// ducks.insert("donald");
//
// // Force a rehash of the node hash map
// // Force a rehash of the node hash set
// ducks.rehash(0);
//
// // See if "dewey" is present
@ -100,7 +100,7 @@ class node_hash_set
public:
// Constructors and Assignment Operators
//
// A node_hash_set supports the same overload set as `std::unordered_map`
// A node_hash_set supports the same overload set as `std::unordered_set`
// for construction and assignment:
//
// * Default constructor
@ -167,7 +167,7 @@ class node_hash_set
// available within the `node_hash_set`.
//
// NOTE: this member function is particular to `absl::node_hash_set` and is
// not provided in the `std::unordered_map` API.
// not provided in the `std::unordered_set` API.
using Base::capacity;
// node_hash_set::empty()
@ -208,7 +208,7 @@ class node_hash_set
// `void`.
//
// NOTE: this return behavior is different than that of STL containers in
// general and `std::unordered_map` in particular.
// general and `std::unordered_set` in particular.
//
// iterator erase(const_iterator first, const_iterator last):
//
@ -314,7 +314,7 @@ class node_hash_set
// node_hash_set::merge()
//
// Extracts elements from a given `source` flat hash map into this
// Extracts elements from a given `source` node hash set into this
// `node_hash_set`. If the destination `node_hash_set` already contains an
// element with an equivalent key, that element is not extracted.
using Base::merge;
@ -322,15 +322,15 @@ class node_hash_set
// node_hash_set::swap(node_hash_set& other)
//
// Exchanges the contents of this `node_hash_set` with those of the `other`
// flat hash map, avoiding invocation of any move, copy, or swap operations on
// node hash set, avoiding invocation of any move, copy, or swap operations on
// individual elements.
//
// All iterators and references on the `node_hash_set` remain valid, excepting
// for the past-the-end iterator, which is invalidated.
//
// `swap()` requires that the flat hash set's hashing and key equivalence
// `swap()` requires that the node hash set's hashing and key equivalence
// functions be Swappable, and are exchaged using unqualified calls to
// non-member `swap()`. If the map's allocator has
// non-member `swap()`. If the set's allocator has
// `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
// set to `true`, the allocators are also exchanged using an unqualified call
// to non-member `swap()`; otherwise, the allocators are not swapped.
@ -385,14 +385,14 @@ class node_hash_set
// node_hash_set::bucket_count()
//
// Returns the number of "buckets" within the `node_hash_set`. Note that
// because a flat hash map contains all elements within its internal storage,
// because a node hash set contains all elements within its internal storage,
// this value simply equals the current capacity of the `node_hash_set`.
using Base::bucket_count;
// node_hash_set::load_factor()
//
// Returns the current load factor of the `node_hash_set` (the average number
// of slots occupied with a value within the hash map).
// of slots occupied with a value within the hash set).
using Base::load_factor;
// node_hash_set::max_load_factor()

@ -12,16 +12,16 @@ else()
set(ABSL_BUILD_DLL FALSE)
endif()
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86_64|amd64|AMD64")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
if (MSVC)
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}")
else()
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}")
endif()
elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*|aarch64")
if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
if (CMAKE_SIZEOF_VOID_P STREQUAL "8")
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM64_FLAGS}")
elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
elseif(CMAKE_SIZEOF_VOID_P STREQUAL "4")
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM32_FLAGS}")
else()
message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.")
@ -32,20 +32,19 @@ else()
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}")
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# MATCHES so we get both Clang and AppleClang
if(MSVC)
# clang-cl is half MSVC, half LLVM
set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_FLAGS};${ABSL_CLANG_CL_TEST_FLAGS}")
set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
else()
set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}")
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# AppleClang doesn't have lsan
# https://developer.apple.com/documentation/code_diagnostics
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
@ -54,7 +53,7 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
endif()
endif()
endif()
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_MSVC_FLAGS};${ABSL_MSVC_TEST_FLAGS}")
set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")

@ -5,47 +5,6 @@
list(APPEND ABSL_CLANG_CL_FLAGS
"/W3"
"-Wno-c++98-compat-pedantic"
"-Wno-conversion"
"-Wno-covered-switch-default"
"-Wno-deprecated"
"-Wno-disabled-macro-expansion"
"-Wno-double-promotion"
"-Wno-comma"
"-Wno-extra-semi"
"-Wno-extra-semi-stmt"
"-Wno-packed"
"-Wno-padded"
"-Wno-sign-compare"
"-Wno-float-conversion"
"-Wno-float-equal"
"-Wno-format-nonliteral"
"-Wno-gcc-compat"
"-Wno-global-constructors"
"-Wno-exit-time-destructors"
"-Wno-non-modular-include-in-module"
"-Wno-old-style-cast"
"-Wno-range-loop-analysis"
"-Wno-reserved-id-macro"
"-Wno-shorten-64-to-32"
"-Wno-switch-enum"
"-Wno-thread-safety-negative"
"-Wno-unknown-warning-option"
"-Wno-unreachable-code"
"-Wno-unused-macros"
"-Wno-weak-vtables"
"-Wno-zero-as-null-pointer-constant"
"-Wbitfield-enum-conversion"
"-Wbool-conversion"
"-Wconstant-conversion"
"-Wenum-conversion"
"-Wint-conversion"
"-Wliteral-conversion"
"-Wnon-literal-null-conversion"
"-Wnull-conversion"
"-Wobjc-literal-conversion"
"-Wno-sign-conversion"
"-Wstring-conversion"
"/DNOMINMAX"
"/DWIN32_LEAN_AND_MEAN"
"/D_CRT_SECURE_NO_WARNINGS"
@ -78,6 +37,7 @@ list(APPEND ABSL_GCC_FLAGS
"-Wextra"
"-Wcast-qual"
"-Wconversion-null"
"-Wformat-security"
"-Wmissing-declarations"
"-Woverlength-strings"
"-Wpointer-arith"
@ -87,8 +47,6 @@ list(APPEND ABSL_GCC_FLAGS
"-Wvarargs"
"-Wvla"
"-Wwrite-strings"
"-Wno-missing-field-initializers"
"-Wno-sign-compare"
"-DNOMINMAX"
)
@ -105,48 +63,36 @@ list(APPEND ABSL_GCC_TEST_FLAGS
list(APPEND ABSL_LLVM_FLAGS
"-Wall"
"-Wextra"
"-Weverything"
"-Wno-c++98-compat-pedantic"
"-Wno-conversion"
"-Wno-covered-switch-default"
"-Wno-deprecated"
"-Wno-disabled-macro-expansion"
"-Wno-double-promotion"
"-Wno-comma"
"-Wno-extra-semi"
"-Wno-extra-semi-stmt"
"-Wno-packed"
"-Wno-padded"
"-Wno-sign-compare"
"-Wcast-qual"
"-Wconversion"
"-Wfloat-overflow-conversion"
"-Wfloat-zero-conversion"
"-Wfor-loop-analysis"
"-Wformat-security"
"-Wgnu-redeclared-enum"
"-Winfinite-recursion"
"-Wliteral-conversion"
"-Wmissing-declarations"
"-Woverlength-strings"
"-Wpointer-arith"
"-Wself-assign"
"-Wshadow"
"-Wstring-conversion"
"-Wtautological-overlap-compare"
"-Wundef"
"-Wuninitialized"
"-Wunreachable-code"
"-Wunused-comparison"
"-Wunused-local-typedefs"
"-Wunused-result"
"-Wvla"
"-Wwrite-strings"
"-Wno-float-conversion"
"-Wno-float-equal"
"-Wno-format-nonliteral"
"-Wno-gcc-compat"
"-Wno-global-constructors"
"-Wno-exit-time-destructors"
"-Wno-non-modular-include-in-module"
"-Wno-old-style-cast"
"-Wno-range-loop-analysis"
"-Wno-reserved-id-macro"
"-Wno-implicit-float-conversion"
"-Wno-implicit-int-float-conversion"
"-Wno-implicit-int-conversion"
"-Wno-shorten-64-to-32"
"-Wno-switch-enum"
"-Wno-thread-safety-negative"
"-Wno-unknown-warning-option"
"-Wno-unreachable-code"
"-Wno-unused-macros"
"-Wno-weak-vtables"
"-Wno-zero-as-null-pointer-constant"
"-Wbitfield-enum-conversion"
"-Wbool-conversion"
"-Wconstant-conversion"
"-Wenum-conversion"
"-Wint-conversion"
"-Wliteral-conversion"
"-Wnon-literal-null-conversion"
"-Wnull-conversion"
"-Wobjc-literal-conversion"
"-Wno-sign-conversion"
"-Wstring-conversion"
"-DNOMINMAX"
)

@ -6,47 +6,6 @@
ABSL_CLANG_CL_FLAGS = [
"/W3",
"-Wno-c++98-compat-pedantic",
"-Wno-conversion",
"-Wno-covered-switch-default",
"-Wno-deprecated",
"-Wno-disabled-macro-expansion",
"-Wno-double-promotion",
"-Wno-comma",
"-Wno-extra-semi",
"-Wno-extra-semi-stmt",
"-Wno-packed",
"-Wno-padded",
"-Wno-sign-compare",
"-Wno-float-conversion",
"-Wno-float-equal",
"-Wno-format-nonliteral",
"-Wno-gcc-compat",
"-Wno-global-constructors",
"-Wno-exit-time-destructors",
"-Wno-non-modular-include-in-module",
"-Wno-old-style-cast",
"-Wno-range-loop-analysis",
"-Wno-reserved-id-macro",
"-Wno-shorten-64-to-32",
"-Wno-switch-enum",
"-Wno-thread-safety-negative",
"-Wno-unknown-warning-option",
"-Wno-unreachable-code",
"-Wno-unused-macros",
"-Wno-weak-vtables",
"-Wno-zero-as-null-pointer-constant",
"-Wbitfield-enum-conversion",
"-Wbool-conversion",
"-Wconstant-conversion",
"-Wenum-conversion",
"-Wint-conversion",
"-Wliteral-conversion",
"-Wnon-literal-null-conversion",
"-Wnull-conversion",
"-Wobjc-literal-conversion",
"-Wno-sign-conversion",
"-Wstring-conversion",
"/DNOMINMAX",
"/DWIN32_LEAN_AND_MEAN",
"/D_CRT_SECURE_NO_WARNINGS",
@ -79,6 +38,7 @@ ABSL_GCC_FLAGS = [
"-Wextra",
"-Wcast-qual",
"-Wconversion-null",
"-Wformat-security",
"-Wmissing-declarations",
"-Woverlength-strings",
"-Wpointer-arith",
@ -88,8 +48,6 @@ ABSL_GCC_FLAGS = [
"-Wvarargs",
"-Wvla",
"-Wwrite-strings",
"-Wno-missing-field-initializers",
"-Wno-sign-compare",
"-DNOMINMAX",
]
@ -106,48 +64,36 @@ ABSL_GCC_TEST_FLAGS = [
ABSL_LLVM_FLAGS = [
"-Wall",
"-Wextra",
"-Weverything",
"-Wno-c++98-compat-pedantic",
"-Wno-conversion",
"-Wno-covered-switch-default",
"-Wno-deprecated",
"-Wno-disabled-macro-expansion",
"-Wno-double-promotion",
"-Wno-comma",
"-Wno-extra-semi",
"-Wno-extra-semi-stmt",
"-Wno-packed",
"-Wno-padded",
"-Wno-sign-compare",
"-Wcast-qual",
"-Wconversion",
"-Wfloat-overflow-conversion",
"-Wfloat-zero-conversion",
"-Wfor-loop-analysis",
"-Wformat-security",
"-Wgnu-redeclared-enum",
"-Winfinite-recursion",
"-Wliteral-conversion",
"-Wmissing-declarations",
"-Woverlength-strings",
"-Wpointer-arith",
"-Wself-assign",
"-Wshadow",
"-Wstring-conversion",
"-Wtautological-overlap-compare",
"-Wundef",
"-Wuninitialized",
"-Wunreachable-code",
"-Wunused-comparison",
"-Wunused-local-typedefs",
"-Wunused-result",
"-Wvla",
"-Wwrite-strings",
"-Wno-float-conversion",
"-Wno-float-equal",
"-Wno-format-nonliteral",
"-Wno-gcc-compat",
"-Wno-global-constructors",
"-Wno-exit-time-destructors",
"-Wno-non-modular-include-in-module",
"-Wno-old-style-cast",
"-Wno-range-loop-analysis",
"-Wno-reserved-id-macro",
"-Wno-implicit-float-conversion",
"-Wno-implicit-int-float-conversion",
"-Wno-implicit-int-conversion",
"-Wno-shorten-64-to-32",
"-Wno-switch-enum",
"-Wno-thread-safety-negative",
"-Wno-unknown-warning-option",
"-Wno-unreachable-code",
"-Wno-unused-macros",
"-Wno-weak-vtables",
"-Wno-zero-as-null-pointer-constant",
"-Wbitfield-enum-conversion",
"-Wbool-conversion",
"-Wconstant-conversion",
"-Wenum-conversion",
"-Wint-conversion",
"-Wliteral-conversion",
"-Wnon-literal-null-conversion",
"-Wnull-conversion",
"-Wobjc-literal-conversion",
"-Wno-sign-conversion",
"-Wstring-conversion",
"-DNOMINMAX",
]

@ -22,21 +22,21 @@ load(
)
ABSL_DEFAULT_COPTS = select({
"//absl:windows": ABSL_MSVC_FLAGS,
"//absl:llvm_compiler": ABSL_LLVM_FLAGS,
"//absl:msvc_compiler": ABSL_MSVC_FLAGS,
"//absl:clang-cl_compiler": ABSL_CLANG_CL_FLAGS,
"//absl:clang_compiler": ABSL_LLVM_FLAGS,
"//conditions:default": ABSL_GCC_FLAGS,
})
# in absence of modules (--compiler=gcc or -c opt), cc_tests leak their copts
# to their (included header) dependencies and fail to build outside absl
ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({
"//absl:windows": ABSL_MSVC_TEST_FLAGS,
"//absl:llvm_compiler": ABSL_LLVM_TEST_FLAGS,
"//absl:msvc_compiler": ABSL_MSVC_TEST_FLAGS,
"//absl:clang-cl_compiler": ABSL_CLANG_CL_TEST_FLAGS,
"//absl:clang_compiler": ABSL_LLVM_TEST_FLAGS,
"//conditions:default": ABSL_GCC_TEST_FLAGS,
})
ABSL_DEFAULT_LINKOPTS = select({
"//absl:windows": ABSL_MSVC_LINKOPTS,
"//absl:msvc_compiler": ABSL_MSVC_LINKOPTS,
"//conditions:default": [],
})

@ -16,77 +16,6 @@ MSVC_BIG_WARNING_FLAGS = [
"/W3",
]
LLVM_BIG_WARNING_FLAGS = [
"-Wall",
"-Wextra",
"-Weverything",
]
# Docs on single flags is preceded by a comment.
# Docs on groups of flags is preceded by ###.
LLVM_DISABLE_WARNINGS_FLAGS = [
# Abseil does not support C++98
"-Wno-c++98-compat-pedantic",
# Turns off all implicit conversion warnings. Most are re-enabled below.
"-Wno-conversion",
"-Wno-covered-switch-default",
"-Wno-deprecated",
"-Wno-disabled-macro-expansion",
"-Wno-double-promotion",
###
# Turned off as they include valid C++ code.
"-Wno-comma",
"-Wno-extra-semi",
"-Wno-extra-semi-stmt",
"-Wno-packed",
"-Wno-padded",
###
# Google style does not use unsigned integers, though STL containers
# have unsigned types.
"-Wno-sign-compare",
###
"-Wno-float-conversion",
"-Wno-float-equal",
"-Wno-format-nonliteral",
# Too aggressive: warns on Clang extensions enclosed in Clang-only
# compilation paths.
"-Wno-gcc-compat",
###
# Some internal globals are necessary. Don't do this at home.
"-Wno-global-constructors",
"-Wno-exit-time-destructors",
###
"-Wno-non-modular-include-in-module",
"-Wno-old-style-cast",
# Warns on preferred usage of non-POD types such as string_view
"-Wno-range-loop-analysis",
"-Wno-reserved-id-macro",
"-Wno-shorten-64-to-32",
"-Wno-switch-enum",
"-Wno-thread-safety-negative",
"-Wno-unknown-warning-option",
"-Wno-unreachable-code",
# Causes warnings on include guards
"-Wno-unused-macros",
"-Wno-weak-vtables",
# Causes warnings on usage of types/compare.h comparison operators.
"-Wno-zero-as-null-pointer-constant",
###
# Implicit conversion warnings turned off by -Wno-conversion
# which are re-enabled below.
"-Wbitfield-enum-conversion",
"-Wbool-conversion",
"-Wconstant-conversion",
"-Wenum-conversion",
"-Wint-conversion",
"-Wliteral-conversion",
"-Wnon-literal-null-conversion",
"-Wnull-conversion",
"-Wobjc-literal-conversion",
"-Wno-sign-conversion",
"-Wstring-conversion",
]
LLVM_TEST_DISABLE_WARNINGS_FLAGS = [
"-Wno-c99-extensions",
"-Wno-deprecated-declarations",
@ -125,6 +54,7 @@ COPT_VARS = {
"-Wextra",
"-Wcast-qual",
"-Wconversion-null",
"-Wformat-security",
"-Wmissing-declarations",
"-Woverlength-strings",
"-Wpointer-arith",
@ -134,13 +64,6 @@ COPT_VARS = {
"-Wvarargs",
"-Wvla", # variable-length array
"-Wwrite-strings",
# gcc-4.x has spurious missing field initializer warnings.
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750
# Remove when gcc-4.x is no longer supported.
"-Wno-missing-field-initializers",
# Google style does not use unsigned integers, though STL containers
# have unsigned types.
"-Wno-sign-compare",
# Don't define min and max macros (Build on Windows using gcc)
"-DNOMINMAX",
],
@ -153,15 +76,48 @@ COPT_VARS = {
"-Wno-unused-parameter",
"-Wno-unused-private-field",
],
"ABSL_LLVM_FLAGS":
LLVM_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS + [
# Don't define min and max macros (Build on Windows using clang)
"-DNOMINMAX",
],
"ABSL_LLVM_FLAGS": [
"-Wall",
"-Wextra",
"-Wcast-qual",
"-Wconversion",
"-Wfloat-overflow-conversion",
"-Wfloat-zero-conversion",
"-Wfor-loop-analysis",
"-Wformat-security",
"-Wgnu-redeclared-enum",
"-Winfinite-recursion",
"-Wliteral-conversion",
"-Wmissing-declarations",
"-Woverlength-strings",
"-Wpointer-arith",
"-Wself-assign",
"-Wshadow",
"-Wstring-conversion",
"-Wtautological-overlap-compare",
"-Wundef",
"-Wuninitialized",
"-Wunreachable-code",
"-Wunused-comparison",
"-Wunused-local-typedefs",
"-Wunused-result",
"-Wvla",
"-Wwrite-strings",
# Warnings that are enabled by group warning flags like -Wall that we
# explicitly disable.
"-Wno-float-conversion",
"-Wno-implicit-float-conversion",
"-Wno-implicit-int-float-conversion",
"-Wno-implicit-int-conversion",
"-Wno-shorten-64-to-32",
"-Wno-sign-conversion",
# Don't define min and max macros (Build on Windows using clang)
"-DNOMINMAX",
],
"ABSL_LLVM_TEST_FLAGS":
LLVM_TEST_DISABLE_WARNINGS_FLAGS,
"ABSL_CLANG_CL_FLAGS":
(MSVC_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS + MSVC_DEFINES),
(MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES),
"ABSL_CLANG_CL_TEST_FLAGS":
LLVM_TEST_DISABLE_WARNINGS_FLAGS,
"ABSL_MSVC_FLAGS":

@ -66,7 +66,8 @@ cc_library(
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS + select({
"//absl:windows": ["-DEFAULTLIB:dbghelp.lib"],
"//absl:msvc_compiler": ["-DEFAULTLIB:dbghelp.lib"],
"//absl:clang-cl_compiler": ["-DEFAULTLIB:dbghelp.lib"],
"//conditions:default": [],
}),
deps = [
@ -86,11 +87,13 @@ cc_test(
name = "symbolize_test",
srcs = ["symbolize_test.cc"],
copts = ABSL_TEST_COPTS + select({
"//absl:windows": ["/Z7"],
"//absl:msvc_compiler": ["/Z7"],
"//absl:clang-cl_compiler": ["/Z7"],
"//conditions:default": [],
}),
linkopts = ABSL_DEFAULT_LINKOPTS + select({
"//absl:windows": ["/DEBUG"],
"//absl:msvc_compiler": ["/DEBUG"],
"//absl:clang-cl_compiler": ["/DEBUG"],
"//conditions:default": [],
}),
deps = [
@ -148,7 +151,8 @@ cc_test(
srcs = ["failure_signal_handler_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = select({
"//absl:windows": [],
"//absl:msvc_compiler": [],
"//absl:clang-cl_compiler": [],
"//absl:wasm": [],
"//conditions:default": ["-pthread"],
}) + ABSL_DEFAULT_LINKOPTS,
@ -239,7 +243,7 @@ cc_library(
# These targets exists for use in tests only, explicitly configuring the
# LEAK_SANITIZER macro. It must be linked with -fsanitize=leak for lsan.
ABSL_LSAN_LINKOPTS = select({
"//absl:llvm_compiler": ["-fsanitize=leak"],
"//absl:clang_compiler": ["-fsanitize=leak"],
"//conditions:default": [],
})
@ -249,13 +253,14 @@ cc_library(
srcs = ["leak_check.cc"],
hdrs = ["leak_check.h"],
copts = select({
"//absl:llvm_compiler": ["-DLEAK_SANITIZER"],
"//absl:clang_compiler": ["-DLEAK_SANITIZER"],
"//conditions:default": [],
}),
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
"//absl/base:config",
"//absl/base:core_headers",
],
)
@ -269,6 +274,7 @@ cc_library(
visibility = ["//visibility:private"],
deps = [
"//absl/base:config",
"//absl/base:core_headers",
],
)
@ -276,7 +282,7 @@ cc_test(
name = "leak_check_test",
srcs = ["leak_check_test.cc"],
copts = select({
"//absl:llvm_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"],
"//absl:clang_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"],
"//conditions:default": [],
}),
linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS,

@ -21,6 +21,7 @@
#ifdef _WIN32
#include <windows.h>
#else
#include <sched.h>
#include <unistd.h>
#endif
@ -219,17 +220,24 @@ static void WriteToStderr(const char* data) {
absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data));
}
static void WriteSignalMessage(int signo, void (*writerfn)(const char*)) {
char buf[64];
static void WriteSignalMessage(int signo, int cpu,
void (*writerfn)(const char*)) {
char buf[96];
char on_cpu[32] = {0};
if (cpu != -1) {
snprintf(on_cpu, sizeof(on_cpu), " on cpu %d", cpu);
}
const char* const signal_string =
debugging_internal::FailureSignalToString(signo);
if (signal_string != nullptr && signal_string[0] != '\0') {
snprintf(buf, sizeof(buf), "*** %s received at time=%ld ***\n",
snprintf(buf, sizeof(buf), "*** %s received at time=%ld%s ***\n",
signal_string,
static_cast<long>(time(nullptr))); // NOLINT(runtime/int)
static_cast<long>(time(nullptr)), // NOLINT(runtime/int)
on_cpu);
} else {
snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld ***\n",
signo, static_cast<long>(time(nullptr))); // NOLINT(runtime/int)
snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld%s ***\n",
signo, static_cast<long>(time(nullptr)), // NOLINT(runtime/int)
on_cpu);
}
writerfn(buf);
}
@ -269,10 +277,10 @@ ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace(
// Called by AbslFailureSignalHandler() to write the failure info. It is
// called once with writerfn set to WriteToStderr() and then possibly
// with writerfn set to the user provided function.
static void WriteFailureInfo(int signo, void* ucontext,
static void WriteFailureInfo(int signo, void* ucontext, int cpu,
void (*writerfn)(const char*)) {
WriterFnStruct writerfn_struct{writerfn};
WriteSignalMessage(signo, writerfn);
WriteSignalMessage(signo, cpu, writerfn);
WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper,
&writerfn_struct);
}
@ -334,6 +342,14 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) {
}
}
// Increase the chance that the CPU we report was the same CPU on which the
// signal was received by doing this as early as possible, i.e. after
// verifying that this is not a recursive signal handler invocation.
int my_cpu = -1;
#ifdef ABSL_HAVE_SCHED_GETCPU
my_cpu = sched_getcpu();
#endif
#ifdef ABSL_HAVE_ALARM
// Set an alarm to abort the program in case this code hangs or deadlocks.
if (fsh_options.alarm_on_failure_secs > 0) {
@ -344,12 +360,12 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) {
#endif
// First write to stderr.
WriteFailureInfo(signo, ucontext, WriteToStderr);
WriteFailureInfo(signo, ucontext, my_cpu, WriteToStderr);
// Riskier code (because it is less likely to be async-signal-safe)
// goes after this point.
if (fsh_options.writerfn != nullptr) {
WriteFailureInfo(signo, ucontext, fsh_options.writerfn);
WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn);
}
if (fsh_options.call_previous_handler) {

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

Loading…
Cancel
Save