Uprev to branch-heads/74
To commit
'Merge to M74: Revert "Remove rtc::TimeMillis() call from ALR detector."'
https://webrtc.googlesource.com/src/+/303fc7963064a46a27e8287e531ded8cec21ff77
Existing aec.ini config files should be modified base on the parameters
change as below:
Added:
AEC_DELAY_DELAY_HEADROOM_SAMPLES "delay:delay_headroom_samples"
AEC_DELAY_HYSTERESIS_LIMIT_BLOCKS "delay:hysteresis_limit_blocks"
AEC_EP_STRENGTH_DEFAULT_GAIN "ep_strength:default_gain"
Removed:
AEC_BUFFERING_USE_NEW_RENDER_BUFFERING "buffering:use_new_render_buffering"
AEC_DELAY_API_CALL_JITTER_BLOCKS "delay:api_call_jitter_blocks"
AEC_DELAY_MIN_ECHO_PATH_DELAY_BLOCKS "delay:min_echo_path_delay_blocks"
AEC_DELAY_DELAY_HEADROOM_BLOCKS "delay:delay_headroom_blocks"
AEC_DELAY_HYSTERESIS_LIMIT_1_BLOCKS "delay:hysteresis_limit_1_blocks"
AEC_DELAY_HYSTERESIS_LIMIT_2_BLOCKS "delay:hysteresis_limit_2_blocks"
AEC_DELAY_SKEW_HYSTERESIS_BLOCKS "delay:skew_hysteresis_blocks"
AEC_EP_STRENGTH_LF "ep_strength:lf"
AEC_EP_STRENGTH_MF "ep_strength:mf"
AEC_EP_STRENGTH_HF "ep_strength:hf"
AEC_ECHO_REMOVAL_CTL_INITIAL_GAIN "echo_removal_control:initial_gain"
AEC_ECHO_REMOVAL_CTL_FIRST_NON_ZERO_GAIN "echo_removal_control:first_non_zero_gain"
AEC_ECHO_REMOVAL_CTL_NON_ZERO_GAIN_BLOCKS "echo_removal_control:non_zero_gain_blocks"
AEC_ECHO_REMOVAL_CTL_FULL_GAIN_BLOCKS "echo_removal_control:full_gain_blocks"
BUG=chromium:947914
TEST=emerge-atlas webrtc-apm
CQ-DEPEND=CL:1545342
Change-Id: Ifb701bfc14844c32b8d7561c4b48fd61be3f7157
Reviewed-on: https://chromium-review.googlesource.com/1545814
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Hsinyu Chao <hychao@chromium.org>
Reviewed-by: Hsinyu Chao <hychao@chromium.org>
Reviewed-by: Per Ã…hgren <peah@chromium.org>
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index edd0274..8e3c177 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt
index 1d09b19..bba0f3e 100644
--- a/absl/CMakeLists.txt
+++ b/absl/CMakeLists.txt
@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel
index d04dc71..8d266db 100644
--- a/absl/algorithm/BUILD.bazel
+++ b/absl/algorithm/BUILD.bazel
@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,7 +15,7 @@
#
load(
- "//absl:copts.bzl",
+ "//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
)
diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt
index fdf45c5..c51eb10 100644
--- a/absl/algorithm/CMakeLists.txt
+++ b/absl/algorithm/CMakeLists.txt
@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,50 +14,50 @@
# limitations under the License.
#
-list(APPEND ALGORITHM_PUBLIC_HEADERS
- "algorithm.h"
- "container.h"
-)
-
-
-#
-## TESTS
-#
-
-# test algorithm_test
-list(APPEND ALGORITHM_TEST_SRC
- "algorithm_test.cc"
- ${ALGORITHM_PUBLIC_HEADERS}
- ${ALGORITHM_INTERNAL_HEADERS}
-)
-
-absl_header_library(
- TARGET
- absl_algorithm
- EXPORT_NAME
+absl_cc_library(
+ NAME
algorithm
+ HDRS
+ "algorithm.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
)
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
algorithm_test
- SOURCES
- ${ALGORITHM_TEST_SRC}
- PUBLIC_LIBRARIES
+ SRCS
+ "algorithm_test.cc"
+ DEPS
absl::algorithm
+ gmock_main
)
-
-
-
-# test container_test
-set(CONTAINER_TEST_SRC "container_test.cc")
-
-absl_test(
- TARGET
- container_test
- SOURCES
- ${CONTAINER_TEST_SRC}
- PUBLIC_LIBRARIES
+absl_cc_library(
+ NAME
+ algorithm_container
+ HDRS
+ "container.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
absl::algorithm
+ absl::core_headers
+ absl::meta
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ container_test
+ SRCS
+ "container_test.cc"
+ DEPS
+ absl::algorithm_container
+ absl::base
+ absl::core_headers
+ absl::memory
+ absl::span
+ gmock_main
)
diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h
index 3d65864..bb90215 100644
--- a/absl/algorithm/algorithm.h
+++ b/absl/algorithm/algorithm.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -94,7 +94,7 @@
// then the predicate is never invoked and the function returns false.
//
// This is a C++11-compatible implementation of C++14 `std::equal`. See
-// http://en.cppreference.com/w/cpp/algorithm/equal for more information.
+// https://en.cppreference.com/w/cpp/algorithm/equal for more information.
template <typename InputIter1, typename InputIter2, typename Pred>
bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
InputIter2 last2, Pred&& pred) {
diff --git a/absl/algorithm/algorithm_test.cc b/absl/algorithm/algorithm_test.cc
index e4322bc..81fccb6 100644
--- a/absl/algorithm/algorithm_test.cc
+++ b/absl/algorithm/algorithm_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 53ab156..752e47b 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -36,7 +36,6 @@
// For template parameter and variable naming, `C` indicates the container type
// to which the function is applied, `Pred` indicates the predicate object type
// to be used by the function and `T` indicates the applicable element type.
-//
#ifndef ABSL_ALGORITHM_CONTAINER_H_
#define ABSL_ALGORITHM_CONTAINER_H_
@@ -46,6 +45,8 @@
#include <iterator>
#include <numeric>
#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -54,7 +55,6 @@
#include "absl/meta/type_traits.h"
namespace absl {
-
namespace container_algorithm_internal {
// NOTE: it is important to defer to ADL lookup for building with C++ modules,
@@ -101,6 +101,17 @@
template <typename C>
ContainerIter<C> c_end(C& c) { return end(c); }
+template <typename T>
+struct IsUnorderedContainer : std::false_type {};
+
+template <class Key, class T, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
+
+template <class Key, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>>
+ : std::true_type {};
+
} // namespace container_algorithm_internal
// PUBLIC API
@@ -636,7 +647,6 @@
// and `unique()` are omitted, because it's not clear whether or not such
// functions should call erase on their supplied sequences afterwards. Either
// behavior would be surprising for a different set of users.
-//
// c_remove_copy()
//
@@ -1154,7 +1164,13 @@
// Container-based version of the <algorithm> `std::set_union()` function
// to return an iterator containing the union of two containers; duplicate
// values are not copied into the output.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
return std::set_union(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
@@ -1164,7 +1180,13 @@
// Overload of c_set_union() for performing a merge using a `comp` other than
// `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
Compare&& comp) {
return std::set_union(container_algorithm_internal::c_begin(c1),
@@ -1178,7 +1200,13 @@
//
// Container-based version of the <algorithm> `std::set_intersection()` function
// to return an iterator containing the intersection of two containers.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_intersection(container_algorithm_internal::c_begin(c1),
@@ -1189,7 +1217,13 @@
// Overload of c_set_intersection() for performing a merge using a `comp` other
// than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) {
return std::set_intersection(container_algorithm_internal::c_begin(c1),
@@ -1204,7 +1238,13 @@
// Container-based version of the <algorithm> `std::set_difference()` function
// to return an iterator containing elements present in the first container but
// not in the second.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_difference(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_difference(container_algorithm_internal::c_begin(c1),
@@ -1215,7 +1255,13 @@
// Overload of c_set_difference() for performing a merge using a `comp` other
// than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_difference(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) {
return std::set_difference(container_algorithm_internal::c_begin(c1),
@@ -1230,7 +1276,13 @@
// Container-based version of the <algorithm> `std::set_symmetric_difference()`
// function to return an iterator containing elements present in either one
// container or the other, but not both.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_symmetric_difference(
@@ -1242,7 +1294,13 @@
// Overload of c_set_symmetric_difference() for performing a merge using a
// `comp` other than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output,
Compare&& comp) {
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc
index 1502b17..04282b8 100644
--- a/absl/algorithm/container_test.cc
+++ b/absl/algorithm/container_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/algorithm/equal_benchmark.cc b/absl/algorithm/equal_benchmark.cc
index 19c0780..7bf62c9 100644
--- a/absl/algorithm/equal_benchmark.cc
+++ b/absl/algorithm/equal_benchmark.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index f7d8101..804f62a 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,7 +15,7 @@
#
load(
- "//absl:copts.bzl",
+ "//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
"ABSL_EXCEPTIONS_FLAG",
@@ -67,6 +67,7 @@
name = "core_headers",
hdrs = [
"attributes.h",
+ "const_init.h",
"macros.h",
"optimization.h",
"port.h",
@@ -75,7 +76,6 @@
copts = ABSL_DEFAULT_COPTS,
deps = [
":config",
- ":dynamic_annotations",
],
)
@@ -89,6 +89,10 @@
"internal/low_level_alloc.h",
],
copts = ABSL_DEFAULT_COPTS,
+ linkopts = select({
+ "//absl:windows": [],
+ "//conditions:default": ["-pthread"],
+ }),
visibility = [
"//absl:__subpackages__",
],
@@ -108,6 +112,7 @@
"internal/identity.h",
"internal/inline_variable.h",
"internal/invoke.h",
+ "internal/scheduling_mode.h",
],
copts = ABSL_DEFAULT_COPTS,
visibility = [
@@ -141,6 +146,10 @@
"log_severity.h",
],
copts = ABSL_DEFAULT_COPTS,
+ linkopts = select({
+ "//absl:windows": [],
+ "//conditions:default": ["-pthread"],
+ }),
deps = [
":base_internal",
":config",
@@ -188,7 +197,6 @@
deps = [
":base",
":config",
- ":core_headers",
],
)
@@ -231,13 +239,12 @@
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
- ":base",
":config",
":pretty_function",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/strings",
- "//absl/types:optional",
+ "//absl/utility",
"@com_google_googletest//:gtest",
],
)
@@ -316,6 +323,33 @@
)
cc_library(
+ name = "spinlock_benchmark_common",
+ testonly = 1,
+ srcs = ["internal/spinlock_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = [
+ "//absl/base:__pkg__",
+ ],
+ deps = [
+ ":base",
+ ":base_internal",
+ "//absl/synchronization",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+ alwayslink = 1,
+)
+
+cc_binary(
+ name = "spinlock_benchmark",
+ testonly = 1,
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":spinlock_benchmark_common",
+ ],
+)
+
+cc_library(
name = "endian",
hdrs = [
"internal/endian.h",
@@ -397,10 +431,6 @@
size = "small",
srcs = ["internal/low_level_alloc_test.cc"],
copts = ABSL_TEST_COPTS,
- linkopts = select({
- "//absl:windows": [],
- "//conditions:default": ["-pthread"],
- }),
tags = ["no_test_ios_x86_64"],
deps = [":malloc_internal"],
)
@@ -410,10 +440,6 @@
size = "small",
srcs = ["internal/thread_identity_test.cc"],
copts = ABSL_TEST_COPTS,
- linkopts = select({
- "//absl:windows": [],
- "//conditions:default": ["-pthread"],
- }),
tags = [
"no_test_wasm",
],
@@ -457,3 +483,25 @@
"@com_google_googletest//:gtest_main",
],
)
+
+cc_library(
+ name = "scoped_set_env",
+ testonly = 1,
+ srcs = ["internal/scoped_set_env.cc"],
+ hdrs = ["internal/scoped_set_env.h"],
+ visibility = [
+ "//absl:__subpackages__",
+ ],
+ deps = [":base"],
+)
+
+cc_test(
+ name = "scoped_set_env_test",
+ size = "small",
+ srcs = ["internal/scoped_set_env_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":scoped_set_env",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/base/BUILD.gn b/absl/base/BUILD.gn
index 6c540f3..c7fa3f3 100644
--- a/absl/base/BUILD.gn
+++ b/absl/base/BUILD.gn
@@ -88,6 +88,7 @@
public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
public = [
"attributes.h",
+ "const_init.h",
"macros.h",
"optimization.h",
"port.h",
@@ -95,7 +96,6 @@
]
deps = [
":config",
- ":dynamic_annotations",
]
}
@@ -136,6 +136,7 @@
"internal/identity.h",
"internal/inline_variable.h",
"internal/invoke.h",
+ "internal/scheduling_mode.h",
]
visibility = []
visibility += [ "../*" ]
@@ -196,7 +197,6 @@
deps = [
":base",
":config",
- ":core_headers",
]
visibility = []
visibility += [ "../*" ]
@@ -251,13 +251,12 @@
# "internal/exception_safety_testing.h",
# ]
# deps = [
-# ":base",
# ":config",
# ":pretty_function",
# "../memory",
# "../meta:type_traits",
# "../strings",
-# "../types:optional",
+# "../utility",
# "//testing/gtest",
# ]
# }
@@ -314,3 +313,23 @@
visibility = []
visibility += [ "../*" ]
}
+
+source_set("scoped_set_env") {
+ testonly = true
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [
+ "//build/config/compiler:no_chromium_code",
+ "//third_party/abseil-cpp:absl_default_cflags_cc",
+ ]
+ public = [
+ "internal/scoped_set_env.h",
+ ]
+ sources = [
+ "internal/scoped_set_env.cc",
+ ]
+ deps = [
+ ":base",
+ ]
+ visibility = []
+ visibility += [ "../*" ]
+}
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 04a6eb3..d8a311c 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,374 +14,427 @@
# limitations under the License.
#
-list(APPEND BASE_PUBLIC_HEADERS
- "attributes.h"
- "call_once.h"
- "casts.h"
- "config.h"
- "dynamic_annotations.h"
- "log_severity.h"
- "macros.h"
- "optimization.h"
- "policy_checks.h"
- "port.h"
- "thread_annotations.h"
+absl_cc_library(
+ NAME
+ spinlock_wait
+ HDRS
+ "internal/scheduling_mode.h"
+ "internal/spinlock_wait.h"
+ SRCS
+ "internal/spinlock_akaros.inc"
+ "internal/spinlock_linux.inc"
+ "internal/spinlock_posix.inc"
+ "internal/spinlock_wait.cc"
+ "internal/spinlock_win32.inc"
+ DEPS
+ absl::core_headers
)
-
-list(APPEND BASE_INTERNAL_HEADERS
- "internal/atomic_hook.h"
- "internal/bits.h"
- "internal/cycleclock.h"
- "internal/direct_mmap.h"
- "internal/endian.h"
- "internal/exception_testing.h"
- "internal/exception_safety_testing.h"
- "internal/hide_ptr.h"
- "internal/identity.h"
- "internal/invoke.h"
- "internal/inline_variable.h"
- "internal/low_level_alloc.h"
- "internal/low_level_scheduling.h"
- "internal/per_thread_tls.h"
- "internal/pretty_function.h"
- "internal/raw_logging.h"
- "internal/scheduling_mode.h"
- "internal/spinlock.h"
- "internal/spinlock_wait.h"
- "internal/sysinfo.h"
- "internal/thread_identity.h"
- "internal/throw_delegate.h"
- "internal/tsan_mutex_interface.h"
- "internal/unaligned_access.h"
- "internal/unscaledcycleclock.h"
+absl_cc_library(
+ NAME
+ config
+ HDRS
+ "config.h"
+ "policy_checks.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
)
-
-# absl_base main library
-list(APPEND BASE_SRC
- "internal/cycleclock.cc"
- "internal/raw_logging.cc"
- "internal/spinlock.cc"
- "internal/sysinfo.cc"
- "internal/thread_identity.cc"
- "internal/unscaledcycleclock.cc"
- "internal/low_level_alloc.cc"
- ${BASE_PUBLIC_HEADERS}
- ${BASE_INTERNAL_HEADERS}
+absl_cc_library(
+ NAME
+ dynamic_annotations
+ HDRS
+ "dynamic_annotations.h"
+ SRCS
+ "dynamic_annotations.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEFINES
+ "__CLANG_SUPPORT_DYN_ANNOTATION__"
+ PUBLIC
)
-absl_library(
- TARGET
- absl_base
- SOURCES
- ${BASE_SRC}
- PUBLIC_LIBRARIES
- absl_dynamic_annotations
- absl_spinlock_wait
- EXPORT_NAME
- base
+absl_cc_library(
+ NAME
+ core_headers
+ HDRS
+ "attributes.h"
+ "const_init.h"
+ "macros.h"
+ "optimization.h"
+ "port.h"
+ "thread_annotations.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ PUBLIC
)
-# throw delegate library
-set(THROW_DELEGATE_SRC "internal/throw_delegate.cc")
-
-absl_library(
- TARGET
- absl_throw_delegate
- SOURCES
- ${THROW_DELEGATE_SRC}
- PUBLIC_LIBRARIES
- ${THROW_DELEGATE_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_EXCEPTIONS_FLAG}
- EXPORT_NAME
- throw_delegate
-)
-
-if(BUILD_TESTING)
- # exception-safety testing library
- set(EXCEPTION_SAFETY_TESTING_SRC
- "internal/exception_safety_testing.h"
- "internal/exception_safety_testing.cc"
- )
- set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES
- ${ABSL_TEST_COMMON_LIBRARIES}
+absl_cc_library(
+ NAME
+ malloc_internal
+ HDRS
+ "internal/direct_mmap.h"
+ "internal/low_level_alloc.h"
+ SRCS
+ "internal/low_level_alloc.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
absl::base
+ absl::config
+ absl::core_headers
+ absl::dynamic_annotations
+ absl::spinlock_wait
+)
+
+absl_cc_library(
+ NAME
+ base_internal
+ HDRS
+ "internal/hide_ptr.h"
+ "internal/identity.h"
+ "internal/inline_variable.h"
+ "internal/invoke.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+)
+
+absl_cc_library(
+ NAME
+ base
+ HDRS
+ "call_once.h"
+ "casts.h"
+ "internal/atomic_hook.h"
+ "internal/cycleclock.h"
+ "internal/low_level_scheduling.h"
+ "internal/per_thread_tls.h"
+ "internal/raw_logging.h"
+ "internal/spinlock.h"
+ "internal/sysinfo.h"
+ "internal/thread_identity.h"
+ "internal/tsan_mutex_interface.h"
+ "internal/unscaledcycleclock.h"
+ "log_severity.h"
+ SRCS
+ "internal/cycleclock.cc"
+ "internal/raw_logging.cc"
+ "internal/spinlock.cc"
+ "internal/sysinfo.cc"
+ "internal/thread_identity.cc"
+ "internal/unscaledcycleclock.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base_internal
+ absl::config
+ absl::core_headers
+ absl::dynamic_annotations
+ absl::spinlock_wait
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ throw_delegate
+ HDRS
+ "internal/throw_delegate.h"
+ SRCS
+ "internal/throw_delegate.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ ${ABSL_EXCEPTIONS_FLAG}
+ DEPS
+ absl::base
+)
+
+absl_cc_library(
+ NAME
+ exception_testing
+ HDRS
+ "internal/exception_testing.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ gtest
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ pretty_function
+ HDRS
+ "internal/pretty_function.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+)
+
+absl_cc_library(
+ NAME
+ exception_safety_testing
+ HDRS
+ "internal/exception_safety_testing.h"
+ SRCS
+ "internal/exception_safety_testing.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ ${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::config
+ absl::pretty_function
absl::memory
absl::meta
absl::strings
- absl::optional
+ absl::utility
gtest
- )
-
-absl_library(
- TARGET
- absl_base_internal_exception_safety_testing
- SOURCES
- ${EXCEPTION_SAFETY_TESTING_SRC}
- PUBLIC_LIBRARIES
- ${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_EXCEPTIONS_FLAG}
-)
-endif()
-
-
-# dynamic_annotations library
-set(DYNAMIC_ANNOTATIONS_SRC "dynamic_annotations.cc")
-
-absl_library(
- TARGET
- absl_dynamic_annotations
- SOURCES
- ${DYNAMIC_ANNOTATIONS_SRC}
+ TESTONLY
)
-
-# spinlock_wait library
-set(SPINLOCK_WAIT_SRC "internal/spinlock_wait.cc")
-
-absl_library(
- TARGET
- absl_spinlock_wait
- SOURCES
- ${SPINLOCK_WAIT_SRC}
-)
-
-
-# malloc_internal library
-list(APPEND MALLOC_INTERNAL_SRC
- "internal/low_level_alloc.cc"
-)
-
-absl_library(
- TARGET
- absl_malloc_internal
- SOURCES
- ${MALLOC_INTERNAL_SRC}
- PUBLIC_LIBRARIES
- absl_dynamic_annotations
-)
-
-
-
-#
-## TESTS
-#
-
-# call once test
-set(ATOMIC_HOOK_TEST_SRC "internal/atomic_hook_test.cc")
-set(ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
- atomic_hook_test
- SOURCES
- ${ATOMIC_HOOK_TEST_SRC}
- PUBLIC_LIBRARIES
- ${ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# call once test
-set(CALL_ONCE_TEST_SRC "call_once_test.cc")
-set(CALL_ONCE_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- call_once_test
- SOURCES
- ${CALL_ONCE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${CALL_ONCE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test bit_cast_test
-set(BIT_CAST_TEST_SRC "bit_cast_test.cc")
-
-absl_test(
- TARGET
- bit_cast_test
- SOURCES
- ${BIT_CAST_TEST_SRC}
-)
-
-
-# test absl_throw_delegate_test
-set(THROW_DELEGATE_TEST_SRC "throw_delegate_test.cc")
-set(THROW_DELEGATE_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate)
-
-absl_test(
- TARGET
- throw_delegate_test
- SOURCES
- ${THROW_DELEGATE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${THROW_DELEGATE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test invoke_test
-set(INVOKE_TEST_SRC "invoke_test.cc")
-set(INVOKE_TEST_PUBLIC_LIBRARIES absl::strings)
-
-absl_test(
- TARGET
- invoke_test
- SOURCES
- ${INVOKE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INVOKE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test inline_variable_test
-list(APPEND INLINE_VARIABLE_TEST_SRC
- "internal/inline_variable_testing.h"
- "inline_variable_test.cc"
- "inline_variable_test_a.cc"
- "inline_variable_test_b.cc"
-)
-
-set(INLINE_VARIABLE_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
- inline_variable_test
- SOURCES
- ${INLINE_VARIABLE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INLINE_VARIABLE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test spinlock_test_common
-set(SPINLOCK_TEST_COMMON_SRC "spinlock_test_common.cc")
-set(SPINLOCK_TEST_COMMON_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- spinlock_test_common
- SOURCES
- ${SPINLOCK_TEST_COMMON_SRC}
- PUBLIC_LIBRARIES
- ${SPINLOCK_TEST_COMMON_PUBLIC_LIBRARIES}
-)
-
-
-# test spinlock_test
-set(SPINLOCK_TEST_SRC "spinlock_test_common.cc")
-set(SPINLOCK_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- spinlock_test
- SOURCES
- ${SPINLOCK_TEST_SRC}
- PUBLIC_LIBRARIES
- ${SPINLOCK_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test endian_test
-set(ENDIAN_TEST_SRC "internal/endian_test.cc")
-
-absl_test(
- TARGET
- endian_test
- SOURCES
- ${ENDIAN_TEST_SRC}
-)
-
-
-# test config_test
-set(CONFIG_TEST_SRC "config_test.cc")
-set(CONFIG_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-absl_test(
- TARGET
- config_test
- SOURCES
- ${CONFIG_TEST_SRC}
- PUBLIC_LIBRARIES
- ${CONFIG_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test raw_logging_test
-set(RAW_LOGGING_TEST_SRC "raw_logging_test.cc")
-set(RAW_LOGGING_TEST_PUBLIC_LIBRARIES absl::base absl::strings)
-
-absl_test(
- TARGET
- raw_logging_test
- SOURCES
- ${RAW_LOGGING_TEST_SRC}
- PUBLIC_LIBRARIES
- ${RAW_LOGGING_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test sysinfo_test
-set(SYSINFO_TEST_SRC "internal/sysinfo_test.cc")
-set(SYSINFO_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- sysinfo_test
- SOURCES
- ${SYSINFO_TEST_SRC}
- PUBLIC_LIBRARIES
- ${SYSINFO_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test low_level_alloc_test
-set(LOW_LEVEL_ALLOC_TEST_SRC "internal/low_level_alloc_test.cc")
-set(LOW_LEVEL_ALLOC_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
- low_level_alloc_test
- SOURCES
- ${LOW_LEVEL_ALLOC_TEST_SRC}
- PUBLIC_LIBRARIES
- ${LOW_LEVEL_ALLOC_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test thread_identity_test
-set(THREAD_IDENTITY_TEST_SRC "internal/thread_identity_test.cc")
-set(THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- thread_identity_test
- SOURCES
- ${THREAD_IDENTITY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES}
-)
-
-#test exceptions_safety_testing_test
-set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc")
-set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES
- absl::base
- absl_base_internal_exception_safety_testing
- absl::memory
- absl::meta
- absl::strings
- absl::optional
-)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
absl_exception_safety_testing_test
- SOURCES
- ${EXCEPTION_SAFETY_TESTING_TEST_SRC}
- PUBLIC_LIBRARIES
- ${EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
+ SRCS
+ "exception_safety_testing_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::exception_safety_testing
+ absl::memory
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ atomic_hook_test
+ SRCS
+ "internal/atomic_hook_test.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ bit_cast_test
+ SRCS
+ "bit_cast_test.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ throw_delegate_test
+ SRCS
+ "throw_delegate_test.cc"
+ DEPS
+ absl::base
+ absl::throw_delegate
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ inline_variable_test
+ SRCS
+ "internal/inline_variable_testing.h"
+ "inline_variable_test.cc"
+ "inline_variable_test_a.cc"
+ "inline_variable_test_b.cc"
+ DEPS
+ absl::base_internal
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ invoke_test
+ SRCS
+ "invoke_test.cc"
+ DEPS
+ absl::base_internal
+ absl::memory
+ absl::strings
+ gmock
+ gtest_main
+)
+
+absl_cc_library(
+ NAME
+ spinlock_test_common
+ SRCS
+ "spinlock_test_common.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::spinlock_wait
+ absl::synchronization
+ gtest
+ TESTONLY
+)
+
+# On bazel BUILD this target use "alwayslink = 1" which is not implemented here
+absl_cc_test(
+ NAME
+ spinlock_test
+ SRCS
+ "spinlock_test_common.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::spinlock_wait
+ absl::synchronization
+ gtest_main
+)
+
+absl_cc_library(
+ NAME
+ endian
+ HDRS
+ "internal/endian.h"
+ "internal/unaligned_access.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ absl::core_headers
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ endian_test
+ SRCS
+ "internal/endian_test.cc"
+ DEPS
+ absl::base
+ absl::config
+ absl::endian
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ config_test
+ SRCS
+ "config_test.cc"
+ DEPS
+ absl::config
+ absl::synchronization
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ call_once_test
+ SRCS
+ "call_once_test.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::synchronization
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ raw_logging_test
+ SRCS
+ "raw_logging_test.cc"
+ DEPS
+ absl::base
+ absl::strings
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ sysinfo_test
+ SRCS
+ "internal/sysinfo_test.cc"
+ DEPS
+ absl::base
+ absl::synchronization
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ low_level_alloc_test
+ SRCS
+ "internal/low_level_alloc_test.cc"
+ DEPS
+ absl::malloc_internal
+ Threads::Threads
+)
+
+absl_cc_test(
+ NAME
+ thread_identity_test
+ SRCS
+ "internal/thread_identity_test.cc"
+ DEPS
+ absl::base
+ absl::core_headers
+ absl::synchronization
+ Threads::Threads
+ gtest_main
+)
+
+absl_cc_library(
+ NAME
+ bits
+ HDRS
+ "internal/bits.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+)
+
+absl_cc_test(
+ NAME
+ bits_test
+ SRCS
+ "internal/bits_test.cc"
+ DEPS
+ absl::bits
+ gtest_main
+)
+
+absl_cc_library(
+ NAME
+ scoped_set_env
+ SRCS
+ "internal/scoped_set_env.cc"
+ HDRS
+ "internal/scoped_set_env.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base
+)
+
+absl_cc_test(
+ NAME
+ scoped_set_env_test
+ SRCS
+ "internal/scoped_set_env_test.cc"
+ DEPS
+ absl::scoped_set_env
+ gtest_main
)
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index e850022..48195d6 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -80,7 +80,7 @@
//
// A function-like feature checking macro that accepts C++11 style attributes.
// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6
-// (http://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
+// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
// find `__has_cpp_attribute`, will evaluate to 0.
#if defined(__cplusplus) && defined(__has_cpp_attribute)
// NOTE: requiring __cplusplus above should not be necessary, but
@@ -102,7 +102,7 @@
//
// Tells the compiler to perform `printf` format string checking if the
// compiler supports it; see the 'format' attribute in
-// <http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>.
+// <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>.
//
// Note: As the GCC manual states, "[s]ince non-static C++ methods
// have an implicit 'this' argument, the arguments of such methods
@@ -155,7 +155,12 @@
// ABSL_ATTRIBUTE_WEAK
//
// Tags a function as weak for the purposes of compilation and linking.
-#if ABSL_HAVE_ATTRIBUTE(weak) || (defined(__GNUC__) && !defined(__clang__))
+// Weak attributes currently do not work properly in LLVM's Windows backend,
+// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
+// for futher information.
+#if (ABSL_HAVE_ATTRIBUTE(weak) || \
+ (defined(__GNUC__) && !defined(__clang__))) && \
+ !(defined(__llvm__) && defined(_WIN32))
#undef ABSL_ATTRIBUTE_WEAK
#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
#define ABSL_HAVE_ATTRIBUTE_WEAK 1
@@ -282,6 +287,17 @@
#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI
#endif
+// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
+//
+// Tells the SafeStack to not instrument a given function.
+// See https://clang.llvm.org/docs/SafeStack.html for details.
+#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \
+ __attribute__((no_sanitize("safe-stack")))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
+#endif
+
// ABSL_ATTRIBUTE_RETURNS_NONNULL
//
// Tells the compiler that a particular function never returns a null pointer.
@@ -296,13 +312,13 @@
// ABSL_HAVE_ATTRIBUTE_SECTION
//
-// Indicates whether labeled sections are supported. Labeled sections are not
-// supported on Darwin/iOS.
+// Indicates whether labeled sections are supported. Weak symbol support is
+// a prerequisite. Labeled sections are not supported on Darwin/iOS.
#ifdef ABSL_HAVE_ATTRIBUTE_SECTION
#error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set
#elif (ABSL_HAVE_ATTRIBUTE(section) || \
(defined(__GNUC__) && !defined(__clang__))) && \
- !defined(__APPLE__)
+ !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK
#define ABSL_HAVE_ATTRIBUTE_SECTION 1
// ABSL_ATTRIBUTE_SECTION
@@ -397,17 +413,28 @@
// ABSL_MUST_USE_RESULT
//
-// Tells the compiler to warn about unused return values for functions declared
-// with this macro. The macro must appear as the very first part of a function
-// declaration or definition:
+// Tells the compiler to warn about unused results.
//
-// Example:
+// When annotating a function, it must appear as the first part of the
+// declaration or definition. The compiler will warn if the return value from
+// such a function is unused:
//
// ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket();
+// AllocateSprocket(); // Triggers a warning.
//
-// This placement has the broadest compatibility with GCC, Clang, and MSVC, with
-// both defs and decls, and with GCC-style attributes, MSVC declspec, C++11
-// and C++17 attributes.
+// When annotating a class, it is equivalent to annotating every function which
+// returns an instance.
+//
+// class ABSL_MUST_USE_RESULT Sprocket {};
+// Sprocket(); // Triggers a warning.
+//
+// Sprocket MakeSprocket();
+// MakeSprocket(); // Triggers a warning.
+//
+// Note that references and pointers are not instances:
+//
+// Sprocket* SprocketPointer();
+// SprocketPointer(); // Does *not* trigger a warning.
//
// ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result
// warning. For that, warn_unused_result is used only for clang but not for gcc.
diff --git a/absl/base/bit_cast_test.cc b/absl/base/bit_cast_test.cc
index 8cd878d..4846add 100644
--- a/absl/base/bit_cast_test.cc
+++ b/absl/base/bit_cast_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 532ee2e..8c4f297 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -150,12 +150,8 @@
old_control != kOnceRunning &&
old_control != kOnceWaiter &&
old_control != kOnceDone) {
- ABSL_RAW_LOG(
- FATAL,
- "Unexpected value for control word: %lx. Either the control word "
- "has non-static storage duration (where GoogleOnceDynamic might "
- "be appropriate), or there's been a memory corruption.",
- static_cast<unsigned long>(old_control)); // NOLINT
+ ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",
+ static_cast<unsigned long>(old_control)); // NOLINT
}
}
#endif // NDEBUG
diff --git a/absl/base/call_once_test.cc b/absl/base/call_once_test.cc
index cd58ee1..9c2a0c4 100644
--- a/absl/base/call_once_test.cc
+++ b/absl/base/call_once_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -18,6 +18,8 @@
#include <vector>
#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/base/const_init.h"
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
@@ -25,7 +27,8 @@
namespace {
absl::once_flag once;
-Mutex counters_mu;
+
+ABSL_CONST_INIT Mutex counters_mu(absl::kConstInit);
int running_thread_count GUARDED_BY(counters_mu) = 0;
int call_once_invoke_count GUARDED_BY(counters_mu) = 0;
diff --git a/absl/base/casts.h b/absl/base/casts.h
index 20fd34d..00196d2 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -5,7 +5,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -105,7 +105,7 @@
//
// Such implicit cast chaining may be useful within template logic.
template <typename To>
-inline To implicit_cast(typename absl::internal::identity_t<To> to) {
+constexpr To implicit_cast(typename absl::internal::identity_t<To> to) {
return to;
}
diff --git a/absl/base/config.h b/absl/base/config.h
index d4eb7d0..3b81e26 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -5,7 +5,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -118,7 +118,7 @@
// Checks whether `std::is_trivially_copy_assignable<T>` is supported.
// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with
-// either libc++ or libstdc++, and Visual Studio.
+// either libc++ or libstdc++, and Visual Studio (but not NVCC).
#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
@@ -127,7 +127,7 @@
(!defined(__clang__) && defined(__GNUC__) && \
(__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
- defined(_MSC_VER)
+ (defined(_MSC_VER) && !defined(__NVCC__))
#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
#endif
@@ -139,12 +139,18 @@
#ifdef ABSL_HAVE_THREAD_LOCAL
#error ABSL_HAVE_THREAD_LOCAL cannot be directly set
#elif defined(__APPLE__)
-// Notes: Xcode's clang did not support `thread_local` until version
-// 8, and even then not for all iOS < 9.0. Also, Xcode 9.3 started disallowing
-// `thread_local` for 32-bit iOS simulator targeting iOS 9.x.
-// `__has_feature` is only supported by Clang so it has be inside
+// Notes:
+// * Xcode's clang did not support `thread_local` until version 8, and
+// even then not for all iOS < 9.0.
+// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
+// targeting iOS 9.x.
+// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
+// making __has_feature unreliable there.
+//
+// Otherwise, `__has_feature` is only supported by Clang so it has be inside
// `defined(__APPLE__)` check.
-#if __has_feature(cxx_thread_local)
+#if __has_feature(cxx_thread_local) && \
+ !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
#define ABSL_HAVE_THREAD_LOCAL 1
#endif
#else // !defined(__APPLE__)
@@ -268,7 +274,8 @@
#error ABSL_HAVE_MMAP cannot be directly set
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
- defined(__wasm__) || defined(__Fuchsia__) || defined(__sun)
+ defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
+ defined(__ASYLO__)
#define ABSL_HAVE_MMAP 1
#endif
@@ -322,6 +329,8 @@
#define ABSL_HAVE_ALARM 1
#elif defined(_MSC_VER)
// feature tests for Microsoft's library
+#elif defined(__EMSCRIPTEN__)
+// emscripten doesn't support signals
#elif defined(__native_client__)
#else
// other standard libraries
@@ -356,6 +365,18 @@
#error "absl endian detection needs to be set up for your compiler"
#endif
+// MacOS 10.13 doesn't let you use <any>, <optional>, or <variant> even though
+// the headers exist and are publicly noted to work. See
+// https://github.com/abseil/abseil-cpp/issues/207 and
+// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes
+#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \
+ defined(__MAC_OS_X_VERSION_MIN_REQUIRED__) && \
+ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400
+#define ABSL_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE 1
+#else
+#define ABSL_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE 0
+#endif
+
// ABSL_HAVE_STD_ANY
//
// Checks whether C++17 std::any is available by checking whether <any> exists.
@@ -364,7 +385,8 @@
#endif
#ifdef __has_include
-#if __has_include(<any>) && __cplusplus >= 201703L
+#if __has_include(<any>) && __cplusplus >= 201703L && \
+ !ABSL_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_ANY 1
#endif
#endif
@@ -377,7 +399,8 @@
#endif
#ifdef __has_include
-#if __has_include(<optional>) && __cplusplus >= 201703L
+#if __has_include(<optional>) && __cplusplus >= 201703L && \
+ !ABSL_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_OPTIONAL 1
#endif
#endif
@@ -390,7 +413,8 @@
#endif
#ifdef __has_include
-#if __has_include(<variant>) && __cplusplus >= 201703L
+#if __has_include(<variant>) && __cplusplus >= 201703L && \
+ !ABSL_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_VARIANT 1
#endif
#endif
@@ -423,4 +447,12 @@
#define ABSL_HAVE_STD_STRING_VIEW 1
#endif
+// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
+// SEH exception from emplace for variant<SomeStruct> when constructing the
+// struct can throw. This defeats some of variant_test and
+// variant_exception_safety_test.
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG)
+#define ABSL_INTERNAL_MSVC_2017_DBG_MODE
+#endif
+
#endif // ABSL_BASE_CONFIG_H_
diff --git a/absl/base/config_test.cc b/absl/base/config_test.cc
index c839712..7e0c033 100644
--- a/absl/base/config_test.cc
+++ b/absl/base/config_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/const_init.h b/absl/base/const_init.h
new file mode 100644
index 0000000..17858a7
--- /dev/null
+++ b/absl/base/const_init.h
@@ -0,0 +1,72 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// kConstInit
+// -----------------------------------------------------------------------------
+//
+// A constructor tag used to mark an object as safe for use as a global
+// variable, avoiding the usual lifetime issues that can affect globals.
+
+#ifndef ABSL_BASE_CONST_INIT_H_
+#define ABSL_BASE_CONST_INIT_H_
+
+// In general, objects with static storage duration (such as global variables)
+// can trigger tricky object lifetime situations. Attempting to access them
+// from the constructors or destructors of other global objects can result in
+// undefined behavior, unless their constructors and destructors are designed
+// with this issue in mind.
+//
+// The normal way to deal with this issue in C++11 is to use constant
+// initialization and trivial destructors.
+//
+// Constant initialization is guaranteed to occur before any other code
+// executes. Constructors that are declared 'constexpr' are eligible for
+// constant initialization. You can annotate a variable declaration with the
+// ABSL_CONST_INIT macro to express this intent. For compilers that support
+// it, this annotation will cause a compilation error for declarations that
+// aren't subject to constant initialization (perhaps because a runtime value
+// was passed as a constructor argument).
+//
+// On program shutdown, lifetime issues can be avoided on global objects by
+// ensuring that they contain trivial destructors. A class has a trivial
+// destructor unless it has a user-defined destructor, a virtual method or base
+// class, or a data member or base class with a non-trivial destructor of its
+// own. Objects with static storage duration and a trivial destructor are not
+// cleaned up on program shutdown, and are thus safe to access from other code
+// running during shutdown.
+//
+// For a few core Abseil classes, we make a best effort to allow for safe global
+// instances, even though these classes have non-trivial destructors. These
+// objects can be created with the absl::kConstInit tag. For example:
+// ABSL_CONST_INIT absl::Mutex global_mutex(absl::kConstInit);
+//
+// The line above declares a global variable of type absl::Mutex which can be
+// accessed at any point during startup or shutdown. global_mutex's destructor
+// will still run, but will not invalidate the object. Note that C++ specifies
+// that accessing an object after its destructor has run results in undefined
+// behavior, but this pattern works on the toolchains we support.
+//
+// The absl::kConstInit tag should only be used to define objects with static
+// or thread_local storage duration.
+
+namespace absl {
+
+enum ConstInitType {
+ kConstInit,
+};
+
+} // namespace absl
+
+#endif // ABSL_BASE_CONST_INIT_H_
diff --git a/absl/base/dynamic_annotations.cc b/absl/base/dynamic_annotations.cc
index b97fa3a..1411093 100644
--- a/absl/base/dynamic_annotations.cc
+++ b/absl/base/dynamic_annotations.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h
index 88048b0..8ac7498 100644
--- a/absl/base/dynamic_annotations.h
+++ b/absl/base/dynamic_annotations.h
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -139,6 +139,7 @@
#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */
#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */
#endif /* ABSL_DYNAMIC_ANNOTATIONS_ENABLED || MEMORY_SANITIZER */
+
/* TODO(delesley) -- Replace __CLANG_SUPPORT_DYN_ANNOTATION__ with the
appropriate feature ID. */
#if defined(__clang__) && (!defined(SWIG)) \
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
index 106bc34..2ed3860 100644
--- a/absl/base/exception_safety_testing_test.cc
+++ b/absl/base/exception_safety_testing_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -770,6 +770,18 @@
.Test(invoker));
}
+TEST(ExceptionSafetyTesterTest, ResetsCountdown) {
+ auto test =
+ testing::MakeExceptionSafetyTester()
+ .WithInitialValue(ThrowingValue<>())
+ .WithContracts([](ThrowingValue<>*) { return AssertionSuccess(); })
+ .WithOperation([](ThrowingValue<>*) {});
+ ASSERT_TRUE(test.Test());
+ // If the countdown isn't reset because there were no exceptions thrown, then
+ // this will fail with a termination from an unhandled exception
+ EXPECT_TRUE(test.Test());
+}
+
struct NonCopyable : public NonNegative {
NonCopyable(const NonCopyable&) = delete;
NonCopyable() : NonNegative{0} {}
diff --git a/absl/base/inline_variable_test.cc b/absl/base/inline_variable_test.cc
index 5499189..471f706 100644
--- a/absl/base/inline_variable_test.cc
+++ b/absl/base/inline_variable_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/inline_variable_test_a.cc b/absl/base/inline_variable_test_a.cc
index a3bf3b6..d0b8e7d 100644
--- a/absl/base/inline_variable_test_a.cc
+++ b/absl/base/inline_variable_test_a.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/inline_variable_test_b.cc b/absl/base/inline_variable_test_b.cc
index b4b9393..931d56d 100644
--- a/absl/base/inline_variable_test_b.cc
+++ b/absl/base/inline_variable_test_b.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h
index b458511..803e905 100644
--- a/absl/base/internal/atomic_hook.h
+++ b/absl/base/internal/atomic_hook.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/atomic_hook_test.cc b/absl/base/internal/atomic_hook_test.cc
index cf74075..ecc8040 100644
--- a/absl/base/internal/atomic_hook_test.cc
+++ b/absl/base/internal/atomic_hook_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h
index bc7faae..b0780f2 100644
--- a/absl/base/internal/bits.h
+++ b/absl/base/internal/bits.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/bits_test.cc b/absl/base/internal/bits_test.cc
index e5d991d..7855fa6 100644
--- a/absl/base/internal/bits_test.cc
+++ b/absl/base/internal/bits_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc
index a742df0..4b553c2 100644
--- a/absl/base/internal/cycleclock.cc
+++ b/absl/base/internal/cycleclock.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -22,6 +22,7 @@
#include "absl/base/internal/cycleclock.h"
+#include <atomic>
#include <chrono> // NOLINT(build/c++11)
#include "absl/base/internal/unscaledcycleclock.h"
@@ -52,17 +53,26 @@
#endif
static constexpr double kFrequencyScale = 1.0 / (1 << kShift);
+static std::atomic<CycleClockSourceFunc> cycle_clock_source;
} // namespace
int64_t CycleClock::Now() {
- return base_internal::UnscaledCycleClock::Now() >> kShift;
+ auto fn = cycle_clock_source.load(std::memory_order_relaxed);
+ if (fn == nullptr) {
+ return base_internal::UnscaledCycleClock::Now() >> kShift;
+ }
+ return fn() >> kShift;
}
double CycleClock::Frequency() {
return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency();
}
+void CycleClockSource::Register(CycleClockSourceFunc source) {
+ cycle_clock_source.store(source, std::memory_order_relaxed);
+}
+
#else
int64_t CycleClock::Now() {
diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h
index 60e9715..794564e 100644
--- a/absl/base/internal/cycleclock.h
+++ b/absl/base/internal/cycleclock.h
@@ -5,7 +5,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -28,7 +28,6 @@
// not necessarily "CPU cycles" and code should not rely on that behavior, even
// if experimentally observed.
//
-//
// An arbitrary offset may have been added to the counter at power on.
//
// On some platforms, the rate and offset of the counter may differ
@@ -71,6 +70,20 @@
CycleClock& operator=(const CycleClock&) = delete;
};
+using CycleClockSourceFunc = int64_t (*)();
+
+class CycleClockSource {
+ private:
+ // CycleClockSource::Register()
+ //
+ // Register a function that provides an alternate source for the unscaled CPU
+ // cycle count value. The source function must be async signal safe, must not
+ // call CycleClock::Now(), and must have a frequency that matches that of the
+ // unscaled clock used by CycleClock. A nullptr value resets CycleClock to use
+ // the default source.
+ static void Register(CycleClockSourceFunc source);
+};
+
} // namespace base_internal
} // namespace absl
diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h
index 0426e11..0401ddf 100644
--- a/absl/base/internal/direct_mmap.h
+++ b/absl/base/internal/direct_mmap.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -75,7 +75,11 @@
// On these architectures, implement mmap with mmap2.
static int pagesize = 0;
if (pagesize == 0) {
+#if defined(__wasm__) || defined(__asmjs__)
pagesize = getpagesize();
+#else
+ pagesize = sysconf(_SC_PAGESIZE);
+#endif
}
if (offset < 0 || offset % pagesize != 0) {
errno = EINVAL;
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h
index edc10f1..6b828b6 100644
--- a/absl/base/internal/endian.h
+++ b/absl/base/internal/endian.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -75,21 +75,21 @@
if (__builtin_constant_p(host_int)) {
return __bswap_constant_64(host_int);
} else {
- register uint64_t result;
+ uint64_t result;
__asm__("bswap %0" : "=r"(result) : "0"(host_int));
return result;
}
#elif defined(__GLIBC__)
return bswap_64(host_int);
#else
- return (((x & uint64_t{(0xFF}) << 56) |
- ((x & uint64_t{(0xFF00}) << 40) |
- ((x & uint64_t{(0xFF0000}) << 24) |
- ((x & uint64_t{(0xFF000000}) << 8) |
- ((x & uint64_t{(0xFF00000000}) >> 8) |
- ((x & uint64_t{(0xFF0000000000}) >> 24) |
- ((x & uint64_t{(0xFF000000000000}) >> 40) |
- ((x & uint64_t{(0xFF00000000000000}) >> 56));
+ return (((host_int & uint64_t{0xFF}) << 56) |
+ ((host_int & uint64_t{0xFF00}) << 40) |
+ ((host_int & uint64_t{0xFF0000}) << 24) |
+ ((host_int & uint64_t{0xFF000000}) << 8) |
+ ((host_int & uint64_t{0xFF00000000}) >> 8) |
+ ((host_int & uint64_t{0xFF0000000000}) >> 24) |
+ ((host_int & uint64_t{0xFF000000000000}) >> 40) |
+ ((host_int & uint64_t{0xFF00000000000000}) >> 56));
#endif // bswap_64
}
@@ -97,8 +97,10 @@
#if defined(__GLIBC__)
return bswap_32(host_int);
#else
- return (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) |
- ((x & 0xFF000000) >> 24));
+ return (((host_int & uint32_t{0xFF}) << 24) |
+ ((host_int & uint32_t{0xFF00}) << 8) |
+ ((host_int & uint32_t{0xFF0000}) >> 8) |
+ ((host_int & uint32_t{0xFF000000}) >> 24));
#endif
}
@@ -106,7 +108,8 @@
#if defined(__GLIBC__)
return bswap_16(host_int);
#else
- return uint16_t{((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)};
+ return (((host_int & uint16_t{0xFF}) << 8) |
+ ((host_int & uint16_t{0xFF00}) >> 8));
#endif
}
diff --git a/absl/base/internal/endian_test.cc b/absl/base/internal/endian_test.cc
index e276915..98a099e 100644
--- a/absl/base/internal/endian_test.cc
+++ b/absl/base/internal/endian_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc
index f1d081f..6ef4325 100644
--- a/absl/base/internal/exception_safety_testing.cc
+++ b/absl/base/internal/exception_safety_testing.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -23,6 +23,10 @@
exceptions_internal::StrongGuaranteeTagType strong_guarantee;
+exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester() {
+ return {};
+}
+
namespace exceptions_internal {
int countdown = -1;
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index 5665a1b..be38ba5 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -33,7 +33,7 @@
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
-#include "absl/types/optional.h"
+#include "absl/utility/utility.h"
namespace testing {
@@ -127,10 +127,8 @@
void* address = it.first;
TrackedAddress& tracked_address = it.second;
if (tracked_address.is_alive) {
- ADD_FAILURE() << "Object at address " << address
- << " with countdown of " << countdown_
- << " was not destroyed [" << tracked_address.description
- << "]";
+ ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
+ countdown_, "Object was not destroyed.");
}
}
}
@@ -141,11 +139,11 @@
TrackedAddress& tracked_address =
current_tracker_instance_->address_map_[address];
if (tracked_address.is_alive) {
- ADD_FAILURE() << "Object at address " << address << " with countdown of "
- << current_tracker_instance_->countdown_
- << " was re-constructed. Previously: ["
- << tracked_address.description << "] Now: [" << description
- << "]";
+ ADD_FAILURE() << ErrorMessage(
+ address, tracked_address.description,
+ current_tracker_instance_->countdown_,
+ "Object was re-constructed. Current object was constructed by " +
+ description);
}
tracked_address = {true, std::move(description)};
}
@@ -159,10 +157,9 @@
TrackedAddress& tracked_address = it->second;
if (!tracked_address.is_alive) {
- ADD_FAILURE() << "Object at address " << address << " with countdown of "
- << current_tracker_instance_->countdown_
- << " was re-destroyed or created prior to construction "
- << "tracking [" << tracked_address.description << "]";
+ ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
+ current_tracker_instance_->countdown_,
+ "Object was re-destroyed.");
}
tracked_address.is_alive = false;
}
@@ -172,6 +169,18 @@
return current_tracker_instance_ != nullptr;
}
+ static std::string ErrorMessage(void* address,
+ const std::string& address_description,
+ int countdown,
+ const std::string& error_description) {
+ return absl::Substitute(
+ "With coundtown at $0:\n"
+ " $1\n"
+ " Object originally constructed by $2\n"
+ " Object address: $3\n",
+ countdown, error_description, address_description, address);
+ }
+
std::unordered_map<void*, TrackedAddress> address_map_;
int countdown_;
@@ -190,70 +199,6 @@
~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); }
};
-
-template <typename Factory, typename Operation, typename Contract>
-absl::optional<testing::AssertionResult> TestSingleContractAtCountdownImpl(
- const Factory& factory, const Operation& operation, int count,
- const Contract& contract) {
- auto t_ptr = factory();
- absl::optional<testing::AssertionResult> current_res;
- SetCountdown(count);
- try {
- operation(t_ptr.get());
- } catch (const exceptions_internal::TestException& e) {
- current_res.emplace(contract(t_ptr.get()));
- if (!current_res.value()) {
- *current_res << e.what() << " failed contract check";
- }
- }
- UnsetCountdown();
- return current_res;
-}
-
-template <typename Factory, typename Operation>
-absl::optional<testing::AssertionResult> TestSingleContractAtCountdownImpl(
- const Factory& factory, const Operation& operation, int count,
- StrongGuaranteeTagType) {
- using TPtr = typename decltype(factory())::pointer;
- auto t_is_strong = [&](TPtr t) { return *t == *factory(); };
- return TestSingleContractAtCountdownImpl(factory, operation, count,
- t_is_strong);
-}
-
-template <typename Factory, typename Operation, typename Contract>
-int TestSingleContractAtCountdown(
- const Factory& factory, const Operation& operation, int count,
- const Contract& contract,
- absl::optional<testing::AssertionResult>* reduced_res) {
- // If reduced_res is empty, it means the current call to
- // TestSingleContractAtCountdown(...) is the first test being run so we do
- // want to run it. Alternatively, if it's not empty (meaning a previous test
- // has run) we want to check if it passed. If the previous test did pass, we
- // want to contine running tests so we do want to run the current one. If it
- // failed, we want to short circuit so as not to overwrite the AssertionResult
- // output. If that's the case, we do not run the current test and instead we
- // simply return.
- if (!reduced_res->has_value() || reduced_res->value()) {
- *reduced_res =
- TestSingleContractAtCountdownImpl(factory, operation, count, contract);
- }
- return 0;
-}
-
-template <typename Factory, typename Operation, typename... Contracts>
-inline absl::optional<testing::AssertionResult> TestAllContractsAtCountdown(
- const Factory& factory, const Operation& operation, int count,
- const Contracts&... contracts) {
- absl::optional<testing::AssertionResult> reduced_res;
-
- // Run each checker, short circuiting after the first failure
- int dummy[] = {
- 0, (TestSingleContractAtCountdown(factory, operation, count, contracts,
- &reduced_res))...};
- static_cast<void>(dummy);
- return reduced_res;
-}
-
} // namespace exceptions_internal
extern exceptions_internal::NoThrowTag nothrow_ctor;
@@ -613,8 +558,8 @@
// We provide both regular and templated operator delete because if only the
// templated version is provided as we did with operator new, the compiler has
// no way of knowing which overload of operator delete to call. See
- // http://en.cppreference.com/w/cpp/memory/new/operator_delete and
- // http://en.cppreference.com/w/cpp/language/delete for the gory details.
+ // https://en.cppreference.com/w/cpp/memory/new/operator_delete and
+ // https://en.cppreference.com/w/cpp/language/delete for the gory details.
void operator delete(void* p) noexcept { ::operator delete(p); }
template <typename... Args>
@@ -773,7 +718,7 @@
}
size_type max_size() const noexcept {
- return std::numeric_limits<difference_type>::max() / sizeof(value_type);
+ return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
}
ThrowingAllocator select_on_container_copy_construction() noexcept(
@@ -871,7 +816,7 @@
namespace exceptions_internal {
-// Dummy struct for ExceptionSafetyTester<> partial state.
+// Dummy struct for ExceptionSafetyTestBuilder<> partial state.
struct UninitializedT {};
template <typename T>
@@ -893,20 +838,97 @@
template <typename Factory = UninitializedT,
typename Operation = UninitializedT, typename... Contracts>
-class ExceptionSafetyTester;
+class ExceptionSafetyTestBuilder;
} // namespace exceptions_internal
-exceptions_internal::ExceptionSafetyTester<> MakeExceptionSafetyTester();
+/*
+ * Constructs an empty ExceptionSafetyTestBuilder. All
+ * ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation
+ * methods return new instances of ExceptionSafetyTestBuilder.
+ *
+ * In order to test a T for exception safety, a factory for that T, a testable
+ * operation, and at least one contract callback returning an assertion
+ * result must be applied using the respective methods.
+ */
+exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester();
namespace exceptions_internal {
+template <typename T>
+struct IsUniquePtr : std::false_type {};
+
+template <typename T, typename D>
+struct IsUniquePtr<std::unique_ptr<T, D>> : std::true_type {};
+
+template <typename Factory>
+struct FactoryPtrTypeHelper {
+ using type = decltype(std::declval<const Factory&>()());
+
+ static_assert(IsUniquePtr<type>::value, "Factories must return a unique_ptr");
+};
+
+template <typename Factory>
+using FactoryPtrType = typename FactoryPtrTypeHelper<Factory>::type;
+
+template <typename Factory>
+using FactoryElementType = typename FactoryPtrType<Factory>::element_type;
+
+template <typename T>
+class ExceptionSafetyTest {
+ using Factory = std::function<std::unique_ptr<T>()>;
+ using Operation = std::function<void(T*)>;
+ using Contract = std::function<AssertionResult(T*)>;
+
+ public:
+ template <typename... Contracts>
+ explicit ExceptionSafetyTest(const Factory& f, const Operation& op,
+ const Contracts&... contracts)
+ : factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {}
+
+ AssertionResult Test() const {
+ for (int count = 0;; ++count) {
+ exceptions_internal::ConstructorTracker ct(count);
+
+ for (const auto& contract : contracts_) {
+ auto t_ptr = factory_();
+ try {
+ SetCountdown(count);
+ operation_(t_ptr.get());
+ // Unset for the case that the operation throws no exceptions, which
+ // would leave the countdown set and break the *next* exception safety
+ // test after this one.
+ UnsetCountdown();
+ return AssertionSuccess();
+ } catch (const exceptions_internal::TestException& e) {
+ if (!contract(t_ptr.get())) {
+ return AssertionFailure() << e.what() << " failed contract check";
+ }
+ }
+ }
+ }
+ }
+
+ private:
+ template <typename ContractFn>
+ Contract WrapContract(const ContractFn& contract) {
+ return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); };
+ }
+
+ Contract WrapContract(StrongGuaranteeTagType) {
+ return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); };
+ }
+
+ Factory factory_;
+ Operation operation_;
+ std::vector<Contract> contracts_;
+};
/*
* Builds a tester object that tests if performing a operation on a T follows
* exception safety guarantees. Verification is done via contract assertion
* callbacks applied to T instances post-throw.
*
- * Template parameters for ExceptionSafetyTester:
+ * Template parameters for ExceptionSafetyTestBuilder:
*
* - Factory: The factory object (passed in via tester.WithFactory(...) or
* tester.WithInitialValue(...)) must be invocable with the signature
@@ -933,13 +955,13 @@
* please.
*/
template <typename Factory, typename Operation, typename... Contracts>
-class ExceptionSafetyTester {
+class ExceptionSafetyTestBuilder {
public:
/*
- * Returns a new ExceptionSafetyTester with an included T factory based on the
- * provided T instance. The existing factory will not be included in the newly
- * created tester instance. The created factory returns a new T instance by
- * copy-constructing the provided const T& t.
+ * Returns a new ExceptionSafetyTestBuilder with an included T factory based
+ * on the provided T instance. The existing factory will not be included in
+ * the newly created tester instance. The created factory returns a new T
+ * instance by copy-constructing the provided const T& t.
*
* Preconditions for tester.WithInitialValue(const T& t):
*
@@ -948,41 +970,41 @@
* tester.WithFactory(...).
*/
template <typename T>
- ExceptionSafetyTester<DefaultFactory<T>, Operation, Contracts...>
+ ExceptionSafetyTestBuilder<DefaultFactory<T>, Operation, Contracts...>
WithInitialValue(const T& t) const {
return WithFactory(DefaultFactory<T>(t));
}
/*
- * Returns a new ExceptionSafetyTester with the provided T factory included.
- * The existing factory will not be included in the newly-created tester
- * instance. This method is intended for use with types lacking a copy
+ * Returns a new ExceptionSafetyTestBuilder with the provided T factory
+ * included. The existing factory will not be included in the newly-created
+ * tester instance. This method is intended for use with types lacking a copy
* constructor. Types that can be copy-constructed should instead use the
* method tester.WithInitialValue(...).
*/
template <typename NewFactory>
- ExceptionSafetyTester<absl::decay_t<NewFactory>, Operation, Contracts...>
+ ExceptionSafetyTestBuilder<absl::decay_t<NewFactory>, Operation, Contracts...>
WithFactory(const NewFactory& new_factory) const {
return {new_factory, operation_, contracts_};
}
/*
- * Returns a new ExceptionSafetyTester with the provided testable operation
- * included. The existing operation will not be included in the newly created
- * tester.
+ * Returns a new ExceptionSafetyTestBuilder with the provided testable
+ * operation included. The existing operation will not be included in the
+ * newly created tester.
*/
template <typename NewOperation>
- ExceptionSafetyTester<Factory, absl::decay_t<NewOperation>, Contracts...>
+ ExceptionSafetyTestBuilder<Factory, absl::decay_t<NewOperation>, Contracts...>
WithOperation(const NewOperation& new_operation) const {
return {factory_, new_operation, contracts_};
}
/*
- * Returns a new ExceptionSafetyTester with the provided MoreContracts...
+ * Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts...
* combined with the Contracts... that were already included in the instance
* on which the method was called. Contracts... cannot be removed or replaced
- * once added to an ExceptionSafetyTester instance. A fresh object must be
- * created in order to get an empty Contracts... list.
+ * once added to an ExceptionSafetyTestBuilder instance. A fresh object must
+ * be created in order to get an empty Contracts... list.
*
* In addition to passing in custom contract assertion callbacks, this method
* accepts `testing::strong_guarantee` as an argument which checks T instances
@@ -991,8 +1013,8 @@
* properly rolled back.
*/
template <typename... MoreContracts>
- ExceptionSafetyTester<Factory, Operation, Contracts...,
- absl::decay_t<MoreContracts>...>
+ ExceptionSafetyTestBuilder<Factory, Operation, Contracts...,
+ absl::decay_t<MoreContracts>...>
WithContracts(const MoreContracts&... more_contracts) const {
return {
factory_, operation_,
@@ -1039,48 +1061,27 @@
typename LazyOperation = Operation,
typename = EnableIfTestable<sizeof...(Contracts), Factory, LazyOperation>>
testing::AssertionResult Test() const {
- return TestImpl(operation_, absl::index_sequence_for<Contracts...>());
+ return Test(operation_);
}
private:
template <typename, typename, typename...>
- friend class ExceptionSafetyTester;
+ friend class ExceptionSafetyTestBuilder;
- friend ExceptionSafetyTester<> testing::MakeExceptionSafetyTester();
+ friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester();
- ExceptionSafetyTester() {}
+ ExceptionSafetyTestBuilder() {}
- ExceptionSafetyTester(const Factory& f, const Operation& o,
- const std::tuple<Contracts...>& i)
+ ExceptionSafetyTestBuilder(const Factory& f, const Operation& o,
+ const std::tuple<Contracts...>& i)
: factory_(f), operation_(o), contracts_(i) {}
template <typename SelectedOperation, size_t... Indices>
- testing::AssertionResult TestImpl(const SelectedOperation& selected_operation,
+ testing::AssertionResult TestImpl(SelectedOperation selected_operation,
absl::index_sequence<Indices...>) const {
- // Starting from 0 and counting upwards until one of the exit conditions is
- // hit...
- for (int count = 0;; ++count) {
- exceptions_internal::ConstructorTracker ct(count);
-
- // Run the full exception safety test algorithm for the current countdown
- auto reduced_res =
- TestAllContractsAtCountdown(factory_, selected_operation, count,
- std::get<Indices>(contracts_)...);
- // If there is no value in the optional, no contracts were run because no
- // exception was thrown. This means that the test is complete and the loop
- // can exit successfully.
- if (!reduced_res.has_value()) {
- return testing::AssertionSuccess();
- }
- // If the optional is not empty and the value is falsy, an contract check
- // failed so the test must exit to propegate the failure.
- if (!reduced_res.value()) {
- return reduced_res.value();
- }
- // If the optional is not empty and the value is not falsy, it means
- // exceptions were thrown but the contracts passed so the test must
- // continue to run.
- }
+ return ExceptionSafetyTest<FactoryElementType<Factory>>(
+ factory_, selected_operation, std::get<Indices>(contracts_)...)
+ .Test();
}
Factory factory_;
@@ -1090,20 +1091,6 @@
} // namespace exceptions_internal
-/*
- * Constructs an empty ExceptionSafetyTester. All ExceptionSafetyTester
- * objects are immutable and all With[thing] mutation methods return new
- * instances of ExceptionSafetyTester.
- *
- * In order to test a T for exception safety, a factory for that T, a testable
- * operation, and at least one contract callback returning an assertion
- * result must be applied using the respective methods.
- */
-inline exceptions_internal::ExceptionSafetyTester<>
-MakeExceptionSafetyTester() {
- return {};
-}
-
} // namespace testing
#endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
diff --git a/absl/base/internal/exception_testing.h b/absl/base/internal/exception_testing.h
index 0cf7918..01b5465 100644
--- a/absl/base/internal/exception_testing.h
+++ b/absl/base/internal/exception_testing.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/hide_ptr.h b/absl/base/internal/hide_ptr.h
index 45cf438..cf8f408 100644
--- a/absl/base/internal/hide_ptr.h
+++ b/absl/base/internal/hide_ptr.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h
index a1a5d70..086447c 100644
--- a/absl/base/internal/identity.h
+++ b/absl/base/internal/identity.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/inline_variable.h b/absl/base/internal/inline_variable.h
index f7bb8c5..130d8c2 100644
--- a/absl/base/internal/inline_variable.h
+++ b/absl/base/internal/inline_variable.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/inline_variable_testing.h b/absl/base/internal/inline_variable_testing.h
index a0dd2bb..15dc481 100644
--- a/absl/base/internal/inline_variable_testing.h
+++ b/absl/base/internal/inline_variable_testing.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h
index 8c3f4f6..8da2869 100644
--- a/absl/base/internal/invoke.h
+++ b/absl/base/internal/invoke.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 159f945..e4030ed 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -208,7 +208,7 @@
int32_t allocation_count GUARDED_BY(mu);
// flags passed to NewArena
const uint32_t flags;
- // Result of getpagesize()
+ // Result of sysconf(_SC_PAGESIZE)
const size_t pagesize;
// Lowest power of two >= max(16, sizeof(AllocList))
const size_t roundup;
@@ -324,8 +324,10 @@
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
return std::max(system_info.dwPageSize, system_info.dwAllocationGranularity);
-#else
+#elif defined(__wasm__) || defined(__asmjs__)
return getpagesize();
+#else
+ return sysconf(_SC_PAGESIZE);
#endif
}
diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h
index fba9466..b35673d 100644
--- a/absl/base/internal/low_level_alloc.h
+++ b/absl/base/internal/low_level_alloc.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -119,4 +119,5 @@
} // namespace base_internal
} // namespace absl
+
#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_
diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc
index cf2b363..34a080c 100644
--- a/absl/base/internal/low_level_alloc_test.cc
+++ b/absl/base/internal/low_level_alloc_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -137,6 +137,7 @@
TEST_ASSERT(LowLevelAlloc::DeleteArena(arena));
}
}
+
// LowLevelAlloc is designed to be safe to call before main().
static struct BeforeMain {
BeforeMain() {
diff --git a/absl/base/internal/low_level_scheduling.h b/absl/base/internal/low_level_scheduling.h
index e716f2b..0fcc8d3 100644
--- a/absl/base/internal/low_level_scheduling.h
+++ b/absl/base/internal/low_level_scheduling.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -86,6 +86,7 @@
//------------------------------------------------------------------------------
// End of public interfaces.
//------------------------------------------------------------------------------
+
inline bool SchedulingGuard::ReschedulingIsAllowed() {
return false;
}
@@ -98,7 +99,7 @@
return;
}
-
} // namespace base_internal
} // namespace absl
+
#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_
diff --git a/absl/base/internal/per_thread_tls.h b/absl/base/internal/per_thread_tls.h
index 2428bdc..cf5e97a 100644
--- a/absl/base/internal/per_thread_tls.h
+++ b/absl/base/internal/per_thread_tls.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,13 +16,17 @@
#define ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_
// This header defines two macros:
+//
// If the platform supports thread-local storage:
-// ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a
-// thread-local variable ABSL_PER_THREAD_TLS is 1
+//
+// * ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a
+// thread-local variable
+// * ABSL_PER_THREAD_TLS is 1
//
// Otherwise:
-// ABSL_PER_THREAD_TLS_KEYWORD is empty
-// ABSL_PER_THREAD_TLS is 0
+//
+// * ABSL_PER_THREAD_TLS_KEYWORD is empty
+// * ABSL_PER_THREAD_TLS is 0
//
// Microsoft C supports thread-local storage.
// GCC supports it if the appropriate version of glibc is available,
diff --git a/absl/base/internal/pretty_function.h b/absl/base/internal/pretty_function.h
index 01b0547..35d5167 100644
--- a/absl/base/internal/pretty_function.h
+++ b/absl/base/internal/pretty_function.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index d9485a6..b5a05e8 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index 79a7bb9..6a4c093 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -38,6 +38,7 @@
// ABSL_RAW_LOG(ERROR, "Failed foo with %i: %s", status, error);
// This will print an almost standard log line like this to stderr only:
// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file
+
#define ABSL_RAW_LOG(severity, ...) \
do { \
constexpr const char* absl_raw_logging_internal_basename = \
@@ -79,13 +80,13 @@
absl_raw_logging_internal_basename, __LINE__, message); \
} while (0)
-#define ABSL_INTERNAL_CHECK(condition, message) \
- do { \
- if (ABSL_PREDICT_FALSE(!(condition))) { \
+#define ABSL_INTERNAL_CHECK(condition, message) \
+ do { \
+ if (ABSL_PREDICT_FALSE(!(condition))) { \
std::string death_message = "Check " #condition " failed: "; \
death_message += std::string(message); \
- ABSL_INTERNAL_LOG(FATAL, death_message); \
- } \
+ ABSL_INTERNAL_LOG(FATAL, death_message); \
+ } \
} while (0)
#define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo
diff --git a/absl/base/internal/scheduling_mode.h b/absl/base/internal/scheduling_mode.h
index 1b6497a..d5b4b7f 100644
--- a/absl/base/internal/scheduling_mode.h
+++ b/absl/base/internal/scheduling_mode.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/scoped_set_env.cc b/absl/base/internal/scoped_set_env.cc
new file mode 100644
index 0000000..3ac3f68
--- /dev/null
+++ b/absl/base/internal/scoped_set_env.cc
@@ -0,0 +1,79 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/scoped_set_env.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <cstdlib>
+
+#include "absl/base/internal/raw_logging.h"
+
+namespace absl {
+namespace base_internal {
+
+namespace {
+
+#ifdef _WIN32
+const int kMaxEnvVarValueSize = 1024;
+#endif
+
+void SetEnvVar(const char* name, const char* value) {
+#ifdef _WIN32
+ SetEnvironmentVariableA(name, value);
+#else
+ if (value == nullptr) {
+ ::unsetenv(name);
+ } else {
+ ::setenv(name, value, 1);
+ }
+#endif
+}
+
+} // namespace
+
+ScopedSetEnv::ScopedSetEnv(const char* var_name, const char* new_value)
+ : var_name_(var_name), was_unset_(false) {
+#ifdef _WIN32
+ char buf[kMaxEnvVarValueSize];
+ auto get_res = GetEnvironmentVariableA(var_name_.c_str(), buf, sizeof(buf));
+ ABSL_INTERNAL_CHECK(get_res < sizeof(buf), "value exceeds buffer size");
+
+ if (get_res == 0) {
+ was_unset_ = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
+ } else {
+ old_value_.assign(buf, get_res);
+ }
+
+ SetEnvironmentVariableA(var_name_.c_str(), new_value);
+#else
+ const char* val = ::getenv(var_name_.c_str());
+ if (val == nullptr) {
+ was_unset_ = true;
+ } else {
+ old_value_ = val;
+ }
+#endif
+
+ SetEnvVar(var_name_.c_str(), new_value);
+}
+
+ScopedSetEnv::~ScopedSetEnv() {
+ SetEnvVar(var_name_.c_str(), was_unset_ ? nullptr : old_value_.c_str());
+}
+
+} // namespace base_internal
+} // namespace absl
diff --git a/absl/base/internal/scoped_set_env.h b/absl/base/internal/scoped_set_env.h
new file mode 100644
index 0000000..855b22f
--- /dev/null
+++ b/absl/base/internal/scoped_set_env.h
@@ -0,0 +1,41 @@
+//
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_
+#define ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_
+
+#include <string>
+
+namespace absl {
+namespace base_internal {
+
+class ScopedSetEnv {
+ public:
+ ScopedSetEnv(const char* var_name, const char* new_value);
+ ~ScopedSetEnv();
+
+ private:
+ std::string var_name_;
+ std::string old_value_;
+
+ // True if the environment variable was initially not set.
+ bool was_unset_;
+};
+
+} // namespace base_internal
+} // namespace absl
+
+#endif // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_
diff --git a/absl/base/internal/scoped_set_env_test.cc b/absl/base/internal/scoped_set_env_test.cc
new file mode 100644
index 0000000..5cbad24
--- /dev/null
+++ b/absl/base/internal/scoped_set_env_test.cc
@@ -0,0 +1,99 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/scoped_set_env.h"
+
+namespace {
+
+using absl::base_internal::ScopedSetEnv;
+
+std::string GetEnvVar(const char* name) {
+#ifdef _WIN32
+ char buf[1024];
+ auto get_res = GetEnvironmentVariableA(name, buf, sizeof(buf));
+ if (get_res >= sizeof(buf)) {
+ return "TOO_BIG";
+ }
+
+ if (get_res == 0) {
+ return "UNSET";
+ }
+
+ return std::string(buf, get_res);
+#else
+ const char* val = ::getenv(name);
+ if (val == nullptr) {
+ return "UNSET";
+ }
+
+ return val;
+#endif
+}
+
+TEST(ScopedSetEnvTest, SetNonExistingVarToString) {
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
+
+ {
+ ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value");
+
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
+ }
+
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
+}
+
+TEST(ScopedSetEnvTest, SetNonExistingVarToNull) {
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
+
+ {
+ ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", nullptr);
+
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
+ }
+
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
+}
+
+TEST(ScopedSetEnvTest, SetExistingVarToString) {
+ ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value");
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
+
+ {
+ ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "new_value");
+
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "new_value");
+ }
+
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
+}
+
+TEST(ScopedSetEnvTest, SetExistingVarToNull) {
+ ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value");
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
+
+ {
+ ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", nullptr);
+
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
+ }
+
+ EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
+}
+
+} // namespace
diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc
index 1b97efb..7354438 100644
--- a/absl/base/internal/spinlock.cc
+++ b/absl/base/internal/spinlock.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -95,13 +95,9 @@
}
// Monitor the lock to see if its value changes within some time period
-// (adaptive_spin_count loop iterations). A timestamp indicating
-// when the thread initially started waiting for the lock is passed in via
-// the initial_wait_timestamp value. The total wait time in cycles for the
-// lock is returned in the wait_cycles parameter. The last value read
-// from the lock is returned from the method.
-uint32_t SpinLock::SpinLoop(int64_t initial_wait_timestamp,
- uint32_t *wait_cycles) {
+// (adaptive_spin_count loop iterations). The last value read from the lock
+// is returned from the method.
+uint32_t SpinLock::SpinLoop() {
// We are already in the slow path of SpinLock, initialize the
// adaptive_spin_count here.
ABSL_CONST_INIT static absl::once_flag init_adaptive_spin_count;
@@ -115,22 +111,21 @@
do {
lock_value = lockword_.load(std::memory_order_relaxed);
} while ((lock_value & kSpinLockHeld) != 0 && --c > 0);
- uint32_t spin_loop_wait_cycles =
- EncodeWaitCycles(initial_wait_timestamp, CycleClock::Now());
- *wait_cycles = spin_loop_wait_cycles;
-
- return TryLockInternal(lock_value, spin_loop_wait_cycles);
+ return lock_value;
}
void SpinLock::SlowLock() {
+ uint32_t lock_value = SpinLoop();
+ lock_value = TryLockInternal(lock_value, 0);
+ if ((lock_value & kSpinLockHeld) == 0) {
+ return;
+ }
// The lock was not obtained initially, so this thread needs to wait for
// it. Record the current timestamp in the local variable wait_start_time
// so the total wait time can be stored in the lockword once this thread
// obtains the lock.
int64_t wait_start_time = CycleClock::Now();
- uint32_t wait_cycles;
- uint32_t lock_value = SpinLoop(wait_start_time, &wait_cycles);
-
+ uint32_t wait_cycles = 0;
int lock_wait_call_count = 0;
while ((lock_value & kSpinLockHeld) != 0) {
// If the lock is currently held, but not marked as having a sleeper, mark
@@ -141,7 +136,7 @@
// owner to think it experienced contention.
if (lockword_.compare_exchange_strong(
lock_value, lock_value | kSpinLockSleeper,
- std::memory_order_acquire, std::memory_order_relaxed)) {
+ std::memory_order_relaxed, std::memory_order_relaxed)) {
// Successfully transitioned to kSpinLockSleeper. Pass
// kSpinLockSleeper to the SpinLockWait routine to properly indicate
// the last lock_value observed.
@@ -170,7 +165,9 @@
ABSL_TSAN_MUTEX_POST_DIVERT(this, 0);
// Spin again after returning from the wait routine to give this thread
// some chance of obtaining the lock.
- lock_value = SpinLoop(wait_start_time, &wait_cycles);
+ lock_value = SpinLoop();
+ wait_cycles = EncodeWaitCycles(wait_start_time, CycleClock::Now());
+ lock_value = TryLockInternal(lock_value, wait_cycles);
}
}
@@ -206,14 +203,20 @@
(wait_end_time - wait_start_time) >> PROFILE_TIMESTAMP_SHIFT;
// Return a representation of the time spent waiting that can be stored in
- // the lock word's upper bits. bit_cast is required as Atomic32 is signed.
- const uint32_t clamped = static_cast<uint32_t>(
+ // the lock word's upper bits.
+ uint32_t clamped = static_cast<uint32_t>(
std::min(scaled_wait_time, kMaxWaitTime) << LOCKWORD_RESERVED_SHIFT);
- // bump up value if necessary to avoid returning kSpinLockSleeper.
- const uint32_t after_spinlock_sleeper =
- kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT);
- return clamped == kSpinLockSleeper ? after_spinlock_sleeper : clamped;
+ if (clamped == 0) {
+ return kSpinLockSleeper; // Just wake waiters, but don't record contention.
+ }
+ // Bump up value if necessary to avoid returning kSpinLockSleeper.
+ const uint32_t kMinWaitTime =
+ kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT);
+ if (clamped == kSpinLockSleeper) {
+ return kMinWaitTime;
+ }
+ return clamped;
}
uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) {
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h
index 212abc6..4a31639 100644
--- a/absl/base/internal/spinlock.h
+++ b/absl/base/internal/spinlock.h
@@ -5,7 +5,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -101,8 +101,8 @@
inline void Unlock() UNLOCK_FUNCTION() {
ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0);
uint32_t lock_value = lockword_.load(std::memory_order_relaxed);
- lockword_.store(lock_value & kSpinLockCooperative,
- std::memory_order_release);
+ lock_value = lockword_.exchange(lock_value & kSpinLockCooperative,
+ std::memory_order_release);
if ((lock_value & kSpinLockDisabledScheduling) != 0) {
base_internal::SchedulingGuard::EnableRescheduling(true);
@@ -161,7 +161,7 @@
void InitLinkerInitializedAndCooperative();
void SlowLock() ABSL_ATTRIBUTE_COLD;
void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD;
- uint32_t SpinLoop(int64_t initial_wait_timestamp, uint32_t* wait_cycles);
+ uint32_t SpinLoop();
inline bool TryLockImpl() {
uint32_t lock_value = lockword_.load(std::memory_order_relaxed);
diff --git a/absl/base/internal/spinlock_akaros.inc b/absl/base/internal/spinlock_akaros.inc
index 051c8cf..bc46894 100644
--- a/absl/base/internal/spinlock_akaros.inc
+++ b/absl/base/internal/spinlock_akaros.inc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/spinlock_benchmark.cc b/absl/base/internal/spinlock_benchmark.cc
new file mode 100644
index 0000000..0451c65
--- /dev/null
+++ b/absl/base/internal/spinlock_benchmark.cc
@@ -0,0 +1,52 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// See also //absl/synchronization:mutex_benchmark for a comparison of SpinLock
+// and Mutex performance under varying levels of contention.
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/scheduling_mode.h"
+#include "absl/base/internal/spinlock.h"
+#include "absl/synchronization/internal/create_thread_identity.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+template <absl::base_internal::SchedulingMode scheduling_mode>
+static void BM_SpinLock(benchmark::State& state) {
+ // Ensure a ThreadIdentity is installed.
+ ABSL_INTERNAL_CHECK(
+ absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() !=
+ nullptr,
+ "GetOrCreateCurrentThreadIdentity() failed");
+
+ static auto* spinlock = new absl::base_internal::SpinLock(scheduling_mode);
+ for (auto _ : state) {
+ absl::base_internal::SpinLockHolder holder(spinlock);
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_SpinLock,
+ absl::base_internal::SCHEDULE_KERNEL_ONLY)
+ ->UseRealTime()
+ ->Threads(1)
+ ->ThreadPerCpu();
+
+BENCHMARK_TEMPLATE(BM_SpinLock,
+ absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL)
+ ->UseRealTime()
+ ->Threads(1)
+ ->ThreadPerCpu();
+
+} // namespace
diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc
index 94c861d..28e29d1 100644
--- a/absl/base/internal/spinlock_linux.inc
+++ b/absl/base/internal/spinlock_linux.inc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -51,17 +51,12 @@
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
std::atomic<uint32_t> *w, uint32_t value, int loop,
absl::base_internal::SchedulingMode) {
- if (loop != 0) {
- int save_errno = errno;
- struct timespec tm;
- tm.tv_sec = 0;
- // Increase the delay; we expect (but do not rely on) explicit wakeups.
- // We don't rely on explicit wakeups because we intentionally allow for
- // a race on the kSpinLockSleeper bit.
- tm.tv_nsec = 16 * absl::base_internal::SpinLockSuggestedDelayNS(loop);
- syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
- errno = save_errno;
- }
+ int save_errno = errno;
+ struct timespec tm;
+ tm.tv_sec = 0;
+ tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop);
+ syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
+ errno = save_errno;
}
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w,
diff --git a/absl/base/internal/spinlock_posix.inc b/absl/base/internal/spinlock_posix.inc
index 0098c1c..f025b5f 100644
--- a/absl/base/internal/spinlock_posix.inc
+++ b/absl/base/internal/spinlock_posix.inc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/spinlock_wait.cc b/absl/base/internal/spinlock_wait.cc
index 0fde9c0..fac8a21 100644
--- a/absl/base/internal/spinlock_wait.cc
+++ b/absl/base/internal/spinlock_wait.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -38,14 +38,15 @@
uint32_t SpinLockWait(std::atomic<uint32_t> *w, int n,
const SpinLockWaitTransition trans[],
base_internal::SchedulingMode scheduling_mode) {
- for (int loop = 0; ; loop++) {
+ int loop = 0;
+ for (;;) {
uint32_t v = w->load(std::memory_order_acquire);
int i;
for (i = 0; i != n && v != trans[i].from; i++) {
}
if (i == n) {
- SpinLockDelay(w, v, loop, scheduling_mode); // no matching transition
- } else if (trans[i].to == v || // null transition
+ SpinLockDelay(w, v, ++loop, scheduling_mode); // no matching transition
+ } else if (trans[i].to == v || // null transition
w->compare_exchange_strong(v, trans[i].to,
std::memory_order_acquire,
std::memory_order_relaxed)) {
@@ -64,17 +65,14 @@
r = 0x5deece66dLL * r + 0xb; // numbers from nrand48()
delay_rand.store(r, std::memory_order_relaxed);
- r <<= 16; // 48-bit random number now in top 48-bits.
if (loop < 0 || loop > 32) { // limit loop to 0..32
loop = 32;
}
- // loop>>3 cannot exceed 4 because loop cannot exceed 32.
- // Select top 20..24 bits of lower 48 bits,
- // giving approximately 0ms to 16ms.
- // Mean is exponential in loop for first 32 iterations, then 8ms.
- // The futex path multiplies this by 16, since we expect explicit wakeups
- // almost always on that path.
- return static_cast<int>(r >> (44 - (loop >> 3)));
+ const int kMinDelay = 128 << 10; // 128us
+ // Double delay every 8 iterations, up to 16x (2ms).
+ int delay = kMinDelay << (loop / 8);
+ // Randomize in delay..2*delay range, for resulting 128us..4ms range.
+ return delay | ((delay - 1) & static_cast<int>(r));
}
} // namespace base_internal
diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h
index 5c6cc7f..6642ce1 100644
--- a/absl/base/internal/spinlock_wait.h
+++ b/absl/base/internal/spinlock_wait.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/spinlock_win32.inc b/absl/base/internal/spinlock_win32.inc
index 32c8fc0..78654b5 100644
--- a/absl/base/internal/spinlock_win32.inc
+++ b/absl/base/internal/spinlock_win32.inc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc
index db41bac..4dd3add 100644
--- a/absl/base/internal/sysinfo.cc
+++ b/absl/base/internal/sysinfo.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h
index 5bd1c50..b864a59 100644
--- a/absl/base/internal/sysinfo.h
+++ b/absl/base/internal/sysinfo.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/sysinfo_test.cc b/absl/base/internal/sysinfo_test.cc
index e0d9aab..247f3d8 100644
--- a/absl/base/internal/sysinfo_test.cc
+++ b/absl/base/internal/sysinfo_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc
index cff9c1b..91273a6 100644
--- a/absl/base/internal/thread_identity.cc
+++ b/absl/base/internal/thread_identity.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h
index a51722f..b34674a 100644
--- a/absl/base/internal/thread_identity.h
+++ b/absl/base/internal/thread_identity.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -42,9 +42,9 @@
class SpinLock;
struct ThreadIdentity;
-// Used by the implementation of base::Mutex and base::CondVar.
+// Used by the implementation of absl::Mutex and absl::CondVar.
struct PerThreadSynch {
- // The internal representation of base::Mutex and base::CondVar rely
+ // The internal representation of absl::Mutex and absl::CondVar rely
// on the alignment of PerThreadSynch. Both store the address of the
// PerThreadSynch in the high-order bits of their internal state,
// which means the low kLowZeroBits of the address of PerThreadSynch
@@ -237,4 +237,5 @@
} // namespace base_internal
} // namespace absl
+
#endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_
diff --git a/absl/base/internal/thread_identity_benchmark.cc b/absl/base/internal/thread_identity_benchmark.cc
index 242522b..0ae10f2 100644
--- a/absl/base/internal/thread_identity_benchmark.cc
+++ b/absl/base/internal/thread_identity_benchmark.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc
index ecb8af6..13bfbe3 100644
--- a/absl/base/internal/thread_identity_test.cc
+++ b/absl/base/internal/thread_identity_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc
index 46dc573..8e928b8 100644
--- a/absl/base/internal/throw_delegate.cc
+++ b/absl/base/internal/throw_delegate.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -30,8 +30,8 @@
#ifdef ABSL_HAVE_EXCEPTIONS
throw error;
#else
- ABSL_RAW_LOG(ERROR, "%s", error.what());
- abort();
+ ABSL_RAW_LOG(FATAL, "%s", error.what());
+ std::abort();
#endif
}
} // namespace
diff --git a/absl/base/internal/throw_delegate.h b/absl/base/internal/throw_delegate.h
index 70e2d77..03c700b 100644
--- a/absl/base/internal/throw_delegate.h
+++ b/absl/base/internal/throw_delegate.h
@@ -5,7 +5,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/tsan_mutex_interface.h b/absl/base/internal/tsan_mutex_interface.h
index 6bb4fae..2a51060 100644
--- a/absl/base/internal/tsan_mutex_interface.h
+++ b/absl/base/internal/tsan_mutex_interface.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h
index f9df3b7..2d66737 100644
--- a/absl/base/internal/unaligned_access.h
+++ b/absl/base/internal/unaligned_access.h
@@ -5,7 +5,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc
index a12d68b..593762b 100644
--- a/absl/base/internal/unscaledcycleclock.cc
+++ b/absl/base/internal/unscaledcycleclock.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h
index 049f1ca..58950cc 100644
--- a/absl/base/internal/unscaledcycleclock.h
+++ b/absl/base/internal/unscaledcycleclock.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -83,6 +83,7 @@
defined(_M_IX86) || defined(_M_X64))
#define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
#endif
+
namespace absl {
namespace time_internal {
class UnscaledCycleClockWrapperForGetCurrentTime;
@@ -114,6 +115,7 @@
} // namespace base_internal
} // namespace absl
+
#endif // ABSL_USE_UNSCALED_CYCLECLOCK
#endif // ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_
diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc
index 466bf11..691f553 100644
--- a/absl/base/invoke_test.cc
+++ b/absl/base/invoke_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h
index 5770d36..b19a7ff 100644
--- a/absl/base/log_severity.h
+++ b/absl/base/log_severity.h
@@ -4,14 +4,13 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT 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_LOG_SEVERITY_H_
#define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
diff --git a/absl/base/macros.h b/absl/base/macros.h
index ca3d5ed..5b43d7c 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -5,7 +5,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -24,7 +24,6 @@
// This code is compiled directly on many platforms, including client
// platforms like Windows, Mac, and embedded systems. Before making
// any changes here, make sure that you're not breaking any platforms.
-//
#ifndef ABSL_BASE_MACROS_H_
#define ABSL_BASE_MACROS_H_
@@ -199,4 +198,14 @@
: [] { assert(false && #expr); }()) // NOLINT
#endif
+#ifdef ABSL_HAVE_EXCEPTIONS
+#define ABSL_INTERNAL_TRY try
+#define ABSL_INTERNAL_CATCH_ANY catch (...)
+#define ABSL_INTERNAL_RETHROW do { throw; } while (false)
+#else // ABSL_HAVE_EXCEPTIONS
+#define ABSL_INTERNAL_TRY if (true)
+#define ABSL_INTERNAL_CATCH_ANY else if (false)
+#define ABSL_INTERNAL_RETHROW do {} while (false)
+#endif // ABSL_HAVE_EXCEPTIONS
+
#endif // ABSL_BASE_MACROS_H_
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index 2fddfc8..6974f1f 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -5,7 +5,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -111,9 +111,9 @@
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html
// for more information.
//
-// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to
-// `__attribute__((aligned(ABSL_CACHELINE_SIZE)))`. For compilers where this is
-// not known to work, the macro expands to nothing.
+// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__`
+// or `__declspec` attribute. For compilers where this is not known to work,
+// the macro expands to nothing.
//
// No further guarantees are made here. The result of applying the macro
// to variables and types is always implementation-defined.
@@ -122,6 +122,14 @@
// of causing bugs that are difficult to diagnose, crash, etc. It does not
// of itself guarantee that objects are aligned to a cache line.
//
+// NOTE: Some compilers are picky about the locations of annotations such as
+// this attribute, so prefer to put it at the beginning of your declaration.
+// For example,
+//
+// ABSL_CACHELINE_ALIGNED static Foo* foo = ...
+//
+// class ABSL_CACHELINE_ALIGNED Bar { ...
+//
// Recommendations:
//
// 1) Consult compiler documentation; this comment is not kept in sync as
@@ -131,8 +139,10 @@
// 3) Prefer applying this attribute to individual variables. Avoid
// applying it to types. This tends to localize the effect.
#define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE)))
-
-#else // not GCC
+#elif defined(_MSC_VER)
+#define ABSL_CACHELINE_SIZE 64
+#define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE))
+#else
#define ABSL_CACHELINE_SIZE 64
#define ABSL_CACHELINE_ALIGNED
#endif
diff --git a/absl/base/policy_checks.h b/absl/base/policy_checks.h
index 0a07fc0..699fb1a 100644
--- a/absl/base/policy_checks.h
+++ b/absl/base/policy_checks.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/port.h b/absl/base/port.h
index 1c67257..6c28068 100644
--- a/absl/base/port.h
+++ b/absl/base/port.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/raw_logging_test.cc b/absl/base/raw_logging_test.cc
index b21cf65..3d30bd3 100644
--- a/absl/base/raw_logging_test.cc
+++ b/absl/base/raw_logging_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc
index 1b50884..e62b2ea 100644
--- a/absl/base/spinlock_test_common.cc
+++ b/absl/base/spinlock_test_common.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -54,6 +54,7 @@
static constexpr int kArrayLength = 10;
static uint32_t values[kArrayLength];
+
static SpinLock static_spinlock(base_internal::kLinkerInitialized);
static SpinLock static_cooperative_spinlock(
base_internal::kLinkerInitialized,
@@ -155,7 +156,8 @@
// Test corner cases
int64_t start_time = time_distribution(generator);
- EXPECT_EQ(0, SpinLockTest::EncodeWaitCycles(start_time, start_time));
+ EXPECT_EQ(kSpinLockSleeper,
+ SpinLockTest::EncodeWaitCycles(start_time, start_time));
EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(0));
EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(kLockwordReservedMask));
EXPECT_EQ(kMaxCycles & ~kProfileTimestampMask,
@@ -188,6 +190,7 @@
SpinLockTest::DecodeWaitCycles(before_max_value);
EXPECT_GT(expected_max_value_decoded, before_max_value_decoded);
}
+
TEST(SpinLockWithThreads, StaticSpinLock) {
ThreadedTest(&static_spinlock);
}
diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h
index fbb2797..0b2c306 100644
--- a/absl/base/thread_annotations.h
+++ b/absl/base/thread_annotations.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -21,7 +21,6 @@
// code. The annotations can also help program analysis tools to identify
// potential thread safety issues.
//
-//
// These annotations are implemented using compiler attributes. Using the macros
// defined here instead of raw attributes allow for portability and future
// compatibility.
@@ -34,6 +33,7 @@
#ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_
#define ABSL_BASE_THREAD_ANNOTATIONS_H_
+
#if defined(__clang__)
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
@@ -108,13 +108,23 @@
// The mutex is expected to be held both on entry to, and exit from, the
// function.
//
+// An exclusive lock allows read-write access to the guarded data member(s), and
+// only one thread can acquire a lock exclusively at any one time. A shared lock
+// allows read-only access, and any number of threads can acquire a shared lock
+// concurrently.
+//
+// Generally, non-const methods should be annotated with
+// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with
+// SHARED_LOCKS_REQUIRED.
+//
// Example:
//
// Mutex mu1, mu2;
// int a GUARDED_BY(mu1);
// int b GUARDED_BY(mu2);
//
-// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... };
+// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
+// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
diff --git a/absl/base/throw_delegate_test.cc b/absl/base/throw_delegate_test.cc
index 0f15df0..a74dd3c 100644
--- a/absl/base/throw_delegate_test.cc
+++ b/absl/base/throw_delegate_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/compiler_config_setting.bzl b/absl/compiler_config_setting.bzl
index b77c4f5..6696229 100644
--- a/absl/compiler_config_setting.bzl
+++ b/absl/compiler_config_setting.bzl
@@ -5,35 +5,34 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT 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,
- )
+ # 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,
+ )
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index f2210e3..cd914ba 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,11 +15,11 @@
#
load(
- "//absl:copts.bzl",
+ "//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
- "ABSL_TEST_COPTS",
"ABSL_EXCEPTIONS_FLAG",
"ABSL_EXCEPTIONS_FLAG_LINKOPTS",
+ "ABSL_TEST_COPTS",
)
package(default_visibility = ["//visibility:public"])
@@ -41,6 +41,8 @@
copts = ABSL_TEST_COPTS,
deps = [
":compressed_tuple",
+ "//absl/memory",
+ "//absl/utility",
"@com_google_googletest//:gtest_main",
],
)
@@ -110,10 +112,20 @@
)
cc_library(
+ name = "inlined_vector_internal",
+ hdrs = ["internal/inlined_vector.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/meta:type_traits",
+ ],
+)
+
+cc_library(
name = "inlined_vector",
hdrs = ["inlined_vector.h"],
copts = ABSL_DEFAULT_COPTS,
deps = [
+ ":inlined_vector_internal",
"//absl/algorithm",
"//absl/base:core_headers",
"//absl/base:throw_delegate",
@@ -121,12 +133,21 @@
],
)
+cc_library(
+ name = "counting_allocator",
+ testonly = 1,
+ hdrs = ["internal/counting_allocator.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
+)
+
cc_test(
name = "inlined_vector_test",
srcs = ["inlined_vector_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
+ ":counting_allocator",
":inlined_vector",
":test_instance_tracker",
"//absl/base",
@@ -144,6 +165,7 @@
srcs = ["inlined_vector_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
+ ":counting_allocator",
":inlined_vector",
":test_instance_tracker",
"//absl/base",
@@ -212,6 +234,7 @@
":container_memory",
":hash_function_defaults",
":raw_hash_map",
+ "//absl/algorithm:container",
"//absl/memory",
],
)
@@ -219,13 +242,14 @@
cc_test(
name = "flat_hash_map_test",
srcs = ["flat_hash_map_test.cc"],
- copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"],
+ copts = ABSL_TEST_COPTS,
tags = NOTEST_TAGS_NONMOBILE,
deps = [
":flat_hash_map",
":hash_generator_testing",
":unordered_map_constructor_test",
":unordered_map_lookup_test",
+ ":unordered_map_members_test",
":unordered_map_modifiers_test",
"//absl/types:any",
"@com_google_googletest//:gtest_main",
@@ -240,6 +264,7 @@
":container_memory",
":hash_function_defaults",
":raw_hash_set",
+ "//absl/algorithm:container",
"//absl/base:core_headers",
"//absl/memory",
],
@@ -255,6 +280,7 @@
":hash_generator_testing",
":unordered_set_constructor_test",
":unordered_set_lookup_test",
+ ":unordered_set_members_test",
":unordered_set_modifiers_test",
"//absl/memory",
"//absl/strings",
@@ -271,6 +297,7 @@
":hash_function_defaults",
":node_hash_policy",
":raw_hash_map",
+ "//absl/algorithm:container",
"//absl/memory",
],
)
@@ -278,7 +305,7 @@
cc_test(
name = "node_hash_map_test",
srcs = ["node_hash_map_test.cc"],
- copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"],
+ copts = ABSL_TEST_COPTS,
tags = NOTEST_TAGS_NONMOBILE,
deps = [
":hash_generator_testing",
@@ -286,6 +313,7 @@
":tracked",
":unordered_map_constructor_test",
":unordered_map_lookup_test",
+ ":unordered_map_members_test",
":unordered_map_modifiers_test",
"@com_google_googletest//:gtest_main",
],
@@ -296,10 +324,10 @@
hdrs = ["node_hash_set.h"],
copts = ABSL_DEFAULT_COPTS,
deps = [
- ":container_memory",
":hash_function_defaults",
":node_hash_policy",
":raw_hash_set",
+ "//absl/algorithm:container",
"//absl/memory",
],
)
@@ -310,10 +338,10 @@
copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
tags = NOTEST_TAGS_NONMOBILE,
deps = [
- ":hash_generator_testing",
":node_hash_set",
":unordered_set_constructor_test",
":unordered_set_lookup_test",
+ ":unordered_set_members_test",
":unordered_set_modifiers_test",
"@com_google_googletest//:gtest_main",
],
@@ -432,6 +460,39 @@
)
cc_library(
+ name = "hashtablez_sampler",
+ srcs = [
+ "internal/hashtablez_sampler.cc",
+ "internal/hashtablez_sampler_force_weak_definition.cc",
+ ],
+ hdrs = ["internal/hashtablez_sampler.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":have_sse",
+ "//absl/base",
+ "//absl/base:core_headers",
+ "//absl/debugging:stacktrace",
+ "//absl/memory",
+ "//absl/synchronization",
+ "//absl/utility",
+ ],
+)
+
+cc_test(
+ name = "hashtablez_sampler_test",
+ srcs = ["internal/hashtablez_sampler_test.cc"],
+ deps = [
+ ":hashtablez_sampler",
+ ":have_sse",
+ "//absl/base:core_headers",
+ "//absl/synchronization",
+ "//absl/synchronization:thread_pool",
+ "//absl/time",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
name = "node_hash_policy",
hdrs = ["internal/node_hash_policy.h"],
copts = ABSL_DEFAULT_COPTS,
@@ -459,15 +520,35 @@
)
cc_library(
+ name = "have_sse",
+ hdrs = ["internal/have_sse.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
+)
+
+cc_library(
+ name = "common",
+ hdrs = ["internal/common.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/meta:type_traits",
+ "//absl/types:optional",
+ ],
+)
+
+cc_library(
name = "raw_hash_set",
srcs = ["internal/raw_hash_set.cc"],
hdrs = ["internal/raw_hash_set.h"],
copts = ABSL_DEFAULT_COPTS,
deps = [
+ ":common",
":compressed_tuple",
":container_memory",
":hash_policy_traits",
":hashtable_debug_hooks",
+ ":hashtablez_sampler",
+ ":have_sse",
":layout",
"//absl/base:bits",
"//absl/base:config",
@@ -475,7 +556,6 @@
"//absl/base:endian",
"//absl/memory",
"//absl/meta:type_traits",
- "//absl/types:optional",
"//absl/utility",
],
)
@@ -592,6 +672,29 @@
deps = [
":hash_generator_testing",
":hash_policy_testing",
+ "//absl/meta:type_traits",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "unordered_set_members_test",
+ testonly = 1,
+ hdrs = ["internal/unordered_set_members_test.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ "//absl/meta:type_traits",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "unordered_map_members_test",
+ testonly = 1,
+ hdrs = ["internal/unordered_map_members_test.h"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ "//absl/meta:type_traits",
"@com_google_googletest//:gtest",
],
)
@@ -628,6 +731,7 @@
deps = [
":unordered_set_constructor_test",
":unordered_set_lookup_test",
+ ":unordered_set_members_test",
":unordered_set_modifiers_test",
"@com_google_googletest//:gtest_main",
],
@@ -641,6 +745,7 @@
deps = [
":unordered_map_constructor_test",
":unordered_map_lookup_test",
+ ":unordered_map_members_test",
":unordered_map_modifiers_test",
"@com_google_googletest//:gtest_main",
],
diff --git a/absl/container/BUILD.gn b/absl/container/BUILD.gn
index a2fbd54..3b8ece7 100644
--- a/absl/container/BUILD.gn
+++ b/absl/container/BUILD.gn
@@ -49,6 +49,21 @@
]
}
+source_set("inlined_vector_internal") {
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [
+ "//build/config/compiler:no_chromium_code",
+ "//third_party/abseil-cpp:absl_default_cflags_cc",
+ ]
+ public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
+ public = [
+ "internal/inlined_vector.h",
+ ]
+ deps = [
+ "../meta:type_traits",
+ ]
+}
+
source_set("inlined_vector") {
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
@@ -60,6 +75,7 @@
"inlined_vector.h",
]
deps = [
+ ":inlined_vector_internal",
"../algorithm",
"../base:core_headers",
"../base:throw_delegate",
@@ -67,6 +83,21 @@
]
}
+source_set("counting_allocator") {
+ testonly = true
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [
+ "//build/config/compiler:no_chromium_code",
+ "//third_party/abseil-cpp:absl_default_cflags_cc",
+ ]
+ public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
+ public = [
+ "internal/counting_allocator.h",
+ ]
+ visibility = []
+ visibility += [ ":*" ]
+}
+
source_set("test_instance_tracker") {
testonly = true
configs -= [ "//build/config/compiler:chromium_code" ]
@@ -99,6 +130,7 @@
":container_memory",
":hash_function_defaults",
":raw_hash_map",
+ "../algorithm:container",
"../memory",
]
}
@@ -117,6 +149,7 @@
":container_memory",
":hash_function_defaults",
":raw_hash_set",
+ "../algorithm:container",
"../base:core_headers",
"../memory",
]
@@ -137,6 +170,7 @@
":hash_function_defaults",
":node_hash_policy",
":raw_hash_map",
+ "../algorithm:container",
"../memory",
]
}
@@ -156,6 +190,7 @@
":hash_function_defaults",
":node_hash_policy",
":raw_hash_set",
+ "../algorithm:container",
"../memory",
]
}
@@ -273,6 +308,31 @@
]
}
+source_set("hashtablez_sampler") {
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [
+ "//build/config/compiler:no_chromium_code",
+ "//third_party/abseil-cpp:absl_default_cflags_cc",
+ ]
+ public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
+ public = [
+ "internal/hashtablez_sampler.h",
+ ]
+ sources = [
+ "internal/hashtablez_sampler.cc",
+ "internal/hashtablez_sampler_force_weak_definition.cc",
+ ]
+ deps = [
+ ":have_sse",
+ "../base",
+ "../base:core_headers",
+ "../debugging:stacktrace",
+ "../memory",
+ "../synchronization",
+ "../utility",
+ ]
+}
+
source_set("node_hash_policy") {
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
@@ -301,6 +361,36 @@
]
}
+source_set("have_sse") {
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [
+ "//build/config/compiler:no_chromium_code",
+ "//third_party/abseil-cpp:absl_default_cflags_cc",
+ ]
+ public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
+ public = [
+ "internal/have_sse.h",
+ ]
+ visibility = []
+ visibility += [ ":*" ]
+}
+
+source_set("common") {
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [
+ "//build/config/compiler:no_chromium_code",
+ "//third_party/abseil-cpp:absl_default_cflags_cc",
+ ]
+ public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
+ public = [
+ "internal/common.h",
+ ]
+ deps = [
+ "../meta:type_traits",
+ "../types:optional",
+ ]
+}
+
source_set("raw_hash_set") {
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
@@ -315,10 +405,13 @@
"internal/raw_hash_set.h",
]
deps = [
+ ":common",
":compressed_tuple",
":container_memory",
":hash_policy_traits",
":hashtable_debug_hooks",
+ ":hashtablez_sampler",
+ ":have_sse",
":layout",
"../base:bits",
"../base:config",
@@ -326,7 +419,6 @@
"../base:endian",
"../memory",
"../meta:type_traits",
- "../types:optional",
"../utility",
]
}
@@ -431,6 +523,41 @@
deps = [
":hash_generator_testing",
":hash_policy_testing",
+ "../meta:type_traits",
+ "//testing/gtest",
+ ]
+}
+
+source_set("unordered_set_members_test") {
+ testonly = true
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [
+ "//build/config/compiler:no_chromium_code",
+ "//third_party/abseil-cpp:absl_default_cflags_cc",
+ ]
+ public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
+ public = [
+ "internal/unordered_set_members_test.h",
+ ]
+ deps = [
+ "../meta:type_traits",
+ "//testing/gtest",
+ ]
+}
+
+source_set("unordered_map_members_test") {
+ testonly = true
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [
+ "//build/config/compiler:no_chromium_code",
+ "//third_party/abseil-cpp:absl_default_cflags_cc",
+ ]
+ public_configs = [ "//third_party/abseil-cpp:absl_include_config" ]
+ public = [
+ "internal/unordered_map_members_test.h",
+ ]
+ deps = [
+ "../meta:type_traits",
"//testing/gtest",
]
}
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 9e40690..292fea2 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,163 +14,772 @@
# limitations under the License.
#
-
-list(APPEND CONTAINER_PUBLIC_HEADERS
- "fixed_array.h"
- "flat_hash_map.h"
- "flat_hash_set.h"
- "inlined_vector.h"
- "node_hash_map.h"
- "node_hash_set.h"
-)
-
-
-list(APPEND CONTAINER_INTERNAL_HEADERS
- "internal/compressed_tuple.h"
- "internal/container_memory.h"
- "internal/hash_function_defaults.h"
- "internal/hash_generator_testing.h"
- "internal/hash_policy_testing.h"
- "internal/hash_policy_traits.h"
- "internal/hashtable_debug.h"
- "internal/layout.h"
- "internal/node_hash_policy.h"
- "internal/raw_hash_map.h"
- "internal/raw_hash_set.h"
- "internal/test_instance_tracker.h"
- "internal/tracked.h"
- "internal/unordered_map_constructor_test.h"
- "internal/unordered_map_lookup_test.h"
- "internal/unordered_map_modifiers_test.h"
- "internal/unordered_set_constructor_test.h"
- "internal/unordered_set_lookup_test.h"
- "internal/unordered_set_modifiers_test.h"
-)
-
-
-absl_library(
- TARGET
- absl_container
- SOURCES
- "internal/raw_hash_set.cc"
- EXPORT_NAME
+# 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
)
-
-#
-## TESTS
-#
-
-list(APPEND TEST_INSTANCE_TRACKER_LIB_SRC
- "internal/test_instance_tracker.cc"
- ${CONTAINER_PUBLIC_HEADERS}
- ${CONTAINER_INTERNAL_HEADERS}
+absl_cc_library(
+ NAME
+ compressed_tuple
+ HDRS
+ "internal/compressed_tuple.h"
+ DEPS
+ absl::utility
+ PUBLIC
)
-
-absl_library(
- TARGET
- test_instance_tracker_lib
- SOURCES
- ${TEST_INSTANCE_TRACKER_LIB_SRC}
- PUBLIC_LIBRARIES
- absl::container
+absl_cc_test(
+ NAME
+ compressed_tuple_test
+ SRCS
+ "internal/compressed_tuple_test.cc"
+ DEPS
+ absl::compressed_tuple
+ absl::memory
+ absl::utility
+ gmock_main
)
+absl_cc_library(
+ NAME
+ fixed_array
+ HDRS
+ "fixed_array.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::compressed_tuple
+ absl::algorithm
+ absl::core_headers
+ absl::dynamic_annotations
+ absl::throw_delegate
+ absl::memory
+ PUBLIC
+)
-
-# test fixed_array_test
-set(FIXED_ARRAY_TEST_SRC "fixed_array_test.cc")
-set(FIXED_ARRAY_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
fixed_array_test
- SOURCES
- ${FIXED_ARRAY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${FIXED_ARRAY_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
+ SRCS
+ "fixed_array_test.cc"
+ COPTS
${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::fixed_array
+ absl::exception_testing
+ absl::hash_testing
+ absl::memory
+ gmock_main
)
-
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
fixed_array_test_noexceptions
- SOURCES
- ${FIXED_ARRAY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${FIXED_ARRAY_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "fixed_array_test.cc"
+ DEPS
+ absl::fixed_array
+ absl::exception_testing
+ absl::hash_testing
+ absl::memory
+ gmock_main
)
-
-# test fixed_array_exception_safety_test
-set(FIXED_ARRAY_EXCEPTION_SAFETY_TEST_SRC "fixed_array_exception_safety_test.cc")
-set(FIXED_ARRAY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
- absl::container
- absl_base_internal_exception_safety_testing
-)
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
fixed_array_exception_safety_test
- SOURCES
- ${FIXED_ARRAY_EXCEPTION_SAFETY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${FIXED_ARRAY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
+ SRCS
+ "fixed_array_exception_safety_test.cc"
+ COPTS
${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::fixed_array
+ absl::exception_safety_testing
+ gmock_main
)
+absl_cc_library(
+ NAME
+ inlined_vector_internal
+ HDRS
+ "internal/inlined_vector.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::type_traits
+ PUBLIC
+)
-# test inlined_vector_test
-set(INLINED_VECTOR_TEST_SRC "inlined_vector_test.cc")
-set(INLINED_VECTOR_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib)
+absl_cc_library(
+ NAME
+ inlined_vector
+ HDRS
+ "inlined_vector.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::algorithm
+ absl::core_headers
+ absl::throw_delegate
+ absl::memory
+ PUBLIC
+)
-absl_test(
- TARGET
+absl_cc_library(
+ NAME
+ counting_allocator
+ HDRS
+ "internal/counting_allocator.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+)
+
+absl_cc_test(
+ NAME
inlined_vector_test
- SOURCES
- ${INLINED_VECTOR_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INLINED_VECTOR_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "inlined_vector_test.cc"
+ COPTS
+ ${ABSL_EXCEPTIONS_FLAG}
+ LINKOPTS
+ ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
+ DEPS
+ absl::counting_allocator
+ absl::inlined_vector
+ absl::test_instance_tracker
+ absl::base
+ absl::core_headers
+ absl::exception_testing
+ absl::hash_testing
+ absl::memory
+ absl::strings
+ gmock_main
)
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
inlined_vector_test_noexceptions
- SOURCES
- ${INLINED_VECTOR_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INLINED_VECTOR_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_NOEXCEPTION_CXXFLAGS}
+ SRCS
+ "inlined_vector_test.cc"
+ DEPS
+ absl::inlined_vector
+ absl::test_instance_tracker
+ absl::base
+ absl::core_headers
+ absl::exception_testing
+ absl::hash_testing
+ absl::memory
+ absl::strings
+ gmock_main
)
+absl_cc_library(
+ NAME
+ test_instance_tracker
+ HDRS
+ "internal/test_instance_tracker.h"
+ SRCS
+ "internal/test_instance_tracker.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ TESTONLY
+)
-# test test_instance_tracker_test
-set(TEST_INSTANCE_TRACKER_TEST_SRC "internal/test_instance_tracker_test.cc")
-set(TEST_INSTANCE_TRACKER_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib)
-
-
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
test_instance_tracker_test
- SOURCES
- ${TEST_INSTANCE_TRACKER_TEST_SRC}
- PUBLIC_LIBRARIES
- ${TEST_INSTANCE_TRACKER_TEST_PUBLIC_LIBRARIES}
+ SRCS
+ "internal/test_instance_tracker_test.cc"
+ DEPS
+ absl::test_instance_tracker
+ gmock_main
)
+absl_cc_library(
+ NAME
+ flat_hash_map
+ HDRS
+ "flat_hash_map.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::container_memory
+ absl::hash_function_defaults
+ absl::raw_hash_map
+ absl::algorithm_container
+ absl::memory
+ PUBLIC
+)
-absl_test(
- TARGET
+absl_cc_test(
+ NAME
+ flat_hash_map_test
+ SRCS
+ "flat_hash_map_test.cc"
+ DEPS
+ absl::flat_hash_map
+ absl::hash_generator_testing
+ absl::unordered_map_constructor_test
+ absl::unordered_map_lookup_test
+ absl::unordered_map_members_test
+ absl::unordered_map_modifiers_test
+ absl::any
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ flat_hash_set
+ HDRS
+ "flat_hash_set.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::container_memory
+ absl::hash_function_defaults
+ absl::raw_hash_set
+ absl::algorithm_container
+ absl::core_headers
+ absl::memory
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ flat_hash_set_test
+ SRCS
+ "flat_hash_set_test.cc"
+ COPTS
+ "-DUNORDERED_SET_CXX17"
+ DEPS
+ absl::flat_hash_set
+ absl::hash_generator_testing
+ absl::unordered_set_constructor_test
+ absl::unordered_set_lookup_test
+ absl::unordered_set_members_test
+ absl::unordered_set_modifiers_test
+ absl::memory
+ absl::strings
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ node_hash_map
+ HDRS
+ "node_hash_map.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::container_memory
+ absl::hash_function_defaults
+ absl::node_hash_policy
+ absl::raw_hash_map
+ absl::algorithm_container
+ absl::memory
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ node_hash_map_test
+ SRCS
+ "node_hash_map_test.cc"
+ DEPS
+ absl::hash_generator_testing
+ absl::node_hash_map
+ absl::tracked
+ absl::unordered_map_constructor_test
+ absl::unordered_map_lookup_test
+ absl::unordered_map_members_test
+ absl::unordered_map_modifiers_test
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ node_hash_set
+ HDRS
+ "node_hash_set.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::hash_function_defaults
+ absl::node_hash_policy
+ absl::raw_hash_set
+ absl::algorithm_container
+ absl::memory
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ node_hash_set_test
+ SRCS
+ "node_hash_set_test.cc"
+ COPTS
+ "-DUNORDERED_SET_CXX17"
+ DEPS
+ absl::hash_generator_testing
+ absl::node_hash_set
+ absl::unordered_set_constructor_test
+ absl::unordered_set_lookup_test
+ absl::unordered_set_members_test
+ absl::unordered_set_modifiers_test
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ container_memory
+ HDRS
+ "internal/container_memory.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::memory
+ absl::utility
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ container_memory_test
+ SRCS
+ "internal/container_memory_test.cc"
+ DEPS
+ absl::container_memory
+ absl::strings
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ hash_function_defaults
+ HDRS
+ "internal/hash_function_defaults.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ absl::hash
+ absl::strings
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ hash_function_defaults_test
+ SRCS
+ "internal/hash_function_defaults_test.cc"
+ DEPS
+ absl::hash_function_defaults
+ absl::hash
+ absl::strings
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ hash_generator_testing
+ HDRS
+ "internal/hash_generator_testing.h"
+ SRCS
+ "internal/hash_generator_testing.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_policy_testing
+ absl::meta
+ absl::strings
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ hash_policy_testing
+ HDRS
+ "internal/hash_policy_testing.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash
+ absl::strings
+ TESTONLY
+)
+
+absl_cc_test(
+ NAME
+ hash_policy_testing_test
+ SRCS
+ "internal/hash_policy_testing_test.cc"
+ DEPS
+ absl::hash_policy_testing
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ hash_policy_traits
+ HDRS
+ "internal/hash_policy_traits.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::meta
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ hash_policy_traits_test
+ SRCS
+ "internal/hash_policy_traits_test.cc"
+ DEPS
+ absl::hash_policy_traits
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ hashtablez_sampler
+ HDRS
+ "internal/hashtablez_sampler.h"
+ SRCS
+ "internal/hashtablez_sampler.cc"
+ "internal/hashtablez_sampler_force_weak_definition.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base
+ absl::have_sse
+ absl::synchronization
+)
+
+absl_cc_test(
+ NAME
+ hashtablez_sampler_test
+ SRCS
+ "internal/hashtablez_sampler_test.cc"
+ DEPS
+ absl::hashtablez_sampler
+ absl::have_sse
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ hashtable_debug
+ HDRS
+ "internal/hashtable_debug.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::hashtable_debug_hooks
+)
+
+absl_cc_library(
+ NAME
+ hashtable_debug_hooks
+ HDRS
+ "internal/hashtable_debug_hooks.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ have_sse
+ HDRS
+ "internal/have_sse.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+)
+
+absl_cc_library(
+ NAME
+ node_hash_policy
+ HDRS
+ "internal/node_hash_policy.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ node_hash_policy_test
+ SRCS
+ "internal/node_hash_policy_test.cc"
+ DEPS
+ absl::hash_policy_traits
+ absl::node_hash_policy
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ raw_hash_map
+ HDRS
+ "internal/raw_hash_map.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::container_memory
+ absl::raw_hash_set
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ container_common
+ HDRS
+ "internal/commom.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::type_traits
+)
+
+absl_cc_library(
+ NAME
+ raw_hash_set
+ HDRS
+ "internal/raw_hash_set.h"
+ SRCS
+ "internal/raw_hash_set.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::bits
+ absl::compressed_tuple
+ absl::config
+ absl::container_common
+ absl::container_memory
+ absl::core_headers
+ absl::endian
+ absl::hash_policy_traits
+ absl::hashtable_debug_hooks
+ absl::have_sse
+ absl::layout
+ absl::memory
+ absl::meta
+ absl::optional
+ absl::utility
+ absl::hashtablez_sampler
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
raw_hash_set_test
- SOURCES
+ SRCS
"internal/raw_hash_set_test.cc"
- PUBLIC_LIBRARIES
- absl::base absl::hash absl_throw_delegate test_instance_tracker_lib
+ DEPS
+ absl::container_memory
+ absl::hash_function_defaults
+ absl::hash_policy_testing
+ absl::hashtable_debug
+ absl::raw_hash_set
+ absl::base
+ absl::core_headers
+ absl::strings
+ gmock_main
+)
+
+absl_cc_test(
+ NAME
+ raw_hash_set_allocator_test
+ SRCS
+ "internal/raw_hash_set_allocator_test.cc"
+ DEPS
+ absl::raw_hash_set
+ absl::tracked
+ absl::core_headers
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ layout
+ HDRS
+ "internal/layout.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ absl::meta
+ absl::strings
+ absl::span
+ absl::utility
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ layout_test
+ SRCS
+ "internal/layout_test.cc"
+ DEPS
+ absl::layout
+ absl::base
+ absl::core_headers
+ absl::span
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ tracked
+ HDRS
+ "internal/tracked.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_map_constructor_test
+ HDRS
+ "internal/unordered_map_constructor_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_map_lookup_test
+ HDRS
+ "internal/unordered_map_lookup_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_map_members_test
+ HDRS
+ "internal/unordered_map_members_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::type_traits
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_map_modifiers_test
+ HDRS
+ "internal/unordered_map_modifiers_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_set_constructor_test
+ HDRS
+ "internal/unordered_set_constructor_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_set_lookup_test
+ HDRS
+ "internal/unordered_set_lookup_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_set_members_test
+ HDRS
+ "internal/unordered_set_members_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::type_traits
+ gmock
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ unordered_set_modifiers_test
+ HDRS
+ "internal/unordered_set_modifiers_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::hash_generator_testing
+ absl::hash_policy_testing
+ gmock
+ TESTONLY
+)
+
+absl_cc_test(
+ NAME
+ unordered_set_test
+ SRCS
+ "internal/unordered_set_test.cc"
+ DEPS
+ absl::unordered_set_constructor_test
+ absl::unordered_set_lookup_test
+ absl::unordered_set_members_test
+ absl::unordered_set_modifiers_test
+ gmock_main
+)
+
+absl_cc_test(
+ NAME
+ unordered_map_test
+ SRCS
+ "internal/unordered_map_test.cc"
+ DEPS
+ absl::unordered_map_constructor_test
+ absl::unordered_map_lookup_test
+ absl::unordered_map_members_test
+ absl::unordered_map_modifiers_test
+ gmock_main
)
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index 6d9fa5f..60da048 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -188,7 +188,7 @@
// `FixedArray<T>`. This is equivalent to the most possible addressable bytes
// over the number of bytes taken by T.
constexpr size_type max_size() const {
- return std::numeric_limits<difference_type>::max() / sizeof(value_type);
+ return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
}
// FixedArray::empty()
@@ -515,4 +515,5 @@
static_cast<void>(n); // Mark used when not in asan mode
}
} // namespace absl
+
#endif // ABSL_CONTAINER_FIXED_ARRAY_H_
diff --git a/absl/container/fixed_array_benchmark.cc b/absl/container/fixed_array_benchmark.cc
index b4f0cf2..ff56f46 100644
--- a/absl/container/fixed_array_benchmark.cc
+++ b/absl/container/fixed_array_benchmark.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc
index da63dbf..826eca6 100644
--- a/absl/container/fixed_array_exception_safety_test.cc
+++ b/absl/container/fixed_array_exception_safety_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc
index 205ff41..a4f2498 100644
--- a/absl/container/fixed_array_test.cc
+++ b/absl/container/fixed_array_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -150,7 +150,7 @@
}
{
- // Arrays of > default size should be on the stack
+ // Arrays of > default size should be on the heap
absl::FixedArray<int, 100> array(101);
EXPECT_FALSE(IsOnStack(array));
}
@@ -365,7 +365,8 @@
TEST(IteratorConstructorTest, NonPod) {
char const* kInput[] =
{ "red", "orange", "yellow", "green", "blue", "indigo", "violet" };
- absl::FixedArray<std::string> const fixed(kInput, kInput + ABSL_ARRAYSIZE(kInput));
+ absl::FixedArray<std::string> const fixed(kInput,
+ kInput + ABSL_ARRAYSIZE(kInput));
ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size());
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) {
ASSERT_EQ(kInput[i], fixed[i]);
@@ -869,4 +870,21 @@
}
#endif // ADDRESS_SANITIZER
+TEST(FixedArrayTest, AbslHashValueWorks) {
+ using V = absl::FixedArray<int>;
+ std::vector<V> cases;
+
+ // Generate a variety of vectors some of these are small enough for the inline
+ // space but are stored out of line.
+ for (int i = 0; i < 10; ++i) {
+ V v(i);
+ for (int j = 0; j < i; ++j) {
+ v[j] = j;
+ }
+ cases.push_back(v);
+ }
+
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
+}
+
} // namespace
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h
index e5570d1..00cc4dc 100644
--- a/absl/container/flat_hash_map.h
+++ b/absl/container/flat_hash_map.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -35,6 +35,7 @@
#include <type_traits>
#include <utility>
+#include "absl/algorithm/container.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export
@@ -69,7 +70,7 @@
// By default, `flat_hash_map` uses the `absl::Hash` hashing framework.
// All fundamental and Abseil types that support the `absl::Hash` framework have
// a compatible equality operator for comparing insertions into `flat_hash_map`.
-// If your type is not yet supported by the `asbl::Hash` framework, see
+// If your type is not yet supported by the `absl::Hash` framework, see
// absl/hash/hash.h for information on extending Abseil hashing to user-defined
// types.
//
@@ -109,6 +110,46 @@
using Base = typename flat_hash_map::raw_hash_map;
public:
+ // Constructors and Assignment Operators
+ //
+ // A flat_hash_map supports the same overload set as `std::unordered_map`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // // No allocation for the table's elements is made.
+ // absl::flat_hash_map<int, std::string> map1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::flat_hash_map<int, std::string> map2 =
+ // {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::flat_hash_map<int, std::string> map3(map2);
+ //
+ // * Copy assignment operator
+ //
+ // // Hash functor and Comparator are copied as well
+ // absl::flat_hash_map<int, std::string> map4;
+ // map4 = map3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::flat_hash_map<int, std::string> map5(std::move(map4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::flat_hash_map<int, std::string> map6;
+ // map6 = std::move(map5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
+ // absl::flat_hash_map<int, std::string> map7(v.begin(), v.end());
flat_hash_map() {}
using Base::Base;
@@ -178,8 +219,12 @@
// Erases the element at `position` of the `flat_hash_map`, returning
// `void`.
//
- // NOTE: this return behavior is different than that of STL containers in
- // general and `std::unordered_map` in particular.
+ // NOTE: returning `void` in this case is different than that of STL
+ // containers in general and `std::unordered_map` in particular (which
+ // return an iterator to the element following the erased element). If that
+ // iterator is needed, simply post increment the iterator:
+ //
+ // map.erase(it++);
//
// iterator erase(const_iterator first, const_iterator last):
//
@@ -486,25 +531,26 @@
template <class K, class V>
struct FlatHashMapPolicy {
- using slot_type = container_internal::slot_type<K, V>;
+ using slot_policy = container_internal::map_slot_policy<K, V>;
+ using slot_type = typename slot_policy::slot_type;
using key_type = K;
using mapped_type = V;
using init_type = std::pair</*non const*/ key_type, mapped_type>;
template <class Allocator, class... Args>
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
- slot_type::construct(alloc, slot, std::forward<Args>(args)...);
+ slot_policy::construct(alloc, slot, std::forward<Args>(args)...);
}
template <class Allocator>
static void destroy(Allocator* alloc, slot_type* slot) {
- slot_type::destroy(alloc, slot);
+ slot_policy::destroy(alloc, slot);
}
template <class Allocator>
static void transfer(Allocator* alloc, slot_type* new_slot,
slot_type* old_slot) {
- slot_type::transfer(alloc, new_slot, old_slot);
+ slot_policy::transfer(alloc, new_slot, old_slot);
}
template <class F, class... Args>
@@ -524,5 +570,16 @@
};
} // namespace container_internal
+
+namespace container_algorithm_internal {
+
+// Specialization of trait in absl/algorithm/container.h
+template <class Key, class T, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<
+ absl::flat_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
+
+} // namespace container_algorithm_internal
+
} // namespace absl
+
#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_
diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc
index 10a781f..ebcb560 100644
--- a/absl/container/flat_hash_map_test.cc
+++ b/absl/container/flat_hash_map_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,6 +17,7 @@
#include "absl/container/internal/hash_generator_testing.h"
#include "absl/container/internal/unordered_map_constructor_test.h"
#include "absl/container/internal/unordered_map_lookup_test.h"
+#include "absl/container/internal/unordered_map_members_test.h"
#include "absl/container/internal/unordered_map_modifiers_test.h"
#include "absl/types/any.h"
@@ -30,19 +31,20 @@
using ::testing::UnorderedElementsAre;
template <class K, class V>
-using Map =
- flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual, Alloc<>>;
+using Map = flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual,
+ Alloc<std::pair<const K, V>>>;
static_assert(!std::is_standard_layout<NonStandardLayout>(), "");
using MapTypes =
- ::testing::Types<Map<int, int>, Map<std::string, int>, Map<Enum, std::string>,
- Map<EnumClass, int>, Map<int, NonStandardLayout>,
- Map<NonStandardLayout, int>>;
+ ::testing::Types<Map<int, int>, Map<std::string, int>,
+ Map<Enum, std::string>, Map<EnumClass, int>,
+ Map<int, NonStandardLayout>, Map<NonStandardLayout, int>>;
-INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ConstructorTest, MapTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, LookupTest, MapTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ModifiersTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ConstructorTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes);
TEST(FlatHashMap, StandardLayout) {
struct Int {
@@ -94,7 +96,7 @@
}
}
-// Demonstration of the "Lazy Key" pattern. This uses heterogenous insert to
+// Demonstration of the "Lazy Key" pattern. This uses heterogeneous insert to
// avoid creating expensive key elements when the item is already present in the
// map.
struct LazyInt {
@@ -139,6 +141,7 @@
int conversions = 0;
int hashes = 0;
flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes});
+ m.reserve(3);
m[LazyInt(1, &conversions)] = 1;
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1)));
@@ -203,7 +206,9 @@
m.insert(std::move(node));
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9)));
}
-#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
+
+#if (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && \
+ !defined(__EMSCRIPTEN__)
TEST(FlatHashMap, Any) {
absl::flat_hash_map<int, absl::any> m;
m.emplace(1, 7);
@@ -234,7 +239,8 @@
ASSERT_NE(it2, m2.end());
EXPECT_EQ(7, it2->second);
}
-#endif // __ANDROID__
+#endif // (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) &&
+ // !defined(__EMSCRIPTEN__)
} // namespace
} // namespace container_internal
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
index 98aead1..6bf5183 100644
--- a/absl/container/flat_hash_set.h
+++ b/absl/container/flat_hash_set.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -32,6 +32,7 @@
#include <type_traits>
#include <utility>
+#include "absl/algorithm/container.h"
#include "absl/base/macros.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
@@ -66,7 +67,7 @@
// By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All
// fundamental and Abseil types that support the `absl::Hash` framework have a
// compatible equality operator for comparing insertions into `flat_hash_map`.
-// If your type is not yet supported by the `asbl::Hash` framework, see
+// If your type is not yet supported by the `absl::Hash` framework, see
// absl/hash/hash.h for information on extending Abseil hashing to user-defined
// types.
//
@@ -84,7 +85,7 @@
// {"huey", "dewey", "louie"};
//
// // Insert a new element into the flat hash set
-// ducks.insert("donald"};
+// ducks.insert("donald");
//
// // Force a rehash of the flat hash set
// ducks.rehash(0);
@@ -102,6 +103,46 @@
using Base = typename flat_hash_set::raw_hash_set;
public:
+ // Constructors and Assignment Operators
+ //
+ // A flat_hash_set supports the same overload set as `std::unordered_map`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // // No allocation for the table's elements is made.
+ // absl::flat_hash_set<std::string> set1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::flat_hash_set<std::string> set2 =
+ // {{"huey"}, {"dewey"}, {"louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::flat_hash_set<std::string> set3(set2);
+ //
+ // * Copy assignment operator
+ //
+ // // Hash functor and Comparator are copied as well
+ // absl::flat_hash_set<std::string> set4;
+ // set4 = set3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::flat_hash_set<std::string> set5(std::move(set4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::flat_hash_set<std::string> set6;
+ // set6 = std::move(set5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::string> v = {"a", "b"};
+ // absl::flat_hash_set<std::string> set7(v.begin(), v.end());
flat_hash_set() {}
using Base::Base;
@@ -171,8 +212,12 @@
// Erases the element at `position` of the `flat_hash_set`, returning
// `void`.
//
- // NOTE: this return behavior is different than that of STL containers in
- // general and `std::unordered_map` in particular.
+ // NOTE: returning `void` in this case is different than that of STL
+ // containers in general and `std::unordered_set` in particular (which
+ // return an iterator to the element following the erased element). If that
+ // iterator is needed, simply post increment the iterator:
+ //
+ // set.erase(it++);
//
// iterator erase(const_iterator first, const_iterator last):
//
@@ -238,8 +283,7 @@
//
// The element may be constructed even if there already is an element with the
// key in the container, in which case the newly constructed element will be
- // destroyed immediately. Prefer `try_emplace()` unless your key is not
- // copyable or moveable.
+ // destroyed immediately.
//
// If rehashing occurs due to the insertion, all iterators are invalidated.
using Base::emplace;
@@ -253,8 +297,7 @@
//
// The element may be constructed even if there already is an element with the
// key in the container, in which case the newly constructed element will be
- // destroyed immediately. Prefer `try_emplace()` unless your key is not
- // copyable or moveable.
+ // destroyed immediately.
//
// If rehashing occurs due to the insertion, all iterators are invalidated.
using Base::emplace_hint;
@@ -435,5 +478,16 @@
static size_t space_used(const T*) { return 0; }
};
} // namespace container_internal
+
+namespace container_algorithm_internal {
+
+// Specialization of trait in absl/algorithm/container.h
+template <class Key, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>>
+ : std::true_type {};
+
+} // namespace container_algorithm_internal
+
} // namespace absl
+
#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_
diff --git a/absl/container/flat_hash_set_test.cc b/absl/container/flat_hash_set_test.cc
index e52fd53..b55be59 100644
--- a/absl/container/flat_hash_set_test.cc
+++ b/absl/container/flat_hash_set_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -19,6 +19,7 @@
#include "absl/container/internal/hash_generator_testing.h"
#include "absl/container/internal/unordered_set_constructor_test.h"
#include "absl/container/internal/unordered_set_lookup_test.h"
+#include "absl/container/internal/unordered_set_members_test.h"
#include "absl/container/internal/unordered_set_modifiers_test.h"
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
@@ -40,9 +41,10 @@
using SetTypes =
::testing::Types<Set<int>, Set<std::string>, Set<Enum>, Set<EnumClass>>;
-INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ConstructorTest, SetTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, LookupTest, SetTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ModifiersTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, ConstructorTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, LookupTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, MembersTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, ModifiersTest, SetTypes);
TEST(FlatHashSet, EmplaceString) {
std::vector<std::string> v = {"a", "b"};
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 12756bb..7798805 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -1,10 +1,10 @@
-// Copyright 2017 The Abseil Authors.
+// Copyright 2019 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,17 +20,17 @@
// vector" which behaves in an equivalent fashion to a `std::vector`, except
// that storage for small sequences of the vector are provided inline without
// requiring any heap allocation.
-
-// An `absl::InlinedVector<T,N>` specifies the size N at which to inline as one
-// of its template parameters. Vectors of length <= N are provided inline.
-// Typically N is very small (e.g., 4) so that sequences that are expected to be
-// short do not require allocations.
-
-// An `absl::InlinedVector` does not usually require a specific allocator; if
+//
+// An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of
+// its template parameters. Instances where `size() <= N` hold contained
+// elements in inline space. Typically `N` is very small so that sequences that
+// are expected to be short do not require allocations.
+//
+// An `absl::InlinedVector` does not usually require a specific allocator. If
// the inlined vector grows beyond its initial constraints, it will need to
-// allocate (as any normal `std::vector` would) and it will generally use the
-// default allocator in that case; optionally, a custom allocator may be
-// specified using an `absl::InlinedVector<T,N,A>` construction.
+// allocate (as any normal `std::vector` would). This is usually performed with
+// the default allocator (defined as `std::allocator<T>`). Optionally, a custom
+// allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`.
#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_
#define ABSL_CONTAINER_INLINED_VECTOR_H_
@@ -50,10 +50,10 @@
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h"
+#include "absl/container/internal/inlined_vector.h"
#include "absl/memory/memory.h"
namespace absl {
-
// -----------------------------------------------------------------------------
// InlinedVector
// -----------------------------------------------------------------------------
@@ -61,323 +61,633 @@
// An `absl::InlinedVector` is designed to be a drop-in replacement for
// `std::vector` for use cases where the vector's size is sufficiently small
// that it can be inlined. If the inlined vector does grow beyond its estimated
-// size, it will trigger an initial allocation on the heap, and will behave as a
-// `std:vector`. The API of the `absl::InlinedVector` within this file is
+// capacity, it will trigger an initial allocation on the heap, and will behave
+// as a `std:vector`. The API of the `absl::InlinedVector` within this file is
// designed to cover the same API footprint as covered by `std::vector`.
-template <typename T, size_t N, typename A = std::allocator<T> >
+template <typename T, size_t N, typename A = std::allocator<T>>
class InlinedVector {
- using AllocatorTraits = std::allocator_traits<A>;
+ static_assert(
+ N > 0, "InlinedVector cannot be instantiated with `0` inlined elements.");
+
+ using Storage = inlined_vector_internal::Storage<InlinedVector>;
+ using Tag = typename Storage::Tag;
+ using AllocatorAndTag = typename Storage::AllocatorAndTag;
+ using Allocation = typename Storage::Allocation;
+
+ template <typename Iterator>
+ using IsAtLeastForwardIterator = std::is_convertible<
+ typename std::iterator_traits<Iterator>::iterator_category,
+ std::forward_iterator_tag>;
+
+ template <typename Iterator>
+ using EnableIfAtLeastForwardIterator =
+ absl::enable_if_t<IsAtLeastForwardIterator<Iterator>::value>;
+
+ template <typename Iterator>
+ using DisableIfAtLeastForwardIterator =
+ absl::enable_if_t<!IsAtLeastForwardIterator<Iterator>::value>;
+
+ using rvalue_reference = typename Storage::rvalue_reference;
public:
- using allocator_type = A;
- using value_type = typename allocator_type::value_type;
- using pointer = typename allocator_type::pointer;
- using const_pointer = typename allocator_type::const_pointer;
- using reference = typename allocator_type::reference;
- using const_reference = typename allocator_type::const_reference;
- using size_type = typename allocator_type::size_type;
- using difference_type = typename allocator_type::difference_type;
- using iterator = pointer;
- using const_iterator = const_pointer;
- using reverse_iterator = std::reverse_iterator<iterator>;
- using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using allocator_type = typename Storage::allocator_type;
+ using value_type = typename Storage::value_type;
+ using pointer = typename Storage::pointer;
+ using const_pointer = typename Storage::const_pointer;
+ using reference = typename Storage::reference;
+ using const_reference = typename Storage::const_reference;
+ using size_type = typename Storage::size_type;
+ using difference_type = typename Storage::difference_type;
+ using iterator = typename Storage::iterator;
+ using const_iterator = typename Storage::const_iterator;
+ using reverse_iterator = typename Storage::reverse_iterator;
+ using const_reverse_iterator = typename Storage::const_reverse_iterator;
+ // ---------------------------------------------------------------------------
+ // InlinedVector Constructors and Destructor
+ // ---------------------------------------------------------------------------
+
+ // Creates an empty inlined vector with a default initialized allocator.
InlinedVector() noexcept(noexcept(allocator_type()))
- : allocator_and_tag_(allocator_type()) {}
+ : storage_(allocator_type()) {}
+ // Creates an empty inlined vector with a specified allocator.
explicit InlinedVector(const allocator_type& alloc) noexcept
- : allocator_and_tag_(alloc) {}
+ : storage_(alloc) {}
- // Create a vector with n copies of value_type().
+ // Creates an inlined vector with `n` copies of `value_type()`.
explicit InlinedVector(size_type n,
const allocator_type& alloc = allocator_type())
- : allocator_and_tag_(alloc) {
+ : storage_(alloc) {
InitAssign(n);
}
- // Create a vector with n copies of elem
- InlinedVector(size_type n, const value_type& elem,
+ // Creates an inlined vector with `n` copies of `v`.
+ InlinedVector(size_type n, const_reference v,
const allocator_type& alloc = allocator_type())
- : allocator_and_tag_(alloc) {
- InitAssign(n, elem);
+ : storage_(alloc) {
+ InitAssign(n, v);
}
- // Create and initialize with the elements [first .. last).
- // The unused enable_if argument restricts this constructor so that it is
- // elided when value_type is an integral type. This prevents ambiguous
- // interpretation between a call to this constructor with two integral
- // arguments and a call to the preceding (n, elem) constructor.
- template <typename InputIterator>
- InlinedVector(
- InputIterator first, InputIterator last,
- const allocator_type& alloc = allocator_type(),
- typename std::enable_if<!std::is_integral<InputIterator>::value>::type* =
- nullptr)
- : allocator_and_tag_(alloc) {
- AppendRange(first, last);
- }
-
- InlinedVector(std::initializer_list<value_type> init,
+ // Creates an inlined vector of copies of the values in `list`.
+ InlinedVector(std::initializer_list<value_type> list,
const allocator_type& alloc = allocator_type())
- : allocator_and_tag_(alloc) {
- AppendRange(init.begin(), init.end());
+ : storage_(alloc) {
+ AppendForwardRange(list.begin(), list.end());
}
- InlinedVector(const InlinedVector& v);
- InlinedVector(const InlinedVector& v, const allocator_type& alloc);
+ // Creates an inlined vector with elements constructed from the provided
+ // forward iterator range [`first`, `last`).
+ //
+ // NOTE: The `enable_if` prevents ambiguous interpretation between a call to
+ // this constructor with two integral arguments and a call to the above
+ // `InlinedVector(size_type, const_reference)` constructor.
+ template <typename ForwardIterator,
+ EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
+ InlinedVector(ForwardIterator first, ForwardIterator last,
+ const allocator_type& alloc = allocator_type())
+ : storage_(alloc) {
+ AppendForwardRange(first, last);
+ }
- // This move constructor does not allocate and only moves the underlying
- // objects, so its `noexcept` specification depends on whether moving the
- // underlying objects can throw or not. We assume
- // a) move constructors should only throw due to allocation failure and
- // b) if `value_type`'s move constructor allocates, it uses the same
- // allocation function as the `InlinedVector`'s allocator, so the move
+ // Creates an inlined vector with elements constructed from the provided input
+ // iterator range [`first`, `last`).
+ template <typename InputIterator,
+ DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
+ InlinedVector(InputIterator first, InputIterator last,
+ const allocator_type& alloc = allocator_type())
+ : storage_(alloc) {
+ std::copy(first, last, std::back_inserter(*this));
+ }
+
+ // Creates a copy of an `other` inlined vector using `other`'s allocator.
+ InlinedVector(const InlinedVector& other)
+ : InlinedVector(other, other.allocator()) {}
+
+ // Creates a copy of an `other` inlined vector using a specified allocator.
+ InlinedVector(const InlinedVector& other, const allocator_type& alloc)
+ : storage_(alloc) {
+ reserve(other.size());
+ if (allocated()) {
+ UninitializedCopy(other.begin(), other.end(), allocated_space());
+ tag().set_allocated_size(other.size());
+ } else {
+ UninitializedCopy(other.begin(), other.end(), inlined_space());
+ tag().set_inline_size(other.size());
+ }
+ }
+
+ // Creates an inlined vector by moving in the contents of an `other` inlined
+ // vector without performing any allocations. If `other` contains allocated
+ // memory, the newly-created instance will take ownership of that memory
+ // (leaving `other` itself empty). However, if `other` does not contain any
+ // allocated memory, the new inlined vector will will perform element-wise
+ // move construction of `other`s elements.
+ //
+ // NOTE: since no allocation is performed for the inlined vector in either
+ // case, the `noexcept(...)` specification depends on whether moving the
+ // underlying objects can throw. We assume:
+ // a) Move constructors should only throw due to allocation failure.
+ // b) If `value_type`'s move constructor allocates, it uses the same
+ // allocation function as the `InlinedVector`'s allocator. Thus, the move
// constructor is non-throwing if the allocator is non-throwing or
// `value_type`'s move constructor is specified as `noexcept`.
- InlinedVector(InlinedVector&& v) noexcept(
+ InlinedVector(InlinedVector&& other) noexcept(
absl::allocator_is_nothrow<allocator_type>::value ||
- std::is_nothrow_move_constructible<value_type>::value);
+ std::is_nothrow_move_constructible<value_type>::value)
+ : storage_(other.allocator()) {
+ if (other.allocated()) {
+ // We can just steal the underlying buffer from the source.
+ // That leaves the source empty, so we clear its size.
+ init_allocation(other.allocation());
+ tag().set_allocated_size(other.size());
+ other.tag() = Tag();
+ } else {
+ UninitializedCopy(
+ std::make_move_iterator(other.inlined_space()),
+ std::make_move_iterator(other.inlined_space() + other.size()),
+ inlined_space());
+ tag().set_inline_size(other.size());
+ }
+ }
- // This move constructor allocates and also moves the underlying objects, so
- // its `noexcept` specification depends on whether the allocation can throw
- // and whether moving the underlying objects can throw. Based on the same
- // assumptions above, the `noexcept` specification is dominated by whether the
- // allocation can throw regardless of whether `value_type`'s move constructor
- // is specified as `noexcept`.
- InlinedVector(InlinedVector&& v, const allocator_type& alloc) noexcept(
- absl::allocator_is_nothrow<allocator_type>::value);
+ // Creates an inlined vector by moving in the contents of an `other` inlined
+ // vector, performing allocations with the specified `alloc` allocator. If
+ // `other`'s allocator is not equal to `alloc` and `other` contains allocated
+ // memory, this move constructor will create a new allocation.
+ //
+ // NOTE: since allocation is performed in this case, this constructor can
+ // only be `noexcept` if the specified allocator is also `noexcept`. If this
+ // is the case, or if `other` contains allocated memory, this constructor
+ // performs element-wise move construction of its contents.
+ //
+ // Only in the case where `other`'s allocator is equal to `alloc` and `other`
+ // contains allocated memory will the newly created inlined vector take
+ // ownership of `other`'s allocated memory.
+ InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept(
+ absl::allocator_is_nothrow<allocator_type>::value)
+ : storage_(alloc) {
+ if (other.allocated()) {
+ if (alloc == other.allocator()) {
+ // We can just steal the allocation from the source.
+ tag() = other.tag();
+ init_allocation(other.allocation());
+ other.tag() = Tag();
+ } else {
+ // We need to use our own allocator
+ reserve(other.size());
+ UninitializedCopy(std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()),
+ allocated_space());
+ tag().set_allocated_size(other.size());
+ }
+ } else {
+ UninitializedCopy(
+ std::make_move_iterator(other.inlined_space()),
+ std::make_move_iterator(other.inlined_space() + other.size()),
+ inlined_space());
+ tag().set_inline_size(other.size());
+ }
+ }
~InlinedVector() { clear(); }
- InlinedVector& operator=(const InlinedVector& v) {
- if (this == &v) {
- return *this;
+ // ---------------------------------------------------------------------------
+ // InlinedVector Member Accessors
+ // ---------------------------------------------------------------------------
+
+ // `InlinedVector::empty()`
+ //
+ // Checks if the inlined vector has no elements.
+ bool empty() const noexcept { return !size(); }
+
+ // `InlinedVector::size()`
+ //
+ // Returns the number of elements in the inlined vector.
+ size_type size() const noexcept { return tag().size(); }
+
+ // `InlinedVector::max_size()`
+ //
+ // Returns the maximum number of elements the vector can hold.
+ size_type max_size() const noexcept {
+ // One bit of the size storage is used to indicate whether the inlined
+ // vector is allocated. As a result, the maximum size of the container that
+ // we can express is half of the max for `size_type`.
+ return (std::numeric_limits<size_type>::max)() / 2;
+ }
+
+ // `InlinedVector::capacity()`
+ //
+ // Returns the number of elements that can be stored in the inlined vector
+ // without requiring a reallocation of underlying memory.
+ //
+ // NOTE: For most inlined vectors, `capacity()` should equal the template
+ // parameter `N`. For inlined vectors which exceed this capacity, they
+ // will no longer be inlined and `capacity()` will equal its capacity on the
+ // allocated heap.
+ size_type capacity() const noexcept {
+ return allocated() ? allocation().capacity() : static_cast<size_type>(N);
+ }
+
+ // `InlinedVector::data()`
+ //
+ // Returns a `pointer` to elements of the inlined vector. This pointer can be
+ // used to access and modify the contained elements.
+ // Only results within the range [`0`, `size()`) are defined.
+ pointer data() noexcept {
+ return allocated() ? allocated_space() : inlined_space();
+ }
+
+ // Overload of `InlinedVector::data()` to return a `const_pointer` to elements
+ // of the inlined vector. This pointer can be used to access (but not modify)
+ // the contained elements.
+ const_pointer data() const noexcept {
+ return allocated() ? allocated_space() : inlined_space();
+ }
+
+ // `InlinedVector::operator[]()`
+ //
+ // Returns a `reference` to the `i`th element of the inlined vector using the
+ // array operator.
+ reference operator[](size_type i) {
+ assert(i < size());
+ return data()[i];
+ }
+
+ // Overload of `InlinedVector::operator[]()` to return a `const_reference` to
+ // the `i`th element of the inlined vector.
+ const_reference operator[](size_type i) const {
+ assert(i < size());
+ return data()[i];
+ }
+
+ // `InlinedVector::at()`
+ //
+ // Returns a `reference` to the `i`th element of the inlined vector.
+ reference at(size_type i) {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
+ base_internal::ThrowStdOutOfRange(
+ "`InlinedVector::at(size_type)` failed bounds check");
}
+ return data()[i];
+ }
+
+ // Overload of `InlinedVector::at()` to return a `const_reference` to the
+ // `i`th element of the inlined vector.
+ const_reference at(size_type i) const {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
+ base_internal::ThrowStdOutOfRange(
+ "`InlinedVector::at(size_type) const` failed bounds check");
+ }
+ return data()[i];
+ }
+
+ // `InlinedVector::front()`
+ //
+ // Returns a `reference` to the first element of the inlined vector.
+ reference front() {
+ assert(!empty());
+ return at(0);
+ }
+
+ // Overload of `InlinedVector::front()` returns a `const_reference` to the
+ // first element of the inlined vector.
+ const_reference front() const {
+ assert(!empty());
+ return at(0);
+ }
+
+ // `InlinedVector::back()`
+ //
+ // Returns a `reference` to the last element of the inlined vector.
+ reference back() {
+ assert(!empty());
+ return at(size() - 1);
+ }
+
+ // Overload of `InlinedVector::back()` to return a `const_reference` to the
+ // last element of the inlined vector.
+ const_reference back() const {
+ assert(!empty());
+ return at(size() - 1);
+ }
+
+ // `InlinedVector::begin()`
+ //
+ // Returns an `iterator` to the beginning of the inlined vector.
+ iterator begin() noexcept { return data(); }
+
+ // Overload of `InlinedVector::begin()` to return a `const_iterator` to
+ // the beginning of the inlined vector.
+ const_iterator begin() const noexcept { return data(); }
+
+ // `InlinedVector::end()`
+ //
+ // Returns an `iterator` to the end of the inlined vector.
+ iterator end() noexcept { return data() + size(); }
+
+ // Overload of `InlinedVector::end()` to return a `const_iterator` to the
+ // end of the inlined vector.
+ const_iterator end() const noexcept { return data() + size(); }
+
+ // `InlinedVector::cbegin()`
+ //
+ // Returns a `const_iterator` to the beginning of the inlined vector.
+ const_iterator cbegin() const noexcept { return begin(); }
+
+ // `InlinedVector::cend()`
+ //
+ // Returns a `const_iterator` to the end of the inlined vector.
+ const_iterator cend() const noexcept { return end(); }
+
+ // `InlinedVector::rbegin()`
+ //
+ // Returns a `reverse_iterator` from the end of the inlined vector.
+ reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
+
+ // Overload of `InlinedVector::rbegin()` to return a
+ // `const_reverse_iterator` from the end of the inlined vector.
+ const_reverse_iterator rbegin() const noexcept {
+ return const_reverse_iterator(end());
+ }
+
+ // `InlinedVector::rend()`
+ //
+ // Returns a `reverse_iterator` from the beginning of the inlined vector.
+ reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
+
+ // Overload of `InlinedVector::rend()` to return a `const_reverse_iterator`
+ // from the beginning of the inlined vector.
+ const_reverse_iterator rend() const noexcept {
+ return const_reverse_iterator(begin());
+ }
+
+ // `InlinedVector::crbegin()`
+ //
+ // Returns a `const_reverse_iterator` from the end of the inlined vector.
+ const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+
+ // `InlinedVector::crend()`
+ //
+ // Returns a `const_reverse_iterator` from the beginning of the inlined
+ // vector.
+ const_reverse_iterator crend() const noexcept { return rend(); }
+
+ // `InlinedVector::get_allocator()`
+ //
+ // Returns a copy of the allocator of the inlined vector.
+ allocator_type get_allocator() const { return allocator(); }
+
+ // ---------------------------------------------------------------------------
+ // InlinedVector Member Mutators
+ // ---------------------------------------------------------------------------
+
+ // `InlinedVector::operator=()`
+ //
+ // Replaces the contents of the inlined vector with copies of the elements in
+ // the provided `std::initializer_list`.
+ InlinedVector& operator=(std::initializer_list<value_type> list) {
+ AssignForwardRange(list.begin(), list.end());
+ return *this;
+ }
+
+ // Overload of `InlinedVector::operator=()` to replace the contents of the
+ // inlined vector with the contents of `other`.
+ InlinedVector& operator=(const InlinedVector& other) {
+ if (ABSL_PREDICT_FALSE(this == &other)) return *this;
+
// Optimized to avoid reallocation.
// Prefer reassignment to copy construction for elements.
- if (size() < v.size()) { // grow
- reserve(v.size());
- std::copy(v.begin(), v.begin() + size(), begin());
- std::copy(v.begin() + size(), v.end(), std::back_inserter(*this));
+ if (size() < other.size()) { // grow
+ reserve(other.size());
+ std::copy(other.begin(), other.begin() + size(), begin());
+ std::copy(other.begin() + size(), other.end(), std::back_inserter(*this));
} else { // maybe shrink
- erase(begin() + v.size(), end());
- std::copy(v.begin(), v.end(), begin());
+ erase(begin() + other.size(), end());
+ std::copy(other.begin(), other.end(), begin());
}
return *this;
}
- InlinedVector& operator=(InlinedVector&& v) {
- if (this == &v) {
- return *this;
- }
- if (v.allocated()) {
+ // Overload of `InlinedVector::operator=()` to replace the contents of the
+ // inlined vector with the contents of `other`.
+ //
+ // NOTE: As a result of calling this overload, `other` may be empty or it's
+ // contents may be left in a moved-from state.
+ InlinedVector& operator=(InlinedVector&& other) {
+ if (ABSL_PREDICT_FALSE(this == &other)) return *this;
+
+ if (other.allocated()) {
clear();
- tag().set_allocated_size(v.size());
- init_allocation(v.allocation());
- v.tag() = Tag();
+ tag().set_allocated_size(other.size());
+ init_allocation(other.allocation());
+ other.tag() = Tag();
} else {
if (allocated()) clear();
// Both are inlined now.
- if (size() < v.size()) {
- auto mid = std::make_move_iterator(v.begin() + size());
- std::copy(std::make_move_iterator(v.begin()), mid, begin());
- UninitializedCopy(mid, std::make_move_iterator(v.end()), end());
+ if (size() < other.size()) {
+ auto mid = std::make_move_iterator(other.begin() + size());
+ std::copy(std::make_move_iterator(other.begin()), mid, begin());
+ UninitializedCopy(mid, std::make_move_iterator(other.end()), end());
} else {
- auto new_end = std::copy(std::make_move_iterator(v.begin()),
- std::make_move_iterator(v.end()), begin());
+ auto new_end = std::copy(std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()), begin());
Destroy(new_end, end());
}
- tag().set_inline_size(v.size());
+ tag().set_inline_size(other.size());
}
return *this;
}
- InlinedVector& operator=(std::initializer_list<value_type> init) {
- AssignRange(init.begin(), init.end());
- return *this;
- }
-
- // InlinedVector::assign()
+ // `InlinedVector::assign()`
//
- // Replaces the contents of the inlined vector with copies of those in the
- // iterator range [first, last).
- template <typename InputIterator>
- void assign(
- InputIterator first, InputIterator last,
- typename std::enable_if<!std::is_integral<InputIterator>::value>::type* =
- nullptr) {
- AssignRange(first, last);
- }
-
- // Overload of `InlinedVector::assign()` to take values from elements of an
- // initializer list
- void assign(std::initializer_list<value_type> init) {
- AssignRange(init.begin(), init.end());
- }
-
- // Overload of `InlinedVector::assign()` to replace the first `n` elements of
- // the inlined vector with `elem` values.
- void assign(size_type n, const value_type& elem) {
+ // Replaces the contents of the inlined vector with `n` copies of `v`.
+ void assign(size_type n, const_reference v) {
if (n <= size()) { // Possibly shrink
- std::fill_n(begin(), n, elem);
+ std::fill_n(begin(), n, v);
erase(begin() + n, end());
return;
}
// Grow
reserve(n);
- std::fill_n(begin(), size(), elem);
+ std::fill_n(begin(), size(), v);
if (allocated()) {
- UninitializedFill(allocated_space() + size(), allocated_space() + n,
- elem);
+ UninitializedFill(allocated_space() + size(), allocated_space() + n, v);
tag().set_allocated_size(n);
} else {
- UninitializedFill(inlined_space() + size(), inlined_space() + n, elem);
+ UninitializedFill(inlined_space() + size(), inlined_space() + n, v);
tag().set_inline_size(n);
}
}
- // InlinedVector::size()
- //
- // Returns the number of elements in the inlined vector.
- size_type size() const noexcept { return tag().size(); }
-
- // InlinedVector::empty()
- //
- // Checks if the inlined vector has no elements.
- bool empty() const noexcept { return (size() == 0); }
-
- // InlinedVector::capacity()
- //
- // Returns the number of elements that can be stored in an inlined vector
- // without requiring a reallocation of underlying memory. Note that for
- // most inlined vectors, `capacity()` should equal its initial size `N`; for
- // inlined vectors which exceed this capacity, they will no longer be inlined,
- // and `capacity()` will equal its capacity on the allocated heap.
- size_type capacity() const noexcept {
- return allocated() ? allocation().capacity() : N;
+ // Overload of `InlinedVector::assign()` to replace the contents of the
+ // inlined vector with copies of the values in the provided
+ // `std::initializer_list`.
+ void assign(std::initializer_list<value_type> list) {
+ AssignForwardRange(list.begin(), list.end());
}
- // InlinedVector::max_size()
- //
- // Returns the maximum number of elements the vector can hold.
- size_type max_size() const noexcept {
- // One bit of the size storage is used to indicate whether the inlined
- // vector is allocated; as a result, the maximum size of the container that
- // we can express is half of the max for our size type.
- return std::numeric_limits<size_type>::max() / 2;
+ // Overload of `InlinedVector::assign()` to replace the contents of the
+ // inlined vector with the forward iterator range [`first`, `last`).
+ template <typename ForwardIterator,
+ EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
+ void assign(ForwardIterator first, ForwardIterator last) {
+ AssignForwardRange(first, last);
}
- // InlinedVector::data()
- //
- // Returns a const T* pointer to elements of the inlined vector. This pointer
- // can be used to access (but not modify) the contained elements.
- // Only results within the range `[0,size())` are defined.
- const_pointer data() const noexcept {
- return allocated() ? allocated_space() : inlined_space();
+ // Overload of `InlinedVector::assign()` to replace the contents of the
+ // inlined vector with the input iterator range [`first`, `last`).
+ template <typename InputIterator,
+ DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
+ void assign(InputIterator first, InputIterator last) {
+ size_type assign_index = 0;
+ for (; (assign_index < size()) && (first != last);
+ static_cast<void>(++assign_index), static_cast<void>(++first)) {
+ *(data() + assign_index) = *first;
+ }
+ erase(data() + assign_index, data() + size());
+ std::copy(first, last, std::back_inserter(*this));
}
- // Overload of InlinedVector::data() to return a T* pointer to elements of the
- // inlined vector. This pointer can be used to access and modify the contained
- // elements.
- pointer data() noexcept {
- return allocated() ? allocated_space() : inlined_space();
- }
-
- // InlinedVector::clear()
+ // `InlinedVector::resize()`
//
- // Removes all elements from the inlined vector.
- void clear() noexcept {
+ // Resizes the inlined vector to contain `n` elements. If `n` is smaller than
+ // the inlined vector's current size, extra elements are destroyed. If `n` is
+ // larger than the initial size, new elements are value-initialized.
+ void resize(size_type n) {
size_type s = size();
+ if (n < s) {
+ erase(begin() + n, end());
+ return;
+ }
+ reserve(n);
+ assert(capacity() >= n);
+
+ // Fill new space with elements constructed in-place.
if (allocated()) {
- Destroy(allocated_space(), allocated_space() + s);
- allocation().Dealloc(allocator());
- } else if (s != 0) { // do nothing for empty vectors
- Destroy(inlined_space(), inlined_space() + s);
+ UninitializedFill(allocated_space() + s, allocated_space() + n);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space() + s, inlined_space() + n);
+ tag().set_inline_size(n);
}
- tag() = Tag();
}
- // InlinedVector::at()
- //
- // Returns the ith element of an inlined vector.
- const value_type& at(size_type i) const {
- if (ABSL_PREDICT_FALSE(i >= size())) {
- base_internal::ThrowStdOutOfRange(
- "InlinedVector::at failed bounds check");
- }
- return data()[i];
- }
-
- // InlinedVector::operator[]
- //
- // Returns the ith element of an inlined vector using the array operator.
- const value_type& operator[](size_type i) const {
- assert(i < size());
- return data()[i];
- }
-
- // Overload of InlinedVector::at() to return the ith element of an inlined
- // vector.
- value_type& at(size_type i) {
- if (i >= size()) {
- base_internal::ThrowStdOutOfRange(
- "InlinedVector::at failed bounds check");
- }
- return data()[i];
- }
-
- // Overload of InlinedVector::operator[] to return the ith element of an
- // inlined vector.
- value_type& operator[](size_type i) {
- assert(i < size());
- return data()[i];
- }
-
- // InlinedVector::back()
- //
- // Returns a reference to the last element of an inlined vector.
- value_type& back() {
- assert(!empty());
- return at(size() - 1);
- }
-
- // Overload of InlinedVector::back() returns a reference to the last element
- // of an inlined vector of const values.
- const value_type& back() const {
- assert(!empty());
- return at(size() - 1);
- }
-
- // InlinedVector::front()
- //
- // Returns a reference to the first element of an inlined vector.
- value_type& front() {
- assert(!empty());
- return at(0);
- }
-
- // Overload of InlinedVector::front() returns a reference to the first element
- // of an inlined vector of const values.
- const value_type& front() const {
- assert(!empty());
- return at(0);
- }
-
- // InlinedVector::emplace_back()
- //
- // Constructs and appends an object to the inlined vector.
- //
- // Returns a reference to the inserted element.
- template <typename... Args>
- value_type& emplace_back(Args&&... args) {
+ // Overload of `InlinedVector::resize()` to resize the inlined vector to
+ // contain `n` elements where, if `n` is larger than `size()`, the new values
+ // will be copy-constructed from `v`.
+ void resize(size_type n, const_reference v) {
size_type s = size();
- assert(s <= capacity());
+ if (n < s) {
+ erase(begin() + n, end());
+ return;
+ }
+ reserve(n);
+ assert(capacity() >= n);
+
+ // Fill new space with copies of `v`.
+ if (allocated()) {
+ UninitializedFill(allocated_space() + s, allocated_space() + n, v);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space() + s, inlined_space() + n, v);
+ tag().set_inline_size(n);
+ }
+ }
+
+ // `InlinedVector::insert()`
+ //
+ // Copies `v` into `pos`, returning an `iterator` pointing to the newly
+ // inserted element.
+ iterator insert(const_iterator pos, const_reference v) {
+ return emplace(pos, v);
+ }
+
+ // Overload of `InlinedVector::insert()` for moving `v` into `pos`, returning
+ // an iterator pointing to the newly inserted element.
+ iterator insert(const_iterator pos, rvalue_reference v) {
+ return emplace(pos, std::move(v));
+ }
+
+ // Overload of `InlinedVector::insert()` for inserting `n` contiguous copies
+ // of `v` starting at `pos`. Returns an `iterator` pointing to the first of
+ // the newly inserted elements.
+ iterator insert(const_iterator pos, size_type n, const_reference v) {
+ return InsertWithCount(pos, n, v);
+ }
+
+ // Overload of `InlinedVector::insert()` for copying the contents of the
+ // `std::initializer_list` into the vector starting at `pos`. Returns an
+ // `iterator` pointing to the first of the newly inserted elements.
+ iterator insert(const_iterator pos, std::initializer_list<value_type> list) {
+ return insert(pos, list.begin(), list.end());
+ }
+
+ // Overload of `InlinedVector::insert()` for inserting elements constructed
+ // from the forward iterator range [`first`, `last`). Returns an `iterator`
+ // pointing to the first of the newly inserted elements.
+ //
+ // NOTE: The `enable_if` is intended to disambiguate the two three-argument
+ // overloads of `insert()`.
+ template <typename ForwardIterator,
+ EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
+ iterator insert(const_iterator pos, ForwardIterator first,
+ ForwardIterator last) {
+ return InsertWithForwardRange(pos, first, last);
+ }
+
+ // Overload of `InlinedVector::insert()` for inserting elements constructed
+ // from the input iterator range [`first`, `last`). Returns an `iterator`
+ // pointing to the first of the newly inserted elements.
+ template <typename InputIterator,
+ DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
+ iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
+ size_type initial_insert_index = std::distance(cbegin(), pos);
+ for (size_type insert_index = initial_insert_index; first != last;
+ static_cast<void>(++insert_index), static_cast<void>(++first)) {
+ insert(data() + insert_index, *first);
+ }
+ return iterator(data() + initial_insert_index);
+ }
+
+ // `InlinedVector::emplace()`
+ //
+ // Constructs and inserts an object in the inlined vector at the given `pos`,
+ // returning an `iterator` pointing to the newly emplaced element.
+ template <typename... Args>
+ iterator emplace(const_iterator pos, Args&&... args) {
+ assert(pos >= begin());
+ assert(pos <= end());
+ if (ABSL_PREDICT_FALSE(pos == end())) {
+ emplace_back(std::forward<Args>(args)...);
+ return end() - 1;
+ }
+
+ T new_t = T(std::forward<Args>(args)...);
+
+ auto range = ShiftRight(pos, 1);
+ if (range.first == range.second) {
+ // constructing into uninitialized memory
+ Construct(range.first, std::move(new_t));
+ } else {
+ // assigning into moved-from object
+ *range.first = T(std::move(new_t));
+ }
+
+ return range.first;
+ }
+
+ // `InlinedVector::emplace_back()`
+ //
+ // Constructs and appends a new element to the end of the inlined vector,
+ // returning a `reference` to the emplaced element.
+ template <typename... Args>
+ reference emplace_back(Args&&... args) {
+ size_type s = size();
if (ABSL_PREDICT_FALSE(s == capacity())) {
return GrowAndEmplaceBack(std::forward<Args>(args)...);
}
- assert(s < capacity());
-
- value_type* space;
+ pointer space;
if (allocated()) {
tag().set_allocated_size(s + 1);
space = allocated_space();
@@ -388,19 +698,22 @@
return Construct(space + s, std::forward<Args>(args)...);
}
- // InlinedVector::push_back()
+ // `InlinedVector::push_back()`
//
- // Appends a const element to the inlined vector.
- void push_back(const value_type& t) { emplace_back(t); }
+ // Appends a copy of `v` to the end of the inlined vector.
+ void push_back(const_reference v) { static_cast<void>(emplace_back(v)); }
- // Overload of InlinedVector::push_back() to append a move-only element to the
- // inlined vector.
- void push_back(value_type&& t) { emplace_back(std::move(t)); }
+ // Overload of `InlinedVector::push_back()` for moving `v` into a newly
+ // appended element.
+ void push_back(rvalue_reference v) {
+ static_cast<void>(emplace_back(std::move(v)));
+ }
- // InlinedVector::pop_back()
+ // `InlinedVector::pop_back()`
//
- // Removes the last element (which is destroyed) in the inlined vector.
- void pop_back() {
+ // Destroys the element at the end of the inlined vector and shrinks the size
+ // by `1` (unless the inlined vector is empty, in which case this is a no-op).
+ void pop_back() noexcept {
assert(!empty());
size_type s = size();
if (allocated()) {
@@ -412,161 +725,75 @@
}
}
- // InlinedVector::resize()
+ // `InlinedVector::erase()`
//
- // Resizes the inlined vector to contain `n` elements. If `n` is smaller than
- // the inlined vector's current size, extra elements are destroyed. If `n` is
- // larger than the initial size, new elements are value-initialized.
- void resize(size_type n);
-
- // Overload of InlinedVector::resize() to resize the inlined vector to contain
- // `n` elements. If `n` is larger than the current size, enough copies of
- // `elem` are appended to increase its size to `n`.
- void resize(size_type n, const value_type& elem);
-
- // InlinedVector::begin()
+ // Erases the element at `pos` of the inlined vector, returning an `iterator`
+ // pointing to the first element following the erased element.
//
- // Returns an iterator to the beginning of the inlined vector.
- iterator begin() noexcept { return data(); }
+ // NOTE: May return the end iterator, which is not dereferencable.
+ iterator erase(const_iterator pos) {
+ assert(pos >= begin());
+ assert(pos < end());
- // Overload of InlinedVector::begin() for returning a const iterator to the
- // beginning of the inlined vector.
- const_iterator begin() const noexcept { return data(); }
-
- // InlinedVector::cbegin()
- //
- // Returns a const iterator to the beginning of the inlined vector.
- const_iterator cbegin() const noexcept { return begin(); }
-
- // InlinedVector::end()
- //
- // Returns an iterator to the end of the inlined vector.
- iterator end() noexcept { return data() + size(); }
-
- // Overload of InlinedVector::end() for returning a const iterator to the end
- // of the inlined vector.
- const_iterator end() const noexcept { return data() + size(); }
-
- // InlinedVector::cend()
- //
- // Returns a const iterator to the end of the inlined vector.
- const_iterator cend() const noexcept { return end(); }
-
- // InlinedVector::rbegin()
- //
- // Returns a reverse iterator from the end of the inlined vector.
- reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
-
- // Overload of InlinedVector::rbegin() for returning a const reverse iterator
- // from the end of the inlined vector.
- const_reverse_iterator rbegin() const noexcept {
- return const_reverse_iterator(end());
- }
-
- // InlinedVector::crbegin()
- //
- // Returns a const reverse iterator from the end of the inlined vector.
- const_reverse_iterator crbegin() const noexcept { return rbegin(); }
-
- // InlinedVector::rend()
- //
- // Returns a reverse iterator from the beginning of the inlined vector.
- reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
-
- // Overload of InlinedVector::rend() for returning a const reverse iterator
- // from the beginning of the inlined vector.
- const_reverse_iterator rend() const noexcept {
- return const_reverse_iterator(begin());
- }
-
- // InlinedVector::crend()
- //
- // Returns a reverse iterator from the beginning of the inlined vector.
- const_reverse_iterator crend() const noexcept { return rend(); }
-
- // InlinedVector::emplace()
- //
- // Constructs and inserts an object to the inlined vector at the given
- // `position`, returning an iterator pointing to the newly emplaced element.
- template <typename... Args>
- iterator emplace(const_iterator position, Args&&... args);
-
- // InlinedVector::insert()
- //
- // Inserts an element of the specified value at `position`, returning an
- // iterator pointing to the newly inserted element.
- iterator insert(const_iterator position, const value_type& v) {
- return emplace(position, v);
- }
-
- // Overload of InlinedVector::insert() for inserting an element of the
- // specified rvalue, returning an iterator pointing to the newly inserted
- // element.
- iterator insert(const_iterator position, value_type&& v) {
- return emplace(position, std::move(v));
- }
-
- // Overload of InlinedVector::insert() for inserting `n` elements of the
- // specified value at `position`, returning an iterator pointing to the first
- // of the newly inserted elements.
- iterator insert(const_iterator position, size_type n, const value_type& v) {
- return InsertWithCount(position, n, v);
- }
-
- // Overload of `InlinedVector::insert()` to disambiguate the two
- // three-argument overloads of `insert()`, returning an iterator pointing to
- // the first of the newly inserted elements.
- template <typename InputIterator,
- typename = typename std::enable_if<std::is_convertible<
- typename std::iterator_traits<InputIterator>::iterator_category,
- std::input_iterator_tag>::value>::type>
- iterator insert(const_iterator position, InputIterator first,
- InputIterator last) {
- using IterType =
- typename std::iterator_traits<InputIterator>::iterator_category;
- return InsertWithRange(position, first, last, IterType());
- }
-
- // Overload of InlinedVector::insert() for inserting a list of elements at
- // `position`, returning an iterator pointing to the first of the newly
- // inserted elements.
- iterator insert(const_iterator position,
- std::initializer_list<value_type> init) {
- return insert(position, init.begin(), init.end());
- }
-
- // InlinedVector::erase()
- //
- // Erases the element at `position` of the inlined vector, returning an
- // iterator pointing to the following element or the container's end if the
- // last element was erased.
- iterator erase(const_iterator position) {
- assert(position >= begin());
- assert(position < end());
-
- iterator pos = const_cast<iterator>(position);
- std::move(pos + 1, end(), pos);
+ iterator position = const_cast<iterator>(pos);
+ std::move(position + 1, end(), position);
pop_back();
- return pos;
+ return position;
}
- // Overload of InlinedVector::erase() for erasing all elements in the
- // iterator range [first, last) in the inlined vector, returning an iterator
- // pointing to the first element following the range erased, or the
- // container's end if range included the container's last element.
- iterator erase(const_iterator first, const_iterator last);
+ // Overload of `InlinedVector::erase()` for erasing all elements in the
+ // range [`from`, `to`) in the inlined vector. Returns an `iterator` pointing
+ // to the first element following the range erased or the end iterator if `to`
+ // was the end iterator.
+ iterator erase(const_iterator from, const_iterator to) {
+ assert(begin() <= from);
+ assert(from <= to);
+ assert(to <= end());
- // InlinedVector::reserve()
+ iterator range_start = const_cast<iterator>(from);
+ iterator range_end = const_cast<iterator>(to);
+
+ size_type s = size();
+ ptrdiff_t erase_gap = std::distance(range_start, range_end);
+ if (erase_gap > 0) {
+ pointer space;
+ if (allocated()) {
+ space = allocated_space();
+ tag().set_allocated_size(s - erase_gap);
+ } else {
+ space = inlined_space();
+ tag().set_inline_size(s - erase_gap);
+ }
+ std::move(range_end, space + s, range_start);
+ Destroy(space + s - erase_gap, space + s);
+ }
+ return range_start;
+ }
+
+ // `InlinedVector::clear()`
+ //
+ // Destroys all elements in the inlined vector, sets the size of `0` and
+ // deallocates the heap allocation if the inlined vector was allocated.
+ void clear() noexcept {
+ size_type s = size();
+ if (allocated()) {
+ Destroy(allocated_space(), allocated_space() + s);
+ allocation().Dealloc(allocator());
+ } else if (s != 0) { // do nothing for empty vectors
+ Destroy(inlined_space(), inlined_space() + s);
+ }
+ tag() = Tag();
+ }
+
+ // `InlinedVector::reserve()`
//
// Enlarges the underlying representation of the inlined vector so it can hold
// at least `n` elements. This method does not change `size()` or the actual
// contents of the vector.
//
- // Note that if `n` does not exceed the inlined vector's initial size `N`,
- // `reserve()` will have no effect; if it does exceed its initial size,
- // `reserve()` will trigger an initial allocation and move the inlined vector
- // onto the heap. If the vector already exists on the heap and the requested
- // size exceeds it, a reallocation will be performed.
+ // NOTE: If `n` does not exceed `capacity()`, `reserve()` will have no
+ // effects. Otherwise, `reserve()` will reallocate, performing an n-time
+ // element-wise move of everything contained.
void reserve(size_type n) {
if (n > capacity()) {
// Make room for new elements
@@ -574,26 +801,25 @@
}
}
- // InlinedVector::shrink_to_fit()
+ // `InlinedVector::shrink_to_fit()`
//
- // Reduces memory usage by freeing unused memory.
- // After this call `capacity()` will be equal to `max(N, size())`.
+ // Reduces memory usage by freeing unused memory. After this call, calls to
+ // `capacity()` will be equal to `max(N, size())`.
//
// If `size() <= N` and the elements are currently stored on the heap, they
- // will be moved to the inlined storage and the heap memory deallocated.
- // If `size() > N` and `size() < capacity()` the elements will be moved to
- // a reallocated storage on heap.
+ // will be moved to the inlined storage and the heap memory will be
+ // deallocated.
+ //
+ // If `size() > N` and `size() < capacity()` the elements will be moved to a
+ // smaller heap allocation.
void shrink_to_fit() {
const auto s = size();
- if (!allocated() || s == capacity()) {
- // There's nothing to deallocate.
- return;
- }
+ if (ABSL_PREDICT_FALSE(!allocated() || s == capacity())) return;
if (s <= N) {
// Move the elements to the inlined storage.
- // We have to do this using a temporary, because inlined_storage and
- // allocation_storage are in a union field.
+ // We have to do this using a temporary, because `inlined_storage` and
+ // `allocation_storage` are in a union field.
auto temp = std::move(*this);
assign(std::make_move_iterator(temp.begin()),
std::make_move_iterator(temp.end()));
@@ -601,8 +827,8 @@
}
// Reallocate storage and move elements.
- // We can't simply use the same approach as above, because assign() would
- // call into reserve() internally and reserve larger capacity than we need.
+ // We can't simply use the same approach as above, because `assign()` would
+ // call into `reserve()` internally and reserve larger capacity than we need
Allocation new_allocation(allocator(), s);
UninitializedCopy(std::make_move_iterator(allocated_space()),
std::make_move_iterator(allocated_space() + s),
@@ -610,129 +836,62 @@
ResetAllocation(new_allocation, s);
}
- // InlinedVector::swap()
+ // `InlinedVector::swap()`
//
// Swaps the contents of this inlined vector with the contents of `other`.
- void swap(InlinedVector& other);
+ void swap(InlinedVector& other) {
+ if (ABSL_PREDICT_FALSE(this == &other)) return;
- // InlinedVector::get_allocator()
- //
- // Returns the allocator of this inlined vector.
- allocator_type get_allocator() const { return allocator(); }
-
- template <typename H>
- friend H AbslHashValue(H h, const InlinedVector& v) {
- return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()),
- v.size());
+ SwapImpl(other);
}
private:
- static_assert(N > 0, "inlined vector with nonpositive size");
+ template <typename H, typename TheT, size_t TheN, typename TheA>
+ friend auto AbslHashValue(H h, const InlinedVector<TheT, TheN, TheA>& v) -> H;
- // It holds whether the vector is allocated or not in the lowest bit.
- // The size is held in the high bits:
- // size_ = (size << 1) | is_allocated;
- //
- // Maintainer's Note: size_type is user defined. The contract is limited to
- // arithmetic operators to avoid depending on compliant overloaded bitwise
- // operators.
- class Tag {
- public:
- Tag() : size_(0) {}
- size_type size() const { return size_ / 2; }
- void add_size(size_type n) { size_ += n * 2; }
- void set_inline_size(size_type n) { size_ = n * 2; }
- void set_allocated_size(size_type n) { size_ = (n * 2) + 1; }
- bool allocated() const { return size_ % 2; }
+ const Tag& tag() const { return storage_.allocator_and_tag_.tag(); }
- private:
- size_type size_;
- };
-
- // Derives from allocator_type to use the empty base class optimization.
- // If the allocator_type is stateless, we can 'store'
- // our instance of it for free.
- class AllocatorAndTag : private allocator_type {
- public:
- explicit AllocatorAndTag(const allocator_type& a) : allocator_type(a) {}
- Tag& tag() { return tag_; }
- const Tag& tag() const { return tag_; }
- allocator_type& allocator() { return *this; }
- const allocator_type& allocator() const { return *this; }
-
- private:
- Tag tag_;
- };
-
- class Allocation {
- public:
- Allocation(allocator_type& a, // NOLINT(runtime/references)
- size_type capacity)
- : capacity_(capacity),
- buffer_(AllocatorTraits::allocate(a, capacity_)) {}
-
- void Dealloc(allocator_type& a) { // NOLINT(runtime/references)
- AllocatorTraits::deallocate(a, buffer(), capacity());
- }
-
- size_type capacity() const { return capacity_; }
- const value_type* buffer() const { return buffer_; }
- value_type* buffer() { return buffer_; }
-
- private:
- size_type capacity_;
- value_type* buffer_;
- };
-
- const Tag& tag() const { return allocator_and_tag_.tag(); }
- Tag& tag() { return allocator_and_tag_.tag(); }
+ Tag& tag() { return storage_.allocator_and_tag_.tag(); }
Allocation& allocation() {
- return reinterpret_cast<Allocation&>(rep_.allocation_storage.allocation);
+ return reinterpret_cast<Allocation&>(
+ storage_.rep_.allocation_storage.allocation);
}
+
const Allocation& allocation() const {
return reinterpret_cast<const Allocation&>(
- rep_.allocation_storage.allocation);
+ storage_.rep_.allocation_storage.allocation);
}
+
void init_allocation(const Allocation& allocation) {
- new (&rep_.allocation_storage.allocation) Allocation(allocation);
+ new (&storage_.rep_.allocation_storage.allocation) Allocation(allocation);
}
// TODO(absl-team): investigate whether the reinterpret_cast is appropriate.
- value_type* inlined_space() {
- return reinterpret_cast<value_type*>(
- std::addressof(rep_.inlined_storage.inlined[0]));
- }
- const value_type* inlined_space() const {
- return reinterpret_cast<const value_type*>(
- std::addressof(rep_.inlined_storage.inlined[0]));
+ pointer inlined_space() {
+ return reinterpret_cast<pointer>(
+ std::addressof(storage_.rep_.inlined_storage.inlined[0]));
}
- value_type* allocated_space() { return allocation().buffer(); }
- const value_type* allocated_space() const { return allocation().buffer(); }
+ const_pointer inlined_space() const {
+ return reinterpret_cast<const_pointer>(
+ std::addressof(storage_.rep_.inlined_storage.inlined[0]));
+ }
+
+ pointer allocated_space() { return allocation().buffer(); }
+
+ const_pointer allocated_space() const { return allocation().buffer(); }
const allocator_type& allocator() const {
- return allocator_and_tag_.allocator();
+ return storage_.allocator_and_tag_.allocator();
}
- allocator_type& allocator() { return allocator_and_tag_.allocator(); }
+
+ allocator_type& allocator() {
+ return storage_.allocator_and_tag_.allocator();
+ }
bool allocated() const { return tag().allocated(); }
- // Enlarge the underlying representation so we can store size_ + delta elems.
- // The size is not changed, and any newly added memory is not initialized.
- void EnlargeBy(size_type delta);
-
- // Shift all elements from position to end() n places to the right.
- // If the vector needs to be enlarged, memory will be allocated.
- // Returns iterators pointing to the start of the previously-initialized
- // portion and the start of the uninitialized portion of the created gap.
- // The number of initialized spots is pair.second - pair.first;
- // the number of raw spots is n - (pair.second - pair.first).
- //
- // Updates the size of the InlinedVector internally.
- std::pair<iterator, iterator> ShiftRight(const_iterator position,
- size_type n);
-
void ResetAllocation(Allocation new_allocation, size_type new_size) {
if (allocated()) {
Destroy(allocated_space(), allocated_space() + size());
@@ -747,13 +906,137 @@
}
template <typename... Args>
- value_type& GrowAndEmplaceBack(Args&&... args) {
+ reference Construct(pointer p, Args&&... args) {
+ std::allocator_traits<allocator_type>::construct(
+ allocator(), p, std::forward<Args>(args)...);
+ return *p;
+ }
+
+ template <typename Iterator>
+ void UninitializedCopy(Iterator src, Iterator src_last, pointer dst) {
+ for (; src != src_last; ++dst, ++src) Construct(dst, *src);
+ }
+
+ template <typename... Args>
+ void UninitializedFill(pointer dst, pointer dst_last, const Args&... args) {
+ for (; dst != dst_last; ++dst) Construct(dst, args...);
+ }
+
+ // Destroy [`from`, `to`) in place.
+ void Destroy(pointer from, pointer to) {
+ for (pointer cur = from; cur != to; ++cur) {
+ std::allocator_traits<allocator_type>::destroy(allocator(), cur);
+ }
+#if !defined(NDEBUG)
+ // Overwrite unused memory with `0xab` so we can catch uninitialized usage.
+ // Cast to `void*` to tell the compiler that we don't care that we might be
+ // scribbling on a vtable pointer.
+ if (from != to) {
+ auto len = sizeof(value_type) * std::distance(from, to);
+ std::memset(reinterpret_cast<void*>(from), 0xab, len);
+ }
+#endif // !defined(NDEBUG)
+ }
+
+ // Enlarge the underlying representation so we can store `size_ + delta` elems
+ // in allocated space. The size is not changed, and any newly added memory is
+ // not initialized.
+ void EnlargeBy(size_type delta) {
+ const size_type s = size();
+ assert(s <= capacity());
+
+ size_type target = (std::max)(N, s + delta);
+
+ // Compute new capacity by repeatedly doubling current capacity
+ // TODO(psrc): Check and avoid overflow?
+ size_type new_capacity = capacity();
+ while (new_capacity < target) {
+ new_capacity <<= 1;
+ }
+
+ Allocation new_allocation(allocator(), new_capacity);
+
+ UninitializedCopy(std::make_move_iterator(data()),
+ std::make_move_iterator(data() + s),
+ new_allocation.buffer());
+
+ ResetAllocation(new_allocation, s);
+ }
+
+ // Shift all elements from `position` to `end()` by `n` places to the right.
+ // If the vector needs to be enlarged, memory will be allocated.
+ // Returns `iterator`s pointing to the start of the previously-initialized
+ // portion and the start of the uninitialized portion of the created gap.
+ // The number of initialized spots is `pair.second - pair.first`. The number
+ // of raw spots is `n - (pair.second - pair.first)`.
+ //
+ // Updates the size of the InlinedVector internally.
+ std::pair<iterator, iterator> ShiftRight(const_iterator position,
+ size_type n) {
+ iterator start_used = const_cast<iterator>(position);
+ iterator start_raw = const_cast<iterator>(position);
+ size_type s = size();
+ size_type required_size = s + n;
+
+ if (required_size > capacity()) {
+ // Compute new capacity by repeatedly doubling current capacity
+ size_type new_capacity = capacity();
+ while (new_capacity < required_size) {
+ new_capacity <<= 1;
+ }
+ // Move everyone into the new allocation, leaving a gap of `n` for the
+ // requested shift.
+ Allocation new_allocation(allocator(), new_capacity);
+ size_type index = position - begin();
+ UninitializedCopy(std::make_move_iterator(data()),
+ std::make_move_iterator(data() + index),
+ new_allocation.buffer());
+ UninitializedCopy(std::make_move_iterator(data() + index),
+ std::make_move_iterator(data() + s),
+ new_allocation.buffer() + index + n);
+ ResetAllocation(new_allocation, s);
+
+ // New allocation means our iterator is invalid, so we'll recalculate.
+ // Since the entire gap is in new space, there's no used space to reuse.
+ start_raw = begin() + index;
+ start_used = start_raw;
+ } else {
+ // If we had enough space, it's a two-part move. Elements going into
+ // previously-unoccupied space need an `UninitializedCopy()`. Elements
+ // going into a previously-occupied space are just a `std::move()`.
+ iterator pos = const_cast<iterator>(position);
+ iterator raw_space = end();
+ size_type slots_in_used_space = raw_space - pos;
+ size_type new_elements_in_used_space = (std::min)(n, slots_in_used_space);
+ size_type new_elements_in_raw_space = n - new_elements_in_used_space;
+ size_type old_elements_in_used_space =
+ slots_in_used_space - new_elements_in_used_space;
+
+ UninitializedCopy(
+ std::make_move_iterator(pos + old_elements_in_used_space),
+ std::make_move_iterator(raw_space),
+ raw_space + new_elements_in_raw_space);
+ std::move_backward(pos, pos + old_elements_in_used_space, raw_space);
+
+ // If the gap is entirely in raw space, the used space starts where the
+ // raw space starts, leaving no elements in used space. If the gap is
+ // entirely in used space, the raw space starts at the end of the gap,
+ // leaving all elements accounted for within the used space.
+ start_used = pos;
+ start_raw = pos + new_elements_in_used_space;
+ }
+ tag().add_size(n);
+ return std::make_pair(start_used, start_raw);
+ }
+
+ template <typename... Args>
+ reference GrowAndEmplaceBack(Args&&... args) {
assert(size() == capacity());
const size_type s = size();
Allocation new_allocation(allocator(), 2 * capacity());
- value_type& new_element =
+ reference new_element =
Construct(new_allocation.buffer() + s, std::forward<Args>(args)...);
UninitializedCopy(std::make_move_iterator(data()),
std::make_move_iterator(data() + s),
@@ -764,628 +1047,268 @@
return new_element;
}
- void InitAssign(size_type n);
- void InitAssign(size_type n, const value_type& t);
-
- template <typename... Args>
- value_type& Construct(pointer p, Args&&... args) {
- AllocatorTraits::construct(allocator(), p, std::forward<Args>(args)...);
- return *p;
+ void InitAssign(size_type n) {
+ if (n > N) {
+ Allocation new_allocation(allocator(), n);
+ init_allocation(new_allocation);
+ UninitializedFill(allocated_space(), allocated_space() + n);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space(), inlined_space() + n);
+ tag().set_inline_size(n);
+ }
}
- template <typename Iter>
- void UninitializedCopy(Iter src, Iter src_last, value_type* dst) {
- for (; src != src_last; ++dst, ++src) Construct(dst, *src);
+ void InitAssign(size_type n, const_reference v) {
+ if (n > N) {
+ Allocation new_allocation(allocator(), n);
+ init_allocation(new_allocation);
+ UninitializedFill(allocated_space(), allocated_space() + n, v);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space(), inlined_space() + n, v);
+ tag().set_inline_size(n);
+ }
}
- template <typename... Args>
- void UninitializedFill(value_type* dst, value_type* dst_last,
- const Args&... args) {
- for (; dst != dst_last; ++dst) Construct(dst, args...);
+ template <typename ForwardIt>
+ void AssignForwardRange(ForwardIt first, ForwardIt last) {
+ static_assert(IsAtLeastForwardIterator<ForwardIt>::value, "");
+
+ auto length = std::distance(first, last);
+
+ // Prefer reassignment to copy construction for elements.
+ if (static_cast<size_type>(length) <= size()) {
+ erase(std::copy(first, last, begin()), end());
+ return;
+ }
+
+ reserve(length);
+ iterator out = begin();
+ for (; out != end(); ++first, ++out) *out = *first;
+ if (allocated()) {
+ UninitializedCopy(first, last, out);
+ tag().set_allocated_size(length);
+ } else {
+ UninitializedCopy(first, last, out);
+ tag().set_inline_size(length);
+ }
}
- // Destroy [ptr, ptr_last) in place.
- void Destroy(value_type* ptr, value_type* ptr_last);
+ template <typename ForwardIt>
+ void AppendForwardRange(ForwardIt first, ForwardIt last) {
+ static_assert(IsAtLeastForwardIterator<ForwardIt>::value, "");
- template <typename Iter>
- void AppendRange(Iter first, Iter last, std::input_iterator_tag) {
- std::copy(first, last, std::back_inserter(*this));
- }
-
- // Faster path for forward iterators.
- template <typename Iter>
- void AppendRange(Iter first, Iter last, std::forward_iterator_tag);
-
- template <typename Iter>
- void AppendRange(Iter first, Iter last) {
- using IterTag = typename std::iterator_traits<Iter>::iterator_category;
- AppendRange(first, last, IterTag());
- }
-
- template <typename Iter>
- void AssignRange(Iter first, Iter last, std::input_iterator_tag);
-
- // Faster path for forward iterators.
- template <typename Iter>
- void AssignRange(Iter first, Iter last, std::forward_iterator_tag);
-
- template <typename Iter>
- void AssignRange(Iter first, Iter last) {
- using IterTag = typename std::iterator_traits<Iter>::iterator_category;
- AssignRange(first, last, IterTag());
+ auto length = std::distance(first, last);
+ reserve(size() + length);
+ if (allocated()) {
+ UninitializedCopy(first, last, allocated_space() + size());
+ tag().set_allocated_size(size() + length);
+ } else {
+ UninitializedCopy(first, last, inlined_space() + size());
+ tag().set_inline_size(size() + length);
+ }
}
iterator InsertWithCount(const_iterator position, size_type n,
- const value_type& v);
+ const_reference v) {
+ assert(position >= begin() && position <= end());
+ if (ABSL_PREDICT_FALSE(n == 0)) return const_cast<iterator>(position);
- template <typename InputIter>
- iterator InsertWithRange(const_iterator position, InputIter first,
- InputIter last, std::input_iterator_tag);
- template <typename ForwardIter>
- iterator InsertWithRange(const_iterator position, ForwardIter first,
- ForwardIter last, std::forward_iterator_tag);
+ value_type copy = v;
+ std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
+ std::fill(it_pair.first, it_pair.second, copy);
+ UninitializedFill(it_pair.second, it_pair.first + n, copy);
- AllocatorAndTag allocator_and_tag_;
+ return it_pair.first;
+ }
- // Either the inlined or allocated representation
- union Rep {
- // Use struct to perform indirection that solves a bizarre compilation
- // error on Visual Studio (all known versions).
- struct {
- typename std::aligned_storage<sizeof(value_type),
- alignof(value_type)>::type inlined[N];
- } inlined_storage;
- struct {
- typename std::aligned_storage<sizeof(Allocation),
- alignof(Allocation)>::type allocation;
- } allocation_storage;
- } rep_;
+ template <typename ForwardIt>
+ iterator InsertWithForwardRange(const_iterator position, ForwardIt first,
+ ForwardIt last) {
+ static_assert(IsAtLeastForwardIterator<ForwardIt>::value, "");
+ assert(position >= begin() && position <= end());
+
+ if (ABSL_PREDICT_FALSE(first == last))
+ return const_cast<iterator>(position);
+
+ auto n = std::distance(first, last);
+ std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
+ size_type used_spots = it_pair.second - it_pair.first;
+ auto open_spot = std::next(first, used_spots);
+ std::copy(first, open_spot, it_pair.first);
+ UninitializedCopy(open_spot, last, it_pair.second);
+ return it_pair.first;
+ }
+
+ void SwapImpl(InlinedVector& other) {
+ using std::swap; // Augment ADL with `std::swap`.
+
+ if (allocated() && other.allocated()) {
+ // Both out of line, so just swap the tag, allocation, and allocator.
+ swap(tag(), other.tag());
+ swap(allocation(), other.allocation());
+ swap(allocator(), other.allocator());
+ return;
+ }
+ if (!allocated() && !other.allocated()) {
+ // Both inlined: swap up to smaller size, then move remaining elements.
+ InlinedVector* a = this;
+ InlinedVector* b = &other;
+ if (size() < other.size()) {
+ swap(a, b);
+ }
+
+ const size_type a_size = a->size();
+ const size_type b_size = b->size();
+ assert(a_size >= b_size);
+ // `a` is larger. Swap the elements up to the smaller array size.
+ std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size,
+ b->inlined_space());
+
+ // Move the remaining elements:
+ // [`b_size`, `a_size`) from `a` -> [`b_size`, `a_size`) from `b`
+ b->UninitializedCopy(a->inlined_space() + b_size,
+ a->inlined_space() + a_size,
+ b->inlined_space() + b_size);
+ a->Destroy(a->inlined_space() + b_size, a->inlined_space() + a_size);
+
+ swap(a->tag(), b->tag());
+ swap(a->allocator(), b->allocator());
+ assert(b->size() == a_size);
+ assert(a->size() == b_size);
+ return;
+ }
+
+ // One is out of line, one is inline.
+ // We first move the elements from the inlined vector into the
+ // inlined space in the other vector. We then put the other vector's
+ // pointer/capacity into the originally inlined vector and swap
+ // the tags.
+ InlinedVector* a = this;
+ InlinedVector* b = &other;
+ if (a->allocated()) {
+ swap(a, b);
+ }
+ assert(!a->allocated());
+ assert(b->allocated());
+ const size_type a_size = a->size();
+ const size_type b_size = b->size();
+ // In an optimized build, `b_size` would be unused.
+ static_cast<void>(b_size);
+
+ // Made Local copies of `size()`, don't need `tag()` accurate anymore
+ swap(a->tag(), b->tag());
+
+ // Copy `b_allocation` out before `b`'s union gets clobbered by
+ // `inline_space`
+ Allocation b_allocation = b->allocation();
+
+ b->UninitializedCopy(a->inlined_space(), a->inlined_space() + a_size,
+ b->inlined_space());
+ a->Destroy(a->inlined_space(), a->inlined_space() + a_size);
+
+ a->allocation() = b_allocation;
+
+ if (a->allocator() != b->allocator()) {
+ swap(a->allocator(), b->allocator());
+ }
+
+ assert(b->size() == a_size);
+ assert(a->size() == b_size);
+ }
+
+ Storage storage_;
};
// -----------------------------------------------------------------------------
// InlinedVector Non-Member Functions
// -----------------------------------------------------------------------------
-// swap()
+// `swap()`
//
// Swaps the contents of two inlined vectors. This convenience function
-// simply calls InlinedVector::swap(other_inlined_vector).
+// simply calls `InlinedVector::swap()`.
template <typename T, size_t N, typename A>
-void swap(InlinedVector<T, N, A>& a,
- InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) {
+auto swap(InlinedVector<T, N, A>& a,
+ InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) -> void {
a.swap(b);
}
-// operator==()
+// `operator==()`
//
// Tests the equivalency of the contents of two inlined vectors.
template <typename T, size_t N, typename A>
-bool operator==(const InlinedVector<T, N, A>& a,
- const InlinedVector<T, N, A>& b) {
+auto operator==(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) -> bool {
return absl::equal(a.begin(), a.end(), b.begin(), b.end());
}
-// operator!=()
+// `operator!=()`
//
// Tests the inequality of the contents of two inlined vectors.
template <typename T, size_t N, typename A>
-bool operator!=(const InlinedVector<T, N, A>& a,
- const InlinedVector<T, N, A>& b) {
+auto operator!=(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) -> bool {
return !(a == b);
}
-// operator<()
+// `operator<()`
//
// Tests whether the contents of one inlined vector are less than the contents
// of another through a lexicographical comparison operation.
template <typename T, size_t N, typename A>
-bool operator<(const InlinedVector<T, N, A>& a,
- const InlinedVector<T, N, A>& b) {
+auto operator<(const InlinedVector<T, N, A>& a, const InlinedVector<T, N, A>& b)
+ -> bool {
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
}
-// operator>()
+// `operator>()`
//
// Tests whether the contents of one inlined vector are greater than the
// contents of another through a lexicographical comparison operation.
template <typename T, size_t N, typename A>
-bool operator>(const InlinedVector<T, N, A>& a,
- const InlinedVector<T, N, A>& b) {
+auto operator>(const InlinedVector<T, N, A>& a, const InlinedVector<T, N, A>& b)
+ -> bool {
return b < a;
}
-// operator<=()
+// `operator<=()`
//
// Tests whether the contents of one inlined vector are less than or equal to
// the contents of another through a lexicographical comparison operation.
template <typename T, size_t N, typename A>
-bool operator<=(const InlinedVector<T, N, A>& a,
- const InlinedVector<T, N, A>& b) {
+auto operator<=(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) -> bool {
return !(b < a);
}
-// operator>=()
+// `operator>=()`
//
// Tests whether the contents of one inlined vector are greater than or equal to
// the contents of another through a lexicographical comparison operation.
template <typename T, size_t N, typename A>
-bool operator>=(const InlinedVector<T, N, A>& a,
- const InlinedVector<T, N, A>& b) {
+auto operator>=(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) -> bool {
return !(a < b);
}
+// AbslHashValue()
+//
+// Provides `absl::Hash` support for inlined vectors. You do not normally call
+// this function directly.
+template <typename H, typename TheT, size_t TheN, typename TheA>
+auto AbslHashValue(H h, const InlinedVector<TheT, TheN, TheA>& v) -> H {
+ auto p = v.data();
+ auto n = v.size();
+ return H::combine(H::combine_contiguous(std::move(h), p, n), n);
+}
+} // namespace absl
+
// -----------------------------------------------------------------------------
// Implementation of InlinedVector
-// -----------------------------------------------------------------------------
//
-// Do not depend on any implementation details below this line.
-
-template <typename T, size_t N, typename A>
-InlinedVector<T, N, A>::InlinedVector(const InlinedVector& v)
- : allocator_and_tag_(v.allocator()) {
- reserve(v.size());
- if (allocated()) {
- UninitializedCopy(v.begin(), v.end(), allocated_space());
- tag().set_allocated_size(v.size());
- } else {
- UninitializedCopy(v.begin(), v.end(), inlined_space());
- tag().set_inline_size(v.size());
- }
-}
-
-template <typename T, size_t N, typename A>
-InlinedVector<T, N, A>::InlinedVector(const InlinedVector& v,
- const allocator_type& alloc)
- : allocator_and_tag_(alloc) {
- reserve(v.size());
- if (allocated()) {
- UninitializedCopy(v.begin(), v.end(), allocated_space());
- tag().set_allocated_size(v.size());
- } else {
- UninitializedCopy(v.begin(), v.end(), inlined_space());
- tag().set_inline_size(v.size());
- }
-}
-
-template <typename T, size_t N, typename A>
-InlinedVector<T, N, A>::InlinedVector(InlinedVector&& v) noexcept(
- absl::allocator_is_nothrow<allocator_type>::value ||
- std::is_nothrow_move_constructible<value_type>::value)
- : allocator_and_tag_(v.allocator_and_tag_) {
- if (v.allocated()) {
- // We can just steal the underlying buffer from the source.
- // That leaves the source empty, so we clear its size.
- init_allocation(v.allocation());
- v.tag() = Tag();
- } else {
- UninitializedCopy(std::make_move_iterator(v.inlined_space()),
- std::make_move_iterator(v.inlined_space() + v.size()),
- inlined_space());
- }
-}
-
-template <typename T, size_t N, typename A>
-InlinedVector<T, N, A>::InlinedVector(
- InlinedVector&& v,
- const allocator_type&
- alloc) noexcept(absl::allocator_is_nothrow<allocator_type>::value)
- : allocator_and_tag_(alloc) {
- if (v.allocated()) {
- if (alloc == v.allocator()) {
- // We can just steal the allocation from the source.
- tag() = v.tag();
- init_allocation(v.allocation());
- v.tag() = Tag();
- } else {
- // We need to use our own allocator
- reserve(v.size());
- UninitializedCopy(std::make_move_iterator(v.begin()),
- std::make_move_iterator(v.end()), allocated_space());
- tag().set_allocated_size(v.size());
- }
- } else {
- UninitializedCopy(std::make_move_iterator(v.inlined_space()),
- std::make_move_iterator(v.inlined_space() + v.size()),
- inlined_space());
- tag().set_inline_size(v.size());
- }
-}
-
-template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::InitAssign(size_type n, const value_type& t) {
- if (n > static_cast<size_type>(N)) {
- Allocation new_allocation(allocator(), n);
- init_allocation(new_allocation);
- UninitializedFill(allocated_space(), allocated_space() + n, t);
- tag().set_allocated_size(n);
- } else {
- UninitializedFill(inlined_space(), inlined_space() + n, t);
- tag().set_inline_size(n);
- }
-}
-
-template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::InitAssign(size_type n) {
- if (n > static_cast<size_type>(N)) {
- Allocation new_allocation(allocator(), n);
- init_allocation(new_allocation);
- UninitializedFill(allocated_space(), allocated_space() + n);
- tag().set_allocated_size(n);
- } else {
- UninitializedFill(inlined_space(), inlined_space() + n);
- tag().set_inline_size(n);
- }
-}
-
-template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::resize(size_type n) {
- size_type s = size();
- if (n < s) {
- erase(begin() + n, end());
- return;
- }
- reserve(n);
- assert(capacity() >= n);
-
- // Fill new space with elements constructed in-place.
- if (allocated()) {
- UninitializedFill(allocated_space() + s, allocated_space() + n);
- tag().set_allocated_size(n);
- } else {
- UninitializedFill(inlined_space() + s, inlined_space() + n);
- tag().set_inline_size(n);
- }
-}
-
-template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::resize(size_type n, const value_type& elem) {
- size_type s = size();
- if (n < s) {
- erase(begin() + n, end());
- return;
- }
- reserve(n);
- assert(capacity() >= n);
-
- // Fill new space with copies of 'elem'.
- if (allocated()) {
- UninitializedFill(allocated_space() + s, allocated_space() + n, elem);
- tag().set_allocated_size(n);
- } else {
- UninitializedFill(inlined_space() + s, inlined_space() + n, elem);
- tag().set_inline_size(n);
- }
-}
-
-template <typename T, size_t N, typename A>
-template <typename... Args>
-typename InlinedVector<T, N, A>::iterator InlinedVector<T, N, A>::emplace(
- const_iterator position, Args&&... args) {
- assert(position >= begin());
- assert(position <= end());
- if (position == end()) {
- emplace_back(std::forward<Args>(args)...);
- return end() - 1;
- }
-
- T new_t = T(std::forward<Args>(args)...);
-
- auto range = ShiftRight(position, 1);
- if (range.first == range.second) {
- // constructing into uninitialized memory
- Construct(range.first, std::move(new_t));
- } else {
- // assigning into moved-from object
- *range.first = T(std::move(new_t));
- }
-
- return range.first;
-}
-
-template <typename T, size_t N, typename A>
-typename InlinedVector<T, N, A>::iterator InlinedVector<T, N, A>::erase(
- const_iterator first, const_iterator last) {
- assert(begin() <= first);
- assert(first <= last);
- assert(last <= end());
-
- iterator range_start = const_cast<iterator>(first);
- iterator range_end = const_cast<iterator>(last);
-
- size_type s = size();
- ptrdiff_t erase_gap = std::distance(range_start, range_end);
- if (erase_gap > 0) {
- pointer space;
- if (allocated()) {
- space = allocated_space();
- tag().set_allocated_size(s - erase_gap);
- } else {
- space = inlined_space();
- tag().set_inline_size(s - erase_gap);
- }
- std::move(range_end, space + s, range_start);
- Destroy(space + s - erase_gap, space + s);
- }
- return range_start;
-}
-
-template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::swap(InlinedVector& other) {
- using std::swap; // Augment ADL with std::swap.
- if (&other == this) {
- return;
- }
- if (allocated() && other.allocated()) {
- // Both out of line, so just swap the tag, allocation, and allocator.
- swap(tag(), other.tag());
- swap(allocation(), other.allocation());
- swap(allocator(), other.allocator());
- return;
- }
- if (!allocated() && !other.allocated()) {
- // Both inlined: swap up to smaller size, then move remaining elements.
- InlinedVector* a = this;
- InlinedVector* b = &other;
- if (size() < other.size()) {
- swap(a, b);
- }
-
- const size_type a_size = a->size();
- const size_type b_size = b->size();
- assert(a_size >= b_size);
- // 'a' is larger. Swap the elements up to the smaller array size.
- std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size,
- b->inlined_space());
-
- // Move the remaining elements: A[b_size,a_size) -> B[b_size,a_size)
- b->UninitializedCopy(a->inlined_space() + b_size,
- a->inlined_space() + a_size,
- b->inlined_space() + b_size);
- a->Destroy(a->inlined_space() + b_size, a->inlined_space() + a_size);
-
- swap(a->tag(), b->tag());
- swap(a->allocator(), b->allocator());
- assert(b->size() == a_size);
- assert(a->size() == b_size);
- return;
- }
- // One is out of line, one is inline.
- // We first move the elements from the inlined vector into the
- // inlined space in the other vector. We then put the other vector's
- // pointer/capacity into the originally inlined vector and swap
- // the tags.
- InlinedVector* a = this;
- InlinedVector* b = &other;
- if (a->allocated()) {
- swap(a, b);
- }
- assert(!a->allocated());
- assert(b->allocated());
- const size_type a_size = a->size();
- const size_type b_size = b->size();
- // In an optimized build, b_size would be unused.
- (void)b_size;
-
- // Made Local copies of size(), don't need tag() accurate anymore
- swap(a->tag(), b->tag());
-
- // Copy b_allocation out before b's union gets clobbered by inline_space.
- Allocation b_allocation = b->allocation();
-
- b->UninitializedCopy(a->inlined_space(), a->inlined_space() + a_size,
- b->inlined_space());
- a->Destroy(a->inlined_space(), a->inlined_space() + a_size);
-
- a->allocation() = b_allocation;
-
- if (a->allocator() != b->allocator()) {
- swap(a->allocator(), b->allocator());
- }
-
- assert(b->size() == a_size);
- assert(a->size() == b_size);
-}
-
-template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::EnlargeBy(size_type delta) {
- const size_type s = size();
- assert(s <= capacity());
-
- size_type target = std::max(static_cast<size_type>(N), s + delta);
-
- // Compute new capacity by repeatedly doubling current capacity
- // TODO(psrc): Check and avoid overflow?
- size_type new_capacity = capacity();
- while (new_capacity < target) {
- new_capacity <<= 1;
- }
-
- Allocation new_allocation(allocator(), new_capacity);
-
- UninitializedCopy(std::make_move_iterator(data()),
- std::make_move_iterator(data() + s),
- new_allocation.buffer());
-
- ResetAllocation(new_allocation, s);
-}
-
-template <typename T, size_t N, typename A>
-auto InlinedVector<T, N, A>::ShiftRight(const_iterator position, size_type n)
- -> std::pair<iterator, iterator> {
- iterator start_used = const_cast<iterator>(position);
- iterator start_raw = const_cast<iterator>(position);
- size_type s = size();
- size_type required_size = s + n;
-
- if (required_size > capacity()) {
- // Compute new capacity by repeatedly doubling current capacity
- size_type new_capacity = capacity();
- while (new_capacity < required_size) {
- new_capacity <<= 1;
- }
- // Move everyone into the new allocation, leaving a gap of n for the
- // requested shift.
- Allocation new_allocation(allocator(), new_capacity);
- size_type index = position - begin();
- UninitializedCopy(std::make_move_iterator(data()),
- std::make_move_iterator(data() + index),
- new_allocation.buffer());
- UninitializedCopy(std::make_move_iterator(data() + index),
- std::make_move_iterator(data() + s),
- new_allocation.buffer() + index + n);
- ResetAllocation(new_allocation, s);
-
- // New allocation means our iterator is invalid, so we'll recalculate.
- // Since the entire gap is in new space, there's no used space to reuse.
- start_raw = begin() + index;
- start_used = start_raw;
- } else {
- // If we had enough space, it's a two-part move. Elements going into
- // previously-unoccupied space need an UninitializedCopy. Elements
- // going into a previously-occupied space are just a move.
- iterator pos = const_cast<iterator>(position);
- iterator raw_space = end();
- size_type slots_in_used_space = raw_space - pos;
- size_type new_elements_in_used_space = std::min(n, slots_in_used_space);
- size_type new_elements_in_raw_space = n - new_elements_in_used_space;
- size_type old_elements_in_used_space =
- slots_in_used_space - new_elements_in_used_space;
-
- UninitializedCopy(std::make_move_iterator(pos + old_elements_in_used_space),
- std::make_move_iterator(raw_space),
- raw_space + new_elements_in_raw_space);
- std::move_backward(pos, pos + old_elements_in_used_space, raw_space);
-
- // If the gap is entirely in raw space, the used space starts where the raw
- // space starts, leaving no elements in used space. If the gap is entirely
- // in used space, the raw space starts at the end of the gap, leaving all
- // elements accounted for within the used space.
- start_used = pos;
- start_raw = pos + new_elements_in_used_space;
- }
- tag().add_size(n);
- return std::make_pair(start_used, start_raw);
-}
-
-template <typename T, size_t N, typename A>
-void InlinedVector<T, N, A>::Destroy(value_type* ptr, value_type* ptr_last) {
- for (value_type* p = ptr; p != ptr_last; ++p) {
- AllocatorTraits::destroy(allocator(), p);
- }
-
- // Overwrite unused memory with 0xab so we can catch uninitialized usage.
- // Cast to void* to tell the compiler that we don't care that we might be
- // scribbling on a vtable pointer.
-#ifndef NDEBUG
- if (ptr != ptr_last) {
- memset(reinterpret_cast<void*>(ptr), 0xab, sizeof(*ptr) * (ptr_last - ptr));
- }
-#endif
-}
-
-template <typename T, size_t N, typename A>
-template <typename Iter>
-void InlinedVector<T, N, A>::AppendRange(Iter first, Iter last,
- std::forward_iterator_tag) {
- using Length = typename std::iterator_traits<Iter>::difference_type;
- Length length = std::distance(first, last);
- reserve(size() + length);
- if (allocated()) {
- UninitializedCopy(first, last, allocated_space() + size());
- tag().set_allocated_size(size() + length);
- } else {
- UninitializedCopy(first, last, inlined_space() + size());
- tag().set_inline_size(size() + length);
- }
-}
-
-template <typename T, size_t N, typename A>
-template <typename Iter>
-void InlinedVector<T, N, A>::AssignRange(Iter first, Iter last,
- std::input_iterator_tag) {
- // Optimized to avoid reallocation.
- // Prefer reassignment to copy construction for elements.
- iterator out = begin();
- for (; first != last && out != end(); ++first, ++out) {
- *out = *first;
- }
- erase(out, end());
- std::copy(first, last, std::back_inserter(*this));
-}
-
-template <typename T, size_t N, typename A>
-template <typename Iter>
-void InlinedVector<T, N, A>::AssignRange(Iter first, Iter last,
- std::forward_iterator_tag) {
- using Length = typename std::iterator_traits<Iter>::difference_type;
- Length length = std::distance(first, last);
- // Prefer reassignment to copy construction for elements.
- if (static_cast<size_type>(length) <= size()) {
- erase(std::copy(first, last, begin()), end());
- return;
- }
- reserve(length);
- iterator out = begin();
- for (; out != end(); ++first, ++out) *out = *first;
- if (allocated()) {
- UninitializedCopy(first, last, out);
- tag().set_allocated_size(length);
- } else {
- UninitializedCopy(first, last, out);
- tag().set_inline_size(length);
- }
-}
-
-template <typename T, size_t N, typename A>
-auto InlinedVector<T, N, A>::InsertWithCount(const_iterator position,
- size_type n, const value_type& v)
- -> iterator {
- assert(position >= begin() && position <= end());
- if (n == 0) return const_cast<iterator>(position);
-
- value_type copy = v;
- std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
- std::fill(it_pair.first, it_pair.second, copy);
- UninitializedFill(it_pair.second, it_pair.first + n, copy);
-
- return it_pair.first;
-}
-
-template <typename T, size_t N, typename A>
-template <typename InputIter>
-auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
- InputIter first, InputIter last,
- std::input_iterator_tag)
- -> iterator {
- assert(position >= begin() && position <= end());
- size_type index = position - cbegin();
- size_type i = index;
- while (first != last) insert(begin() + i++, *first++);
- return begin() + index;
-}
-
-// Overload of InlinedVector::InsertWithRange()
-template <typename T, size_t N, typename A>
-template <typename ForwardIter>
-auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
- ForwardIter first,
- ForwardIter last,
- std::forward_iterator_tag)
- -> iterator {
- assert(position >= begin() && position <= end());
- if (first == last) {
- return const_cast<iterator>(position);
- }
- using Length = typename std::iterator_traits<ForwardIter>::difference_type;
- Length n = std::distance(first, last);
- std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
- size_type used_spots = it_pair.second - it_pair.first;
- ForwardIter open_spot = std::next(first, used_spots);
- std::copy(first, open_spot, it_pair.first);
- UninitializedCopy(open_spot, last, it_pair.second);
- return it_pair.first;
-}
-
-} // namespace absl
+// Do not depend on any below implementation details!
+// -----------------------------------------------------------------------------
#endif // ABSL_CONTAINER_INLINED_VECTOR_H_
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
index a3ad0f8..867a29e 100644
--- a/absl/container/inlined_vector_benchmark.cc
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -23,17 +23,13 @@
namespace {
-using IntVec = absl::InlinedVector<int, 8>;
-
void BM_InlinedVectorFill(benchmark::State& state) {
- const int len = state.range(0);
+ absl::InlinedVector<int, 8> v;
+ int val = 10;
for (auto _ : state) {
- IntVec v;
- for (int i = 0; i < len; i++) {
- v.push_back(i);
- }
+ benchmark::DoNotOptimize(v);
+ v.push_back(val);
}
- state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
}
BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024);
@@ -43,23 +39,25 @@
for (int i = 0; i < len; i++) {
ia[i] = i;
}
+ auto* from = ia.get();
+ auto* to = from + len;
for (auto _ : state) {
- IntVec v(ia.get(), ia.get() + len);
+ benchmark::DoNotOptimize(from);
+ benchmark::DoNotOptimize(to);
+ absl::InlinedVector<int, 8> v(from, to);
benchmark::DoNotOptimize(v);
}
- state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
}
BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024);
void BM_StdVectorFill(benchmark::State& state) {
- const int len = state.range(0);
+ std::vector<int> v;
+ int val = 10;
for (auto _ : state) {
- std::vector<int> v;
- for (int i = 0; i < len; i++) {
- v.push_back(i);
- }
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(val);
+ v.push_back(val);
}
- state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
}
BENCHMARK(BM_StdVectorFill)->Range(0, 1024);
@@ -89,7 +87,7 @@
const int len = state.range(0);
const int no_sso = GetNonShortStringOptimizationSize();
std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'),
- std::string(no_sso, 'C'), std::string(no_sso, 'D')};
+ std::string(no_sso, 'C'), std::string(no_sso, 'D')};
for (auto _ : state) {
absl::InlinedVector<std::string, 8> v;
@@ -105,7 +103,7 @@
const int len = state.range(0);
const int no_sso = GetNonShortStringOptimizationSize();
std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'),
- std::string(no_sso, 'C'), std::string(no_sso, 'D')};
+ std::string(no_sso, 'C'), std::string(no_sso, 'D')};
for (auto _ : state) {
std::vector<std::string> v;
@@ -124,7 +122,7 @@
void* user_data;
};
-void BM_InlinedVectorTenAssignments(benchmark::State& state) {
+void BM_InlinedVectorAssignments(benchmark::State& state) {
const int len = state.range(0);
using BufferVec = absl::InlinedVector<Buffer, 2>;
@@ -133,18 +131,25 @@
BufferVec dst;
for (auto _ : state) {
- for (int i = 0; i < 10; ++i) {
- dst = src;
- }
+ benchmark::DoNotOptimize(dst);
+ benchmark::DoNotOptimize(src);
+ dst = src;
}
}
-BENCHMARK(BM_InlinedVectorTenAssignments)
- ->Arg(0)->Arg(1)->Arg(2)->Arg(3)->Arg(4)->Arg(20);
+BENCHMARK(BM_InlinedVectorAssignments)
+ ->Arg(0)
+ ->Arg(1)
+ ->Arg(2)
+ ->Arg(3)
+ ->Arg(4)
+ ->Arg(20);
void BM_CreateFromContainer(benchmark::State& state) {
for (auto _ : state) {
- absl::InlinedVector<int, 4> x(absl::InlinedVector<int, 4>{1, 2, 3});
- benchmark::DoNotOptimize(x);
+ absl::InlinedVector<int, 4> src{1, 2, 3};
+ benchmark::DoNotOptimize(src);
+ absl::InlinedVector<int, 4> dst(std::move(src));
+ benchmark::DoNotOptimize(dst);
}
}
BENCHMARK(BM_CreateFromContainer);
@@ -159,15 +164,14 @@
struct LargeCopyableSwappable {
LargeCopyableSwappable() : d(1024, 17) {}
+
LargeCopyableSwappable(const LargeCopyableSwappable& o) = default;
- LargeCopyableSwappable(LargeCopyableSwappable&& o) = delete;
LargeCopyableSwappable& operator=(LargeCopyableSwappable o) {
using std::swap;
swap(*this, o);
return *this;
}
- LargeCopyableSwappable& operator=(LargeCopyableSwappable&& o) = delete;
friend void swap(LargeCopyableSwappable& a, LargeCopyableSwappable& b) {
using std::swap;
@@ -215,6 +219,8 @@
Vec b;
for (auto _ : state) {
using std::swap;
+ benchmark::DoNotOptimize(a);
+ benchmark::DoNotOptimize(b);
swap(a, b);
}
}
@@ -260,60 +266,44 @@
void BM_InlinedVectorIndexInlined(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
for (auto _ : state) {
- for (int i = 0; i < 1000; ++i) {
- benchmark::DoNotOptimize(v);
- benchmark::DoNotOptimize(v[4]);
- }
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v[4]);
}
- state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorIndexInlined);
void BM_InlinedVectorIndexExternal(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
- for (int i = 0; i < 1000; ++i) {
- benchmark::DoNotOptimize(v);
- benchmark::DoNotOptimize(v[4]);
- }
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v[4]);
}
- state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorIndexExternal);
void BM_StdVectorIndex(benchmark::State& state) {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
- for (int i = 0; i < 1000; ++i) {
- benchmark::DoNotOptimize(v);
- benchmark::DoNotOptimize(v[4]);
- }
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v[4]);
}
- state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_StdVectorIndex);
-#define UNROLL_2(x) \
- benchmark::DoNotOptimize(x); \
- benchmark::DoNotOptimize(x);
-
-#define UNROLL_4(x) UNROLL_2(x) UNROLL_2(x)
-#define UNROLL_8(x) UNROLL_4(x) UNROLL_4(x)
-#define UNROLL_16(x) UNROLL_8(x) UNROLL_8(x);
-
void BM_InlinedVectorDataInlined(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
for (auto _ : state) {
- UNROLL_16(v.data());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.data());
}
- state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorDataInlined);
void BM_InlinedVectorDataExternal(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
- UNROLL_16(v.data());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.data());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
@@ -322,7 +312,8 @@
void BM_StdVectorData(benchmark::State& state) {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
- UNROLL_16(v.data());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.data());
}
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
@@ -331,54 +322,54 @@
void BM_InlinedVectorSizeInlined(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
for (auto _ : state) {
- UNROLL_16(v.size());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.size());
}
- state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorSizeInlined);
void BM_InlinedVectorSizeExternal(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
- UNROLL_16(v.size());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.size());
}
- state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorSizeExternal);
void BM_StdVectorSize(benchmark::State& state) {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
- UNROLL_16(v.size());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.size());
}
- state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_StdVectorSize);
void BM_InlinedVectorEmptyInlined(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
for (auto _ : state) {
- UNROLL_16(v.empty());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.empty());
}
- state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorEmptyInlined);
void BM_InlinedVectorEmptyExternal(benchmark::State& state) {
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
- UNROLL_16(v.empty());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.empty());
}
- state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_InlinedVectorEmptyExternal);
void BM_StdVectorEmpty(benchmark::State& state) {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto _ : state) {
- UNROLL_16(v.empty());
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v.empty());
}
- state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
}
BENCHMARK(BM_StdVectorEmpty);
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index 08dcd3e..6037001 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -30,6 +30,7 @@
#include "absl/base/internal/exception_testing.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
+#include "absl/container/internal/counting_allocator.h"
#include "absl/container/internal/test_instance_tracker.h"
#include "absl/hash/hash_testing.h"
#include "absl/memory/memory.h"
@@ -37,6 +38,7 @@
namespace {
+using absl::container_internal::CountingAllocator;
using absl::test_internal::CopyableMovableInstance;
using absl::test_internal::CopyableOnlyInstance;
using absl::test_internal::InstanceTracker;
@@ -68,7 +70,7 @@
// test_instance_tracker.h.
template <typename T>
class InstanceTest : public ::testing::Test {};
-TYPED_TEST_CASE_P(InstanceTest);
+TYPED_TEST_SUITE_P(InstanceTest);
// A simple reference counted class to make sure that the proper elements are
// destroyed in the erase(begin, end) test.
@@ -138,57 +140,6 @@
return v;
}
-// This is a stateful allocator, but the state lives outside of the
-// allocator (in whatever test is using the allocator). This is odd
-// but helps in tests where the allocator is propagated into nested
-// containers - that chain of allocators uses the same state and is
-// thus easier to query for aggregate allocation information.
-template <typename T>
-class CountingAllocator : public std::allocator<T> {
- public:
- using Alloc = std::allocator<T>;
- using pointer = typename Alloc::pointer;
- using size_type = typename Alloc::size_type;
-
- CountingAllocator() : bytes_used_(nullptr) {}
- explicit CountingAllocator(int64_t* b) : bytes_used_(b) {}
-
- template <typename U>
- CountingAllocator(const CountingAllocator<U>& x)
- : Alloc(x), bytes_used_(x.bytes_used_) {}
-
- pointer allocate(size_type n,
- std::allocator<void>::const_pointer hint = nullptr) {
- assert(bytes_used_ != nullptr);
- *bytes_used_ += n * sizeof(T);
- return Alloc::allocate(n, hint);
- }
-
- void deallocate(pointer p, size_type n) {
- Alloc::deallocate(p, n);
- assert(bytes_used_ != nullptr);
- *bytes_used_ -= n * sizeof(T);
- }
-
- template<typename U>
- class rebind {
- public:
- using other = CountingAllocator<U>;
- };
-
- friend bool operator==(const CountingAllocator& a,
- const CountingAllocator& b) {
- return a.bytes_used_ == b.bytes_used_;
- }
-
- friend bool operator!=(const CountingAllocator& a,
- const CountingAllocator& b) {
- return !(a == b);
- }
-
- int64_t* bytes_used_;
-};
-
TEST(IntVec, SimpleOps) {
for (int len = 0; len < 20; len++) {
IntVec v;
@@ -906,6 +857,8 @@
InstanceTracker tracker;
InstanceVec a, b;
const size_t inlined_capacity = a.capacity();
+ auto min_len = std::min(l1, l2);
+ auto max_len = std::max(l1, l2);
for (int i = 0; i < l1; i++) a.push_back(Instance(i));
for (int i = 0; i < l2; i++) b.push_back(Instance(100+i));
EXPECT_EQ(tracker.instances(), l1 + l2);
@@ -919,15 +872,15 @@
EXPECT_EQ(tracker.swaps(), 0); // Allocations are swapped.
EXPECT_EQ(tracker.moves(), 0);
} else if (a.size() <= inlined_capacity && b.size() <= inlined_capacity) {
- EXPECT_EQ(tracker.swaps(), std::min(l1, l2));
- // TODO(bsamwel): This should use moves when the type is movable.
- EXPECT_EQ(tracker.copies(), std::max(l1, l2) - std::min(l1, l2));
+ EXPECT_EQ(tracker.swaps(), min_len);
+ EXPECT_EQ((tracker.moves() ? tracker.moves() : tracker.copies()),
+ max_len - min_len);
} else {
// One is allocated and the other isn't. The allocation is transferred
// without copying elements, and the inlined instances are copied/moved.
EXPECT_EQ(tracker.swaps(), 0);
- // TODO(bsamwel): This should use moves when the type is movable.
- EXPECT_EQ(tracker.copies(), std::min(l1, l2));
+ EXPECT_EQ((tracker.moves() ? tracker.moves() : tracker.copies()),
+ min_len);
}
EXPECT_EQ(l1, b.size());
@@ -1726,39 +1679,58 @@
std::scoped_allocator_adaptor<CountingAllocator<StdVector>>;
using AllocVec = absl::InlinedVector<StdVector, 4, MyAlloc>;
+ // MSVC 2017's std::vector allocates different amounts of memory in debug
+ // versus opt mode.
+ int64_t test_allocated = 0;
+ StdVector v(CountingAllocator<int>{&test_allocated});
+ // The amount of memory allocated by a default constructed vector<int>
+ auto default_std_vec_allocated = test_allocated;
+ v.push_back(1);
+ // The amound of memory allocated by a copy-constructed vector<int> with one
+ // element.
+ int64_t one_element_std_vec_copy_allocated = test_allocated;
+
int64_t allocated = 0;
AllocVec vec(MyAlloc{CountingAllocator<StdVector>{&allocated}});
EXPECT_EQ(allocated, 0);
// This default constructs a vector<int>, but the allocator should pass itself
- // into the vector<int>.
+ // into the vector<int>, so check allocation compared to that.
// The absl::InlinedVector does not allocate any memory.
- // The vector<int> does not allocate any memory.
+ // The vector<int> may allocate any memory.
+ auto expected = default_std_vec_allocated;
vec.resize(1);
- EXPECT_EQ(allocated, 0);
+ EXPECT_EQ(allocated, expected);
// We make vector<int> allocate memory.
// It must go through the allocator even though we didn't construct the
- // vector directly.
+ // vector directly. This assumes that vec[0] doesn't need to grow its
+ // allocation.
+ expected += sizeof(int);
vec[0].push_back(1);
- EXPECT_EQ(allocated, sizeof(int) * 1);
+ EXPECT_EQ(allocated, expected);
// Another allocating vector.
+ expected += one_element_std_vec_copy_allocated;
vec.push_back(vec[0]);
- EXPECT_EQ(allocated, sizeof(int) * 2);
+ EXPECT_EQ(allocated, expected);
// Overflow the inlined memory.
// The absl::InlinedVector will now allocate.
+ expected += sizeof(StdVector) * 8 + default_std_vec_allocated * 3;
vec.resize(5);
- EXPECT_EQ(allocated, sizeof(int) * 2 + sizeof(StdVector) * 8);
+ EXPECT_EQ(allocated, expected);
// Adding one more in external mode should also work.
+ expected += one_element_std_vec_copy_allocated;
vec.push_back(vec[0]);
- EXPECT_EQ(allocated, sizeof(int) * 3 + sizeof(StdVector) * 8);
+ EXPECT_EQ(allocated, expected);
- // And extending these should still work.
+ // And extending these should still work. This assumes that vec[0] does not
+ // need to grow its allocation.
+ expected += sizeof(int);
vec[0].push_back(1);
- EXPECT_EQ(allocated, sizeof(int) * 4 + sizeof(StdVector) * 8);
+ EXPECT_EQ(allocated, expected);
vec.clear();
EXPECT_EQ(allocated, 0);
@@ -1790,4 +1762,23 @@
}
}
+TEST(InlinedVectorTest, AbslHashValueWorks) {
+ using V = absl::InlinedVector<int, 4>;
+ std::vector<V> cases;
+
+ // Generate a variety of vectors some of these are small enough for the inline
+ // space but are stored out of line.
+ for (int i = 0; i < 10; ++i) {
+ V v;
+ for (int j = 0; j < i; ++j) {
+ v.push_back(j);
+ }
+ cases.push_back(v);
+ v.resize(i % 4);
+ cases.push_back(v);
+ }
+
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
+}
+
} // anonymous namespace
diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h
new file mode 100644
index 0000000..b06e711
--- /dev/null
+++ b/absl/container/internal/common.h
@@ -0,0 +1,183 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_H_
+#define ABSL_CONTAINER_INTERNAL_CONTAINER_H_
+
+#include <cassert>
+#include <type_traits>
+
+#include "absl/meta/type_traits.h"
+#include "absl/types/optional.h"
+
+namespace absl {
+namespace container_internal {
+
+template <class, class = void>
+struct IsTransparent : std::false_type {};
+template <class T>
+struct IsTransparent<T, absl::void_t<typename T::is_transparent>>
+ : std::true_type {};
+
+template <bool is_transparent>
+struct KeyArg {
+ // Transparent. Forward `K`.
+ template <typename K, typename key_type>
+ using type = K;
+};
+
+template <>
+struct KeyArg<false> {
+ // Not transparent. Always use `key_type`.
+ template <typename K, typename key_type>
+ using type = key_type;
+};
+
+// The node_handle concept from C++17.
+// We specialize node_handle for sets and maps. node_handle_base holds the
+// common API of both.
+template <typename PolicyTraits, typename Alloc>
+class node_handle_base {
+ protected:
+ using slot_type = typename PolicyTraits::slot_type;
+
+ public:
+ using allocator_type = Alloc;
+
+ constexpr node_handle_base() {}
+ node_handle_base(node_handle_base&& other) noexcept {
+ *this = std::move(other);
+ }
+ ~node_handle_base() { destroy(); }
+ node_handle_base& operator=(node_handle_base&& other) noexcept {
+ destroy();
+ if (!other.empty()) {
+ alloc_ = other.alloc_;
+ PolicyTraits::transfer(alloc(), slot(), other.slot());
+ other.reset();
+ }
+ return *this;
+ }
+
+ bool empty() const noexcept { return !alloc_; }
+ explicit operator bool() const noexcept { return !empty(); }
+ allocator_type get_allocator() const { return *alloc_; }
+
+ protected:
+ friend struct CommonAccess;
+
+ node_handle_base(const allocator_type& a, slot_type* s) : alloc_(a) {
+ PolicyTraits::transfer(alloc(), slot(), s);
+ }
+
+ void destroy() {
+ if (!empty()) {
+ PolicyTraits::destroy(alloc(), slot());
+ reset();
+ }
+ }
+
+ void reset() {
+ assert(alloc_.has_value());
+ alloc_ = absl::nullopt;
+ }
+
+ slot_type* slot() const {
+ assert(!empty());
+ return reinterpret_cast<slot_type*>(std::addressof(slot_space_));
+ }
+ allocator_type* alloc() { return std::addressof(*alloc_); }
+
+ private:
+ absl::optional<allocator_type> alloc_;
+ mutable absl::aligned_storage_t<sizeof(slot_type), alignof(slot_type)>
+ slot_space_;
+};
+
+// For sets.
+template <typename Policy, typename PolicyTraits, typename Alloc,
+ typename = void>
+class node_handle : public node_handle_base<PolicyTraits, Alloc> {
+ using Base = typename node_handle::node_handle_base;
+
+ public:
+ using value_type = typename PolicyTraits::value_type;
+
+ constexpr node_handle() {}
+
+ value_type& value() const { return PolicyTraits::element(this->slot()); }
+
+ private:
+ friend struct CommonAccess;
+
+ node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
+};
+
+// For maps.
+template <typename Policy, typename PolicyTraits, typename Alloc>
+class node_handle<Policy, PolicyTraits, Alloc,
+ absl::void_t<typename Policy::mapped_type>>
+ : public node_handle_base<PolicyTraits, Alloc> {
+ using Base = typename node_handle::node_handle_base;
+
+ public:
+ using key_type = typename Policy::key_type;
+ using mapped_type = typename Policy::mapped_type;
+
+ constexpr node_handle() {}
+
+ auto key() const -> decltype(PolicyTraits::key(this->slot())) {
+ return PolicyTraits::key(this->slot());
+ }
+
+ mapped_type& mapped() const {
+ return PolicyTraits::value(&PolicyTraits::element(this->slot()));
+ }
+
+ private:
+ friend struct CommonAccess;
+
+ node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
+};
+
+// Provide access to non-public node-handle functions.
+struct CommonAccess {
+ template <typename Node>
+ static auto GetSlot(const Node& node) -> decltype(node.slot()) {
+ return node.slot();
+ }
+
+ template <typename Node>
+ static void Reset(Node* node) {
+ node->reset();
+ }
+
+ template <typename T, typename... Args>
+ static T Make(Args&&... args) {
+ return T(std::forward<Args>(args)...);
+ }
+};
+
+// Implement the insert_return_type<> concept of C++17.
+template <class Iterator, class NodeType>
+struct InsertReturnType {
+ Iterator position;
+ bool inserted;
+ NodeType node;
+};
+
+} // namespace container_internal
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_H_
diff --git a/absl/container/internal/compressed_tuple.h b/absl/container/internal/compressed_tuple.h
index cc52614..b9bd91a 100644
--- a/absl/container/internal/compressed_tuple.h
+++ b/absl/container/internal/compressed_tuple.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,7 +27,7 @@
// const T2& t2 = value.get<2>();
// ...
//
-// http://en.cppreference.com/w/cpp/language/ebo
+// https://en.cppreference.com/w/cpp/language/ebo
#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
@@ -89,8 +89,10 @@
T value;
constexpr Storage() = default;
explicit constexpr Storage(T&& v) : value(absl::forward<T>(v)) {}
- constexpr const T& get() const { return value; }
- T& get() { return value; }
+ constexpr const T& get() const& { return value; }
+ T& get() & { return value; }
+ constexpr const T&& get() const&& { return absl::move(*this).value; }
+ T&& get() && { return std::move(*this).value; }
};
template <typename D, size_t I>
@@ -99,8 +101,10 @@
using T = internal_compressed_tuple::ElemT<D, I>;
constexpr Storage() = default;
explicit constexpr Storage(T&& v) : T(absl::forward<T>(v)) {}
- constexpr const T& get() const { return *this; }
- T& get() { return *this; }
+ constexpr const T& get() const& { return *this; }
+ T& get() & { return *this; }
+ constexpr const T&& get() const&& { return absl::move(*this); }
+ T&& get() && { return std::move(*this); }
};
template <typename D, typename I>
@@ -137,7 +141,7 @@
// const T2& t2 = value.get<2>();
// ...
//
-// http://en.cppreference.com/w/cpp/language/ebo
+// https://en.cppreference.com/w/cpp/language/ebo
template <typename... Ts>
class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple
: private internal_compressed_tuple::CompressedTupleImpl<
@@ -152,14 +156,26 @@
: CompressedTuple::CompressedTupleImpl(absl::forward<Ts>(base)...) {}
template <int I>
- ElemT<I>& get() {
+ ElemT<I>& get() & {
return internal_compressed_tuple::Storage<CompressedTuple, I>::get();
}
template <int I>
- constexpr const ElemT<I>& get() const {
+ constexpr const ElemT<I>& get() const& {
return internal_compressed_tuple::Storage<CompressedTuple, I>::get();
}
+
+ template <int I>
+ ElemT<I>&& get() && {
+ return std::move(*this)
+ .internal_compressed_tuple::template Storage<CompressedTuple, I>::get();
+ }
+
+ template <int I>
+ constexpr const ElemT<I>&& get() const&& {
+ return absl::move(*this)
+ .internal_compressed_tuple::template Storage<CompressedTuple, I>::get();
+ }
};
// Explicit specialization for a zero-element tuple
diff --git a/absl/container/internal/compressed_tuple_test.cc b/absl/container/internal/compressed_tuple_test.cc
index 45030c6..28e7741 100644
--- a/absl/container/internal/compressed_tuple_test.cc
+++ b/absl/container/internal/compressed_tuple_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,17 +14,25 @@
#include "absl/container/internal/compressed_tuple.h"
+#include <memory>
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
+#include "absl/utility/utility.h"
namespace absl {
namespace container_internal {
namespace {
+enum class CallType { kConstRef, kConstMove };
+
template <int>
-struct Empty {};
+struct Empty {
+ constexpr CallType value() const& { return CallType::kConstRef; }
+ constexpr CallType value() const&& { return CallType::kConstMove; }
+};
template <typename T>
struct NotEmpty {
@@ -140,15 +148,44 @@
EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value);
}
+TEST(CompressedTupleTest, MoveOnlyElements) {
+ CompressedTuple<std::unique_ptr<std::string>> str_tup(
+ absl::make_unique<std::string>("str"));
+
+ CompressedTuple<CompressedTuple<std::unique_ptr<std::string>>,
+ std::unique_ptr<int>>
+ x(std::move(str_tup), absl::make_unique<int>(5));
+
+ EXPECT_EQ(*x.get<0>().get<0>(), "str");
+ EXPECT_EQ(*x.get<1>(), 5);
+
+ std::unique_ptr<std::string> x0 = std::move(x.get<0>()).get<0>();
+ std::unique_ptr<int> x1 = std::move(x).get<1>();
+
+ EXPECT_EQ(*x0, "str");
+ EXPECT_EQ(*x1, 5);
+}
+
TEST(CompressedTupleTest, Constexpr) {
- constexpr CompressedTuple<int, double, CompressedTuple<int>> x(
- 7, 1.25, CompressedTuple<int>(5));
+ constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x(
+ 7, 1.25, CompressedTuple<int>(5), {});
constexpr int x0 = x.get<0>();
constexpr double x1 = x.get<1>();
constexpr int x2 = x.get<2>().get<0>();
+ constexpr CallType x3 = x.get<3>().value();
+
EXPECT_EQ(x0, 7);
EXPECT_EQ(x1, 1.25);
EXPECT_EQ(x2, 5);
+ EXPECT_EQ(x3, CallType::kConstRef);
+
+#if defined(__clang__)
+ // An apparent bug in earlier versions of gcc claims these are ambiguous.
+ constexpr int x2m = absl::move(x.get<2>()).get<0>();
+ constexpr CallType x3m = absl::move(x).get<3>().value();
+ EXPECT_EQ(x2m, 5);
+ EXPECT_EQ(x3m, CallType::kConstMove);
+#endif
}
#if defined(__clang__) || defined(__GNUC__)
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h
index 56c5d2d..e5bb977 100644
--- a/absl/container/internal/container_memory.h
+++ b/absl/container/internal/container_memory.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -286,13 +286,48 @@
} // namespace memory_internal
-// If kMutableKeys is false, only the value member is accessed.
+// The internal storage type for key-value containers like flat_hash_map.
//
-// If kMutableKeys is true, key is accessed through all slots while value and
-// mutable_value are accessed only via INITIALIZED slots. Slots are created and
-// destroyed via mutable_value so that the key can be moved later.
+// It is convenient for the value_type of a flat_hash_map<K, V> to be
+// pair<const K, V>; the "const K" prevents accidental modification of the key
+// when dealing with the reference returned from find() and similar methods.
+// However, this creates other problems; we want to be able to emplace(K, V)
+// efficiently with move operations, and similarly be able to move a
+// pair<K, V> in insert().
+//
+// The solution is this union, which aliases the const and non-const versions
+// of the pair. This also allows flat_hash_map<const K, V> to work, even though
+// that has the same efficiency issues with move in emplace() and insert() -
+// but people do it anyway.
+//
+// If kMutableKeys is false, only the value member can be accessed.
+//
+// If kMutableKeys is true, key can be accessed through all slots while value
+// and mutable_value must be accessed only via INITIALIZED slots. Slots are
+// created and destroyed via mutable_value so that the key can be moved later.
+//
+// Accessing one of the union fields while the other is active is safe as
+// long as they are layout-compatible, which is guaranteed by the definition of
+// kMutableKeys. For C++11, the relevant section of the standard is
+// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19)
template <class K, class V>
-union slot_type {
+union map_slot_type {
+ map_slot_type() {}
+ ~map_slot_type() = delete;
+ using value_type = std::pair<const K, V>;
+ using mutable_value_type = std::pair<K, V>;
+
+ value_type value;
+ mutable_value_type mutable_value;
+ K key;
+};
+
+template <class K, class V>
+struct map_slot_policy {
+ using slot_type = map_slot_type<K, V>;
+ using value_type = std::pair<const K, V>;
+ using mutable_value_type = std::pair<K, V>;
+
private:
static void emplace(slot_type* slot) {
// The construction of union doesn't do anything at runtime but it allows us
@@ -302,19 +337,17 @@
// If pair<const K, V> and pair<K, V> are layout-compatible, we can accept one
// or the other via slot_type. We are also free to access the key via
// slot_type::key in this case.
- using kMutableKeys =
- std::integral_constant<bool,
- memory_internal::IsLayoutCompatible<K, V>::value>;
+ using kMutableKeys = memory_internal::IsLayoutCompatible<K, V>;
public:
- slot_type() {}
- ~slot_type() = delete;
- using value_type = std::pair<const K, V>;
- using mutable_value_type = std::pair<K, V>;
+ static value_type& element(slot_type* slot) { return slot->value; }
+ static const value_type& element(const slot_type* slot) {
+ return slot->value;
+ }
- value_type value;
- mutable_value_type mutable_value;
- K key;
+ static const K& key(const slot_type* slot) {
+ return kMutableKeys::value ? slot->key : slot->value.first;
+ }
template <class Allocator, class... Args>
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
diff --git a/absl/container/internal/container_memory_test.cc b/absl/container/internal/container_memory_test.cc
index f1c4058..d6b0495 100644
--- a/absl/container/internal/container_memory_test.cc
+++ b/absl/container/internal/container_memory_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/counting_allocator.h b/absl/container/internal/counting_allocator.h
new file mode 100644
index 0000000..4e717be
--- /dev/null
+++ b/absl/container/internal/counting_allocator.h
@@ -0,0 +1,79 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
+#define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
+
+#include <cassert>
+#include <cstdint>
+#include <memory>
+
+namespace absl {
+namespace container_internal {
+
+// This is a stateful allocator, but the state lives outside of the
+// allocator (in whatever test is using the allocator). This is odd
+// but helps in tests where the allocator is propagated into nested
+// containers - that chain of allocators uses the same state and is
+// thus easier to query for aggregate allocation information.
+template <typename T>
+class CountingAllocator : public std::allocator<T> {
+ public:
+ using Alloc = std::allocator<T>;
+ using pointer = typename Alloc::pointer;
+ using size_type = typename Alloc::size_type;
+
+ CountingAllocator() : bytes_used_(nullptr) {}
+ explicit CountingAllocator(int64_t* b) : bytes_used_(b) {}
+
+ template <typename U>
+ CountingAllocator(const CountingAllocator<U>& x)
+ : Alloc(x), bytes_used_(x.bytes_used_) {}
+
+ pointer allocate(size_type n,
+ std::allocator<void>::const_pointer hint = nullptr) {
+ assert(bytes_used_ != nullptr);
+ *bytes_used_ += n * sizeof(T);
+ return Alloc::allocate(n, hint);
+ }
+
+ void deallocate(pointer p, size_type n) {
+ Alloc::deallocate(p, n);
+ assert(bytes_used_ != nullptr);
+ *bytes_used_ -= n * sizeof(T);
+ }
+
+ template<typename U>
+ class rebind {
+ public:
+ using other = CountingAllocator<U>;
+ };
+
+ friend bool operator==(const CountingAllocator& a,
+ const CountingAllocator& b) {
+ return a.bytes_used_ == b.bytes_used_;
+ }
+
+ friend bool operator!=(const CountingAllocator& a,
+ const CountingAllocator& b) {
+ return !(a == b);
+ }
+
+ int64_t* bytes_used_;
+};
+
+} // namespace container_internal
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
diff --git a/absl/container/internal/hash_function_defaults.h b/absl/container/internal/hash_function_defaults.h
index 1f0d794..cb8f03c 100644
--- a/absl/container/internal/hash_function_defaults.h
+++ b/absl/container/internal/hash_function_defaults.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -39,8 +39,8 @@
// equal functions are still bound to T. This is important because some type U
// can be hashed by/tested for equality differently depending on T. A notable
// example is `const char*`. `const char*` is treated as a c-style string when
-// the hash function is hash<string> but as a pointer when the hash function is
-// hash<void*>.
+// the hash function is hash<std::string> but as a pointer when the hash
+// function is hash<void*>.
//
#ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
#define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
@@ -83,6 +83,7 @@
}
};
};
+
template <>
struct HashEq<std::string> : StringHashEq {};
template <>
diff --git a/absl/container/internal/hash_function_defaults_test.cc b/absl/container/internal/hash_function_defaults_test.cc
index 464baae..82708db 100644
--- a/absl/container/internal/hash_function_defaults_test.cc
+++ b/absl/container/internal/hash_function_defaults_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -77,14 +77,14 @@
hash_default_eq<T> key_eq;
};
-TYPED_TEST_CASE(EqString, StringTypes);
+TYPED_TEST_SUITE(EqString, StringTypes);
template <class T>
struct HashString : ::testing::Test {
hash_default_hash<T> hasher;
};
-TYPED_TEST_CASE(HashString, StringTypes);
+TYPED_TEST_SUITE(HashString, StringTypes);
TYPED_TEST(EqString, Works) {
auto eq = this->key_eq;
@@ -121,14 +121,14 @@
hash_default_eq<T> key_eq;
};
-TYPED_TEST_CASE(EqPointer, PointerTypes);
+TYPED_TEST_SUITE(EqPointer, PointerTypes);
template <class T>
struct HashPointer : ::testing::Test {
hash_default_hash<T> hasher;
};
-TYPED_TEST_CASE(HashPointer, PointerTypes);
+TYPED_TEST_SUITE(HashPointer, PointerTypes);
TYPED_TEST(EqPointer, Works) {
int dummy;
@@ -202,15 +202,11 @@
EXPECT_NE(hash(&dummy), hash(cuptr));
}
-// Cartesian product of (string, std::string, absl::string_view)
-// with (string, std::string, absl::string_view, const char*).
+// Cartesian product of (std::string, absl::string_view)
+// with (std::string, absl::string_view, const char*).
using StringTypesCartesianProduct = Types<
// clang-format off
- std::pair<std::string, std::string>,
- std::pair<std::string, absl::string_view>,
- std::pair<std::string, const char*>,
-
std::pair<absl::string_view, std::string>,
std::pair<absl::string_view, absl::string_view>,
std::pair<absl::string_view, const char*>>;
@@ -248,7 +244,7 @@
EXPECT_NE(this->hash(this->a1), this->hash(this->b2));
}
-TYPED_TEST_CASE(StringLikeTest, StringTypesCartesianProduct);
+TYPED_TEST_SUITE(StringLikeTest, StringTypesCartesianProduct);
} // namespace
} // namespace container_internal
diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc
index 0d6a9df..37a23d6 100644
--- a/absl/container/internal/hash_generator_testing.cc
+++ b/absl/container/internal/hash_generator_testing.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -39,9 +39,9 @@
} // namespace
-std::mt19937_64* GetThreadLocalRng() {
+std::mt19937_64* GetSharedRng() {
RandomDeviceSeedSeq seed_seq;
- thread_local auto* rng = new std::mt19937_64(seed_seq);
+ static auto* rng = new std::mt19937_64(seed_seq);
return rng;
}
@@ -51,7 +51,7 @@
std::string res;
res.resize(32);
std::generate(res.begin(), res.end(),
- [&]() { return chars(*GetThreadLocalRng()); });
+ [&]() { return chars(*GetSharedRng()); });
return res;
}
@@ -63,7 +63,7 @@
auto& res = arena->back();
res.resize(32);
std::generate(res.begin(), res.end(),
- [&]() { return chars(*GetThreadLocalRng()); });
+ [&]() { return chars(*GetSharedRng()); });
return res;
}
diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h
index 50d7710..27fb84f 100644
--- a/absl/container/internal/hash_generator_testing.h
+++ b/absl/container/internal/hash_generator_testing.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -43,7 +43,7 @@
} // namespace generator_internal
-std::mt19937_64* GetThreadLocalRng();
+std::mt19937_64* GetSharedRng();
enum Enum {
kEnumEmpty,
@@ -66,7 +66,7 @@
struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> {
T operator()() const {
std::uniform_int_distribution<T> dist;
- return dist(*GetThreadLocalRng());
+ return dist(*GetSharedRng());
}
};
@@ -76,7 +76,7 @@
std::uniform_int_distribution<typename std::underlying_type<Enum>::type>
dist;
while (true) {
- auto variate = dist(*GetThreadLocalRng());
+ auto variate = dist(*GetSharedRng());
if (variate != kEnumEmpty && variate != kEnumDeleted)
return static_cast<Enum>(variate);
}
@@ -90,7 +90,7 @@
typename std::underlying_type<EnumClass>::type>
dist;
while (true) {
- EnumClass variate = static_cast<EnumClass>(dist(*GetThreadLocalRng()));
+ EnumClass variate = static_cast<EnumClass>(dist(*GetSharedRng()));
if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted)
return static_cast<EnumClass>(variate);
}
diff --git a/absl/container/internal/hash_policy_testing.h b/absl/container/internal/hash_policy_testing.h
index ffc76ea..c57407a 100644
--- a/absl/container/internal/hash_policy_testing.h
+++ b/absl/container/internal/hash_policy_testing.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -139,7 +139,7 @@
friend bool operator!=(const Alloc& a, const Alloc& b) { return !(a == b); }
private:
- size_t id_ = std::numeric_limits<size_t>::max();
+ size_t id_ = (std::numeric_limits<size_t>::max)();
};
template <class Map>
@@ -169,7 +169,11 @@
// take allocator arguments. This test is defined ad-hoc for the platforms
// we care about (notably Crosstool 17) because libstdcxx's useless
// versioning scheme precludes a more principled solution.
-#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425
+// From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html)
+// "the unordered associative containers in <unordered_map> and <unordered_set>
+// meet the allocator-aware container requirements;"
+#if (defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425 ) || \
+( __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9 ))
#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 0
#else
#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 1
diff --git a/absl/container/internal/hash_policy_testing_test.cc b/absl/container/internal/hash_policy_testing_test.cc
index c215c42..0c95eb5 100644
--- a/absl/container/internal/hash_policy_testing_test.cc
+++ b/absl/container/internal/hash_policy_testing_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/hash_policy_traits.h b/absl/container/internal/hash_policy_traits.h
index 029e47e..fd007de 100644
--- a/absl/container/internal/hash_policy_traits.h
+++ b/absl/container/internal/hash_policy_traits.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -84,7 +84,7 @@
}
// Transfers the `old_slot` to `new_slot`. Any memory allocated by the
- // allocator inside `old_slot` to `new_slot` can be transfered.
+ // allocator inside `old_slot` to `new_slot` can be transferred.
//
// OPTIONAL: defaults to:
//
diff --git a/absl/container/internal/hash_policy_traits_test.cc b/absl/container/internal/hash_policy_traits_test.cc
index 423f154..e643d18 100644
--- a/absl/container/internal/hash_policy_traits_test.cc
+++ b/absl/container/internal/hash_policy_traits_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h
index c3bd65c..7193000 100644
--- a/absl/container/internal/hashtable_debug.h
+++ b/absl/container/internal/hashtable_debug.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -60,7 +60,7 @@
size_t num_probes = GetHashtableDebugNumProbes(
container,
absl::container_internal::hashtable_debug_internal::GetKey<C>(*it, 0));
- v.resize(std::max(v.size(), num_probes + 1));
+ v.resize((std::max)(v.size(), num_probes + 1));
v[num_probes]++;
}
return v;
diff --git a/absl/container/internal/hashtable_debug_hooks.h b/absl/container/internal/hashtable_debug_hooks.h
index 8f21972..371ce81 100644
--- a/absl/container/internal/hashtable_debug_hooks.h
+++ b/absl/container/internal/hashtable_debug_hooks.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc
new file mode 100644
index 0000000..6667d3a
--- /dev/null
+++ b/absl/container/internal/hashtablez_sampler.cc
@@ -0,0 +1,308 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/internal/hashtablez_sampler.h"
+
+#include <atomic>
+#include <cassert>
+#include <cmath>
+#include <functional>
+#include <limits>
+
+#include "absl/base/attributes.h"
+#include "absl/container/internal/have_sse.h"
+#include "absl/debugging/stacktrace.h"
+#include "absl/memory/memory.h"
+#include "absl/synchronization/mutex.h"
+
+namespace absl {
+namespace container_internal {
+constexpr int HashtablezInfo::kMaxStackDepth;
+
+namespace {
+ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{
+ false
+};
+ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10};
+ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20};
+
+// Returns the next pseudo-random value.
+// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48
+// This is the lrand64 generator.
+uint64_t NextRandom(uint64_t rnd) {
+ const uint64_t prng_mult = uint64_t{0x5DEECE66D};
+ const uint64_t prng_add = 0xB;
+ const uint64_t prng_mod_power = 48;
+ const uint64_t prng_mod_mask = ~(~uint64_t{0} << prng_mod_power);
+ return (prng_mult * rnd + prng_add) & prng_mod_mask;
+}
+
+// Generates a geometric variable with the specified mean.
+// This is done by generating a random number between 0 and 1 and applying
+// the inverse cumulative distribution function for an exponential.
+// Specifically: Let m be the inverse of the sample period, then
+// the probability distribution function is m*exp(-mx) so the CDF is
+// p = 1 - exp(-mx), so
+// q = 1 - p = exp(-mx)
+// log_e(q) = -mx
+// -log_e(q)/m = x
+// log_2(q) * (-log_e(2) * 1/m) = x
+// In the code, q is actually in the range 1 to 2**26, hence the -26 below
+//
+int64_t GetGeometricVariable(int64_t mean) {
+#if ABSL_HAVE_THREAD_LOCAL
+ thread_local
+#else // ABSL_HAVE_THREAD_LOCAL
+ // SampleSlow and hence GetGeometricVariable is guarded by a single mutex when
+ // there are not thread locals. Thus, a single global rng is acceptable for
+ // that case.
+ static
+#endif // ABSL_HAVE_THREAD_LOCAL
+ uint64_t rng = []() {
+ // We don't get well distributed numbers from this so we call
+ // NextRandom() a bunch to mush the bits around. We use a global_rand
+ // to handle the case where the same thread (by memory address) gets
+ // created and destroyed repeatedly.
+ ABSL_CONST_INIT static std::atomic<uint32_t> global_rand(0);
+ uint64_t r = reinterpret_cast<uint64_t>(&rng) +
+ global_rand.fetch_add(1, std::memory_order_relaxed);
+ for (int i = 0; i < 20; ++i) {
+ r = NextRandom(r);
+ }
+ return r;
+ }();
+
+ rng = NextRandom(rng);
+
+ // Take the top 26 bits as the random number
+ // (This plus the 1<<58 sampling bound give a max possible step of
+ // 5194297183973780480 bytes.)
+ const uint64_t prng_mod_power = 48; // Number of bits in prng
+ // The uint32_t cast is to prevent a (hard-to-reproduce) NAN
+ // under piii debug for some binaries.
+ double q = static_cast<uint32_t>(rng >> (prng_mod_power - 26)) + 1.0;
+ // Put the computed p-value through the CDF of a geometric.
+ double interval = (log2(q) - 26) * (-std::log(2.0) * mean);
+
+ // Very large values of interval overflow int64_t. If we happen to
+ // hit such improbable condition, we simply cheat and clamp interval
+ // to largest supported value.
+ if (interval > static_cast<double>(std::numeric_limits<int64_t>::max() / 2)) {
+ return std::numeric_limits<int64_t>::max() / 2;
+ }
+
+ // Small values of interval are equivalent to just sampling next time.
+ if (interval < 1) {
+ return 1;
+ }
+ return static_cast<int64_t>(interval);
+}
+
+} // namespace
+
+HashtablezSampler& HashtablezSampler::Global() {
+ static auto* sampler = new HashtablezSampler();
+ return *sampler;
+}
+
+HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback(
+ DisposeCallback f) {
+ return dispose_.exchange(f, std::memory_order_relaxed);
+}
+
+HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
+HashtablezInfo::~HashtablezInfo() = default;
+
+void HashtablezInfo::PrepareForSampling() {
+ capacity.store(0, std::memory_order_relaxed);
+ size.store(0, std::memory_order_relaxed);
+ num_erases.store(0, std::memory_order_relaxed);
+ max_probe_length.store(0, std::memory_order_relaxed);
+ total_probe_length.store(0, std::memory_order_relaxed);
+ hashes_bitwise_or.store(0, std::memory_order_relaxed);
+ hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed);
+
+ create_time = absl::Now();
+ // The inliner makes hardcoded skip_count difficult (especially when combined
+ // with LTO). We use the ability to exclude stacks by regex when encoding
+ // instead.
+ depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth,
+ /* skip_count= */ 0);
+ dead = nullptr;
+}
+
+HashtablezSampler::HashtablezSampler()
+ : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) {
+ absl::MutexLock l(&graveyard_.init_mu);
+ graveyard_.dead = &graveyard_;
+}
+
+HashtablezSampler::~HashtablezSampler() {
+ HashtablezInfo* s = all_.load(std::memory_order_acquire);
+ while (s != nullptr) {
+ HashtablezInfo* next = s->next;
+ delete s;
+ s = next;
+ }
+}
+
+void HashtablezSampler::PushNew(HashtablezInfo* sample) {
+ sample->next = all_.load(std::memory_order_relaxed);
+ while (!all_.compare_exchange_weak(sample->next, sample,
+ std::memory_order_release,
+ std::memory_order_relaxed)) {
+ }
+}
+
+void HashtablezSampler::PushDead(HashtablezInfo* sample) {
+ if (auto* dispose = dispose_.load(std::memory_order_relaxed)) {
+ dispose(*sample);
+ }
+
+ absl::MutexLock graveyard_lock(&graveyard_.init_mu);
+ absl::MutexLock sample_lock(&sample->init_mu);
+ sample->dead = graveyard_.dead;
+ graveyard_.dead = sample;
+}
+
+HashtablezInfo* HashtablezSampler::PopDead() {
+ absl::MutexLock graveyard_lock(&graveyard_.init_mu);
+
+ // The list is circular, so eventually it collapses down to
+ // graveyard_.dead == &graveyard_
+ // when it is empty.
+ HashtablezInfo* sample = graveyard_.dead;
+ if (sample == &graveyard_) return nullptr;
+
+ absl::MutexLock sample_lock(&sample->init_mu);
+ graveyard_.dead = sample->dead;
+ sample->PrepareForSampling();
+ return sample;
+}
+
+HashtablezInfo* HashtablezSampler::Register() {
+ int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed);
+ if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) {
+ size_estimate_.fetch_sub(1, std::memory_order_relaxed);
+ dropped_samples_.fetch_add(1, std::memory_order_relaxed);
+ return nullptr;
+ }
+
+ HashtablezInfo* sample = PopDead();
+ if (sample == nullptr) {
+ // Resurrection failed. Hire a new warlock.
+ sample = new HashtablezInfo();
+ PushNew(sample);
+ }
+
+ return sample;
+}
+
+void HashtablezSampler::Unregister(HashtablezInfo* sample) {
+ PushDead(sample);
+ size_estimate_.fetch_sub(1, std::memory_order_relaxed);
+}
+
+int64_t HashtablezSampler::Iterate(
+ const std::function<void(const HashtablezInfo& stack)>& f) {
+ HashtablezInfo* s = all_.load(std::memory_order_acquire);
+ while (s != nullptr) {
+ absl::MutexLock l(&s->init_mu);
+ if (s->dead == nullptr) {
+ f(*s);
+ }
+ s = s->next;
+ }
+
+ return dropped_samples_.load(std::memory_order_relaxed);
+}
+
+HashtablezInfo* SampleSlow(int64_t* next_sample) {
+ if (kAbslContainerInternalSampleEverything) {
+ *next_sample = 1;
+ return HashtablezSampler::Global().Register();
+ }
+
+ bool first = *next_sample < 0;
+ *next_sample = GetGeometricVariable(
+ g_hashtablez_sample_parameter.load(std::memory_order_relaxed));
+
+ // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold
+ // low enough that we will start sampling in a reasonable time, so we just use
+ // the default sampling rate.
+ if (!g_hashtablez_enabled.load(std::memory_order_relaxed)) return nullptr;
+
+ // We will only be negative on our first count, so we should just retry in
+ // that case.
+ if (first) {
+ if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
+ return SampleSlow(next_sample);
+ }
+
+ return HashtablezSampler::Global().Register();
+}
+
+#if ABSL_PER_THREAD_TLS == 1
+ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0;
+#endif // ABSL_PER_THREAD_TLS == 1
+
+void UnsampleSlow(HashtablezInfo* info) {
+ HashtablezSampler::Global().Unregister(info);
+}
+
+void RecordInsertSlow(HashtablezInfo* info, size_t hash,
+ size_t distance_from_desired) {
+ // SwissTables probe in groups of 16, so scale this to count items probes and
+ // not offset from desired.
+ size_t probe_length = distance_from_desired;
+#if SWISSTABLE_HAVE_SSE2
+ probe_length /= 16;
+#else
+ probe_length /= 8;
+#endif
+
+ info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed);
+ info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed);
+ info->max_probe_length.store(
+ std::max(info->max_probe_length.load(std::memory_order_relaxed),
+ probe_length),
+ std::memory_order_relaxed);
+ info->total_probe_length.fetch_add(probe_length, std::memory_order_relaxed);
+ info->size.fetch_add(1, std::memory_order_relaxed);
+}
+
+void SetHashtablezEnabled(bool enabled) {
+ g_hashtablez_enabled.store(enabled, std::memory_order_release);
+}
+
+void SetHashtablezSampleParameter(int32_t rate) {
+ if (rate > 0) {
+ g_hashtablez_sample_parameter.store(rate, std::memory_order_release);
+ } else {
+ ABSL_RAW_LOG(ERROR, "Invalid hashtablez sample rate: %lld",
+ static_cast<long long>(rate)); // NOLINT(runtime/int)
+ }
+}
+
+void SetHashtablezMaxSamples(int32_t max) {
+ if (max > 0) {
+ g_hashtablez_max_samples.store(max, std::memory_order_release);
+ } else {
+ ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld",
+ static_cast<long long>(max)); // NOLINT(runtime/int)
+ }
+}
+
+} // namespace container_internal
+} // namespace absl
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
new file mode 100644
index 0000000..a308e78
--- /dev/null
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -0,0 +1,288 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: hashtablez_sampler.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the API for a low level library to sample hashtables
+// and collect runtime statistics about them.
+//
+// `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which
+// store information about a single sample.
+//
+// `Record*` methods store information into samples.
+// `Sample()` and `Unsample()` make use of a single global sampler with
+// properties controlled by the flags hashtablez_enabled,
+// hashtablez_sample_rate, and hashtablez_max_samples.
+//
+// WARNING
+//
+// Using this sampling API may cause sampled Swiss tables to use the global
+// allocator (operator `new`) in addition to any custom allocator. If you
+// are using a table in an unusual circumstance where allocation or calling a
+// linux syscall is unacceptable, this could interfere.
+//
+// This utility is internal-only. Use at your own risk.
+
+#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
+#define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "absl/base/internal/per_thread_tls.h"
+#include "absl/base/optimization.h"
+#include "absl/container/internal/have_sse.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+namespace container_internal {
+
+// Stores information about a sampled hashtable. All mutations to this *must*
+// be made through `Record*` functions below. All reads from this *must* only
+// occur in the callback to `HashtablezSampler::Iterate`.
+struct HashtablezInfo {
+ // Constructs the object but does not fill in any fields.
+ HashtablezInfo();
+ ~HashtablezInfo();
+ HashtablezInfo(const HashtablezInfo&) = delete;
+ HashtablezInfo& operator=(const HashtablezInfo&) = delete;
+
+ // Puts the object into a clean state, fills in the logically `const` members,
+ // blocking for any readers that are currently sampling the object.
+ void PrepareForSampling() EXCLUSIVE_LOCKS_REQUIRED(init_mu);
+
+ // These fields are mutated by the various Record* APIs and need to be
+ // thread-safe.
+ std::atomic<size_t> capacity;
+ std::atomic<size_t> size;
+ std::atomic<size_t> num_erases;
+ std::atomic<size_t> max_probe_length;
+ std::atomic<size_t> total_probe_length;
+ std::atomic<size_t> hashes_bitwise_or;
+ std::atomic<size_t> hashes_bitwise_and;
+
+ // `HashtablezSampler` maintains intrusive linked lists for all samples. See
+ // comments on `HashtablezSampler::all_` for details on these. `init_mu`
+ // guards the ability to restore the sample to a pristine state. This
+ // prevents races with sampling and resurrecting an object.
+ absl::Mutex init_mu;
+ HashtablezInfo* next;
+ HashtablezInfo* dead GUARDED_BY(init_mu);
+
+ // All of the fields below are set by `PrepareForSampling`, they must not be
+ // mutated in `Record*` functions. They are logically `const` in that sense.
+ // These are guarded by init_mu, but that is not externalized to clients, who
+ // can only read them during `HashtablezSampler::Iterate` which will hold the
+ // lock.
+ static constexpr int kMaxStackDepth = 64;
+ absl::Time create_time;
+ int32_t depth;
+ void* stack[kMaxStackDepth];
+};
+
+inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
+#if SWISSTABLE_HAVE_SSE2
+ total_probe_length /= 16;
+#else
+ total_probe_length /= 8;
+#endif
+ info->total_probe_length.store(total_probe_length, std::memory_order_relaxed);
+ info->num_erases.store(0, std::memory_order_relaxed);
+}
+
+inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
+ size_t capacity) {
+ info->size.store(size, std::memory_order_relaxed);
+ info->capacity.store(capacity, std::memory_order_relaxed);
+ if (size == 0) {
+ // This is a clear, reset the total/num_erases too.
+ RecordRehashSlow(info, 0);
+ }
+}
+
+void RecordInsertSlow(HashtablezInfo* info, size_t hash,
+ size_t distance_from_desired);
+
+inline void RecordEraseSlow(HashtablezInfo* info) {
+ info->size.fetch_sub(1, std::memory_order_relaxed);
+ info->num_erases.fetch_add(1, std::memory_order_relaxed);
+}
+
+HashtablezInfo* SampleSlow(int64_t* next_sample);
+void UnsampleSlow(HashtablezInfo* info);
+
+class HashtablezInfoHandle {
+ public:
+ explicit HashtablezInfoHandle() : info_(nullptr) {}
+ explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {}
+ ~HashtablezInfoHandle() {
+ if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
+ UnsampleSlow(info_);
+ }
+
+ HashtablezInfoHandle(const HashtablezInfoHandle&) = delete;
+ HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete;
+
+ HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept
+ : info_(absl::exchange(o.info_, nullptr)) {}
+ HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept {
+ if (ABSL_PREDICT_FALSE(info_ != nullptr)) {
+ UnsampleSlow(info_);
+ }
+ info_ = absl::exchange(o.info_, nullptr);
+ return *this;
+ }
+
+ inline void RecordStorageChanged(size_t size, size_t capacity) {
+ if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
+ RecordStorageChangedSlow(info_, size, capacity);
+ }
+
+ inline void RecordRehash(size_t total_probe_length) {
+ if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
+ RecordRehashSlow(info_, total_probe_length);
+ }
+
+ inline void RecordInsert(size_t hash, size_t distance_from_desired) {
+ if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
+ RecordInsertSlow(info_, hash, distance_from_desired);
+ }
+
+ inline void RecordErase() {
+ if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
+ RecordEraseSlow(info_);
+ }
+
+ friend inline void swap(HashtablezInfoHandle& lhs,
+ HashtablezInfoHandle& rhs) {
+ std::swap(lhs.info_, rhs.info_);
+ }
+
+ private:
+ friend class HashtablezInfoHandlePeer;
+ HashtablezInfo* info_;
+};
+
+#if ABSL_PER_THREAD_TLS == 1
+extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample;
+#endif // ABSL_PER_THREAD_TLS
+
+// Returns an RAII sampling handle that manages registration and unregistation
+// with the global sampler.
+inline HashtablezInfoHandle Sample() {
+#if ABSL_PER_THREAD_TLS == 0
+ static auto* mu = new absl::Mutex;
+ static int64_t global_next_sample = 0;
+ absl::MutexLock l(mu);
+#endif // !ABSL_HAVE_THREAD_LOCAL
+
+ if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
+ return HashtablezInfoHandle(nullptr);
+ }
+ return HashtablezInfoHandle(SampleSlow(&global_next_sample));
+}
+
+// Holds samples and their associated stack traces with a soft limit of
+// `SetHashtablezMaxSamples()`.
+//
+// Thread safe.
+class HashtablezSampler {
+ public:
+ // Returns a global Sampler.
+ static HashtablezSampler& Global();
+
+ HashtablezSampler();
+ ~HashtablezSampler();
+
+ // Registers for sampling. Returns an opaque registration info.
+ HashtablezInfo* Register();
+
+ // Unregisters the sample.
+ void Unregister(HashtablezInfo* sample);
+
+ // The dispose callback will be called on all samples the moment they are
+ // being unregistered. Only affects samples that are unregistered after the
+ // callback has been set.
+ // Returns the previous callback.
+ using DisposeCallback = void (*)(const HashtablezInfo&);
+ DisposeCallback SetDisposeCallback(DisposeCallback f);
+
+ // Iterates over all the registered `StackInfo`s. Returning the number of
+ // samples that have been dropped.
+ int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f);
+
+ private:
+ void PushNew(HashtablezInfo* sample);
+ void PushDead(HashtablezInfo* sample);
+ HashtablezInfo* PopDead();
+
+ std::atomic<size_t> dropped_samples_;
+ std::atomic<size_t> size_estimate_;
+
+ // Intrusive lock free linked lists for tracking samples.
+ //
+ // `all_` records all samples (they are never removed from this list) and is
+ // terminated with a `nullptr`.
+ //
+ // `graveyard_.dead` is a circular linked list. When it is empty,
+ // `graveyard_.dead == &graveyard`. The list is circular so that
+ // every item on it (even the last) has a non-null dead pointer. This allows
+ // `Iterate` to determine if a given sample is live or dead using only
+ // information on the sample itself.
+ //
+ // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead
+ // looks like this (G is the Graveyard):
+ //
+ // +---+ +---+ +---+ +---+ +---+
+ // all -->| A |--->| B |--->| C |--->| D |--->| E |
+ // | | | | | | | | | |
+ // +---+ | | +->| |-+ | | +->| |-+ | |
+ // | G | +---+ | +---+ | +---+ | +---+ | +---+
+ // | | | | | |
+ // | | --------+ +--------+ |
+ // +---+ |
+ // ^ |
+ // +--------------------------------------+
+ //
+ std::atomic<HashtablezInfo*> all_;
+ HashtablezInfo graveyard_;
+
+ std::atomic<DisposeCallback> dispose_;
+};
+
+// Enables or disables sampling for Swiss tables.
+void SetHashtablezEnabled(bool enabled);
+
+// Sets the rate at which Swiss tables will be sampled.
+void SetHashtablezSampleParameter(int32_t rate);
+
+// Sets a soft max for the number of samples that will be kept.
+void SetHashtablezMaxSamples(int32_t max);
+
+// Configuration override.
+// This allows process-wide sampling without depending on order of
+// initialization of static storage duration objects.
+// The definition of this constant is weak, which allows us to inject a
+// different value for it at link time.
+extern "C" const bool kAbslContainerInternalSampleEverything;
+
+} // namespace container_internal
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
diff --git a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
new file mode 100644
index 0000000..4ca6ffd
--- /dev/null
+++ b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
@@ -0,0 +1,27 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/internal/hashtablez_sampler.h"
+
+#include "absl/base/attributes.h"
+
+namespace absl {
+namespace container_internal {
+
+// See hashtablez_sampler.h for details.
+extern "C" ABSL_ATTRIBUTE_WEAK const bool
+ kAbslContainerInternalSampleEverything = false;
+
+} // namespace container_internal
+} // namespace absl
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc
new file mode 100644
index 0000000..d2435ed
--- /dev/null
+++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -0,0 +1,355 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/internal/hashtablez_sampler.h"
+
+#include <atomic>
+#include <limits>
+#include <random>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/container/internal/have_sse.h"
+#include "absl/synchronization/blocking_counter.h"
+#include "absl/synchronization/internal/thread_pool.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/synchronization/notification.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+
+#if SWISSTABLE_HAVE_SSE2
+constexpr int kProbeLength = 16;
+#else
+constexpr int kProbeLength = 8;
+#endif
+
+namespace absl {
+namespace container_internal {
+class HashtablezInfoHandlePeer {
+ public:
+ static bool IsSampled(const HashtablezInfoHandle& h) {
+ return h.info_ != nullptr;
+ }
+
+ static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; }
+};
+
+namespace {
+using ::absl::synchronization_internal::ThreadPool;
+using ::testing::IsEmpty;
+using ::testing::UnorderedElementsAre;
+
+std::vector<size_t> GetSizes(HashtablezSampler* s) {
+ std::vector<size_t> res;
+ s->Iterate([&](const HashtablezInfo& info) {
+ res.push_back(info.size.load(std::memory_order_acquire));
+ });
+ return res;
+}
+
+HashtablezInfo* Register(HashtablezSampler* s, size_t size) {
+ auto* info = s->Register();
+ assert(info != nullptr);
+ info->size.store(size);
+ return info;
+}
+
+TEST(HashtablezInfoTest, PrepareForSampling) {
+ absl::Time test_start = absl::Now();
+ HashtablezInfo info;
+ absl::MutexLock l(&info.init_mu);
+ info.PrepareForSampling();
+
+ EXPECT_EQ(info.capacity.load(), 0);
+ EXPECT_EQ(info.size.load(), 0);
+ EXPECT_EQ(info.num_erases.load(), 0);
+ EXPECT_EQ(info.max_probe_length.load(), 0);
+ 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_GE(info.create_time, test_start);
+
+ info.capacity.store(1, std::memory_order_relaxed);
+ info.size.store(1, std::memory_order_relaxed);
+ info.num_erases.store(1, std::memory_order_relaxed);
+ info.max_probe_length.store(1, std::memory_order_relaxed);
+ 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.create_time = test_start - absl::Hours(20);
+
+ info.PrepareForSampling();
+ EXPECT_EQ(info.capacity.load(), 0);
+ EXPECT_EQ(info.size.load(), 0);
+ EXPECT_EQ(info.num_erases.load(), 0);
+ EXPECT_EQ(info.max_probe_length.load(), 0);
+ 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_GE(info.create_time, test_start);
+}
+
+TEST(HashtablezInfoTest, RecordStorageChanged) {
+ HashtablezInfo info;
+ absl::MutexLock l(&info.init_mu);
+ info.PrepareForSampling();
+ RecordStorageChangedSlow(&info, 17, 47);
+ EXPECT_EQ(info.size.load(), 17);
+ EXPECT_EQ(info.capacity.load(), 47);
+ RecordStorageChangedSlow(&info, 20, 20);
+ EXPECT_EQ(info.size.load(), 20);
+ EXPECT_EQ(info.capacity.load(), 20);
+}
+
+TEST(HashtablezInfoTest, RecordInsert) {
+ HashtablezInfo info;
+ absl::MutexLock l(&info.init_mu);
+ info.PrepareForSampling();
+ EXPECT_EQ(info.max_probe_length.load(), 0);
+ RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
+ EXPECT_EQ(info.max_probe_length.load(), 6);
+ EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00);
+ EXPECT_EQ(info.hashes_bitwise_or.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);
+ 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);
+}
+
+TEST(HashtablezInfoTest, RecordErase) {
+ HashtablezInfo info;
+ absl::MutexLock l(&info.init_mu);
+ info.PrepareForSampling();
+ EXPECT_EQ(info.num_erases.load(), 0);
+ EXPECT_EQ(info.size.load(), 0);
+ RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
+ EXPECT_EQ(info.size.load(), 1);
+ RecordEraseSlow(&info);
+ EXPECT_EQ(info.size.load(), 0);
+ EXPECT_EQ(info.num_erases.load(), 1);
+}
+
+TEST(HashtablezInfoTest, RecordRehash) {
+ HashtablezInfo info;
+ absl::MutexLock l(&info.init_mu);
+ info.PrepareForSampling();
+ RecordInsertSlow(&info, 0x1, 0);
+ RecordInsertSlow(&info, 0x2, kProbeLength);
+ RecordInsertSlow(&info, 0x4, kProbeLength);
+ RecordInsertSlow(&info, 0x8, 2 * kProbeLength);
+ EXPECT_EQ(info.size.load(), 4);
+ EXPECT_EQ(info.total_probe_length.load(), 4);
+
+ RecordEraseSlow(&info);
+ RecordEraseSlow(&info);
+ EXPECT_EQ(info.size.load(), 2);
+ EXPECT_EQ(info.total_probe_length.load(), 4);
+ EXPECT_EQ(info.num_erases.load(), 2);
+
+ RecordRehashSlow(&info, 3 * kProbeLength);
+ EXPECT_EQ(info.size.load(), 2);
+ EXPECT_EQ(info.total_probe_length.load(), 3);
+ EXPECT_EQ(info.num_erases.load(), 0);
+}
+
+TEST(HashtablezSamplerTest, SmallSampleParameter) {
+ SetHashtablezEnabled(true);
+ SetHashtablezSampleParameter(100);
+
+ for (int i = 0; i < 1000; ++i) {
+ int64_t next_sample = 0;
+ HashtablezInfo* sample = SampleSlow(&next_sample);
+ EXPECT_GT(next_sample, 0);
+ EXPECT_NE(sample, nullptr);
+ UnsampleSlow(sample);
+ }
+}
+
+TEST(HashtablezSamplerTest, LargeSampleParameter) {
+ SetHashtablezEnabled(true);
+ SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
+
+ for (int i = 0; i < 1000; ++i) {
+ int64_t next_sample = 0;
+ HashtablezInfo* sample = SampleSlow(&next_sample);
+ EXPECT_GT(next_sample, 0);
+ EXPECT_NE(sample, nullptr);
+ UnsampleSlow(sample);
+ }
+}
+
+TEST(HashtablezSamplerTest, Sample) {
+ SetHashtablezEnabled(true);
+ SetHashtablezSampleParameter(100);
+ int64_t num_sampled = 0;
+ int64_t total = 0;
+ double sample_rate;
+ for (int i = 0; i < 1000000; ++i) {
+ HashtablezInfoHandle h = Sample();
+ ++total;
+ if (HashtablezInfoHandlePeer::IsSampled(h)) {
+ ++num_sampled;
+ }
+ sample_rate = static_cast<double>(num_sampled) / total;
+ if (0.005 < sample_rate && sample_rate < 0.015) break;
+ }
+ EXPECT_NEAR(sample_rate, 0.01, 0.005);
+}
+
+TEST(HashtablezSamplerTest, Handle) {
+ auto& sampler = HashtablezSampler::Global();
+ HashtablezInfoHandle h(sampler.Register());
+ auto* info = HashtablezInfoHandlePeer::GetInfo(&h);
+ info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed);
+
+ bool found = false;
+ sampler.Iterate([&](const HashtablezInfo& h) {
+ if (&h == info) {
+ EXPECT_EQ(h.hashes_bitwise_and.load(), 0x12345678);
+ found = true;
+ }
+ });
+ EXPECT_TRUE(found);
+
+ h = HashtablezInfoHandle();
+ found = false;
+ sampler.Iterate([&](const HashtablezInfo& h) {
+ if (&h == info) {
+ // this will only happen if some other thread has resurrected the info
+ // the old handle was using.
+ if (h.hashes_bitwise_and.load() == 0x12345678) {
+ found = true;
+ }
+ }
+ });
+ EXPECT_FALSE(found);
+}
+
+TEST(HashtablezSamplerTest, Registration) {
+ HashtablezSampler sampler;
+ auto* info1 = Register(&sampler, 1);
+ EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1));
+
+ auto* info2 = Register(&sampler, 2);
+ EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2));
+ info1->size.store(3);
+ EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2));
+
+ sampler.Unregister(info1);
+ sampler.Unregister(info2);
+}
+
+TEST(HashtablezSamplerTest, Unregistration) {
+ HashtablezSampler sampler;
+ std::vector<HashtablezInfo*> infos;
+ for (size_t i = 0; i < 3; ++i) {
+ infos.push_back(Register(&sampler, i));
+ }
+ EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2));
+
+ sampler.Unregister(infos[1]);
+ EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2));
+
+ infos.push_back(Register(&sampler, 3));
+ infos.push_back(Register(&sampler, 4));
+ EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4));
+ sampler.Unregister(infos[3]);
+ EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4));
+
+ sampler.Unregister(infos[0]);
+ sampler.Unregister(infos[2]);
+ sampler.Unregister(infos[4]);
+ EXPECT_THAT(GetSizes(&sampler), IsEmpty());
+}
+
+TEST(HashtablezSamplerTest, MultiThreaded) {
+ HashtablezSampler sampler;
+ Notification stop;
+ ThreadPool pool(10);
+
+ for (int i = 0; i < 10; ++i) {
+ pool.Schedule([&sampler, &stop]() {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+
+ std::vector<HashtablezInfo*> infoz;
+ while (!stop.HasBeenNotified()) {
+ if (infoz.empty()) {
+ infoz.push_back(sampler.Register());
+ }
+ switch (std::uniform_int_distribution<>(0, 2)(gen)) {
+ case 0: {
+ infoz.push_back(sampler.Register());
+ break;
+ }
+ case 1: {
+ size_t p =
+ std::uniform_int_distribution<>(0, infoz.size() - 1)(gen);
+ HashtablezInfo* info = infoz[p];
+ infoz[p] = infoz.back();
+ infoz.pop_back();
+ sampler.Unregister(info);
+ break;
+ }
+ case 2: {
+ absl::Duration oldest = absl::ZeroDuration();
+ sampler.Iterate([&](const HashtablezInfo& info) {
+ oldest = std::max(oldest, absl::Now() - info.create_time);
+ });
+ ASSERT_GE(oldest, absl::ZeroDuration());
+ break;
+ }
+ }
+ }
+ });
+ }
+ // The threads will hammer away. Give it a little bit of time for tsan to
+ // spot errors.
+ absl::SleepFor(absl::Seconds(3));
+ stop.Notify();
+}
+
+TEST(HashtablezSamplerTest, Callback) {
+ HashtablezSampler sampler;
+
+ auto* info1 = Register(&sampler, 1);
+ auto* info2 = Register(&sampler, 2);
+
+ static const HashtablezInfo* expected;
+
+ auto callback = [](const HashtablezInfo& info) {
+ // We can't use `info` outside of this callback because the object will be
+ // disposed as soon as we return from here.
+ EXPECT_EQ(&info, expected);
+ };
+
+ // Set the callback.
+ EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
+ expected = info1;
+ sampler.Unregister(info1);
+
+ // Unset the callback.
+ EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
+ expected = nullptr; // no more calls.
+ sampler.Unregister(info2);
+}
+
+} // namespace
+} // namespace container_internal
+} // namespace absl
diff --git a/absl/container/internal/have_sse.h b/absl/container/internal/have_sse.h
new file mode 100644
index 0000000..4341441
--- /dev/null
+++ b/absl/container/internal/have_sse.h
@@ -0,0 +1,49 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Shared config probing for SSE instructions used in Swiss tables.
+#ifndef ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_
+#define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_
+
+#ifndef SWISSTABLE_HAVE_SSE2
+#if defined(__SSE2__) || \
+ (defined(_MSC_VER) && \
+ (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2)))
+#define SWISSTABLE_HAVE_SSE2 1
+#else
+#define SWISSTABLE_HAVE_SSE2 0
+#endif
+#endif
+
+#ifndef SWISSTABLE_HAVE_SSSE3
+#ifdef __SSSE3__
+#define SWISSTABLE_HAVE_SSSE3 1
+#else
+#define SWISSTABLE_HAVE_SSSE3 0
+#endif
+#endif
+
+#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2
+#error "Bad configuration!"
+#endif
+
+#if SWISSTABLE_HAVE_SSE2
+#include <emmintrin.h>
+#endif
+
+#if SWISSTABLE_HAVE_SSSE3
+#include <tmmintrin.h>
+#endif
+
+#endif // ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
new file mode 100644
index 0000000..24059d9
--- /dev/null
+++ b/absl/container/internal/inlined_vector.h
@@ -0,0 +1,126 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_
+#define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+namespace inlined_vector_internal {
+
+template <typename InlinedVector>
+class Storage;
+
+template <template <typename, size_t, typename> class InlinedVector, typename T,
+ size_t N, typename A>
+class Storage<InlinedVector<T, N, A>> {
+ public:
+ using allocator_type = A;
+ using value_type = typename allocator_type::value_type;
+ using pointer = typename allocator_type::pointer;
+ using const_pointer = typename allocator_type::const_pointer;
+ using reference = typename allocator_type::reference;
+ using const_reference = typename allocator_type::const_reference;
+ using rvalue_reference = typename allocator_type::value_type&&;
+ using size_type = typename allocator_type::size_type;
+ using difference_type = typename allocator_type::difference_type;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ explicit Storage(const allocator_type& a) : allocator_and_tag_(a) {}
+
+ // TODO(johnsoncj): Make the below types and members private after migration
+
+ // Holds whether the vector is allocated or not in the lowest bit and the size
+ // in the high bits:
+ // `size_ = (size << 1) | is_allocated;`
+ class Tag {
+ size_type size_;
+
+ public:
+ Tag() : size_(0) {}
+ size_type size() const { return size_ / 2; }
+ void add_size(size_type n) { size_ += n * 2; }
+ void set_inline_size(size_type n) { size_ = n * 2; }
+ void set_allocated_size(size_type n) { size_ = (n * 2) + 1; }
+ bool allocated() const { return size_ % 2; }
+ };
+
+ // Derives from `allocator_type` to use the empty base class optimization.
+ // If the `allocator_type` is stateless, we can store our instance for free.
+ class AllocatorAndTag : private allocator_type {
+ Tag tag_;
+
+ public:
+ explicit AllocatorAndTag(const allocator_type& a) : allocator_type(a) {}
+ Tag& tag() { return tag_; }
+ const Tag& tag() const { return tag_; }
+ allocator_type& allocator() { return *this; }
+ const allocator_type& allocator() const { return *this; }
+ };
+
+ class Allocation {
+ size_type capacity_;
+ pointer buffer_;
+
+ public:
+ Allocation(allocator_type& a, size_type capacity)
+ : capacity_(capacity), buffer_(Create(a, capacity)) {}
+ void Dealloc(allocator_type& a) {
+ std::allocator_traits<allocator_type>::deallocate(a, buffer_, capacity_);
+ }
+ size_type capacity() const { return capacity_; }
+ const_pointer buffer() const { return buffer_; }
+ pointer buffer() { return buffer_; }
+ static pointer Create(allocator_type& a, size_type n) {
+ return std::allocator_traits<allocator_type>::allocate(a, n);
+ }
+ };
+
+ // Stores either the inlined or allocated representation
+ union Rep {
+ using ValueTypeBuffer =
+ absl::aligned_storage_t<sizeof(value_type), alignof(value_type)>;
+ using AllocationBuffer =
+ absl::aligned_storage_t<sizeof(Allocation), alignof(Allocation)>;
+
+ // Structs wrap the buffers to perform indirection that solves a bizarre
+ // compilation error on Visual Studio (all known versions).
+ struct InlinedRep {
+ ValueTypeBuffer inlined[N];
+ };
+
+ struct AllocatedRep {
+ AllocationBuffer allocation;
+ };
+
+ InlinedRep inlined_storage;
+ AllocatedRep allocation_storage;
+ };
+
+ AllocatorAndTag allocator_and_tag_;
+ Rep rep_;
+};
+
+} // namespace inlined_vector_internal
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_
diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h
index 676c7d6..bbdde50 100644
--- a/absl/container/internal/layout.h
+++ b/absl/container/internal/layout.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -232,13 +232,17 @@
template <class T, size_t N>
struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {};
+// Note: workaround for https://gcc.gnu.org/PR88115
template <class T>
-struct AlignOf : NotAligned<T>, std::integral_constant<size_t, alignof(T)> {};
+struct AlignOf : NotAligned<T> {
+ static constexpr size_t value = alignof(T);
+};
template <class T, size_t N>
-struct AlignOf<Aligned<T, N>> : std::integral_constant<size_t, N> {
+struct AlignOf<Aligned<T, N>> {
static_assert(N % alignof(T) == 0,
"Custom alignment can't be lower than the type's alignment");
+ static constexpr size_t value = N;
};
// Does `Ts...` contain `T`?
@@ -249,8 +253,10 @@
using CopyConst =
typename std::conditional<std::is_const<From>::value, const To, To>::type;
+// Note: We're not qualifying this with absl:: because it doesn't compile under
+// MSVC.
template <class T>
-using SliceType = absl::Span<T>;
+using SliceType = Span<T>;
// This namespace contains no types. It prevents functions defined in it from
// being found by ADL.
@@ -290,7 +296,7 @@
#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
#endif
- if (status == 0 && demangled != nullptr) { // Demangling succeeeded.
+ if (status == 0 && demangled != nullptr) { // Demangling succeeded.
absl::StrAppend(&out, "<", demangled, ">");
free(demangled);
} else {
@@ -396,7 +402,7 @@
static_assert(N < NumOffsets, "Index out of bounds");
return adl_barrier::Align(
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1],
- ElementAlignment<N>());
+ ElementAlignment<N>::value);
}
// Offset in bytes of the array with the specified element type. There must
@@ -445,7 +451,7 @@
return Size<ElementIndex<T>()>();
}
- // The number of elements of all arrays for which they are known.
+ // The number of elements of all arrays for which they are known.
constexpr std::array<size_t, NumSizes> Sizes() const {
return {{Size<SizeSeq>()...}};
}
@@ -610,7 +616,7 @@
#ifdef ADDRESS_SANITIZER
PoisonPadding<Char, N - 1>(p);
// The `if` is an optimization. It doesn't affect the observable behaviour.
- if (ElementAlignment<N - 1>() % ElementAlignment<N>()) {
+ if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) {
size_t start =
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1];
ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start);
@@ -637,7 +643,8 @@
std::string DebugString() const {
const auto offsets = Offsets();
const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>()...};
- const std::string types[] = {adl_barrier::TypeName<ElementType<OffsetSeq>>()...};
+ const std::string types[] = {
+ adl_barrier::TypeName<ElementType<OffsetSeq>>()...};
std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")");
for (size_t i = 0; i != NumOffsets - 1; ++i) {
absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1],
@@ -690,7 +697,7 @@
//
// It's allowed to pass fewer array sizes than the number of arrays. E.g.,
// if all you need is to the offset of the second array, you only need to
- // pass one argument -- the number of elements in the first arrays.
+ // pass one argument -- the number of elements in the first array.
//
// // int[3] followed by 4 bytes of padding and an unknown number of
// // doubles.
diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc
index f35157a..33b72bd 100644
--- a/absl/container/internal/layout_test.cc
+++ b/absl/container/internal/layout_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -45,7 +45,25 @@
return val;
}
-using Int128 = int64_t[2];
+// Helper classes to test different size and alignments.
+struct alignas(8) Int128 {
+ uint64_t a, b;
+ friend bool operator==(Int128 lhs, Int128 rhs) {
+ return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b);
+ }
+
+ static std::string Name() {
+ return internal_layout::adl_barrier::TypeName<Int128>();
+ }
+};
+
+// int64_t is *not* 8-byte aligned on all platforms!
+struct alignas(8) Int64 {
+ int64_t a;
+ friend bool operator==(Int64 lhs, Int64 rhs) {
+ return lhs.a == rhs.a;
+ }
+};
// Properties of types that this test relies on.
static_assert(sizeof(int8_t) == 1, "");
@@ -54,6 +72,8 @@
static_assert(alignof(int16_t) == 2, "");
static_assert(sizeof(int32_t) == 4, "");
static_assert(alignof(int32_t) == 4, "");
+static_assert(sizeof(Int64) == 8, "");
+static_assert(alignof(Int64) == 8, "");
static_assert(sizeof(Int128) == 16, "");
static_assert(alignof(Int128) == 8, "");
@@ -1271,14 +1291,14 @@
TEST(Layout, Alignment) {
static_assert(Layout<int8_t>::Alignment() == 1, "");
static_assert(Layout<int32_t>::Alignment() == 4, "");
- static_assert(Layout<int64_t>::Alignment() == 8, "");
+ static_assert(Layout<Int64>::Alignment() == 8, "");
static_assert(Layout<Aligned<int8_t, 64>>::Alignment() == 64, "");
- static_assert(Layout<int8_t, int32_t, int64_t>::Alignment() == 8, "");
- static_assert(Layout<int8_t, int64_t, int32_t>::Alignment() == 8, "");
- static_assert(Layout<int32_t, int8_t, int64_t>::Alignment() == 8, "");
- static_assert(Layout<int32_t, int64_t, int8_t>::Alignment() == 8, "");
- static_assert(Layout<int64_t, int8_t, int32_t>::Alignment() == 8, "");
- static_assert(Layout<int64_t, int32_t, int8_t>::Alignment() == 8, "");
+ static_assert(Layout<int8_t, int32_t, Int64>::Alignment() == 8, "");
+ static_assert(Layout<int8_t, Int64, int32_t>::Alignment() == 8, "");
+ static_assert(Layout<int32_t, int8_t, Int64>::Alignment() == 8, "");
+ static_assert(Layout<int32_t, Int64, int8_t>::Alignment() == 8, "");
+ static_assert(Layout<Int64, int8_t, int32_t>::Alignment() == 8, "");
+ static_assert(Layout<Int64, int32_t, int8_t>::Alignment() == 8, "");
}
TEST(Layout, ConstexprPartial) {
@@ -1313,7 +1333,7 @@
}
TEST(Layout, PoisonPadding) {
- using L = Layout<int8_t, int64_t, int32_t, Int128>;
+ using L = Layout<int8_t, Int64, int32_t, Int128>;
constexpr size_t n = L::Partial(1, 2, 3, 4).AllocSize();
{
@@ -1361,12 +1381,6 @@
}
TEST(Layout, DebugString) {
- const std::string int64_type =
-#ifdef _MSC_VER
- "__int64";
-#else // _MSC_VER
- std::is_same<int64_t, long long>::value ? "long long" : "long"; // NOLINT
-#endif // _MSC_VER
{
constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial();
EXPECT_EQ("@0<signed char>(1)", x.DebugString());
@@ -1384,24 +1398,24 @@
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<" +
- int64_type + " [2]>(16)",
+ "@16" +
+ Int128::Name() + "(16)",
x.DebugString());
}
{
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<" +
- int64_type + " [2]>(16)[4]",
+ "@16" +
+ Int128::Name() + "(16)[4]",
x.DebugString());
}
{
constexpr Layout<int8_t, int32_t, int8_t, Int128> x(1, 2, 3, 4);
EXPECT_EQ(
"@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
- "@16<" +
- int64_type + " [2]>(16)[4]",
+ "@16" +
+ Int128::Name() + "(16)[4]",
x.DebugString());
}
}
@@ -1528,8 +1542,7 @@
const char* c_str() const {
// Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)).
// The argument in Partial(1) specifies that we have size_t[1] in front of
- // the
- // characters.
+ // the characters.
return L::Partial(1).Pointer<char>(p_.get());
}
diff --git a/absl/container/internal/node_hash_policy.h b/absl/container/internal/node_hash_policy.h
index 065e700..19b4fc0 100644
--- a/absl/container/internal/node_hash_policy.h
+++ b/absl/container/internal/node_hash_policy.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/node_hash_policy_test.cc b/absl/container/internal/node_hash_policy_test.cc
index 43d287e..f1d3ec3 100644
--- a/absl/container/internal/node_hash_policy_test.cc
+++ b/absl/container/internal/node_hash_policy_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
index 1edc007..0014cf8 100644
--- a/absl/container/internal/raw_hash_map.h
+++ b/absl/container/internal/raw_hash_map.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -39,11 +39,14 @@
using MappedConstReference = decltype(P::value(
std::addressof(std::declval<typename raw_hash_map::const_reference>())));
+ using KeyArgImpl =
+ KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
+
public:
using key_type = typename Policy::key_type;
using mapped_type = typename Policy::mapped_type;
- template <typename K>
- using key_arg = typename raw_hash_map::raw_hash_set::template key_arg<K>;
+ template <class K>
+ using key_arg = typename KeyArgImpl::template type<K, key_type>;
static_assert(!std::is_reference<key_type>::value, "");
// TODO(alkis): remove this assertion and verify that reference mapped_type is
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index 1015312..ac2d10a 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,7 @@
#include "absl/container/internal/raw_hash_set.h"
+#include <atomic>
#include <cstddef>
#include "absl/base/config.h"
@@ -29,7 +30,7 @@
static thread_local size_t counter = 0;
size_t value = ++counter;
#else // ABSL_HAVE_THREAD_LOCAL
- static std::atomic<size_t> counter;
+ static std::atomic<size_t> counter(0);
size_t value = counter.fetch_add(1, std::memory_order_relaxed);
#endif // ABSL_HAVE_THREAD_LOCAL
return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 70da90f..85e3334 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -91,30 +91,6 @@
#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
-#ifndef SWISSTABLE_HAVE_SSE2
-#ifdef __SSE2__
-#define SWISSTABLE_HAVE_SSE2 1
-#else
-#define SWISSTABLE_HAVE_SSE2 0
-#endif
-#endif
-
-#ifndef SWISSTABLE_HAVE_SSSE3
-#ifdef __SSSE3__
-#define SWISSTABLE_HAVE_SSSE3 1
-#else
-#define SWISSTABLE_HAVE_SSSE3 0
-#endif
-#endif
-
-#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2
-#error "Bad configuration!"
-#endif
-
-#if SWISSTABLE_HAVE_SSE2
-#include <x86intrin.h>
-#endif
-
#include <algorithm>
#include <cmath>
#include <cstdint>
@@ -129,14 +105,16 @@
#include "absl/base/internal/bits.h"
#include "absl/base/internal/endian.h"
#include "absl/base/port.h"
+#include "absl/container/internal/common.h"
#include "absl/container/internal/compressed_tuple.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_policy_traits.h"
#include "absl/container/internal/hashtable_debug_hooks.h"
+#include "absl/container/internal/hashtablez_sampler.h"
+#include "absl/container/internal/have_sse.h"
#include "absl/container/internal/layout.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
-#include "absl/types/optional.h"
#include "absl/utility/utility.h"
namespace absl {
@@ -187,12 +165,6 @@
std::declval<Ts>()...))>,
Policy, Hash, Eq, Ts...> : std::true_type {};
-template <class, class = void>
-struct IsTransparent : std::false_type {};
-template <class T>
-struct IsTransparent<T, absl::void_t<typename T::is_transparent>>
- : std::true_type {};
-
// TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it.
template <class T>
constexpr bool IsNoThrowSwappable() {
@@ -202,14 +174,17 @@
template <typename T>
int TrailingZeros(T x) {
- return sizeof(T) == 8 ? base_internal::CountTrailingZerosNonZero64(x)
- : base_internal::CountTrailingZerosNonZero32(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(x)
- : base_internal::CountLeadingZeros32(x);
+ return sizeof(T) == 8
+ ? base_internal::CountLeadingZeros64(static_cast<uint64_t>(x))
+ : base_internal::CountLeadingZeros32(static_cast<uint32_t>(x));
}
// An abstraction over a bitmask. It provides an easy way to iterate through the
@@ -337,10 +312,27 @@
inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; }
#if SWISSTABLE_HAVE_SSE2
-struct Group {
+
+// https://github.com/abseil/abseil-cpp/issues/209
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853
+// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char
+// Work around this by using the portable implementation of Group
+// when using -funsigned-char under GCC.
+inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) {
+#if defined(__GNUC__) && !defined(__clang__)
+ if (std::is_unsigned<char>::value) {
+ const __m128i mask = _mm_set1_epi8(0x80);
+ const __m128i diff = _mm_subs_epi8(b, a);
+ return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask);
+ }
+#endif
+ return _mm_cmpgt_epi8(a, b);
+}
+
+struct GroupSse2Impl {
static constexpr size_t kWidth = 16; // the number of slots per group
- explicit Group(const ctrl_t* pos) {
+ explicit GroupSse2Impl(const ctrl_t* pos) {
ctrl = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pos));
}
@@ -366,23 +358,24 @@
BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const {
auto special = _mm_set1_epi8(kSentinel);
return BitMask<uint32_t, kWidth>(
- _mm_movemask_epi8(_mm_cmpgt_epi8(special, ctrl)));
+ _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)));
}
// Returns the number of trailing empty or deleted elements in the group.
uint32_t CountLeadingEmptyOrDeleted() const {
auto special = _mm_set1_epi8(kSentinel);
- return TrailingZeros(_mm_movemask_epi8(_mm_cmpgt_epi8(special, ctrl)) + 1);
+ return TrailingZeros(
+ _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1);
}
void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
- auto msbs = _mm_set1_epi8(0x80);
+ auto msbs = _mm_set1_epi8(static_cast<char>(-128));
auto x126 = _mm_set1_epi8(126);
#if SWISSTABLE_HAVE_SSSE3
auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs);
#else
auto zero = _mm_setzero_si128();
- auto special_mask = _mm_cmpgt_epi8(zero, ctrl);
+ auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl);
auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126));
#endif
_mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res);
@@ -390,11 +383,13 @@
__m128i ctrl;
};
-#else
-struct Group {
+#endif // SWISSTABLE_HAVE_SSE2
+
+struct GroupPortableImpl {
static constexpr size_t kWidth = 8;
- explicit Group(const ctrl_t* pos) : ctrl(little_endian::Load64(pos)) {}
+ explicit GroupPortableImpl(const ctrl_t* pos)
+ : ctrl(little_endian::Load64(pos)) {}
BitMask<uint64_t, kWidth, 3> Match(h2_t hash) const {
// For the technique, see:
@@ -441,15 +436,17 @@
uint64_t ctrl;
};
-#endif // SWISSTABLE_HAVE_SSE2
+
+#if SWISSTABLE_HAVE_SSE2
+using Group = GroupSse2Impl;
+#else
+using Group = GroupPortableImpl;
+#endif
template <class Policy, class Hash, class Eq, class Alloc>
class raw_hash_set;
-
-inline bool IsValidCapacity(size_t n) {
- return ((n + 1) & n) == 0 && n >= Group::kWidth - 1;
-}
+inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; }
// PRECONDITION:
// IsValidCapacity(capacity)
@@ -471,152 +468,32 @@
ctrl[capacity] = kSentinel;
}
-// Rounds up the capacity to the next power of 2 minus 1 and ensures it is
-// greater or equal to Group::kWidth - 1.
+// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1.
inline size_t NormalizeCapacity(size_t n) {
- constexpr size_t kMinCapacity = Group::kWidth - 1;
- return n <= kMinCapacity
- ? kMinCapacity
- : std::numeric_limits<size_t>::max() >> LeadingZeros(n);
+ return n ? ~size_t{} >> LeadingZeros(n) : 1;
}
-// The node_handle concept from C++17.
-// We specialize node_handle for sets and maps. node_handle_base holds the
-// common API of both.
-template <typename Policy, typename Alloc>
-class node_handle_base {
- protected:
- using PolicyTraits = hash_policy_traits<Policy>;
- using slot_type = typename PolicyTraits::slot_type;
-
- public:
- using allocator_type = Alloc;
-
- constexpr node_handle_base() {}
- node_handle_base(node_handle_base&& other) noexcept {
- *this = std::move(other);
+// We use 7/8th as maximum load factor.
+// For 16-wide groups, that gives an average of two empty slots per group.
+inline size_t CapacityToGrowth(size_t capacity) {
+ assert(IsValidCapacity(capacity));
+ // `capacity*7/8`
+ if (Group::kWidth == 8 && capacity == 7) {
+ // x-x/8 does not work when x==7.
+ return 6;
}
- ~node_handle_base() { destroy(); }
- node_handle_base& operator=(node_handle_base&& other) {
- destroy();
- if (!other.empty()) {
- alloc_ = other.alloc_;
- PolicyTraits::transfer(alloc(), slot(), other.slot());
- other.reset();
- }
- return *this;
+ return capacity - capacity / 8;
+}
+// From desired "growth" to a lowerbound of the necessary capacity.
+// Might not be a valid one and required NormalizeCapacity().
+inline size_t GrowthToLowerboundCapacity(size_t growth) {
+ // `growth*8/7`
+ if (Group::kWidth == 8 && growth == 7) {
+ // x+(x-1)/7 does not work when x==7.
+ return 8;
}
-
- bool empty() const noexcept { return !alloc_; }
- explicit operator bool() const noexcept { return !empty(); }
- allocator_type get_allocator() const { return *alloc_; }
-
- protected:
- template <typename, typename, typename, typename>
- friend class raw_hash_set;
-
- node_handle_base(const allocator_type& a, slot_type* s) : alloc_(a) {
- PolicyTraits::transfer(alloc(), slot(), s);
- }
-
- void destroy() {
- if (!empty()) {
- PolicyTraits::destroy(alloc(), slot());
- reset();
- }
- }
-
- void reset() {
- assert(alloc_.has_value());
- alloc_ = absl::nullopt;
- }
-
- slot_type* slot() const {
- assert(!empty());
- return reinterpret_cast<slot_type*>(std::addressof(slot_space_));
- }
- allocator_type* alloc() { return std::addressof(*alloc_); }
-
- private:
- absl::optional<allocator_type> alloc_;
- mutable absl::aligned_storage_t<sizeof(slot_type), alignof(slot_type)>
- slot_space_;
-};
-
-// For sets.
-template <typename Policy, typename Alloc, typename = void>
-class node_handle : public node_handle_base<Policy, Alloc> {
- using Base = typename node_handle::node_handle_base;
-
- public:
- using value_type = typename Base::PolicyTraits::value_type;
-
- constexpr node_handle() {}
-
- value_type& value() const {
- return Base::PolicyTraits::element(this->slot());
- }
-
- private:
- template <typename, typename, typename, typename>
- friend class raw_hash_set;
-
- node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
-};
-
-// For maps.
-template <typename Policy, typename Alloc>
-class node_handle<Policy, Alloc, absl::void_t<typename Policy::mapped_type>>
- : public node_handle_base<Policy, Alloc> {
- using Base = typename node_handle::node_handle_base;
-
- public:
- using key_type = typename Policy::key_type;
- using mapped_type = typename Policy::mapped_type;
-
- constexpr node_handle() {}
-
- auto key() const -> decltype(Base::PolicyTraits::key(this->slot())) {
- return Base::PolicyTraits::key(this->slot());
- }
-
- mapped_type& mapped() const {
- return Base::PolicyTraits::value(
- &Base::PolicyTraits::element(this->slot()));
- }
-
- private:
- template <typename, typename, typename, typename>
- friend class raw_hash_set;
-
- node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
-};
-
-// Implement the insert_return_type<> concept of C++17.
-template <class Iterator, class NodeType>
-struct insert_return_type {
- Iterator position;
- bool inserted;
- NodeType node;
-};
-
-// Helper trait to allow or disallow arbitrary keys when the hash and
-// eq functions are transparent.
-// It is very important that the inner template is an alias and that the type it
-// produces is not a dependent type. Otherwise, type deduction would fail.
-template <bool is_transparent>
-struct KeyArg {
- // Transparent. Forward `K`.
- template <typename K, typename key_type>
- using type = K;
-};
-
-template <>
-struct KeyArg<false> {
- // Not transparent. Always use `key_type`.
- template <typename K, typename key_type>
- using type = key_type;
-};
+ return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7);
+}
// Policy: a policy defines how to perform different operations on
// the slots of the hashtable (see hash_policy_traits.h for the full interface
@@ -632,14 +509,14 @@
// if they are equal, false if they are not. If two keys compare equal, then
// their hash values as defined by Hash MUST be equal.
//
-// Allocator: an Allocator [http://devdocs.io/cpp/concept/allocator] with which
+// Allocator: an Allocator [https://devdocs.io/cpp/concept/allocator] with which
// the storage of the hashtable will be allocated and the elements will be
// constructed and destroyed.
template <class Policy, class Hash, class Eq, class Alloc>
class raw_hash_set {
using PolicyTraits = hash_policy_traits<Policy>;
- using KeyArgImpl = container_internal::KeyArg<IsTransparent<Eq>::value &&
- IsTransparent<Hash>::value>;
+ using KeyArgImpl =
+ KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
public:
using init_type = typename PolicyTraits::init_type;
@@ -662,7 +539,7 @@
allocator_type>::template rebind_traits<value_type>::const_pointer;
// Alias used for heterogeneous lookup functions.
- // `key_arg<K>` evaluates to `K` when the functors are tranparent and to
+ // `key_arg<K>` evaluates to `K` when the functors are transparent and to
// `key_type` otherwise. It permits template argument deduction on `K` for the
// transparent case.
template <class K>
@@ -780,7 +657,11 @@
}
ctrl_t* ctrl_ = nullptr;
- slot_type* slot_;
+ // To avoid uninitialized member warnigs, put slot_ in an anonymous union.
+ // The member is not initialized on singleton and end iterators.
+ union {
+ slot_type* slot_;
+ };
};
class const_iterator {
@@ -820,7 +701,8 @@
iterator inner_;
};
- using node_type = container_internal::node_handle<Policy, Alloc>;
+ using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
+ using insert_return_type = InsertReturnType<iterator, node_type>;
raw_hash_set() noexcept(
std::is_nothrow_default_constructible<hasher>::value&&
@@ -833,7 +715,7 @@
: ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) {
if (bucket_count) {
capacity_ = NormalizeCapacity(bucket_count);
- growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor);
+ reset_growth_left();
initialize_slots();
}
}
@@ -875,8 +757,8 @@
// that accept std::initializer_list<T> and std::initializer_list<init_type>.
// This is advantageous for performance.
//
- // // Turns {"abc", "def"} into std::initializer_list<std::string>, then copies
- // // the strings into the set.
+ // // Turns {"abc", "def"} into std::initializer_list<std::string>, then
+ // // copies the strings into the set.
// std::unordered_set<std::string> s = {"abc", "def"};
//
// // Turns {"abc", "def"} into std::initializer_list<const char*>, then
@@ -939,9 +821,10 @@
// than a full `insert`.
for (const auto& v : that) {
const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v);
- const size_t i = find_first_non_full(hash);
- set_ctrl(i, H2(hash));
- emplace_at(i, v);
+ auto target = find_first_non_full(hash);
+ set_ctrl(target.offset, H2(hash));
+ emplace_at(target.offset, v);
+ infoz_.RecordInsert(hash, target.probe_length);
}
size_ = that.size();
growth_left() -= that.size();
@@ -955,6 +838,7 @@
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.
@@ -975,6 +859,7 @@
std::swap(size_, that.size_);
std::swap(capacity_, that.capacity_);
std::swap(growth_left(), that.growth_left());
+ std::swap(infoz_, that.infoz_);
} else {
reserve(that.size());
// Note: this will copy elements of dense_set and unordered_set instead of
@@ -1022,9 +907,9 @@
bool empty() const { return !size(); }
size_t size() const { return size_; }
size_t capacity() const { return capacity_; }
- size_t max_size() const { return std::numeric_limits<size_t>::max(); }
+ size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
- void clear() {
+ ABSL_ATTRIBUTE_REINITIALIZES void clear() {
// Iterating over this container is O(bucket_count()). When bucket_count()
// is much greater than size(), iteration becomes prohibitively expensive.
// For clear() it is more important to reuse the allocated array when the
@@ -1042,9 +927,10 @@
}
size_ = 0;
reset_ctrl();
- growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor);
+ reset_growth_left();
}
assert(empty());
+ infoz_.RecordStorageChanged(0, capacity_);
}
// This overload kicks in when the argument is an rvalue of insertable and
@@ -1124,13 +1010,14 @@
insert(ilist.begin(), ilist.end());
}
- insert_return_type<iterator, node_type> insert(node_type&& node) {
+ insert_return_type insert(node_type&& node) {
if (!node) return {end(), false, node_type()};
- const auto& elem = PolicyTraits::element(node.slot());
+ const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node));
auto res = PolicyTraits::apply(
- InsertSlot<false>{*this, std::move(*node.slot())}, elem);
+ InsertSlot<false>{*this, std::move(*CommonAccess::GetSlot(node))},
+ elem);
if (res.second) {
- node.reset();
+ CommonAccess::Reset(&node);
return {res.first, true, node_type()};
} else {
return {res.first, false, std::move(node)};
@@ -1294,7 +1181,8 @@
}
node_type extract(const_iterator position) {
- node_type node(alloc_ref(), position.inner_.slot_);
+ auto node =
+ CommonAccess::Make<node_type>(alloc_ref(), position.inner_.slot_);
erase_meta_only(position);
return node;
}
@@ -1319,6 +1207,7 @@
swap(growth_left(), that.growth_left());
swap(hash_ref(), that.hash_ref());
swap(eq_ref(), that.eq_ref());
+ swap(infoz_, that.infoz_);
if (AllocTraits::propagate_on_container_swap::value) {
swap(alloc_ref(), that.alloc_ref());
} else {
@@ -1329,17 +1218,21 @@
void rehash(size_t n) {
if (n == 0 && capacity_ == 0) return;
- if (n == 0 && size_ == 0) return destroy_slots();
- auto m = NormalizeCapacity(std::max(n, NumSlotsFast(size())));
+ if (n == 0 && size_ == 0) {
+ destroy_slots();
+ infoz_.RecordStorageChanged(0, 0);
+ return;
+ }
+ // bitor is a faster way of doing `max` here. We will round up to the next
+ // power-of-2-minus-1, so bitor is good enough.
+ auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size()));
// n == 0 unconditionally rehashes as per the standard.
if (n == 0 || m > capacity_) {
resize(m);
}
}
- void reserve(size_t n) {
- rehash(NumSlotsFast(n));
- }
+ void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); }
// Extension API: support for heterogeneous keys.
//
@@ -1517,13 +1410,6 @@
slot_type&& slot;
};
- // Computes std::ceil(n / kMaxLoadFactor). Faster than calling std::ceil.
- static inline size_t NumSlotsFast(size_t n) {
- return static_cast<size_t>(
- (n * kMaxLoadFactorDenominator + (kMaxLoadFactorNumerator - 1)) /
- kMaxLoadFactorNumerator);
- }
-
// "erases" the object from the container, except that it doesn't actually
// destroy the object. It only updates all the metadata of the class.
// This can be used in conjunction with Policy::transfer to move the object to
@@ -1546,17 +1432,23 @@
set_ctrl(index, was_never_full ? kEmpty : kDeleted);
growth_left() += was_never_full;
+ infoz_.RecordErase();
}
void initialize_slots() {
assert(capacity_);
+ if (slots_ == nullptr) {
+ infoz_ = Sample();
+ }
+
auto layout = MakeLayout(capacity_);
char* mem = static_cast<char*>(
Allocate<Layout::Alignment()>(&alloc_ref(), layout.AllocSize()));
ctrl_ = reinterpret_cast<ctrl_t*>(layout.template Pointer<0>(mem));
slots_ = layout.template Pointer<1>(mem);
reset_ctrl();
- growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor) - size_;
+ reset_growth_left();
+ infoz_.RecordStorageChanged(size_, capacity_);
}
void destroy_slots() {
@@ -1585,11 +1477,14 @@
capacity_ = new_capacity;
initialize_slots();
+ size_t total_probe_length = 0;
for (size_t i = 0; i != old_capacity; ++i) {
if (IsFull(old_ctrl[i])) {
size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
PolicyTraits::element(old_slots + i));
- size_t new_i = find_first_non_full(hash);
+ auto target = find_first_non_full(hash);
+ size_t new_i = target.offset;
+ total_probe_length += target.probe_length;
set_ctrl(new_i, H2(hash));
PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i);
}
@@ -1601,10 +1496,12 @@
Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl,
layout.AllocSize());
}
+ infoz_.RecordRehash(total_probe_length);
}
void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE {
assert(IsValidCapacity(capacity_));
+ assert(!is_small());
// Algorithm:
// - mark all DELETED slots as EMPTY
// - mark all FULL slots as DELETED
@@ -1624,12 +1521,15 @@
ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_);
typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type
raw;
+ size_t total_probe_length = 0;
slot_type* slot = reinterpret_cast<slot_type*>(&raw);
for (size_t i = 0; i != capacity_; ++i) {
if (!IsDeleted(ctrl_[i])) continue;
size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
PolicyTraits::element(slots_ + i));
- size_t new_i = find_first_non_full(hash);
+ auto target = find_first_non_full(hash);
+ size_t new_i = target.offset;
+ total_probe_length += target.probe_length;
// Verify if the old and new i fall within the same group wrt the hash.
// If they do, we don't need to move the object as it falls already in the
@@ -1661,13 +1561,14 @@
--i; // repeat
}
}
- growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor) - size_;
+ reset_growth_left();
+ infoz_.RecordRehash(total_probe_length);
}
void rehash_and_grow_if_necessary() {
if (capacity_ == 0) {
- resize(Group::kWidth - 1);
- } else if (size() <= kMaxLoadFactor / 2 * capacity_) {
+ resize(1);
+ } else if (size() <= CapacityToGrowth(capacity()) / 2) {
// Squash DELETED without growing if there is enough capacity.
drop_deletes_without_resize();
} else {
@@ -1702,24 +1603,26 @@
// - the input is already a set
// - there are enough slots
// - the element with the hash is not in the table
- size_t find_first_non_full(size_t hash) {
+ 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 force small tables to have random entries too, so
- // in debug build we will randomly insert in either the front or back of
+ // 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 (ShouldInsertBackwards(hash, ctrl_))
- return seq.offset(mask.HighestBitSet());
- else
- return seq.offset(mask.LowestBitSet());
-#else
- return seq.offset(mask.LowestBitSet());
+ if (!is_small() && ShouldInsertBackwards(hash, ctrl_)) {
+ return {seq.offset(mask.HighestBitSet()), seq.index()};
+ }
#endif
+ return {seq.offset(mask.LowestBitSet()), seq.index()};
}
assert(seq.index() < capacity_ && "full table!");
seq.next();
@@ -1758,15 +1661,17 @@
}
size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE {
- size_t target = find_first_non_full(hash);
- if (ABSL_PREDICT_FALSE(growth_left() == 0 && !IsDeleted(ctrl_[target]))) {
+ auto target = find_first_non_full(hash);
+ if (ABSL_PREDICT_FALSE(growth_left() == 0 &&
+ !IsDeleted(ctrl_[target.offset]))) {
rehash_and_grow_if_necessary();
target = find_first_non_full(hash);
}
++size_;
- growth_left() -= IsEmpty(ctrl_[target]);
- set_ctrl(target, H2(hash));
- return target;
+ growth_left() -= IsEmpty(ctrl_[target.offset]);
+ set_ctrl(target.offset, H2(hash));
+ infoz_.RecordInsert(hash, target.probe_length);
+ return target.offset;
}
// Constructs the value in the space pointed by the iterator. This only works
@@ -1804,6 +1709,10 @@
SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
}
+ void reset_growth_left() {
+ growth_left() = CapacityToGrowth(capacity()) - size_;
+ }
+
// Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at
// the end too.
void set_ctrl(size_t i, ctrl_t h) {
@@ -1816,11 +1725,28 @@
}
ctrl_[i] = h;
- ctrl_[((i - Group::kWidth) & capacity_) + Group::kWidth] = h;
+ ctrl_[((i - Group::kWidth) & capacity_) + 1 +
+ ((Group::kWidth - 1) & capacity_)] = h;
}
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>(); }
@@ -1830,12 +1756,6 @@
return settings_.template get<3>();
}
- // On average each group has 2 empty slot (for the vectorized case).
- static constexpr int64_t kMaxLoadFactorNumerator = 14;
- static constexpr int64_t kMaxLoadFactorDenominator = 16;
- static constexpr float kMaxLoadFactor =
- 1.0 * kMaxLoadFactorNumerator / kMaxLoadFactorDenominator;
-
// TODO(alkis): Investigate removing some of these fields:
// - ctrl/slots can be derived from each other
// - size can be moved into the slot array
@@ -1843,6 +1763,7 @@
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,
key_equal, allocator_type>
settings_{0, hasher{}, key_equal{}, allocator_type{}};
@@ -1895,10 +1816,9 @@
}
static size_t LowerBoundAllocatedByteSize(size_t size) {
- size_t capacity = container_internal::NormalizeCapacity(
- std::ceil(size / Set::kMaxLoadFactor));
+ size_t capacity = GrowthToLowerboundCapacity(size);
if (capacity == 0) return 0;
- auto layout = Set::MakeLayout(capacity);
+ auto layout = Set::MakeLayout(NormalizeCapacity(capacity));
size_t m = layout.AllocSize();
size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
if (per_slot != ~size_t{}) {
diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc
index 891fa45..a5eff0b 100644
--- a/absl/container/internal/raw_hash_set_allocator_test.cc
+++ b/absl/container/internal/raw_hash_set_allocator_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index cd33a3a..02fd0bf 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +14,6 @@
#include "absl/container/internal/raw_hash_set.h"
-#include <array>
#include <cmath>
#include <cstdint>
#include <deque>
@@ -49,18 +48,47 @@
using ::testing::DoubleNear;
using ::testing::ElementsAre;
+using ::testing::Ge;
+using ::testing::Lt;
using ::testing::Optional;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
TEST(Util, NormalizeCapacity) {
- constexpr size_t kMinCapacity = Group::kWidth - 1;
- EXPECT_EQ(kMinCapacity, NormalizeCapacity(0));
- EXPECT_EQ(kMinCapacity, NormalizeCapacity(1));
- EXPECT_EQ(kMinCapacity, NormalizeCapacity(2));
- EXPECT_EQ(kMinCapacity, NormalizeCapacity(kMinCapacity));
- EXPECT_EQ(kMinCapacity * 2 + 1, NormalizeCapacity(kMinCapacity + 1));
- EXPECT_EQ(kMinCapacity * 2 + 1, NormalizeCapacity(kMinCapacity + 2));
+ EXPECT_EQ(1, NormalizeCapacity(0));
+ EXPECT_EQ(1, NormalizeCapacity(1));
+ EXPECT_EQ(3, NormalizeCapacity(2));
+ EXPECT_EQ(3, NormalizeCapacity(3));
+ EXPECT_EQ(7, NormalizeCapacity(4));
+ EXPECT_EQ(7, NormalizeCapacity(7));
+ EXPECT_EQ(15, NormalizeCapacity(8));
+ EXPECT_EQ(15, NormalizeCapacity(15));
+ EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 1));
+ EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 2));
+}
+
+TEST(Util, GrowthAndCapacity) {
+ // Verify that GrowthToCapacity gives the minimum capacity that has enough
+ // growth.
+ for (size_t growth = 0; growth < 10000; ++growth) {
+ SCOPED_TRACE(growth);
+ size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth));
+ // The capacity is large enough for `growth`
+ EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth));
+ if (growth != 0 && capacity > 1) {
+ // There is no smaller capacity that works.
+ EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth));
+ }
+ }
+
+ for (size_t capacity = Group::kWidth - 1; capacity < 10000;
+ capacity = 2 * capacity + 1) {
+ SCOPED_TRACE(capacity);
+ size_t growth = CapacityToGrowth(capacity);
+ EXPECT_THAT(growth, Lt(capacity));
+ EXPECT_LE(GrowthToLowerboundCapacity(growth), capacity);
+ EXPECT_EQ(NormalizeCapacity(GrowthToLowerboundCapacity(growth)), capacity);
+ }
}
TEST(Util, probe_seq) {
@@ -107,14 +135,14 @@
}
TEST(BitMask, LeadingTrailing) {
- EXPECT_EQ((BitMask<uint32_t, 16>(0b0001101001000000).LeadingZeros()), 3);
- EXPECT_EQ((BitMask<uint32_t, 16>(0b0001101001000000).TrailingZeros()), 6);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).LeadingZeros()), 3);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).TrailingZeros()), 6);
- EXPECT_EQ((BitMask<uint32_t, 16>(0b0000000000000001).LeadingZeros()), 15);
- EXPECT_EQ((BitMask<uint32_t, 16>(0b0000000000000001).TrailingZeros()), 0);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).LeadingZeros()), 15);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).TrailingZeros()), 0);
- EXPECT_EQ((BitMask<uint32_t, 16>(0b1000000000000000).LeadingZeros()), 0);
- EXPECT_EQ((BitMask<uint32_t, 16>(0b1000000000000000).TrailingZeros()), 15);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).LeadingZeros()), 0);
+ EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).TrailingZeros()), 15);
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).LeadingZeros()), 3);
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).TrailingZeros()), 1);
@@ -130,45 +158,50 @@
for (h2_t h = 0; h != 128; ++h) EXPECT_FALSE(Group{EmptyGroup()}.Match(h));
}
-#if SWISSTABLE_HAVE_SSE2
TEST(Group, Match) {
- ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
- 7, 5, 3, 1, 1, 1, 1, 1};
- EXPECT_THAT(Group{group}.Match(0), ElementsAre());
- EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
- EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
- EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
- EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
+ if (Group::kWidth == 16) {
+ ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
+ 7, 5, 3, 1, 1, 1, 1, 1};
+ EXPECT_THAT(Group{group}.Match(0), ElementsAre());
+ EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
+ EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
+ EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
+ EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
+ } else if (Group::kWidth == 8) {
+ ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+ EXPECT_THAT(Group{group}.Match(0), ElementsAre());
+ EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
+ EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
+ } else {
+ FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
+ }
}
TEST(Group, MatchEmpty) {
- ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
- 7, 5, 3, 1, 1, 1, 1, 1};
- EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4));
+ if (Group::kWidth == 16) {
+ ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
+ 7, 5, 3, 1, 1, 1, 1, 1};
+ EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4));
+ } else if (Group::kWidth == 8) {
+ ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+ EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0));
+ } else {
+ FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
+ }
}
TEST(Group, MatchEmptyOrDeleted) {
- ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
- 7, 5, 3, 1, 1, 1, 1, 1};
- EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4));
+ if (Group::kWidth == 16) {
+ ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
+ 7, 5, 3, 1, 1, 1, 1, 1};
+ EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4));
+ } else if (Group::kWidth == 8) {
+ ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+ EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3));
+ } else {
+ FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
+ }
}
-#else
-TEST(Group, Match) {
- ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
- EXPECT_THAT(Group{group}.Match(0), ElementsAre());
- EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
- EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
-}
-TEST(Group, MatchEmpty) {
- ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
- EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0));
-}
-
-TEST(Group, MatchEmptyOrDeleted) {
- ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
- EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3));
-}
-#endif
TEST(Batch, DropDeletes) {
constexpr size_t kCapacity = 63;
@@ -338,6 +371,7 @@
size_t size;
size_t capacity;
size_t growth_left;
+ void* infoz;
};
struct StatelessHash {
size_t operator()(absl::string_view) const { return 0; }
@@ -386,7 +420,8 @@
!defined(UNDEFINED_BEHAVIOR_SANITIZER)
const auto now = [] { return absl::base_internal::CycleClock::Now(); };
- static constexpr int size = 1000000;
+ // Make size enough to not fit in L2 cache (16.7 Mb)
+ static constexpr int size = 1 << 22;
for (int i = 0; i < size; ++i) t.insert(i);
int64_t no_prefetch = 0, prefetch = 0;
@@ -687,7 +722,7 @@
Modulo1000HashTable t;
// Adding the same length (and the same hash) strings
// to have at least kMinFullGroups groups
- // with Group::kWidth collisions. Then feel upto MaxDensitySize;
+ // with Group::kWidth collisions. Then fill up to MaxDensitySize;
const size_t kMinFullGroups = 7;
std::vector<int> keys;
for (size_t i = 0; i < MaxDensitySize(Group::kWidth * kMinFullGroups); ++i) {
@@ -779,7 +814,7 @@
TEST(Table, ClearBug) {
IntTable t;
constexpr size_t capacity = container_internal::Group::kWidth - 1;
- constexpr size_t max_size = capacity / 2;
+ constexpr size_t max_size = capacity / 2 + 1;
for (size_t i = 0; i < max_size; ++i) {
t.insert(i);
}
@@ -810,6 +845,25 @@
EXPECT_TRUE(t.find(0) == t.end());
}
+TEST(Table, EraseMaintainsValidIterator) {
+ IntTable t;
+ const int kNumElements = 100;
+ for (int i = 0; i < kNumElements; i ++) {
+ EXPECT_TRUE(t.emplace(i).second);
+ }
+ EXPECT_EQ(t.size(), kNumElements);
+
+ int num_erase_calls = 0;
+ auto it = t.begin();
+ while (it != t.end()) {
+ t.erase(it++);
+ num_erase_calls++;
+ }
+
+ EXPECT_TRUE(t.empty());
+ EXPECT_EQ(num_erase_calls, kNumElements);
+}
+
// Collect N bad keys by following algorithm:
// 1. Create an empty table and reserve it to 2 * N.
// 2. Insert N random elements.
@@ -1029,7 +1083,6 @@
{{0.95, 0.1}},
{{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
}
- break;
case 16:
if (kRandomizesInserts) {
return {0.1,
@@ -1042,12 +1095,11 @@
{{0.95, 0.05}},
{{0.95, 0}, {0.99, 1}, {0.999, 4}, {0.9999, 10}}};
}
- break;
- default:
- ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
}
+ ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
return {};
}
+
TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) {
ProbeStatsPerSize stats;
std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
@@ -1125,7 +1177,6 @@
{{0.95, 0.3}},
{{0.95, 0}, {0.99, 3}, {0.999, 15}, {0.9999, 25}}};
}
- break;
case 16:
if (kRandomizesInserts) {
return {0.1,
@@ -1138,12 +1189,11 @@
{{0.95, 0.1}},
{{0.95, 0}, {0.99, 1}, {0.999, 6}, {0.9999, 10}}};
}
- break;
- default:
- ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
}
+ ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
return {};
}
+
TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) {
ProbeStatsPerSize stats;
std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
@@ -1431,7 +1481,8 @@
TEST(Table, Equality) {
StringTable t;
- std::vector<std::pair<std::string, std::string>> v = {{"a", "b"}, {"aa", "bb"}};
+ std::vector<std::pair<std::string, std::string>> v = {{"a", "b"},
+ {"aa", "bb"}};
t.insert(std::begin(v), std::end(v));
StringTable u = t;
EXPECT_EQ(u, t);
@@ -1439,20 +1490,24 @@
TEST(Table, Equality2) {
StringTable t;
- std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"}, {"aa", "bb"}};
+ std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"},
+ {"aa", "bb"}};
t.insert(std::begin(v1), std::end(v1));
StringTable u;
- std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"}, {"aa", "aa"}};
+ std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
+ {"aa", "aa"}};
u.insert(std::begin(v2), std::end(v2));
EXPECT_NE(u, t);
}
TEST(Table, Equality3) {
StringTable t;
- std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"}, {"bb", "bb"}};
+ std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"},
+ {"bb", "bb"}};
t.insert(std::begin(v1), std::end(v1));
StringTable u;
- std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"}, {"aa", "aa"}};
+ std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
+ {"aa", "aa"}};
u.insert(std::begin(v2), std::end(v2));
EXPECT_NE(u, t);
}
@@ -1677,7 +1732,7 @@
EXPECT_FALSE(node.empty());
StringTable t2;
- auto res = t2.insert(std::move(node));
+ StringTable::insert_return_type res = t2.insert(std::move(node));
EXPECT_TRUE(res.inserted);
EXPECT_THAT(*res.position, Pair(k0, ""));
EXPECT_FALSE(res.node);
@@ -1707,73 +1762,49 @@
EXPECT_FALSE(node);
}
-StringTable MakeSimpleTable(size_t size) {
- StringTable t;
- for (size_t i = 0; i < size; ++i) t.emplace(std::string(1, 'A' + i), "");
+IntTable MakeSimpleTable(size_t size) {
+ IntTable t;
+ while (t.size() < size) t.insert(t.size());
return t;
}
-std::string OrderOfIteration(const StringTable& t) {
- std::string order;
- for (auto& p : t) order += p.first;
- return order;
+std::vector<int> OrderOfIteration(const IntTable& t) {
+ return {t.begin(), t.end()};
}
+// These IterationOrderChanges tests depend on non-deterministic behavior.
+// We are injecting non-determinism from the pointer of the table, but do so in
+// a way that only the page matters. We have to retry enough times to make sure
+// we are touching different memory pages to cause the ordering to change.
+// We also need to keep the old tables around to avoid getting the same memory
+// blocks over and over.
TEST(Table, IterationOrderChangesByInstance) {
- // Needs to be more than kWidth elements to be able to affect order.
- const StringTable reference = MakeSimpleTable(20);
+ for (size_t size : {2, 6, 12, 20}) {
+ const auto reference_table = MakeSimpleTable(size);
+ const auto reference = OrderOfIteration(reference_table);
- // Since order is non-deterministic we can't just try once and verify.
- // We'll try until we find that order changed. It should not take many tries
- // for that.
- // Important: we have to keep the old tables around. Otherwise tcmalloc will
- // just give us the same blocks and we would be doing the same order again.
- std::vector<StringTable> garbage;
- for (int i = 0; i < 10; ++i) {
- auto trial = MakeSimpleTable(20);
- if (OrderOfIteration(trial) != OrderOfIteration(reference)) {
- // We are done.
- return;
+ std::vector<IntTable> tables;
+ bool found_difference = false;
+ for (int i = 0; !found_difference && i < 500; ++i) {
+ tables.push_back(MakeSimpleTable(size));
+ found_difference = OrderOfIteration(tables.back()) != reference;
}
- garbage.push_back(std::move(trial));
+ if (!found_difference) {
+ FAIL()
+ << "Iteration order remained the same across many attempts with size "
+ << size;
+ }
}
- FAIL();
}
TEST(Table, IterationOrderChangesOnRehash) {
- // Since order is non-deterministic we can't just try once and verify.
- // We'll try until we find that order changed. It should not take many tries
- // for that.
- // Important: we have to keep the old tables around. Otherwise tcmalloc will
- // just give us the same blocks and we would be doing the same order again.
- std::vector<StringTable> garbage;
- for (int i = 0; i < 10; ++i) {
- // Needs to be more than kWidth elements to be able to affect order.
- StringTable t = MakeSimpleTable(20);
- const std::string reference = OrderOfIteration(t);
+ std::vector<IntTable> garbage;
+ for (int i = 0; i < 500; ++i) {
+ auto t = MakeSimpleTable(20);
+ const auto reference = OrderOfIteration(t);
// Force rehash to the same size.
t.rehash(0);
- std::string trial = OrderOfIteration(t);
- if (trial != reference) {
- // We are done.
- return;
- }
- garbage.push_back(std::move(t));
- }
- FAIL();
-}
-
-TEST(Table, IterationOrderChangesForSmallTables) {
- // Since order is non-deterministic we can't just try once and verify.
- // We'll try until we find that order changed.
- // Important: we have to keep the old tables around. Otherwise tcmalloc will
- // just give us the same blocks and we would be doing the same order again.
- StringTable reference_table = MakeSimpleTable(5);
- const std::string reference = OrderOfIteration(reference_table);
- std::vector<StringTable> garbage;
- for (int i = 0; i < 50; ++i) {
- StringTable t = MakeSimpleTable(5);
- std::string trial = OrderOfIteration(t);
+ auto trial = OrderOfIteration(t);
if (trial != reference) {
// We are done.
return;
@@ -1783,136 +1814,22 @@
FAIL() << "Iteration order remained the same across many attempts.";
}
-// Fill the table to 3 different load factors (min, median, max) and evaluate
-// the percentage of perfect hits using the debug API.
-template <class Table, class AddFn>
-std::vector<double> CollectPerfectRatios(Table t, AddFn add) {
- using Key = typename Table::key_type;
+// Verify that pointers are invalidated as soon as a second element is inserted.
+// This prevents dependency on pointer stability on small tables.
+TEST(Table, UnstablePointers) {
+ IntTable table;
- // First, fill enough to have a good distribution.
- constexpr size_t kMinSize = 10000;
- std::vector<Key> keys;
- while (t.size() < kMinSize) keys.push_back(add(t));
- // Then, insert until we reach min load factor.
- double lf = t.load_factor();
- while (lf <= t.load_factor()) keys.push_back(add(t));
-
- // We are now at min load factor. Take a snapshot.
- size_t perfect = 0;
- auto update_perfect = [&](Key k) {
- perfect += GetHashtableDebugNumProbes(t, k) == 0;
+ const auto addr = [&](int i) {
+ return reinterpret_cast<uintptr_t>(&*table.find(i));
};
- for (const auto& k : keys) update_perfect(k);
- std::vector<double> perfect_ratios;
- // Keep going until we hit max load factor.
- while (t.load_factor() < .6) {
- perfect_ratios.push_back(1.0 * perfect / t.size());
- update_perfect(add(t));
- }
- while (t.load_factor() > .5) {
- perfect_ratios.push_back(1.0 * perfect / t.size());
- update_perfect(add(t));
- }
- return perfect_ratios;
-}
+ table.insert(0);
+ const uintptr_t old_ptr = addr(0);
-std::vector<std::pair<double, double>> StringTablePefectRatios() {
- constexpr bool kRandomizesInserts =
-#if NDEBUG
- false;
-#else // NDEBUG
- true;
-#endif // NDEBUG
+ // This causes a rehash.
+ table.insert(1);
- // The effective load factor is larger in non-opt mode because we insert
- // elements out of order.
- switch (container_internal::Group::kWidth) {
- case 8:
- if (kRandomizesInserts) {
- return {{0.986, 0.02}, {0.95, 0.02}, {0.89, 0.02}};
- } else {
- return {{0.995, 0.01}, {0.97, 0.01}, {0.89, 0.02}};
- }
- break;
- case 16:
- if (kRandomizesInserts) {
- return {{0.973, 0.01}, {0.965, 0.01}, {0.92, 0.02}};
- } else {
- return {{0.995, 0.005}, {0.99, 0.005}, {0.94, 0.01}};
- }
- break;
- default:
- // Ignore anything else.
- return {};
- }
-}
-
-// This is almost a change detector, but it allows us to know how we are
-// affecting the probe distribution.
-TEST(Table, EffectiveLoadFactorStrings) {
- std::vector<double> perfect_ratios =
- CollectPerfectRatios(StringTable(), [](StringTable& t) {
- return t.emplace(std::to_string(t.size()), "").first->first;
- });
-
- auto ratios = StringTablePefectRatios();
- if (ratios.empty()) return;
-
- EXPECT_THAT(perfect_ratios.front(),
- DoubleNear(ratios[0].first, ratios[0].second));
- EXPECT_THAT(perfect_ratios[perfect_ratios.size() / 2],
- DoubleNear(ratios[1].first, ratios[1].second));
- EXPECT_THAT(perfect_ratios.back(),
- DoubleNear(ratios[2].first, ratios[2].second));
-}
-
-std::vector<std::pair<double, double>> IntTablePefectRatios() {
- constexpr bool kRandomizesInserts =
-#ifdef NDEBUG
- false;
-#else // NDEBUG
- true;
-#endif // NDEBUG
-
- // The effective load factor is larger in non-opt mode because we insert
- // elements out of order.
- switch (container_internal::Group::kWidth) {
- case 8:
- if (kRandomizesInserts) {
- return {{0.99, 0.02}, {0.985, 0.02}, {0.95, 0.05}};
- } else {
- return {{0.99, 0.01}, {0.99, 0.01}, {0.95, 0.02}};
- }
- break;
- case 16:
- if (kRandomizesInserts) {
- return {{0.98, 0.02}, {0.978, 0.02}, {0.96, 0.02}};
- } else {
- return {{0.998, 0.003}, {0.995, 0.01}, {0.975, 0.02}};
- }
- break;
- default:
- // Ignore anything else.
- return {};
- }
-}
-
-// This is almost a change detector, but it allows us to know how we are
-// affecting the probe distribution.
-TEST(Table, EffectiveLoadFactorInts) {
- std::vector<double> perfect_ratios = CollectPerfectRatios(
- IntTable(), [](IntTable& t) { return *t.emplace(t.size()).first; });
-
- auto ratios = IntTablePefectRatios();
- if (ratios.empty()) return;
-
- EXPECT_THAT(perfect_ratios.front(),
- DoubleNear(ratios[0].first, ratios[0].second));
- EXPECT_THAT(perfect_ratios[perfect_ratios.size() / 2],
- DoubleNear(ratios[1].first, ratios[1].second));
- EXPECT_THAT(perfect_ratios.back(),
- DoubleNear(ratios[2].first, ratios[2].second));
+ EXPECT_NE(old_ptr, addr(0));
}
// Confirm that we assert if we try to erase() end().
@@ -1931,9 +1848,31 @@
EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg);
}
+TEST(RawHashSamplerTest, Sample) {
+ // Enable the feature even if the prod default is off.
+ SetHashtablezEnabled(true);
+ SetHashtablezSampleParameter(100);
+
+ auto& sampler = HashtablezSampler::Global();
+ size_t start_size = 0;
+ start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
+
+ std::vector<IntTable> tables;
+ for (int i = 0; i < 1000000; ++i) {
+ tables.emplace_back();
+ tables.back().insert(1);
+ }
+ size_t end_size = 0;
+ end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
+
+ EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
+ 0.01, 0.005);
+}
+
#ifdef ADDRESS_SANITIZER
TEST(Sanitizer, PoisoningUnused) {
IntTable t;
+ t.reserve(5);
// Insert something to force an allocation.
int64_t& v1 = *t.insert(0).first;
diff --git a/absl/container/internal/test_instance_tracker.cc b/absl/container/internal/test_instance_tracker.cc
index b18e0bb..5a66cb4 100644
--- a/absl/container/internal/test_instance_tracker.cc
+++ b/absl/container/internal/test_instance_tracker.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/test_instance_tracker.h b/absl/container/internal/test_instance_tracker.h
index ec45f57..032d16d 100644
--- a/absl/container/internal/test_instance_tracker.h
+++ b/absl/container/internal/test_instance_tracker.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/test_instance_tracker_test.cc b/absl/container/internal/test_instance_tracker_test.cc
index 0ae5763..091f428 100644
--- a/absl/container/internal/test_instance_tracker_test.cc
+++ b/absl/container/internal/test_instance_tracker_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/tracked.h b/absl/container/internal/tracked.h
index 7d14af0..75173ab 100644
--- a/absl/container/internal/tracked.h
+++ b/absl/container/internal/tracked.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h
index 2ffb646..68817e4 100644
--- a/absl/container/internal/unordered_map_constructor_test.h
+++ b/absl/container/internal/unordered_map_constructor_test.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -29,7 +29,7 @@
template <class UnordMap>
class ConstructorTest : public ::testing::Test {};
-TYPED_TEST_CASE_P(ConstructorTest);
+TYPED_TEST_SUITE_P(ConstructorTest);
TYPED_TEST_P(ConstructorTest, NoArgs) {
TypeParam m;
@@ -83,8 +83,28 @@
EXPECT_GE(m.bucket_count(), 123);
}
-TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
+template <typename T>
+struct is_std_unordered_map : std::false_type {};
+
+template <typename... T>
+struct is_std_unordered_map<std::unordered_map<T...>> : std::true_type {};
+
#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+using has_cxx14_std_apis = std::true_type;
+#else
+using has_cxx14_std_apis = std::false_type;
+#endif
+
+template <typename T>
+using expect_cxx14_apis =
+ absl::disjunction<absl::negation<is_std_unordered_map<T>>,
+ has_cxx14_std_apis>;
+
+template <typename TypeParam>
+void BucketCountAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void BucketCountAllocTest(std::true_type) {
using A = typename TypeParam::allocator_type;
A alloc(0);
TypeParam m(123, alloc);
@@ -92,11 +112,17 @@
EXPECT_TRUE(m.empty());
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
-#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
+ BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
+template <typename TypeParam>
+void BucketCountHashAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void BucketCountHashAllocTest(std::true_type) {
using H = typename TypeParam::hasher;
using A = typename TypeParam::allocator_type;
H hasher;
@@ -107,18 +133,38 @@
EXPECT_TRUE(m.empty());
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, BucketAlloc) {
+TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
+ BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+using has_alloc_std_constructors = std::true_type;
+#else
+using has_alloc_std_constructors = std::false_type;
+#endif
+
+template <typename T>
+using expect_alloc_constructors =
+ absl::disjunction<absl::negation<is_std_unordered_map<T>>,
+ has_alloc_std_constructors>;
+
+template <typename TypeParam>
+void AllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void AllocTest(std::true_type) {
using A = typename TypeParam::allocator_type;
A alloc(0);
TypeParam m(alloc);
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_TRUE(m.empty());
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, Alloc) {
+ AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
}
TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
@@ -140,8 +186,11 @@
EXPECT_GE(m.bucket_count(), 123);
}
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
-#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+template <typename TypeParam>
+void InputIteratorBucketAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void InputIteratorBucketAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using A = typename TypeParam::allocator_type;
A alloc(0);
@@ -152,11 +201,17 @@
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
-#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
+ InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
+template <typename TypeParam>
+void InputIteratorBucketHashAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void InputIteratorBucketHashAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using A = typename TypeParam::allocator_type;
@@ -170,7 +225,10 @@
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
EXPECT_GE(m.bucket_count(), 123);
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
+ InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
}
TYPED_TEST_P(ConstructorTest, CopyConstructor) {
@@ -190,8 +248,11 @@
EXPECT_EQ(m, n);
}
-TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
-#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+template <typename TypeParam>
+void CopyConstructorAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void CopyConstructorAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using E = typename TypeParam::key_equal;
@@ -206,7 +267,10 @@
EXPECT_EQ(m.key_eq(), n.key_eq());
EXPECT_NE(m.get_allocator(), n.get_allocator());
EXPECT_EQ(m, n);
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
+ CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
}
// TODO(alkis): Test non-propagating allocators on copy constructors.
@@ -229,8 +293,11 @@
EXPECT_EQ(m, n);
}
-TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
-#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+template <typename TypeParam>
+void MoveConstructorAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void MoveConstructorAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using E = typename TypeParam::key_equal;
@@ -246,7 +313,10 @@
EXPECT_EQ(m.key_eq(), n.key_eq());
EXPECT_NE(m.get_allocator(), n.get_allocator());
EXPECT_EQ(m, n);
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
+ MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
}
// TODO(alkis): Test non-propagating allocators on move constructors.
@@ -269,8 +339,11 @@
EXPECT_GE(m.bucket_count(), 123);
}
-TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
-#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+template <typename TypeParam>
+void InitializerListBucketAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void InitializerListBucketAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using A = typename TypeParam::allocator_type;
hash_internal::Generator<T> gen;
@@ -280,11 +353,17 @@
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
-#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
+TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
+ InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
+template <typename TypeParam>
+void InitializerListBucketHashAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void InitializerListBucketHashAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using A = typename TypeParam::allocator_type;
@@ -297,7 +376,10 @@
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
EXPECT_GE(m.bucket_count(), 123);
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
+ InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
}
TYPED_TEST_P(ConstructorTest, Assignment) {
@@ -390,15 +472,16 @@
REGISTER_TYPED_TEST_CASE_P(
ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual,
- BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc,
- BucketAlloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
+ BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc,
+ InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc,
MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc,
InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment,
- MoveAssignment, AssignmentFromInitializerList,
- AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting,
+ MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting,
+ MoveAssignmentOverwritesExisting,
AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
} // namespace container_internal
} // namespace absl
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
diff --git a/absl/container/internal/unordered_map_lookup_test.h b/absl/container/internal/unordered_map_lookup_test.h
index 1f1b6b4..ebd3612 100644
--- a/absl/container/internal/unordered_map_lookup_test.h
+++ b/absl/container/internal/unordered_map_lookup_test.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -26,7 +26,7 @@
template <class UnordMap>
class LookupTest : public ::testing::Test {};
-TYPED_TEST_CASE_P(LookupTest);
+TYPED_TEST_SUITE_P(LookupTest);
TYPED_TEST_P(LookupTest, At) {
using T = hash_internal::GeneratedType<TypeParam>;
@@ -111,4 +111,5 @@
} // namespace container_internal
} // namespace absl
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_
diff --git a/absl/container/internal/unordered_map_members_test.h b/absl/container/internal/unordered_map_members_test.h
new file mode 100644
index 0000000..1bf31ab
--- /dev/null
+++ b/absl/container/internal/unordered_map_members_test.h
@@ -0,0 +1,85 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
+#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
+
+#include <type_traits>
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+namespace container_internal {
+
+template <class UnordMap>
+class MembersTest : public ::testing::Test {};
+
+TYPED_TEST_SUITE_P(MembersTest);
+
+template <typename T>
+void UseType() {}
+
+TYPED_TEST_P(MembersTest, Typedefs) {
+ EXPECT_TRUE((std::is_same<std::pair<const typename TypeParam::key_type,
+ typename TypeParam::mapped_type>,
+ typename TypeParam::value_type>()));
+ EXPECT_TRUE((absl::conjunction<
+ absl::negation<std::is_signed<typename TypeParam::size_type>>,
+ std::is_integral<typename TypeParam::size_type>>()));
+ EXPECT_TRUE((absl::conjunction<
+ std::is_signed<typename TypeParam::difference_type>,
+ std::is_integral<typename TypeParam::difference_type>>()));
+ EXPECT_TRUE((std::is_convertible<
+ decltype(std::declval<const typename TypeParam::hasher&>()(
+ std::declval<const typename TypeParam::key_type&>())),
+ size_t>()));
+ EXPECT_TRUE((std::is_convertible<
+ decltype(std::declval<const typename TypeParam::key_equal&>()(
+ std::declval<const typename TypeParam::key_type&>(),
+ std::declval<const typename TypeParam::key_type&>())),
+ bool>()));
+ EXPECT_TRUE((std::is_same<typename TypeParam::allocator_type::value_type,
+ typename TypeParam::value_type>()));
+ EXPECT_TRUE((std::is_same<typename TypeParam::value_type&,
+ typename TypeParam::reference>()));
+ EXPECT_TRUE((std::is_same<const typename TypeParam::value_type&,
+ typename TypeParam::const_reference>()));
+ EXPECT_TRUE((std::is_same<typename std::allocator_traits<
+ typename TypeParam::allocator_type>::pointer,
+ typename TypeParam::pointer>()));
+ EXPECT_TRUE(
+ (std::is_same<typename std::allocator_traits<
+ typename TypeParam::allocator_type>::const_pointer,
+ typename TypeParam::const_pointer>()));
+}
+
+TYPED_TEST_P(MembersTest, SimpleFunctions) {
+ EXPECT_GT(TypeParam().max_size(), 0);
+}
+
+TYPED_TEST_P(MembersTest, BeginEnd) {
+ TypeParam t = {typename TypeParam::value_type{}};
+ EXPECT_EQ(t.begin(), t.cbegin());
+ EXPECT_EQ(t.end(), t.cend());
+ EXPECT_NE(t.begin(), t.end());
+ EXPECT_NE(t.cbegin(), t.cend());
+}
+
+REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd);
+
+} // namespace container_internal
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
diff --git a/absl/container/internal/unordered_map_modifiers_test.h b/absl/container/internal/unordered_map_modifiers_test.h
index b6c633a..52a1092 100644
--- a/absl/container/internal/unordered_map_modifiers_test.h
+++ b/absl/container/internal/unordered_map_modifiers_test.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -26,7 +26,7 @@
template <class UnordMap>
class ModifiersTest : public ::testing::Test {};
-TYPED_TEST_CASE_P(ModifiersTest);
+TYPED_TEST_SUITE_P(ModifiersTest);
TYPED_TEST_P(ModifiersTest, Clear) {
using T = hash_internal::GeneratedType<TypeParam>;
@@ -269,4 +269,5 @@
} // namespace container_internal
} // namespace absl
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
diff --git a/absl/container/internal/unordered_map_test.cc b/absl/container/internal/unordered_map_test.cc
index 40e799c..72567ea 100644
--- a/absl/container/internal/unordered_map_test.cc
+++ b/absl/container/internal/unordered_map_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,6 +16,7 @@
#include "absl/container/internal/unordered_map_constructor_test.h"
#include "absl/container/internal/unordered_map_lookup_test.h"
+#include "absl/container/internal/unordered_map_members_test.h"
#include "absl/container/internal/unordered_map_modifiers_test.h"
namespace absl {
@@ -29,9 +30,10 @@
StatefulTestingEqual,
Alloc<std::pair<const std::string, std::string>>>>;
-INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, ConstructorTest, MapTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, LookupTest, MapTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, ModifiersTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ConstructorTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, LookupTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, MembersTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ModifiersTest, MapTypes);
} // namespace
} // namespace container_internal
diff --git a/absl/container/internal/unordered_set_constructor_test.h b/absl/container/internal/unordered_set_constructor_test.h
index cb59370..f484468 100644
--- a/absl/container/internal/unordered_set_constructor_test.h
+++ b/absl/container/internal/unordered_set_constructor_test.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,12 +16,14 @@
#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
#include <algorithm>
+#include <unordered_set>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/internal/hash_generator_testing.h"
#include "absl/container/internal/hash_policy_testing.h"
+#include "absl/meta/type_traits.h"
namespace absl {
namespace container_internal {
@@ -29,7 +31,7 @@
template <class UnordMap>
class ConstructorTest : public ::testing::Test {};
-TYPED_TEST_CASE_P(ConstructorTest);
+TYPED_TEST_SUITE_P(ConstructorTest);
TYPED_TEST_P(ConstructorTest, NoArgs) {
TypeParam m;
@@ -91,8 +93,28 @@
EXPECT_GE(cm.bucket_count(), 123);
}
-TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
+template <typename T>
+struct is_std_unordered_set : std::false_type {};
+
+template <typename... T>
+struct is_std_unordered_set<std::unordered_set<T...>> : std::true_type {};
+
#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+using has_cxx14_std_apis = std::true_type;
+#else
+using has_cxx14_std_apis = std::false_type;
+#endif
+
+template <typename T>
+using expect_cxx14_apis =
+ absl::disjunction<absl::negation<is_std_unordered_set<T>>,
+ has_cxx14_std_apis>;
+
+template <typename TypeParam>
+void BucketCountAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void BucketCountAllocTest(std::true_type) {
using A = typename TypeParam::allocator_type;
A alloc(0);
TypeParam m(123, alloc);
@@ -100,11 +122,17 @@
EXPECT_TRUE(m.empty());
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
-#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
+ BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
+template <typename TypeParam>
+void BucketCountHashAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void BucketCountHashAllocTest(std::true_type) {
using H = typename TypeParam::hasher;
using A = typename TypeParam::allocator_type;
H hasher;
@@ -115,18 +143,38 @@
EXPECT_TRUE(m.empty());
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, BucketAlloc) {
+TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
+ BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+using has_alloc_std_constructors = std::true_type;
+#else
+using has_alloc_std_constructors = std::false_type;
+#endif
+
+template <typename T>
+using expect_alloc_constructors =
+ absl::disjunction<absl::negation<is_std_unordered_set<T>>,
+ has_alloc_std_constructors>;
+
+template <typename TypeParam>
+void AllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void AllocTest(std::true_type) {
using A = typename TypeParam::allocator_type;
A alloc(0);
TypeParam m(alloc);
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_TRUE(m.empty());
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, Alloc) {
+ AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
}
TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
@@ -148,8 +196,11 @@
EXPECT_GE(m.bucket_count(), 123);
}
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
-#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+template <typename TypeParam>
+void InputIteratorBucketAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void InputIteratorBucketAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using A = typename TypeParam::allocator_type;
A alloc(0);
@@ -160,11 +211,17 @@
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
-#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
+ InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
+template <typename TypeParam>
+void InputIteratorBucketHashAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void InputIteratorBucketHashAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using A = typename TypeParam::allocator_type;
@@ -178,7 +235,10 @@
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
EXPECT_GE(m.bucket_count(), 123);
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
+ InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
}
TYPED_TEST_P(ConstructorTest, CopyConstructor) {
@@ -196,10 +256,14 @@
EXPECT_EQ(m.key_eq(), n.key_eq());
EXPECT_EQ(m.get_allocator(), n.get_allocator());
EXPECT_EQ(m, n);
+ EXPECT_NE(TypeParam(0, hasher, equal, alloc), n);
}
-TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
-#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+template <typename TypeParam>
+void CopyConstructorAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void CopyConstructorAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using E = typename TypeParam::key_equal;
@@ -214,7 +278,10 @@
EXPECT_EQ(m.key_eq(), n.key_eq());
EXPECT_NE(m.get_allocator(), n.get_allocator());
EXPECT_EQ(m, n);
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
+ CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
}
// TODO(alkis): Test non-propagating allocators on copy constructors.
@@ -237,8 +304,11 @@
EXPECT_EQ(m, n);
}
-TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
-#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
+template <typename TypeParam>
+void MoveConstructorAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void MoveConstructorAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using E = typename TypeParam::key_equal;
@@ -254,7 +324,10 @@
EXPECT_EQ(m.key_eq(), n.key_eq());
EXPECT_NE(m.get_allocator(), n.get_allocator());
EXPECT_EQ(m, n);
-#endif
+}
+
+TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
+ MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
}
// TODO(alkis): Test non-propagating allocators on move constructors.
@@ -277,8 +350,11 @@
EXPECT_GE(m.bucket_count(), 123);
}
-TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
-#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+template <typename TypeParam>
+void InitializerListBucketAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void InitializerListBucketAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using A = typename TypeParam::allocator_type;
hash_internal::Generator<T> gen;
@@ -288,11 +364,17 @@
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
-#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
+TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
+ InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
+template <typename TypeParam>
+void InitializerListBucketHashAllocTest(std::false_type) {}
+
+template <typename TypeParam>
+void InitializerListBucketHashAllocTest(std::true_type) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using A = typename TypeParam::allocator_type;
@@ -305,10 +387,13 @@
EXPECT_EQ(m.get_allocator(), alloc);
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
EXPECT_GE(m.bucket_count(), 123);
-#endif
}
-TYPED_TEST_P(ConstructorTest, Assignment) {
+TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
+ InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+}
+
+TYPED_TEST_P(ConstructorTest, CopyAssignment) {
using T = hash_internal::GeneratedType<TypeParam>;
using H = typename TypeParam::hasher;
using E = typename TypeParam::key_equal;
@@ -394,15 +479,16 @@
REGISTER_TYPED_TEST_CASE_P(
ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual,
- BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc,
- BucketAlloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
+ BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc,
+ InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc,
MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc,
- InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment,
- MoveAssignment, AssignmentFromInitializerList,
- AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting,
+ InitializerListBucketAlloc, InitializerListBucketHashAlloc, CopyAssignment,
+ MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting,
+ MoveAssignmentOverwritesExisting,
AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
} // namespace container_internal
} // namespace absl
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
diff --git a/absl/container/internal/unordered_set_lookup_test.h b/absl/container/internal/unordered_set_lookup_test.h
index aca9c6a..05b32b5 100644
--- a/absl/container/internal/unordered_set_lookup_test.h
+++ b/absl/container/internal/unordered_set_lookup_test.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -26,7 +26,7 @@
template <class UnordSet>
class LookupTest : public ::testing::Test {};
-TYPED_TEST_CASE_P(LookupTest);
+TYPED_TEST_SUITE_P(LookupTest);
TYPED_TEST_P(LookupTest, Count) {
using T = hash_internal::GeneratedType<TypeParam>;
@@ -85,4 +85,5 @@
} // namespace container_internal
} // namespace absl
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
diff --git a/absl/container/internal/unordered_set_members_test.h b/absl/container/internal/unordered_set_members_test.h
new file mode 100644
index 0000000..b96c945
--- /dev/null
+++ b/absl/container/internal/unordered_set_members_test.h
@@ -0,0 +1,84 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_
+#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_
+
+#include <type_traits>
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+namespace container_internal {
+
+template <class UnordSet>
+class MembersTest : public ::testing::Test {};
+
+TYPED_TEST_SUITE_P(MembersTest);
+
+template <typename T>
+void UseType() {}
+
+TYPED_TEST_P(MembersTest, Typedefs) {
+ EXPECT_TRUE((std::is_same<typename TypeParam::key_type,
+ typename TypeParam::value_type>()));
+ EXPECT_TRUE((absl::conjunction<
+ absl::negation<std::is_signed<typename TypeParam::size_type>>,
+ std::is_integral<typename TypeParam::size_type>>()));
+ EXPECT_TRUE((absl::conjunction<
+ std::is_signed<typename TypeParam::difference_type>,
+ std::is_integral<typename TypeParam::difference_type>>()));
+ EXPECT_TRUE((std::is_convertible<
+ decltype(std::declval<const typename TypeParam::hasher&>()(
+ std::declval<const typename TypeParam::key_type&>())),
+ size_t>()));
+ EXPECT_TRUE((std::is_convertible<
+ decltype(std::declval<const typename TypeParam::key_equal&>()(
+ std::declval<const typename TypeParam::key_type&>(),
+ std::declval<const typename TypeParam::key_type&>())),
+ bool>()));
+ EXPECT_TRUE((std::is_same<typename TypeParam::allocator_type::value_type,
+ typename TypeParam::value_type>()));
+ EXPECT_TRUE((std::is_same<typename TypeParam::value_type&,
+ typename TypeParam::reference>()));
+ EXPECT_TRUE((std::is_same<const typename TypeParam::value_type&,
+ typename TypeParam::const_reference>()));
+ EXPECT_TRUE((std::is_same<typename std::allocator_traits<
+ typename TypeParam::allocator_type>::pointer,
+ typename TypeParam::pointer>()));
+ EXPECT_TRUE(
+ (std::is_same<typename std::allocator_traits<
+ typename TypeParam::allocator_type>::const_pointer,
+ typename TypeParam::const_pointer>()));
+}
+
+TYPED_TEST_P(MembersTest, SimpleFunctions) {
+ EXPECT_GT(TypeParam().max_size(), 0);
+}
+
+TYPED_TEST_P(MembersTest, BeginEnd) {
+ TypeParam t = {typename TypeParam::value_type{}};
+ EXPECT_EQ(t.begin(), t.cbegin());
+ EXPECT_EQ(t.end(), t.cend());
+ EXPECT_NE(t.begin(), t.end());
+ EXPECT_NE(t.cbegin(), t.cend());
+}
+
+REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd);
+
+} // namespace container_internal
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_
diff --git a/absl/container/internal/unordered_set_modifiers_test.h b/absl/container/internal/unordered_set_modifiers_test.h
index 9beacf3..79a8d42 100644
--- a/absl/container/internal/unordered_set_modifiers_test.h
+++ b/absl/container/internal/unordered_set_modifiers_test.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -26,7 +26,7 @@
template <class UnordSet>
class ModifiersTest : public ::testing::Test {};
-TYPED_TEST_CASE_P(ModifiersTest);
+TYPED_TEST_SUITE_P(ModifiersTest);
TYPED_TEST_P(ModifiersTest, Clear) {
using T = hash_internal::GeneratedType<TypeParam>;
@@ -184,4 +184,5 @@
} // namespace container_internal
} // namespace absl
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
diff --git a/absl/container/internal/unordered_set_test.cc b/absl/container/internal/unordered_set_test.cc
index 1281ce5..6478fac 100644
--- a/absl/container/internal/unordered_set_test.cc
+++ b/absl/container/internal/unordered_set_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,21 +16,23 @@
#include "absl/container/internal/unordered_set_constructor_test.h"
#include "absl/container/internal/unordered_set_lookup_test.h"
+#include "absl/container/internal/unordered_set_members_test.h"
#include "absl/container/internal/unordered_set_modifiers_test.h"
namespace absl {
namespace container_internal {
namespace {
-using SetTypes =
- ::testing::Types<std::unordered_set<int, StatefulTestingHash,
- StatefulTestingEqual, Alloc<int>>,
- std::unordered_set<std::string, StatefulTestingHash,
- StatefulTestingEqual, Alloc<std::string>>>;
+using SetTypes = ::testing::Types<
+ std::unordered_set<int, StatefulTestingHash, StatefulTestingEqual,
+ Alloc<int>>,
+ std::unordered_set<std::string, StatefulTestingHash, StatefulTestingEqual,
+ Alloc<std::string>>>;
-INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, ConstructorTest, SetTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, LookupTest, SetTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, ModifiersTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ConstructorTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, LookupTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, MembersTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ModifiersTest, SetTypes);
} // namespace
} // namespace container_internal
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h
index 6369ec3..a841f5a 100644
--- a/absl/container/node_hash_map.h
+++ b/absl/container/node_hash_map.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -40,6 +40,7 @@
#include <type_traits>
#include <utility>
+#include "absl/algorithm/container.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/node_hash_policy.h"
@@ -71,7 +72,7 @@
// By default, `node_hash_map` uses the `absl::Hash` hashing framework.
// All fundamental and Abseil types that support the `absl::Hash` framework have
// a compatible equality operator for comparing insertions into `node_hash_map`.
-// If your type is not yet supported by the `asbl::Hash` framework, see
+// If your type is not yet supported by the `absl::Hash` framework, see
// absl/hash/hash.h for information on extending Abseil hashing to user-defined
// types.
//
@@ -91,7 +92,7 @@
// std::string search_key = "b";
// auto result = ducks.find(search_key);
// if (result != ducks.end()) {
-// std::cout << "Result: " << search_key->second << std::endl;
+// std::cout << "Result: " << result->second << std::endl;
// }
template <class Key, class Value,
class Hash = absl::container_internal::hash_default_hash<Key>,
@@ -104,6 +105,46 @@
using Base = typename node_hash_map::raw_hash_map;
public:
+ // Constructors and Assignment Operators
+ //
+ // A node_hash_map supports the same overload set as `std::unordered_map`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // // No allocation for the table's elements is made.
+ // absl::node_hash_map<int, std::string> map1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::node_hash_map<int, std::string> map2 =
+ // {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::node_hash_map<int, std::string> map3(map2);
+ //
+ // * Copy assignment operator
+ //
+ // // Hash functor and Comparator are copied as well
+ // absl::node_hash_map<int, std::string> map4;
+ // map4 = map3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::node_hash_map<int, std::string> map5(std::move(map4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::node_hash_map<int, std::string> map6;
+ // map6 = std::move(map5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
+ // absl::node_hash_map<int, std::string> map7(v.begin(), v.end());
node_hash_map() {}
using Base::Base;
@@ -526,5 +567,16 @@
static const Value& value(const value_type* elem) { return elem->second; }
};
} // namespace container_internal
+
+namespace container_algorithm_internal {
+
+// Specialization of trait in absl/algorithm/container.h
+template <class Key, class T, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<
+ absl::node_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
+
+} // namespace container_algorithm_internal
+
} // namespace absl
+
#endif // ABSL_CONTAINER_NODE_HASH_MAP_H_
diff --git a/absl/container/node_hash_map_test.cc b/absl/container/node_hash_map_test.cc
index bd78964..0f2714a 100644
--- a/absl/container/node_hash_map_test.cc
+++ b/absl/container/node_hash_map_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,6 +17,7 @@
#include "absl/container/internal/tracked.h"
#include "absl/container/internal/unordered_map_constructor_test.h"
#include "absl/container/internal/unordered_map_lookup_test.h"
+#include "absl/container/internal/unordered_map_members_test.h"
#include "absl/container/internal/unordered_map_modifiers_test.h"
namespace absl {
@@ -34,9 +35,10 @@
StatefulTestingEqual,
Alloc<std::pair<const std::string, std::string>>>>;
-INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, ConstructorTest, MapTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, LookupTest, MapTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, ModifiersTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ConstructorTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, LookupTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, MembersTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ModifiersTest, MapTypes);
using M = absl::node_hash_map<std::string, Tracked<int>>;
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h
index 90d4ce0..0cd1fe5 100644
--- a/absl/container/node_hash_set.h
+++ b/absl/container/node_hash_set.h
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -37,6 +37,7 @@
#include <type_traits>
+#include "absl/algorithm/container.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/node_hash_policy.h"
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
@@ -67,7 +68,7 @@
// By default, `node_hash_set` uses the `absl::Hash` hashing framework.
// All fundamental and Abseil types that support the `absl::Hash` framework have
// a compatible equality operator for comparing insertions into `node_hash_set`.
-// If your type is not yet supported by the `asbl::Hash` framework, see
+// If your type is not yet supported by the `absl::Hash` framework, see
// absl/hash/hash.h for information on extending Abseil hashing to user-defined
// types.
//
@@ -96,6 +97,46 @@
using Base = typename node_hash_set::raw_hash_set;
public:
+ // Constructors and Assignment Operators
+ //
+ // A node_hash_set supports the same overload set as `std::unordered_map`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // // No allocation for the table's elements is made.
+ // absl::node_hash_set<std::string> set1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::node_hash_set<std::string> set2 =
+ // {{"huey"}, {"dewey"}, {"louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::node_hash_set<std::string> set3(set2);
+ //
+ // * Copy assignment operator
+ //
+ // // Hash functor and Comparator are copied as well
+ // absl::node_hash_set<std::string> set4;
+ // set4 = set3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::node_hash_set<std::string> set5(std::move(set4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::node_hash_set<std::string> set6;
+ // set6 = std::move(set5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::string> v = {"a", "b"};
+ // absl::node_hash_set<std::string> set7(v.begin(), v.end());
node_hash_set() {}
using Base::Base;
@@ -232,8 +273,7 @@
//
// The element may be constructed even if there already is an element with the
// key in the container, in which case the newly constructed element will be
- // destroyed immediately. Prefer `try_emplace()` unless your key is not
- // copyable or moveable.
+ // destroyed immediately.
//
// If rehashing occurs due to the insertion, all iterators are invalidated.
using Base::emplace;
@@ -247,8 +287,7 @@
//
// The element may be constructed even if there already is an element with the
// key in the container, in which case the newly constructed element will be
- // destroyed immediately. Prefer `try_emplace()` unless your key is not
- // copyable or moveable.
+ // destroyed immediately.
//
// If rehashing occurs due to the insertion, all iterators are invalidated.
using Base::emplace_hint;
@@ -435,5 +474,15 @@
static size_t element_space_used(const T*) { return sizeof(T); }
};
} // namespace container_internal
+
+namespace container_algorithm_internal {
+
+// Specialization of trait in absl/algorithm/container.h
+template <class Key, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<absl::node_hash_set<Key, Hash, KeyEqual, Allocator>>
+ : std::true_type {};
+
+} // namespace container_algorithm_internal
} // namespace absl
+
#endif // ABSL_CONTAINER_NODE_HASH_SET_H_
diff --git a/absl/container/node_hash_set_test.cc b/absl/container/node_hash_set_test.cc
index 7e498f0..0ea76e7 100644
--- a/absl/container/node_hash_set_test.cc
+++ b/absl/container/node_hash_set_test.cc
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,6 +16,7 @@
#include "absl/container/internal/unordered_set_constructor_test.h"
#include "absl/container/internal/unordered_set_lookup_test.h"
+#include "absl/container/internal/unordered_set_members_test.h"
#include "absl/container/internal/unordered_set_modifiers_test.h"
namespace absl {
@@ -29,14 +30,15 @@
using SetTypes = ::testing::Types<
node_hash_set<int, StatefulTestingHash, StatefulTestingEqual, Alloc<int>>,
node_hash_set<std::string, StatefulTestingHash, StatefulTestingEqual,
- Alloc<int>>,
+ Alloc<std::string>>,
node_hash_set<Enum, StatefulTestingHash, StatefulTestingEqual, Alloc<Enum>>,
node_hash_set<EnumClass, StatefulTestingHash, StatefulTestingEqual,
Alloc<EnumClass>>>;
-INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, ConstructorTest, SetTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, LookupTest, SetTypes);
-INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, ModifiersTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, ConstructorTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, LookupTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, MembersTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, ModifiersTest, SetTypes);
TEST(NodeHashSet, MoveableNotCopyableCompiles) {
node_hash_set<std::unique_ptr<void*>> t;
diff --git a/absl/copts.bzl b/absl/copts.bzl
deleted file mode 100644
index 5c508f1..0000000
--- a/absl/copts.bzl
+++ /dev/null
@@ -1,164 +0,0 @@
-"""absl specific copts.
-
-Flags specified here must not impact ABI. Code compiled with and without these
-opts will be linked together, and in some cases headers compiled with and
-without these options will be part of the same program.
-"""
-GCC_FLAGS = [
- "-Wall",
- "-Wextra",
- "-Wcast-qual",
- "-Wconversion-null",
- "-Wmissing-declarations",
- "-Woverlength-strings",
- "-Wpointer-arith",
- "-Wunused-local-typedefs",
- "-Wunused-result",
- "-Wvarargs",
- "-Wvla", # variable-length array
- "-Wwrite-strings",
- # Google style does not use unsigned integers, though STL containers
- # have unsigned types.
- "-Wno-sign-compare",
-]
-
-GCC_TEST_FLAGS = [
- "-Wno-conversion-null",
- "-Wno-missing-declarations",
- "-Wno-sign-compare",
- "-Wno-unused-function",
- "-Wno-unused-parameter",
- "-Wno-unused-private-field",
-]
-
-# Docs on single flags is preceded by a comment.
-# Docs on groups of flags is preceded by ###.
-
-LLVM_FLAGS = [
- "-Wall",
- "-Wextra",
- "-Weverything",
- # 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-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-nested-anon-types",
- "-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-undef",
- "-Wno-unknown-warning-option",
- "-Wno-unreachable-code",
- # Causes warnings on include guards
- "-Wno-unused-macros",
- "-Wno-weak-vtables",
- ###
- # 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_FLAGS = [
- "-Wno-c99-extensions",
- "-Wno-missing-noreturn",
- "-Wno-missing-prototypes",
- "-Wno-missing-variable-declarations",
- "-Wno-null-conversion",
- "-Wno-shadow",
- "-Wno-shift-sign-overflow",
- "-Wno-sign-compare",
- "-Wno-unused-function",
- "-Wno-unused-member-function",
- "-Wno-unused-parameter",
- "-Wno-unused-private-field",
- "-Wno-unused-template",
- "-Wno-used-but-marked-unused",
- "-Wno-zero-as-null-pointer-constant",
- # gtest depends on this GNU extension being offered.
- "-Wno-gnu-zero-variadic-macro-arguments",
-]
-
-MSVC_FLAGS = [
- "/W3",
- "/wd4005", # macro-redefinition
- "/wd4068", # unknown pragma
- "/wd4180", # qualifier applied to function type has no meaning; ignored
- "/wd4244", # conversion from 'type1' to 'type2', possible loss of data
- "/wd4267", # conversion from 'size_t' to 'type', possible loss of data
- "/wd4800", # forcing value to bool 'true' or 'false' (performance warning)
- "/DNOMINMAX", # Don't define min and max macros (windows.h)
- "/DWIN32_LEAN_AND_MEAN", # Don't bloat namespace with incompatible winsock versions.
- "/D_CRT_SECURE_NO_WARNINGS", # Don't warn about usage of insecure C functions
-]
-
-MSVC_TEST_FLAGS = [
- "/wd4018", # signed/unsigned mismatch
- "/wd4101", # unreferenced local variable
- "/wd4503", # decorated name length exceeded, name was truncated
-]
-
-# /Wall with msvc includes unhelpful warnings such as C4711, C4710, ...
-ABSL_DEFAULT_COPTS = select({
- "//absl:windows": MSVC_FLAGS,
- "//absl:llvm_compiler": LLVM_FLAGS,
- "//conditions:default": 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": MSVC_TEST_FLAGS,
- "//absl:llvm_compiler": LLVM_TEST_FLAGS,
- "//conditions:default": GCC_TEST_FLAGS,
-})
-
-ABSL_EXCEPTIONS_FLAG = select({
- "//absl:windows": ["/U_HAS_EXCEPTIONS", "/D_HAS_EXCEPTIONS=1", "/EHsc"],
- "//conditions:default": ["-fexceptions"],
-})
-
-ABSL_EXCEPTIONS_FLAG_LINKOPTS = select({
- "//conditions:default": [],
-})
diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake
new file mode 100644
index 0000000..5084958
--- /dev/null
+++ b/absl/copts/AbseilConfigureCopts.cmake
@@ -0,0 +1,52 @@
+# See absl/copts/copts.py and absl/copts/generate_copts.py
+include(GENERATED_AbseilCopts)
+
+set(ABSL_LSAN_LINKOPTS "")
+set(ABSL_HAVE_LSAN OFF)
+
+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}")
+ set(ABSL_EXCEPTIONS_FLAG "${ABSL_GCC_EXCEPTIONS_FLAGS}")
+elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "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_EXCEPTIONS_FLAG "${ABSL_CLANG_CL_EXCEPTIONS_FLAGS}")
+ else()
+ set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}")
+ set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}")
+ set(ABSL_EXCEPTIONS_FLAG "${ABSL_LLVM_EXCEPTIONS_FLAGS}")
+ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ # AppleClang doesn't have lsan
+ # https://developer.apple.com/documentation/code_diagnostics
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 3.5)
+ set(ABSL_LSAN_LINKOPTS "-fsanitize=leak")
+ set(ABSL_HAVE_LSAN ON)
+ endif()
+ endif()
+ endif()
+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_EXCEPTIONS_FLAG "${ABSL_MSVC_EXCEPTIONS_FLAGS}")
+else()
+ message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER}. Building with no default flags")
+ set(ABSL_DEFAULT_COPTS "")
+ set(ABSL_TEST_COPTS "")
+ set(ABSL_EXCEPTIONS_FLAG "")
+endif()
+
+# This flag is used internally for Bazel builds and is kept here for consistency
+set(ABSL_EXCEPTIONS_FLAG_LINKOPTS "")
+
+if("${CMAKE_CXX_STANDARD}" EQUAL 98)
+ message(FATAL_ERROR "Abseil requires at least C++11")
+elseif(NOT "${CMAKE_CXX_STANDARD}")
+ message(STATUS "No CMAKE_CXX_STANDARD set, assuming 11")
+ set(ABSL_CXX_STANDARD 11)
+else()
+ set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}")
+endif()
diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake
new file mode 100644
index 0000000..9031bfa
--- /dev/null
+++ b/absl/copts/GENERATED_AbseilCopts.cmake
@@ -0,0 +1,208 @@
+# GENERATED! DO NOT MANUALLY EDIT THIS FILE.
+#
+# (1) Edit absl/copts/copts.py.
+# (2) Run `python <path_to_absl>/copts/generate_copts.py`.
+
+list(APPEND ABSL_CLANG_CL_EXCEPTIONS_FLAGS
+ "/U_HAS_EXCEPTIONS"
+ "/D_HAS_EXCEPTIONS=1"
+ "/EHsc"
+)
+
+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-nested-anon-types"
+ "-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-undef"
+ "-Wno-unknown-warning-option"
+ "-Wno-unreachable-code"
+ "-Wno-unused-macros"
+ "-Wno-weak-vtables"
+ "-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"
+ "/D_SCL_SECURE_NO_WARNINGS"
+ "/D_ENABLE_EXTENDED_ALIGNED_STORAGE"
+)
+
+list(APPEND ABSL_CLANG_CL_TEST_FLAGS
+ "-Wno-c99-extensions"
+ "-Wno-missing-noreturn"
+ "-Wno-missing-prototypes"
+ "-Wno-missing-variable-declarations"
+ "-Wno-null-conversion"
+ "-Wno-shadow"
+ "-Wno-shift-sign-overflow"
+ "-Wno-sign-compare"
+ "-Wno-unused-function"
+ "-Wno-unused-member-function"
+ "-Wno-unused-parameter"
+ "-Wno-unused-private-field"
+ "-Wno-unused-template"
+ "-Wno-used-but-marked-unused"
+ "-Wno-zero-as-null-pointer-constant"
+ "-Wno-gnu-zero-variadic-macro-arguments"
+)
+
+list(APPEND ABSL_GCC_EXCEPTIONS_FLAGS
+ "-fexceptions"
+)
+
+list(APPEND ABSL_GCC_FLAGS
+ "-Wall"
+ "-Wextra"
+ "-Wcast-qual"
+ "-Wconversion-null"
+ "-Wmissing-declarations"
+ "-Woverlength-strings"
+ "-Wpointer-arith"
+ "-Wunused-local-typedefs"
+ "-Wunused-result"
+ "-Wvarargs"
+ "-Wvla"
+ "-Wwrite-strings"
+ "-Wno-missing-field-initializers"
+ "-Wno-sign-compare"
+)
+
+list(APPEND ABSL_GCC_TEST_FLAGS
+ "-Wno-conversion-null"
+ "-Wno-missing-declarations"
+ "-Wno-sign-compare"
+ "-Wno-unused-function"
+ "-Wno-unused-parameter"
+ "-Wno-unused-private-field"
+)
+
+list(APPEND ABSL_LLVM_EXCEPTIONS_FLAGS
+ "-fexceptions"
+)
+
+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"
+ "-Wno-float-conversion"
+ "-Wno-float-equal"
+ "-Wno-format-nonliteral"
+ "-Wno-gcc-compat"
+ "-Wno-global-constructors"
+ "-Wno-exit-time-destructors"
+ "-Wno-nested-anon-types"
+ "-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-undef"
+ "-Wno-unknown-warning-option"
+ "-Wno-unreachable-code"
+ "-Wno-unused-macros"
+ "-Wno-weak-vtables"
+ "-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"
+)
+
+list(APPEND ABSL_LLVM_TEST_FLAGS
+ "-Wno-c99-extensions"
+ "-Wno-missing-noreturn"
+ "-Wno-missing-prototypes"
+ "-Wno-missing-variable-declarations"
+ "-Wno-null-conversion"
+ "-Wno-shadow"
+ "-Wno-shift-sign-overflow"
+ "-Wno-sign-compare"
+ "-Wno-unused-function"