diff options
Diffstat (limited to 'absl')
557 files changed, 35126 insertions, 6548 deletions
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index 853330d4..5a03acf8 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -14,12 +14,15 @@ # limitations under the License. # +load( + ":compiler_config_setting.bzl", + "create_llvm_config", +) + package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 -load(":compiler_config_setting.bzl", "create_llvm_config") - create_llvm_config( name = "llvm_compiler", visibility = [":__subpackages__"], diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt index 3e78397c..fbfa7822 100644 --- a/absl/CMakeLists.txt +++ b/absl/CMakeLists.txt @@ -14,20 +14,24 @@ # limitations under the License. # - - add_subdirectory(base) add_subdirectory(algorithm) add_subdirectory(container) add_subdirectory(debugging) add_subdirectory(flags) +add_subdirectory(functional) add_subdirectory(hash) add_subdirectory(memory) add_subdirectory(meta) add_subdirectory(numeric) add_subdirectory(random) +add_subdirectory(status) add_subdirectory(strings) add_subdirectory(synchronization) add_subdirectory(time) add_subdirectory(types) add_subdirectory(utility) + +if (${ABSL_BUILD_DLL}) + absl_make_dll() +endif() diff --git a/absl/abseil.podspec.gen.py b/absl/abseil.podspec.gen.py new file mode 100755 index 00000000..6aefb794 --- /dev/null +++ b/absl/abseil.podspec.gen.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""This script generates abseil.podspec from all BUILD.bazel files. + +This is expected to run on abseil git repository with Bazel 1.0 on Linux. +It recursively analyzes BUILD.bazel files using query command of Bazel to +dump its build rules in XML format. From these rules, it constructs podspec +structure. +""" + +import argparse +import collections +import os +import re +import subprocess +import xml.etree.ElementTree + +# Template of root podspec. +SPEC_TEMPLATE = """ +# This file has been automatically generated from a script. +# Please make modifications to `abseil.podspec.gen.py` instead. +Pod::Spec.new do |s| + s.name = 'abseil' + s.version = '${version}' + s.summary = 'Abseil Common Libraries (C++) from Google' + s.homepage = 'https://abseil.io' + s.license = 'Apache License, Version 2.0' + s.authors = { 'Abseil Team' => 'abseil-io@googlegroups.com' } + s.source = { + :git => 'https://github.com/abseil/abseil-cpp.git', + :tag => '${tag}', + } + s.module_name = 'absl' + s.header_mappings_dir = 'absl' + s.header_dir = 'absl' + s.libraries = 'c++' + s.compiler_flags = '-Wno-everything' + s.pod_target_xcconfig = { + 'USER_HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)"', + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } + s.ios.deployment_target = '7.0' + s.osx.deployment_target = '10.9' + s.tvos.deployment_target = '9.0' + s.watchos.deployment_target = '2.0' +""" + +# Rule object representing the rule of Bazel BUILD. +Rule = collections.namedtuple( + "Rule", "type name package srcs hdrs textual_hdrs deps visibility testonly") + + +def get_elem_value(elem, name): + """Returns the value of XML element with the given name.""" + for child in elem: + if child.attrib.get("name") != name: + continue + if child.tag == "string": + return child.attrib.get("value") + if child.tag == "boolean": + return child.attrib.get("value") == "true" + if child.tag == "list": + return [nested_child.attrib.get("value") for nested_child in child] + raise "Cannot recognize tag: " + child.tag + return None + + +def normalize_paths(paths): + """Returns the list of normalized path.""" + # e.g. ["//absl/strings:dir/header.h"] -> ["absl/strings/dir/header.h"] + return [path.lstrip("/").replace(":", "/") for path in paths] + + +def parse_rule(elem, package): + """Returns a rule from bazel XML rule.""" + return Rule( + type=elem.attrib["class"], + name=get_elem_value(elem, "name"), + package=package, + srcs=normalize_paths(get_elem_value(elem, "srcs") or []), + hdrs=normalize_paths(get_elem_value(elem, "hdrs") or []), + textual_hdrs=normalize_paths(get_elem_value(elem, "textual_hdrs") or []), + deps=get_elem_value(elem, "deps") or [], + visibility=get_elem_value(elem, "visibility") or [], + testonly=get_elem_value(elem, "testonly") or False) + + +def read_build(package): + """Runs bazel query on given package file and returns all cc rules.""" + result = subprocess.check_output( + ["bazel", "query", package + ":all", "--output", "xml"]) + root = xml.etree.ElementTree.fromstring(result) + return [ + parse_rule(elem, package) + for elem in root + if elem.tag == "rule" and elem.attrib["class"].startswith("cc_") + ] + + +def collect_rules(root_path): + """Collects and returns all rules from root path recursively.""" + rules = [] + for cur, _, _ in os.walk(root_path): + build_path = os.path.join(cur, "BUILD.bazel") + if os.path.exists(build_path): + rules.extend(read_build("//" + cur)) + return rules + + +def relevant_rule(rule): + """Returns true if a given rule is relevant when generating a podspec.""" + return ( + # cc_library only (ignore cc_test, cc_binary) + rule.type == "cc_library" and + # ignore empty rule + (rule.hdrs + rule.textual_hdrs + rule.srcs) and + # ignore test-only rule + not rule.testonly) + + +def get_spec_var(depth): + """Returns the name of variable for spec with given depth.""" + return "s" if depth == 0 else "s{}".format(depth) + + +def get_spec_name(label): + """Converts the label of bazel rule to the name of podspec.""" + assert label.startswith("//absl/"), "{} doesn't start with //absl/".format( + label) + # e.g. //absl/apple/banana -> abseil/apple/banana + return "abseil/" + label[7:] + + +def write_podspec(f, rules, args): + """Writes a podspec from given rules and args.""" + rule_dir = build_rule_directory(rules)["abseil"] + # Write root part with given arguments + spec = re.sub(r"\$\{(\w+)\}", lambda x: args[x.group(1)], + SPEC_TEMPLATE).lstrip() + f.write(spec) + # Write all target rules + write_podspec_map(f, rule_dir, 0) + f.write("end\n") + + +def build_rule_directory(rules): + """Builds a tree-style rule directory from given rules.""" + rule_dir = {} + for rule in rules: + cur = rule_dir + for frag in get_spec_name(rule.package).split("/"): + cur = cur.setdefault(frag, {}) + cur[rule.name] = rule + return rule_dir + + +def write_podspec_map(f, cur_map, depth): + """Writes podspec from rule map recursively.""" + for key, value in sorted(cur_map.items()): + indent = " " * (depth + 1) + f.write("{indent}{var0}.subspec '{key}' do |{var1}|\n".format( + indent=indent, + key=key, + var0=get_spec_var(depth), + var1=get_spec_var(depth + 1))) + if isinstance(value, dict): + write_podspec_map(f, value, depth + 1) + else: + write_podspec_rule(f, value, depth + 1) + f.write("{indent}end\n".format(indent=indent)) + + +def write_podspec_rule(f, rule, depth): + """Writes podspec from given rule.""" + indent = " " * (depth + 1) + spec_var = get_spec_var(depth) + # Puts all files in hdrs, textual_hdrs, and srcs into source_files. + # Since CocoaPods treats header_files a bit differently from bazel, + # this won't generate a header_files field so that all source_files + # are considered as header files. + srcs = sorted(set(rule.hdrs + rule.textual_hdrs + rule.srcs)) + write_indented_list( + f, "{indent}{var}.source_files = ".format(indent=indent, var=spec_var), + srcs) + # Writes dependencies of this rule. + for dep in sorted(rule.deps): + name = get_spec_name(dep.replace(":", "/")) + f.write("{indent}{var}.dependency '{dep}'\n".format( + indent=indent, var=spec_var, dep=name)) + + +def write_indented_list(f, leading, values): + """Writes leading values in an indented style.""" + f.write(leading) + f.write((",\n" + " " * len(leading)).join("'{}'".format(v) for v in values)) + f.write("\n") + + +def generate(args): + """Generates a podspec file from all BUILD files under absl directory.""" + rules = filter(relevant_rule, collect_rules("absl")) + with open(args.output, "wt") as f: + write_podspec(f, rules, vars(args)) + + +def main(): + parser = argparse.ArgumentParser( + description="Generates abseil.podspec from BUILD.bazel") + parser.add_argument( + "-v", "--version", help="The version of podspec", required=True) + parser.add_argument( + "-t", + "--tag", + default=None, + help="The name of git tag (default: version)") + parser.add_argument( + "-o", + "--output", + default="abseil.podspec", + help="The name of output file (default: abseil.podspec)") + args = parser.parse_args() + if args.tag is None: + args.tag = args.version + generate(args) + + +if __name__ == "__main__": + main() diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel index c506f3b9..6a96420b 100644 --- a/absl/algorithm/BUILD.bazel +++ b/absl/algorithm/BUILD.bazel @@ -14,6 +14,7 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -30,6 +31,7 @@ cc_library( hdrs = ["algorithm.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + deps = ["//absl/base:config"], ) cc_test( diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index 9fbe36f6..56cd0fb8 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt @@ -21,6 +21,8 @@ absl_cc_library( "algorithm.h" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::config PUBLIC ) diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h index 7c2b787e..e9b47338 100644 --- a/absl/algorithm/algorithm.h +++ b/absl/algorithm/algorithm.h @@ -26,8 +26,10 @@ #include <iterator> #include <type_traits> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace algorithm_internal { @@ -85,6 +87,8 @@ It RotateImpl(It first, It middle, It last, std::false_type) { } // namespace algorithm_internal +// equal() +// // Compares the equality of two ranges specified by pairs of iterators, using // the given predicate, returning true iff for each corresponding iterator i1 // and i2 in the first and second range respectively, pred(*i1, *i2) == true @@ -105,8 +109,8 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, typename std::iterator_traits<InputIter2>::iterator_category{}); } -// Performs comparison of two ranges specified by pairs of iterators using -// operator==. +// Overload of equal() that performs comparison of two ranges specified by pairs +// of iterators using operator==. template <typename InputIter1, typename InputIter2> bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2) { @@ -114,6 +118,8 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, algorithm_internal::EqualTo{}); } +// linear_search() +// // Performs a linear search for `value` using the iterator `first` up to // but not including `last`, returning true if [`first`, `last`) contains an // element equal to `value`. @@ -127,6 +133,8 @@ bool linear_search(InputIterator first, InputIterator last, return std::find(first, last, value) != last; } +// rotate() +// // Performs a left rotation on a range of elements (`first`, `last`) such that // `middle` is now the first element. `rotate()` returns an iterator pointing to // the first element before rotation. This function is exactly the same as @@ -136,7 +144,6 @@ bool linear_search(InputIterator first, InputIterator last, // The complexity of this algorithm is the same as that of `std::rotate`, but if // `ForwardIterator` is not a random-access iterator, then `absl::rotate` // performs an additional pass over the range to construct the return value. - template <typename ForwardIterator> ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last) { @@ -146,7 +153,7 @@ ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator>()); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index 16389be7..d72532de 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -55,7 +55,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_algorithm_internal { // NOTE: it is important to defer to ADL lookup for building with C++ modules, @@ -113,6 +113,18 @@ template <class Key, class Hash, class KeyEqual, class Allocator> struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>> : std::true_type {}; +// container_algorithm_internal::c_size. It is meant for internal use only. + +template <class C> +auto c_size(C& c) -> decltype(c.size()) { + return c.size(); +} + +template <class T, std::size_t N> +constexpr std::size_t c_size(T (&)[N]) { + return N; +} + } // namespace container_algorithm_internal // PUBLIC API @@ -257,7 +269,8 @@ container_algorithm_internal::ContainerIter<Sequence1> c_find_end( // c_find_first_of() // // Container-based version of the <algorithm> `std::find_first_of()` function to -// find the first elements in an ordered set within a container. +// find the first element within the container that is also within the options +// container. template <typename C1, typename C2> container_algorithm_internal::ContainerIter<C1> c_find_first_of(C1& container, C2& options) { @@ -366,7 +379,8 @@ c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) { template <typename C1, typename C2> bool c_equal(const C1& c1, const C2& c2) { - return ((c1.size() == c2.size()) && + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && std::equal(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2))); @@ -376,7 +390,8 @@ bool c_equal(const C1& c1, const C2& c2) { // the function's test condition. template <typename C1, typename C2, typename BinaryPredicate> bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) { - return ((c1.size() == c2.size()) && + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && std::equal(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1706,7 +1721,7 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, output_first, std::forward<BinaryOp>(op)); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index 86bf9d3e..0a4abe94 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc @@ -163,23 +163,29 @@ TEST_F(NonMutatingTest, MismatchWithPredicate) { TEST_F(NonMutatingTest, Equal) { EXPECT_TRUE(absl::c_equal(vector_, sequence_)); EXPECT_TRUE(absl::c_equal(sequence_, vector_)); + EXPECT_TRUE(absl::c_equal(sequence_, array_)); + EXPECT_TRUE(absl::c_equal(array_, vector_)); // Test that behavior appropriately differs from that of equal(). std::vector<int> vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(absl::c_equal(vector_plus, sequence_)); EXPECT_FALSE(absl::c_equal(sequence_, vector_plus)); + EXPECT_FALSE(absl::c_equal(array_, vector_plus)); } TEST_F(NonMutatingTest, EqualWithPredicate) { EXPECT_TRUE(absl::c_equal(vector_, sequence_, Equals)); EXPECT_TRUE(absl::c_equal(sequence_, vector_, Equals)); + EXPECT_TRUE(absl::c_equal(array_, sequence_, Equals)); + EXPECT_TRUE(absl::c_equal(vector_, array_, Equals)); // Test that behavior appropriately differs from that of equal(). std::vector<int> vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(absl::c_equal(vector_plus, sequence_, Equals)); EXPECT_FALSE(absl::c_equal(sequence_, vector_plus, Equals)); + EXPECT_FALSE(absl::c_equal(vector_plus, array_, Equals)); } TEST_F(NonMutatingTest, IsPermutation) { diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index a512272a..bae79427 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -14,12 +14,11 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", "ABSL_DEFAULT_LINKOPTS", - "ABSL_EXCEPTIONS_FLAG", - "ABSL_EXCEPTIONS_FLAG_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -35,6 +34,21 @@ cc_library( visibility = [ "//absl:__subpackages__", ], + deps = [ + ":config", + ":core_headers", + ], +) + +cc_library( + name = "errno_saver", + hdrs = ["internal/errno_saver.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [":config"], ) cc_library( @@ -43,16 +57,27 @@ cc_library( hdrs = ["log_severity.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - deps = [":core_headers"], + deps = [ + ":config", + ":core_headers", + ], ) cc_library( name = "raw_logging_internal", + srcs = ["internal/raw_logging.cc"], + hdrs = ["internal/raw_logging.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], + deps = [ + ":atomic_hook", + ":config", + ":core_headers", + ":log_severity", + ], ) cc_library( @@ -64,22 +89,24 @@ cc_library( "internal/spinlock_wait.cc", "internal/spinlock_win32.inc", ], - hdrs = [ - "internal/scheduling_mode.h", - "internal/spinlock_wait.h", - ], + hdrs = ["internal/spinlock_wait.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/base:__pkg__", ], - deps = [":core_headers"], + deps = [ + ":base_internal", + ":core_headers", + ":errno_saver", + ], ) cc_library( name = "config", hdrs = [ "config.h", + "options.h", "policy_checks.h", ], copts = ABSL_DEFAULT_COPTS, @@ -130,14 +157,15 @@ cc_library( "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, visibility = [ - "//absl:__subpackages__", + "//visibility:public", ], deps = [ ":base", + ":base_internal", ":config", ":core_headers", ":dynamic_annotations", - ":spinlock_wait", + ":raw_logging_internal", ], ) @@ -156,6 +184,7 @@ cc_library( "//absl:__subpackages__", ], deps = [ + ":config", "//absl/meta:type_traits", ], ) @@ -164,7 +193,6 @@ cc_library( name = "base", srcs = [ "internal/cycleclock.cc", - "internal/raw_logging.cc", "internal/spinlock.cc", "internal/sysinfo.cc", "internal/thread_identity.cc", @@ -176,7 +204,6 @@ cc_library( "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", @@ -185,7 +212,9 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = select({ - "//absl:windows": [], + "//absl:windows": [ + "-DEFAULTLIB:advapi32.lib", + ], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, deps = [ @@ -195,11 +224,25 @@ cc_library( ":core_headers", ":dynamic_annotations", ":log_severity", + ":raw_logging_internal", ":spinlock_wait", "//absl/meta:type_traits", ], ) +cc_library( + name = "atomic_hook_test_helper", + testonly = 1, + srcs = ["internal/atomic_hook_test_helper.cc"], + hdrs = ["internal/atomic_hook_test_helper.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":atomic_hook", + ":core_headers", + ], +) + cc_test( name = "atomic_hook_test", size = "small", @@ -208,6 +251,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":atomic_hook", + ":atomic_hook_test_helper", ":core_headers", "@com_google_googletest//:gtest_main", ], @@ -232,28 +276,41 @@ cc_library( name = "throw_delegate", srcs = ["internal/throw_delegate.cc"], hdrs = ["internal/throw_delegate.h"], - copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], deps = [ - ":base", ":config", + ":raw_logging_internal", ], ) cc_test( name = "throw_delegate_test", srcs = ["throw_delegate_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":config", ":throw_delegate", "@com_google_googletest//:gtest_main", ], ) +cc_test( + name = "errno_saver_test", + size = "small", + srcs = ["internal/errno_saver_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":errno_saver", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "exception_testing", testonly = 1, @@ -281,8 +338,8 @@ cc_library( testonly = 1, srcs = ["internal/exception_safety_testing.cc"], hdrs = ["internal/exception_safety_testing.h"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", ":pretty_function", @@ -297,8 +354,8 @@ cc_library( cc_test( name = "exception_safety_testing_test", srcs = ["exception_safety_testing_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":exception_safety_testing", "//absl/memory", @@ -347,8 +404,8 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", + ":base_internal", ":core_headers", - ":spinlock_wait", "//absl/synchronization", "@com_google_googletest//:gtest", ], @@ -363,8 +420,8 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", + ":base_internal", ":core_headers", - ":spinlock_wait", "//absl/synchronization", "@com_google_googletest//:gtest_main", ], @@ -382,6 +439,7 @@ cc_library( deps = [ ":base", ":base_internal", + ":raw_logging_internal", "//absl/synchronization", "@com_github_google_benchmark//:benchmark_main", ], @@ -455,7 +513,7 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":base", + ":raw_logging_internal", "//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -519,7 +577,10 @@ cc_library( visibility = [ "//absl:__subpackages__", ], - deps = [":core_headers"], + deps = [ + ":config", + ":core_headers", + ], ) cc_test( @@ -535,6 +596,75 @@ cc_test( ) cc_library( + name = "exponential_biased", + srcs = ["internal/exponential_biased.cc"], + hdrs = ["internal/exponential_biased.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":config", + ":core_headers", + ], +) + +cc_test( + name = "exponential_biased_test", + size = "small", + srcs = ["internal/exponential_biased_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":exponential_biased", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "periodic_sampler", + srcs = ["internal/periodic_sampler.cc"], + hdrs = ["internal/periodic_sampler.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":core_headers", + ":exponential_biased", + ], +) + +cc_test( + name = "periodic_sampler_test", + size = "small", + srcs = ["internal/periodic_sampler_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":core_headers", + ":periodic_sampler", + "@com_google_googletest//:gtest_main", + ], +) + +cc_binary( + name = "periodic_sampler_benchmark", + testonly = 1, + srcs = ["internal/periodic_sampler_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":core_headers", + ":periodic_sampler", + "@com_github_google_benchmark//:benchmark_main", + ], +) + +cc_library( name = "scoped_set_env", testonly = 1, srcs = ["internal/scoped_set_env.cc"], @@ -543,7 +673,10 @@ cc_library( visibility = [ "//absl:__subpackages__", ], - deps = [":base"], + deps = [ + ":config", + ":raw_logging_internal", + ], ) cc_test( @@ -565,8 +698,10 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":base", ":log_severity", + "//absl/flags:flag_internal", + "//absl/flags:marshalling", + "//absl/strings", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index cc7960e3..14c52eab 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -14,11 +14,27 @@ # limitations under the License. # +find_library(LIBRT rt) + absl_cc_library( NAME atomic_hook HDRS "internal/atomic_hook.h" + DEPS + absl::config + absl::core_headers + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME + errno_saver + HDRS + "internal/errno_saver.h" + DEPS + absl::config COPTS ${ABSL_DEFAULT_COPTS} ) @@ -39,6 +55,15 @@ absl_cc_library( absl_cc_library( NAME raw_logging_internal + HDRS + "internal/raw_logging.h" + SRCS + "internal/raw_logging.cc" + DEPS + absl::atomic_hook + absl::config + absl::core_headers + absl::log_severity COPTS ${ABSL_DEFAULT_COPTS} ) @@ -47,7 +72,6 @@ absl_cc_library( NAME spinlock_wait HDRS - "internal/scheduling_mode.h" "internal/spinlock_wait.h" SRCS "internal/spinlock_akaros.inc" @@ -58,7 +82,9 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base_internal absl::core_headers + absl::errno_saver ) absl_cc_library( @@ -66,6 +92,7 @@ absl_cc_library( config HDRS "config.h" + "options.h" "policy_checks.h" COPTS ${ABSL_DEFAULT_COPTS} @@ -116,10 +143,11 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base + absl::base_internal absl::config absl::core_headers absl::dynamic_annotations - absl::spinlock_wait + absl::raw_logging_internal Threads::Threads ) @@ -131,9 +159,11 @@ absl_cc_library( "internal/identity.h" "internal/inline_variable.h" "internal/invoke.h" + "internal/scheduling_mode.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::type_traits ) @@ -146,23 +176,23 @@ absl_cc_library( "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" - "log_severity.cc" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + $<$<BOOL:${LIBRT}>:${LIBRT}> + $<$<BOOL:${MINGW}>:"advapi32"> DEPS absl::atomic_hook absl::base_internal @@ -170,6 +200,7 @@ absl_cc_library( absl::core_headers absl::dynamic_annotations absl::log_severity + absl::raw_logging_internal absl::spinlock_wait absl::type_traits Threads::Threads @@ -185,9 +216,9 @@ absl_cc_library( "internal/throw_delegate.cc" COPTS ${ABSL_DEFAULT_COPTS} - ${ABSL_EXCEPTIONS_FLAG} DEPS - absl::base + absl::config + absl::raw_logging_internal ) absl_cc_library( @@ -221,9 +252,6 @@ absl_cc_library( "internal/exception_safety_testing.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::config absl::pretty_function @@ -242,15 +270,25 @@ absl_cc_test( "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_library( + NAME + atomic_hook_test_helper + SRCS + "internal/atomic_hook_test_helper.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::atomic_hook + absl::core_headers + TESTONLY +) + absl_cc_test( NAME atomic_hook_test @@ -259,8 +297,10 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::atomic_hook_test_helper absl::atomic_hook absl::core_headers + gmock gtest_main ) @@ -279,6 +319,19 @@ absl_cc_test( absl_cc_test( NAME + errno_saver_test + SRCS + "internal/errno_saver_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::errno_saver + gmock + gtest_main +) + +absl_cc_test( + NAME throw_delegate_test SRCS "throw_delegate_test.cc" @@ -286,6 +339,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::base + absl::config absl::throw_delegate gtest_main ) @@ -329,8 +383,8 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::base + absl::base_internal absl::core_headers - absl::spinlock_wait absl::synchronization gtest TESTONLY @@ -346,8 +400,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::base + absl::base_internal absl::core_headers - absl::spinlock_wait absl::synchronization gtest_main ) @@ -415,7 +469,7 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS - absl::base + absl::raw_logging_internal absl::strings gtest_main ) @@ -468,6 +522,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::core_headers ) @@ -485,6 +540,60 @@ absl_cc_test( absl_cc_library( NAME + exponential_biased + SRCS + "internal/exponential_biased.cc" + HDRS + "internal/exponential_biased.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers +) + +absl_cc_test( + NAME + exponential_biased_test + SRCS + "internal/exponential_biased_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::exponential_biased + absl::strings + gmock_main +) + +absl_cc_library( + NAME + periodic_sampler + SRCS + "internal/periodic_sampler.cc" + HDRS + "internal/periodic_sampler.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::exponential_biased +) + +absl_cc_test( + NAME + periodic_sampler_test + SRCS + "internal/periodic_sampler_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::core_headers + absl::periodic_sampler + gmock_main +) + +absl_cc_library( + NAME scoped_set_env SRCS "internal/scoped_set_env.cc" @@ -493,7 +602,8 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS - absl::base + absl::config + absl::raw_logging_internal ) absl_cc_test( @@ -525,8 +635,10 @@ absl_cc_test( SRCS "log_severity_test.cc" DEPS - absl::base + absl::flags_internal + absl::flags_marshalling absl::log_severity + absl::strings gmock gtest_main ) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index a7da62af..ff138629 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -157,10 +157,12 @@ // Tags a function as weak for the purposes of compilation and linking. // Weak attributes currently do not work properly in LLVM's Windows backend, // so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 -// for futher information. -#if (ABSL_HAVE_ATTRIBUTE(weak) || \ +// for further information. +// The MinGW compiler doesn't complain about the weak attribute until the link +// step, presumably because Windows doesn't use ELF binaries. +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ (defined(__GNUC__) && !defined(__clang__))) && \ - !(defined(__llvm__) && defined(_WIN32)) + !(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) #define ABSL_HAVE_ATTRIBUTE_WEAK 1 @@ -561,7 +563,19 @@ // ABSL_ATTRIBUTE_PACKED // -// Prevents the compiler from padding a structure to natural alignment +// Instructs the compiler not to use natural alignment for a tagged data +// structure, but instead to reduce its alignment to 1. This attribute can +// either be applied to members of a structure or to a structure in its +// entirety. Applying this attribute (judiciously) to a structure in its +// entirety to optimize the memory footprint of very commonly-used structs is +// fine. Do not apply this attribute to a structure in its entirety if the +// purpose is to control the offsets of the members in the structure. Instead, +// apply this attribute only to structure members that need it. +// +// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the +// natural alignment of structure members not annotated is preserved. Aligned +// member accesses are faster than non-aligned member accesses even if the +// targeted microprocessor supports non-aligned accesses. #if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__)) #define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__)) #else @@ -599,7 +613,6 @@ // // Note that this attribute is redundant if the variable is declared constexpr. #if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) -// NOLINTNEXTLINE(whitespace/braces) #define ABSL_CONST_INIT [[clang::require_constant_initialization]] #else #define ABSL_CONST_INIT diff --git a/absl/base/bit_cast_test.cc b/absl/base/bit_cast_test.cc index be201856..8a3a41ea 100644 --- a/absl/base/bit_cast_test.cc +++ b/absl/base/bit_cast_test.cc @@ -22,7 +22,7 @@ #include "absl/base/macros.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { template <int N> @@ -105,5 +105,5 @@ TEST(BitCast, Double) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/call_once.h b/absl/base/call_once.h index 373f015f..bc5ec937 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h @@ -41,7 +41,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN class once_flag; @@ -143,12 +143,13 @@ enum { }; template <typename Callable, typename... Args> +ABSL_ATTRIBUTE_NOINLINE void CallOnceImpl(std::atomic<uint32_t>* control, base_internal::SchedulingMode scheduling_mode, Callable&& fn, Args&&... args) { #ifndef NDEBUG { - uint32_t old_control = control->load(std::memory_order_acquire); + uint32_t old_control = control->load(std::memory_order_relaxed); if (old_control != kOnceInit && old_control != kOnceRunning && old_control != kOnceWaiter && @@ -166,14 +167,23 @@ void CallOnceImpl(std::atomic<uint32_t>* control, // Must do this before potentially modifying control word's state. base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode); // Short circuit the simplest case to avoid procedure call overhead. + // The base_internal::SpinLockWait() call returns either kOnceInit or + // kOnceDone. If it returns kOnceDone, it must have loaded the control word + // with std::memory_order_acquire and seen a value of kOnceDone. uint32_t old_control = kOnceInit; if (control->compare_exchange_strong(old_control, kOnceRunning, - std::memory_order_acquire, std::memory_order_relaxed) || base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans, scheduling_mode) == kOnceInit) { base_internal::Invoke(std::forward<Callable>(fn), std::forward<Args>(args)...); + // The call to SpinLockWake below is an optimization, because the waiter + // in SpinLockWait is waiting with a short timeout. The atomic load/store + // sequence is slightly faster than an atomic exchange: + // old_control = control->exchange(base_internal::kOnceDone, + // std::memory_order_release); + // We opt for a slightly faster case when there are no waiters, in spite + // of longer tail latency when there are waiters. old_control = control->load(std::memory_order_relaxed); control->store(base_internal::kOnceDone, std::memory_order_release); if (old_control == base_internal::kOnceWaiter) { @@ -210,7 +220,7 @@ void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { } } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_CALL_ONCE_H_ diff --git a/absl/base/call_once_test.cc b/absl/base/call_once_test.cc index 0e89bd24..11d26c44 100644 --- a/absl/base/call_once_test.cc +++ b/absl/base/call_once_test.cc @@ -24,18 +24,18 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { absl::once_flag once; 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; -int call_once_finished_count GUARDED_BY(counters_mu) = 0; -int call_once_return_count GUARDED_BY(counters_mu) = 0; -bool done_blocking GUARDED_BY(counters_mu) = false; +int running_thread_count ABSL_GUARDED_BY(counters_mu) = 0; +int call_once_invoke_count ABSL_GUARDED_BY(counters_mu) = 0; +int call_once_finished_count ABSL_GUARDED_BY(counters_mu) = 0; +int call_once_return_count ABSL_GUARDED_BY(counters_mu) = 0; +bool done_blocking ABSL_GUARDED_BY(counters_mu) = false; // Function to be called from absl::call_once. Waits for a notification. void WaitAndIncrement() { @@ -61,7 +61,7 @@ void ThreadBody() { } // Returns true if all threads are set up for the test. -bool ThreadsAreSetup(void*) EXCLUSIVE_LOCKS_REQUIRED(counters_mu) { +bool ThreadsAreSetup(void*) ABSL_EXCLUSIVE_LOCKS_REQUIRED(counters_mu) { // All ten threads must be running, and WaitAndIncrement should be blocked. return running_thread_count == 10 && call_once_invoke_count == 1; } @@ -103,5 +103,5 @@ TEST(CallOnceTest, ExecutionCount) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/casts.h b/absl/base/casts.h index b67b047c..322cc1d2 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -34,7 +34,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace internal_casts { @@ -178,7 +178,7 @@ inline Dest bit_cast(const Source& source) { return dest; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_CASTS_H_ diff --git a/absl/base/config.h b/absl/base/config.h index 1c3cb08e..ee99f946 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -63,8 +63,72 @@ #include <TargetConditionals.h> #endif +#include "absl/base/options.h" #include "absl/base/policy_checks.h" +// Helper macro to convert a CPP variable to a string literal. +#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x +#define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) + +// ----------------------------------------------------------------------------- +// Abseil namespace annotations +// ----------------------------------------------------------------------------- + +// ABSL_NAMESPACE_BEGIN/ABSL_NAMESPACE_END +// +// An annotation placed at the beginning/end of each `namespace absl` scope. +// This is used to inject an inline namespace. +// +// The proper way to write Abseil code in the `absl` namespace is: +// +// namespace absl { +// ABSL_NAMESPACE_BEGIN +// +// void Foo(); // absl::Foo(). +// +// ABSL_NAMESPACE_END +// } // namespace absl +// +// Users of Abseil should not use these macros, because users of Abseil should +// not write `namespace absl {` in their own code for any reason. (Abseil does +// not support forward declarations of its own types, nor does it support +// user-provided specialization of Abseil templates. Code that violates these +// rules may be broken without warning.) +#if !defined(ABSL_OPTION_USE_INLINE_NAMESPACE) || \ + !defined(ABSL_OPTION_INLINE_NAMESPACE_NAME) +#error options.h is misconfigured. +#endif + +// Check that ABSL_OPTION_INLINE_NAMESPACE_NAME is neither "head" nor "" +#if defined(__cplusplus) && ABSL_OPTION_USE_INLINE_NAMESPACE == 1 + +#define ABSL_INTERNAL_INLINE_NAMESPACE_STR \ + ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) + +static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != '\0', + "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " + "not be empty."); +static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[1] != 'e' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[2] != 'a' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[3] != 'd' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[4] != '\0', + "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " + "be changed to a new, unique identifier name."); + +#endif + +#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 +#define ABSL_NAMESPACE_BEGIN +#define ABSL_NAMESPACE_END +#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1 +#define ABSL_NAMESPACE_BEGIN \ + inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME { +#define ABSL_NAMESPACE_END } +#else +#error options.h is misconfigured. +#endif + // ----------------------------------------------------------------------------- // Compiler Feature Checks // ----------------------------------------------------------------------------- @@ -84,6 +148,12 @@ #define ABSL_HAVE_BUILTIN(x) 0 #endif +#if defined(__is_identifier) +#define ABSL_INTERNAL_HAS_KEYWORD(x) !(__is_identifier(x)) +#else +#define ABSL_INTERNAL_HAS_KEYWORD(x) 0 +#endif + // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. // We assume __thread is supported on Linux when compiled with Clang or compiled // against libstdc++ with _GLIBCXX_HAVE_TLS defined. @@ -125,13 +195,24 @@ #error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set #elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ (!defined(__clang__) && defined(__GNUC__) && \ - (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \ + (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ (defined(_MSC_VER) && !defined(__NVCC__)) #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 #endif +// ABSL_HAVE_SOURCE_LOCATION_CURRENT +// +// Indicates whether `absl::SourceLocation::current()` will return useful +// information in some contexts. +#ifndef ABSL_HAVE_SOURCE_LOCATION_CURRENT +#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \ + ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE) +#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 +#endif +#endif + // ABSL_HAVE_THREAD_LOCAL // // Checks whether C++11's `thread_local` storage duration specifier is @@ -235,13 +316,19 @@ #error ABSL_HAVE_EXCEPTIONS cannot be directly set. #elif defined(__clang__) -// TODO(calabrese) -// Switch to using __cpp_exceptions when we no longer support versions < 3.6. -// For details on this check, see: -// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro + +#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) +// Clang >= 3.6 +#if __has_feature(cxx_exceptions) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif // __has_feature(cxx_exceptions) +#else +// Clang < 3.6 +// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro #if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions) #define ABSL_HAVE_EXCEPTIONS 1 #endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions) +#endif // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) // Handle remaining special cases and default to exceptions being supported. #elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ @@ -307,7 +394,7 @@ // ABSL_HAVE_SEMAPHORE_H // -// Checks whether the platform supports the <semaphore.h> header and sem_open(3) +// Checks whether the platform supports the <semaphore.h> header and sem_init(3) // family of functions as standardized in POSIX.1-2001. // // Note: While Apple provides <semaphore.h> for both iOS and macOS, it is @@ -334,8 +421,15 @@ #define ABSL_HAVE_ALARM 1 #elif defined(_MSC_VER) // feature tests for Microsoft's library +#elif defined(__MINGW32__) +// mingw32 doesn't provide alarm(2): +// https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h +// mingw-w64 provides a no-op implementation: +// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c #elif defined(__EMSCRIPTEN__) // emscripten doesn't support signals +#elif defined(__Fuchsia__) +// Signals don't exist on fuchsia. #elif defined(__native_client__) #else // other standard libraries @@ -461,6 +555,68 @@ #define ABSL_HAVE_STD_STRING_VIEW 1 #endif +// ABSL_USES_STD_ANY +// +// Indicates whether absl::any is an alias for std::any. +#if !defined(ABSL_OPTION_USE_STD_ANY) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_ANY == 0 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY)) +#undef ABSL_USES_STD_ANY +#elif ABSL_OPTION_USE_STD_ANY == 1 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY)) +#define ABSL_USES_STD_ANY 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_OPTIONAL +// +// Indicates whether absl::optional is an alias for std::optional. +#if !defined(ABSL_OPTION_USE_STD_OPTIONAL) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL)) +#undef ABSL_USES_STD_OPTIONAL +#elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL)) +#define ABSL_USES_STD_OPTIONAL 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_VARIANT +// +// Indicates whether absl::variant is an alias for std::variant. +#if !defined(ABSL_OPTION_USE_STD_VARIANT) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT)) +#undef ABSL_USES_STD_VARIANT +#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT)) +#define ABSL_USES_STD_VARIANT 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_STRING_VIEW +// +// Indicates whether absl::string_view is an alias for std::string_view. +#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + !defined(ABSL_HAVE_STD_STRING_VIEW)) +#undef ABSL_USES_STD_STRING_VIEW +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + defined(ABSL_HAVE_STD_STRING_VIEW)) +#define ABSL_USES_STD_STRING_VIEW 1 +#else +#error options.h is misconfigured. +#endif + // In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION // SEH exception from emplace for variant<SomeStruct> when constructing the // struct can throw. This defeats some of variant_test and @@ -469,4 +625,47 @@ #define ABSL_INTERNAL_MSVC_2017_DBG_MODE #endif +// ABSL_INTERNAL_MANGLED_NS +// ABSL_INTERNAL_MANGLED_BACKREFERENCE +// +// Internal macros for building up mangled names in our internal fork of CCTZ. +// This implementation detail is only needed and provided for the MSVC build. +// +// These macros both expand to string literals. ABSL_INTERNAL_MANGLED_NS is +// the mangled spelling of the `absl` namespace, and +// ABSL_INTERNAL_MANGLED_BACKREFERENCE is a back-reference integer representing +// the proper count to skip past the CCTZ fork namespace names. (This number +// is one larger when there is an inline namespace name to skip.) +#if defined(_MSC_VER) +#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 +#define ABSL_INTERNAL_MANGLED_NS "absl" +#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "5" +#else +#define ABSL_INTERNAL_MANGLED_NS \ + ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) "@absl" +#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "6" +#endif +#endif + +#undef ABSL_INTERNAL_HAS_KEYWORD + +// ABSL_DLL +// +// When building Abseil as a DLL, this macro expands to `__declspec(dllexport)` +// so we can annotate symbols appropriately as being exported. When used in +// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so +// that consumers know the symbol is defined inside the DLL. In all other cases, +// the macro expands to nothing. +#if defined(_MSC_VER) +#if defined(ABSL_BUILD_DLL) +#define ABSL_DLL __declspec(dllexport) +#elif defined(ABSL_CONSUME_DLL) +#define ABSL_DLL __declspec(dllimport) +#else +#define ABSL_DLL +#endif +#else +#define ABSL_DLL +#endif // defined(_MSC_VER) + #endif // ABSL_BASE_CONFIG_H_ diff --git a/absl/base/const_init.h b/absl/base/const_init.h index 30d6a3ca..16520b61 100644 --- a/absl/base/const_init.h +++ b/absl/base/const_init.h @@ -22,6 +22,8 @@ #ifndef ABSL_BASE_CONST_INIT_H_ #define ABSL_BASE_CONST_INIT_H_ +#include "absl/base/config.h" + // In general, objects with static storage duration (such as global variables) // can trigger tricky object lifetime situations. Attempting to access them // from the constructors or destructors of other global objects can result in @@ -62,13 +64,13 @@ // or thread_local storage duration. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN enum ConstInitType { kConstInit, }; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_CONST_INIT_H_ diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc index 2ed38606..a59be29e 100644 --- a/absl/base/exception_safety_testing_test.cc +++ b/absl/base/exception_safety_testing_test.cc @@ -14,6 +14,8 @@ #include "absl/base/internal/exception_safety_testing.h" +#ifdef ABSL_HAVE_EXCEPTIONS + #include <cstddef> #include <exception> #include <iostream> @@ -326,17 +328,15 @@ TEST(ThrowingValueTest, NonThrowingDelete) { UnsetCountdown(); } -using Storage = - absl::aligned_storage_t<sizeof(ThrowingValue<>), alignof(ThrowingValue<>)>; - TEST(ThrowingValueTest, NonThrowingPlacementDelete) { constexpr int kArrayLen = 2; // We intentionally create extra space to store the tag allocated by placement // new[]. constexpr int kStorageLen = 4; - Storage buf; - Storage array_buf[kStorageLen]; + alignas(ThrowingValue<>) unsigned char buf[sizeof(ThrowingValue<>)]; + alignas(ThrowingValue<>) unsigned char + array_buf[sizeof(ThrowingValue<>[kStorageLen])]; auto* placed = new (&buf) ThrowingValue<>(1); auto placed_array = new (&array_buf) ThrowingValue<>[kArrayLen]; @@ -900,12 +900,12 @@ TEST(ConstructorTrackerTest, CreatedAfter) { } TEST(ConstructorTrackerTest, NotDestroyedAfter) { - absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage; + alignas(Tracked) unsigned char storage[sizeof(Tracked)]; EXPECT_NONFATAL_FAILURE( { exceptions_internal::ConstructorTracker ct( exceptions_internal::countdown); - new (&storage) Tracked; + new (&storage) Tracked(); }, "not destroyed"); } @@ -922,11 +922,11 @@ TEST(ConstructorTrackerTest, DestroyedTwice) { TEST(ConstructorTrackerTest, ConstructedTwice) { exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown); - absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage; + alignas(Tracked) unsigned char storage[sizeof(Tracked)]; EXPECT_NONFATAL_FAILURE( { - new (&storage) Tracked; - new (&storage) Tracked; + new (&storage) Tracked(); + new (&storage) Tracked(); reinterpret_cast<Tracked*>(&storage)->~Tracked(); }, "re-constructed"); @@ -952,3 +952,5 @@ TEST(ThrowingAllocatorTraitsTest, Assignablility) { } // namespace } // namespace testing + +#endif // ABSL_HAVE_EXCEPTIONS diff --git a/absl/base/inline_variable_test.cc b/absl/base/inline_variable_test.cc index 40e8b57b..37a40e1e 100644 --- a/absl/base/inline_variable_test.cc +++ b/absl/base/inline_variable_test.cc @@ -20,7 +20,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace inline_variable_testing_internal { namespace { @@ -60,5 +60,5 @@ TEST(InlineVariableTest, FunPtrType) { } // namespace } // namespace inline_variable_testing_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/inline_variable_test_a.cc b/absl/base/inline_variable_test_a.cc index c19a6e55..f96a58d9 100644 --- a/absl/base/inline_variable_test_a.cc +++ b/absl/base/inline_variable_test_a.cc @@ -15,7 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace inline_variable_testing_internal { const Foo& get_foo_a() { return inline_variable_foo; } @@ -23,5 +23,5 @@ const Foo& get_foo_a() { return inline_variable_foo; } const int& get_int_a() { return inline_variable_int; } } // namespace inline_variable_testing_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/inline_variable_test_b.cc b/absl/base/inline_variable_test_b.cc index ae8da104..038adc30 100644 --- a/absl/base/inline_variable_test_b.cc +++ b/absl/base/inline_variable_test_b.cc @@ -15,7 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace inline_variable_testing_internal { const Foo& get_foo_b() { return inline_variable_foo; } @@ -23,5 +23,5 @@ const Foo& get_foo_b() { return inline_variable_foo; } const int& get_int_b() { return inline_variable_int; } } // namespace inline_variable_testing_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h index 6757e75b..ae21cd7f 100644 --- a/absl/base/internal/atomic_hook.h +++ b/absl/base/internal/atomic_hook.h @@ -11,7 +11,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// #ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ #define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ @@ -21,29 +20,51 @@ #include <cstdint> #include <utility> -#ifdef _MSC_FULL_VER +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +#if defined(_MSC_VER) && !defined(__clang__) +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0 +#else +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1 +#endif + +#if defined(_MSC_VER) #define ABSL_HAVE_WORKING_ATOMIC_POINTER 0 #else #define ABSL_HAVE_WORKING_ATOMIC_POINTER 1 #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { template <typename T> class AtomicHook; -// AtomicHook is a helper class, templatized on a raw function pointer type, for -// implementing Abseil customization hooks. It is a callable object that -// dispatches to the registered hook. +// To workaround AtomicHook not being constant-initializable on some platforms, +// prefer to annotate instances with `ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES` +// instead of `ABSL_CONST_INIT`. +#if ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT +#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_CONST_INIT +#else +#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +#endif + +// `AtomicHook` is a helper class, templatized on a raw function pointer type, +// for implementing Abseil customization hooks. It is a callable object that +// dispatches to the registered hook. Objects of type `AtomicHook` must have +// static or thread storage duration. // // A default constructed object performs a no-op (and returns a default // constructed object) if no hook has been registered. // -// Hooks can be pre-registered via constant initialization, for example, -// ABSL_CONST_INIT static AtomicHook<void(*)()> my_hook(DefaultAction); -// and then changed at runtime via a call to Store(). +// Hooks can be pre-registered via constant initialization, for example: +// +// ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static AtomicHook<void(*)()> +// my_hook(DefaultAction); +// +// and then changed at runtime via a call to `Store()`. // // Reads and writes guarantee memory_order_acquire/memory_order_release // semantics. @@ -58,12 +79,23 @@ class AtomicHook<ReturnType (*)(Args...)> { // Constructs an object that by default dispatches to/returns the // pre-registered default_fn when no hook has been registered at runtime. -#if ABSL_HAVE_WORKING_ATOMIC_POINTER +#if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT explicit constexpr AtomicHook(FnPtr default_fn) : hook_(default_fn), default_fn_(default_fn) {} -#else +#elif ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT explicit constexpr AtomicHook(FnPtr default_fn) : hook_(kUninitialized), default_fn_(default_fn) {} +#else + // As of January 2020, on all known versions of MSVC this constructor runs in + // the global constructor sequence. If `Store()` is called by a dynamic + // initializer, we want to preserve the value, even if this constructor runs + // after the call to `Store()`. If not, `hook_` will be + // zero-initialized by the linker and we have no need to set it. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html + explicit constexpr AtomicHook(FnPtr default_fn) + : /* hook_(deliberately omitted), */ default_fn_(default_fn) { + static_assert(kUninitialized == 0, "here we rely on zero-initialization"); + } #endif // Stores the provided function pointer as the value for this hook. @@ -159,9 +191,10 @@ class AtomicHook<ReturnType (*)(Args...)> { }; #undef ABSL_HAVE_WORKING_ATOMIC_POINTER +#undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ diff --git a/absl/base/internal/atomic_hook_test.cc b/absl/base/internal/atomic_hook_test.cc index ecc80406..e577a8fd 100644 --- a/absl/base/internal/atomic_hook_test.cc +++ b/absl/base/internal/atomic_hook_test.cc @@ -14,16 +14,22 @@ #include "absl/base/internal/atomic_hook.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook_test_helper.h" namespace { +using ::testing::Eq; + int value = 0; void TestHook(int x) { value = x; } TEST(AtomicHookTest, NoDefaultFunction) { - ABSL_CONST_INIT static absl::base_internal::AtomicHook<void(*)(int)> hook; + ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook< + void (*)(int)> + hook; value = 0; // Test the default DummyFunction. @@ -49,8 +55,9 @@ TEST(AtomicHookTest, NoDefaultFunction) { TEST(AtomicHookTest, WithDefaultFunction) { // Set the default value to TestHook at compile-time. - ABSL_CONST_INIT static absl::base_internal::AtomicHook<void (*)(int)> hook( - TestHook); + ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook< + void (*)(int)> + hook(TestHook); value = 0; // Test the default value is TestHook. @@ -67,4 +74,24 @@ TEST(AtomicHookTest, WithDefaultFunction) { EXPECT_EQ(value, 2); } +ABSL_CONST_INIT int override_func_calls = 0; +void OverrideFunc() { override_func_calls++; } +static struct OverrideInstaller { + OverrideInstaller() { absl::atomic_hook_internal::func.Store(OverrideFunc); } +} override_installer; + +TEST(AtomicHookTest, DynamicInitFromAnotherTU) { + // MSVC 14.2 doesn't do constexpr static init correctly; in particular it + // tends to sequence static init (i.e. defaults) of `AtomicHook` objects + // after their dynamic init (i.e. overrides), overwriting whatever value was + // written during dynamic init. This regression test validates the fix. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html + EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0)); + EXPECT_THAT(override_func_calls, Eq(0)); + absl::atomic_hook_internal::func(); + EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0)); + EXPECT_THAT(override_func_calls, Eq(1)); + EXPECT_THAT(absl::atomic_hook_internal::func.Load(), Eq(OverrideFunc)); +} + } // namespace diff --git a/absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc b/absl/base/internal/atomic_hook_test_helper.cc index 8797e2e7..537d47cd 100644 --- a/absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc +++ b/absl/base/internal/atomic_hook_test_helper.cc @@ -12,19 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <iostream> -#include <random> +#include "absl/base/internal/atomic_hook_test_helper.h" -#include "absl/random/random.h" +#include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook.h" -// This program is used in integration tests. +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace atomic_hook_internal { -int main() { - std::seed_seq seed_seq{}; - absl::BitGen rng(seed_seq); - constexpr size_t kSequenceLength = 8; - for (size_t i = 0; i < kSequenceLength; i++) { - std::cout << rng() << "\n"; - } - return 0; -} +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<VoidF> + func(DefaultFunc); +ABSL_CONST_INIT int default_func_calls = 0; +void DefaultFunc() { default_func_calls++; } +void RegisterFunc(VoidF f) { func.Store(f); } + +} // namespace atomic_hook_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/random/internal/seed_salting_sequence_generator.cc b/absl/base/internal/atomic_hook_test_helper.h index 31fdcfe1..3e72b497 100644 --- a/absl/random/internal/seed_salting_sequence_generator.cc +++ b/absl/base/internal/atomic_hook_test_helper.h @@ -12,19 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <iostream> -#include <random> +#ifndef ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ +#define ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ -#include "absl/random/random.h" +#include "absl/base/internal/atomic_hook.h" -// This program is used in integration tests. +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace atomic_hook_internal { -int main() { - std::seed_seq seed_seq{1234}; - absl::BitGen rng(seed_seq); - constexpr size_t kSequenceLength = 8; - for (size_t i = 0; i < kSequenceLength; i++) { - std::cout << rng() << "\n"; - } - return 0; -} +using VoidF = void (*)(); +extern absl::base_internal::AtomicHook<VoidF> func; +extern int default_func_calls; +void DefaultFunc(); +void RegisterFunc(VoidF func); + +} // namespace atomic_hook_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h index ef978e9b..8b03453c 100644 --- a/absl/base/internal/bits.h +++ b/absl/base/internal/bits.h @@ -20,6 +20,8 @@ #include <cstdint> +#include "absl/base/config.h" + // Clang on Windows has __builtin_clzll; otherwise we need to use the // windows intrinsic functions. #if defined(_MSC_VER) @@ -46,15 +48,27 @@ namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { int zeroes = 60; - if (n >> 32) zeroes -= 32, n >>= 32; - if (n >> 16) zeroes -= 16, n >>= 16; - if (n >> 8) zeroes -= 8, n >>= 8; - if (n >> 4) zeroes -= 4, n >>= 4; + if (n >> 32) { + zeroes -= 32; + n >>= 32; + } + if (n >> 16) { + zeroes -= 16; + n >>= 16; + } + if (n >> 8) { + zeroes -= 8; + n >>= 8; + } + if (n >> 4) { + zeroes -= 4; + n >>= 4; + } return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; } @@ -96,9 +110,18 @@ ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { int zeroes = 28; - if (n >> 16) zeroes -= 16, n >>= 16; - if (n >> 8) zeroes -= 8, n >>= 8; - if (n >> 4) zeroes -= 4, n >>= 4; + if (n >> 16) { + zeroes -= 16; + n >>= 16; + } + if (n >> 8) { + zeroes -= 8; + n >>= 8; + } + if (n >> 4) { + zeroes -= 4; + n >>= 4; + } return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; } @@ -189,7 +212,7 @@ ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { #undef ABSL_BASE_INTERNAL_FORCEINLINE } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_BITS_H_ diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc index 9868e549..0e65005b 100644 --- a/absl/base/internal/cycleclock.cc +++ b/absl/base/internal/cycleclock.cc @@ -28,7 +28,7 @@ #include "absl/base/internal/unscaledcycleclock.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { #if ABSL_USE_UNSCALED_CYCLECLOCK @@ -103,5 +103,5 @@ double CycleClock::Frequency() { #endif } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h index 39bce06a..a18b5844 100644 --- a/absl/base/internal/cycleclock.h +++ b/absl/base/internal/cycleclock.h @@ -44,8 +44,10 @@ #include <cstdint> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // ----------------------------------------------------------------------------- @@ -86,7 +88,7 @@ class CycleClockSource { }; } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_CYCLECLOCK_H_ diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h index 2e5e422c..5618867b 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -62,7 +62,7 @@ extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); #endif // __BIONIC__ namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // Platform specific logic extracted from @@ -129,7 +129,7 @@ inline int DirectMunmap(void* start, size_t length) { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #else // !__linux__ @@ -138,7 +138,7 @@ inline int DirectMunmap(void* start, size_t length) { // actual mmap()/munmap() methods. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, @@ -151,7 +151,7 @@ inline int DirectMunmap(void* start, size_t length) { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // __linux__ diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index 8643bffa..9677530e 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h @@ -19,9 +19,6 @@ // The following guarantees declaration of the byte swap functions #ifdef _MSC_VER #include <stdlib.h> // NOLINT(build/include) -#elif defined(__APPLE__) -// macOS / Darwin features -#include <libkern/OSByteOrder.h> #elif defined(__FreeBSD__) #include <sys/endian.h> #elif defined(__GLIBC__) @@ -34,7 +31,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Use compiler byte-swapping intrinsics if they are available. 32-bit // and 64-bit versions are available in Clang and GCC as of GCC 4.3.0. @@ -64,11 +61,6 @@ inline uint16_t gbswap_16(uint16_t host_int) { return _byteswap_ushort(host_int); } -#elif defined(__APPLE__) -inline uint64_t gbswap_64(uint64_t host_int) { return OSSwapInt16(host_int); } -inline uint32_t gbswap_32(uint32_t host_int) { return OSSwapInt32(host_int); } -inline uint16_t gbswap_16(uint16_t host_int) { return OSSwapInt64(host_int); } - #else inline uint64_t gbswap_64(uint64_t host_int) { #if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) @@ -114,7 +106,7 @@ inline uint16_t gbswap_16(uint16_t host_int) { #endif } -#endif // intrinics available +#endif // intrinsics available #ifdef ABSL_IS_LITTLE_ENDIAN @@ -268,7 +260,7 @@ inline void Store64(void *p, uint64_t v) { } // namespace big_endian -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_ENDIAN_H_ diff --git a/absl/base/internal/endian_test.cc b/absl/base/internal/endian_test.cc index 0c683286..aa6b8496 100644 --- a/absl/base/internal/endian_test.cc +++ b/absl/base/internal/endian_test.cc @@ -24,7 +24,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { const uint64_t kInitialNumber{0x0123456789abcdef}; @@ -261,5 +261,5 @@ TEST(EndianessTest, big_endian) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/errno_saver.h b/absl/base/internal/errno_saver.h new file mode 100644 index 00000000..251de510 --- /dev/null +++ b/absl/base/internal/errno_saver.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ +#define ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ + +#include <cerrno> + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// `ErrnoSaver` captures the value of `errno` upon construction and restores it +// upon deletion. It is used in low-level code and must be super fast. Do not +// add instrumentation, even in debug modes. +class ErrnoSaver { + public: + ErrnoSaver() : saved_errno_(errno) {} + ~ErrnoSaver() { errno = saved_errno_; } + int operator()() const { return saved_errno_; } + + private: + const int saved_errno_; +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ diff --git a/absl/base/internal/errno_saver_test.cc b/absl/base/internal/errno_saver_test.cc new file mode 100644 index 00000000..b845e2dd --- /dev/null +++ b/absl/base/internal/errno_saver_test.cc @@ -0,0 +1,44 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/errno_saver.h" + +#include <cerrno> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace { +using ::testing::Eq; + +struct ErrnoPrinter { + int no; +}; +std::ostream &operator<<(std::ostream &os, ErrnoPrinter ep) { + return os << strerror(ep.no) << " [" << ep.no << "]"; +} +bool operator==(ErrnoPrinter one, ErrnoPrinter two) { return one.no == two.no; } + +TEST(ErrnoSaverTest, Works) { + errno = EDOM; + { + absl::base_internal::ErrnoSaver errno_saver; + EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{EDOM})); + errno = ERANGE; + EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{ERANGE})); + EXPECT_THAT(ErrnoPrinter{errno_saver()}, Eq(ErrnoPrinter{EDOM})); + } + EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{EDOM})); +} +} // namespace diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc index 6ef4325c..6ccac418 100644 --- a/absl/base/internal/exception_safety_testing.cc +++ b/absl/base/internal/exception_safety_testing.cc @@ -14,6 +14,8 @@ #include "absl/base/internal/exception_safety_testing.h" +#ifdef ABSL_HAVE_EXCEPTIONS + #include "gtest/gtest.h" #include "absl/meta/type_traits.h" @@ -73,3 +75,5 @@ std::string GetSpecString(AllocSpec spec) { } // namespace exceptions_internal } // namespace testing + +#endif // ABSL_HAVE_EXCEPTIONS diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h index be38ba54..6ba89d05 100644 --- a/absl/base/internal/exception_safety_testing.h +++ b/absl/base/internal/exception_safety_testing.h @@ -17,6 +17,10 @@ #ifndef ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ #define ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ +#include "absl/base/config.h" + +#ifdef ABSL_HAVE_EXCEPTIONS + #include <cstddef> #include <cstdint> #include <functional> @@ -27,7 +31,6 @@ #include <unordered_map> #include "gtest/gtest.h" -#include "absl/base/config.h" #include "absl/base/internal/pretty_function.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" @@ -1093,4 +1096,6 @@ class ExceptionSafetyTestBuilder { } // namespace testing +#endif // ABSL_HAVE_EXCEPTIONS + #endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ diff --git a/absl/base/internal/exponential_biased.cc b/absl/base/internal/exponential_biased.cc new file mode 100644 index 00000000..1b30c061 --- /dev/null +++ b/absl/base/internal/exponential_biased.cc @@ -0,0 +1,93 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/exponential_biased.h" + +#include <stdint.h> + +#include <algorithm> +#include <atomic> +#include <cmath> +#include <limits> + +#include "absl/base/attributes.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// The algorithm generates a random number between 0 and 1 and applies the +// inverse cumulative distribution function for an exponential. Specifically: +// Let m be the inverse of the sample period, then the probability +// distribution function is m*exp(-mx) so the CDF is +// p = 1 - exp(-mx), so +// q = 1 - p = exp(-mx) +// log_e(q) = -mx +// -log_e(q)/m = x +// log_2(q) * (-log_e(2) * 1/m) = x +// In the code, q is actually in the range 1 to 2**26, hence the -26 below +int64_t ExponentialBiased::GetSkipCount(int64_t mean) { + if (ABSL_PREDICT_FALSE(!initialized_)) { + Initialize(); + } + + uint64_t rng = NextRandom(rng_); + rng_ = rng; + + // Take the top 26 bits as the random number + // (This plus the 1<<58 sampling bound give a max possible step of + // 5194297183973780480 bytes.) + // The uint32_t cast is to prevent a (hard-to-reproduce) NAN + // under piii debug for some binaries. + double q = static_cast<uint32_t>(rng >> (kPrngNumBits - 26)) + 1.0; + // Put the computed p-value through the CDF of a geometric. + double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean); + // Very large values of interval overflow int64_t. To avoid that, we will + // cheat and clamp any huge values to (int64_t max)/2. This is a potential + // source of bias, but the mean would need to be such a large value that it's + // not likely to come up. For example, with a mean of 1e18, the probability of + // hitting this condition is about 1/1000. For a mean of 1e17, standard + // calculators claim that this event won't happen. + if (interval > static_cast<double>(std::numeric_limits<int64_t>::max() / 2)) { + // Assume huge values are bias neutral, retain bias for next call. + return std::numeric_limits<int64_t>::max() / 2; + } + double value = std::round(interval); + bias_ = interval - value; + return value; +} + +int64_t ExponentialBiased::GetStride(int64_t mean) { + return GetSkipCount(mean - 1) + 1; +} + +void ExponentialBiased::Initialize() { + // We don't get well distributed numbers from `this` so we call NextRandom() a + // bunch to mush the bits around. We use a global_rand to handle the case + // where the same thread (by memory address) gets created and destroyed + // repeatedly. + ABSL_CONST_INIT static std::atomic<uint32_t> global_rand(0); + uint64_t r = reinterpret_cast<uint64_t>(this) + + global_rand.fetch_add(1, std::memory_order_relaxed); + for (int i = 0; i < 20; ++i) { + r = NextRandom(r); + } + rng_ = r; + initialized_ = true; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/base/internal/exponential_biased.h b/absl/base/internal/exponential_biased.h new file mode 100644 index 00000000..94f79a33 --- /dev/null +++ b/absl/base/internal/exponential_biased.h @@ -0,0 +1,130 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ +#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ + +#include <stdint.h> + +#include "absl/base/config.h" +#include "absl/base/macros.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// ExponentialBiased provides a small and fast random number generator for a +// rounded exponential distribution. This generator manages very little state, +// and imposes no synchronization overhead. This makes it useful in specialized +// scenarios requiring minimum overhead, such as stride based periodic sampling. +// +// ExponentialBiased provides two closely related functions, GetSkipCount() and +// GetStride(), both returning a rounded integer defining a number of events +// required before some event with a given mean probability occurs. +// +// The distribution is useful to generate a random wait time or some periodic +// event with a given mean probability. For example, if an action is supposed to +// happen on average once every 'N' events, then we can get a random 'stride' +// counting down how long before the event to happen. For example, if we'd want +// to sample one in every 1000 'Frobber' calls, our code could look like this: +// +// Frobber::Frobber() { +// stride_ = exponential_biased_.GetStride(1000); +// } +// +// void Frobber::Frob(int arg) { +// if (--stride == 0) { +// SampleFrob(arg); +// stride_ = exponential_biased_.GetStride(1000); +// } +// ... +// } +// +// The rounding of the return value creates a bias, especially for smaller means +// where the distribution of the fraction is not evenly distributed. We correct +// this bias by tracking the fraction we rounded up or down on each iteration, +// effectively tracking the distance between the cumulative value, and the +// rounded cumulative value. For example, given a mean of 2: +// +// raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923 +// raw = 0.14624, cumulative = 1.77701, rounded = 2, bias = 0.14624 +// raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805 +// raw = 0.24206, cumulative = 6.95101, rounded = 7, bias = 0.24206 +// etc... +// +// Adjusting with rounding bias is relatively trivial: +// +// double value = bias_ + exponential_distribution(mean)(); +// double rounded_value = std::round(value); +// bias_ = value - rounded_value; +// return rounded_value; +// +// This class is thread-compatible. +class ExponentialBiased { + public: + // The number of bits set by NextRandom. + static constexpr int kPrngNumBits = 48; + + // `GetSkipCount()` returns the number of events to skip before some chosen + // event happens. For example, randomly tossing a coin, we will on average + // throw heads once before we get tails. We can simulate random coin tosses + // using GetSkipCount() as: + // + // ExponentialBiased eb; + // for (...) { + // int number_of_heads_before_tail = eb.GetSkipCount(1); + // for (int flips = 0; flips < number_of_heads_before_tail; ++flips) { + // printf("head..."); + // } + // printf("tail\n"); + // } + // + int64_t GetSkipCount(int64_t mean); + + // GetStride() returns the number of events required for a specific event to + // happen. See the class comments for a usage example. `GetStride()` is + // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or + // `GetSkipCount()` depends mostly on what best fits the use case. + int64_t GetStride(int64_t mean); + + // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] + // + // This is public to enable testing. + static uint64_t NextRandom(uint64_t rnd); + + private: + void Initialize(); + + uint64_t rng_{0}; + double bias_{0}; + bool initialized_{false}; +}; + +// Returns the next prng value. +// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 +// This is the lrand64 generator. +inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { + const uint64_t prng_mult = uint64_t{0x5DEECE66D}; + const uint64_t prng_add = 0xB; + const uint64_t prng_mod_power = 48; + const uint64_t prng_mod_mask = + ~((~static_cast<uint64_t>(0)) << prng_mod_power); + return (prng_mult * rnd + prng_add) & prng_mod_mask; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ diff --git a/absl/base/internal/exponential_biased_test.cc b/absl/base/internal/exponential_biased_test.cc new file mode 100644 index 00000000..90a482d2 --- /dev/null +++ b/absl/base/internal/exponential_biased_test.cc @@ -0,0 +1,199 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/exponential_biased.h" + +#include <stddef.h> + +#include <cmath> +#include <cstdint> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" + +using ::testing::Ge; + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +MATCHER_P2(IsBetween, a, b, + absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a, + " and ", b)) { + return a <= arg && arg <= b; +} + +// Tests of the quality of the random numbers generated +// This uses the Anderson Darling test for uniformity. +// See "Evaluating the Anderson-Darling Distribution" by Marsaglia +// for details. + +// Short cut version of ADinf(z), z>0 (from Marsaglia) +// This returns the p-value for Anderson Darling statistic in +// the limit as n-> infinity. For finite n, apply the error fix below. +double AndersonDarlingInf(double z) { + if (z < 2) { + return exp(-1.2337141 / z) / sqrt(z) * + (2.00012 + + (0.247105 - + (0.0649821 - (0.0347962 - (0.011672 - 0.00168691 * z) * z) * z) * + z) * + z); + } + return exp( + -exp(1.0776 - + (2.30695 - + (0.43424 - (0.082433 - (0.008056 - 0.0003146 * z) * z) * z) * z) * + z)); +} + +// Corrects the approximation error in AndersonDarlingInf for small values of n +// Add this to AndersonDarlingInf to get a better approximation +// (from Marsaglia) +double AndersonDarlingErrFix(int n, double x) { + if (x > 0.8) { + return (-130.2137 + + (745.2337 - + (1705.091 - (1950.646 - (1116.360 - 255.7844 * x) * x) * x) * x) * + x) / + n; + } + double cutoff = 0.01265 + 0.1757 / n; + if (x < cutoff) { + double t = x / cutoff; + t = sqrt(t) * (1 - t) * (49 * t - 102); + return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n; + } else { + double t = (x - cutoff) / (0.8 - cutoff); + t = -0.00022633 + + (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864 * t) * t) * t) * t) * + t; + return t * (0.04213 + 0.01365 / n) / n; + } +} + +// Returns the AndersonDarling p-value given n and the value of the statistic +double AndersonDarlingPValue(int n, double z) { + double ad = AndersonDarlingInf(z); + double errfix = AndersonDarlingErrFix(n, ad); + return ad + errfix; +} + +double AndersonDarlingStatistic(const std::vector<double>& random_sample) { + int n = random_sample.size(); + double ad_sum = 0; + for (int i = 0; i < n; i++) { + ad_sum += (2 * i + 1) * + std::log(random_sample[i] * (1 - random_sample[n - 1 - i])); + } + double ad_statistic = -n - 1 / static_cast<double>(n) * ad_sum; + return ad_statistic; +} + +// Tests if the array of doubles is uniformly distributed. +// Returns the p-value of the Anderson Darling Statistic +// for the given set of sorted random doubles +// See "Evaluating the Anderson-Darling Distribution" by +// Marsaglia and Marsaglia for details. +double AndersonDarlingTest(const std::vector<double>& random_sample) { + double ad_statistic = AndersonDarlingStatistic(random_sample); + double p = AndersonDarlingPValue(random_sample.size(), ad_statistic); + return p; +} + +TEST(ExponentialBiasedTest, CoinTossDemoWithGetSkipCount) { + ExponentialBiased eb; + for (int runs = 0; runs < 10; ++runs) { + for (int flips = eb.GetSkipCount(1); flips > 0; --flips) { + printf("head..."); + } + printf("tail\n"); + } + int heads = 0; + for (int i = 0; i < 10000000; i += 1 + eb.GetSkipCount(1)) { + ++heads; + } + printf("Heads = %d (%f%%)\n", heads, 100.0 * heads / 10000000); +} + +TEST(ExponentialBiasedTest, SampleDemoWithStride) { + ExponentialBiased eb; + int stride = eb.GetStride(10); + int samples = 0; + for (int i = 0; i < 10000000; ++i) { + if (--stride == 0) { + ++samples; + stride = eb.GetStride(10); + } + } + printf("Samples = %d (%f%%)\n", samples, 100.0 * samples / 10000000); +} + + +// Testing that NextRandom generates uniform random numbers. Applies the +// Anderson-Darling test for uniformity +TEST(ExponentialBiasedTest, TestNextRandom) { + for (auto n : std::vector<int>({ + 10, // Check short-range correlation + 100, 1000, + 10000 // Make sure there's no systemic error + })) { + uint64_t x = 1; + // This assumes that the prng returns 48 bit numbers + uint64_t max_prng_value = static_cast<uint64_t>(1) << 48; + // Initialize. + for (int i = 1; i <= 20; i++) { + x = ExponentialBiased::NextRandom(x); + } + std::vector<uint64_t> int_random_sample(n); + // Collect samples + for (int i = 0; i < n; i++) { + int_random_sample[i] = x; + x = ExponentialBiased::NextRandom(x); + } + // First sort them... + std::sort(int_random_sample.begin(), int_random_sample.end()); + std::vector<double> random_sample(n); + // Convert them to uniform randoms (in the range [0,1]) + for (int i = 0; i < n; i++) { + random_sample[i] = + static_cast<double>(int_random_sample[i]) / max_prng_value; + } + // Now compute the Anderson-Darling statistic + double ad_pvalue = AndersonDarlingTest(random_sample); + EXPECT_GT(std::min(ad_pvalue, 1 - ad_pvalue), 0.0001) + << "prng is not uniform: n = " << n << " p = " << ad_pvalue; + } +} + +// The generator needs to be available as a thread_local and as a static +// variable. +TEST(ExponentialBiasedTest, InitializationModes) { + ABSL_CONST_INIT static ExponentialBiased eb_static; + EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0)); + +#if ABSL_HAVE_THREAD_LOCAL + thread_local ExponentialBiased eb_thread; + EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0)); +#endif + + ExponentialBiased eb_stack; + EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0)); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/base/internal/hide_ptr.h b/absl/base/internal/hide_ptr.h index 3720db18..1dba8090 100644 --- a/absl/base/internal/hide_ptr.h +++ b/absl/base/internal/hide_ptr.h @@ -17,8 +17,10 @@ #include <cstdint> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // Arbitrary value with high bits set. Xor'ing with it is unlikely @@ -43,7 +45,7 @@ inline T* UnhidePtr(uintptr_t hidden) { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_ diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h index b9d0f621..a3154ed7 100644 --- a/absl/base/internal/identity.h +++ b/absl/base/internal/identity.h @@ -16,8 +16,10 @@ #ifndef ABSL_BASE_INTERNAL_IDENTITY_H_ #define ABSL_BASE_INTERNAL_IDENTITY_H_ +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace internal { template <typename T> @@ -29,7 +31,7 @@ template <typename T> using identity_t = typename identity<T>::type; } // namespace internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_IDENTITY_H_ diff --git a/absl/base/internal/inline_variable_testing.h b/absl/base/internal/inline_variable_testing.h index 0ebdb9d8..3856b9f8 100644 --- a/absl/base/internal/inline_variable_testing.h +++ b/absl/base/internal/inline_variable_testing.h @@ -18,7 +18,7 @@ #include "absl/base/internal/inline_variable.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace inline_variable_testing_internal { struct Foo { @@ -40,7 +40,7 @@ const int& get_int_a(); const int& get_int_b(); } // namespace inline_variable_testing_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_ diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index 030b98df..c4eceebd 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -45,7 +45,7 @@ // top of this file for the API documentation. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // The five classes below each implement one of the clauses from the definition @@ -181,7 +181,7 @@ InvokeT<F, Args...> Invoke(F&& f, Args&&... args) { std::forward<Args>(args)...); } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_INVOKE_H_ diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc index 419c0e45..1bf94438 100644 --- a/absl/base/internal/low_level_alloc.cc +++ b/absl/base/internal/low_level_alloc.cc @@ -63,7 +63,7 @@ #endif // __APPLE__ namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // A first-fit allocator with amortized logarithmic free() time. @@ -204,32 +204,33 @@ struct LowLevelAlloc::Arena { base_internal::SpinLock mu; // Head of free list, sorted by address - AllocList freelist GUARDED_BY(mu); + AllocList freelist ABSL_GUARDED_BY(mu); // Count of allocated blocks - int32_t allocation_count GUARDED_BY(mu); + int32_t allocation_count ABSL_GUARDED_BY(mu); // flags passed to NewArena const uint32_t flags; // Result of sysconf(_SC_PAGESIZE) const size_t pagesize; // Lowest power of two >= max(16, sizeof(AllocList)) - const size_t roundup; + const size_t round_up; // Smallest allocation block size const size_t min_size; // PRNG state - uint32_t random GUARDED_BY(mu); + uint32_t random ABSL_GUARDED_BY(mu); }; namespace { -using ArenaStorage = std::aligned_storage<sizeof(LowLevelAlloc::Arena), - alignof(LowLevelAlloc::Arena)>::type; - // Static storage space for the lazily-constructed, default global arena // instances. We require this space because the whole point of LowLevelAlloc // is to avoid relying on malloc/new. -ArenaStorage default_arena_storage; -ArenaStorage unhooked_arena_storage; +alignas(LowLevelAlloc::Arena) unsigned char default_arena_storage[sizeof( + LowLevelAlloc::Arena)]; +alignas(LowLevelAlloc::Arena) unsigned char unhooked_arena_storage[sizeof( + LowLevelAlloc::Arena)]; #ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING -ArenaStorage unhooked_async_sig_safe_arena_storage; +alignas( + LowLevelAlloc::Arena) unsigned char unhooked_async_sig_safe_arena_storage + [sizeof(LowLevelAlloc::Arena)]; #endif // We must use LowLevelCallOnce here to construct the global arenas, rather than @@ -276,10 +277,10 @@ static const uintptr_t kMagicAllocated = 0x4c833e95U; static const uintptr_t kMagicUnallocated = ~kMagicAllocated; namespace { -class SCOPED_LOCKABLE ArenaLock { +class ABSL_SCOPED_LOCKABLE ArenaLock { public: explicit ArenaLock(LowLevelAlloc::Arena *arena) - EXCLUSIVE_LOCK_FUNCTION(arena->mu) + ABSL_EXCLUSIVE_LOCK_FUNCTION(arena->mu) : arena_(arena) { #ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { @@ -291,7 +292,7 @@ class SCOPED_LOCKABLE ArenaLock { arena_->mu.Lock(); } ~ArenaLock() { ABSL_RAW_CHECK(left_, "haven't left Arena region"); } - void Leave() UNLOCK_FUNCTION() { + void Leave() ABSL_UNLOCK_FUNCTION() { arena_->mu.Unlock(); #ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if (mask_valid_) { @@ -337,11 +338,11 @@ size_t GetPageSize() { size_t RoundedUpBlockSize() { // Round up block sizes to a power of two close to the header size. - size_t roundup = 16; - while (roundup < sizeof(AllocList::Header)) { - roundup += roundup; + size_t round_up = 16; + while (round_up < sizeof(AllocList::Header)) { + round_up += round_up; } - return roundup; + return round_up; } } // namespace @@ -351,8 +352,8 @@ LowLevelAlloc::Arena::Arena(uint32_t flags_value) allocation_count(0), flags(flags_value), pagesize(GetPageSize()), - roundup(RoundedUpBlockSize()), - min_size(2 * roundup), + round_up(RoundedUpBlockSize()), + min_size(2 * round_up), random(0) { freelist.header.size = 0; freelist.header.magic = @@ -448,7 +449,7 @@ static inline uintptr_t RoundUp(uintptr_t addr, uintptr_t align) { // that the freelist is in the correct order, that it // consists of regions marked "unallocated", and that no two regions // are adjacent in memory (they should have been coalesced). -// L < arena->mu +// L >= arena->mu static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) { ABSL_RAW_CHECK(i < prev->levels, "too few levels in Next()"); AllocList *next = prev->next[i]; @@ -509,8 +510,6 @@ void LowLevelAlloc::Free(void *v) { if (v != nullptr) { AllocList *f = reinterpret_cast<AllocList *>( reinterpret_cast<char *>(v) - sizeof (f->header)); - ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header), - "bad magic number in Free()"); LowLevelAlloc::Arena *arena = f->header.arena; ArenaLock section(arena); AddToFreelist(v, arena); @@ -529,7 +528,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { ArenaLock section(arena); // round up with header size_t req_rnd = RoundUp(CheckedAdd(request, sizeof (s->header)), - arena->roundup); + arena->round_up); for (;;) { // loop until we find a suitable region // find the minimum levels that a block of this size must have int i = LLA_SkiplistLevels(req_rnd, arena->min_size, nullptr) - 1; @@ -615,7 +614,7 @@ void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h index 32b6ec17..db91951c 100644 --- a/absl/base/internal/low_level_alloc.h +++ b/absl/base/internal/low_level_alloc.h @@ -55,7 +55,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { class LowLevelAlloc { @@ -120,7 +120,7 @@ class LowLevelAlloc { }; } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 b6eb8b30..7abbbf9c 100644 --- a/absl/base/internal/low_level_alloc_test.cc +++ b/absl/base/internal/low_level_alloc_test.cc @@ -22,7 +22,7 @@ #include <utility> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { namespace { @@ -150,7 +150,7 @@ static struct BeforeMain { } // namespace } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl int main(int argc, char *argv[]) { diff --git a/absl/base/internal/low_level_scheduling.h b/absl/base/internal/low_level_scheduling.h index b762279d..961cc981 100644 --- a/absl/base/internal/low_level_scheduling.h +++ b/absl/base/internal/low_level_scheduling.h @@ -28,7 +28,7 @@ extern "C" bool __google_disable_rescheduling(void); extern "C" void __google_enable_rescheduling(bool disable_result); namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { class SchedulingHelper; // To allow use of SchedulingGuard. @@ -101,7 +101,7 @@ inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ diff --git a/absl/base/internal/periodic_sampler.cc b/absl/base/internal/periodic_sampler.cc new file mode 100644 index 00000000..520dabba --- /dev/null +++ b/absl/base/internal/periodic_sampler.cc @@ -0,0 +1,53 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/periodic_sampler.h" + +#include <atomic> + +#include "absl/base/internal/exponential_biased.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept { + return rng_.GetStride(period); +} + +bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { + int current_period = period(); + + // Deal with period case 0 (always off) and 1 (always on) + if (ABSL_PREDICT_FALSE(current_period < 2)) { + stride_ = 0; + return current_period == 1; + } + + // Check if this is the first call to Sample() + if (ABSL_PREDICT_FALSE(stride_ == 1)) { + stride_ = static_cast<uint64_t>(-GetExponentialBiased(current_period)); + if (static_cast<int64_t>(stride_) < -1) { + ++stride_; + return false; + } + } + + stride_ = static_cast<uint64_t>(-GetExponentialBiased(current_period)); + return true; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/base/internal/periodic_sampler.h b/absl/base/internal/periodic_sampler.h new file mode 100644 index 00000000..f8a86796 --- /dev/null +++ b/absl/base/internal/periodic_sampler.h @@ -0,0 +1,211 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ +#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ + +#include <stdint.h> + +#include <atomic> + +#include "absl/base/internal/exponential_biased.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// PeriodicSamplerBase provides the basic period sampler implementation. +// +// This is the base class for the templated PeriodicSampler class, which holds +// a global std::atomic value identified by a user defined tag, such that +// each specific PeriodSampler implementation holds its own global period. +// +// PeriodicSamplerBase is thread-compatible except where stated otherwise. +class PeriodicSamplerBase { + public: + // PeriodicSamplerBase is trivial / copyable / movable / destructible. + PeriodicSamplerBase() = default; + PeriodicSamplerBase(PeriodicSamplerBase&&) = default; + PeriodicSamplerBase(const PeriodicSamplerBase&) = default; + + // Returns true roughly once every `period` calls. This is established by a + // randomly picked `stride` that is counted down on each call to `Sample`. + // This stride is picked such that the probability of `Sample()` returning + // true is 1 in `period`. + inline bool Sample() noexcept; + + // The below methods are intended for optimized use cases where the + // size of the inlined fast path code is highly important. Applications + // should use the `Sample()` method unless they have proof that their + // specific use case requires the optimizations offered by these methods. + // + // An example of such a use case is SwissTable sampling. All sampling checks + // are in inlined SwissTable methods, and the number of call sites is huge. + // In this case, the inlined code size added to each translation unit calling + // SwissTable methods is non-trivial. + // + // The `SubtleMaybeSample()` function spuriously returns true even if the + // function should not be sampled, applications MUST match each call to + // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call, + // and use the result of the latter as the sampling decision. + // In other words: the code should logically be equivalent to: + // + // if (SubtleMaybeSample() && SubtleConfirmSample()) { + // // Sample this call + // } + // + // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can + // be placed out of line, for example, the typical use case looks as follows: + // + // // --- frobber.h ----------- + // void FrobberSampled(); + // + // inline void FrobberImpl() { + // // ... + // } + // + // inline void Frobber() { + // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) { + // FrobberSampled(); + // } else { + // FrobberImpl(); + // } + // } + // + // // --- frobber.cc ----------- + // void FrobberSampled() { + // if (!sampler.SubtleConfirmSample())) { + // // Spurious false positive + // FrobberImpl(); + // return; + // } + // + // // Sampled execution + // // ... + // } + inline bool SubtleMaybeSample() noexcept; + bool SubtleConfirmSample() noexcept; + + protected: + // We explicitly don't use a virtual destructor as this class is never + // virtually destroyed, and it keeps the class trivial, which avoids TLS + // prologue and epilogue code for our TLS instances. + ~PeriodicSamplerBase() = default; + + // Returns the next stride for our sampler. + // This function is virtual for testing purposes only. + virtual int64_t GetExponentialBiased(int period) noexcept; + + private: + // Returns the current period of this sampler. Thread-safe. + virtual int period() const noexcept = 0; + + // Keep and decrement stride_ as an unsigned integer, but compare the value + // to zero casted as a signed int. clang and msvc do not create optimum code + // if we use signed for the combined decrement and sign comparison. + // + // Below 3 alternative options, all compiles generate the best code + // using the unsigned increment <---> signed int comparison option. + // + // Option 1: + // int64_t stride_; + // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA + // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt + // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd + // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W + // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS + // + // Option 2: + // int64_t stride_ = 0; + // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK + // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA + // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX + // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd + // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE + // + // Option 3: + // uint64_t stride_; + // if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy + // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE + // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4 + // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD + // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5 + uint64_t stride_ = 0; + ExponentialBiased rng_; +}; + +inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { + // See comments on `stride_` for the unsigned increment / signed compare. + if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) { + return false; + } + return true; +} + +inline bool PeriodicSamplerBase::Sample() noexcept { + return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample() + : false; +} + +// PeriodicSampler is a concreted periodic sampler implementation. +// The user provided Tag identifies the implementation, and is required to +// isolate the global state of this instance from other instances. +// +// Typical use case: +// +// struct HashTablezTag {}; +// thread_local PeriodicSampler sampler; +// +// void HashTableSamplingLogic(...) { +// if (sampler.Sample()) { +// HashTableSlowSamplePath(...); +// } +// } +// +template <typename Tag, int default_period = 0> +class PeriodicSampler final : public PeriodicSamplerBase { + public: + ~PeriodicSampler() = default; + + int period() const noexcept final { + return period_.load(std::memory_order_relaxed); + } + + // Sets the global period for this sampler. Thread-safe. + // Setting a period of 0 disables the sampler, i.e., every call to Sample() + // will return false. Setting a period of 1 puts the sampler in 'always on' + // mode, i.e., every call to Sample() returns true. + static void SetGlobalPeriod(int period) { + period_.store(period, std::memory_order_relaxed); + } + + private: + static std::atomic<int> period_; +}; + +template <typename Tag, int default_period> +std::atomic<int> PeriodicSampler<Tag, default_period>::period_(default_period); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ diff --git a/absl/base/internal/periodic_sampler_benchmark.cc b/absl/base/internal/periodic_sampler_benchmark.cc new file mode 100644 index 00000000..5ad469ce --- /dev/null +++ b/absl/base/internal/periodic_sampler_benchmark.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 "benchmark/benchmark.h" +#include "absl/base/internal/periodic_sampler.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { +namespace { + +template <typename Sampler> +void BM_Sample(Sampler* sampler, benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(sampler); + benchmark::DoNotOptimize(sampler->Sample()); + } +} + +template <typename Sampler> +void BM_SampleMinunumInlined(Sampler* sampler, benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(sampler); + if (ABSL_PREDICT_FALSE(sampler->SubtleMaybeSample())) { + benchmark::DoNotOptimize(sampler->SubtleConfirmSample()); + } + } +} + +void BM_PeriodicSampler_TinySample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler<Tag, 10> sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_TinySample); + +void BM_PeriodicSampler_ShortSample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler<Tag, 1024> sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_ShortSample); + +void BM_PeriodicSampler_LongSample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler<Tag, 1024 * 1024> sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_LongSample); + +void BM_PeriodicSampler_LongSampleMinunumInlined(benchmark::State& state) { + struct Tag {}; + PeriodicSampler<Tag, 1024 * 1024> sampler; + BM_SampleMinunumInlined(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_LongSampleMinunumInlined); + +void BM_PeriodicSampler_Disabled(benchmark::State& state) { + struct Tag {}; + PeriodicSampler<Tag, 0> sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_Disabled); + +} // namespace +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/base/internal/periodic_sampler_test.cc b/absl/base/internal/periodic_sampler_test.cc new file mode 100644 index 00000000..3b301e37 --- /dev/null +++ b/absl/base/internal/periodic_sampler_test.cc @@ -0,0 +1,177 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/periodic_sampler.h" + +#include <thread> // NOLINT(build/c++11) + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/macros.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { +namespace { + +using testing::Eq; +using testing::Return; +using testing::StrictMock; + +class MockPeriodicSampler : public PeriodicSamplerBase { + public: + virtual ~MockPeriodicSampler() = default; + + MOCK_METHOD(int, period, (), (const, noexcept)); + MOCK_METHOD(int64_t, GetExponentialBiased, (int), (noexcept)); +}; + +TEST(PeriodicSamplerBaseTest, Sample) { + StrictMock<MockPeriodicSampler> sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .WillOnce(Return(2)) + .WillOnce(Return(3)) + .WillOnce(Return(4)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, ImmediatelySample) { + StrictMock<MockPeriodicSampler> sampler; + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .WillOnce(Return(3)); + + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Disabled) { + StrictMock<MockPeriodicSampler> sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(0)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, AlwaysOn) { + StrictMock<MockPeriodicSampler> sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(1)); + + EXPECT_TRUE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Disable) { + StrictMock<MockPeriodicSampler> sampler; + + EXPECT_CALL(sampler, period()).WillOnce(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(3)); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(0)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Enable) { + StrictMock<MockPeriodicSampler> sampler; + + EXPECT_CALL(sampler, period()).WillOnce(Return(0)); + EXPECT_FALSE(sampler.Sample()); + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .Times(2) + .WillRepeatedly(Return(3)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerTest, ConstructConstInit) { + struct Tag {}; + ABSL_CONST_INIT static PeriodicSampler<Tag> sampler; + (void)sampler; +} + +TEST(PeriodicSamplerTest, DefaultPeriod0) { + struct Tag {}; + PeriodicSampler<Tag> sampler; + EXPECT_THAT(sampler.period(), Eq(0)); +} + +TEST(PeriodicSamplerTest, DefaultPeriod) { + struct Tag {}; + PeriodicSampler<Tag, 100> sampler; + EXPECT_THAT(sampler.period(), Eq(100)); +} + +TEST(PeriodicSamplerTest, SetGlobalPeriod) { + struct Tag1 {}; + struct Tag2 {}; + PeriodicSampler<Tag1, 25> sampler1; + PeriodicSampler<Tag2, 50> sampler2; + + EXPECT_THAT(sampler1.period(), Eq(25)); + EXPECT_THAT(sampler2.period(), Eq(50)); + + std::thread thread([] { + PeriodicSampler<Tag1, 25> sampler1; + PeriodicSampler<Tag2, 50> sampler2; + EXPECT_THAT(sampler1.period(), Eq(25)); + EXPECT_THAT(sampler2.period(), Eq(50)); + sampler1.SetGlobalPeriod(10); + sampler2.SetGlobalPeriod(20); + }); + thread.join(); + + EXPECT_THAT(sampler1.period(), Eq(10)); + EXPECT_THAT(sampler2.period(), Eq(20)); +} + +} // namespace +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index 1a36d5b6..40cea550 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -37,9 +37,9 @@ // this, consider moving both to config.h instead. #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ defined(__Fuchsia__) || defined(__native_client__) || \ - defined(__EMSCRIPTEN__) -#include <unistd.h> + defined(__EMSCRIPTEN__) || defined(__ASYLO__) +#include <unistd.h> #define ABSL_HAVE_POSIX_WRITE 1 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 @@ -71,10 +71,12 @@ // Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a // whitelisted set of platforms for which we expect not to be able to raw log. -ABSL_CONST_INIT static absl::base_internal::AtomicHook< - absl::raw_logging_internal::LogPrefixHook> log_prefix_hook; -ABSL_CONST_INIT static absl::base_internal::AtomicHook< - absl::raw_logging_internal::AbortHook> abort_hook; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook< + absl::raw_logging_internal::LogPrefixHook> + log_prefix_hook; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook< + absl::raw_logging_internal::AbortHook> + abort_hook; #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED static const char kTruncated[] = " ... (message truncated)\n"; @@ -182,7 +184,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } // namespace namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace raw_logging_internal { void SafeWriteToStderr(const char *s, size_t len) { #if defined(ABSL_HAVE_SYSCALL_WRITE) @@ -225,13 +227,14 @@ bool RawLoggingFullySupported() { #endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED } -ABSL_CONST_INIT absl::base_internal::AtomicHook<InternalLogFunction> - internal_log_function(DefaultInternalLog); +ABSL_DLL ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES + absl::base_internal::AtomicHook<InternalLogFunction> + internal_log_function(DefaultInternalLog); void RegisterInternalLogFunction(InternalLogFunction func) { internal_log_function.Store(func); } } // namespace raw_logging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index 8e5a34ef..418d6c85 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -22,9 +22,11 @@ #include <string> #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/internal/atomic_hook.h" #include "absl/base/log_severity.h" #include "absl/base/macros.h" +#include "absl/base/optimization.h" #include "absl/base/port.h" // This is similar to LOG(severity) << format..., but @@ -70,14 +72,10 @@ // // The API is a subset of the above: each macro only takes two arguments. Use // StrCat if you need to build a richer message. -#define ABSL_INTERNAL_LOG(severity, message) \ - do { \ - constexpr const char* absl_raw_logging_internal_basename = \ - ::absl::raw_logging_internal::Basename(__FILE__, \ - sizeof(__FILE__) - 1); \ - ::absl::raw_logging_internal::internal_log_function( \ - ABSL_RAW_LOGGING_INTERNAL_##severity, \ - absl_raw_logging_internal_basename, __LINE__, message); \ +#define ABSL_INTERNAL_LOG(severity, message) \ + do { \ + ::absl::raw_logging_internal::internal_log_function( \ + ABSL_RAW_LOGGING_INTERNAL_##severity, __FILE__, __LINE__, message); \ } while (0) #define ABSL_INTERNAL_CHECK(condition, message) \ @@ -97,7 +95,7 @@ ::absl::NormalizeLogSeverity(severity) namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace raw_logging_internal { // Helper function to implement ABSL_RAW_LOG @@ -158,7 +156,7 @@ using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, // // 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro // was located. -// The null-terminated logged message lives in the buffer between 'buf_start' +// The NUL-terminated logged message lives in the buffer between 'buf_start' // and 'buf_end'. 'prefix_end' points to the first non-prefix character of the // buffer (as written by the LogPrefixHook.) using AbortHook = void (*)(const char* file, int line, const char* buf_start, @@ -172,12 +170,14 @@ using InternalLogFunction = void (*)(absl::LogSeverity severity, const char* file, int line, const std::string& message); -extern base_internal::AtomicHook<InternalLogFunction> internal_log_function; +ABSL_DLL ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES extern base_internal::AtomicHook< + InternalLogFunction> + internal_log_function; void RegisterInternalLogFunction(InternalLogFunction func); } // namespace raw_logging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_RAW_LOGGING_H_ diff --git a/absl/base/internal/scheduling_mode.h b/absl/base/internal/scheduling_mode.h index 48767920..8be5ab6d 100644 --- a/absl/base/internal/scheduling_mode.h +++ b/absl/base/internal/scheduling_mode.h @@ -18,8 +18,10 @@ #ifndef ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ #define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // Used to describe how a thread may be scheduled. Typically associated with @@ -50,7 +52,7 @@ enum SchedulingMode { }; } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ diff --git a/absl/base/internal/scoped_set_env.cc b/absl/base/internal/scoped_set_env.cc index eb7a0116..8a934cb5 100644 --- a/absl/base/internal/scoped_set_env.cc +++ b/absl/base/internal/scoped_set_env.cc @@ -23,7 +23,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { namespace { @@ -77,5 +77,5 @@ ScopedSetEnv::~ScopedSetEnv() { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/scoped_set_env.h b/absl/base/internal/scoped_set_env.h index 709843bc..19ec7b5d 100644 --- a/absl/base/internal/scoped_set_env.h +++ b/absl/base/internal/scoped_set_env.h @@ -19,8 +19,10 @@ #include <string> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { class ScopedSetEnv { @@ -37,7 +39,7 @@ class ScopedSetEnv { }; } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index 5f443bfa..830d4729 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc @@ -54,11 +54,11 @@ // holder to acquire the lock. There may be outstanding waiter(s). namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { -ABSL_CONST_INIT static base_internal::AtomicHook<void (*)(const void *lock, - int64_t wait_cycles)> +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static base_internal::AtomicHook<void (*)( + const void *lock, int64_t wait_cycles)> submit_profile_data; void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock, @@ -229,5 +229,5 @@ uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h index df947661..24e2e9a6 100644 --- a/absl/base/internal/spinlock.h +++ b/absl/base/internal/spinlock.h @@ -46,10 +46,10 @@ #include "absl/base/thread_annotations.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { -class LOCKABLE SpinLock { +class ABSL_LOCKABLE SpinLock { public: SpinLock() : lockword_(kSpinLockCooperative) { ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); @@ -80,7 +80,7 @@ class LOCKABLE SpinLock { ~SpinLock() { ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); } // Acquire this SpinLock. - inline void Lock() EXCLUSIVE_LOCK_FUNCTION() { + inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { ABSL_TSAN_MUTEX_PRE_LOCK(this, 0); if (!TryLockImpl()) { SlowLock(); @@ -92,7 +92,7 @@ class LOCKABLE SpinLock { // acquisition was successful. If the lock was not acquired, false is // returned. If this SpinLock is free at the time of the call, TryLock // will return true with high probability. - inline bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) { + inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) { ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock); bool res = TryLockImpl(); ABSL_TSAN_MUTEX_POST_LOCK( @@ -102,7 +102,7 @@ class LOCKABLE SpinLock { } // Release this SpinLock, which must be held by the calling thread. - inline void Unlock() UNLOCK_FUNCTION() { + inline void Unlock() ABSL_UNLOCK_FUNCTION() { ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0); uint32_t lock_value = lockword_.load(std::memory_order_relaxed); lock_value = lockword_.exchange(lock_value & kSpinLockCooperative, @@ -180,13 +180,13 @@ class LOCKABLE SpinLock { // Corresponding locker object that arranges to acquire a spinlock for // the duration of a C++ scope. -class SCOPED_LOCKABLE SpinLockHolder { +class ABSL_SCOPED_LOCKABLE SpinLockHolder { public: - inline explicit SpinLockHolder(SpinLock* l) EXCLUSIVE_LOCK_FUNCTION(l) + inline explicit SpinLockHolder(SpinLock* l) ABSL_EXCLUSIVE_LOCK_FUNCTION(l) : lock_(l) { l->Lock(); } - inline ~SpinLockHolder() UNLOCK_FUNCTION() { lock_->Unlock(); } + inline ~SpinLockHolder() ABSL_UNLOCK_FUNCTION() { lock_->Unlock(); } SpinLockHolder(const SpinLockHolder&) = delete; SpinLockHolder& operator=(const SpinLockHolder&) = delete; @@ -226,11 +226,10 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, } } - if (lockword_.compare_exchange_strong( + if (!lockword_.compare_exchange_strong( lock_value, kSpinLockHeld | lock_value | wait_cycles | sched_disabled_bit, std::memory_order_acquire, std::memory_order_relaxed)) { - } else { base_internal::SchedulingGuard::EnableRescheduling(sched_disabled_bit != 0); } @@ -238,7 +237,7 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_SPINLOCK_H_ diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc index 28e29d19..323edd62 100644 --- a/absl/base/internal/spinlock_linux.inc +++ b/absl/base/internal/spinlock_linux.inc @@ -19,12 +19,12 @@ #include <unistd.h> #include <atomic> -#include <cerrno> #include <climits> #include <cstdint> #include <ctime> #include "absl/base/attributes.h" +#include "absl/base/internal/errno_saver.h" // The SpinLock lockword is `std::atomic<uint32_t>`. Here we assert that // `std::atomic<uint32_t>` is bitwise equivalent of the `int` expected @@ -51,12 +51,11 @@ extern "C" { ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( std::atomic<uint32_t> *w, uint32_t value, int loop, absl::base_internal::SchedulingMode) { - int save_errno = errno; + absl::base_internal::ErrnoSaver errno_saver; struct timespec tm; tm.tv_sec = 0; tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); - 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 f025b5f8..fcd21b15 100644 --- a/absl/base/internal/spinlock_posix.inc +++ b/absl/base/internal/spinlock_posix.inc @@ -15,10 +15,11 @@ // This file is a Posix-specific part of spinlock_wait.cc #include <sched.h> + #include <atomic> #include <ctime> -#include <cerrno> +#include "absl/base/internal/errno_saver.h" #include "absl/base/internal/scheduling_mode.h" #include "absl/base/port.h" @@ -27,7 +28,7 @@ extern "C" { ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop, absl::base_internal::SchedulingMode /* mode */) { - int save_errno = errno; + absl::base_internal::ErrnoSaver errno_saver; if (loop == 0) { } else if (loop == 1) { sched_yield(); @@ -37,7 +38,6 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); nanosleep(&tm, nullptr); } - errno = save_errno; } ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake( diff --git a/absl/base/internal/spinlock_wait.cc b/absl/base/internal/spinlock_wait.cc index 60a85196..fa824be1 100644 --- a/absl/base/internal/spinlock_wait.cc +++ b/absl/base/internal/spinlock_wait.cc @@ -32,7 +32,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // See spinlock_wait.h for spec. @@ -77,5 +77,5 @@ int SpinLockSuggestedDelayNS(int loop) { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h index 7e139de0..169bc749 100644 --- a/absl/base/internal/spinlock_wait.h +++ b/absl/base/internal/spinlock_wait.h @@ -24,7 +24,7 @@ #include "absl/base/internal/scheduling_mode.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // SpinLockWait() waits until it can perform one of several transitions from @@ -63,7 +63,7 @@ void SpinLockDelay(std::atomic<uint32_t> *w, uint32_t value, int loop, int SpinLockSuggestedDelayNS(int loop); } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // In some build configurations we pass --detect-odr-violations to the diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 0d5cf821..a0930e97 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -17,7 +17,6 @@ #include "absl/base/attributes.h" #ifdef _WIN32 -#include <shlwapi.h> #include <windows.h> #else #include <fcntl.h> @@ -56,13 +55,9 @@ #include "absl/base/internal/unscaledcycleclock.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { -static once_flag init_system_info_once; -static int num_cpus = 0; -static double nominal_cpu_frequency = 1.0; // 0.0 might be dangerous. - static int GetNumCPUs() { #if defined(__myriad2__) return 1; @@ -77,14 +72,23 @@ static int GetNumCPUs() { #if defined(_WIN32) static double GetNominalCPUFrequency() { - DWORD data; - DWORD data_size = sizeof(data); - #pragma comment(lib, "shlwapi.lib") // For SHGetValue(). - if (SUCCEEDED( - SHGetValueA(HKEY_LOCAL_MACHINE, - "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", - "~MHz", nullptr, &data, &data_size))) { - return data * 1e6; // Value is MHz. +#pragma comment(lib, "advapi32.lib") // For Reg* functions. + HKEY key; + // Use the Reg* functions rather than the SH functions because shlwapi.dll + // pulls in gdi32.dll which makes process destruction much more costly. + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, + KEY_READ, &key) == ERROR_SUCCESS) { + DWORD type = 0; + DWORD data = 0; + DWORD data_size = sizeof(data); + auto result = RegQueryValueExA(key, "~MHz", 0, &type, + reinterpret_cast<LPBYTE>(&data), &data_size); + RegCloseKey(key); + if (result == ERROR_SUCCESS && type == REG_DWORD && + data_size == sizeof(data)) { + return data * 1e6; // Value is MHz. + } } return 1.0; } @@ -257,28 +261,34 @@ static double GetNominalCPUFrequency() { #endif -// InitializeSystemInfo() may be called before main() and before -// malloc is properly initialized, therefore this must not allocate -// memory. -static void InitializeSystemInfo() { - num_cpus = GetNumCPUs(); - nominal_cpu_frequency = GetNominalCPUFrequency(); -} +ABSL_CONST_INIT static once_flag init_num_cpus_once; +ABSL_CONST_INIT static int num_cpus = 0; +// NumCPUs() may be called before main() and before malloc is properly +// initialized, therefore this must not allocate memory. int NumCPUs() { - base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo); + base_internal::LowLevelCallOnce( + &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); }); return num_cpus; } +// A default frequency of 0.0 might be dangerous if it is used in division. +ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once; +ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0; + +// NominalCPUFrequency() may be called before main() and before malloc is +// properly initialized, therefore this must not allocate memory. double NominalCPUFrequency() { - base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo); + base_internal::LowLevelCallOnce( + &init_nominal_cpu_frequency_once, + []() { nominal_cpu_frequency = GetNominalCPUFrequency(); }); return nominal_cpu_frequency; } #if defined(_WIN32) pid_t GetTID() { - return GetCurrentThreadId(); + return pid_t{GetCurrentThreadId()}; } #elif defined(__linux__) @@ -402,5 +412,5 @@ pid_t GetTID() { #endif } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h index 22739aeb..7246d5dd 100644 --- a/absl/base/internal/sysinfo.h +++ b/absl/base/internal/sysinfo.h @@ -26,14 +26,14 @@ #ifndef _WIN32 #include <sys/types.h> -#else -#include <intsafe.h> #endif +#include <cstdint> + #include "absl/base/port.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // Nominal core processor cycles per second of each processor. This is _not_ @@ -52,14 +52,15 @@ int NumCPUs(); // On Linux, you may send a signal to the resulting ID with kill(). However, // it is recommended for portability that you use pthread_kill() instead. #ifdef _WIN32 -// On Windows, process id and thread id are of the same type according to -// the return types of GetProcessId() and GetThreadId() are both DWORD. -using pid_t = DWORD; +// On Windows, process id and thread id are of the same type according to the +// return types of GetProcessId() and GetThreadId() are both DWORD, an unsigned +// 32-bit type. +using pid_t = uint32_t; #endif pid_t GetTID(); } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_SYSINFO_H_ diff --git a/absl/base/internal/sysinfo_test.cc b/absl/base/internal/sysinfo_test.cc index c8323e52..fa8b88b1 100644 --- a/absl/base/internal/sysinfo_test.cc +++ b/absl/base/internal/sysinfo_test.cc @@ -28,7 +28,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { namespace { @@ -59,8 +59,8 @@ TEST(SysinfoTest, GetTID) { #endif // Test that TIDs are unique to each thread. // Uses a few loops to exercise implementations that reallocate IDs. - for (int i = 0; i < 32; ++i) { - constexpr int kNumThreads = 64; + for (int i = 0; i < 10; ++i) { + constexpr int kNumThreads = 10; Barrier all_threads_done(kNumThreads); std::vector<std::thread> threads; @@ -96,5 +96,5 @@ TEST(SysinfoTest, LinuxGetTID) { } // namespace } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index dbec326a..d63a04ae 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -28,7 +28,7 @@ #include "absl/base/internal/spinlock.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { #if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11 @@ -56,7 +56,12 @@ void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) { #ifdef __GNUC__ __attribute__((visibility("protected"))) #endif // __GNUC__ - ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr; +#if ABSL_PER_THREAD_TLS +// Prefer __thread to thread_local as benchmarks indicate it is a bit faster. +ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr = nullptr; +#elif defined(ABSL_HAVE_THREAD_LOCAL) +thread_local ThreadIdentity* thread_identity_ptr = nullptr; +#endif // ABSL_PER_THREAD_TLS #endif // TLS or CPP11 void SetCurrentThreadIdentity( @@ -70,8 +75,8 @@ void SetCurrentThreadIdentity( absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey, reclaimer); -#ifdef __EMSCRIPTEN__ - // Emscripten PThread implementation does not support signals. +#if defined(__EMSCRIPTEN__) || defined(__MINGW32__) + // Emscripten and MinGW pthread implementations does not support signals. // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html // for more information. pthread_setspecific(thread_identity_pthread_key, @@ -90,7 +95,7 @@ void SetCurrentThreadIdentity( pthread_setspecific(thread_identity_pthread_key, reinterpret_cast<void*>(identity)); pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr); -#endif // !__EMSCRIPTEN__ +#endif // !__EMSCRIPTEN__ && !__MINGW32__ #elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS // NOTE: Not async-safe. But can be open-coded. @@ -108,6 +113,18 @@ void SetCurrentThreadIdentity( #endif } +#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ + ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 + +// Please see the comment on `CurrentThreadIdentityIfPresent` in +// thread_identity.h. Because DLLs cannot expose thread_local variables in +// headers, we opt for the correct-but-slower option of placing the definition +// of this function only in a translation unit inside DLL. +#if defined(ABSL_BUILD_DLL) || defined(ABSL_CONSUME_DLL) +ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } +#endif +#endif + void ClearCurrentThreadIdentity() { #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 @@ -131,5 +148,5 @@ ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index bb5aa074..ceb109b4 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -30,10 +30,11 @@ #include <atomic> #include <cstdint> +#include "absl/base/config.h" #include "absl/base/internal/per_thread_tls.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN struct SynchLocksHeld; struct SynchWaitParams; @@ -209,7 +210,7 @@ void ClearCurrentThreadIdentity(); #error ABSL_THREAD_IDENTITY_MODE cannot be direcly set #elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE) #define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE -#elif defined(_WIN32) +#elif defined(_WIN32) && !defined(__MINGW32__) #define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 #elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \ (__GOOGLE_GRTE_VERSION__ >= 20140228L) @@ -225,11 +226,26 @@ void ClearCurrentThreadIdentity(); #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 -extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr; +#if ABSL_PER_THREAD_TLS +ABSL_CONST_INIT extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* + thread_identity_ptr; +#elif defined(ABSL_HAVE_THREAD_LOCAL) +ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr; +#else +#error Thread-local storage not detected on this platform +#endif +// thread_local variables cannot be in headers exposed by DLLs. However, it is +// important for performance reasons in general that +// `CurrentThreadIdentityIfPresent` be inlined. This is not possible across a +// DLL boundary so, with DLLs, we opt to have the function not be inlined. Note +// that `CurrentThreadIdentityIfPresent` is declared above so we can exclude +// this entire inline definition when compiling as a DLL. +#if !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL) inline ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } +#endif #elif ABSL_THREAD_IDENTITY_MODE != \ ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC @@ -237,7 +253,7 @@ inline ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc index 8de6d9eb..3685779c 100644 --- a/absl/base/internal/thread_identity_test.cc +++ b/absl/base/internal/thread_identity_test.cc @@ -25,7 +25,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { namespace { @@ -124,5 +124,5 @@ TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) { } // namespace } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc index bee404d4..c055f75d 100644 --- a/absl/base/internal/throw_delegate.cc +++ b/absl/base/internal/throw_delegate.cc @@ -22,7 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { namespace { @@ -104,5 +104,5 @@ void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); } void ThrowStdBadAlloc() { Throw(std::bad_alloc()); } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/throw_delegate.h b/absl/base/internal/throw_delegate.h index 60ed4f32..075f5272 100644 --- a/absl/base/internal/throw_delegate.h +++ b/absl/base/internal/throw_delegate.h @@ -19,8 +19,10 @@ #include <string> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // Helper functions that allow throwing exceptions consistently from anywhere. @@ -67,7 +69,7 @@ namespace base_internal { // [[noreturn]] void ThrowStdBadArrayNewLength(); } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h index a709a446..6be56c86 100644 --- a/absl/base/internal/unaligned_access.h +++ b/absl/base/internal/unaligned_access.h @@ -18,9 +18,11 @@ #define ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ #include <string.h> + #include <cstdint> #include "absl/base/attributes.h" +#include "absl/base/config.h" // unaligned APIs @@ -56,7 +58,7 @@ void __sanitizer_unaligned_store64(void *p, uint64_t v); } // extern "C" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { inline uint16_t UnalignedLoad16(const void *p) { @@ -84,7 +86,7 @@ inline void UnalignedStore64(void *p, uint64_t v) { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ @@ -104,7 +106,7 @@ inline void UnalignedStore64(void *p, uint64_t v) { #else namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { inline uint16_t UnalignedLoad16(const void *p) { @@ -132,7 +134,7 @@ inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); } inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index 29a927d3..f1e7bbef 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -21,13 +21,18 @@ #endif #if defined(__powerpc__) || defined(__ppc__) +#ifdef __GLIBC__ #include <sys/platform/ppc.h> +#elif defined(__FreeBSD__) +#include <sys/sysctl.h> +#include <sys/types.h> +#endif #endif #include "absl/base/internal/sysinfo.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { #if defined(__i386__) @@ -57,11 +62,43 @@ double UnscaledCycleClock::Frequency() { #elif defined(__powerpc__) || defined(__ppc__) int64_t UnscaledCycleClock::Now() { +#ifdef __GLIBC__ return __ppc_get_timebase(); +#else +#ifdef __powerpc64__ + int64_t tbr; + asm volatile("mfspr %0, 268" : "=r"(tbr)); + return tbr; +#else + int32_t tbu, tbl, tmp; + asm volatile( + "0:\n" + "mftbu %[hi32]\n" + "mftb %[lo32]\n" + "mftbu %[tmp]\n" + "cmpw %[tmp],%[hi32]\n" + "bne 0b\n" + : [ hi32 ] "=r"(tbu), [ lo32 ] "=r"(tbl), [ tmp ] "=r"(tmp)); + return (static_cast<int64_t>(tbu) << 32) | tbl; +#endif +#endif } double UnscaledCycleClock::Frequency() { +#ifdef __GLIBC__ return __ppc_get_timebase_freq(); +#elif defined(__FreeBSD__) + static once_flag init_timebase_frequency_once; + static double timebase_frequency = 0.0; + base_internal::LowLevelCallOnce(&init_timebase_frequency_once, [&]() { + size_t length = sizeof(timebase_frequency); + sysctlbyname("kern.timecounter.tc.timebase.frequency", &timebase_frequency, + &length, nullptr, 0); + }); + return timebase_frequency; +#else +#error Must implement UnscaledCycleClock::Frequency() +#endif } #elif defined(__aarch64__) @@ -97,7 +134,7 @@ double UnscaledCycleClock::Frequency() { #endif } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index e6fc9103..cdce9bf8 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -86,7 +86,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { class UnscaledCycleClockWrapperForGetCurrentTime; } // namespace time_internal @@ -116,7 +116,7 @@ class UnscaledCycleClock { }; } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index 685501a4..6aa613c9 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc @@ -25,7 +25,7 @@ #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { namespace { @@ -219,5 +219,5 @@ TEST(InvokeTest, SfinaeFriendly) { } // namespace } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/log_severity.cc b/absl/base/log_severity.cc index 8109da19..72312afd 100644 --- a/absl/base/log_severity.cc +++ b/absl/base/log_severity.cc @@ -17,11 +17,11 @@ #include <ostream> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN std::ostream& operator<<(std::ostream& os, absl::LogSeverity s) { if (s == absl::NormalizeLogSeverity(s)) return os << absl::LogSeverityName(s); return os << "absl::LogSeverity(" << static_cast<int>(s) << ")"; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h index 45f79cca..65a3b166 100644 --- a/absl/base/log_severity.h +++ b/absl/base/log_severity.h @@ -19,13 +19,53 @@ #include <ostream> #include "absl/base/attributes.h" +#include "absl/base/config.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN -// Four severity levels are defined. Logging APIs should terminate the program +// absl::LogSeverity +// +// Four severity levels are defined. Logging APIs should terminate the program // when a message is logged at severity `kFatal`; the other levels have no // special semantics. +// +// Values other than the four defined levels (e.g. produced by `static_cast`) +// are valid, but their semantics when passed to a function, macro, or flag +// depend on the function, macro, or flag. The usual behavior is to normalize +// such values to a defined severity level, however in some cases values other +// than the defined levels are useful for comparison. +// +// Exmaple: +// +// // Effectively disables all logging: +// SetMinLogLevel(static_cast<absl::LogSeverity>(100)); +// +// Abseil flags may be defined with type `LogSeverity`. Dependency layering +// constraints require that the `AbslParseFlag()` overload be declared and +// defined in the flags library itself rather than here. The `AbslUnparseFlag()` +// overload is defined there as well for consistency. +// +// absl::LogSeverity Flag String Representation +// +// An `absl::LogSeverity` has a string representation used for parsing +// command-line flags based on the enumerator name (e.g. `kFatal`) or +// its unprefixed name (without the `k`) in any case-insensitive form. (E.g. +// "FATAL", "fatal" or "Fatal" are all valid.) Unparsing such flags produces an +// unprefixed string representation in all caps (e.g. "FATAL") or an integer. +// +// Additionally, the parser accepts arbitrary integers (as if the type were +// `int`). +// +// Examples: +// +// --my_log_level=kInfo +// --my_log_level=INFO +// --my_log_level=info +// --my_log_level=0 +// +// Unparsing a flag produces the same result as `absl::LogSeverityName()` for +// the standard levels and a base-ten integer otherwise. enum class LogSeverity : int { kInfo = 0, kWarning = 1, @@ -33,6 +73,8 @@ enum class LogSeverity : int { kFatal = 3, }; +// LogSeverities() +// // Returns an iterable of all standard `absl::LogSeverity` values, ordered from // least to most severe. constexpr std::array<absl::LogSeverity, 4> LogSeverities() { @@ -40,8 +82,10 @@ constexpr std::array<absl::LogSeverity, 4> LogSeverities() { absl::LogSeverity::kError, absl::LogSeverity::kFatal}}; } +// LogSeverityName() +// // Returns the all-caps string representation (e.g. "INFO") of the specified -// severity level if it is one of the normal levels and "UNKNOWN" otherwise. +// severity level if it is one of the standard levels and "UNKNOWN" otherwise. constexpr const char* LogSeverityName(absl::LogSeverity s) { return s == absl::LogSeverity::kInfo ? "INFO" @@ -52,6 +96,8 @@ constexpr const char* LogSeverityName(absl::LogSeverity s) { : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN"; } +// NormalizeLogSeverity() +// // Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal` // normalize to `kError` (**NOT** `kFatal`). constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) { @@ -60,14 +106,16 @@ constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) { : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s; } constexpr absl::LogSeverity NormalizeLogSeverity(int s) { - return NormalizeLogSeverity(static_cast<absl::LogSeverity>(s)); + return absl::NormalizeLogSeverity(static_cast<absl::LogSeverity>(s)); } +// operator<< +// // The exact representation of a streamed `absl::LogSeverity` is deliberately // unspecified; do not rely on it. std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ diff --git a/absl/base/log_severity_test.cc b/absl/base/log_severity_test.cc index 1de2d101..2302aa12 100644 --- a/absl/base/log_severity_test.cc +++ b/absl/base/log_severity_test.cc @@ -14,14 +14,26 @@ #include "absl/base/log_severity.h" +#include <cstdint> +#include <ios> +#include <limits> +#include <ostream> #include <sstream> #include <string> +#include <tuple> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/flags/internal/flag.h" +#include "absl/flags/marshalling.h" +#include "absl/strings/str_cat.h" namespace { -using testing::Eq; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; +using ::testing::TestWithParam; +using ::testing::Values; std::string StreamHelper(absl::LogSeverity value) { std::ostringstream stream; @@ -40,4 +52,153 @@ TEST(StreamTest, Works) { Eq("absl::LogSeverity(4)")); } +static_assert( + absl::flags_internal::IsAtomicFlagTypeTrait<absl::LogSeverity>::value, + "Flags of type absl::LogSeverity ought to be lock-free."); + +using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>; +INSTANTIATE_TEST_SUITE_P( + Instantiation, ParseFlagFromOutOfRangeIntegerTest, + Values(static_cast<int64_t>(std::numeric_limits<int>::min()) - 1, + static_cast<int64_t>(std::numeric_limits<int>::max()) + 1)); +TEST_P(ParseFlagFromOutOfRangeIntegerTest, ReturnsError) { + const std::string to_parse = absl::StrCat(GetParam()); + absl::LogSeverity value; + std::string error; + EXPECT_THAT(absl::ParseFlag(to_parse, &value, &error), IsFalse()) << value; +} + +using ParseFlagFromAlmostOutOfRangeIntegerTest = TestWithParam<int>; +INSTANTIATE_TEST_SUITE_P(Instantiation, + ParseFlagFromAlmostOutOfRangeIntegerTest, + Values(std::numeric_limits<int>::min(), + std::numeric_limits<int>::max())); +TEST_P(ParseFlagFromAlmostOutOfRangeIntegerTest, YieldsExpectedValue) { + const auto expected = static_cast<absl::LogSeverity>(GetParam()); + const std::string to_parse = absl::StrCat(GetParam()); + absl::LogSeverity value; + std::string error; + ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error; + EXPECT_THAT(value, Eq(expected)); +} + +using ParseFlagFromIntegerMatchingEnumeratorTest = + TestWithParam<std::tuple<absl::string_view, absl::LogSeverity>>; +INSTANTIATE_TEST_SUITE_P( + Instantiation, ParseFlagFromIntegerMatchingEnumeratorTest, + Values(std::make_tuple("0", absl::LogSeverity::kInfo), + std::make_tuple(" 0", absl::LogSeverity::kInfo), + std::make_tuple("-0", absl::LogSeverity::kInfo), + std::make_tuple("+0", absl::LogSeverity::kInfo), + std::make_tuple("00", absl::LogSeverity::kInfo), + std::make_tuple("0 ", absl::LogSeverity::kInfo), + std::make_tuple("0x0", absl::LogSeverity::kInfo), + std::make_tuple("1", absl::LogSeverity::kWarning), + std::make_tuple("+1", absl::LogSeverity::kWarning), + std::make_tuple("2", absl::LogSeverity::kError), + std::make_tuple("3", absl::LogSeverity::kFatal))); +TEST_P(ParseFlagFromIntegerMatchingEnumeratorTest, YieldsExpectedValue) { + const absl::string_view to_parse = std::get<0>(GetParam()); + const absl::LogSeverity expected = std::get<1>(GetParam()); + absl::LogSeverity value; + std::string error; + ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error; + EXPECT_THAT(value, Eq(expected)); +} + +using ParseFlagFromOtherIntegerTest = + TestWithParam<std::tuple<absl::string_view, int>>; +INSTANTIATE_TEST_SUITE_P(Instantiation, ParseFlagFromOtherIntegerTest, + Values(std::make_tuple("-1", -1), + std::make_tuple("4", 4), + std::make_tuple("010", 10), + std::make_tuple("0x10", 16))); +TEST_P(ParseFlagFromOtherIntegerTest, YieldsExpectedValue) { + const absl::string_view to_parse = std::get<0>(GetParam()); + const auto expected = static_cast<absl::LogSeverity>(std::get<1>(GetParam())); + absl::LogSeverity value; + std::string error; + ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error; + EXPECT_THAT(value, Eq(expected)); +} + +using ParseFlagFromEnumeratorTest = + TestWithParam<std::tuple<absl::string_view, absl::LogSeverity>>; +INSTANTIATE_TEST_SUITE_P( + Instantiation, ParseFlagFromEnumeratorTest, + Values(std::make_tuple("INFO", absl::LogSeverity::kInfo), + std::make_tuple("info", absl::LogSeverity::kInfo), + std::make_tuple("kInfo", absl::LogSeverity::kInfo), + std::make_tuple("iNfO", absl::LogSeverity::kInfo), + std::make_tuple("kInFo", absl::LogSeverity::kInfo), + std::make_tuple("WARNING", absl::LogSeverity::kWarning), + std::make_tuple("warning", absl::LogSeverity::kWarning), + std::make_tuple("kWarning", absl::LogSeverity::kWarning), + std::make_tuple("WaRnInG", absl::LogSeverity::kWarning), + std::make_tuple("KwArNiNg", absl::LogSeverity::kWarning), + std::make_tuple("ERROR", absl::LogSeverity::kError), + std::make_tuple("error", absl::LogSeverity::kError), + std::make_tuple("kError", absl::LogSeverity::kError), + std::make_tuple("eRrOr", absl::LogSeverity::kError), + std::make_tuple("kErRoR", absl::LogSeverity::kError), + std::make_tuple("FATAL", absl::LogSeverity::kFatal), + std::make_tuple("fatal", absl::LogSeverity::kFatal), + std::make_tuple("kFatal", absl::LogSeverity::kFatal), + std::make_tuple("FaTaL", absl::LogSeverity::kFatal), + std::make_tuple("KfAtAl", absl::LogSeverity::kFatal))); +TEST_P(ParseFlagFromEnumeratorTest, YieldsExpectedValue) { + const absl::string_view to_parse = std::get<0>(GetParam()); + const absl::LogSeverity expected = std::get<1>(GetParam()); + absl::LogSeverity value; + std::string error; + ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error; + EXPECT_THAT(value, Eq(expected)); +} + +using ParseFlagFromGarbageTest = TestWithParam<absl::string_view>; +INSTANTIATE_TEST_SUITE_P(Instantiation, ParseFlagFromGarbageTest, + Values("", "\0", " ", "garbage", "kkinfo", "I")); +TEST_P(ParseFlagFromGarbageTest, ReturnsError) { + const absl::string_view to_parse = GetParam(); + absl::LogSeverity value; + std::string error; + EXPECT_THAT(absl::ParseFlag(to_parse, &value, &error), IsFalse()) << value; +} + +using UnparseFlagToEnumeratorTest = + TestWithParam<std::tuple<absl::LogSeverity, absl::string_view>>; +INSTANTIATE_TEST_SUITE_P( + Instantiation, UnparseFlagToEnumeratorTest, + Values(std::make_tuple(absl::LogSeverity::kInfo, "INFO"), + std::make_tuple(absl::LogSeverity::kWarning, "WARNING"), + std::make_tuple(absl::LogSeverity::kError, "ERROR"), + std::make_tuple(absl::LogSeverity::kFatal, "FATAL"))); +TEST_P(UnparseFlagToEnumeratorTest, ReturnsExpectedValueAndRoundTrips) { + const absl::LogSeverity to_unparse = std::get<0>(GetParam()); + const absl::string_view expected = std::get<1>(GetParam()); + const std::string stringified_value = absl::UnparseFlag(to_unparse); + EXPECT_THAT(stringified_value, Eq(expected)); + absl::LogSeverity reparsed_value; + std::string error; + EXPECT_THAT(absl::ParseFlag(stringified_value, &reparsed_value, &error), + IsTrue()); + EXPECT_THAT(reparsed_value, Eq(to_unparse)); +} + +using UnparseFlagToOtherIntegerTest = TestWithParam<int>; +INSTANTIATE_TEST_SUITE_P(Instantiation, UnparseFlagToOtherIntegerTest, + Values(std::numeric_limits<int>::min(), -1, 4, + std::numeric_limits<int>::max())); +TEST_P(UnparseFlagToOtherIntegerTest, ReturnsExpectedValueAndRoundTrips) { + const absl::LogSeverity to_unparse = + static_cast<absl::LogSeverity>(GetParam()); + const std::string expected = absl::StrCat(GetParam()); + const std::string stringified_value = absl::UnparseFlag(to_unparse); + EXPECT_THAT(stringified_value, Eq(expected)); + absl::LogSeverity reparsed_value; + std::string error; + EXPECT_THAT(absl::ParseFlag(stringified_value, &reparsed_value, &error), + IsTrue()); + EXPECT_THAT(reparsed_value, Eq(to_unparse)); +} } // namespace diff --git a/absl/base/macros.h b/absl/base/macros.h index 3121088a..547f93ba 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h @@ -31,6 +31,7 @@ #include <cassert> #include <cstddef> +#include "absl/base/attributes.h" #include "absl/base/optimization.h" #include "absl/base/port.h" @@ -43,14 +44,14 @@ (sizeof(::absl::macros_internal::ArraySizeHelper(array))) namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace macros_internal { // Note: this internal template function declaration is used by ABSL_ARRAYSIZE. // The function doesn't need a definition, as we only use its type. template <typename T, size_t N> auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; } // namespace macros_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // kLinkerInitialized @@ -74,13 +75,13 @@ auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; // // Invocation // static MyClass my_global(absl::base_internal::kLinkerInitialized); namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { enum LinkerInitialized { kLinkerInitialized = 0, }; } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // ABSL_FALLTHROUGH_INTENDED @@ -111,7 +112,7 @@ enum LinkerInitialized { // when performing switch labels fall-through diagnostic // (`-Wimplicit-fallthrough`). See clang documentation on language extensions // for details: -// http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough +// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough // // When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro // has no effect on diagnostics. In any case this macro has no effect on runtime @@ -141,10 +142,15 @@ enum LinkerInitialized { // declarations. The macro argument is used as a custom diagnostic message (e.g. // suggestion of a better alternative). // -// Example: +// Examples: // // class ABSL_DEPRECATED("Use Bar instead") Foo {...}; -// ABSL_DEPRECATED("Use Baz instead") void Bar() {...} +// +// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} +// +// template <typename T> +// ABSL_DEPRECATED("Use DoThat() instead") +// void DoThis(); // // Every usage of a deprecated entity will trigger a warning when compiled with // clang's `-Wdeprecated-declarations` option. This option is turned off by @@ -162,7 +168,7 @@ enum LinkerInitialized { // Used on a function overload to trap bad calls: any call that matches the // overload will cause a compile-time error. This macro uses a clang-specific // "enable_if" attribute, as described at -// http://clang.llvm.org/docs/AttributeReference.html#enable-if +// https://clang.llvm.org/docs/AttributeReference.html#enable-if // // Overloads which use this macro should be bracketed by // `#ifdef ABSL_BAD_CALL_IF`. @@ -175,12 +181,9 @@ enum LinkerInitialized { // ABSL_BAD_CALL_IF(c <= -1 || c > 255, // "'c' must have the value of an unsigned char or EOF"); // #endif // ABSL_BAD_CALL_IF - -#if defined(__clang__) -# if __has_attribute(enable_if) -# define ABSL_BAD_CALL_IF(expr, msg) \ - __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) -# endif +#if ABSL_HAVE_ATTRIBUTE(enable_if) +#define ABSL_BAD_CALL_IF(expr, msg) \ + __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) #endif // ABSL_ASSERT() diff --git a/absl/base/optimization.h b/absl/base/optimization.h index 0dcbef32..646523b3 100644 --- a/absl/base/optimization.h +++ b/absl/base/optimization.h @@ -172,7 +172,7 @@ #if ABSL_HAVE_BUILTIN(__builtin_expect) || \ (defined(__GNUC__) && !defined(__clang__)) #define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0)) -#define ABSL_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true)) #else #define ABSL_PREDICT_FALSE(x) (x) #define ABSL_PREDICT_TRUE(x) (x) diff --git a/absl/base/options.h b/absl/base/options.h new file mode 100644 index 00000000..50f26e24 --- /dev/null +++ b/absl/base/options.h @@ -0,0 +1,211 @@ +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: options.h +// ----------------------------------------------------------------------------- +// +// This file contains Abseil configuration options for setting specific +// implementations instead of letting Abseil determine which implementation to +// use at compile-time. Setting these options may be useful for package or build +// managers who wish to guarantee ABI stability within binary builds (which are +// otherwise difficult to enforce). +// +// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS: It is important that +// maintainers of package managers who wish to package Abseil read and +// understand this file! *** +// +// Abseil contains a number of possible configuration endpoints, based on +// parameters such as the detected platform, language version, or command-line +// flags used to invoke the underlying binary. As is the case with all +// libraries, binaries which contain Abseil code must ensure that separate +// packages use the same compiled copy of Abseil to avoid a diamond dependency +// problem, which can occur if two packages built with different Abseil +// configuration settings are linked together. Diamond dependency problems in +// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in +// linker errors), or undefined behavior (resulting in crashes). +// +// Diamond dependency problems can be avoided if all packages utilize the same +// exact version of Abseil. Building from source code with the same compilation +// parameters is the easiest way to avoid such dependency problems. However, for +// package managers who cannot control such compilation parameters, we are +// providing the file to allow you to inject ABI (Application Binary Interface) +// stability across builds. Settings options in this file will neither change +// API nor ABI, providing a stable copy of Abseil between packages. +// +// Care must be taken to keep options within these configurations isolated +// from any other dynamic settings, such as command-line flags which could alter +// these options. This file is provided specifically to help build and package +// managers provide a stable copy of Abseil within their libraries and binaries; +// other developers should not have need to alter the contents of this file. +// +// ----------------------------------------------------------------------------- +// Usage +// ----------------------------------------------------------------------------- +// +// For any particular package release, set the appropriate definitions within +// this file to whatever value makes the most sense for your package(s). Note +// that, by default, most of these options, at the moment, affect the +// implementation of types; future options may affect other implementation +// details. +// +// NOTE: the defaults within this file all assume that Abseil can select the +// proper Abseil implementation at compile-time, which will not be sufficient +// to guarantee ABI stability to package managers. + +// Include a standard library header to allow configuration based on the +// standard library in use. +#ifdef __cplusplus +#include <ciso646> +#endif + +// ----------------------------------------------------------------------------- +// Type Compatibility Options +// ----------------------------------------------------------------------------- +// +// ABSL_OPTION_USE_STD_ANY +// +// This option controls whether absl::any is implemented as an alias to +// std::any, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::any. This requires that all code +// using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::any is available. This option is +// useful when you are building your entire program, including all of its +// dependencies, from source. It should not be used otherwise -- for example, +// if you are distributing Abseil in a binary package manager -- since in +// mode 2, absl::any will name a different type, with a different mangled name +// and binary layout, depending on the compiler flags passed by the end user. +// For more info, see https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY. + +#define ABSL_OPTION_USE_STD_ANY 2 + + +// ABSL_OPTION_USE_STD_OPTIONAL +// +// This option controls whether absl::optional is implemented as an alias to +// std::optional, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::optional. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::optional is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::optional will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. + +// User code should not inspect this macro. To check in the preprocessor if +// absl::optional is a typedef of std::optional, use the feature macro +// ABSL_USES_STD_OPTIONAL. + +#define ABSL_OPTION_USE_STD_OPTIONAL 2 + + +// ABSL_OPTION_USE_STD_STRING_VIEW +// +// This option controls whether absl::string_view is implemented as an alias to +// std::string_view, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::string_view. This requires that +// all code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::string_view is available. This +// option is useful when you are building your program from source. It should +// not be used otherwise -- for example, if you are distributing Abseil in a +// binary package manager -- since in mode 2, absl::string_view will name a +// different type, with a different mangled name and binary layout, depending on +// the compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::string_view is a typedef of std::string_view, use the feature macro +// ABSL_USES_STD_STRING_VIEW. + +#define ABSL_OPTION_USE_STD_STRING_VIEW 2 + +// ABSL_OPTION_USE_STD_VARIANT +// +// This option controls whether absl::variant is implemented as an alias to +// std::variant, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::variant. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::variant is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::variant will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::variant is a typedef of std::variant, use the feature macro +// ABSL_USES_STD_VARIANT. + +#define ABSL_OPTION_USE_STD_VARIANT 2 + + +// ABSL_OPTION_USE_INLINE_NAMESPACE +// ABSL_OPTION_INLINE_NAMESPACE_NAME +// +// These options controls whether all entities in the absl namespace are +// contained within an inner inline namespace. This does not affect the +// user-visible API of Abseil, but it changes the mangled names of all symbols. +// +// This can be useful as a version tag if you are distributing Abseil in +// precompiled form. This will prevent a binary library build of Abseil with +// one inline namespace being used with headers configured with a different +// inline namespace name. Binary packagers are reminded that Abseil does not +// guarantee any ABI stability in Abseil, so any update of Abseil or +// configuration change in such a binary package should be combined with a +// new, unique value for the inline namespace name. +// +// A value of 0 means not to use inline namespaces. +// +// A value of 1 means to use an inline namespace with the given name inside +// namespace absl. If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also +// be changed to a new, unique identifier name. In particular "head" is not +// allowed. + +#define ABSL_OPTION_USE_INLINE_NAMESPACE 1 +#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_2020_02_25 + +#endif // ABSL_BASE_OPTIONS_H_ diff --git a/absl/base/policy_checks.h b/absl/base/policy_checks.h index 699fb1a2..4dfa49e5 100644 --- a/absl/base/policy_checks.h +++ b/absl/base/policy_checks.h @@ -82,16 +82,6 @@ // Standard Library Check // ----------------------------------------------------------------------------- -// We have chosen glibc 2.12 as the minimum as it was tagged for release -// in May, 2010 and includes some functionality used in Google software -// (for instance pthread_setname_np): -// https://sourceware.org/ml/libc-alpha/2010-05/msg00000.html -#if defined(__GLIBC__) && defined(__GLIBC_PREREQ) -#if !__GLIBC_PREREQ(2, 12) -#error "Minimum required version of glibc is 2.12." -#endif -#endif - #if defined(_STLPORT_VERSION) #error "STLPort is not supported." #endif diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc index 06860e71..08f61ba8 100644 --- a/absl/base/spinlock_test_common.cc +++ b/absl/base/spinlock_test_common.cc @@ -36,7 +36,7 @@ constexpr int32_t kNumThreads = 10; constexpr int32_t kIters = 1000; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // This is defined outside of anonymous namespace so that it can be @@ -267,5 +267,5 @@ TEST(SpinLockWithThreads, DoesNotDeadlock) { } // namespace } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h index f3e96589..5f51c0c2 100644 --- a/absl/base/thread_annotations.h +++ b/absl/base/thread_annotations.h @@ -34,6 +34,7 @@ #ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_ #define ABSL_BASE_THREAD_ANNOTATIONS_H_ +#include "absl/base/config.h" // TODO(mbonadei): Remove after the backward compatibility period. #include "absl/base/internal/thread_annotations.h" // IWYU pragma: export @@ -256,7 +257,7 @@ #define ABSL_TS_UNCHECKED_READ(x) absl::base_internal::ts_unchecked_read(x) namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace base_internal { // Takes a reference to a guarded data member, and returns an unguarded @@ -273,7 +274,7 @@ inline T& ts_unchecked_read(T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { } } // namespace base_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_THREAD_ANNOTATIONS_H_ diff --git a/absl/base/throw_delegate_test.cc b/absl/base/throw_delegate_test.cc index a74dd3cd..5ba4ce55 100644 --- a/absl/base/throw_delegate_test.cc +++ b/absl/base/throw_delegate_test.cc @@ -18,6 +18,7 @@ #include <new> #include <stdexcept> +#include "absl/base/config.h" #include "gtest/gtest.h" namespace { @@ -38,31 +39,43 @@ constexpr const char* what_arg = "The quick brown fox jumps over the lazy dog"; template <typename E> void ExpectThrowChar(void (*f)(const char*)) { +#ifdef ABSL_HAVE_EXCEPTIONS try { f(what_arg); FAIL() << "Didn't throw"; } catch (const E& e) { EXPECT_STREQ(e.what(), what_arg); } +#else + EXPECT_DEATH_IF_SUPPORTED(f(what_arg), what_arg); +#endif } template <typename E> void ExpectThrowString(void (*f)(const std::string&)) { +#ifdef ABSL_HAVE_EXCEPTIONS try { f(what_arg); FAIL() << "Didn't throw"; } catch (const E& e) { EXPECT_STREQ(e.what(), what_arg); } +#else + EXPECT_DEATH_IF_SUPPORTED(f(what_arg), what_arg); +#endif } template <typename E> void ExpectThrowNoWhat(void (*f)()) { +#ifdef ABSL_HAVE_EXCEPTIONS try { f(); FAIL() << "Didn't throw"; } catch (const E& e) { } +#else + EXPECT_DEATH_IF_SUPPORTED(f(), ""); +#endif } TEST(ThrowHelper, Test) { diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 9e2a5b1e..f2217140 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -14,12 +14,11 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", "ABSL_DEFAULT_LINKOPTS", - "ABSL_EXCEPTIONS_FLAG", - "ABSL_EXCEPTIONS_FLAG_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -71,20 +70,6 @@ cc_library( cc_test( name = "fixed_array_test", srcs = ["fixed_array_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, - deps = [ - ":fixed_array", - "//absl/base:exception_testing", - "//absl/hash:hash_testing", - "//absl/memory", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "fixed_array_test_noexceptions", - srcs = ["fixed_array_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ @@ -99,10 +84,11 @@ cc_test( cc_test( name = "fixed_array_exception_safety_test", srcs = ["fixed_array_exception_safety_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":fixed_array", + "//absl/base:config", "//absl/base:exception_safety_testing", "@com_google_googletest//:gtest_main", ], @@ -155,39 +141,21 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], + deps = ["//absl/base:config"], ) cc_test( name = "inlined_vector_test", srcs = ["inlined_vector_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, - deps = [ - ":counting_allocator", - ":inlined_vector", - ":test_instance_tracker", - "//absl/base", - "//absl/base:core_headers", - "//absl/base:exception_testing", - "//absl/hash:hash_testing", - "//absl/memory", - "//absl/strings", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "inlined_vector_test_noexceptions", - srcs = ["inlined_vector_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":counting_allocator", ":inlined_vector", ":test_instance_tracker", - "//absl/base", "//absl/base:core_headers", "//absl/base:exception_testing", + "//absl/base:raw_logging_internal", "//absl/hash:hash_testing", "//absl/memory", "//absl/strings", @@ -203,8 +171,8 @@ cc_test( tags = ["benchmark"], deps = [ ":inlined_vector", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/strings", "@com_github_google_benchmark//:benchmark_main", ], @@ -213,9 +181,10 @@ cc_test( cc_test( name = "inlined_vector_exception_safety_test", srcs = ["inlined_vector_exception_safety_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + copts = ABSL_TEST_COPTS, deps = [ ":inlined_vector", + "//absl/base:config", "//absl/base:exception_safety_testing", "@com_google_googletest//:gtest_main", ], @@ -447,6 +416,7 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_testing", + "//absl/memory", "//absl/meta:type_traits", "//absl/strings", ], @@ -509,6 +479,9 @@ cc_library( hdrs = ["internal/hashtable_debug_hooks.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + ], ) cc_library( @@ -524,6 +497,7 @@ cc_library( ":have_sse", "//absl/base", "//absl/base:core_headers", + "//absl/base:exponential_biased", "//absl/debugging:stacktrace", "//absl/memory", "//absl/synchronization", @@ -551,6 +525,7 @@ cc_library( hdrs = ["internal/node_hash_policy.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + deps = ["//absl/base:config"], ) cc_test( @@ -635,6 +610,7 @@ cc_test( ":raw_hash_set", "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -678,8 +654,8 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":layout", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/types:span", "@com_google_googletest//:gtest_main", ], @@ -691,6 +667,9 @@ cc_library( hdrs = ["internal/tracked.h"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + ], ) cc_library( @@ -825,3 +804,99 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "btree", + srcs = [ + "internal/btree.h", + "internal/btree_container.h", + ], + hdrs = [ + "btree_map.h", + "btree_set.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], + deps = [ + ":common", + ":compressed_tuple", + ":container_memory", + ":layout", + "//absl/base:core_headers", + "//absl/base:throw_delegate", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/types:compare", + "//absl/utility", + ], +) + +cc_library( + name = "btree_test_common", + testonly = 1, + hdrs = ["btree_test.h"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":btree", + ":flat_hash_set", + "//absl/strings", + "//absl/time", + ], +) + +cc_test( + name = "btree_test", + size = "large", + srcs = [ + "btree_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + shard_count = 10, + visibility = ["//visibility:private"], + deps = [ + ":btree", + ":btree_test_common", + ":counting_allocator", + ":test_instance_tracker", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/flags:flag", + "//absl/hash:hash_testing", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/types:compare", + "@com_google_googletest//:gtest_main", + ], +) + +cc_binary( + name = "btree_benchmark", + testonly = 1, + srcs = [ + "btree_benchmark.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":btree", + ":btree_test_common", + ":flat_hash_map", + ":flat_hash_set", + ":hashtable_debug", + "//absl/base:raw_logging_internal", + "//absl/flags:flag", + "//absl/hash", + "//absl/memory", + "//absl/strings:str_format", + "//absl/time", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 7988b12f..e702ba85 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -25,6 +25,73 @@ absl_cc_library( absl_cc_library( NAME + btree + HDRS + "btree_map.h" + "btree_set.h" + "internal/btree.h" + "internal/btree_container.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::container_common + absl::compare + absl::compressed_tuple + absl::container_memory + absl::core_headers + absl::layout + absl::memory + absl::strings + absl::throw_delegate + absl::type_traits + absl::utility +) + +absl_cc_library( + NAME + btree_test_common + hdrs + "btree_test.h" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::btree + absl::flat_hash_set + absl::strings + absl::time + TESTONLY +) + +absl_cc_test( + NAME + btree_test + SRCS + "btree_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::btree + absl::btree_test_common + absl::compare + absl::core_headers + absl::counting_allocator + absl::flags + absl::hash_testing + absl::raw_logging_internal + absl::strings + absl::test_instance_tracker + absl::type_traits + gmock_main +) + +absl_cc_library( + NAME compressed_tuple HDRS "internal/compressed_tuple.h" @@ -76,24 +143,6 @@ absl_cc_test( "fixed_array_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} - DEPS - absl::fixed_array - absl::exception_testing - absl::hash_testing - absl::memory - gmock_main -) - -absl_cc_test( - NAME - fixed_array_test_noexceptions - SRCS - "fixed_array_test.cc" - COPTS - ${ABSL_TEST_COPTS} DEPS absl::fixed_array absl::exception_testing @@ -109,11 +158,9 @@ absl_cc_test( "fixed_array_exception_safety_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::fixed_array + absl::config absl::exception_safety_testing gmock_main ) @@ -157,6 +204,8 @@ absl_cc_library( "internal/counting_allocator.h" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::config ) absl_cc_test( @@ -166,37 +215,15 @@ absl_cc_test( "inlined_vector_test.cc" COPTS ${ABSL_TEST_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_cc_test( - NAME - inlined_vector_test_noexceptions - SRCS - "inlined_vector_test.cc" - COPTS - ${ABSL_TEST_COPTS} - DEPS - absl::inlined_vector - absl::test_instance_tracker - absl::base absl::core_headers absl::exception_testing absl::hash_testing absl::memory + absl::raw_logging_internal absl::strings gmock_main ) @@ -208,11 +235,9 @@ absl_cc_test( "inlined_vector_exception_safety_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::inlined_vector + absl::config absl::exception_safety_testing gmock_main ) @@ -448,6 +473,7 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::hash_policy_testing + absl::memory absl::meta absl::strings TESTONLY @@ -514,6 +540,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base + absl::exponential_biased absl::have_sse absl::synchronization ) @@ -549,6 +576,8 @@ absl_cc_library( "internal/hashtable_debug_hooks.h" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::config PUBLIC ) @@ -568,6 +597,8 @@ absl_cc_library( "internal/node_hash_policy.h" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::config PUBLIC ) @@ -602,7 +633,7 @@ absl_cc_library( NAME container_common HDRS - "internal/commom.h" + "internal/common.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -653,6 +684,7 @@ absl_cc_test( absl::raw_hash_set absl::base absl::core_headers + absl::raw_logging_internal absl::strings gmock_main ) @@ -696,8 +728,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::layout - absl::base absl::core_headers + absl::raw_logging_internal absl::span gmock_main ) @@ -709,6 +741,8 @@ absl_cc_library( "internal/tracked.h" COPTS ${ABSL_TEST_COPTS} + DEPS + absl::config TESTONLY ) diff --git a/absl/container/btree_benchmark.cc b/absl/container/btree_benchmark.cc new file mode 100644 index 00000000..4af92f9f --- /dev/null +++ b/absl/container/btree_benchmark.cc @@ -0,0 +1,707 @@ +// 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 <stdint.h> + +#include <algorithm> +#include <functional> +#include <map> +#include <numeric> +#include <random> +#include <set> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "absl/base/internal/raw_logging.h" +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" +#include "absl/container/btree_test.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/container/internal/hashtable_debug.h" +#include "absl/flags/flag.h" +#include "absl/hash/hash.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_format.h" +#include "absl/time/time.h" +#include "benchmark/benchmark.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace { + +constexpr size_t kBenchmarkValues = 1 << 20; + +// How many times we add and remove sub-batches in one batch of *AddRem +// benchmarks. +constexpr size_t kAddRemBatchSize = 1 << 2; + +// Generates n values in the range [0, 4 * n]. +template <typename V> +std::vector<V> GenerateValues(int n) { + constexpr int kSeed = 23; + return GenerateValuesWithSeed<V>(n, 4 * n, kSeed); +} + +// Benchmark insertion of values into a container. +template <typename T> +void BM_InsertImpl(benchmark::State& state, bool sorted) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + if (sorted) { + std::sort(values.begin(), values.end()); + } + T container(values.begin(), values.end()); + + // Remove and re-insert 10% of the keys per batch. + const int batch_size = (kBenchmarkValues + 9) / 10; + while (state.KeepRunningBatch(batch_size)) { + state.PauseTiming(); + const auto i = static_cast<int>(state.iterations()); + + for (int j = i; j < i + batch_size; j++) { + int x = j % kBenchmarkValues; + container.erase(key_of_value(values[x])); + } + + state.ResumeTiming(); + + for (int j = i; j < i + batch_size; j++) { + int x = j % kBenchmarkValues; + container.insert(values[x]); + } + } +} + +template <typename T> +void BM_Insert(benchmark::State& state) { + BM_InsertImpl<T>(state, false); +} + +template <typename T> +void BM_InsertSorted(benchmark::State& state) { + BM_InsertImpl<T>(state, true); +} + +// container::insert sometimes returns a pair<iterator, bool> and sometimes +// returns an iterator (for multi- containers). +template <typename Iter> +Iter GetIterFromInsert(const std::pair<Iter, bool>& pair) { + return pair.first; +} +template <typename Iter> +Iter GetIterFromInsert(const Iter iter) { + return iter; +} + +// Benchmark insertion of values into a container at the end. +template <typename T> +void BM_InsertEnd(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + T container; + const int kSize = 10000; + for (int i = 0; i < kSize; ++i) { + container.insert(Generator<V>(kSize)(i)); + } + V v = Generator<V>(kSize)(kSize - 1); + typename T::key_type k = key_of_value(v); + + auto it = container.find(k); + while (state.KeepRunning()) { + // Repeatedly removing then adding v. + container.erase(it); + it = GetIterFromInsert(container.insert(v)); + } +} + +template <typename T> +void BM_LookupImpl(benchmark::State& state, bool sorted) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + if (sorted) { + std::sort(values.begin(), values.end()); + } + T container(values.begin(), values.end()); + + while (state.KeepRunning()) { + int idx = state.iterations() % kBenchmarkValues; + benchmark::DoNotOptimize(container.find(key_of_value(values[idx]))); + } +} + +// Benchmark lookup of values in a container. +template <typename T> +void BM_Lookup(benchmark::State& state) { + BM_LookupImpl<T>(state, false); +} + +// Benchmark lookup of values in a full container, meaning that values +// are inserted in-order to take advantage of biased insertion, which +// yields a full tree. +template <typename T> +void BM_FullLookup(benchmark::State& state) { + BM_LookupImpl<T>(state, true); +} + +// Benchmark deletion of values from a container. +template <typename T> +void BM_Delete(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + T container(values.begin(), values.end()); + + // Remove and re-insert 10% of the keys per batch. + const int batch_size = (kBenchmarkValues + 9) / 10; + while (state.KeepRunningBatch(batch_size)) { + const int i = state.iterations(); + + for (int j = i; j < i + batch_size; j++) { + int x = j % kBenchmarkValues; + container.erase(key_of_value(values[x])); + } + + state.PauseTiming(); + for (int j = i; j < i + batch_size; j++) { + int x = j % kBenchmarkValues; + container.insert(values[x]); + } + state.ResumeTiming(); + } +} + +// Benchmark deletion of multiple values from a container. +template <typename T> +void BM_DeleteRange(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + T container(values.begin(), values.end()); + + // Remove and re-insert 10% of the keys per batch. + const int batch_size = (kBenchmarkValues + 9) / 10; + while (state.KeepRunningBatch(batch_size)) { + const int i = state.iterations(); + + const int start_index = i % kBenchmarkValues; + + state.PauseTiming(); + { + std::vector<V> removed; + removed.reserve(batch_size); + auto itr = container.find(key_of_value(values[start_index])); + auto start = itr; + for (int j = 0; j < batch_size; j++) { + if (itr == container.end()) { + state.ResumeTiming(); + container.erase(start, itr); + state.PauseTiming(); + itr = container.begin(); + start = itr; + } + removed.push_back(*itr++); + } + + state.ResumeTiming(); + container.erase(start, itr); + state.PauseTiming(); + + container.insert(removed.begin(), removed.end()); + } + state.ResumeTiming(); + } +} + +// Benchmark steady-state insert (into first half of range) and remove (from +// second half of range), treating the container approximately like a queue with +// log-time access for all elements. This benchmark does not test the case where +// insertion and removal happen in the same region of the tree. This benchmark +// counts two value constructors. +template <typename T> +void BM_QueueAddRem(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance"); + + T container; + + const size_t half = kBenchmarkValues / 2; + std::vector<int> remove_keys(half); + std::vector<int> add_keys(half); + + // We want to do the exact same work repeatedly, and the benchmark can end + // after a different number of iterations depending on the speed of the + // individual run so we use a large batch size here and ensure that we do + // deterministic work every batch. + while (state.KeepRunningBatch(half * kAddRemBatchSize)) { + state.PauseTiming(); + + container.clear(); + + for (size_t i = 0; i < half; ++i) { + remove_keys[i] = i; + add_keys[i] = i; + } + constexpr int kSeed = 5; + std::mt19937_64 rand(kSeed); + std::shuffle(remove_keys.begin(), remove_keys.end(), rand); + std::shuffle(add_keys.begin(), add_keys.end(), rand); + + // Note needs lazy generation of values. + Generator<V> g(kBenchmarkValues * kAddRemBatchSize); + + for (size_t i = 0; i < half; ++i) { + container.insert(g(add_keys[i])); + container.insert(g(half + remove_keys[i])); + } + + // There are three parts each of size "half": + // 1 is being deleted from [offset - half, offset) + // 2 is standing [offset, offset + half) + // 3 is being inserted into [offset + half, offset + 2 * half) + size_t offset = 0; + + for (size_t i = 0; i < kAddRemBatchSize; ++i) { + std::shuffle(remove_keys.begin(), remove_keys.end(), rand); + std::shuffle(add_keys.begin(), add_keys.end(), rand); + offset += half; + + state.ResumeTiming(); + for (size_t idx = 0; idx < half; ++idx) { + container.erase(key_of_value(g(offset - half + remove_keys[idx]))); + container.insert(g(offset + half + add_keys[idx])); + } + state.PauseTiming(); + } + state.ResumeTiming(); + } +} + +// Mixed insertion and deletion in the same range using pre-constructed values. +template <typename T> +void BM_MixedAddRem(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance"); + + T container; + + // Create two random shuffles + std::vector<int> remove_keys(kBenchmarkValues); + std::vector<int> add_keys(kBenchmarkValues); + + // We want to do the exact same work repeatedly, and the benchmark can end + // after a different number of iterations depending on the speed of the + // individual run so we use a large batch size here and ensure that we do + // deterministic work every batch. + while (state.KeepRunningBatch(kBenchmarkValues * kAddRemBatchSize)) { + state.PauseTiming(); + + container.clear(); + + constexpr int kSeed = 7; + std::mt19937_64 rand(kSeed); + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues * 2); + + // Insert the first half of the values (already in random order) + container.insert(values.begin(), values.begin() + kBenchmarkValues); + + // Insert the first half of the values (already in random order) + for (size_t i = 0; i < kBenchmarkValues; ++i) { + // remove_keys and add_keys will be swapped before each round, + // therefore fill add_keys here w/ the keys being inserted, so + // they'll be the first to be removed. + remove_keys[i] = i + kBenchmarkValues; + add_keys[i] = i; + } + + for (size_t i = 0; i < kAddRemBatchSize; ++i) { + remove_keys.swap(add_keys); + std::shuffle(remove_keys.begin(), remove_keys.end(), rand); + std::shuffle(add_keys.begin(), add_keys.end(), rand); + + state.ResumeTiming(); + for (size_t idx = 0; idx < kBenchmarkValues; ++idx) { + container.erase(key_of_value(values[remove_keys[idx]])); + container.insert(values[add_keys[idx]]); + } + state.PauseTiming(); + } + state.ResumeTiming(); + } +} + +// Insertion at end, removal from the beginning. This benchmark +// counts two value constructors. +// TODO(ezb): we could add a GenerateNext version of generator that could reduce +// noise for string-like types. +template <typename T> +void BM_Fifo(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + + T container; + // Need lazy generation of values as state.max_iterations is large. + Generator<V> g(kBenchmarkValues + state.max_iterations); + + for (int i = 0; i < kBenchmarkValues; i++) { + container.insert(g(i)); + } + + while (state.KeepRunning()) { + container.erase(container.begin()); + container.insert(container.end(), g(state.iterations() + kBenchmarkValues)); + } +} + +// Iteration (forward) through the tree +template <typename T> +void BM_FwdIter(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + using R = typename T::value_type const*; + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + T container(values.begin(), values.end()); + + auto iter = container.end(); + + R r = nullptr; + + while (state.KeepRunning()) { + if (iter == container.end()) iter = container.begin(); + r = &(*iter); + ++iter; + } + + benchmark::DoNotOptimize(r); +} + +// Benchmark random range-construction of a container. +template <typename T> +void BM_RangeConstructionImpl(benchmark::State& state, bool sorted) { + using V = typename remove_pair_const<typename T::value_type>::type; + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + if (sorted) { + std::sort(values.begin(), values.end()); + } + { + T container(values.begin(), values.end()); + } + + while (state.KeepRunning()) { + T container(values.begin(), values.end()); + benchmark::DoNotOptimize(container); + } +} + +template <typename T> +void BM_InsertRangeRandom(benchmark::State& state) { + BM_RangeConstructionImpl<T>(state, false); +} + +template <typename T> +void BM_InsertRangeSorted(benchmark::State& state) { + BM_RangeConstructionImpl<T>(state, true); +} + +#define STL_ORDERED_TYPES(value) \ + using stl_set_##value = std::set<value>; \ + using stl_map_##value = std::map<value, intptr_t>; \ + using stl_multiset_##value = std::multiset<value>; \ + using stl_multimap_##value = std::multimap<value, intptr_t> + +using StdString = std::string; +STL_ORDERED_TYPES(int32_t); +STL_ORDERED_TYPES(int64_t); +STL_ORDERED_TYPES(StdString); +STL_ORDERED_TYPES(Time); + +#define STL_UNORDERED_TYPES(value) \ + using stl_unordered_set_##value = std::unordered_set<value>; \ + using stl_unordered_map_##value = std::unordered_map<value, intptr_t>; \ + using flat_hash_set_##value = flat_hash_set<value>; \ + using flat_hash_map_##value = flat_hash_map<value, intptr_t>; \ + using stl_unordered_multiset_##value = std::unordered_multiset<value>; \ + using stl_unordered_multimap_##value = \ + std::unordered_multimap<value, intptr_t> + +#define STL_UNORDERED_TYPES_CUSTOM_HASH(value, hash) \ + using stl_unordered_set_##value = std::unordered_set<value, hash>; \ + using stl_unordered_map_##value = std::unordered_map<value, intptr_t, hash>; \ + using flat_hash_set_##value = flat_hash_set<value, hash>; \ + using flat_hash_map_##value = flat_hash_map<value, intptr_t, hash>; \ + using stl_unordered_multiset_##value = std::unordered_multiset<value, hash>; \ + using stl_unordered_multimap_##value = \ + std::unordered_multimap<value, intptr_t, hash> + +STL_UNORDERED_TYPES(int32_t); +STL_UNORDERED_TYPES(int64_t); +STL_UNORDERED_TYPES(StdString); +STL_UNORDERED_TYPES_CUSTOM_HASH(Time, absl::Hash<absl::Time>); + +#define BTREE_TYPES(value) \ + using btree_256_set_##value = \ + btree_set<value, std::less<value>, std::allocator<value>>; \ + using btree_256_map_##value = \ + btree_map<value, intptr_t, std::less<value>, \ + std::allocator<std::pair<const value, intptr_t>>>; \ + using btree_256_multiset_##value = \ + btree_multiset<value, std::less<value>, std::allocator<value>>; \ + using btree_256_multimap_##value = \ + btree_multimap<value, intptr_t, std::less<value>, \ + std::allocator<std::pair<const value, intptr_t>>> + +BTREE_TYPES(int32_t); +BTREE_TYPES(int64_t); +BTREE_TYPES(StdString); +BTREE_TYPES(Time); + +#define MY_BENCHMARK4(type, func) \ + void BM_##type##_##func(benchmark::State& state) { BM_##func<type>(state); } \ + BENCHMARK(BM_##type##_##func) + +#define MY_BENCHMARK3(type) \ + MY_BENCHMARK4(type, Insert); \ + MY_BENCHMARK4(type, InsertSorted); \ + MY_BENCHMARK4(type, InsertEnd); \ + MY_BENCHMARK4(type, Lookup); \ + MY_BENCHMARK4(type, FullLookup); \ + MY_BENCHMARK4(type, Delete); \ + MY_BENCHMARK4(type, DeleteRange); \ + MY_BENCHMARK4(type, QueueAddRem); \ + MY_BENCHMARK4(type, MixedAddRem); \ + MY_BENCHMARK4(type, Fifo); \ + MY_BENCHMARK4(type, FwdIter); \ + MY_BENCHMARK4(type, InsertRangeRandom); \ + MY_BENCHMARK4(type, InsertRangeSorted) + +#define MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type) \ + MY_BENCHMARK3(stl_##type); \ + MY_BENCHMARK3(stl_unordered_##type); \ + MY_BENCHMARK3(btree_256_##type) + +#define MY_BENCHMARK2(type) \ + MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type); \ + MY_BENCHMARK3(flat_hash_##type) + +// Define MULTI_TESTING to see benchmarks for multi-containers also. +// +// You can use --copt=-DMULTI_TESTING. +#ifdef MULTI_TESTING +#define MY_BENCHMARK(type) \ + MY_BENCHMARK2(set_##type); \ + MY_BENCHMARK2(map_##type); \ + MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multiset_##type); \ + MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multimap_##type) +#else +#define MY_BENCHMARK(type) \ + MY_BENCHMARK2(set_##type); \ + MY_BENCHMARK2(map_##type) +#endif + +MY_BENCHMARK(int32_t); +MY_BENCHMARK(int64_t); +MY_BENCHMARK(StdString); +MY_BENCHMARK(Time); + +// Define a type whose size and cost of moving are independently customizable. +// When sizeof(value_type) increases, we expect btree to no longer have as much +// cache-locality advantage over STL. When cost of moving increases, we expect +// btree to actually do more work than STL because it has to move values around +// and STL doesn't have to. +template <int Size, int Copies> +struct BigType { + BigType() : BigType(0) {} + explicit BigType(int x) { std::iota(values.begin(), values.end(), x); } + + void Copy(const BigType& x) { + for (int i = 0; i < Size && i < Copies; ++i) values[i] = x.values[i]; + // If Copies > Size, do extra copies. + for (int i = Size, idx = 0; i < Copies; ++i) { + int64_t tmp = x.values[idx]; + benchmark::DoNotOptimize(tmp); + idx = idx + 1 == Size ? 0 : idx + 1; + } + } + + BigType(const BigType& x) { Copy(x); } + BigType& operator=(const BigType& x) { + Copy(x); + return *this; + } + + // Compare only the first Copies elements if Copies is less than Size. + bool operator<(const BigType& other) const { + return std::lexicographical_compare( + values.begin(), values.begin() + std::min(Size, Copies), + other.values.begin(), other.values.begin() + std::min(Size, Copies)); + } + bool operator==(const BigType& other) const { + return std::equal(values.begin(), values.begin() + std::min(Size, Copies), + other.values.begin()); + } + + // Support absl::Hash. + template <typename State> + friend State AbslHashValue(State h, const BigType& b) { + for (int i = 0; i < Size && i < Copies; ++i) + h = State::combine(std::move(h), b.values[i]); + return h; + } + + std::array<int64_t, Size> values; +}; + +#define BIG_TYPE_BENCHMARKS(SIZE, COPIES) \ + using stl_set_size##SIZE##copies##COPIES = std::set<BigType<SIZE, COPIES>>; \ + using stl_map_size##SIZE##copies##COPIES = \ + std::map<BigType<SIZE, COPIES>, intptr_t>; \ + using stl_multiset_size##SIZE##copies##COPIES = \ + std::multiset<BigType<SIZE, COPIES>>; \ + using stl_multimap_size##SIZE##copies##COPIES = \ + std::multimap<BigType<SIZE, COPIES>, intptr_t>; \ + using stl_unordered_set_size##SIZE##copies##COPIES = \ + std::unordered_set<BigType<SIZE, COPIES>, \ + absl::Hash<BigType<SIZE, COPIES>>>; \ + using stl_unordered_map_size##SIZE##copies##COPIES = \ + std::unordered_map<BigType<SIZE, COPIES>, intptr_t, \ + absl::Hash<BigType<SIZE, COPIES>>>; \ + using flat_hash_set_size##SIZE##copies##COPIES = \ + flat_hash_set<BigType<SIZE, COPIES>>; \ + using flat_hash_map_size##SIZE##copies##COPIES = \ + flat_hash_map<BigType<SIZE, COPIES>, intptr_t>; \ + using stl_unordered_multiset_size##SIZE##copies##COPIES = \ + std::unordered_multiset<BigType<SIZE, COPIES>, \ + absl::Hash<BigType<SIZE, COPIES>>>; \ + using stl_unordered_multimap_size##SIZE##copies##COPIES = \ + std::unordered_multimap<BigType<SIZE, COPIES>, intptr_t, \ + absl::Hash<BigType<SIZE, COPIES>>>; \ + using btree_256_set_size##SIZE##copies##COPIES = \ + btree_set<BigType<SIZE, COPIES>>; \ + using btree_256_map_size##SIZE##copies##COPIES = \ + btree_map<BigType<SIZE, COPIES>, intptr_t>; \ + using btree_256_multiset_size##SIZE##copies##COPIES = \ + btree_multiset<BigType<SIZE, COPIES>>; \ + using btree_256_multimap_size##SIZE##copies##COPIES = \ + btree_multimap<BigType<SIZE, COPIES>, intptr_t>; \ + MY_BENCHMARK(size##SIZE##copies##COPIES) + +// Define BIG_TYPE_TESTING to see benchmarks for more big types. +// +// You can use --copt=-DBIG_TYPE_TESTING. +#ifndef NODESIZE_TESTING +#ifdef BIG_TYPE_TESTING +BIG_TYPE_BENCHMARKS(1, 4); +BIG_TYPE_BENCHMARKS(4, 1); +BIG_TYPE_BENCHMARKS(4, 4); +BIG_TYPE_BENCHMARKS(1, 8); +BIG_TYPE_BENCHMARKS(8, 1); +BIG_TYPE_BENCHMARKS(8, 8); +BIG_TYPE_BENCHMARKS(1, 16); +BIG_TYPE_BENCHMARKS(16, 1); +BIG_TYPE_BENCHMARKS(16, 16); +BIG_TYPE_BENCHMARKS(1, 32); +BIG_TYPE_BENCHMARKS(32, 1); +BIG_TYPE_BENCHMARKS(32, 32); +#else +BIG_TYPE_BENCHMARKS(32, 32); +#endif +#endif + +// Benchmark using unique_ptrs to large value types. In order to be able to use +// the same benchmark code as the other types, use a type that holds a +// unique_ptr and has a copy constructor. +template <int Size> +struct BigTypePtr { + BigTypePtr() : BigTypePtr(0) {} + explicit BigTypePtr(int x) { + ptr = absl::make_unique<BigType<Size, Size>>(x); + } + BigTypePtr(const BigTypePtr& x) { + ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr); + } + BigTypePtr(BigTypePtr&& x) noexcept = default; + BigTypePtr& operator=(const BigTypePtr& x) { + ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr); + } + BigTypePtr& operator=(BigTypePtr&& x) noexcept = default; + + bool operator<(const BigTypePtr& other) const { return *ptr < *other.ptr; } + bool operator==(const BigTypePtr& other) const { return *ptr == *other.ptr; } + + std::unique_ptr<BigType<Size, Size>> ptr; +}; + +template <int Size> +double ContainerInfo(const btree_set<BigTypePtr<Size>>& b) { + const double bytes_used = + b.bytes_used() + b.size() * sizeof(BigType<Size, Size>); + const double bytes_per_value = bytes_used / b.size(); + BtreeContainerInfoLog(b, bytes_used, bytes_per_value); + return bytes_per_value; +} +template <int Size> +double ContainerInfo(const btree_map<int, BigTypePtr<Size>>& b) { + const double bytes_used = + b.bytes_used() + b.size() * sizeof(BigType<Size, Size>); + const double bytes_per_value = bytes_used / b.size(); + BtreeContainerInfoLog(b, bytes_used, bytes_per_value); + return bytes_per_value; +} + +#define BIG_TYPE_PTR_BENCHMARKS(SIZE) \ + using stl_set_size##SIZE##copies##SIZE##ptr = std::set<BigType<SIZE, SIZE>>; \ + using stl_map_size##SIZE##copies##SIZE##ptr = \ + std::map<int, BigType<SIZE, SIZE>>; \ + using stl_unordered_set_size##SIZE##copies##SIZE##ptr = \ + std::unordered_set<BigType<SIZE, SIZE>, \ + absl::Hash<BigType<SIZE, SIZE>>>; \ + using stl_unordered_map_size##SIZE##copies##SIZE##ptr = \ + std::unordered_map<int, BigType<SIZE, SIZE>>; \ + using flat_hash_set_size##SIZE##copies##SIZE##ptr = \ + flat_hash_set<BigType<SIZE, SIZE>>; \ + using flat_hash_map_size##SIZE##copies##SIZE##ptr = \ + flat_hash_map<int, BigTypePtr<SIZE>>; \ + using btree_256_set_size##SIZE##copies##SIZE##ptr = \ + btree_set<BigTypePtr<SIZE>>; \ + using btree_256_map_size##SIZE##copies##SIZE##ptr = \ + btree_map<int, BigTypePtr<SIZE>>; \ + MY_BENCHMARK3(stl_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(stl_unordered_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(flat_hash_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(btree_256_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(stl_map_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(stl_unordered_map_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(flat_hash_map_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(btree_256_map_size##SIZE##copies##SIZE##ptr) + +BIG_TYPE_PTR_BENCHMARKS(32); + +} // namespace +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h new file mode 100644 index 00000000..d23f4ee5 --- /dev/null +++ b/absl/container/btree_map.h @@ -0,0 +1,759 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: btree_map.h +// ----------------------------------------------------------------------------- +// +// This header file defines B-tree maps: sorted associative containers mapping +// keys to values. +// +// * `absl::btree_map<>` +// * `absl::btree_multimap<>` +// +// These B-tree types are similar to the corresponding types in the STL +// (`std::map` and `std::multimap`) and generally conform to the STL interfaces +// of those types. However, because they are implemented using B-trees, they +// are more efficient in most situations. +// +// Unlike `std::map` and `std::multimap`, which are commonly implemented using +// red-black tree nodes, B-tree maps use more generic B-tree nodes able to hold +// multiple values per node. Holding multiple values per node often makes +// B-tree maps perform better than their `std::map` counterparts, because +// multiple entries can be checked within the same cache hit. +// +// However, these types should not be considered drop-in replacements for +// `std::map` and `std::multimap` as there are some API differences, which are +// noted in this header file. +// +// Importantly, insertions and deletions may invalidate outstanding iterators, +// pointers, and references to elements. Such invalidations are typically only +// an issue if insertion and deletion operations are interleaved with the use of +// more than one iterator, pointer, or reference simultaneously. For this +// reason, `insert()` and `erase()` return a valid iterator at the current +// position. + +#ifndef ABSL_CONTAINER_BTREE_MAP_H_ +#define ABSL_CONTAINER_BTREE_MAP_H_ + +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/btree_container.h" // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// absl::btree_map<> +// +// An `absl::btree_map<K, V>` is an ordered associative container of +// unique keys and associated values designed to be a more efficient replacement +// for `std::map` (in most cases). +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less<K>`. +// +// An `absl::btree_map<K, V>` uses a default allocator of +// `std::allocator<std::pair<const K, V>>` to allocate (and deallocate) +// nodes, and construct and destruct values within those nodes. You may +// instead specify a custom allocator `A` (which in turn requires specifying a +// custom comparator `C`) as in `absl::btree_map<K, V, C, A>`. +// +template <typename Key, typename Value, typename Compare = std::less<Key>, + typename Alloc = std::allocator<std::pair<const Key, Value>>> +class btree_map + : public container_internal::btree_map_container< + container_internal::btree<container_internal::map_params< + Key, Value, Compare, Alloc, /*TargetNodeSize=*/256, + /*Multi=*/false>>> { + using Base = typename btree_map::btree_map_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_map` supports the same overload set as `std::map` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_map<int, std::string> map1; + // + // * Initializer List constructor + // + // absl::btree_map<int, std::string> map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::btree_map<int, std::string> map3(map2); + // + // * Copy assignment operator + // + // absl::btree_map<int, std::string> map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_map<int, std::string> map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_map<int, std::string> map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}}; + // absl::btree_map<int, std::string> map7(v.begin(), v.end()); + btree_map() {} + using Base::Base; + + // btree_map::begin() + // + // Returns an iterator to the beginning of the `btree_map`. + using Base::begin; + + // btree_map::cbegin() + // + // Returns a const iterator to the beginning of the `btree_map`. + using Base::cbegin; + + // btree_map::end() + // + // Returns an iterator to the end of the `btree_map`. + using Base::end; + + // btree_map::cend() + // + // Returns a const iterator to the end of the `btree_map`. + using Base::cend; + + // btree_map::empty() + // + // Returns whether or not the `btree_map` is empty. + using Base::empty; + + // btree_map::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_map` under current memory constraints. This value can be thought + // of as the largest value of `std::distance(begin(), end())` for a + // `btree_map<Key, T>`. + using Base::max_size; + + // btree_map::size() + // + // Returns the number of elements currently within the `btree_map`. + using Base::size; + + // btree_map::clear() + // + // Removes all elements from the `btree_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_map::erase() + // + // Erases elements within the `btree_map`. If an erase occurs, any references, + // pointers, or iterators are invalidated. + // Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_map`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template <typename K> size_type erase(const K& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased. + using Base::erase; + + // btree_map::insert() + // + // Inserts an element of the specified value into the `btree_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If an insertion + // occurs, any references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // std::pair<iterator,bool> insert(const value_type& value): + // + // Inserts a value into the `btree_map`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair<iterator,bool> insert(value_type&& value): + // + // Inserts a moveable value into the `btree_map`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list<init_type> ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_map::insert_or_assign() + // + // Inserts an element of the specified value into the `btree_map` provided + // that a value with the given key does not already exist, or replaces the + // corresponding mapped type with the forwarded `obj` argument if a key for + // that value already exists, returning an iterator pointing to the newly + // inserted element. Overloads are listed below. + // + // pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj): + // pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `btree_map`. If the returned bool is true, insertion took place, and if + // it's false, assignment took place. + // + // iterator insert_or_assign(const_iterator hint, + // const key_type& k, M&& obj): + // iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `btree_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::insert_or_assign; + + // btree_map::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_map::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace_hint; + + // btree_map::try_emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, provided that no element with the given key + // already exists. Unlike `emplace()`, if an element with the given key + // already exists, we guarantee that no element is constructed. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + // + // Overloads are listed below. + // + // std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args): + // std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `btree_map`. + // + // iterator try_emplace(const_iterator hint, + // const key_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `btree_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::try_emplace; + + // btree_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template <typename K> node_type extract(const K& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_map` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_map::merge() + // + // Extracts elements from a given `source` btree_map into this + // `btree_map`. If the destination `btree_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_map::swap(btree_map& other) + // + // Exchanges the contents of this `btree_map` with those of the `other` + // btree_map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `btree_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // btree_map::contains() + // + // template <typename K> bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_map`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::contains; + + // btree_map::count() + // + // template <typename K> size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_map`. Note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `btree_map`. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::count; + + // btree_map::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_map`. + using Base::equal_range; + + // btree_map::find() + // + // template <typename K> iterator find(const K& key): + // template <typename K> const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_map`. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::find; + + // btree_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `btree_map`, performing an `insert()` if the key does not already + // exist. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. Otherwise iterators are not affected and references are not + // invalidated. Overloads are listed below. + // + // T& operator[](key_type&& key): + // T& operator[](const key_type& key): + // + // Inserts a value_type object constructed in-place if the element with the + // given key does not exist. + using Base::operator[]; + + // btree_map::get_allocator() + // + // Returns the allocator function associated with this `btree_map`. + using Base::get_allocator; + + // btree_map::key_comp(); + // + // Returns the key comparator associated with this `btree_map`. + using Base::key_comp; + + // btree_map::value_comp(); + // + // Returns the value comparator associated with this `btree_map`. + using Base::value_comp; +}; + +// absl::swap(absl::btree_map<>, absl::btree_map<>) +// +// Swaps the contents of two `absl::btree_map` containers. +template <typename K, typename V, typename C, typename A> +void swap(btree_map<K, V, C, A> &x, btree_map<K, V, C, A> &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_map<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +template <typename K, typename V, typename C, typename A, typename Pred> +void erase_if(btree_map<K, V, C, A> &map, Pred pred) { + for (auto it = map.begin(); it != map.end();) { + if (pred(*it)) { + it = map.erase(it); + } else { + ++it; + } + } +} + +// absl::btree_multimap +// +// An `absl::btree_multimap<K, V>` is an ordered associative container of +// keys and associated values designed to be a more efficient replacement for +// `std::multimap` (in most cases). Unlike `absl::btree_map`, a B-tree multimap +// allows multiple elements with equivalent keys. +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less<K>`. +// +// An `absl::btree_multimap<K, V>` uses a default allocator of +// `std::allocator<std::pair<const K, V>>` to allocate (and deallocate) +// nodes, and construct and destruct values within those nodes. You may +// instead specify a custom allocator `A` (which in turn requires specifying a +// custom comparator `C`) as in `absl::btree_multimap<K, V, C, A>`. +// +template <typename Key, typename Value, typename Compare = std::less<Key>, + typename Alloc = std::allocator<std::pair<const Key, Value>>> +class btree_multimap + : public container_internal::btree_multimap_container< + container_internal::btree<container_internal::map_params< + Key, Value, Compare, Alloc, /*TargetNodeSize=*/256, + /*Multi=*/true>>> { + using Base = typename btree_multimap::btree_multimap_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_multimap` supports the same overload set as `std::multimap` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_multimap<int, std::string> map1; + // + // * Initializer List constructor + // + // absl::btree_multimap<int, std::string> map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::btree_multimap<int, std::string> map3(map2); + // + // * Copy assignment operator + // + // absl::btree_multimap<int, std::string> map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_multimap<int, std::string> map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_multimap<int, std::string> map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}}; + // absl::btree_multimap<int, std::string> map7(v.begin(), v.end()); + btree_multimap() {} + using Base::Base; + + // btree_multimap::begin() + // + // Returns an iterator to the beginning of the `btree_multimap`. + using Base::begin; + + // btree_multimap::cbegin() + // + // Returns a const iterator to the beginning of the `btree_multimap`. + using Base::cbegin; + + // btree_multimap::end() + // + // Returns an iterator to the end of the `btree_multimap`. + using Base::end; + + // btree_multimap::cend() + // + // Returns a const iterator to the end of the `btree_multimap`. + using Base::cend; + + // btree_multimap::empty() + // + // Returns whether or not the `btree_multimap` is empty. + using Base::empty; + + // btree_multimap::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_multimap` under current memory constraints. This value can be + // thought of as the largest value of `std::distance(begin(), end())` for a + // `btree_multimap<Key, T>`. + using Base::max_size; + + // btree_multimap::size() + // + // Returns the number of elements currently within the `btree_multimap`. + using Base::size; + + // btree_multimap::clear() + // + // Removes all elements from the `btree_multimap`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_multimap::erase() + // + // Erases elements within the `btree_multimap`. If an erase occurs, any + // references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_multimap`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template <typename K> size_type erase(const K& key): + // + // Erases the elements matching the key, if any exist, returning the + // number of elements erased. + using Base::erase; + + // btree_multimap::insert() + // + // Inserts an element of the specified value into the `btree_multimap`, + // returning an iterator pointing to the newly inserted element. + // Any references, pointers, or iterators are invalidated. Overloads are + // listed below. + // + // iterator insert(const value_type& value): + // + // Inserts a value into the `btree_multimap`, returning an iterator to the + // inserted element. + // + // iterator insert(value_type&& value): + // + // Inserts a moveable value into the `btree_multimap`, returning an iterator + // to the inserted element. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list<init_type> ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_multimap::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multimap`. Any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_multimap::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multimap`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search. + // + // Any references, pointers, or iterators are invalidated. + using Base::emplace_hint; + + // btree_multimap::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template <typename K> node_type extract(const K& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_multimap` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_multimap::merge() + // + // Extracts elements from a given `source` btree_multimap into this + // `btree_multimap`. If the destination `btree_multimap` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_multimap::swap(btree_multimap& other) + // + // Exchanges the contents of this `btree_multimap` with those of the `other` + // btree_multimap, avoiding invocation of any move, copy, or swap operations + // on individual elements. + // + // All iterators and references on the `btree_multimap` remain valid, + // excepting for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_multimap::contains() + // + // template <typename K> bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_multimap`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::contains; + + // btree_multimap::count() + // + // template <typename K> size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::count; + + // btree_multimap::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_multimap`. + using Base::equal_range; + + // btree_multimap::find() + // + // template <typename K> iterator find(const K& key): + // template <typename K> const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::find; + + // btree_multimap::get_allocator() + // + // Returns the allocator function associated with this `btree_multimap`. + using Base::get_allocator; + + // btree_multimap::key_comp(); + // + // Returns the key comparator associated with this `btree_multimap`. + using Base::key_comp; + + // btree_multimap::value_comp(); + // + // Returns the value comparator associated with this `btree_multimap`. + using Base::value_comp; +}; + +// absl::swap(absl::btree_multimap<>, absl::btree_multimap<>) +// +// Swaps the contents of two `absl::btree_multimap` containers. +template <typename K, typename V, typename C, typename A> +void swap(btree_multimap<K, V, C, A> &x, btree_multimap<K, V, C, A> &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_multimap<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +template <typename K, typename V, typename C, typename A, typename Pred> +void erase_if(btree_multimap<K, V, C, A> &map, Pred pred) { + for (auto it = map.begin(); it != map.end();) { + if (pred(*it)) { + it = map.erase(it); + } else { + ++it; + } + } +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_BTREE_MAP_H_ diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h new file mode 100644 index 00000000..127fb940 --- /dev/null +++ b/absl/container/btree_set.h @@ -0,0 +1,683 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: btree_set.h +// ----------------------------------------------------------------------------- +// +// This header file defines B-tree sets: sorted associative containers of +// values. +// +// * `absl::btree_set<>` +// * `absl::btree_multiset<>` +// +// These B-tree types are similar to the corresponding types in the STL +// (`std::set` and `std::multiset`) and generally conform to the STL interfaces +// of those types. However, because they are implemented using B-trees, they +// are more efficient in most situations. +// +// Unlike `std::set` and `std::multiset`, which are commonly implemented using +// red-black tree nodes, B-tree sets use more generic B-tree nodes able to hold +// multiple values per node. Holding multiple values per node often makes +// B-tree sets perform better than their `std::set` counterparts, because +// multiple entries can be checked within the same cache hit. +// +// However, these types should not be considered drop-in replacements for +// `std::set` and `std::multiset` as there are some API differences, which are +// noted in this header file. +// +// Importantly, insertions and deletions may invalidate outstanding iterators, +// pointers, and references to elements. Such invalidations are typically only +// an issue if insertion and deletion operations are interleaved with the use of +// more than one iterator, pointer, or reference simultaneously. For this +// reason, `insert()` and `erase()` return a valid iterator at the current +// position. + +#ifndef ABSL_CONTAINER_BTREE_SET_H_ +#define ABSL_CONTAINER_BTREE_SET_H_ + +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/btree_container.h" // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// absl::btree_set<> +// +// An `absl::btree_set<K>` is an ordered associative container of unique key +// values designed to be a more efficient replacement for `std::set` (in most +// cases). +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less<K>`. +// +// An `absl::btree_set<K>` uses a default allocator of `std::allocator<K>` to +// allocate (and deallocate) nodes, and construct and destruct values within +// those nodes. You may instead specify a custom allocator `A` (which in turn +// requires specifying a custom comparator `C`) as in +// `absl::btree_set<K, C, A>`. +// +template <typename Key, typename Compare = std::less<Key>, + typename Alloc = std::allocator<Key>> +class btree_set + : public container_internal::btree_set_container< + container_internal::btree<container_internal::set_params< + Key, Compare, Alloc, /*TargetNodeSize=*/256, + /*Multi=*/false>>> { + using Base = typename btree_set::btree_set_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_set` supports the same overload set as `std::set` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_set<std::string> set1; + // + // * Initializer List constructor + // + // absl::btree_set<std::string> set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::btree_set<std::string> set3(set2); + // + // * Copy assignment operator + // + // absl::btree_set<std::string> set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_set<std::string> set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_set<std::string> set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector<std::string> v = {"a", "b"}; + // absl::btree_set<std::string> set7(v.begin(), v.end()); + btree_set() {} + using Base::Base; + + // btree_set::begin() + // + // Returns an iterator to the beginning of the `btree_set`. + using Base::begin; + + // btree_set::cbegin() + // + // Returns a const iterator to the beginning of the `btree_set`. + using Base::cbegin; + + // btree_set::end() + // + // Returns an iterator to the end of the `btree_set`. + using Base::end; + + // btree_set::cend() + // + // Returns a const iterator to the end of the `btree_set`. + using Base::cend; + + // btree_set::empty() + // + // Returns whether or not the `btree_set` is empty. + using Base::empty; + + // btree_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_set` under current memory constraints. This value can be thought + // of as the largest value of `std::distance(begin(), end())` for a + // `btree_set<Key>`. + using Base::max_size; + + // btree_set::size() + // + // Returns the number of elements currently within the `btree_set`. + using Base::size; + + // btree_set::clear() + // + // Removes all elements from the `btree_set`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_set::erase() + // + // Erases elements within the `btree_set`. Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_set`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template <typename K> size_type erase(const K& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased. + using Base::erase; + + // btree_set::insert() + // + // Inserts an element of the specified value into the `btree_set`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If an insertion + // occurs, any references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // std::pair<iterator,bool> insert(const value_type& value): + // + // Inserts a value into the `btree_set`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair<iterator,bool> insert(value_type&& value): + // + // Inserts a moveable value into the `btree_set`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list<init_type> ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_set::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_set`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_set::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_set`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace_hint; + + // btree_set::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template <typename K> node_type extract(const K& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_set` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_set::merge() + // + // Extracts elements from a given `source` btree_set into this + // `btree_set`. If the destination `btree_set` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_set::swap(btree_set& other) + // + // Exchanges the contents of this `btree_set` with those of the `other` + // btree_set, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `btree_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_set::contains() + // + // template <typename K> bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_set`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::contains; + + // btree_set::count() + // + // template <typename K> size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_set`. Note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `btree_set`. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::count; + + // btree_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_set`. + using Base::equal_range; + + // btree_set::find() + // + // template <typename K> iterator find(const K& key): + // template <typename K> const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::find; + + // btree_set::get_allocator() + // + // Returns the allocator function associated with this `btree_set`. + using Base::get_allocator; + + // btree_set::key_comp(); + // + // Returns the key comparator associated with this `btree_set`. + using Base::key_comp; + + // btree_set::value_comp(); + // + // Returns the value comparator associated with this `btree_set`. The keys to + // sort the elements are the values themselves, therefore `value_comp` and its + // sibling member function `key_comp` are equivalent. + using Base::value_comp; +}; + +// absl::swap(absl::btree_set<>, absl::btree_set<>) +// +// Swaps the contents of two `absl::btree_set` containers. +template <typename K, typename C, typename A> +void swap(btree_set<K, C, A> &x, btree_set<K, C, A> &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_set<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +template <typename K, typename C, typename A, typename Pred> +void erase_if(btree_set<K, C, A> &set, Pred pred) { + for (auto it = set.begin(); it != set.end();) { + if (pred(*it)) { + it = set.erase(it); + } else { + ++it; + } + } +} + +// absl::btree_multiset<> +// +// An `absl::btree_multiset<K>` is an ordered associative container of +// keys and associated values designed to be a more efficient replacement +// for `std::multiset` (in most cases). Unlike `absl::btree_set`, a B-tree +// multiset allows equivalent elements. +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less<K>`. +// +// An `absl::btree_multiset<K>` uses a default allocator of `std::allocator<K>` +// to allocate (and deallocate) nodes, and construct and destruct values within +// those nodes. You may instead specify a custom allocator `A` (which in turn +// requires specifying a custom comparator `C`) as in +// `absl::btree_multiset<K, C, A>`. +// +template <typename Key, typename Compare = std::less<Key>, + typename Alloc = std::allocator<Key>> +class btree_multiset + : public container_internal::btree_multiset_container< + container_internal::btree<container_internal::set_params< + Key, Compare, Alloc, /*TargetNodeSize=*/256, + /*Multi=*/true>>> { + using Base = typename btree_multiset::btree_multiset_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_multiset` supports the same overload set as `std::set` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_multiset<std::string> set1; + // + // * Initializer List constructor + // + // absl::btree_multiset<std::string> set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::btree_multiset<std::string> set3(set2); + // + // * Copy assignment operator + // + // absl::btree_multiset<std::string> set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_multiset<std::string> set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_multiset<std::string> set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector<std::string> v = {"a", "b"}; + // absl::btree_multiset<std::string> set7(v.begin(), v.end()); + btree_multiset() {} + using Base::Base; + + // btree_multiset::begin() + // + // Returns an iterator to the beginning of the `btree_multiset`. + using Base::begin; + + // btree_multiset::cbegin() + // + // Returns a const iterator to the beginning of the `btree_multiset`. + using Base::cbegin; + + // btree_multiset::end() + // + // Returns an iterator to the end of the `btree_multiset`. + using Base::end; + + // btree_multiset::cend() + // + // Returns a const iterator to the end of the `btree_multiset`. + using Base::cend; + + // btree_multiset::empty() + // + // Returns whether or not the `btree_multiset` is empty. + using Base::empty; + + // btree_multiset::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_multiset` under current memory constraints. This value can be + // thought of as the largest value of `std::distance(begin(), end())` for a + // `btree_multiset<Key>`. + using Base::max_size; + + // btree_multiset::size() + // + // Returns the number of elements currently within the `btree_multiset`. + using Base::size; + + // btree_multiset::clear() + // + // Removes all elements from the `btree_multiset`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_multiset::erase() + // + // Erases elements within the `btree_multiset`. Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_multiset`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template <typename K> size_type erase(const K& key): + // + // Erases the elements matching the key, if any exist, returning the + // number of elements erased. + using Base::erase; + + // btree_multiset::insert() + // + // Inserts an element of the specified value into the `btree_multiset`, + // returning an iterator pointing to the newly inserted element. + // Any references, pointers, or iterators are invalidated. Overloads are + // listed below. + // + // iterator insert(const value_type& value): + // + // Inserts a value into the `btree_multiset`, returning an iterator to the + // inserted element. + // + // iterator insert(value_type&& value): + // + // Inserts a moveable value into the `btree_multiset`, returning an iterator + // to the inserted element. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list<init_type> ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_multiset::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multiset`. Any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_multiset::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multiset`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search. + // + // Any references, pointers, or iterators are invalidated. + using Base::emplace_hint; + + // btree_multiset::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template <typename K> node_type extract(const K& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_multiset` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_multiset::merge() + // + // Extracts elements from a given `source` btree_multiset into this + // `btree_multiset`. If the destination `btree_multiset` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_multiset::swap(btree_multiset& other) + // + // Exchanges the contents of this `btree_multiset` with those of the `other` + // btree_multiset, avoiding invocation of any move, copy, or swap operations + // on individual elements. + // + // All iterators and references on the `btree_multiset` remain valid, + // excepting for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_multiset::contains() + // + // template <typename K> bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_multiset`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::contains; + + // btree_multiset::count() + // + // template <typename K> size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::count; + + // btree_multiset::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_multiset`. + using Base::equal_range; + + // btree_multiset::find() + // + // template <typename K> iterator find(const K& key): + // template <typename K> const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::find; + + // btree_multiset::get_allocator() + // + // Returns the allocator function associated with this `btree_multiset`. + using Base::get_allocator; + + // btree_multiset::key_comp(); + // + // Returns the key comparator associated with this `btree_multiset`. + using Base::key_comp; + + // btree_multiset::value_comp(); + // + // Returns the value comparator associated with this `btree_multiset`. The + // keys to sort the elements are the values themselves, therefore `value_comp` + // and its sibling member function `key_comp` are equivalent. + using Base::value_comp; +}; + +// absl::swap(absl::btree_multiset<>, absl::btree_multiset<>) +// +// Swaps the contents of two `absl::btree_multiset` containers. +template <typename K, typename C, typename A> +void swap(btree_multiset<K, C, A> &x, btree_multiset<K, C, A> &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_multiset<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +template <typename K, typename C, typename A, typename Pred> +void erase_if(btree_multiset<K, C, A> &set, Pred pred) { + for (auto it = set.begin(); it != set.end();) { + if (pred(*it)) { + it = set.erase(it); + } else { + ++it; + } + } +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_BTREE_SET_H_ diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc new file mode 100644 index 00000000..9edf38f9 --- /dev/null +++ b/absl/container/btree_test.cc @@ -0,0 +1,2404 @@ +// 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/btree_test.h" + +#include <cstdint> +#include <map> +#include <memory> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" +#include "absl/container/internal/counting_allocator.h" +#include "absl/container/internal/test_instance_tracker.h" +#include "absl/flags/flag.h" +#include "absl/hash/hash_testing.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/types/compare.h" + +ABSL_FLAG(int, test_values, 10000, "The number of values to use for tests"); + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace { + +using ::absl::test_internal::CopyableMovableInstance; +using ::absl::test_internal::InstanceTracker; +using ::absl::test_internal::MovableOnlyInstance; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::IsEmpty; +using ::testing::Pair; + +template <typename T, typename U> +void CheckPairEquals(const T &x, const U &y) { + ABSL_INTERNAL_CHECK(x == y, "Values are unequal."); +} + +template <typename T, typename U, typename V, typename W> +void CheckPairEquals(const std::pair<T, U> &x, const std::pair<V, W> &y) { + CheckPairEquals(x.first, y.first); + CheckPairEquals(x.second, y.second); +} +} // namespace + +// The base class for a sorted associative container checker. TreeType is the +// container type to check and CheckerType is the container type to check +// against. TreeType is expected to be btree_{set,map,multiset,multimap} and +// CheckerType is expected to be {set,map,multiset,multimap}. +template <typename TreeType, typename CheckerType> +class base_checker { + public: + using key_type = typename TreeType::key_type; + using value_type = typename TreeType::value_type; + using key_compare = typename TreeType::key_compare; + using pointer = typename TreeType::pointer; + using const_pointer = typename TreeType::const_pointer; + using reference = typename TreeType::reference; + using const_reference = typename TreeType::const_reference; + using size_type = typename TreeType::size_type; + using difference_type = typename TreeType::difference_type; + using iterator = typename TreeType::iterator; + using const_iterator = typename TreeType::const_iterator; + using reverse_iterator = typename TreeType::reverse_iterator; + using const_reverse_iterator = typename TreeType::const_reverse_iterator; + + public: + base_checker() : const_tree_(tree_) {} + base_checker(const base_checker &x) + : tree_(x.tree_), const_tree_(tree_), checker_(x.checker_) {} + template <typename InputIterator> + base_checker(InputIterator b, InputIterator e) + : tree_(b, e), const_tree_(tree_), checker_(b, e) {} + + iterator begin() { return tree_.begin(); } + const_iterator begin() const { return tree_.begin(); } + iterator end() { return tree_.end(); } + const_iterator end() const { return tree_.end(); } + reverse_iterator rbegin() { return tree_.rbegin(); } + const_reverse_iterator rbegin() const { return tree_.rbegin(); } + reverse_iterator rend() { return tree_.rend(); } + const_reverse_iterator rend() const { return tree_.rend(); } + + template <typename IterType, typename CheckerIterType> + IterType iter_check(IterType tree_iter, CheckerIterType checker_iter) const { + if (tree_iter == tree_.end()) { + ABSL_INTERNAL_CHECK(checker_iter == checker_.end(), + "Checker iterator not at end."); + } else { + CheckPairEquals(*tree_iter, *checker_iter); + } + return tree_iter; + } + template <typename IterType, typename CheckerIterType> + IterType riter_check(IterType tree_iter, CheckerIterType checker_iter) const { + if (tree_iter == tree_.rend()) { + ABSL_INTERNAL_CHECK(checker_iter == checker_.rend(), + "Checker iterator not at rend."); + } else { + CheckPairEquals(*tree_iter, *checker_iter); + } + return tree_iter; + } + void value_check(const value_type &x) { + typename KeyOfValue<typename TreeType::key_type, + typename TreeType::value_type>::type key_of_value; + const key_type &key = key_of_value(x); + CheckPairEquals(*find(key), x); + lower_bound(key); + upper_bound(key); + equal_range(key); + contains(key); + count(key); + } + void erase_check(const key_type &key) { + EXPECT_FALSE(tree_.contains(key)); + EXPECT_EQ(tree_.find(key), const_tree_.end()); + EXPECT_FALSE(const_tree_.contains(key)); + EXPECT_EQ(const_tree_.find(key), tree_.end()); + EXPECT_EQ(tree_.equal_range(key).first, + const_tree_.equal_range(key).second); + } + + iterator lower_bound(const key_type &key) { + return iter_check(tree_.lower_bound(key), checker_.lower_bound(key)); + } + const_iterator lower_bound(const key_type &key) const { + return iter_check(tree_.lower_bound(key), checker_.lower_bound(key)); + } + iterator upper_bound(const key_type &key) { + return iter_check(tree_.upper_bound(key), checker_.upper_bound(key)); + } + const_iterator upper_bound(const key_type &key) const { + return iter_check(tree_.upper_bound(key), checker_.upper_bound(key)); + } + std::pair<iterator, iterator> equal_range(const key_type &key) { + std::pair<typename CheckerType::iterator, typename CheckerType::iterator> + checker_res = checker_.equal_range(key); + std::pair<iterator, iterator> tree_res = tree_.equal_range(key); + iter_check(tree_res.first, checker_res.first); + iter_check(tree_res.second, checker_res.second); + return tree_res; + } + std::pair<const_iterator, const_iterator> equal_range( + const key_type &key) const { + std::pair<typename CheckerType::const_iterator, + typename CheckerType::const_iterator> + checker_res = checker_.equal_range(key); + std::pair<const_iterator, const_iterator> tree_res = tree_.equal_range(key); + iter_check(tree_res.first, checker_res.first); + iter_check(tree_res.second, checker_res.second); + return tree_res; + } + iterator find(const key_type &key) { + return iter_check(tree_.find(key), checker_.find(key)); + } + const_iterator find(const key_type &key) const { + return iter_check(tree_.find(key), checker_.find(key)); + } + bool contains(const key_type &key) const { return find(key) != end(); } + size_type count(const key_type &key) const { + size_type res = checker_.count(key); + EXPECT_EQ(res, tree_.count(key)); + return res; + } + + base_checker &operator=(const base_checker &x) { + tree_ = x.tree_; + checker_ = x.checker_; + return *this; + } + + int erase(const key_type &key) { + int size = tree_.size(); + int res = checker_.erase(key); + EXPECT_EQ(res, tree_.count(key)); + EXPECT_EQ(res, tree_.erase(key)); + EXPECT_EQ(tree_.count(key), 0); + EXPECT_EQ(tree_.size(), size - res); + erase_check(key); + return res; + } + iterator erase(iterator iter) { + key_type key = iter.key(); + int size = tree_.size(); + int count = tree_.count(key); + auto checker_iter = checker_.lower_bound(key); + for (iterator tmp(tree_.lower_bound(key)); tmp != iter; ++tmp) { + ++checker_iter; + } + auto checker_next = checker_iter; + ++checker_next; + checker_.erase(checker_iter); + iter = tree_.erase(iter); + EXPECT_EQ(tree_.size(), checker_.size()); + EXPECT_EQ(tree_.size(), size - 1); + EXPECT_EQ(tree_.count(key), count - 1); + if (count == 1) { + erase_check(key); + } + return iter_check(iter, checker_next); + } + + void erase(iterator begin, iterator end) { + int size = tree_.size(); + int count = std::distance(begin, end); + auto checker_begin = checker_.lower_bound(begin.key()); + for (iterator tmp(tree_.lower_bound(begin.key())); tmp != begin; ++tmp) { + ++checker_begin; + } + auto checker_end = + end == tree_.end() ? checker_.end() : checker_.lower_bound(end.key()); + if (end != tree_.end()) { + for (iterator tmp(tree_.lower_bound(end.key())); tmp != end; ++tmp) { + ++checker_end; + } + } + const auto checker_ret = checker_.erase(checker_begin, checker_end); + const auto tree_ret = tree_.erase(begin, end); + EXPECT_EQ(std::distance(checker_.begin(), checker_ret), + std::distance(tree_.begin(), tree_ret)); + EXPECT_EQ(tree_.size(), checker_.size()); + EXPECT_EQ(tree_.size(), size - count); + } + + void clear() { + tree_.clear(); + checker_.clear(); + } + void swap(base_checker &x) { + tree_.swap(x.tree_); + checker_.swap(x.checker_); + } + + void verify() const { + tree_.verify(); + EXPECT_EQ(tree_.size(), checker_.size()); + + // Move through the forward iterators using increment. + auto checker_iter = checker_.begin(); + const_iterator tree_iter(tree_.begin()); + for (; tree_iter != tree_.end(); ++tree_iter, ++checker_iter) { + CheckPairEquals(*tree_iter, *checker_iter); + } + + // Move through the forward iterators using decrement. + for (int n = tree_.size() - 1; n >= 0; --n) { + iter_check(tree_iter, checker_iter); + --tree_iter; + --checker_iter; + } + EXPECT_EQ(tree_iter, tree_.begin()); + EXPECT_EQ(checker_iter, checker_.begin()); + + // Move through the reverse iterators using increment. + auto checker_riter = checker_.rbegin(); + const_reverse_iterator tree_riter(tree_.rbegin()); + for (; tree_riter != tree_.rend(); ++tree_riter, ++checker_riter) { + CheckPairEquals(*tree_riter, *checker_riter); + } + + // Move through the reverse iterators using decrement. + for (int n = tree_.size() - 1; n >= 0; --n) { + riter_check(tree_riter, checker_riter); + --tree_riter; + --checker_riter; + } + EXPECT_EQ(tree_riter, tree_.rbegin()); + EXPECT_EQ(checker_riter, checker_.rbegin()); + } + + const TreeType &tree() const { return tree_; } + + size_type size() const { + EXPECT_EQ(tree_.size(), checker_.size()); + return tree_.size(); + } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { + EXPECT_EQ(tree_.empty(), checker_.empty()); + return tree_.empty(); + } + + protected: + TreeType tree_; + const TreeType &const_tree_; + CheckerType checker_; +}; + +namespace { +// A checker for unique sorted associative containers. TreeType is expected to +// be btree_{set,map} and CheckerType is expected to be {set,map}. +template <typename TreeType, typename CheckerType> +class unique_checker : public base_checker<TreeType, CheckerType> { + using super_type = base_checker<TreeType, CheckerType>; + + public: + using iterator = typename super_type::iterator; + using value_type = typename super_type::value_type; + + public: + unique_checker() : super_type() {} + unique_checker(const unique_checker &x) : super_type(x) {} + template <class InputIterator> + unique_checker(InputIterator b, InputIterator e) : super_type(b, e) {} + unique_checker &operator=(const unique_checker &) = default; + + // Insertion routines. + std::pair<iterator, bool> insert(const value_type &x) { + int size = this->tree_.size(); + std::pair<typename CheckerType::iterator, bool> checker_res = + this->checker_.insert(x); + std::pair<iterator, bool> tree_res = this->tree_.insert(x); + CheckPairEquals(*tree_res.first, *checker_res.first); + EXPECT_EQ(tree_res.second, checker_res.second); + EXPECT_EQ(this->tree_.size(), this->checker_.size()); + EXPECT_EQ(this->tree_.size(), size + tree_res.second); + return tree_res; + } + iterator insert(iterator position, const value_type &x) { + int size = this->tree_.size(); + std::pair<typename CheckerType::iterator, bool> checker_res = + this->checker_.insert(x); + iterator tree_res = this->tree_.insert(position, x); + CheckPairEquals(*tree_res, *checker_res.first); + EXPECT_EQ(this->tree_.size(), this->checker_.size()); + EXPECT_EQ(this->tree_.size(), size + checker_res.second); + return tree_res; + } + template <typename InputIterator> + void insert(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert(*b); + } + } +}; + +// A checker for multiple sorted associative containers. TreeType is expected +// to be btree_{multiset,multimap} and CheckerType is expected to be +// {multiset,multimap}. +template <typename TreeType, typename CheckerType> +class multi_checker : public base_checker<TreeType, CheckerType> { + using super_type = base_checker<TreeType, CheckerType>; + + public: + using iterator = typename super_type::iterator; + using value_type = typename super_type::value_type; + + public: + multi_checker() : super_type() {} + multi_checker(const multi_checker &x) : super_type(x) {} + template <class InputIterator> + multi_checker(InputIterator b, InputIterator e) : super_type(b, e) {} + multi_checker &operator=(const multi_checker &) = default; + + // Insertion routines. + iterator insert(const value_type &x) { + int size = this->tree_.size(); + auto checker_res = this->checker_.insert(x); + iterator tree_res = this->tree_.insert(x); + CheckPairEquals(*tree_res, *checker_res); + EXPECT_EQ(this->tree_.size(), this->checker_.size()); + EXPECT_EQ(this->tree_.size(), size + 1); + return tree_res; + } + iterator insert(iterator position, const value_type &x) { + int size = this->tree_.size(); + auto checker_res = this->checker_.insert(x); + iterator tree_res = this->tree_.insert(position, x); + CheckPairEquals(*tree_res, *checker_res); + EXPECT_EQ(this->tree_.size(), this->checker_.size()); + EXPECT_EQ(this->tree_.size(), size + 1); + return tree_res; + } + template <typename InputIterator> + void insert(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert(*b); + } + } +}; + +template <typename T, typename V> +void DoTest(const char *name, T *b, const std::vector<V> &values) { + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + T &mutable_b = *b; + const T &const_b = *b; + + // Test insert. + for (int i = 0; i < values.size(); ++i) { + mutable_b.insert(values[i]); + mutable_b.value_check(values[i]); + } + ASSERT_EQ(mutable_b.size(), values.size()); + + const_b.verify(); + + // Test copy constructor. + T b_copy(const_b); + EXPECT_EQ(b_copy.size(), const_b.size()); + for (int i = 0; i < values.size(); ++i) { + CheckPairEquals(*b_copy.find(key_of_value(values[i])), values[i]); + } + + // Test range constructor. + T b_range(const_b.begin(), const_b.end()); + EXPECT_EQ(b_range.size(), const_b.size()); + for (int i = 0; i < values.size(); ++i) { + CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]); + } + + // Test range insertion for values that already exist. + b_range.insert(b_copy.begin(), b_copy.end()); + b_range.verify(); + + // Test range insertion for new values. + b_range.clear(); + b_range.insert(b_copy.begin(), b_copy.end()); + EXPECT_EQ(b_range.size(), b_copy.size()); + for (int i = 0; i < values.size(); ++i) { + CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]); + } + + // Test assignment to self. Nothing should change. + b_range.operator=(b_range); + EXPECT_EQ(b_range.size(), b_copy.size()); + + // Test assignment of new values. + b_range.clear(); + b_range = b_copy; + EXPECT_EQ(b_range.size(), b_copy.size()); + + // Test swap. + b_range.clear(); + b_range.swap(b_copy); + EXPECT_EQ(b_copy.size(), 0); + EXPECT_EQ(b_range.size(), const_b.size()); + for (int i = 0; i < values.size(); ++i) { + CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]); + } + b_range.swap(b_copy); + + // Test non-member function swap. + swap(b_range, b_copy); + EXPECT_EQ(b_copy.size(), 0); + EXPECT_EQ(b_range.size(), const_b.size()); + for (int i = 0; i < values.size(); ++i) { + CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]); + } + swap(b_range, b_copy); + + // Test erase via values. + for (int i = 0; i < values.size(); ++i) { + mutable_b.erase(key_of_value(values[i])); + // Erasing a non-existent key should have no effect. + ASSERT_EQ(mutable_b.erase(key_of_value(values[i])), 0); + } + + const_b.verify(); + EXPECT_EQ(const_b.size(), 0); + + // Test erase via iterators. + mutable_b = b_copy; + for (int i = 0; i < values.size(); ++i) { + mutable_b.erase(mutable_b.find(key_of_value(values[i]))); + } + + const_b.verify(); + EXPECT_EQ(const_b.size(), 0); + + // Test insert with hint. + for (int i = 0; i < values.size(); i++) { + mutable_b.insert(mutable_b.upper_bound(key_of_value(values[i])), values[i]); + } + + const_b.verify(); + + // Test range erase. + mutable_b.erase(mutable_b.begin(), mutable_b.end()); + EXPECT_EQ(mutable_b.size(), 0); + const_b.verify(); + + // First half. + mutable_b = b_copy; + typename T::iterator mutable_iter_end = mutable_b.begin(); + for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_end; + mutable_b.erase(mutable_b.begin(), mutable_iter_end); + EXPECT_EQ(mutable_b.size(), values.size() - values.size() / 2); + const_b.verify(); + + // Second half. + mutable_b = b_copy; + typename T::iterator mutable_iter_begin = mutable_b.begin(); + for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_begin; + mutable_b.erase(mutable_iter_begin, mutable_b.end()); + EXPECT_EQ(mutable_b.size(), values.size() / 2); + const_b.verify(); + + // Second quarter. + mutable_b = b_copy; + mutable_iter_begin = mutable_b.begin(); + for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_begin; + mutable_iter_end = mutable_iter_begin; + for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_end; + mutable_b.erase(mutable_iter_begin, mutable_iter_end); + EXPECT_EQ(mutable_b.size(), values.size() - values.size() / 4); + const_b.verify(); + + mutable_b.clear(); +} + +template <typename T> +void ConstTest() { + using value_type = typename T::value_type; + typename KeyOfValue<typename T::key_type, value_type>::type key_of_value; + + T mutable_b; + const T &const_b = mutable_b; + + // Insert a single value into the container and test looking it up. + value_type value = Generator<value_type>(2)(2); + mutable_b.insert(value); + EXPECT_TRUE(mutable_b.contains(key_of_value(value))); + EXPECT_NE(mutable_b.find(key_of_value(value)), const_b.end()); + EXPECT_TRUE(const_b.contains(key_of_value(value))); + EXPECT_NE(const_b.find(key_of_value(value)), mutable_b.end()); + EXPECT_EQ(*const_b.lower_bound(key_of_value(value)), value); + EXPECT_EQ(const_b.upper_bound(key_of_value(value)), const_b.end()); + EXPECT_EQ(*const_b.equal_range(key_of_value(value)).first, value); + + // We can only create a non-const iterator from a non-const container. + typename T::iterator mutable_iter(mutable_b.begin()); + EXPECT_EQ(mutable_iter, const_b.begin()); + EXPECT_NE(mutable_iter, const_b.end()); + EXPECT_EQ(const_b.begin(), mutable_iter); + EXPECT_NE(const_b.end(), mutable_iter); + typename T::reverse_iterator mutable_riter(mutable_b.rbegin()); + EXPECT_EQ(mutable_riter, const_b.rbegin()); + EXPECT_NE(mutable_riter, const_b.rend()); + EXPECT_EQ(const_b.rbegin(), mutable_riter); + EXPECT_NE(const_b.rend(), mutable_riter); + + // We can create a const iterator from a non-const iterator. + typename T::const_iterator const_iter(mutable_iter); + EXPECT_EQ(const_iter, mutable_b.begin()); + EXPECT_NE(const_iter, mutable_b.end()); + EXPECT_EQ(mutable_b.begin(), const_iter); + EXPECT_NE(mutable_b.end(), const_iter); + typename T::const_reverse_iterator const_riter(mutable_riter); + EXPECT_EQ(const_riter, mutable_b.rbegin()); + EXPECT_NE(const_riter, mutable_b.rend()); + EXPECT_EQ(mutable_b.rbegin(), const_riter); + EXPECT_NE(mutable_b.rend(), const_riter); + + // Make sure various methods can be invoked on a const container. + const_b.verify(); + ASSERT_TRUE(!const_b.empty()); + EXPECT_EQ(const_b.size(), 1); + EXPECT_GT(const_b.max_size(), 0); + EXPECT_TRUE(const_b.contains(key_of_value(value))); + EXPECT_EQ(const_b.count(key_of_value(value)), 1); +} + +template <typename T, typename C> +void BtreeTest() { + ConstTest<T>(); + + using V = typename remove_pair_const<typename T::value_type>::type; + const std::vector<V> random_values = GenerateValuesWithSeed<V>( + absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values), + testing::GTEST_FLAG(random_seed)); + + unique_checker<T, C> container; + + // Test key insertion/deletion in sorted order. + std::vector<V> sorted_values(random_values); + std::sort(sorted_values.begin(), sorted_values.end()); + DoTest("sorted: ", &container, sorted_values); + + // Test key insertion/deletion in reverse sorted order. + std::reverse(sorted_values.begin(), sorted_values.end()); + DoTest("rsorted: ", &container, sorted_values); + + // Test key insertion/deletion in random order. + DoTest("random: ", &container, random_values); +} + +template <typename T, typename C> +void BtreeMultiTest() { + ConstTest<T>(); + + using V = typename remove_pair_const<typename T::value_type>::type; + const std::vector<V> random_values = GenerateValuesWithSeed<V>( + absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values), + testing::GTEST_FLAG(random_seed)); + + multi_checker<T, C> container; + + // Test keys in sorted order. + std::vector<V> sorted_values(random_values); + std::sort(sorted_values.begin(), sorted_values.end()); + DoTest("sorted: ", &container, sorted_values); + + // Test keys in reverse sorted order. + std::reverse(sorted_values.begin(), sorted_values.end()); + DoTest("rsorted: ", &container, sorted_values); + + // Test keys in random order. + DoTest("random: ", &container, random_values); + + // Test keys in random order w/ duplicates. + std::vector<V> duplicate_values(random_values); + duplicate_values.insert(duplicate_values.end(), random_values.begin(), + random_values.end()); + DoTest("duplicates:", &container, duplicate_values); + + // Test all identical keys. + std::vector<V> identical_values(100); + std::fill(identical_values.begin(), identical_values.end(), + Generator<V>(2)(2)); + DoTest("identical: ", &container, identical_values); +} + +template <typename T> +struct PropagatingCountingAlloc : public CountingAllocator<T> { + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + + using Base = CountingAllocator<T>; + using Base::Base; + + template <typename U> + explicit PropagatingCountingAlloc(const PropagatingCountingAlloc<U> &other) + : Base(other.bytes_used_) {} + + template <typename U> + struct rebind { + using other = PropagatingCountingAlloc<U>; + }; +}; + +template <typename T> +void BtreeAllocatorTest() { + using value_type = typename T::value_type; + + int64_t bytes1 = 0, bytes2 = 0; + PropagatingCountingAlloc<T> allocator1(&bytes1); + PropagatingCountingAlloc<T> allocator2(&bytes2); + Generator<value_type> generator(1000); + + // Test that we allocate properly aligned memory. If we don't, then Layout + // will assert fail. + auto unused1 = allocator1.allocate(1); + auto unused2 = allocator2.allocate(1); + + // Test copy assignment + { + T b1(typename T::key_compare(), allocator1); + T b2(typename T::key_compare(), allocator2); + + int64_t original_bytes1 = bytes1; + b1.insert(generator(0)); + EXPECT_GT(bytes1, original_bytes1); + + // This should propagate the allocator. + b1 = b2; + EXPECT_EQ(b1.size(), 0); + EXPECT_EQ(b2.size(), 0); + EXPECT_EQ(bytes1, original_bytes1); + + for (int i = 1; i < 1000; i++) { + b1.insert(generator(i)); + } + + // We should have allocated out of allocator2. + EXPECT_GT(bytes2, bytes1); + } + + // Test move assignment + { + T b1(typename T::key_compare(), allocator1); + T b2(typename T::key_compare(), allocator2); + + int64_t original_bytes1 = bytes1; + b1.insert(generator(0)); + EXPECT_GT(bytes1, original_bytes1); + + // This should propagate the allocator. + b1 = std::move(b2); + EXPECT_EQ(b1.size(), 0); + EXPECT_EQ(bytes1, original_bytes1); + + for (int i = 1; i < 1000; i++) { + b1.insert(generator(i)); + } + + // We should have allocated out of allocator2. + EXPECT_GT(bytes2, bytes1); + } + + // Test swap + { + T b1(typename T::key_compare(), allocator1); + T b2(typename T::key_compare(), allocator2); + + int64_t original_bytes1 = bytes1; + b1.insert(generator(0)); + EXPECT_GT(bytes1, original_bytes1); + + // This should swap the allocators. + swap(b1, b2); + EXPECT_EQ(b1.size(), 0); + EXPECT_EQ(b2.size(), 1); + EXPECT_GT(bytes1, original_bytes1); + + for (int i = 1; i < 1000; i++) { + b1.insert(generator(i)); + } + + // We should have allocated out of allocator2. + EXPECT_GT(bytes2, bytes1); + } + + allocator1.deallocate(unused1, 1); + allocator2.deallocate(unused2, 1); +} + +template <typename T> +void BtreeMapTest() { + using value_type = typename T::value_type; + using mapped_type = typename T::mapped_type; + + mapped_type m = Generator<mapped_type>(0)(0); + (void)m; + + T b; + + // Verify we can insert using operator[]. + for (int i = 0; i < 1000; i++) { + value_type v = Generator<value_type>(1000)(i); + b[v.first] = v.second; + } + EXPECT_EQ(b.size(), 1000); + + // Test whether we can use the "->" operator on iterators and + // reverse_iterators. This stresses the btree_map_params::pair_pointer + // mechanism. + EXPECT_EQ(b.begin()->first, Generator<value_type>(1000)(0).first); + EXPECT_EQ(b.begin()->second, Generator<value_type>(1000)(0).second); + EXPECT_EQ(b.rbegin()->first, Generator<value_type>(1000)(999).first); + EXPECT_EQ(b.rbegin()->second, Generator<value_type>(1000)(999).second); +} + +template <typename T> +void BtreeMultiMapTest() { + using mapped_type = typename T::mapped_type; + mapped_type m = Generator<mapped_type>(0)(0); + (void)m; +} + +template <typename K, int N = 256> +void SetTest() { + EXPECT_EQ( + sizeof(absl::btree_set<K>), + 2 * sizeof(void *) + sizeof(typename absl::btree_set<K>::size_type)); + using BtreeSet = absl::btree_set<K>; + using CountingBtreeSet = + absl::btree_set<K, std::less<K>, PropagatingCountingAlloc<K>>; + BtreeTest<BtreeSet, std::set<K>>(); + BtreeAllocatorTest<CountingBtreeSet>(); +} + +template <typename K, int N = 256> +void MapTest() { + EXPECT_EQ( + sizeof(absl::btree_map<K, K>), + 2 * sizeof(void *) + sizeof(typename absl::btree_map<K, K>::size_type)); + using BtreeMap = absl::btree_map<K, K>; + using CountingBtreeMap = + absl::btree_map<K, K, std::less<K>, + PropagatingCountingAlloc<std::pair<const K, K>>>; + BtreeTest<BtreeMap, std::map<K, K>>(); + BtreeAllocatorTest<CountingBtreeMap>(); + BtreeMapTest<BtreeMap>(); +} + +TEST(Btree, set_int32) { SetTest<int32_t>(); } +TEST(Btree, set_int64) { SetTest<int64_t>(); } +TEST(Btree, set_string) { SetTest<std::string>(); } +TEST(Btree, set_pair) { SetTest<std::pair<int, int>>(); } +TEST(Btree, map_int32) { MapTest<int32_t>(); } +TEST(Btree, map_int64) { MapTest<int64_t>(); } +TEST(Btree, map_string) { MapTest<std::string>(); } +TEST(Btree, map_pair) { MapTest<std::pair<int, int>>(); } + +template <typename K, int N = 256> +void MultiSetTest() { + EXPECT_EQ( + sizeof(absl::btree_multiset<K>), + 2 * sizeof(void *) + sizeof(typename absl::btree_multiset<K>::size_type)); + using BtreeMSet = absl::btree_multiset<K>; + using CountingBtreeMSet = + absl::btree_multiset<K, std::less<K>, PropagatingCountingAlloc<K>>; + BtreeMultiTest<BtreeMSet, std::multiset<K>>(); + BtreeAllocatorTest<CountingBtreeMSet>(); +} + +template <typename K, int N = 256> +void MultiMapTest() { + EXPECT_EQ(sizeof(absl::btree_multimap<K, K>), + 2 * sizeof(void *) + + sizeof(typename absl::btree_multimap<K, K>::size_type)); + using BtreeMMap = absl::btree_multimap<K, K>; + using CountingBtreeMMap = + absl::btree_multimap<K, K, std::less<K>, + PropagatingCountingAlloc<std::pair<const K, K>>>; + BtreeMultiTest<BtreeMMap, std::multimap<K, K>>(); + BtreeMultiMapTest<BtreeMMap>(); + BtreeAllocatorTest<CountingBtreeMMap>(); +} + +TEST(Btree, multiset_int32) { MultiSetTest<int32_t>(); } +TEST(Btree, multiset_int64) { MultiSetTest<int64_t>(); } +TEST(Btree, multiset_string) { MultiSetTest<std::string>(); } +TEST(Btree, multiset_pair) { MultiSetTest<std::pair<int, int>>(); } +TEST(Btree, multimap_int32) { MultiMapTest<int32_t>(); } +TEST(Btree, multimap_int64) { MultiMapTest<int64_t>(); } +TEST(Btree, multimap_string) { MultiMapTest<std::string>(); } +TEST(Btree, multimap_pair) { MultiMapTest<std::pair<int, int>>(); } + +struct CompareIntToString { + bool operator()(const std::string &a, const std::string &b) const { + return a < b; + } + bool operator()(const std::string &a, int b) const { + return a < absl::StrCat(b); + } + bool operator()(int a, const std::string &b) const { + return absl::StrCat(a) < b; + } + using is_transparent = void; +}; + +struct NonTransparentCompare { + template <typename T, typename U> + bool operator()(const T &t, const U &u) const { + // Treating all comparators as transparent can cause inefficiencies (see + // N3657 C++ proposal). Test that for comparators without 'is_transparent' + // alias (like this one), we do not attempt heterogeneous lookup. + EXPECT_TRUE((std::is_same<T, U>())); + return t < u; + } +}; + +template <typename T> +bool CanEraseWithEmptyBrace(T t, decltype(t.erase({})) *) { + return true; +} + +template <typename T> +bool CanEraseWithEmptyBrace(T, ...) { + return false; +} + +template <typename T> +void TestHeterogeneous(T table) { + auto lb = table.lower_bound("3"); + EXPECT_EQ(lb, table.lower_bound(3)); + EXPECT_NE(lb, table.lower_bound(4)); + EXPECT_EQ(lb, table.lower_bound({"3"})); + EXPECT_NE(lb, table.lower_bound({})); + + auto ub = table.upper_bound("3"); + EXPECT_EQ(ub, table.upper_bound(3)); + EXPECT_NE(ub, table.upper_bound(5)); + EXPECT_EQ(ub, table.upper_bound({"3"})); + EXPECT_NE(ub, table.upper_bound({})); + + auto er = table.equal_range("3"); + EXPECT_EQ(er, table.equal_range(3)); + EXPECT_NE(er, table.equal_range(4)); + EXPECT_EQ(er, table.equal_range({"3"})); + EXPECT_NE(er, table.equal_range({})); + + auto it = table.find("3"); + EXPECT_EQ(it, table.find(3)); + EXPECT_NE(it, table.find(4)); + EXPECT_EQ(it, table.find({"3"})); + EXPECT_NE(it, table.find({})); + + EXPECT_TRUE(table.contains(3)); + EXPECT_FALSE(table.contains(4)); + EXPECT_TRUE(table.count({"3"})); + EXPECT_FALSE(table.contains({})); + + EXPECT_EQ(1, table.count(3)); + EXPECT_EQ(0, table.count(4)); + EXPECT_EQ(1, table.count({"3"})); + EXPECT_EQ(0, table.count({})); + + auto copy = table; + copy.erase(3); + EXPECT_EQ(table.size() - 1, copy.size()); + copy.erase(4); + EXPECT_EQ(table.size() - 1, copy.size()); + copy.erase({"5"}); + EXPECT_EQ(table.size() - 2, copy.size()); + EXPECT_FALSE(CanEraseWithEmptyBrace(table, nullptr)); + + // Also run it with const T&. + if (std::is_class<T>()) TestHeterogeneous<const T &>(table); +} + +TEST(Btree, HeterogeneousLookup) { + TestHeterogeneous(btree_set<std::string, CompareIntToString>{"1", "3", "5"}); + TestHeterogeneous(btree_map<std::string, int, CompareIntToString>{ + {"1", 1}, {"3", 3}, {"5", 5}}); + TestHeterogeneous( + btree_multiset<std::string, CompareIntToString>{"1", "3", "5"}); + TestHeterogeneous(btree_multimap<std::string, int, CompareIntToString>{ + {"1", 1}, {"3", 3}, {"5", 5}}); + + // Only maps have .at() + btree_map<std::string, int, CompareIntToString> map{ + {"", -1}, {"1", 1}, {"3", 3}, {"5", 5}}; + EXPECT_EQ(1, map.at(1)); + EXPECT_EQ(3, map.at({"3"})); + EXPECT_EQ(-1, map.at({})); + const auto &cmap = map; + EXPECT_EQ(1, cmap.at(1)); + EXPECT_EQ(3, cmap.at({"3"})); + EXPECT_EQ(-1, cmap.at({})); +} + +TEST(Btree, NoHeterogeneousLookupWithoutAlias) { + using StringSet = absl::btree_set<std::string, NonTransparentCompare>; + StringSet s; + ASSERT_TRUE(s.insert("hello").second); + ASSERT_TRUE(s.insert("world").second); + EXPECT_TRUE(s.end() == s.find("blah")); + EXPECT_TRUE(s.begin() == s.lower_bound("hello")); + EXPECT_EQ(1, s.count("world")); + EXPECT_TRUE(s.contains("hello")); + EXPECT_TRUE(s.contains("world")); + EXPECT_FALSE(s.contains("blah")); + + using StringMultiSet = + absl::btree_multiset<std::string, NonTransparentCompare>; + StringMultiSet ms; + ms.insert("hello"); + ms.insert("world"); + ms.insert("world"); + EXPECT_TRUE(ms.end() == ms.find("blah")); + EXPECT_TRUE(ms.begin() == ms.lower_bound("hello")); + EXPECT_EQ(2, ms.count("world")); + EXPECT_TRUE(ms.contains("hello")); + EXPECT_TRUE(ms.contains("world")); + EXPECT_FALSE(ms.contains("blah")); +} + +TEST(Btree, DefaultTransparent) { + { + // `int` does not have a default transparent comparator. + // The input value is converted to key_type. + btree_set<int> s = {1}; + double d = 1.1; + EXPECT_EQ(s.begin(), s.find(d)); + EXPECT_TRUE(s.contains(d)); + } + + { + // `std::string` has heterogeneous support. + btree_set<std::string> s = {"A"}; + EXPECT_EQ(s.begin(), s.find(absl::string_view("A"))); + EXPECT_TRUE(s.contains(absl::string_view("A"))); + } +} + +class StringLike { + public: + StringLike() = default; + + StringLike(const char *s) : s_(s) { // NOLINT + ++constructor_calls_; + } + + bool operator<(const StringLike &a) const { return s_ < a.s_; } + + static void clear_constructor_call_count() { constructor_calls_ = 0; } + + static int constructor_calls() { return constructor_calls_; } + + private: + static int constructor_calls_; + std::string s_; +}; + +int StringLike::constructor_calls_ = 0; + +TEST(Btree, HeterogeneousLookupDoesntDegradePerformance) { + using StringSet = absl::btree_set<StringLike>; + StringSet s; + for (int i = 0; i < 100; ++i) { + ASSERT_TRUE(s.insert(absl::StrCat(i).c_str()).second); + } + StringLike::clear_constructor_call_count(); + s.find("50"); + ASSERT_EQ(1, StringLike::constructor_calls()); + + StringLike::clear_constructor_call_count(); + s.contains("50"); + ASSERT_EQ(1, StringLike::constructor_calls()); + + StringLike::clear_constructor_call_count(); + s.count("50"); + ASSERT_EQ(1, StringLike::constructor_calls()); + + StringLike::clear_constructor_call_count(); + s.lower_bound("50"); + ASSERT_EQ(1, StringLike::constructor_calls()); + + StringLike::clear_constructor_call_count(); + s.upper_bound("50"); + ASSERT_EQ(1, StringLike::constructor_calls()); + + StringLike::clear_constructor_call_count(); + s.equal_range("50"); + ASSERT_EQ(1, StringLike::constructor_calls()); + + StringLike::clear_constructor_call_count(); + s.erase("50"); + ASSERT_EQ(1, StringLike::constructor_calls()); +} + +// Verify that swapping btrees swaps the key comparison functors and that we can +// use non-default constructible comparators. +struct SubstringLess { + SubstringLess() = delete; + explicit SubstringLess(int length) : n(length) {} + bool operator()(const std::string &a, const std::string &b) const { + return absl::string_view(a).substr(0, n) < + absl::string_view(b).substr(0, n); + } + int n; +}; + +TEST(Btree, SwapKeyCompare) { + using SubstringSet = absl::btree_set<std::string, SubstringLess>; + SubstringSet s1(SubstringLess(1), SubstringSet::allocator_type()); + SubstringSet s2(SubstringLess(2), SubstringSet::allocator_type()); + + ASSERT_TRUE(s1.insert("a").second); + ASSERT_FALSE(s1.insert("aa").second); + + ASSERT_TRUE(s2.insert("a").second); + ASSERT_TRUE(s2.insert("aa").second); + ASSERT_FALSE(s2.insert("aaa").second); + + swap(s1, s2); + + ASSERT_TRUE(s1.insert("b").second); + ASSERT_TRUE(s1.insert("bb").second); + ASSERT_FALSE(s1.insert("bbb").second); + + ASSERT_TRUE(s2.insert("b").second); + ASSERT_FALSE(s2.insert("bb").second); +} + +TEST(Btree, UpperBoundRegression) { + // Regress a bug where upper_bound would default-construct a new key_compare + // instead of copying the existing one. + using SubstringSet = absl::btree_set<std::string, SubstringLess>; + SubstringSet my_set(SubstringLess(3)); + my_set.insert("aab"); + my_set.insert("abb"); + // We call upper_bound("aaa"). If this correctly uses the length 3 + // comparator, aaa < aab < abb, so we should get aab as the result. + // If it instead uses the default-constructed length 2 comparator, + // aa == aa < ab, so we'll get abb as our result. + SubstringSet::iterator it = my_set.upper_bound("aaa"); + ASSERT_TRUE(it != my_set.end()); + EXPECT_EQ("aab", *it); +} + +TEST(Btree, Comparison) { + const int kSetSize = 1201; + absl::btree_set<int64_t> my_set; + for (int i = 0; i < kSetSize; ++i) { + my_set.insert(i); + } + absl::btree_set<int64_t> my_set_copy(my_set); + EXPECT_TRUE(my_set_copy == my_set); + EXPECT_TRUE(my_set == my_set_copy); + EXPECT_FALSE(my_set_copy != my_set); + EXPECT_FALSE(my_set != my_set_copy); + + my_set.insert(kSetSize); + EXPECT_FALSE(my_set_copy == my_set); + EXPECT_FALSE(my_set == my_set_copy); + EXPECT_TRUE(my_set_copy != my_set); + EXPECT_TRUE(my_set != my_set_copy); + + my_set.erase(kSetSize - 1); + EXPECT_FALSE(my_set_copy == my_set); + EXPECT_FALSE(my_set == my_set_copy); + EXPECT_TRUE(my_set_copy != my_set); + EXPECT_TRUE(my_set != my_set_copy); + + absl::btree_map<std::string, int64_t> my_map; + for (int i = 0; i < kSetSize; ++i) { + my_map[std::string(i, 'a')] = i; + } + absl::btree_map<std::string, int64_t> my_map_copy(my_map); + EXPECT_TRUE(my_map_copy == my_map); + EXPECT_TRUE(my_map == my_map_copy); + EXPECT_FALSE(my_map_copy != my_map); + EXPECT_FALSE(my_map != my_map_copy); + + ++my_map_copy[std::string(7, 'a')]; + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); + + my_map_copy = my_map; + my_map["hello"] = kSetSize; + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); + + my_map.erase(std::string(kSetSize - 1, 'a')); + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); +} + +TEST(Btree, RangeCtorSanity) { + std::vector<int> ivec; + ivec.push_back(1); + std::map<int, int> imap; + imap.insert(std::make_pair(1, 2)); + absl::btree_multiset<int> tmset(ivec.begin(), ivec.end()); + absl::btree_multimap<int, int> tmmap(imap.begin(), imap.end()); + absl::btree_set<int> tset(ivec.begin(), ivec.end()); + absl::btree_map<int, int> tmap(imap.begin(), imap.end()); + EXPECT_EQ(1, tmset.size()); + EXPECT_EQ(1, tmmap.size()); + EXPECT_EQ(1, tset.size()); + EXPECT_EQ(1, tmap.size()); +} + +TEST(Btree, BtreeMapCanHoldMoveOnlyTypes) { + absl::btree_map<std::string, std::unique_ptr<std::string>> m; + + std::unique_ptr<std::string> &v = m["A"]; + EXPECT_TRUE(v == nullptr); + v.reset(new std::string("X")); + + auto iter = m.find("A"); + EXPECT_EQ("X", *iter->second); +} + +TEST(Btree, InitializerListConstructor) { + absl::btree_set<std::string> set({"a", "b"}); + EXPECT_EQ(set.count("a"), 1); + EXPECT_EQ(set.count("b"), 1); + + absl::btree_multiset<int> mset({1, 1, 4}); + EXPECT_EQ(mset.count(1), 2); + EXPECT_EQ(mset.count(4), 1); + + absl::btree_map<int, int> map({{1, 5}, {2, 10}}); + EXPECT_EQ(map[1], 5); + EXPECT_EQ(map[2], 10); + + absl::btree_multimap<int, int> mmap({{1, 5}, {1, 10}}); + auto range = mmap.equal_range(1); + auto it = range.first; + ASSERT_NE(it, range.second); + EXPECT_EQ(it->second, 5); + ASSERT_NE(++it, range.second); + EXPECT_EQ(it->second, 10); + EXPECT_EQ(++it, range.second); +} + +TEST(Btree, InitializerListInsert) { + absl::btree_set<std::string> set; + set.insert({"a", "b"}); + EXPECT_EQ(set.count("a"), 1); + EXPECT_EQ(set.count("b"), 1); + + absl::btree_multiset<int> mset; + mset.insert({1, 1, 4}); + EXPECT_EQ(mset.count(1), 2); + EXPECT_EQ(mset.count(4), 1); + + absl::btree_map<int, int> map; + map.insert({{1, 5}, {2, 10}}); + // Test that inserting one element using an initializer list also works. + map.insert({3, 15}); + EXPECT_EQ(map[1], 5); + EXPECT_EQ(map[2], 10); + EXPECT_EQ(map[3], 15); + + absl::btree_multimap<int, int> mmap; + mmap.insert({{1, 5}, {1, 10}}); + auto range = mmap.equal_range(1); + auto it = range.first; + ASSERT_NE(it, range.second); + EXPECT_EQ(it->second, 5); + ASSERT_NE(++it, range.second); + EXPECT_EQ(it->second, 10); + EXPECT_EQ(++it, range.second); +} + +template <typename Compare, typename K> +void AssertKeyCompareToAdapted() { + using Adapted = typename key_compare_to_adapter<Compare>::type; + static_assert(!std::is_same<Adapted, Compare>::value, + "key_compare_to_adapter should have adapted this comparator."); + static_assert( + std::is_same<absl::weak_ordering, + absl::result_of_t<Adapted(const K &, const K &)>>::value, + "Adapted comparator should be a key-compare-to comparator."); +} +template <typename Compare, typename K> +void AssertKeyCompareToNotAdapted() { + using Unadapted = typename key_compare_to_adapter<Compare>::type; + static_assert( + std::is_same<Unadapted, Compare>::value, + "key_compare_to_adapter shouldn't have adapted this comparator."); + static_assert( + std::is_same<bool, + absl::result_of_t<Unadapted(const K &, const K &)>>::value, + "Un-adapted comparator should return bool."); +} + +TEST(Btree, KeyCompareToAdapter) { + AssertKeyCompareToAdapted<std::less<std::string>, std::string>(); + AssertKeyCompareToAdapted<std::greater<std::string>, std::string>(); + AssertKeyCompareToAdapted<std::less<absl::string_view>, absl::string_view>(); + AssertKeyCompareToAdapted<std::greater<absl::string_view>, + absl::string_view>(); + AssertKeyCompareToNotAdapted<std::less<int>, int>(); + AssertKeyCompareToNotAdapted<std::greater<int>, int>(); +} + +TEST(Btree, RValueInsert) { + InstanceTracker tracker; + + absl::btree_set<MovableOnlyInstance> set; + set.insert(MovableOnlyInstance(1)); + set.insert(MovableOnlyInstance(3)); + MovableOnlyInstance two(2); + set.insert(set.find(MovableOnlyInstance(3)), std::move(two)); + auto it = set.find(MovableOnlyInstance(2)); + ASSERT_NE(it, set.end()); + ASSERT_NE(++it, set.end()); + EXPECT_EQ(it->value(), 3); + + absl::btree_multiset<MovableOnlyInstance> mset; + MovableOnlyInstance zero(0); + MovableOnlyInstance zero2(0); + mset.insert(std::move(zero)); + mset.insert(mset.find(MovableOnlyInstance(0)), std::move(zero2)); + EXPECT_EQ(mset.count(MovableOnlyInstance(0)), 2); + + absl::btree_map<int, MovableOnlyInstance> map; + std::pair<const int, MovableOnlyInstance> p1 = {1, MovableOnlyInstance(5)}; + std::pair<const int, MovableOnlyInstance> p2 = {2, MovableOnlyInstance(10)}; + std::pair<const int, MovableOnlyInstance> p3 = {3, MovableOnlyInstance(15)}; + map.insert(std::move(p1)); + map.insert(std::move(p3)); + map.insert(map.find(3), std::move(p2)); + ASSERT_NE(map.find(2), map.end()); + EXPECT_EQ(map.find(2)->second.value(), 10); + + absl::btree_multimap<int, MovableOnlyInstance> mmap; + std::pair<const int, MovableOnlyInstance> p4 = {1, MovableOnlyInstance(5)}; + std::pair<const int, MovableOnlyInstance> p5 = {1, MovableOnlyInstance(10)}; + mmap.insert(std::move(p4)); + mmap.insert(mmap.find(1), std::move(p5)); + auto range = mmap.equal_range(1); + auto it1 = range.first; + ASSERT_NE(it1, range.second); + EXPECT_EQ(it1->second.value(), 10); + ASSERT_NE(++it1, range.second); + EXPECT_EQ(it1->second.value(), 5); + EXPECT_EQ(++it1, range.second); + + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.swaps(), 0); +} + +} // namespace + +class BtreeNodePeer { + public: + // Yields the size of a leaf node with a specific number of values. + template <typename ValueType> + constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) { + return btree_node< + set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>, + /*TargetNodeSize=*/256, // This parameter isn't used here. + /*Multi=*/false>>::SizeWithNValues(target_values_per_node); + } + + // Yields the number of values in a (non-root) leaf node for this set. + template <typename Set> + constexpr static size_t GetNumValuesPerNode() { + return btree_node<typename Set::params_type>::kNodeValues; + } +}; + +namespace { + +// A btree set with a specific number of values per node. +template <typename Key, int TargetValuesPerNode, typename Cmp = std::less<Key>> +class SizedBtreeSet + : public btree_set_container<btree< + set_params<Key, Cmp, std::allocator<Key>, + BtreeNodePeer::GetTargetNodeSize<Key>(TargetValuesPerNode), + /*Multi=*/false>>> { + using Base = typename SizedBtreeSet::btree_set_container; + + public: + SizedBtreeSet() {} + using Base::Base; +}; + +template <typename Set> +void ExpectOperationCounts(const int expected_moves, + const int expected_comparisons, + const std::vector<int> &values, + InstanceTracker *tracker, Set *set) { + for (const int v : values) set->insert(MovableOnlyInstance(v)); + set->clear(); + EXPECT_EQ(tracker->moves(), expected_moves); + EXPECT_EQ(tracker->comparisons(), expected_comparisons); + EXPECT_EQ(tracker->copies(), 0); + EXPECT_EQ(tracker->swaps(), 0); + tracker->ResetCopiesMovesSwaps(); +} + +// Note: when the values in this test change, it is expected to have an impact +// on performance. +TEST(Btree, MovesComparisonsCopiesSwapsTracking) { + InstanceTracker tracker; + // Note: this is minimum number of values per node. + SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3> set3; + // Note: this is the default number of values per node for a set of int32s + // (with 64-bit pointers). + SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61> set61; + SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/100> set100; + + // Don't depend on flags for random values because then the expectations will + // fail if the flags change. + std::vector<int> values = + GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23); + + EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3); + EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61); + EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100); + if (sizeof(void *) == 8) { + EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(), + BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>()); + } + + // Test key insertion/deletion in random order. + ExpectOperationCounts(45281, 132551, values, &tracker, &set3); + ExpectOperationCounts(386718, 129807, values, &tracker, &set61); + ExpectOperationCounts(586761, 130310, values, &tracker, &set100); + + // Test key insertion/deletion in sorted order. + std::sort(values.begin(), values.end()); + ExpectOperationCounts(26638, 92134, values, &tracker, &set3); + ExpectOperationCounts(20208, 87757, values, &tracker, &set61); + ExpectOperationCounts(20124, 96583, values, &tracker, &set100); + + // Test key insertion/deletion in reverse sorted order. + std::reverse(values.begin(), values.end()); + ExpectOperationCounts(49951, 119325, values, &tracker, &set3); + ExpectOperationCounts(338813, 118266, values, &tracker, &set61); + ExpectOperationCounts(534529, 125279, values, &tracker, &set100); +} + +struct MovableOnlyInstanceThreeWayCompare { + absl::weak_ordering operator()(const MovableOnlyInstance &a, + const MovableOnlyInstance &b) const { + return a.compare(b); + } +}; + +// Note: when the values in this test change, it is expected to have an impact +// on performance. +TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) { + InstanceTracker tracker; + // Note: this is minimum number of values per node. + SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3, + MovableOnlyInstanceThreeWayCompare> + set3; + // Note: this is the default number of values per node for a set of int32s + // (with 64-bit pointers). + SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61, + MovableOnlyInstanceThreeWayCompare> + set61; + SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/100, + MovableOnlyInstanceThreeWayCompare> + set100; + + // Don't depend on flags for random values because then the expectations will + // fail if the flags change. + std::vector<int> values = + GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23); + + EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3); + EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61); + EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100); + if (sizeof(void *) == 8) { + EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(), + BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>()); + } + + // Test key insertion/deletion in random order. + ExpectOperationCounts(45281, 122560, values, &tracker, &set3); + ExpectOperationCounts(386718, 119816, values, &tracker, &set61); + ExpectOperationCounts(586761, 120319, values, &tracker, &set100); + + // Test key insertion/deletion in sorted order. + std::sort(values.begin(), values.end()); + ExpectOperationCounts(26638, 92134, values, &tracker, &set3); + ExpectOperationCounts(20208, 87757, values, &tracker, &set61); + ExpectOperationCounts(20124, 96583, values, &tracker, &set100); + + // Test key insertion/deletion in reverse sorted order. + std::reverse(values.begin(), values.end()); + ExpectOperationCounts(49951, 109326, values, &tracker, &set3); + ExpectOperationCounts(338813, 108267, values, &tracker, &set61); + ExpectOperationCounts(534529, 115280, values, &tracker, &set100); +} + +struct NoDefaultCtor { + int num; + explicit NoDefaultCtor(int i) : num(i) {} + + friend bool operator<(const NoDefaultCtor &a, const NoDefaultCtor &b) { + return a.num < b.num; + } +}; + +TEST(Btree, BtreeMapCanHoldNoDefaultCtorTypes) { + absl::btree_map<NoDefaultCtor, NoDefaultCtor> m; + + for (int i = 1; i <= 99; ++i) { + SCOPED_TRACE(i); + EXPECT_TRUE(m.emplace(NoDefaultCtor(i), NoDefaultCtor(100 - i)).second); + } + EXPECT_FALSE(m.emplace(NoDefaultCtor(78), NoDefaultCtor(0)).second); + + auto iter99 = m.find(NoDefaultCtor(99)); + ASSERT_NE(iter99, m.end()); + EXPECT_EQ(iter99->second.num, 1); + + auto iter1 = m.find(NoDefaultCtor(1)); + ASSERT_NE(iter1, m.end()); + EXPECT_EQ(iter1->second.num, 99); + + auto iter50 = m.find(NoDefaultCtor(50)); + ASSERT_NE(iter50, m.end()); + EXPECT_EQ(iter50->second.num, 50); + + auto iter25 = m.find(NoDefaultCtor(25)); + ASSERT_NE(iter25, m.end()); + EXPECT_EQ(iter25->second.num, 75); +} + +TEST(Btree, BtreeMultimapCanHoldNoDefaultCtorTypes) { + absl::btree_multimap<NoDefaultCtor, NoDefaultCtor> m; + + for (int i = 1; i <= 99; ++i) { + SCOPED_TRACE(i); + m.emplace(NoDefaultCtor(i), NoDefaultCtor(100 - i)); + } + + auto iter99 = m.find(NoDefaultCtor(99)); + ASSERT_NE(iter99, m.end()); + EXPECT_EQ(iter99->second.num, 1); + + auto iter1 = m.find(NoDefaultCtor(1)); + ASSERT_NE(iter1, m.end()); + EXPECT_EQ(iter1->second.num, 99); + + auto iter50 = m.find(NoDefaultCtor(50)); + ASSERT_NE(iter50, m.end()); + EXPECT_EQ(iter50->second.num, 50); + + auto iter25 = m.find(NoDefaultCtor(25)); + ASSERT_NE(iter25, m.end()); + EXPECT_EQ(iter25->second.num, 75); +} + +TEST(Btree, MapAt) { + absl::btree_map<int, int> map = {{1, 2}, {2, 4}}; + EXPECT_EQ(map.at(1), 2); + EXPECT_EQ(map.at(2), 4); + map.at(2) = 8; + const absl::btree_map<int, int> &const_map = map; + EXPECT_EQ(const_map.at(1), 2); + EXPECT_EQ(const_map.at(2), 8); +#ifdef ABSL_HAVE_EXCEPTIONS + EXPECT_THROW(map.at(3), std::out_of_range); +#else + EXPECT_DEATH(map.at(3), "absl::btree_map::at"); +#endif +} + +TEST(Btree, BtreeMultisetEmplace) { + const int value_to_insert = 123456; + absl::btree_multiset<int> s; + auto iter = s.emplace(value_to_insert); + ASSERT_NE(iter, s.end()); + EXPECT_EQ(*iter, value_to_insert); + auto iter2 = s.emplace(value_to_insert); + EXPECT_NE(iter2, iter); + ASSERT_NE(iter2, s.end()); + EXPECT_EQ(*iter2, value_to_insert); + auto result = s.equal_range(value_to_insert); + EXPECT_EQ(std::distance(result.first, result.second), 2); +} + +TEST(Btree, BtreeMultisetEmplaceHint) { + const int value_to_insert = 123456; + absl::btree_multiset<int> s; + auto iter = s.emplace(value_to_insert); + ASSERT_NE(iter, s.end()); + EXPECT_EQ(*iter, value_to_insert); + auto emplace_iter = s.emplace_hint(iter, value_to_insert); + EXPECT_NE(emplace_iter, iter); + ASSERT_NE(emplace_iter, s.end()); + EXPECT_EQ(*emplace_iter, value_to_insert); +} + +TEST(Btree, BtreeMultimapEmplace) { + const int key_to_insert = 123456; + const char value0[] = "a"; + absl::btree_multimap<int, std::string> s; + auto iter = s.emplace(key_to_insert, value0); + ASSERT_NE(iter, s.end()); + EXPECT_EQ(iter->first, key_to_insert); + EXPECT_EQ(iter->second, value0); + const char value1[] = "b"; + auto iter2 = s.emplace(key_to_insert, value1); + EXPECT_NE(iter2, iter); + ASSERT_NE(iter2, s.end()); + EXPECT_EQ(iter2->first, key_to_insert); + EXPECT_EQ(iter2->second, value1); + auto result = s.equal_range(key_to_insert); + EXPECT_EQ(std::distance(result.first, result.second), 2); +} + +TEST(Btree, BtreeMultimapEmplaceHint) { + const int key_to_insert = 123456; + const char value0[] = "a"; + absl::btree_multimap<int, std::string> s; + auto iter = s.emplace(key_to_insert, value0); + ASSERT_NE(iter, s.end()); + EXPECT_EQ(iter->first, key_to_insert); + EXPECT_EQ(iter->second, value0); + const char value1[] = "b"; + auto emplace_iter = s.emplace_hint(iter, key_to_insert, value1); + EXPECT_NE(emplace_iter, iter); + ASSERT_NE(emplace_iter, s.end()); + EXPECT_EQ(emplace_iter->first, key_to_insert); + EXPECT_EQ(emplace_iter->second, value1); +} + +TEST(Btree, ConstIteratorAccessors) { + absl::btree_set<int> set; + for (int i = 0; i < 100; ++i) { + set.insert(i); + } + + auto it = set.cbegin(); + auto r_it = set.crbegin(); + for (int i = 0; i < 100; ++i, ++it, ++r_it) { + ASSERT_EQ(*it, i); + ASSERT_EQ(*r_it, 99 - i); + } + EXPECT_EQ(it, set.cend()); + EXPECT_EQ(r_it, set.crend()); +} + +TEST(Btree, StrSplitCompatible) { + const absl::btree_set<std::string> split_set = absl::StrSplit("a,b,c", ','); + const absl::btree_set<std::string> expected_set = {"a", "b", "c"}; + + EXPECT_EQ(split_set, expected_set); +} + +// We can't use EXPECT_EQ/etc. to compare absl::weak_ordering because they +// convert literal 0 to int and absl::weak_ordering can only be compared with +// literal 0. Defining this function allows for avoiding ClangTidy warnings. +bool Identity(const bool b) { return b; } + +TEST(Btree, ValueComp) { + absl::btree_set<int> s; + EXPECT_TRUE(s.value_comp()(1, 2)); + EXPECT_FALSE(s.value_comp()(2, 2)); + EXPECT_FALSE(s.value_comp()(2, 1)); + + absl::btree_map<int, int> m1; + EXPECT_TRUE(m1.value_comp()(std::make_pair(1, 0), std::make_pair(2, 0))); + EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(2, 0))); + EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(1, 0))); + + absl::btree_map<std::string, int> m2; + EXPECT_TRUE(Identity( + m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)) < 0)); + EXPECT_TRUE(Identity( + m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)) == 0)); + EXPECT_TRUE(Identity( + m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)) > 0)); +} + +TEST(Btree, DefaultConstruction) { + absl::btree_set<int> s; + absl::btree_map<int, int> m; + absl::btree_multiset<int> ms; + absl::btree_multimap<int, int> mm; + + EXPECT_TRUE(s.empty()); + EXPECT_TRUE(m.empty()); + EXPECT_TRUE(ms.empty()); + EXPECT_TRUE(mm.empty()); +} + +TEST(Btree, SwissTableHashable) { + static constexpr int kValues = 10000; + std::vector<int> values(kValues); + std::iota(values.begin(), values.end(), 0); + std::vector<std::pair<int, int>> map_values; + for (int v : values) map_values.emplace_back(v, -v); + + using set = absl::btree_set<int>; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ + set{}, + set{1}, + set{2}, + set{1, 2}, + set{2, 1}, + set(values.begin(), values.end()), + set(values.rbegin(), values.rend()), + })); + + using mset = absl::btree_multiset<int>; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ + mset{}, + mset{1}, + mset{1, 1}, + mset{2}, + mset{2, 2}, + mset{1, 2}, + mset{1, 1, 2}, + mset{1, 2, 2}, + mset{1, 1, 2, 2}, + mset(values.begin(), values.end()), + mset(values.rbegin(), values.rend()), + })); + + using map = absl::btree_map<int, int>; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ + map{}, + map{{1, 0}}, + map{{1, 1}}, + map{{2, 0}}, + map{{2, 2}}, + map{{1, 0}, {2, 1}}, + map(map_values.begin(), map_values.end()), + map(map_values.rbegin(), map_values.rend()), + })); + + using mmap = absl::btree_multimap<int, int>; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ + mmap{}, + mmap{{1, 0}}, + mmap{{1, 1}}, + mmap{{1, 0}, {1, 1}}, + mmap{{1, 1}, {1, 0}}, + mmap{{2, 0}}, + mmap{{2, 2}}, + mmap{{1, 0}, {2, 1}}, + mmap(map_values.begin(), map_values.end()), + mmap(map_values.rbegin(), map_values.rend()), + })); +} + +TEST(Btree, ComparableSet) { + absl::btree_set<int> s1 = {1, 2}; + absl::btree_set<int> s2 = {2, 3}; + EXPECT_LT(s1, s2); + EXPECT_LE(s1, s2); + EXPECT_LE(s1, s1); + EXPECT_GT(s2, s1); + EXPECT_GE(s2, s1); + EXPECT_GE(s1, s1); +} + +TEST(Btree, ComparableSetsDifferentLength) { + absl::btree_set<int> s1 = {1, 2}; + absl::btree_set<int> s2 = {1, 2, 3}; + EXPECT_LT(s1, s2); + EXPECT_LE(s1, s2); + EXPECT_GT(s2, s1); + EXPECT_GE(s2, s1); +} + +TEST(Btree, ComparableMultiset) { + absl::btree_multiset<int> s1 = {1, 2}; + absl::btree_multiset<int> s2 = {2, 3}; + EXPECT_LT(s1, s2); + EXPECT_LE(s1, s2); + EXPECT_LE(s1, s1); + EXPECT_GT(s2, s1); + EXPECT_GE(s2, s1); + EXPECT_GE(s1, s1); +} + +TEST(Btree, ComparableMap) { + absl::btree_map<int, int> s1 = {{1, 2}}; + absl::btree_map<int, int> s2 = {{2, 3}}; + EXPECT_LT(s1, s2); + EXPECT_LE(s1, s2); + EXPECT_LE(s1, s1); + EXPECT_GT(s2, s1); + EXPECT_GE(s2, s1); + EXPECT_GE(s1, s1); +} + +TEST(Btree, ComparableMultimap) { + absl::btree_multimap<int, int> s1 = {{1, 2}}; + absl::btree_multimap<int, int> s2 = {{2, 3}}; + EXPECT_LT(s1, s2); + EXPECT_LE(s1, s2); + EXPECT_LE(s1, s1); + EXPECT_GT(s2, s1); + EXPECT_GE(s2, s1); + EXPECT_GE(s1, s1); +} + +TEST(Btree, ComparableSetWithCustomComparator) { + // As specified by + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf section + // [container.requirements.general].12, ordering associative containers always + // uses default '<' operator + // - even if otherwise the container uses custom functor. + absl::btree_set<int, std::greater<int>> s1 = {1, 2}; + absl::btree_set<int, std::greater<int>> s2 = {2, 3}; + EXPECT_LT(s1, s2); + EXPECT_LE(s1, s2); + EXPECT_LE(s1, s1); + EXPECT_GT(s2, s1); + EXPECT_GE(s2, s1); + EXPECT_GE(s1, s1); +} + +TEST(Btree, EraseReturnsIterator) { + absl::btree_set<int> set = {1, 2, 3, 4, 5}; + auto result_it = set.erase(set.begin(), set.find(3)); + EXPECT_EQ(result_it, set.find(3)); + result_it = set.erase(set.find(5)); + EXPECT_EQ(result_it, set.end()); +} + +TEST(Btree, ExtractAndInsertNodeHandleSet) { + absl::btree_set<int> src1 = {1, 2, 3, 4, 5}; + auto nh = src1.extract(src1.find(3)); + EXPECT_THAT(src1, ElementsAre(1, 2, 4, 5)); + absl::btree_set<int> other; + absl::btree_set<int>::insert_return_type res = other.insert(std::move(nh)); + EXPECT_THAT(other, ElementsAre(3)); + EXPECT_EQ(res.position, other.find(3)); + EXPECT_TRUE(res.inserted); + EXPECT_TRUE(res.node.empty()); + + absl::btree_set<int> src2 = {3, 4}; + nh = src2.extract(src2.find(3)); + EXPECT_THAT(src2, ElementsAre(4)); + res = other.insert(std::move(nh)); + EXPECT_THAT(other, ElementsAre(3)); + EXPECT_EQ(res.position, other.find(3)); + EXPECT_FALSE(res.inserted); + ASSERT_FALSE(res.node.empty()); + EXPECT_EQ(res.node.value(), 3); +} + +template <typename Set> +void TestExtractWithTrackingForSet() { + InstanceTracker tracker; + { + Set s; + // Add enough elements to make sure we test internal nodes too. + const size_t kSize = 1000; + while (s.size() < kSize) { + s.insert(MovableOnlyInstance(s.size())); + } + for (int i = 0; i < kSize; ++i) { + // Extract with key + auto nh = s.extract(MovableOnlyInstance(i)); + EXPECT_EQ(s.size(), kSize - 1); + EXPECT_EQ(nh.value().value(), i); + // Insert with node + s.insert(std::move(nh)); + EXPECT_EQ(s.size(), kSize); + + // Extract with iterator + auto it = s.find(MovableOnlyInstance(i)); + nh = s.extract(it); + EXPECT_EQ(s.size(), kSize - 1); + EXPECT_EQ(nh.value().value(), i); + // Insert with node and hint + s.insert(s.begin(), std::move(nh)); + EXPECT_EQ(s.size(), kSize); + } + } + EXPECT_EQ(0, tracker.instances()); +} + +template <typename Map> +void TestExtractWithTrackingForMap() { + InstanceTracker tracker; + { + Map m; + // Add enough elements to make sure we test internal nodes too. + const size_t kSize = 1000; + while (m.size() < kSize) { + m.insert( + {CopyableMovableInstance(m.size()), MovableOnlyInstance(m.size())}); + } + for (int i = 0; i < kSize; ++i) { + // Extract with key + auto nh = m.extract(CopyableMovableInstance(i)); + EXPECT_EQ(m.size(), kSize - 1); + EXPECT_EQ(nh.key().value(), i); + EXPECT_EQ(nh.mapped().value(), i); + // Insert with node + m.insert(std::move(nh)); + EXPECT_EQ(m.size(), kSize); + + // Extract with iterator + auto it = m.find(CopyableMovableInstance(i)); + nh = m.extract(it); + EXPECT_EQ(m.size(), kSize - 1); + EXPECT_EQ(nh.key().value(), i); + EXPECT_EQ(nh.mapped().value(), i); + // Insert with node and hint + m.insert(m.begin(), std::move(nh)); + EXPECT_EQ(m.size(), kSize); + } + } + EXPECT_EQ(0, tracker.instances()); +} + +TEST(Btree, ExtractTracking) { + TestExtractWithTrackingForSet<absl::btree_set<MovableOnlyInstance>>(); + TestExtractWithTrackingForSet<absl::btree_multiset<MovableOnlyInstance>>(); + TestExtractWithTrackingForMap< + absl::btree_map<CopyableMovableInstance, MovableOnlyInstance>>(); + TestExtractWithTrackingForMap< + absl::btree_multimap<CopyableMovableInstance, MovableOnlyInstance>>(); +} + +TEST(Btree, ExtractAndInsertNodeHandleMultiSet) { + absl::btree_multiset<int> src1 = {1, 2, 3, 3, 4, 5}; + auto nh = src1.extract(src1.find(3)); + EXPECT_THAT(src1, ElementsAre(1, 2, 3, 4, 5)); + absl::btree_multiset<int> other; + auto res = other.insert(std::move(nh)); + EXPECT_THAT(other, ElementsAre(3)); + EXPECT_EQ(res, other.find(3)); + + absl::btree_multiset<int> src2 = {3, 4}; + nh = src2.extract(src2.find(3)); + EXPECT_THAT(src2, ElementsAre(4)); + res = other.insert(std::move(nh)); + EXPECT_THAT(other, ElementsAre(3, 3)); + EXPECT_EQ(res, ++other.find(3)); +} + +TEST(Btree, ExtractAndInsertNodeHandleMap) { + absl::btree_map<int, int> src1 = {{1, 2}, {3, 4}, {5, 6}}; + auto nh = src1.extract(src1.find(3)); + EXPECT_THAT(src1, ElementsAre(Pair(1, 2), Pair(5, 6))); + absl::btree_map<int, int> other; + absl::btree_map<int, int>::insert_return_type res = + other.insert(std::move(nh)); + EXPECT_THAT(other, ElementsAre(Pair(3, 4))); + EXPECT_EQ(res.position, other.find(3)); + EXPECT_TRUE(res.inserted); + EXPECT_TRUE(res.node.empty()); + + absl::btree_map<int, int> src2 = {{3, 6}}; + nh = src2.extract(src2.find(3)); + EXPECT_TRUE(src2.empty()); + res = other.insert(std::move(nh)); + EXPECT_THAT(other, ElementsAre(Pair(3, 4))); + EXPECT_EQ(res.position, other.find(3)); + EXPECT_FALSE(res.inserted); + ASSERT_FALSE(res.node.empty()); + EXPECT_EQ(res.node.key(), 3); + EXPECT_EQ(res.node.mapped(), 6); +} + +TEST(Btree, ExtractAndInsertNodeHandleMultiMap) { + absl::btree_multimap<int, int> src1 = {{1, 2}, {3, 4}, {5, 6}}; + auto nh = src1.extract(src1.find(3)); + EXPECT_THAT(src1, ElementsAre(Pair(1, 2), Pair(5, 6))); + absl::btree_multimap<int, int> other; + auto res = other.insert(std::move(nh)); + EXPECT_THAT(other, ElementsAre(Pair(3, 4))); + EXPECT_EQ(res, other.find(3)); + + absl::btree_multimap<int, int> src2 = {{3, 6}}; + nh = src2.extract(src2.find(3)); + EXPECT_TRUE(src2.empty()); + res = other.insert(std::move(nh)); + EXPECT_THAT(other, ElementsAre(Pair(3, 4), Pair(3, 6))); + EXPECT_EQ(res, ++other.begin()); +} + +// For multisets, insert with hint also affects correctness because we need to +// insert immediately before the hint if possible. +struct InsertMultiHintData { + int key; + int not_key; + bool operator==(const InsertMultiHintData other) const { + return key == other.key && not_key == other.not_key; + } +}; + +struct InsertMultiHintDataKeyCompare { + using is_transparent = void; + bool operator()(const InsertMultiHintData a, + const InsertMultiHintData b) const { + return a.key < b.key; + } + bool operator()(const int a, const InsertMultiHintData b) const { + return a < b.key; + } + bool operator()(const InsertMultiHintData a, const int b) const { + return a.key < b; + } +}; + +TEST(Btree, InsertHintNodeHandle) { + // For unique sets, insert with hint is just a performance optimization. + // Test that insert works correctly when the hint is right or wrong. + { + absl::btree_set<int> src = {1, 2, 3, 4, 5}; + auto nh = src.extract(src.find(3)); + EXPECT_THAT(src, ElementsAre(1, 2, 4, 5)); + absl::btree_set<int> other = {0, 100}; + // Test a correct hint. + auto it = other.insert(other.lower_bound(3), std::move(nh)); + EXPECT_THAT(other, ElementsAre(0, 3, 100)); + EXPECT_EQ(it, other.find(3)); + + nh = src.extract(src.find(5)); + // Test an incorrect hint. + it = other.insert(other.end(), std::move(nh)); + EXPECT_THAT(other, ElementsAre(0, 3, 5, 100)); + EXPECT_EQ(it, other.find(5)); + } + + absl::btree_multiset<InsertMultiHintData, InsertMultiHintDataKeyCompare> src = + {{1, 2}, {3, 4}, {3, 5}}; + auto nh = src.extract(src.lower_bound(3)); + EXPECT_EQ(nh.value(), (InsertMultiHintData{3, 4})); + absl::btree_multiset<InsertMultiHintData, InsertMultiHintDataKeyCompare> + other = {{3, 1}, {3, 2}, {3, 3}}; + auto it = other.insert(--other.end(), std::move(nh)); + EXPECT_THAT( + other, ElementsAre(InsertMultiHintData{3, 1}, InsertMultiHintData{3, 2}, + InsertMultiHintData{3, 4}, InsertMultiHintData{3, 3})); + EXPECT_EQ(it, --(--other.end())); + + nh = src.extract(src.find(3)); + EXPECT_EQ(nh.value(), (InsertMultiHintData{3, 5})); + it = other.insert(other.begin(), std::move(nh)); + EXPECT_THAT(other, + ElementsAre(InsertMultiHintData{3, 5}, InsertMultiHintData{3, 1}, + InsertMultiHintData{3, 2}, InsertMultiHintData{3, 4}, + InsertMultiHintData{3, 3})); + EXPECT_EQ(it, other.begin()); +} + +struct IntCompareToCmp { + absl::weak_ordering operator()(int a, int b) const { + if (a < b) return absl::weak_ordering::less; + if (a > b) return absl::weak_ordering::greater; + return absl::weak_ordering::equivalent; + } +}; + +TEST(Btree, MergeIntoUniqueContainers) { + absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3}; + absl::btree_multiset<int> src2 = {3, 4, 4, 5}; + absl::btree_set<int> dst; + + dst.merge(src1); + EXPECT_TRUE(src1.empty()); + EXPECT_THAT(dst, ElementsAre(1, 2, 3)); + dst.merge(src2); + EXPECT_THAT(src2, ElementsAre(3, 4)); + EXPECT_THAT(dst, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(Btree, MergeIntoUniqueContainersWithCompareTo) { + absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3}; + absl::btree_multiset<int> src2 = {3, 4, 4, 5}; + absl::btree_set<int, IntCompareToCmp> dst; + + dst.merge(src1); + EXPECT_TRUE(src1.empty()); + EXPECT_THAT(dst, ElementsAre(1, 2, 3)); + dst.merge(src2); + EXPECT_THAT(src2, ElementsAre(3, 4)); + EXPECT_THAT(dst, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(Btree, MergeIntoMultiContainers) { + absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3}; + absl::btree_multiset<int> src2 = {3, 4, 4, 5}; + absl::btree_multiset<int> dst; + + dst.merge(src1); + EXPECT_TRUE(src1.empty()); + EXPECT_THAT(dst, ElementsAre(1, 2, 3)); + dst.merge(src2); + EXPECT_TRUE(src2.empty()); + EXPECT_THAT(dst, ElementsAre(1, 2, 3, 3, 4, 4, 5)); +} + +TEST(Btree, MergeIntoMultiContainersWithCompareTo) { + absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3}; + absl::btree_multiset<int> src2 = {3, 4, 4, 5}; + absl::btree_multiset<int, IntCompareToCmp> dst; + + dst.merge(src1); + EXPECT_TRUE(src1.empty()); + EXPECT_THAT(dst, ElementsAre(1, 2, 3)); + dst.merge(src2); + EXPECT_TRUE(src2.empty()); + EXPECT_THAT(dst, ElementsAre(1, 2, 3, 3, 4, 4, 5)); +} + +TEST(Btree, MergeIntoMultiMapsWithDifferentComparators) { + absl::btree_map<int, int, IntCompareToCmp> src1 = {{1, 1}, {2, 2}, {3, 3}}; + absl::btree_multimap<int, int, std::greater<int>> src2 = { + {5, 5}, {4, 1}, {4, 4}, {3, 2}}; + absl::btree_multimap<int, int> dst; + + dst.merge(src1); + EXPECT_TRUE(src1.empty()); + EXPECT_THAT(dst, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3))); + dst.merge(src2); + EXPECT_TRUE(src2.empty()); + EXPECT_THAT(dst, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), Pair(3, 2), + Pair(4, 1), Pair(4, 4), Pair(5, 5))); +} + +struct KeyCompareToWeakOrdering { + template <typename T> + absl::weak_ordering operator()(const T &a, const T &b) const { + return a < b ? absl::weak_ordering::less + : a == b ? absl::weak_ordering::equivalent + : absl::weak_ordering::greater; + } +}; + +struct KeyCompareToStrongOrdering { + template <typename T> + absl::strong_ordering operator()(const T &a, const T &b) const { + return a < b ? absl::strong_ordering::less + : a == b ? absl::strong_ordering::equal + : absl::strong_ordering::greater; + } +}; + +TEST(Btree, UserProvidedKeyCompareToComparators) { + absl::btree_set<int, KeyCompareToWeakOrdering> weak_set = {1, 2, 3}; + EXPECT_TRUE(weak_set.contains(2)); + EXPECT_FALSE(weak_set.contains(4)); + + absl::btree_set<int, KeyCompareToStrongOrdering> strong_set = {1, 2, 3}; + EXPECT_TRUE(strong_set.contains(2)); + EXPECT_FALSE(strong_set.contains(4)); +} + +TEST(Btree, TryEmplaceBasicTest) { + absl::btree_map<int, std::string> m; + + // Should construct a std::string from the literal. + m.try_emplace(1, "one"); + EXPECT_EQ(1, m.size()); + + // Try other std::string constructors and const lvalue key. + const int key(42); + m.try_emplace(key, 3, 'a'); + m.try_emplace(2, std::string("two")); + + EXPECT_TRUE(std::is_sorted(m.begin(), m.end())); + EXPECT_THAT(m, ElementsAreArray(std::vector<std::pair<int, std::string>>{ + {1, "one"}, {2, "two"}, {42, "aaa"}})); +} + +TEST(Btree, TryEmplaceWithHintWorks) { + // Use a counting comparator here to verify that hint is used. + int calls = 0; + auto cmp = [&calls](int x, int y) { + ++calls; + return x < y; + }; + using Cmp = decltype(cmp); + + absl::btree_map<int, int, Cmp> m(cmp); + for (int i = 0; i < 128; ++i) { + m.emplace(i, i); + } + + // Sanity check for the comparator + calls = 0; + m.emplace(127, 127); + EXPECT_GE(calls, 4); + + // Try with begin hint: + calls = 0; + auto it = m.try_emplace(m.begin(), -1, -1); + EXPECT_EQ(129, m.size()); + EXPECT_EQ(it, m.begin()); + EXPECT_LE(calls, 2); + + // Try with end hint: + calls = 0; + std::pair<int, int> pair1024 = {1024, 1024}; + it = m.try_emplace(m.end(), pair1024.first, pair1024.second); + EXPECT_EQ(130, m.size()); + EXPECT_EQ(it, --m.end()); + EXPECT_LE(calls, 2); + + // Try value already present, bad hint; ensure no duplicate added: + calls = 0; + it = m.try_emplace(m.end(), 16, 17); + EXPECT_EQ(130, m.size()); + EXPECT_GE(calls, 4); + EXPECT_EQ(it, m.find(16)); + + // Try value already present, hint points directly to it: + calls = 0; + it = m.try_emplace(it, 16, 17); + EXPECT_EQ(130, m.size()); + EXPECT_LE(calls, 2); + EXPECT_EQ(it, m.find(16)); + + m.erase(2); + EXPECT_EQ(129, m.size()); + auto hint = m.find(3); + // Try emplace in the middle of two other elements. + calls = 0; + m.try_emplace(hint, 2, 2); + EXPECT_EQ(130, m.size()); + EXPECT_LE(calls, 2); + + EXPECT_TRUE(std::is_sorted(m.begin(), m.end())); +} + +TEST(Btree, TryEmplaceWithBadHint) { + absl::btree_map<int, int> m = {{1, 1}, {9, 9}}; + + // Bad hint (too small), should still emplace: + auto it = m.try_emplace(m.begin(), 2, 2); + EXPECT_EQ(it, ++m.begin()); + EXPECT_THAT(m, ElementsAreArray( + std::vector<std::pair<int, int>>{{1, 1}, {2, 2}, {9, 9}})); + + // Bad hint, too large this time: + it = m.try_emplace(++(++m.begin()), 0, 0); + EXPECT_EQ(it, m.begin()); + EXPECT_THAT(m, ElementsAreArray(std::vector<std::pair<int, int>>{ + {0, 0}, {1, 1}, {2, 2}, {9, 9}})); +} + +TEST(Btree, TryEmplaceMaintainsSortedOrder) { + absl::btree_map<int, std::string> m; + std::pair<int, std::string> pair5 = {5, "five"}; + + // Test both lvalue & rvalue emplace. + m.try_emplace(10, "ten"); + m.try_emplace(pair5.first, pair5.second); + EXPECT_EQ(2, m.size()); + EXPECT_TRUE(std::is_sorted(m.begin(), m.end())); + + int int100{100}; + m.try_emplace(int100, "hundred"); + m.try_emplace(1, "one"); + EXPECT_EQ(4, m.size()); + EXPECT_TRUE(std::is_sorted(m.begin(), m.end())); +} + +TEST(Btree, TryEmplaceWithHintAndNoValueArgsWorks) { + absl::btree_map<int, int> m; + m.try_emplace(m.end(), 1); + EXPECT_EQ(0, m[1]); +} + +TEST(Btree, TryEmplaceWithHintAndMultipleValueArgsWorks) { + absl::btree_map<int, std::string> m; + m.try_emplace(m.end(), 1, 10, 'a'); + EXPECT_EQ(std::string(10, 'a'), m[1]); +} + +TEST(Btree, MoveAssignmentAllocatorPropagation) { + InstanceTracker tracker; + + int64_t bytes1 = 0, bytes2 = 0; + PropagatingCountingAlloc<MovableOnlyInstance> allocator1(&bytes1); + PropagatingCountingAlloc<MovableOnlyInstance> allocator2(&bytes2); + std::less<MovableOnlyInstance> cmp; + + // Test propagating allocator_type. + { + absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>, + PropagatingCountingAlloc<MovableOnlyInstance>> + set1(cmp, allocator1), set2(cmp, allocator2); + + for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i)); + + tracker.ResetCopiesMovesSwaps(); + set2 = std::move(set1); + EXPECT_EQ(tracker.moves(), 0); + } + // Test non-propagating allocator_type with equal allocators. + { + absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>, + CountingAllocator<MovableOnlyInstance>> + set1(cmp, allocator1), set2(cmp, allocator1); + + for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i)); + + tracker.ResetCopiesMovesSwaps(); + set2 = std::move(set1); + EXPECT_EQ(tracker.moves(), 0); + } + // Test non-propagating allocator_type with different allocators. + { + absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>, + CountingAllocator<MovableOnlyInstance>> + set1(cmp, allocator1), set2(cmp, allocator2); + + for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i)); + + tracker.ResetCopiesMovesSwaps(); + set2 = std::move(set1); + EXPECT_GE(tracker.moves(), 100); + } +} + +TEST(Btree, EmptyTree) { + absl::btree_set<int> s; + EXPECT_TRUE(s.empty()); + EXPECT_EQ(s.size(), 0); + EXPECT_GT(s.max_size(), 0); +} + +bool IsEven(int k) { return k % 2 == 0; } + +TEST(Btree, EraseIf) { + // Test that erase_if works with all the container types and supports lambdas. + { + absl::btree_set<int> s = {1, 3, 5, 6, 100}; + erase_if(s, [](int k) { return k > 3; }); + EXPECT_THAT(s, ElementsAre(1, 3)); + } + { + absl::btree_multiset<int> s = {1, 3, 3, 5, 6, 6, 100}; + erase_if(s, [](int k) { return k <= 3; }); + EXPECT_THAT(s, ElementsAre(5, 6, 6, 100)); + } + { + absl::btree_map<int, int> m = {{1, 1}, {3, 3}, {6, 6}, {100, 100}}; + erase_if(m, [](std::pair<const int, int> kv) { return kv.first > 3; }); + EXPECT_THAT(m, ElementsAre(Pair(1, 1), Pair(3, 3))); + } + { + absl::btree_multimap<int, int> m = {{1, 1}, {3, 3}, {3, 6}, + {6, 6}, {6, 7}, {100, 6}}; + erase_if(m, [](std::pair<const int, int> kv) { return kv.second == 6; }); + EXPECT_THAT(m, ElementsAre(Pair(1, 1), Pair(3, 3), Pair(6, 7))); + } + // Test that erasing all elements from a large set works and test support for + // function pointers. + { + absl::btree_set<int> s; + for (int i = 0; i < 1000; ++i) s.insert(2 * i); + erase_if(s, IsEven); + EXPECT_THAT(s, IsEmpty()); + } + // Test that erase_if supports other format of function pointers. + { + absl::btree_set<int> s = {1, 3, 5, 6, 100}; + erase_if(s, &IsEven); + EXPECT_THAT(s, ElementsAre(1, 3, 5)); + } +} + +TEST(Btree, InsertOrAssign) { + absl::btree_map<int, int> m = {{1, 1}, {3, 3}}; + using value_type = typename decltype(m)::value_type; + + auto ret = m.insert_or_assign(4, 4); + EXPECT_EQ(*ret.first, value_type(4, 4)); + EXPECT_TRUE(ret.second); + ret = m.insert_or_assign(3, 100); + EXPECT_EQ(*ret.first, value_type(3, 100)); + EXPECT_FALSE(ret.second); + + auto hint_ret = m.insert_or_assign(ret.first, 3, 200); + EXPECT_EQ(*hint_ret, value_type(3, 200)); + hint_ret = m.insert_or_assign(m.find(1), 0, 1); + EXPECT_EQ(*hint_ret, value_type(0, 1)); + // Test with bad hint. + hint_ret = m.insert_or_assign(m.end(), -1, 1); + EXPECT_EQ(*hint_ret, value_type(-1, 1)); + + EXPECT_THAT(m, ElementsAre(Pair(-1, 1), Pair(0, 1), Pair(1, 1), Pair(3, 200), + Pair(4, 4))); +} + +TEST(Btree, InsertOrAssignMovableOnly) { + absl::btree_map<int, MovableOnlyInstance> m; + using value_type = typename decltype(m)::value_type; + + auto ret = m.insert_or_assign(4, MovableOnlyInstance(4)); + EXPECT_EQ(*ret.first, value_type(4, MovableOnlyInstance(4))); + EXPECT_TRUE(ret.second); + ret = m.insert_or_assign(4, MovableOnlyInstance(100)); + EXPECT_EQ(*ret.first, value_type(4, MovableOnlyInstance(100))); + EXPECT_FALSE(ret.second); + + auto hint_ret = m.insert_or_assign(ret.first, 3, MovableOnlyInstance(200)); + EXPECT_EQ(*hint_ret, value_type(3, MovableOnlyInstance(200))); + + EXPECT_EQ(m.size(), 2); +} + +TEST(Btree, BitfieldArgument) { + union { + int n : 1; + }; + n = 0; + absl::btree_map<int, int> m; + m.erase(n); + m.count(n); + m.find(n); + m.contains(n); + m.equal_range(n); + m.insert_or_assign(n, n); + m.insert_or_assign(m.end(), n, n); + m.try_emplace(n); + m.try_emplace(m.end(), n); + m.at(n); + m[n]; +} + +} // namespace +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/container/btree_test.h b/absl/container/btree_test.h new file mode 100644 index 00000000..218ba41d --- /dev/null +++ b/absl/container/btree_test.h @@ -0,0 +1,155 @@ +// 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_BTREE_TEST_H_ +#define ABSL_CONTAINER_BTREE_TEST_H_ + +#include <algorithm> +#include <cassert> +#include <random> +#include <string> +#include <utility> +#include <vector> + +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" +#include "absl/container/flat_hash_set.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Like remove_const but propagates the removal through std::pair. +template <typename T> +struct remove_pair_const { + using type = typename std::remove_const<T>::type; +}; +template <typename T, typename U> +struct remove_pair_const<std::pair<T, U> > { + using type = std::pair<typename remove_pair_const<T>::type, + typename remove_pair_const<U>::type>; +}; + +// Utility class to provide an accessor for a key given a value. The default +// behavior is to treat the value as a pair and return the first element. +template <typename K, typename V> +struct KeyOfValue { + struct type { + const K& operator()(const V& p) const { return p.first; } + }; +}; + +// Partial specialization of KeyOfValue class for when the key and value are +// the same type such as in set<> and btree_set<>. +template <typename K> +struct KeyOfValue<K, K> { + struct type { + const K& operator()(const K& k) const { return k; } + }; +}; + +inline char* GenerateDigits(char buf[16], unsigned val, unsigned maxval) { + assert(val <= maxval); + constexpr unsigned kBase = 64; // avoid integer division. + unsigned p = 15; + buf[p--] = 0; + while (maxval > 0) { + buf[p--] = ' ' + (val % kBase); + val /= kBase; + maxval /= kBase; + } + return buf + p + 1; +} + +template <typename K> +struct Generator { + int maxval; + explicit Generator(int m) : maxval(m) {} + K operator()(int i) const { + assert(i <= maxval); + return K(i); + } +}; + +template <> +struct Generator<absl::Time> { + int maxval; + explicit Generator(int m) : maxval(m) {} + absl::Time operator()(int i) const { return absl::FromUnixMillis(i); } +}; + +template <> +struct Generator<std::string> { + int maxval; + explicit Generator(int m) : maxval(m) {} + std::string operator()(int i) const { + char buf[16]; + return GenerateDigits(buf, i, maxval); + } +}; + +template <typename T, typename U> +struct Generator<std::pair<T, U> > { + Generator<typename remove_pair_const<T>::type> tgen; + Generator<typename remove_pair_const<U>::type> ugen; + + explicit Generator(int m) : tgen(m), ugen(m) {} + std::pair<T, U> operator()(int i) const { + return std::make_pair(tgen(i), ugen(i)); + } +}; + +// Generate n values for our tests and benchmarks. Value range is [0, maxval]. +inline std::vector<int> GenerateNumbersWithSeed(int n, int maxval, int seed) { + // NOTE: Some tests rely on generated numbers not changing between test runs. + // We use std::minstd_rand0 because it is well-defined, but don't use + // std::uniform_int_distribution because platforms use different algorithms. + std::minstd_rand0 rng(seed); + + std::vector<int> values; + absl::flat_hash_set<int> unique_values; + if (values.size() < n) { + for (int i = values.size(); i < n; i++) { + int value; + do { + value = static_cast<int>(rng()) % (maxval + 1); + } while (!unique_values.insert(value).second); + + values.push_back(value); + } + } + return values; +} + +// Generates n values in the range [0, maxval]. +template <typename V> +std::vector<V> GenerateValuesWithSeed(int n, int maxval, int seed) { + const std::vector<int> nums = GenerateNumbersWithSeed(n, maxval, seed); + Generator<V> gen(maxval); + std::vector<V> vec; + + vec.reserve(n); + for (int i = 0; i < n; i++) { + vec.push_back(gen(nums[i])); + } + + return vec; +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_BTREE_TEST_H_ diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index 1e0da5eb..a9ce99ba 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -31,7 +31,6 @@ #define ABSL_CONTAINER_FIXED_ARRAY_H_ #include <algorithm> -#include <array> #include <cassert> #include <cstddef> #include <initializer_list> @@ -51,7 +50,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1); @@ -387,8 +386,7 @@ class FixedArray { // error: call to int __builtin___sprintf_chk(etc...) // will always overflow destination buffer [-Werror] // - template <typename OuterT = value_type, - typename InnerT = absl::remove_extent_t<OuterT>, + template <typename OuterT, typename InnerT = absl::remove_extent_t<OuterT>, size_t InnerN = std::extent<OuterT>::value> struct StorageElementWrapper { InnerT array[InnerN]; @@ -397,8 +395,6 @@ class FixedArray { using StorageElement = absl::conditional_t<std::is_array<value_type>::value, StorageElementWrapper<value_type>, value_type>; - using StorageElementBuffer = - absl::aligned_storage_t<sizeof(StorageElement), alignof(StorageElement)>; static pointer AsValueType(pointer ptr) { return ptr; } static pointer AsValueType(StorageElementWrapper<value_type>* ptr) { @@ -408,25 +404,25 @@ class FixedArray { static_assert(sizeof(StorageElement) == sizeof(value_type), ""); static_assert(alignof(StorageElement) == alignof(value_type), ""); - struct NonEmptyInlinedStorage { - StorageElement* data() { - return reinterpret_cast<StorageElement*>(inlined_storage_.data()); - } + class NonEmptyInlinedStorage { + public: + StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); } + void AnnotateConstruct(size_type n); + void AnnotateDestruct(size_type n); #ifdef ADDRESS_SANITIZER void* RedzoneBegin() { return &redzone_begin_; } void* RedzoneEnd() { return &redzone_end_ + 1; } #endif // ADDRESS_SANITIZER - void AnnotateConstruct(size_type); - void AnnotateDestruct(size_type); - + private: ADDRESS_SANITIZER_REDZONE(redzone_begin_); - std::array<StorageElementBuffer, inline_elements> inlined_storage_; + alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])]; ADDRESS_SANITIZER_REDZONE(redzone_end_); }; - struct EmptyInlinedStorage { + class EmptyInlinedStorage { + public: StorageElement* data() { return nullptr; } void AnnotateConstruct(size_type) {} void AnnotateDestruct(size_type) {} @@ -460,9 +456,7 @@ class FixedArray { size_type size() const { return size_alloc_.template get<0>(); } StorageElement* begin() const { return data_; } StorageElement* end() const { return begin() + size(); } - allocator_type& alloc() { - return size_alloc_.template get<1>(); - } + allocator_type& alloc() { return size_alloc_.template get<1>(); } private: static bool UsingInlinedStorage(size_type n) { @@ -515,7 +509,7 @@ void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct( #endif // ADDRESS_SANITIZER static_cast<void>(n); // Mark used when not in asan mode } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc index 4a67bb46..a5bb009d 100644 --- a/absl/container/fixed_array_exception_safety_test.cc +++ b/absl/container/fixed_array_exception_safety_test.cc @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "absl/base/config.h" +#include "absl/container/fixed_array.h" + +#ifdef ABSL_HAVE_EXCEPTIONS + #include <initializer_list> #include "gtest/gtest.h" #include "absl/base/internal/exception_safety_testing.h" -#include "absl/container/fixed_array.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { @@ -33,10 +37,19 @@ constexpr int kUpdatedValue = 10; using ::testing::TestThrowingCtor; using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>; +using ThrowAlloc = + testing::ThrowingAllocator<Thrower, testing::AllocSpec::kEverythingThrows>; +using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; +using MoveThrowAlloc = + testing::ThrowingAllocator<MoveThrower, + testing::AllocSpec::kEverythingThrows>; + using FixedArr = absl::FixedArray<Thrower, kInlined>; +using FixedArrWithAlloc = absl::FixedArray<Thrower, kInlined, ThrowAlloc>; -using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; using MoveFixedArr = absl::FixedArray<MoveThrower, kInlined>; +using MoveFixedArrWithAlloc = + absl::FixedArray<MoveThrower, kInlined, MoveThrowAlloc>; TEST(FixedArrayExceptionSafety, CopyConstructor) { auto small = FixedArr(kSmallSize); @@ -46,6 +59,14 @@ TEST(FixedArrayExceptionSafety, CopyConstructor) { TestThrowingCtor<FixedArr>(large); } +TEST(FixedArrayExceptionSafety, CopyConstructorWithAlloc) { + auto small = FixedArrWithAlloc(kSmallSize); + TestThrowingCtor<FixedArrWithAlloc>(small); + + auto large = FixedArrWithAlloc(kLargeSize); + TestThrowingCtor<FixedArrWithAlloc>(large); +} + TEST(FixedArrayExceptionSafety, MoveConstructor) { TestThrowingCtor<FixedArr>(FixedArr(kSmallSize)); TestThrowingCtor<FixedArr>(FixedArr(kLargeSize)); @@ -55,16 +76,35 @@ TEST(FixedArrayExceptionSafety, MoveConstructor) { TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kLargeSize)); } +TEST(FixedArrayExceptionSafety, MoveConstructorWithAlloc) { + TestThrowingCtor<FixedArrWithAlloc>(FixedArrWithAlloc(kSmallSize)); + TestThrowingCtor<FixedArrWithAlloc>(FixedArrWithAlloc(kLargeSize)); + + // TypeSpec::kNoThrowMove + TestThrowingCtor<MoveFixedArrWithAlloc>(MoveFixedArrWithAlloc(kSmallSize)); + TestThrowingCtor<MoveFixedArrWithAlloc>(MoveFixedArrWithAlloc(kLargeSize)); +} + TEST(FixedArrayExceptionSafety, SizeConstructor) { TestThrowingCtor<FixedArr>(kSmallSize); TestThrowingCtor<FixedArr>(kLargeSize); } +TEST(FixedArrayExceptionSafety, SizeConstructorWithAlloc) { + TestThrowingCtor<FixedArrWithAlloc>(kSmallSize); + TestThrowingCtor<FixedArrWithAlloc>(kLargeSize); +} + TEST(FixedArrayExceptionSafety, SizeValueConstructor) { TestThrowingCtor<FixedArr>(kSmallSize, Thrower()); TestThrowingCtor<FixedArr>(kLargeSize, Thrower()); } +TEST(FixedArrayExceptionSafety, SizeValueConstructorWithAlloc) { + TestThrowingCtor<FixedArrWithAlloc>(kSmallSize, Thrower()); + TestThrowingCtor<FixedArrWithAlloc>(kLargeSize, Thrower()); +} + TEST(FixedArrayExceptionSafety, IteratorConstructor) { auto small = FixedArr(kSmallSize); TestThrowingCtor<FixedArr>(small.begin(), small.end()); @@ -73,6 +113,14 @@ TEST(FixedArrayExceptionSafety, IteratorConstructor) { TestThrowingCtor<FixedArr>(large.begin(), large.end()); } +TEST(FixedArrayExceptionSafety, IteratorConstructorWithAlloc) { + auto small = FixedArrWithAlloc(kSmallSize); + TestThrowingCtor<FixedArrWithAlloc>(small.begin(), small.end()); + + auto large = FixedArrWithAlloc(kLargeSize); + TestThrowingCtor<FixedArrWithAlloc>(large.begin(), large.end()); +} + TEST(FixedArrayExceptionSafety, InitListConstructor) { constexpr int small_inlined = 3; using SmallFixedArr = absl::FixedArray<Thrower, small_inlined>; @@ -86,7 +134,22 @@ TEST(FixedArrayExceptionSafety, InitListConstructor) { Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}}); } -testing::AssertionResult ReadMemory(FixedArr* fixed_arr) { +TEST(FixedArrayExceptionSafety, InitListConstructorWithAlloc) { + constexpr int small_inlined = 3; + using SmallFixedArrWithAlloc = + absl::FixedArray<Thrower, small_inlined, ThrowAlloc>; + + TestThrowingCtor<SmallFixedArrWithAlloc>(std::initializer_list<Thrower>{}); + // Test inlined allocation + TestThrowingCtor<SmallFixedArrWithAlloc>( + std::initializer_list<Thrower>{Thrower{}, Thrower{}}); + // Test out of line allocation + TestThrowingCtor<SmallFixedArrWithAlloc>(std::initializer_list<Thrower>{ + Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}}); +} + +template <typename FixedArrT> +testing::AssertionResult ReadMemory(FixedArrT* fixed_arr) { // Marked volatile to prevent optimization. Used for running asan tests. volatile int sum = 0; for (const auto& thrower : *fixed_arr) { @@ -97,7 +160,7 @@ testing::AssertionResult ReadMemory(FixedArr* fixed_arr) { TEST(FixedArrayExceptionSafety, Fill) { auto test_fill = testing::MakeExceptionSafetyTester() - .WithContracts(ReadMemory) + .WithContracts(ReadMemory<FixedArr>) .WithOperation([&](FixedArr* fixed_arr_ptr) { auto thrower = Thrower(kUpdatedValue, testing::nothrow_ctor); @@ -112,7 +175,28 @@ TEST(FixedArrayExceptionSafety, Fill) { .Test()); } +TEST(FixedArrayExceptionSafety, FillWithAlloc) { + auto test_fill = testing::MakeExceptionSafetyTester() + .WithContracts(ReadMemory<FixedArrWithAlloc>) + .WithOperation([&](FixedArrWithAlloc* fixed_arr_ptr) { + auto thrower = + Thrower(kUpdatedValue, testing::nothrow_ctor); + fixed_arr_ptr->fill(thrower); + }); + + EXPECT_TRUE(test_fill + .WithInitialValue( + FixedArrWithAlloc(kSmallSize, Thrower(kInitialValue))) + .Test()); + EXPECT_TRUE(test_fill + .WithInitialValue( + FixedArrWithAlloc(kLargeSize, Thrower(kInitialValue))) + .Test()); +} + } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl + +#endif // ABSL_HAVE_EXCEPTIONS diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc index 2b1cf47e..c960fe51 100644 --- a/absl/container/fixed_array_test.cc +++ b/absl/container/fixed_array_test.cc @@ -604,19 +604,16 @@ TEST(FixedArrayTest, Fill) { empty.fill(fill_val); } -// TODO(johnsoncj): Investigate InlinedStorage default initialization in GCC 4.x #ifndef __GNUC__ TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) { using T = char; constexpr auto capacity = 10; using FixedArrType = absl::FixedArray<T, capacity>; - using FixedArrBuffType = - absl::aligned_storage_t<sizeof(FixedArrType), alignof(FixedArrType)>; constexpr auto scrubbed_bits = 0x95; constexpr auto length = capacity / 2; - FixedArrBuffType buff; - std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrBuffType)); + alignas(FixedArrType) unsigned char buff[sizeof(FixedArrType)]; + std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrType)); FixedArrType* arr = ::new (static_cast<void*>(std::addressof(buff))) FixedArrType(length); diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index a711398e..fcb70d86 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -42,7 +42,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class K, class V> struct FlatHashMapPolicy; @@ -361,6 +361,10 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // Inserts (via copy or move) the element of the specified key into the // `flat_hash_map` using the position of `hint` as a non-binding suggestion // for where to begin the insertion search. + // + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. using Base::try_emplace; // flat_hash_map::extract() @@ -398,7 +402,7 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // for the past-the-end iterator, which is invalidated. // // `swap()` requires that the flat hash map's hashing and key equivalence - // functions be Swappable, and are exchaged using unqualified calls to + // functions be Swappable, and are exchanged using unqualified calls to // non-member `swap()`. If the map's allocator has // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value` // set to `true`, the allocators are also exchanged using an unqualified call @@ -528,6 +532,15 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< using Base::key_eq; }; +// erase_if(flat_hash_map<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template <typename K, typename V, typename H, typename E, typename A, + typename Predicate> +void erase_if(flat_hash_map<K, V, H, E, A>& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + namespace container_internal { template <class K, class V> @@ -581,7 +594,7 @@ struct IsUnorderedContainer< } // namespace container_algorithm_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 3f11a52c..728b693a 100644 --- a/absl/container/flat_hash_map_test.cc +++ b/absl/container/flat_hash_map_test.cc @@ -14,6 +14,8 @@ #include "absl/container/flat_hash_map.h" +#include <memory> + #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" @@ -22,12 +24,13 @@ #include "absl/types/any.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { using ::absl::container_internal::hash_internal::Enum; using ::absl::container_internal::hash_internal::EnumClass; using ::testing::_; +using ::testing::IsEmpty; using ::testing::Pair; using ::testing::UnorderedElementsAre; @@ -47,6 +50,11 @@ INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes); INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes); INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes); +using UniquePtrMapTypes = ::testing::Types<Map<int, std::unique_ptr<int>>>; + +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, UniquePtrModifiersTest, + UniquePtrMapTypes); + TEST(FlatHashMap, StandardLayout) { struct Int { explicit Int(size_t value) : value(value) {} @@ -208,42 +216,44 @@ TEST(FlatHashMap, MergeExtractInsert) { EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9))); } -#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); - auto it = m.find(1); - ASSERT_NE(it, m.end()); - EXPECT_EQ(7, absl::any_cast<int>(it->second)); - - m.emplace(std::piecewise_construct, std::make_tuple(2), std::make_tuple(8)); - it = m.find(2); - ASSERT_NE(it, m.end()); - EXPECT_EQ(8, absl::any_cast<int>(it->second)); - - m.emplace(std::piecewise_construct, std::make_tuple(3), - std::make_tuple(absl::any(9))); - it = m.find(3); - ASSERT_NE(it, m.end()); - EXPECT_EQ(9, absl::any_cast<int>(it->second)); - - struct H { - size_t operator()(const absl::any&) const { return 0; } - }; - struct E { - bool operator()(const absl::any&, const absl::any&) const { return true; } - }; - absl::flat_hash_map<absl::any, int, H, E> m2; - m2.emplace(1, 7); - auto it2 = m2.find(1); - ASSERT_NE(it2, m2.end()); - EXPECT_EQ(7, it2->second); +bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; } + +TEST(FlatHashMap, EraseIf) { + // Erase all elements. + { + flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, [](std::pair<const int, int>) { return true; }); + EXPECT_THAT(s, IsEmpty()); + } + // Erase no elements. + { + flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, [](std::pair<const int, int>) { return false; }); + EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), + Pair(4, 4), Pair(5, 5))); + } + // Erase specific elements. + { + flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, + [](std::pair<const int, int> kvp) { return kvp.first % 2 == 1; }); + EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4))); + } + // Predicate is function reference. + { + flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, FirstIsEven); + EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5))); + } + // Predicate is function pointer. + { + flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, &FirstIsEven); + EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5))); + } } -#endif // (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && - // !defined(__EMSCRIPTEN__) } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index 8adbbcd5..94be6e3d 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -40,7 +40,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <typename T> struct FlatHashSetPolicy; @@ -439,6 +439,14 @@ class flat_hash_set using Base::key_eq; }; +// erase_if(flat_hash_set<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template <typename T, typename H, typename E, typename A, typename Predicate> +void erase_if(flat_hash_set<T, H, E, A>& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + namespace container_internal { template <class T> @@ -489,7 +497,7 @@ struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>> } // namespace container_algorithm_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 56140bbe..40d7f85c 100644 --- a/absl/container/flat_hash_set_test.cc +++ b/absl/container/flat_hash_set_test.cc @@ -25,12 +25,13 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { using ::absl::container_internal::hash_internal::Enum; using ::absl::container_internal::hash_internal::EnumClass; +using ::testing::IsEmpty; using ::testing::Pointee; using ::testing::UnorderedElementsAre; using ::testing::UnorderedElementsAreArray; @@ -124,7 +125,42 @@ TEST(FlatHashSet, MergeExtractInsert) { EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23))); } +bool IsEven(int k) { return k % 2 == 0; } + +TEST(FlatHashSet, EraseIf) { + // Erase all elements. + { + flat_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, [](int) { return true; }); + EXPECT_THAT(s, IsEmpty()); + } + // Erase no elements. + { + flat_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, [](int) { return false; }); + EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5)); + } + // Erase specific elements. + { + flat_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, [](int k) { return k % 2 == 1; }); + EXPECT_THAT(s, UnorderedElementsAre(2, 4)); + } + // Predicate is function reference. + { + flat_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, IsEven); + EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5)); + } + // Predicate is function pointer. + { + flat_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, &IsEven); + EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5)); + } +} + } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 27186b15..2388d471 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -54,7 +54,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // InlinedVector // ----------------------------------------------------------------------------- @@ -70,9 +70,10 @@ class InlinedVector { static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity."); using Storage = inlined_vector_internal::Storage<T, N, A>; - using rvalue_reference = typename Storage::rvalue_reference; - using MoveIterator = typename Storage::MoveIterator; + using AllocatorTraits = typename Storage::AllocatorTraits; + using RValueReference = typename Storage::RValueReference; + using MoveIterator = typename Storage::MoveIterator; using IsMemcpyOk = typename Storage::IsMemcpyOk; template <typename Iterator> @@ -93,10 +94,10 @@ class InlinedVector { 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 reference = typename Storage::reference; + using const_reference = typename Storage::const_reference; using iterator = typename Storage::iterator; using const_iterator = typename Storage::const_iterator; using reverse_iterator = typename Storage::reverse_iterator; @@ -534,7 +535,6 @@ class InlinedVector { } erase(data() + i, data() + size()); - std::copy(first, last, std::back_inserter(*this)); } @@ -565,7 +565,7 @@ class InlinedVector { // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using // move semantics, returning an `iterator` to the newly inserted element. - iterator insert(const_iterator pos, rvalue_reference v) { + iterator insert(const_iterator pos, RValueReference v) { return emplace(pos, std::move(v)); } @@ -662,7 +662,7 @@ class InlinedVector { // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` // using move semantics. - void push_back(rvalue_reference v) { + void push_back(RValueReference v) { static_cast<void>(emplace_back(std::move(v))); } @@ -714,6 +714,7 @@ class InlinedVector { inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), size()); storage_.DeallocateIfAllocated(); + storage_.SetInlinedSize(0); } @@ -841,7 +842,7 @@ H AbslHashValue(H h, const absl::InlinedVector<T, N, A>& a) { return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INLINED_VECTOR_H_ diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc index b99bbd62..3f2b4ed2 100644 --- a/absl/container/inlined_vector_benchmark.cc +++ b/absl/container/inlined_vector_benchmark.cc @@ -25,42 +25,45 @@ namespace { void BM_InlinedVectorFill(benchmark::State& state) { + const int len = state.range(0); absl::InlinedVector<int, 8> v; - int val = 10; + v.reserve(len); for (auto _ : state) { + v.resize(0); // Use resize(0) as InlinedVector releases storage on clear(). + for (int i = 0; i < len; ++i) { + v.push_back(i); + } benchmark::DoNotOptimize(v); - v.push_back(val); } } -BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024); +BENCHMARK(BM_InlinedVectorFill)->Range(1, 256); void BM_InlinedVectorFillRange(benchmark::State& state) { const int len = state.range(0); - std::unique_ptr<int[]> ia(new int[len]); - for (int i = 0; i < len; i++) { - ia[i] = i; - } - auto* from = ia.get(); - auto* to = from + len; + const std::vector<int> src(len, len); + absl::InlinedVector<int, 8> v; + v.reserve(len); for (auto _ : state) { - benchmark::DoNotOptimize(from); - benchmark::DoNotOptimize(to); - absl::InlinedVector<int, 8> v(from, to); + benchmark::DoNotOptimize(src); + v.assign(src.begin(), src.end()); benchmark::DoNotOptimize(v); } } -BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024); +BENCHMARK(BM_InlinedVectorFillRange)->Range(1, 256); void BM_StdVectorFill(benchmark::State& state) { + const int len = state.range(0); std::vector<int> v; - int val = 10; + v.reserve(len); for (auto _ : state) { + v.clear(); + for (int i = 0; i < len; ++i) { + v.push_back(i); + } benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(val); - v.push_back(val); } } -BENCHMARK(BM_StdVectorFill)->Range(0, 1024); +BENCHMARK(BM_StdVectorFill)->Range(1, 256); // The purpose of the next two benchmarks is to verify that // absl::InlinedVector is efficient when moving is more efficent than diff --git a/absl/container/inlined_vector_exception_safety_test.cc b/absl/container/inlined_vector_exception_safety_test.cc index ff0da75b..0e6a05b5 100644 --- a/absl/container/inlined_vector_exception_safety_test.cc +++ b/absl/container/inlined_vector_exception_safety_test.cc @@ -12,6 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "absl/container/inlined_vector.h" + +#include "absl/base/config.h" + +#if defined(ABSL_HAVE_EXCEPTIONS) + #include <array> #include <initializer_list> #include <iterator> @@ -20,7 +26,6 @@ #include "gtest/gtest.h" #include "absl/base/internal/exception_safety_testing.h" -#include "absl/container/inlined_vector.h" namespace { @@ -57,8 +62,8 @@ using ThrowAllocMovableThrowerVec = \ : std::initializer_list<T>{T(0, testing::nothrow_ctor), \ T(1, testing::nothrow_ctor)}) -static_assert((kLargeSize == 8 || kSmallSize == 2), - "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)."); +static_assert(kLargeSize == 8, "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)"); +static_assert(kSmallSize == 2, "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)"); template <typename TheVecT, size_t... TheSizes> class TestParams { @@ -359,9 +364,11 @@ TYPED_TEST(OneSizeTest, EmplaceBack) { using VecT = typename TypeParam::VecT; constexpr static auto size = TypeParam::GetSizeAt(0); + // For testing calls to `emplace_back(...)` that reallocate. VecT full_vec{size}; full_vec.resize(full_vec.capacity()); + // For testing calls to `emplace_back(...)` that don't reallocate. VecT nonfull_vec{size}; nonfull_vec.reserve(size + 1); @@ -369,12 +376,11 @@ TYPED_TEST(OneSizeTest, EmplaceBack) { InlinedVectorInvariants<VecT>); EXPECT_TRUE(tester.WithInitialValue(nonfull_vec).Test([](VecT* vec) { - vec->emplace_back(); // + vec->emplace_back(); })); - EXPECT_TRUE(tester.WithInitialValue(full_vec).Test([](VecT* vec) { - vec->emplace_back(); // - })); + EXPECT_TRUE(tester.WithInitialValue(full_vec).Test( + [](VecT* vec) { vec->emplace_back(); })); } TYPED_TEST(OneSizeTest, PopBack) { @@ -413,6 +419,19 @@ TYPED_TEST(OneSizeTest, Erase) { EXPECT_TRUE(tester.Test([](VecT* vec) { auto it = vec->begin(); + vec->erase(it, it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->erase(it, it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() - 1); + vec->erase(it, it); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); vec->erase(it, it + 1); })); EXPECT_TRUE(tester.Test([](VecT* vec) { @@ -447,9 +466,7 @@ TYPED_TEST(TwoSizeTest, Reserve) { .WithInitialValue(VecT{from_size}) .WithContracts(InlinedVectorInvariants<VecT>); - EXPECT_TRUE(tester.Test([](VecT* vec) { - vec->reserve(to_capacity); // - })); + EXPECT_TRUE(tester.Test([](VecT* vec) { vec->reserve(to_capacity); })); } TYPED_TEST(OneSizeTest, ShrinkToFit) { @@ -487,3 +504,5 @@ TYPED_TEST(TwoSizeTest, Swap) { } } // namespace + +#endif // defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index bada4fec..2c9b0d0e 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc @@ -1689,7 +1689,11 @@ TEST(AllocatorSupportTest, ScopedAllocatorWorksInlined) { inlined_case.emplace_back(); int64_t absl_responsible_for_count = total_allocated_byte_count; + + // MSVC's allocator preemptively allocates in debug mode +#if !defined(_MSC_VER) EXPECT_EQ(absl_responsible_for_count, 0); +#endif // !defined(_MSC_VER) inlined_case[0].emplace_back(); EXPECT_GT(total_allocated_byte_count, absl_responsible_for_count); @@ -1750,6 +1754,30 @@ TEST(AllocatorSupportTest, SizeAllocConstructor) { } } +TEST(InlinedVectorTest, MinimumAllocatorCompilesUsingTraits) { + using T = int; + using A = std::allocator<T>; + using ATraits = absl::allocator_traits<A>; + + struct MinimumAllocator { + using value_type = T; + + value_type* allocate(size_t n) { + A a; + return ATraits::allocate(a, n); + } + + void deallocate(value_type* p, size_t n) { + A a; + ATraits::deallocate(a, p, n); + } + }; + + absl::InlinedVector<T, 1, MinimumAllocator> vec; + vec.emplace_back(); + vec.resize(0); +} + TEST(InlinedVectorTest, AbslHashValueWorks) { using V = absl::InlinedVector<int, 4>; std::vector<V> cases; diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h new file mode 100644 index 00000000..fd5c0e7a --- /dev/null +++ b/absl/container/internal/btree.h @@ -0,0 +1,2614 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A btree implementation of the STL set and map interfaces. A btree is smaller +// and generally also faster than STL set/map (refer to the benchmarks below). +// The red-black tree implementation of STL set/map has an overhead of 3 +// pointers (left, right and parent) plus the node color information for each +// stored value. So a set<int32_t> consumes 40 bytes for each value stored in +// 64-bit mode. This btree implementation stores multiple values on fixed +// size nodes (usually 256 bytes) and doesn't store child pointers for leaf +// nodes. The result is that a btree_set<int32_t> may use much less memory per +// stored value. For the random insertion benchmark in btree_bench.cc, a +// btree_set<int32_t> with node-size of 256 uses 5.1 bytes per stored value. +// +// The packing of multiple values on to each node of a btree has another effect +// besides better space utilization: better cache locality due to fewer cache +// lines being accessed. Better cache locality translates into faster +// operations. +// +// CAVEATS +// +// Insertions and deletions on a btree can cause splitting, merging or +// rebalancing of btree nodes. And even without these operations, insertions +// and deletions on a btree will move values around within a node. In both +// cases, the result is that insertions and deletions can invalidate iterators +// pointing to values other than the one being inserted/deleted. Therefore, this +// container does not provide pointer stability. This is notably different from +// STL set/map which takes care to not invalidate iterators on insert/erase +// except, of course, for iterators pointing to the value being erased. A +// partial workaround when erasing is available: erase() returns an iterator +// pointing to the item just after the one that was erased (or end() if none +// exists). + +#ifndef ABSL_CONTAINER_INTERNAL_BTREE_H_ +#define ABSL_CONTAINER_INTERNAL_BTREE_H_ + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <functional> +#include <iterator> +#include <limits> +#include <new> +#include <string> +#include <type_traits> +#include <utility> + +#include "absl/base/macros.h" +#include "absl/container/internal/common.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/layout.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" +#include "absl/types/compare.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A helper class that indicates if the Compare parameter is a key-compare-to +// comparator. +template <typename Compare, typename T> +using btree_is_key_compare_to = + std::is_convertible<absl::result_of_t<Compare(const T &, const T &)>, + absl::weak_ordering>; + +struct StringBtreeDefaultLess { + using is_transparent = void; + + StringBtreeDefaultLess() = default; + + // Compatibility constructor. + StringBtreeDefaultLess(std::less<std::string>) {} // NOLINT + StringBtreeDefaultLess(std::less<string_view>) {} // NOLINT + + absl::weak_ordering operator()(absl::string_view lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(lhs.compare(rhs)); + } +}; + +struct StringBtreeDefaultGreater { + using is_transparent = void; + + StringBtreeDefaultGreater() = default; + + StringBtreeDefaultGreater(std::greater<std::string>) {} // NOLINT + StringBtreeDefaultGreater(std::greater<string_view>) {} // NOLINT + + absl::weak_ordering operator()(absl::string_view lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(rhs.compare(lhs)); + } +}; + +// A helper class to convert a boolean comparison into a three-way "compare-to" +// comparison that returns a negative value to indicate less-than, zero to +// indicate equality and a positive value to indicate greater-than. This helper +// class is specialized for less<std::string>, greater<std::string>, +// less<string_view>, and greater<string_view>. +// +// key_compare_to_adapter is provided so that btree users +// automatically get the more efficient compare-to code when using common +// google string types with common comparison functors. +// These string-like specializations also turn on heterogeneous lookup by +// default. +template <typename Compare> +struct key_compare_to_adapter { + using type = Compare; +}; + +template <> +struct key_compare_to_adapter<std::less<std::string>> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_to_adapter<std::greater<std::string>> { + using type = StringBtreeDefaultGreater; +}; + +template <> +struct key_compare_to_adapter<std::less<absl::string_view>> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_to_adapter<std::greater<absl::string_view>> { + using type = StringBtreeDefaultGreater; +}; + +template <typename Key, typename Compare, typename Alloc, int TargetNodeSize, + bool Multi, typename SlotPolicy> +struct common_params { + // If Compare is a common comparator for a std::string-like type, then we adapt it + // to use heterogeneous lookup and to be a key-compare-to comparator. + using key_compare = typename key_compare_to_adapter<Compare>::type; + // A type which indicates if we have a key-compare-to functor or a plain old + // key-compare functor. + using is_key_compare_to = btree_is_key_compare_to<key_compare, Key>; + + using allocator_type = Alloc; + using key_type = Key; + using size_type = std::make_signed<size_t>::type; + using difference_type = ptrdiff_t; + + // True if this is a multiset or multimap. + using is_multi_container = std::integral_constant<bool, Multi>; + + using slot_policy = SlotPolicy; + using slot_type = typename slot_policy::slot_type; + using value_type = typename slot_policy::value_type; + using init_type = typename slot_policy::mutable_value_type; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + + enum { + kTargetNodeSize = TargetNodeSize, + + // Upper bound for the available space for values. This is largest for leaf + // nodes, which have overhead of at least a pointer + 4 bytes (for storing + // 3 field_types and an enum). + kNodeValueSpace = + TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4), + }; + + // This is an integral type large enough to hold as many + // ValueSize-values as will fit a node of TargetNodeSize bytes. + using node_count_type = + absl::conditional_t<(kNodeValueSpace / sizeof(value_type) > + (std::numeric_limits<uint8_t>::max)()), + uint16_t, uint8_t>; // NOLINT + + // The following methods are necessary for passing this struct as PolicyTraits + // for node_handle and/or are used within btree. + static value_type &element(slot_type *slot) { + return slot_policy::element(slot); + } + static const value_type &element(const slot_type *slot) { + return slot_policy::element(slot); + } + template <class... Args> + static void construct(Alloc *alloc, slot_type *slot, Args &&... args) { + slot_policy::construct(alloc, slot, std::forward<Args>(args)...); + } + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + slot_policy::construct(alloc, slot, other); + } + static void destroy(Alloc *alloc, slot_type *slot) { + slot_policy::destroy(alloc, slot); + } + static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) { + construct(alloc, new_slot, old_slot); + destroy(alloc, old_slot); + } + static void swap(Alloc *alloc, slot_type *a, slot_type *b) { + slot_policy::swap(alloc, a, b); + } + static void move(Alloc *alloc, slot_type *src, slot_type *dest) { + slot_policy::move(alloc, src, dest); + } + static void move(Alloc *alloc, slot_type *first, slot_type *last, + slot_type *result) { + slot_policy::move(alloc, first, last, result); + } +}; + +// A parameters structure for holding the type parameters for a btree_map. +// Compare and Alloc should be nothrow copy-constructible. +template <typename Key, typename Data, typename Compare, typename Alloc, + int TargetNodeSize, bool Multi> +struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi, + map_slot_policy<Key, Data>> { + using super_type = typename map_params::common_params; + using mapped_type = Data; + // This type allows us to move keys when it is safe to do so. It is safe + // for maps in which value_type and mutable_value_type are layout compatible. + using slot_policy = typename super_type::slot_policy; + using slot_type = typename super_type::slot_type; + using value_type = typename super_type::value_type; + using init_type = typename super_type::init_type; + + using key_compare = typename super_type::key_compare; + // Inherit from key_compare for empty base class optimization. + struct value_compare : private key_compare { + value_compare() = default; + explicit value_compare(const key_compare &cmp) : key_compare(cmp) {} + + template <typename T, typename U> + auto operator()(const T &left, const U &right) const + -> decltype(std::declval<key_compare>()(left.first, right.first)) { + return key_compare::operator()(left.first, right.first); + } + }; + using is_map_container = std::true_type; + + static const Key &key(const value_type &x) { return x.first; } + static const Key &key(const init_type &x) { return x.first; } + static const Key &key(const slot_type *x) { return slot_policy::key(x); } + static mapped_type &value(value_type *value) { return value->second; } +}; + +// This type implements the necessary functions from the +// absl::container_internal::slot_type interface. +template <typename Key> +struct set_slot_policy { + using slot_type = Key; + using value_type = Key; + using mutable_value_type = Key; + + static value_type &element(slot_type *slot) { return *slot; } + static const value_type &element(const slot_type *slot) { return *slot; } + + template <typename Alloc, class... Args> + static void construct(Alloc *alloc, slot_type *slot, Args &&... args) { + absl::allocator_traits<Alloc>::construct(*alloc, slot, + std::forward<Args>(args)...); + } + + template <typename Alloc> + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + absl::allocator_traits<Alloc>::construct(*alloc, slot, std::move(*other)); + } + + template <typename Alloc> + static void destroy(Alloc *alloc, slot_type *slot) { + absl::allocator_traits<Alloc>::destroy(*alloc, slot); + } + + template <typename Alloc> + static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) { + using std::swap; + swap(*a, *b); + } + + template <typename Alloc> + static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) { + *dest = std::move(*src); + } + + template <typename Alloc> + static void move(Alloc *alloc, slot_type *first, slot_type *last, + slot_type *result) { + for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) + move(alloc, src, dest); + } +}; + +// A parameters structure for holding the type parameters for a btree_set. +// Compare and Alloc should be nothrow copy-constructible. +template <typename Key, typename Compare, typename Alloc, int TargetNodeSize, + bool Multi> +struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi, + set_slot_policy<Key>> { + using value_type = Key; + using slot_type = typename set_params::common_params::slot_type; + using value_compare = typename set_params::common_params::key_compare; + using is_map_container = std::false_type; + + static const Key &key(const value_type &x) { return x; } + static const Key &key(const slot_type *x) { return *x; } +}; + +// An adapter class that converts a lower-bound compare into an upper-bound +// compare. Note: there is no need to make a version of this adapter specialized +// for key-compare-to functors because the upper-bound (the first value greater +// than the input) is never an exact match. +template <typename Compare> +struct upper_bound_adapter { + explicit upper_bound_adapter(const Compare &c) : comp(c) {} + template <typename K, typename LK> + bool operator()(const K &a, const LK &b) const { + // Returns true when a is not greater than b. + return !compare_internal::compare_result_as_less_than(comp(b, a)); + } + + private: + Compare comp; +}; + +enum class MatchKind : uint8_t { kEq, kNe }; + +template <typename V, bool IsCompareTo> +struct SearchResult { + V value; + MatchKind match; + + static constexpr bool HasMatch() { return true; } + bool IsEq() const { return match == MatchKind::kEq; } +}; + +// When we don't use CompareTo, `match` is not present. +// This ensures that callers can't use it accidentally when it provides no +// useful information. +template <typename V> +struct SearchResult<V, false> { + V value; + + static constexpr bool HasMatch() { return false; } + static constexpr bool IsEq() { return false; } +}; + +// A node in the btree holding. The same node type is used for both internal +// and leaf nodes in the btree, though the nodes are allocated in such a way +// that the children array is only valid in internal nodes. +template <typename Params> +class btree_node { + using is_key_compare_to = typename Params::is_key_compare_to; + using is_multi_container = typename Params::is_multi_container; + using field_type = typename Params::node_count_type; + using allocator_type = typename Params::allocator_type; + using slot_type = typename Params::slot_type; + + public: + using params_type = Params; + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using key_compare = typename Params::key_compare; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + + // Btree decides whether to use linear node search as follows: + // - If the key is arithmetic and the comparator is std::less or + // std::greater, choose linear. + // - Otherwise, choose binary. + // TODO(ezb): Might make sense to add condition(s) based on node-size. + using use_linear_search = std::integral_constant< + bool, + std::is_arithmetic<key_type>::value && + (std::is_same<std::less<key_type>, key_compare>::value || + std::is_same<std::greater<key_type>, key_compare>::value)>; + + // This class is organized by gtl::Layout as if it had the following + // structure: + // // A pointer to the node's parent. + // btree_node *parent; + // + // // The position of the node in the node's parent. + // field_type position; + // // The index of the first populated value in `values`. + // // TODO(ezb): right now, `start` is always 0. Update insertion/merge + // // logic to allow for floating storage within nodes. + // field_type start; + // // The index after the last populated value in `values`. Currently, this + // // is the same as the count of values. + // field_type finish; + // // The maximum number of values the node can hold. This is an integer in + // // [1, kNodeValues] for root leaf nodes, kNodeValues for non-root leaf + // // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal + // // nodes (even though there are still kNodeValues values in the node). + // // TODO(ezb): make max_count use only 4 bits and record log2(capacity) + // // to free extra bits for is_root, etc. + // field_type max_count; + // + // // The array of values. The capacity is `max_count` for leaf nodes and + // // kNodeValues for internal nodes. Only the values in + // // [start, finish) have been initialized and are valid. + // slot_type values[max_count]; + // + // // The array of child pointers. The keys in children[i] are all less + // // than key(i). The keys in children[i + 1] are all greater than key(i). + // // There are 0 children for leaf nodes and kNodeValues + 1 children for + // // internal nodes. + // btree_node *children[kNodeValues + 1]; + // + // This class is only constructed by EmptyNodeType. Normally, pointers to the + // layout above are allocated, cast to btree_node*, and de-allocated within + // the btree implementation. + ~btree_node() = default; + btree_node(btree_node const &) = delete; + btree_node &operator=(btree_node const &) = delete; + + // Public for EmptyNodeType. + constexpr static size_type Alignment() { + static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(), + "Alignment of all nodes must be equal."); + return InternalLayout().Alignment(); + } + + protected: + btree_node() = default; + + private: + using layout_type = absl::container_internal::Layout<btree_node *, field_type, + slot_type, btree_node *>; + constexpr static size_type SizeWithNValues(size_type n) { + return layout_type(/*parent*/ 1, + /*position, start, finish, max_count*/ 4, + /*values*/ n, + /*children*/ 0) + .AllocSize(); + } + // A lower bound for the overhead of fields other than values in a leaf node. + constexpr static size_type MinimumOverhead() { + return SizeWithNValues(1) - sizeof(value_type); + } + + // Compute how many values we can fit onto a leaf node taking into account + // padding. + constexpr static size_type NodeTargetValues(const int begin, const int end) { + return begin == end ? begin + : SizeWithNValues((begin + end) / 2 + 1) > + params_type::kTargetNodeSize + ? NodeTargetValues(begin, (begin + end) / 2) + : NodeTargetValues((begin + end) / 2 + 1, end); + } + + enum { + kTargetNodeSize = params_type::kTargetNodeSize, + kNodeTargetValues = NodeTargetValues(0, params_type::kTargetNodeSize), + + // We need a minimum of 3 values per internal node in order to perform + // splitting (1 value for the two nodes involved in the split and 1 value + // propagated to the parent as the delimiter for the split). + kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3, + + // The node is internal (i.e. is not a leaf node) if and only if `max_count` + // has this value. + kInternalNodeMaxCount = 0, + }; + + // Leaves can have less than kNodeValues values. + constexpr static layout_type LeafLayout(const int max_values = kNodeValues) { + return layout_type(/*parent*/ 1, + /*position, start, finish, max_count*/ 4, + /*values*/ max_values, + /*children*/ 0); + } + constexpr static layout_type InternalLayout() { + return layout_type(/*parent*/ 1, + /*position, start, finish, max_count*/ 4, + /*values*/ kNodeValues, + /*children*/ kNodeValues + 1); + } + constexpr static size_type LeafSize(const int max_values = kNodeValues) { + return LeafLayout(max_values).AllocSize(); + } + constexpr static size_type InternalSize() { + return InternalLayout().AllocSize(); + } + + // N is the index of the type in the Layout definition. + // ElementType<N> is the Nth type in the Layout definition. + template <size_type N> + inline typename layout_type::template ElementType<N> *GetField() { + // We assert that we don't read from values that aren't there. + assert(N < 3 || !leaf()); + return InternalLayout().template Pointer<N>(reinterpret_cast<char *>(this)); + } + template <size_type N> + inline const typename layout_type::template ElementType<N> *GetField() const { + assert(N < 3 || !leaf()); + return InternalLayout().template Pointer<N>( + reinterpret_cast<const char *>(this)); + } + void set_parent(btree_node *p) { *GetField<0>() = p; } + field_type &mutable_finish() { return GetField<1>()[2]; } + slot_type *slot(int i) { return &GetField<2>()[i]; } + slot_type *start_slot() { return slot(start()); } + slot_type *finish_slot() { return slot(finish()); } + const slot_type *slot(int i) const { return &GetField<2>()[i]; } + void set_position(field_type v) { GetField<1>()[0] = v; } + void set_start(field_type v) { GetField<1>()[1] = v; } + void set_finish(field_type v) { GetField<1>()[2] = v; } + // This method is only called by the node init methods. + void set_max_count(field_type v) { GetField<1>()[3] = v; } + + public: + // Whether this is a leaf node or not. This value doesn't change after the + // node is created. + bool leaf() const { return GetField<1>()[3] != kInternalNodeMaxCount; } + + // Getter for the position of this node in its parent. + field_type position() const { return GetField<1>()[0]; } + + // Getter for the offset of the first value in the `values` array. + field_type start() const { + // TODO(ezb): when floating storage is implemented, return GetField<1>()[1]; + assert(GetField<1>()[1] == 0); + return 0; + } + + // Getter for the offset after the last value in the `values` array. + field_type finish() const { return GetField<1>()[2]; } + + // Getters for the number of values stored in this node. + field_type count() const { + assert(finish() >= start()); + return finish() - start(); + } + field_type max_count() const { + // Internal nodes have max_count==kInternalNodeMaxCount. + // Leaf nodes have max_count in [1, kNodeValues]. + const field_type max_count = GetField<1>()[3]; + return max_count == field_type{kInternalNodeMaxCount} + ? field_type{kNodeValues} + : max_count; + } + + // Getter for the parent of this node. + btree_node *parent() const { return *GetField<0>(); } + // Getter for whether the node is the root of the tree. The parent of the + // root of the tree is the leftmost node in the tree which is guaranteed to + // be a leaf. + bool is_root() const { return parent()->leaf(); } + void make_root() { + assert(parent()->is_root()); + set_parent(parent()->parent()); + } + + // Getters for the key/value at position i in the node. + const key_type &key(int i) const { return params_type::key(slot(i)); } + reference value(int i) { return params_type::element(slot(i)); } + const_reference value(int i) const { return params_type::element(slot(i)); } + + // Getters/setter for the child at position i in the node. + btree_node *child(int i) const { return GetField<3>()[i]; } + btree_node *start_child() const { return child(start()); } + btree_node *&mutable_child(int i) { return GetField<3>()[i]; } + void clear_child(int i) { + absl::container_internal::SanitizerPoisonObject(&mutable_child(i)); + } + void set_child(int i, btree_node *c) { + absl::container_internal::SanitizerUnpoisonObject(&mutable_child(i)); + mutable_child(i) = c; + c->set_position(i); + } + void init_child(int i, btree_node *c) { + set_child(i, c); + c->set_parent(this); + } + + // Returns the position of the first value whose key is not less than k. + template <typename K> + SearchResult<int, is_key_compare_to::value> lower_bound( + const K &k, const key_compare &comp) const { + return use_linear_search::value ? linear_search(k, comp) + : binary_search(k, comp); + } + // Returns the position of the first value whose key is greater than k. + template <typename K> + int upper_bound(const K &k, const key_compare &comp) const { + auto upper_compare = upper_bound_adapter<key_compare>(comp); + return use_linear_search::value ? linear_search(k, upper_compare).value + : binary_search(k, upper_compare).value; + } + + template <typename K, typename Compare> + SearchResult<int, btree_is_key_compare_to<Compare, key_type>::value> + linear_search(const K &k, const Compare &comp) const { + return linear_search_impl(k, start(), finish(), comp, + btree_is_key_compare_to<Compare, key_type>()); + } + + template <typename K, typename Compare> + SearchResult<int, btree_is_key_compare_to<Compare, key_type>::value> + binary_search(const K &k, const Compare &comp) const { + return binary_search_impl(k, start(), finish(), comp, + btree_is_key_compare_to<Compare, key_type>()); + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using plain compare. + template <typename K, typename Compare> + SearchResult<int, false> linear_search_impl( + const K &k, int s, const int e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s < e) { + if (!comp(key(s), k)) { + break; + } + ++s; + } + return {s}; + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using compare-to. + template <typename K, typename Compare> + SearchResult<int, true> linear_search_impl( + const K &k, int s, const int e, const Compare &comp, + std::true_type /* IsCompareTo */) const { + while (s < e) { + const absl::weak_ordering c = comp(key(s), k); + if (c == 0) { + return {s, MatchKind::kEq}; + } else if (c > 0) { + break; + } + ++s; + } + return {s, MatchKind::kNe}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using plain compare. + template <typename K, typename Compare> + SearchResult<int, false> binary_search_impl( + const K &k, int s, int e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s != e) { + const int mid = (s + e) >> 1; + if (comp(key(mid), k)) { + s = mid + 1; + } else { + e = mid; + } + } + return {s}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using compare-to. + template <typename K, typename CompareTo> + SearchResult<int, true> binary_search_impl( + const K &k, int s, int e, const CompareTo &comp, + std::true_type /* IsCompareTo */) const { + if (is_multi_container::value) { + MatchKind exact_match = MatchKind::kNe; + while (s != e) { + const int mid = (s + e) >> 1; + const absl::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else { + e = mid; + if (c == 0) { + // Need to return the first value whose key is not less than k, + // which requires continuing the binary search if this is a + // multi-container. + exact_match = MatchKind::kEq; + } + } + } + return {s, exact_match}; + } else { // Not a multi-container. + while (s != e) { + const int mid = (s + e) >> 1; + const absl::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else if (c > 0) { + e = mid; + } else { + return {mid, MatchKind::kEq}; + } + } + return {s, MatchKind::kNe}; + } + } + + // Emplaces a value at position i, shifting all existing values and + // children at positions >= i to the right by 1. + template <typename... Args> + void emplace_value(size_type i, allocator_type *alloc, Args &&... args); + + // Removes the value at position i, shifting all existing values and children + // at positions > i to the left by 1. + void remove_value(int i, allocator_type *alloc); + + // Removes the values at positions [i, i + to_erase), shifting all values + // after that range to the left by to_erase. Does not change children at all. + void remove_values_ignore_children(int i, int to_erase, + allocator_type *alloc); + + // Rebalances a node with its right sibling. + void rebalance_right_to_left(int to_move, btree_node *right, + allocator_type *alloc); + void rebalance_left_to_right(int to_move, btree_node *right, + allocator_type *alloc); + + // Splits a node, moving a portion of the node's values to its right sibling. + void split(int insert_position, btree_node *dest, allocator_type *alloc); + + // Merges a node with its right sibling, moving all of the values and the + // delimiting key in the parent node onto itself. + void merge(btree_node *sibling, allocator_type *alloc); + + // Swap the contents of "this" and "src". + void swap(btree_node *src, allocator_type *alloc); + + // Node allocation/deletion routines. + static btree_node *init_leaf(btree_node *n, btree_node *parent, + int max_count) { + n->set_parent(parent); + n->set_position(0); + n->set_start(0); + n->set_finish(0); + n->set_max_count(max_count); + absl::container_internal::SanitizerPoisonMemoryRegion( + n->start_slot(), max_count * sizeof(slot_type)); + return n; + } + static btree_node *init_internal(btree_node *n, btree_node *parent) { + init_leaf(n, parent, kNodeValues); + // Set `max_count` to a sentinel value to indicate that this node is + // internal. + n->set_max_count(kInternalNodeMaxCount); + absl::container_internal::SanitizerPoisonMemoryRegion( + &n->mutable_child(n->start()), + (kNodeValues + 1) * sizeof(btree_node *)); + return n; + } + void destroy(allocator_type *alloc) { + for (int i = start(); i < finish(); ++i) { + value_destroy(i, alloc); + } + } + + public: + // Exposed only for tests. + static bool testonly_uses_linear_node_search() { + return use_linear_search::value; + } + + private: + template <typename... Args> + void value_init(const size_type i, allocator_type *alloc, Args &&... args) { + absl::container_internal::SanitizerUnpoisonObject(slot(i)); + params_type::construct(alloc, slot(i), std::forward<Args>(args)...); + } + void value_destroy(const size_type i, allocator_type *alloc) { + params_type::destroy(alloc, slot(i)); + absl::container_internal::SanitizerPoisonObject(slot(i)); + } + + // Move n values starting at value i in this node into the values starting at + // value j in node x. + void uninitialized_move_n(const size_type n, const size_type i, + const size_type j, btree_node *x, + allocator_type *alloc) { + absl::container_internal::SanitizerUnpoisonMemoryRegion( + x->slot(j), n * sizeof(slot_type)); + for (slot_type *src = slot(i), *end = src + n, *dest = x->slot(j); + src != end; ++src, ++dest) { + params_type::construct(alloc, dest, src); + } + } + + // Destroys a range of n values, starting at index i. + void value_destroy_n(const size_type i, const size_type n, + allocator_type *alloc) { + for (int j = 0; j < n; ++j) { + value_destroy(i + j, alloc); + } + } + + template <typename P> + friend class btree; + template <typename N, typename R, typename P> + friend struct btree_iterator; + friend class BtreeNodePeer; +}; + +template <typename Node, typename Reference, typename Pointer> +struct btree_iterator { + private: + using key_type = typename Node::key_type; + using size_type = typename Node::size_type; + using params_type = typename Node::params_type; + + using node_type = Node; + using normal_node = typename std::remove_const<Node>::type; + using const_node = const Node; + using normal_pointer = typename params_type::pointer; + using normal_reference = typename params_type::reference; + using const_pointer = typename params_type::const_pointer; + using const_reference = typename params_type::const_reference; + using slot_type = typename params_type::slot_type; + + using iterator = + btree_iterator<normal_node, normal_reference, normal_pointer>; + using const_iterator = + btree_iterator<const_node, const_reference, const_pointer>; + + public: + // These aliases are public for std::iterator_traits. + using difference_type = typename Node::difference_type; + using value_type = typename params_type::value_type; + using pointer = Pointer; + using reference = Reference; + using iterator_category = std::bidirectional_iterator_tag; + + btree_iterator() : node(nullptr), position(-1) {} + explicit btree_iterator(Node *n) : node(n), position(n->start()) {} + btree_iterator(Node *n, int p) : node(n), position(p) {} + + // NOTE: this SFINAE allows for implicit conversions from iterator to + // const_iterator, but it specifically avoids defining copy constructors so + // that btree_iterator can be trivially copyable. This is for performance and + // binary size reasons. + template <typename N, typename R, typename P, + absl::enable_if_t< + std::is_same<btree_iterator<N, R, P>, iterator>::value && + std::is_same<btree_iterator, const_iterator>::value, + int> = 0> + btree_iterator(const btree_iterator<N, R, P> &x) // NOLINT + : node(x.node), position(x.position) {} + + private: + // This SFINAE allows explicit conversions from const_iterator to + // iterator, but also avoids defining a copy constructor. + // NOTE: the const_cast is safe because this constructor is only called by + // non-const methods and the container owns the nodes. + template <typename N, typename R, typename P, + absl::enable_if_t< + std::is_same<btree_iterator<N, R, P>, const_iterator>::value && + std::is_same<btree_iterator, iterator>::value, + int> = 0> + explicit btree_iterator(const btree_iterator<N, R, P> &x) + : node(const_cast<node_type *>(x.node)), position(x.position) {} + + // Increment/decrement the iterator. + void increment() { + if (node->leaf() && ++position < node->finish()) { + return; + } + increment_slow(); + } + void increment_slow(); + + void decrement() { + if (node->leaf() && --position >= node->start()) { + return; + } + decrement_slow(); + } + void decrement_slow(); + + public: + bool operator==(const const_iterator &x) const { + return node == x.node && position == x.position; + } + bool operator!=(const const_iterator &x) const { + return node != x.node || position != x.position; + } + + // Accessors for the key/value the iterator is pointing at. + reference operator*() const { return node->value(position); } + pointer operator->() const { return &node->value(position); } + + btree_iterator &operator++() { + increment(); + return *this; + } + btree_iterator &operator--() { + decrement(); + return *this; + } + btree_iterator operator++(int) { + btree_iterator tmp = *this; + ++*this; + return tmp; + } + btree_iterator operator--(int) { + btree_iterator tmp = *this; + --*this; + return tmp; + } + + private: + template <typename Params> + friend class btree; + template <typename Tree> + friend class btree_container; + template <typename Tree> + friend class btree_set_container; + template <typename Tree> + friend class btree_map_container; + template <typename Tree> + friend class btree_multiset_container; + template <typename N, typename R, typename P> + friend struct btree_iterator; + template <typename TreeType, typename CheckerType> + friend class base_checker; + + const key_type &key() const { return node->key(position); } + slot_type *slot() { return node->slot(position); } + + // The node in the tree the iterator is pointing at. + Node *node; + // The position within the node of the tree the iterator is pointing at. + // TODO(ezb): make this a field_type + int position; +}; + +template <typename Params> +class btree { + using node_type = btree_node<Params>; + using is_key_compare_to = typename Params::is_key_compare_to; + + // We use a static empty node for the root/leftmost/rightmost of empty btrees + // in order to avoid branching in begin()/end(). + struct alignas(node_type::Alignment()) EmptyNodeType : node_type { + using field_type = typename node_type::field_type; + node_type *parent; + field_type position = 0; + field_type start = 0; + field_type finish = 0; + // max_count must be != kInternalNodeMaxCount (so that this node is regarded + // as a leaf node). max_count() is never called when the tree is empty. + field_type max_count = node_type::kInternalNodeMaxCount + 1; + +#ifdef _MSC_VER + // MSVC has constexpr code generations bugs here. + EmptyNodeType() : parent(this) {} +#else + constexpr EmptyNodeType(node_type *p) : parent(p) {} +#endif + }; + + static node_type *EmptyNode() { +#ifdef _MSC_VER + static EmptyNodeType *empty_node = new EmptyNodeType; + // This assert fails on some other construction methods. + assert(empty_node->parent == empty_node); + return empty_node; +#else + static constexpr EmptyNodeType empty_node( + const_cast<EmptyNodeType *>(&empty_node)); + return const_cast<EmptyNodeType *>(&empty_node); +#endif + } + + enum { + kNodeValues = node_type::kNodeValues, + kMinNodeValues = kNodeValues / 2, + }; + + struct node_stats { + using size_type = typename Params::size_type; + + node_stats(size_type l, size_type i) : leaf_nodes(l), internal_nodes(i) {} + + node_stats &operator+=(const node_stats &x) { + leaf_nodes += x.leaf_nodes; + internal_nodes += x.internal_nodes; + return *this; + } + + size_type leaf_nodes; + size_type internal_nodes; + }; + + public: + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + using key_compare = typename Params::key_compare; + using value_compare = typename Params::value_compare; + using allocator_type = typename Params::allocator_type; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using iterator = btree_iterator<node_type, reference, pointer>; + using const_iterator = typename iterator::const_iterator; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using node_handle_type = node_handle<Params, Params, allocator_type>; + + // Internal types made public for use by btree_container types. + using params_type = Params; + using slot_type = typename Params::slot_type; + + private: + // For use in copy_or_move_values_in_order. + const value_type &maybe_move_from_iterator(const_iterator x) { return *x; } + value_type &&maybe_move_from_iterator(iterator x) { return std::move(*x); } + + // Copies or moves (depending on the template parameter) the values in + // x into this btree in their order in x. This btree must be empty before this + // method is called. This method is used in copy construction, copy + // assignment, and move assignment. + template <typename Btree> + void copy_or_move_values_in_order(Btree *x); + + // Validates that various assumptions/requirements are true at compile time. + constexpr static bool static_assert_validation(); + + public: + btree(const key_compare &comp, const allocator_type &alloc); + + btree(const btree &x); + btree(btree &&x) noexcept + : root_(std::move(x.root_)), + rightmost_(absl::exchange(x.rightmost_, EmptyNode())), + size_(absl::exchange(x.size_, 0)) { + x.mutable_root() = EmptyNode(); + } + + ~btree() { + // Put static_asserts in destructor to avoid triggering them before the type + // is complete. + static_assert(static_assert_validation(), "This call must be elided."); + clear(); + } + + // Assign the contents of x to *this. + btree &operator=(const btree &x); + btree &operator=(btree &&x) noexcept; + + iterator begin() { return iterator(leftmost()); } + const_iterator begin() const { return const_iterator(leftmost()); } + iterator end() { return iterator(rightmost_, rightmost_->finish()); } + const_iterator end() const { + return const_iterator(rightmost_, rightmost_->finish()); + } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // Finds the first element whose key is not less than key. + template <typename K> + iterator lower_bound(const K &key) { + return internal_end(internal_lower_bound(key)); + } + template <typename K> + const_iterator lower_bound(const K &key) const { + return internal_end(internal_lower_bound(key)); + } + + // Finds the first element whose key is greater than key. + template <typename K> + iterator upper_bound(const K &key) { + return internal_end(internal_upper_bound(key)); + } + template <typename K> + const_iterator upper_bound(const K &key) const { + return internal_end(internal_upper_bound(key)); + } + + // Finds the range of values which compare equal to key. The first member of + // the returned pair is equal to lower_bound(key). The second member pair of + // the pair is equal to upper_bound(key). + template <typename K> + std::pair<iterator, iterator> equal_range(const K &key) { + return {lower_bound(key), upper_bound(key)}; + } + template <typename K> + std::pair<const_iterator, const_iterator> equal_range(const K &key) const { + return {lower_bound(key), upper_bound(key)}; + } + + // Inserts a value into the btree only if it does not already exist. The + // boolean return value indicates whether insertion succeeded or failed. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template <typename... Args> + std::pair<iterator, bool> insert_unique(const key_type &key, Args &&... args); + + // Inserts with hint. Checks to see if the value should be placed immediately + // before `position` in the tree. If so, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_unique() were made. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template <typename... Args> + std::pair<iterator, bool> insert_hint_unique(iterator position, + const key_type &key, + Args &&... args); + + // Insert a range of values into the btree. + template <typename InputIterator> + void insert_iterator_unique(InputIterator b, InputIterator e); + + // Inserts a value into the btree. + template <typename ValueType> + iterator insert_multi(const key_type &key, ValueType &&v); + + // Inserts a value into the btree. + template <typename ValueType> + iterator insert_multi(ValueType &&v) { + return insert_multi(params_type::key(v), std::forward<ValueType>(v)); + } + + // Insert with hint. Check to see if the value should be placed immediately + // before position in the tree. If it does, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_multi(v) were made. + template <typename ValueType> + iterator insert_hint_multi(iterator position, ValueType &&v); + + // Insert a range of values into the btree. + template <typename InputIterator> + void insert_iterator_multi(InputIterator b, InputIterator e); + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + // Requirement: does not read the value at `*iter`. + iterator erase(iterator iter); + + // Erases range. Returns the number of keys erased and an iterator pointing + // to the element after the last erased element. + std::pair<size_type, iterator> erase_range(iterator begin, iterator end); + + // Erases the specified key from the btree. Returns 1 if an element was + // erased and 0 otherwise. + template <typename K> + size_type erase_unique(const K &key); + + // Erases all of the entries matching the specified key from the + // btree. Returns the number of elements erased. + template <typename K> + size_type erase_multi(const K &key); + + // Finds the iterator corresponding to a key or returns end() if the key is + // not present. + template <typename K> + iterator find(const K &key) { + return internal_end(internal_find(key)); + } + template <typename K> + const_iterator find(const K &key) const { + return internal_end(internal_find(key)); + } + + // Returns a count of the number of times the key appears in the btree. + template <typename K> + size_type count_unique(const K &key) const { + const iterator begin = internal_find(key); + if (begin.node == nullptr) { + // The key doesn't exist in the tree. + return 0; + } + return 1; + } + // Returns a count of the number of times the key appears in the btree. + template <typename K> + size_type count_multi(const K &key) const { + const auto range = equal_range(key); + return std::distance(range.first, range.second); + } + + // Clear the btree, deleting all of the values it contains. + void clear(); + + // Swap the contents of *this and x. + void swap(btree &x); + + const key_compare &key_comp() const noexcept { + return root_.template get<0>(); + } + template <typename K, typename LK> + bool compare_keys(const K &x, const LK &y) const { + return compare_internal::compare_result_as_less_than(key_comp()(x, y)); + } + + value_compare value_comp() const { return value_compare(key_comp()); } + + // Verifies the structure of the btree. + void verify() const; + + // Size routines. + size_type size() const { return size_; } + size_type max_size() const { return (std::numeric_limits<size_type>::max)(); } + bool empty() const { return size_ == 0; } + + // The height of the btree. An empty tree will have height 0. + size_type height() const { + size_type h = 0; + if (!empty()) { + // Count the length of the chain from the leftmost node up to the + // root. We actually count from the root back around to the level below + // the root, but the calculation is the same because of the circularity + // of that traversal. + const node_type *n = root(); + do { + ++h; + n = n->parent(); + } while (n != root()); + } + return h; + } + + // The number of internal, leaf and total nodes used by the btree. + size_type leaf_nodes() const { return internal_stats(root()).leaf_nodes; } + size_type internal_nodes() const { + return internal_stats(root()).internal_nodes; + } + size_type nodes() const { + node_stats stats = internal_stats(root()); + return stats.leaf_nodes + stats.internal_nodes; + } + + // The total number of bytes used by the btree. + size_type bytes_used() const { + node_stats stats = internal_stats(root()); + if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { + return sizeof(*this) + node_type::LeafSize(root()->max_count()); + } else { + return sizeof(*this) + stats.leaf_nodes * node_type::LeafSize() + + stats.internal_nodes * node_type::InternalSize(); + } + } + + // The average number of bytes used per value stored in the btree. + static double average_bytes_per_value() { + // Returns the number of bytes per value on a leaf node that is 75% + // full. Experimentally, this matches up nicely with the computed number of + // bytes per value in trees that had their values inserted in random order. + return node_type::LeafSize() / (kNodeValues * 0.75); + } + + // The fullness of the btree. Computed as the number of elements in the btree + // divided by the maximum number of elements a tree with the current number + // of nodes could hold. A value of 1 indicates perfect space + // utilization. Smaller values indicate space wastage. + // Returns 0 for empty trees. + double fullness() const { + if (empty()) return 0.0; + return static_cast<double>(size()) / (nodes() * kNodeValues); + } + // The overhead of the btree structure in bytes per node. Computed as the + // total number of bytes used by the btree minus the number of bytes used for + // storing elements divided by the number of elements. + // Returns 0 for empty trees. + double overhead() const { + if (empty()) return 0.0; + return (bytes_used() - size() * sizeof(value_type)) / + static_cast<double>(size()); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { return allocator(); } + + private: + // Internal accessor routines. + node_type *root() { return root_.template get<2>(); } + const node_type *root() const { return root_.template get<2>(); } + node_type *&mutable_root() noexcept { return root_.template get<2>(); } + key_compare *mutable_key_comp() noexcept { return &root_.template get<0>(); } + + // The leftmost node is stored as the parent of the root node. + node_type *leftmost() { return root()->parent(); } + const node_type *leftmost() const { return root()->parent(); } + + // Allocator routines. + allocator_type *mutable_allocator() noexcept { + return &root_.template get<1>(); + } + const allocator_type &allocator() const noexcept { + return root_.template get<1>(); + } + + // Allocates a correctly aligned node of at least size bytes using the + // allocator. + node_type *allocate(const size_type size) { + return reinterpret_cast<node_type *>( + absl::container_internal::Allocate<node_type::Alignment()>( + mutable_allocator(), size)); + } + + // Node creation/deletion routines. + node_type *new_internal_node(node_type *parent) { + node_type *p = allocate(node_type::InternalSize()); + return node_type::init_internal(p, parent); + } + node_type *new_leaf_node(node_type *parent) { + node_type *p = allocate(node_type::LeafSize()); + return node_type::init_leaf(p, parent, kNodeValues); + } + node_type *new_leaf_root_node(const int max_count) { + node_type *p = allocate(node_type::LeafSize(max_count)); + return node_type::init_leaf(p, p, max_count); + } + + // Deletion helper routines. + void erase_same_node(iterator begin, iterator end); + iterator erase_from_leaf_node(iterator begin, size_type to_erase); + iterator rebalance_after_delete(iterator iter); + + // Deallocates a node of a certain size in bytes using the allocator. + void deallocate(const size_type size, node_type *node) { + absl::container_internal::Deallocate<node_type::Alignment()>( + mutable_allocator(), node, size); + } + + void delete_internal_node(node_type *node) { + node->destroy(mutable_allocator()); + deallocate(node_type::InternalSize(), node); + } + void delete_leaf_node(node_type *node) { + node->destroy(mutable_allocator()); + deallocate(node_type::LeafSize(node->max_count()), node); + } + + // Rebalances or splits the node iter points to. + void rebalance_or_split(iterator *iter); + + // Merges the values of left, right and the delimiting key on their parent + // onto left, removing the delimiting key and deleting right. + void merge_nodes(node_type *left, node_type *right); + + // Tries to merge node with its left or right sibling, and failing that, + // rebalance with its left or right sibling. Returns true if a merge + // occurred, at which point it is no longer valid to access node. Returns + // false if no merging took place. + bool try_merge_or_rebalance(iterator *iter); + + // Tries to shrink the height of the tree by 1. + void try_shrink(); + + iterator internal_end(iterator iter) { + return iter.node != nullptr ? iter : end(); + } + const_iterator internal_end(const_iterator iter) const { + return iter.node != nullptr ? iter : end(); + } + + // Emplaces a value into the btree immediately before iter. Requires that + // key(v) <= iter.key() and (--iter).key() <= key(v). + template <typename... Args> + iterator internal_emplace(iterator iter, Args &&... args); + + // Returns an iterator pointing to the first value >= the value "iter" is + // pointing at. Note that "iter" might be pointing to an invalid location such + // as iter.position == iter.node->finish(). This routine simply moves iter up + // in the tree to a valid location. + // Requires: iter.node is non-null. + template <typename IterType> + static IterType internal_last(IterType iter); + + // Returns an iterator pointing to the leaf position at which key would + // reside in the tree. We provide 2 versions of internal_locate. The first + // version uses a less-than comparator and is incapable of distinguishing when + // there is an exact match. The second version is for the key-compare-to + // specialization and distinguishes exact matches. The key-compare-to + // specialization allows the caller to avoid a subsequent comparison to + // determine if an exact match was made, which is important for keys with + // expensive comparison, such as strings. + template <typename K> + SearchResult<iterator, is_key_compare_to::value> internal_locate( + const K &key) const; + + template <typename K> + SearchResult<iterator, false> internal_locate_impl( + const K &key, std::false_type /* IsCompareTo */) const; + + template <typename K> + SearchResult<iterator, true> internal_locate_impl( + const K &key, std::true_type /* IsCompareTo */) const; + + // Internal routine which implements lower_bound(). + template <typename K> + iterator internal_lower_bound(const K &key) const; + + // Internal routine which implements upper_bound(). + template <typename K> + iterator internal_upper_bound(const K &key) const; + + // Internal routine which implements find(). + template <typename K> + iterator internal_find(const K &key) const; + + // Deletes a node and all of its children. + void internal_clear(node_type *node); + + // Verifies the tree structure of node. + int internal_verify(const node_type *node, const key_type *lo, + const key_type *hi) const; + + node_stats internal_stats(const node_type *node) const { + // The root can be a static empty node. + if (node == nullptr || (node == root() && empty())) { + return node_stats(0, 0); + } + if (node->leaf()) { + return node_stats(1, 0); + } + node_stats res(0, 1); + for (int i = node->start(); i <= node->finish(); ++i) { + res += internal_stats(node->child(i)); + } + return res; + } + + public: + // Exposed only for tests. + static bool testonly_uses_linear_node_search() { + return node_type::testonly_uses_linear_node_search(); + } + + private: + // We use compressed tuple in order to save space because key_compare and + // allocator_type are usually empty. + absl::container_internal::CompressedTuple<key_compare, allocator_type, + node_type *> + root_; + + // A pointer to the rightmost node. Note that the leftmost node is stored as + // the root's parent. + node_type *rightmost_; + + // Number of values. + size_type size_; +}; + +//// +// btree_node methods +template <typename P> +template <typename... Args> +inline void btree_node<P>::emplace_value(const size_type i, + allocator_type *alloc, + Args &&... args) { + assert(i >= start()); + assert(i <= finish()); + // Shift old values to create space for new value and then construct it in + // place. + if (i < finish()) { + value_init(finish(), alloc, slot(finish() - 1)); + for (size_type j = finish() - 1; j > i; --j) + params_type::move(alloc, slot(j - 1), slot(j)); + value_destroy(i, alloc); + } + value_init(i, alloc, std::forward<Args>(args)...); + set_finish(finish() + 1); + + if (!leaf() && finish() > i + 1) { + for (int j = finish(); j > i + 1; --j) { + set_child(j, child(j - 1)); + } + clear_child(i + 1); + } +} + +template <typename P> +inline void btree_node<P>::remove_value(const int i, allocator_type *alloc) { + if (!leaf() && finish() > i + 1) { + assert(child(i + 1)->count() == 0); + for (size_type j = i + 1; j < finish(); ++j) { + set_child(j, child(j + 1)); + } + clear_child(finish()); + } + + remove_values_ignore_children(i, /*to_erase=*/1, alloc); +} + +template <typename P> +inline void btree_node<P>::remove_values_ignore_children( + const int i, const int to_erase, allocator_type *alloc) { + params_type::move(alloc, slot(i + to_erase), finish_slot(), slot(i)); + value_destroy_n(finish() - to_erase, to_erase, alloc); + set_finish(finish() - to_erase); +} + +template <typename P> +void btree_node<P>::rebalance_right_to_left(const int to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(right->count() >= count()); + assert(to_move >= 1); + assert(to_move <= right->count()); + + // 1) Move the delimiting value in the parent to the left node. + value_init(finish(), alloc, parent()->slot(position())); + + // 2) Move the (to_move - 1) values from the right node to the left node. + right->uninitialized_move_n(to_move - 1, right->start(), finish() + 1, this, + alloc); + + // 3) Move the new delimiting value to the parent from the right node. + params_type::move(alloc, right->slot(to_move - 1), + parent()->slot(position())); + + // 4) Shift the values in the right node to their correct position. + params_type::move(alloc, right->slot(to_move), right->finish_slot(), + right->start_slot()); + + // 5) Destroy the now-empty to_move entries in the right node. + right->value_destroy_n(right->finish() - to_move, to_move, alloc); + + if (!leaf()) { + // Move the child pointers from the right to the left node. + for (int i = 0; i < to_move; ++i) { + init_child(finish() + i + 1, right->child(i)); + } + for (int i = right->start(); i <= right->finish() - to_move; ++i) { + assert(i + to_move <= right->max_count()); + right->init_child(i, right->child(i + to_move)); + right->clear_child(i + to_move); + } + } + + // Fixup `finish` on the left and right nodes. + set_finish(finish() + to_move); + right->set_finish(right->finish() - to_move); +} + +template <typename P> +void btree_node<P>::rebalance_left_to_right(const int to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(count() >= right->count()); + assert(to_move >= 1); + assert(to_move <= count()); + + // Values in the right node are shifted to the right to make room for the + // new to_move values. Then, the delimiting value in the parent and the + // other (to_move - 1) values in the left node are moved into the right node. + // Lastly, a new delimiting value is moved from the left node into the + // parent, and the remaining empty left node entries are destroyed. + + if (right->count() >= to_move) { + // The original location of the right->count() values are sufficient to hold + // the new to_move entries from the parent and left node. + + // 1) Shift existing values in the right node to their correct positions. + right->uninitialized_move_n(to_move, right->finish() - to_move, + right->finish(), right, alloc); + for (slot_type *src = right->slot(right->finish() - to_move - 1), + *dest = right->slot(right->finish() - 1), + *end = right->start_slot(); + src >= end; --src, --dest) { + params_type::move(alloc, src, dest); + } + + // 2) Move the delimiting value in the parent to the right node. + params_type::move(alloc, parent()->slot(position()), + right->slot(to_move - 1)); + + // 3) Move the (to_move - 1) values from the left node to the right node. + params_type::move(alloc, slot(finish() - (to_move - 1)), finish_slot(), + right->start_slot()); + } else { + // The right node does not have enough initialized space to hold the new + // to_move entries, so part of them will move to uninitialized space. + + // 1) Shift existing values in the right node to their correct positions. + right->uninitialized_move_n(right->count(), right->start(), + right->start() + to_move, right, alloc); + + // 2) Move the delimiting value in the parent to the right node. + right->value_init(to_move - 1, alloc, parent()->slot(position())); + + // 3) Move the (to_move - 1) values from the left node to the right node. + const size_type uninitialized_remaining = to_move - right->count() - 1; + uninitialized_move_n(uninitialized_remaining, + finish() - uninitialized_remaining, right->finish(), + right, alloc); + params_type::move(alloc, slot(finish() - (to_move - 1)), + slot(finish() - uninitialized_remaining), + right->start_slot()); + } + + // 4) Move the new delimiting value to the parent from the left node. + params_type::move(alloc, slot(finish() - to_move), + parent()->slot(position())); + + // 5) Destroy the now-empty to_move entries in the left node. + value_destroy_n(finish() - to_move, to_move, alloc); + + if (!leaf()) { + // Move the child pointers from the left to the right node. + for (int i = right->finish(); i >= right->start(); --i) { + right->init_child(i + to_move, right->child(i)); + right->clear_child(i); + } + for (int i = 1; i <= to_move; ++i) { + right->init_child(i - 1, child(finish() - to_move + i)); + clear_child(finish() - to_move + i); + } + } + + // Fixup the counts on the left and right nodes. + set_finish(finish() - to_move); + right->set_finish(right->finish() + to_move); +} + +template <typename P> +void btree_node<P>::split(const int insert_position, btree_node *dest, + allocator_type *alloc) { + assert(dest->count() == 0); + assert(max_count() == kNodeValues); + + // We bias the split based on the position being inserted. If we're + // inserting at the beginning of the left node then bias the split to put + // more values on the right node. If we're inserting at the end of the + // right node then bias the split to put more values on the left node. + if (insert_position == start()) { + dest->set_finish(dest->start() + finish() - 1); + } else if (insert_position == kNodeValues) { + dest->set_finish(dest->start()); + } else { + dest->set_finish(dest->start() + count() / 2); + } + set_finish(finish() - dest->count()); + assert(count() >= 1); + + // Move values from the left sibling to the right sibling. + uninitialized_move_n(dest->count(), finish(), dest->start(), dest, alloc); + + // Destroy the now-empty entries in the left node. + value_destroy_n(finish(), dest->count(), alloc); + + // The split key is the largest value in the left sibling. + --mutable_finish(); + parent()->emplace_value(position(), alloc, finish_slot()); + value_destroy(finish(), alloc); + parent()->init_child(position() + 1, dest); + + if (!leaf()) { + for (int i = dest->start(), j = finish() + 1; i <= dest->finish(); + ++i, ++j) { + assert(child(j) != nullptr); + dest->init_child(i, child(j)); + clear_child(j); + } + } +} + +template <typename P> +void btree_node<P>::merge(btree_node *src, allocator_type *alloc) { + assert(parent() == src->parent()); + assert(position() + 1 == src->position()); + + // Move the delimiting value to the left node. + value_init(finish(), alloc, parent()->slot(position())); + + // Move the values from the right to the left node. + src->uninitialized_move_n(src->count(), src->start(), finish() + 1, this, + alloc); + + // Destroy the now-empty entries in the right node. + src->value_destroy_n(src->start(), src->count(), alloc); + + if (!leaf()) { + // Move the child pointers from the right to the left node. + for (int i = src->start(), j = finish() + 1; i <= src->finish(); ++i, ++j) { + init_child(j, src->child(i)); + src->clear_child(i); + } + } + + // Fixup `finish` on the src and dest nodes. + set_finish(start() + 1 + count() + src->count()); + src->set_finish(src->start()); + + // Remove the value on the parent node. + parent()->remove_value(position(), alloc); +} + +template <typename P> +void btree_node<P>::swap(btree_node *x, allocator_type *alloc) { + using std::swap; + assert(leaf() == x->leaf()); + + // Determine which is the smaller/larger node. + btree_node *smaller = this, *larger = x; + if (smaller->count() > larger->count()) { + swap(smaller, larger); + } + + // Swap the values. + for (slot_type *a = smaller->start_slot(), *b = larger->start_slot(), + *end = smaller->finish_slot(); + a != end; ++a, ++b) { + params_type::swap(alloc, a, b); + } + + // Move values that can't be swapped. + const size_type to_move = larger->count() - smaller->count(); + larger->uninitialized_move_n(to_move, smaller->finish(), smaller->finish(), + smaller, alloc); + larger->value_destroy_n(smaller->finish(), to_move, alloc); + + if (!leaf()) { + // Swap the child pointers. + std::swap_ranges(&smaller->mutable_child(smaller->start()), + &smaller->mutable_child(smaller->finish() + 1), + &larger->mutable_child(larger->start())); + // Update swapped children's parent pointers. + int i = smaller->start(); + int j = larger->start(); + for (; i <= smaller->finish(); ++i, ++j) { + smaller->child(i)->set_parent(smaller); + larger->child(j)->set_parent(larger); + } + // Move the child pointers that couldn't be swapped. + for (; j <= larger->finish(); ++i, ++j) { + smaller->init_child(i, larger->child(j)); + larger->clear_child(j); + } + } + + // Swap the `finish`s. + // TODO(ezb): with floating storage, will also need to swap starts. + swap(mutable_finish(), x->mutable_finish()); +} + +//// +// btree_iterator methods +template <typename N, typename R, typename P> +void btree_iterator<N, R, P>::increment_slow() { + if (node->leaf()) { + assert(position >= node->finish()); + btree_iterator save(*this); + while (position == node->finish() && !node->is_root()) { + assert(node->parent()->child(node->position()) == node); + position = node->position(); + node = node->parent(); + } + if (position == node->finish()) { + *this = save; + } + } else { + assert(position < node->finish()); + node = node->child(position + 1); + while (!node->leaf()) { + node = node->start_child(); + } + position = node->start(); + } +} + +template <typename N, typename R, typename P> +void btree_iterator<N, R, P>::decrement_slow() { + if (node->leaf()) { + assert(position <= -1); + btree_iterator save(*this); + while (position < node->start() && !node->is_root()) { + assert(node->parent()->child(node->position()) == node); + position = node->position() - 1; + node = node->parent(); + } + if (position < node->start()) { + *this = save; + } + } else { + assert(position >= node->start()); + node = node->child(position); + while (!node->leaf()) { + node = node->child(node->finish()); + } + position = node->finish() - 1; + } +} + +//// +// btree methods +template <typename P> +template <typename Btree> +void btree<P>::copy_or_move_values_in_order(Btree *x) { + static_assert(std::is_same<btree, Btree>::value || + std::is_same<const btree, Btree>::value, + "Btree type must be same or const."); + assert(empty()); + + // We can avoid key comparisons because we know the order of the + // values is the same order we'll store them in. + auto iter = x->begin(); + if (iter == x->end()) return; + insert_multi(maybe_move_from_iterator(iter)); + ++iter; + for (; iter != x->end(); ++iter) { + // If the btree is not empty, we can just insert the new value at the end + // of the tree. + internal_emplace(end(), maybe_move_from_iterator(iter)); + } +} + +template <typename P> +constexpr bool btree<P>::static_assert_validation() { + static_assert(std::is_nothrow_copy_constructible<key_compare>::value, + "Key comparison must be nothrow copy constructible"); + static_assert(std::is_nothrow_copy_constructible<allocator_type>::value, + "Allocator must be nothrow copy constructible"); + static_assert(type_traits_internal::is_trivially_copyable<iterator>::value, + "iterator not trivially copyable."); + + // Note: We assert that kTargetValues, which is computed from + // Params::kTargetNodeSize, must fit the node_type::field_type. + static_assert( + kNodeValues < (1 << (8 * sizeof(typename node_type::field_type))), + "target node size too large"); + + // Verify that key_compare returns an absl::{weak,strong}_ordering or bool. + using compare_result_type = + absl::result_of_t<key_compare(key_type, key_type)>; + static_assert( + std::is_same<compare_result_type, bool>::value || + std::is_convertible<compare_result_type, absl::weak_ordering>::value, + "key comparison function must return absl::{weak,strong}_ordering or " + "bool."); + + // Test the assumption made in setting kNodeValueSpace. + static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4, + "node space assumption incorrect"); + + return true; +} + +template <typename P> +btree<P>::btree(const key_compare &comp, const allocator_type &alloc) + : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {} + +template <typename P> +btree<P>::btree(const btree &x) : btree(x.key_comp(), x.allocator()) { + copy_or_move_values_in_order(&x); +} + +template <typename P> +template <typename... Args> +auto btree<P>::insert_unique(const key_type &key, Args &&... args) + -> std::pair<iterator, bool> { + if (empty()) { + mutable_root() = rightmost_ = new_leaf_root_node(1); + } + + auto res = internal_locate(key); + iterator &iter = res.value; + + if (res.HasMatch()) { + if (res.IsEq()) { + // The key already exists in the tree, do nothing. + return {iter, false}; + } + } else { + iterator last = internal_last(iter); + if (last.node && !compare_keys(key, last.key())) { + // The key already exists in the tree, do nothing. + return {last, false}; + } + } + return {internal_emplace(iter, std::forward<Args>(args)...), true}; +} + +template <typename P> +template <typename... Args> +inline auto btree<P>::insert_hint_unique(iterator position, const key_type &key, + Args &&... args) + -> std::pair<iterator, bool> { + if (!empty()) { + if (position == end() || compare_keys(key, position.key())) { + if (position == begin() || compare_keys(std::prev(position).key(), key)) { + // prev.key() < key < position.key() + return {internal_emplace(position, std::forward<Args>(args)...), true}; + } + } else if (compare_keys(position.key(), key)) { + ++position; + if (position == end() || compare_keys(key, position.key())) { + // {original `position`}.key() < key < {current `position`}.key() + return {internal_emplace(position, std::forward<Args>(args)...), true}; + } + } else { + // position.key() == key + return {position, false}; + } + } + return insert_unique(key, std::forward<Args>(args)...); +} + +template <typename P> +template <typename InputIterator> +void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_hint_unique(end(), params_type::key(*b), *b); + } +} + +template <typename P> +template <typename ValueType> +auto btree<P>::insert_multi(const key_type &key, ValueType &&v) -> iterator { + if (empty()) { + mutable_root() = rightmost_ = new_leaf_root_node(1); + } + + iterator iter = internal_upper_bound(key); + if (iter.node == nullptr) { + iter = end(); + } + return internal_emplace(iter, std::forward<ValueType>(v)); +} + +template <typename P> +template <typename ValueType> +auto btree<P>::insert_hint_multi(iterator position, ValueType &&v) -> iterator { + if (!empty()) { + const key_type &key = params_type::key(v); + if (position == end() || !compare_keys(position.key(), key)) { + if (position == begin() || + !compare_keys(key, std::prev(position).key())) { + // prev.key() <= key <= position.key() + return internal_emplace(position, std::forward<ValueType>(v)); + } + } else { + ++position; + if (position == end() || !compare_keys(position.key(), key)) { + // {original `position`}.key() < key < {current `position`}.key() + return internal_emplace(position, std::forward<ValueType>(v)); + } + } + } + return insert_multi(std::forward<ValueType>(v)); +} + +template <typename P> +template <typename InputIterator> +void btree<P>::insert_iterator_multi(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_hint_multi(end(), *b); + } +} + +template <typename P> +auto btree<P>::operator=(const btree &x) -> btree & { + if (this != &x) { + clear(); + + *mutable_key_comp() = x.key_comp(); + if (absl::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + *mutable_allocator() = x.allocator(); + } + + copy_or_move_values_in_order(&x); + } + return *this; +} + +template <typename P> +auto btree<P>::operator=(btree &&x) noexcept -> btree & { + if (this != &x) { + clear(); + + using std::swap; + if (absl::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + // Note: `root_` also contains the allocator and the key comparator. + swap(root_, x.root_); + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); + } else { + if (allocator() == x.allocator()) { + swap(mutable_root(), x.mutable_root()); + swap(*mutable_key_comp(), *x.mutable_key_comp()); + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); + } else { + // We aren't allowed to propagate the allocator and the allocator is + // different so we can't take over its memory. We must move each element + // individually. We need both `x` and `this` to have `x`s key comparator + // while moving the values so we can't swap the key comparators. + *mutable_key_comp() = x.key_comp(); + copy_or_move_values_in_order(&x); + } + } + } + return *this; +} + +template <typename P> +auto btree<P>::erase(iterator iter) -> iterator { + bool internal_delete = false; + if (!iter.node->leaf()) { + // Deletion of a value on an internal node. First, move the largest value + // from our left child here, then delete that position (in remove_value() + // below). We can get to the largest value from our left child by + // decrementing iter. + iterator internal_iter(iter); + --iter; + assert(iter.node->leaf()); + params_type::move(mutable_allocator(), iter.node->slot(iter.position), + internal_iter.node->slot(internal_iter.position)); + internal_delete = true; + } + + // Delete the key from the leaf. + iter.node->remove_value(iter.position, mutable_allocator()); + --size_; + + // We want to return the next value after the one we just erased. If we + // erased from an internal node (internal_delete == true), then the next + // value is ++(++iter). If we erased from a leaf node (internal_delete == + // false) then the next value is ++iter. Note that ++iter may point to an + // internal node and the value in the internal node may move to a leaf node + // (iter.node) when rebalancing is performed at the leaf level. + + iterator res = rebalance_after_delete(iter); + + // If we erased from an internal node, advance the iterator. + if (internal_delete) { + ++res; + } + return res; +} + +template <typename P> +auto btree<P>::rebalance_after_delete(iterator iter) -> iterator { + // Merge/rebalance as we walk back up the tree. + iterator res(iter); + bool first_iteration = true; + for (;;) { + if (iter.node == root()) { + try_shrink(); + if (empty()) { + return end(); + } + break; + } + if (iter.node->count() >= kMinNodeValues) { + break; + } + bool merged = try_merge_or_rebalance(&iter); + // On the first iteration, we should update `res` with `iter` because `res` + // may have been invalidated. + if (first_iteration) { + res = iter; + first_iteration = false; + } + if (!merged) { + break; + } + iter.position = iter.node->position(); + iter.node = iter.node->parent(); + } + + // Adjust our return value. If we're pointing at the end of a node, advance + // the iterator. + if (res.position == res.node->finish()) { + res.position = res.node->finish() - 1; + ++res; + } + + return res; +} + +template <typename P> +auto btree<P>::erase_range(iterator begin, iterator end) + -> std::pair<size_type, iterator> { + difference_type count = std::distance(begin, end); + assert(count >= 0); + + if (count == 0) { + return {0, begin}; + } + + if (count == size_) { + clear(); + return {count, this->end()}; + } + + if (begin.node == end.node) { + erase_same_node(begin, end); + size_ -= count; + return {count, rebalance_after_delete(begin)}; + } + + const size_type target_size = size_ - count; + while (size_ > target_size) { + if (begin.node->leaf()) { + const size_type remaining_to_erase = size_ - target_size; + const size_type remaining_in_node = begin.node->finish() - begin.position; + begin = erase_from_leaf_node( + begin, (std::min)(remaining_to_erase, remaining_in_node)); + } else { + begin = erase(begin); + } + } + return {count, begin}; +} + +template <typename P> +void btree<P>::erase_same_node(iterator begin, iterator end) { + assert(begin.node == end.node); + assert(end.position > begin.position); + + node_type *node = begin.node; + size_type to_erase = end.position - begin.position; + if (!node->leaf()) { + // Delete all children between begin and end. + for (size_type i = 0; i < to_erase; ++i) { + internal_clear(node->child(begin.position + i + 1)); + } + // Rotate children after end into new positions. + for (size_type i = begin.position + to_erase + 1; i <= node->finish(); + ++i) { + node->set_child(i - to_erase, node->child(i)); + node->clear_child(i); + } + } + node->remove_values_ignore_children(begin.position, to_erase, + mutable_allocator()); + + // Do not need to update rightmost_, because + // * either end == this->end(), and therefore node == rightmost_, and still + // exists + // * or end != this->end(), and therefore rightmost_ hasn't been erased, since + // it wasn't covered in [begin, end) +} + +template <typename P> +auto btree<P>::erase_from_leaf_node(iterator begin, size_type to_erase) + -> iterator { + node_type *node = begin.node; + assert(node->leaf()); + assert(node->finish() > begin.position); + assert(begin.position + to_erase <= node->finish()); + + node->remove_values_ignore_children(begin.position, to_erase, + mutable_allocator()); + + size_ -= to_erase; + + return rebalance_after_delete(begin); +} + +template <typename P> +template <typename K> +auto btree<P>::erase_unique(const K &key) -> size_type { + const iterator iter = internal_find(key); + if (iter.node == nullptr) { + // The key doesn't exist in the tree, return nothing done. + return 0; + } + erase(iter); + return 1; +} + +template <typename P> +template <typename K> +auto btree<P>::erase_multi(const K &key) -> size_type { + const iterator begin = internal_lower_bound(key); + if (begin.node == nullptr) { + // The key doesn't exist in the tree, return nothing done. + return 0; + } + // Delete all of the keys between begin and upper_bound(key). + const iterator end = internal_end(internal_upper_bound(key)); + return erase_range(begin, end).first; +} + +template <typename P> +void btree<P>::clear() { + if (!empty()) { + internal_clear(root()); + } + mutable_root() = EmptyNode(); + rightmost_ = EmptyNode(); + size_ = 0; +} + +template <typename P> +void btree<P>::swap(btree &x) { + using std::swap; + if (absl::allocator_traits< + allocator_type>::propagate_on_container_swap::value) { + // Note: `root_` also contains the allocator and the key comparator. + swap(root_, x.root_); + } else { + // It's undefined behavior if the allocators are unequal here. + assert(allocator() == x.allocator()); + swap(mutable_root(), x.mutable_root()); + swap(*mutable_key_comp(), *x.mutable_key_comp()); + } + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); +} + +template <typename P> +void btree<P>::verify() const { + assert(root() != nullptr); + assert(leftmost() != nullptr); + assert(rightmost_ != nullptr); + assert(empty() || size() == internal_verify(root(), nullptr, nullptr)); + assert(leftmost() == (++const_iterator(root(), -1)).node); + assert(rightmost_ == (--const_iterator(root(), root()->finish())).node); + assert(leftmost()->leaf()); + assert(rightmost_->leaf()); +} + +template <typename P> +void btree<P>::rebalance_or_split(iterator *iter) { + node_type *&node = iter->node; + int &insert_position = iter->position; + assert(node->count() == node->max_count()); + assert(kNodeValues == node->max_count()); + + // First try to make room on the node by rebalancing. + node_type *parent = node->parent(); + if (node != root()) { + if (node->position() > parent->start()) { + // Try rebalancing with our left sibling. + node_type *left = parent->child(node->position() - 1); + assert(left->max_count() == kNodeValues); + if (left->count() < kNodeValues) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the end of the right node then we bias rebalancing to + // fill up the left node. + int to_move = (kNodeValues - left->count()) / + (1 + (insert_position < kNodeValues)); + to_move = (std::max)(1, to_move); + + if (insert_position - to_move >= node->start() || + left->count() + to_move < kNodeValues) { + left->rebalance_right_to_left(to_move, node, mutable_allocator()); + + assert(node->max_count() - node->count() == to_move); + insert_position = insert_position - to_move; + if (insert_position < node->start()) { + insert_position = insert_position + left->count() + 1; + node = left; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + if (node->position() < parent->finish()) { + // Try rebalancing with our right sibling. + node_type *right = parent->child(node->position() + 1); + assert(right->max_count() == kNodeValues); + if (right->count() < kNodeValues) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the beginning of the left node then we bias rebalancing + // to fill up the right node. + int to_move = (kNodeValues - right->count()) / + (1 + (insert_position > node->start())); + to_move = (std::max)(1, to_move); + + if (insert_position <= node->finish() - to_move || + right->count() + to_move < kNodeValues) { + node->rebalance_left_to_right(to_move, right, mutable_allocator()); + + if (insert_position > node->finish()) { + insert_position = insert_position - node->count() - 1; + node = right; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + // Rebalancing failed, make sure there is room on the parent node for a new + // value. + assert(parent->max_count() == kNodeValues); + if (parent->count() == kNodeValues) { + iterator parent_iter(node->parent(), node->position()); + rebalance_or_split(&parent_iter); + } + } else { + // Rebalancing not possible because this is the root node. + // Create a new root node and set the current root node as the child of the + // new root. + parent = new_internal_node(parent); + parent->init_child(parent->start(), root()); + mutable_root() = parent; + // If the former root was a leaf node, then it's now the rightmost node. + assert(!parent->start_child()->leaf() || + parent->start_child() == rightmost_); + } + + // Split the node. + node_type *split_node; + if (node->leaf()) { + split_node = new_leaf_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + if (rightmost_ == node) rightmost_ = split_node; + } else { + split_node = new_internal_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + } + + if (insert_position > node->finish()) { + insert_position = insert_position - node->count() - 1; + node = split_node; + } +} + +template <typename P> +void btree<P>::merge_nodes(node_type *left, node_type *right) { + left->merge(right, mutable_allocator()); + if (right->leaf()) { + if (rightmost_ == right) rightmost_ = left; + delete_leaf_node(right); + } else { + delete_internal_node(right); + } +} + +template <typename P> +bool btree<P>::try_merge_or_rebalance(iterator *iter) { + node_type *parent = iter->node->parent(); + if (iter->node->position() > parent->start()) { + // Try merging with our left sibling. + node_type *left = parent->child(iter->node->position() - 1); + assert(left->max_count() == kNodeValues); + if (1 + left->count() + iter->node->count() <= kNodeValues) { + iter->position += 1 + left->count(); + merge_nodes(left, iter->node); + iter->node = left; + return true; + } + } + if (iter->node->position() < parent->finish()) { + // Try merging with our right sibling. + node_type *right = parent->child(iter->node->position() + 1); + assert(right->max_count() == kNodeValues); + if (1 + iter->node->count() + right->count() <= kNodeValues) { + merge_nodes(iter->node, right); + return true; + } + // Try rebalancing with our right sibling. We don't perform rebalancing if + // we deleted the first element from iter->node and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the front of the tree. + if (right->count() > kMinNodeValues && + (iter->node->count() == 0 || iter->position > iter->node->start())) { + int to_move = (right->count() - iter->node->count()) / 2; + to_move = (std::min)(to_move, right->count() - 1); + iter->node->rebalance_right_to_left(to_move, right, mutable_allocator()); + return false; + } + } + if (iter->node->position() > parent->start()) { + // Try rebalancing with our left sibling. We don't perform rebalancing if + // we deleted the last element from iter->node and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the back of the tree. + node_type *left = parent->child(iter->node->position() - 1); + if (left->count() > kMinNodeValues && + (iter->node->count() == 0 || iter->position < iter->node->finish())) { + int to_move = (left->count() - iter->node->count()) / 2; + to_move = (std::min)(to_move, left->count() - 1); + left->rebalance_left_to_right(to_move, iter->node, mutable_allocator()); + iter->position += to_move; + return false; + } + } + return false; +} + +template <typename P> +void btree<P>::try_shrink() { + if (root()->count() > 0) { + return; + } + // Deleted the last item on the root node, shrink the height of the tree. + if (root()->leaf()) { + assert(size() == 0); + delete_leaf_node(root()); + mutable_root() = EmptyNode(); + rightmost_ = EmptyNode(); + } else { + node_type *child = root()->start_child(); + child->make_root(); + delete_internal_node(root()); + mutable_root() = child; + } +} + +template <typename P> +template <typename IterType> +inline IterType btree<P>::internal_last(IterType iter) { + assert(iter.node != nullptr); + while (iter.position == iter.node->finish()) { + iter.position = iter.node->position(); + iter.node = iter.node->parent(); + if (iter.node->leaf()) { + iter.node = nullptr; + break; + } + } + return iter; +} + +template <typename P> +template <typename... Args> +inline auto btree<P>::internal_emplace(iterator iter, Args &&... args) + -> iterator { + if (!iter.node->leaf()) { + // We can't insert on an internal node. Instead, we'll insert after the + // previous value which is guaranteed to be on a leaf node. + --iter; + ++iter.position; + } + const int max_count = iter.node->max_count(); + if (iter.node->count() == max_count) { + // Make room in the leaf for the new item. + if (max_count < kNodeValues) { + // Insertion into the root where the root is smaller than the full node + // size. Simply grow the size of the root node. + assert(iter.node == root()); + iter.node = + new_leaf_root_node((std::min<int>)(kNodeValues, 2 * max_count)); + iter.node->swap(root(), mutable_allocator()); + delete_leaf_node(root()); + mutable_root() = iter.node; + rightmost_ = iter.node; + } else { + rebalance_or_split(&iter); + } + } + iter.node->emplace_value(iter.position, mutable_allocator(), + std::forward<Args>(args)...); + ++size_; + return iter; +} + +template <typename P> +template <typename K> +inline auto btree<P>::internal_locate(const K &key) const + -> SearchResult<iterator, is_key_compare_to::value> { + return internal_locate_impl(key, is_key_compare_to()); +} + +template <typename P> +template <typename K> +inline auto btree<P>::internal_locate_impl( + const K &key, std::false_type /* IsCompareTo */) const + -> SearchResult<iterator, false> { + iterator iter(const_cast<node_type *>(root())); + for (;;) { + iter.position = iter.node->lower_bound(key, key_comp()).value; + // NOTE: we don't need to walk all the way down the tree if the keys are + // equal, but determining equality would require doing an extra comparison + // on each node on the way down, and we will need to go all the way to the + // leaf node in the expected case. + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return {iter}; +} + +template <typename P> +template <typename K> +inline auto btree<P>::internal_locate_impl( + const K &key, std::true_type /* IsCompareTo */) const + -> SearchResult<iterator, true> { + iterator iter(const_cast<node_type *>(root())); + for (;;) { + SearchResult<int, true> res = iter.node->lower_bound(key, key_comp()); + iter.position = res.value; + if (res.match == MatchKind::kEq) { + return {iter, MatchKind::kEq}; + } + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return {iter, MatchKind::kNe}; +} + +template <typename P> +template <typename K> +auto btree<P>::internal_lower_bound(const K &key) const -> iterator { + iterator iter(const_cast<node_type *>(root())); + for (;;) { + iter.position = iter.node->lower_bound(key, key_comp()).value; + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return internal_last(iter); +} + +template <typename P> +template <typename K> +auto btree<P>::internal_upper_bound(const K &key) const -> iterator { + iterator iter(const_cast<node_type *>(root())); + for (;;) { + iter.position = iter.node->upper_bound(key, key_comp()); + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return internal_last(iter); +} + +template <typename P> +template <typename K> +auto btree<P>::internal_find(const K &key) const -> iterator { + auto res = internal_locate(key); + if (res.HasMatch()) { + if (res.IsEq()) { + return res.value; + } + } else { + const iterator iter = internal_last(res.value); + if (iter.node != nullptr && !compare_keys(key, iter.key())) { + return iter; + } + } + return {nullptr, 0}; +} + +template <typename P> +void btree<P>::internal_clear(node_type *node) { + if (!node->leaf()) { + for (int i = node->start(); i <= node->finish(); ++i) { + internal_clear(node->child(i)); + } + delete_internal_node(node); + } else { + delete_leaf_node(node); + } +} + +template <typename P> +int btree<P>::internal_verify(const node_type *node, const key_type *lo, + const key_type *hi) const { + assert(node->count() > 0); + assert(node->count() <= node->max_count()); + if (lo) { + assert(!compare_keys(node->key(node->start()), *lo)); + } + if (hi) { + assert(!compare_keys(*hi, node->key(node->finish() - 1))); + } + for (int i = node->start() + 1; i < node->finish(); ++i) { + assert(!compare_keys(node->key(i), node->key(i - 1))); + } + int count = node->count(); + if (!node->leaf()) { + for (int i = node->start(); i <= node->finish(); ++i) { + assert(node->child(i) != nullptr); + assert(node->child(i)->parent() == node); + assert(node->child(i)->position() == i); + count += internal_verify(node->child(i), + i == node->start() ? lo : &node->key(i - 1), + i == node->finish() ? hi : &node->key(i)); + } + } + return count; +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_BTREE_H_ diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h new file mode 100644 index 00000000..f2e4c3a5 --- /dev/null +++ b/absl/container/internal/btree_container.h @@ -0,0 +1,672 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ +#define ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ + +#include <algorithm> +#include <initializer_list> +#include <iterator> +#include <utility> + +#include "absl/base/internal/throw_delegate.h" +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/common.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A common base class for btree_set, btree_map, btree_multiset, and +// btree_multimap. +template <typename Tree> +class btree_container { + using params_type = typename Tree::params_type; + + protected: + // Alias used for heterogeneous lookup functions. + // `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> + using key_arg = + typename KeyArg<IsTransparent<typename Tree::key_compare>::value>:: + template type<K, typename Tree::key_type>; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using difference_type = typename Tree::difference_type; + using key_compare = typename Tree::key_compare; + using value_compare = typename Tree::value_compare; + using allocator_type = typename Tree::allocator_type; + using reference = typename Tree::reference; + using const_reference = typename Tree::const_reference; + using pointer = typename Tree::pointer; + using const_pointer = typename Tree::const_pointer; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using reverse_iterator = typename Tree::reverse_iterator; + using const_reverse_iterator = typename Tree::const_reverse_iterator; + using node_type = typename Tree::node_handle_type; + + // Constructors/assignments. + btree_container() : tree_(key_compare(), allocator_type()) {} + explicit btree_container(const key_compare &comp, + const allocator_type &alloc = allocator_type()) + : tree_(comp, alloc) {} + btree_container(const btree_container &x) = default; + btree_container(btree_container &&x) noexcept = default; + btree_container &operator=(const btree_container &x) = default; + btree_container &operator=(btree_container &&x) noexcept( + std::is_nothrow_move_assignable<Tree>::value) = default; + + // Iterator routines. + iterator begin() { return tree_.begin(); } + const_iterator begin() const { return tree_.begin(); } + const_iterator cbegin() const { return tree_.begin(); } + iterator end() { return tree_.end(); } + const_iterator end() const { return tree_.end(); } + const_iterator cend() const { return tree_.end(); } + reverse_iterator rbegin() { return tree_.rbegin(); } + const_reverse_iterator rbegin() const { return tree_.rbegin(); } + const_reverse_iterator crbegin() const { return tree_.rbegin(); } + reverse_iterator rend() { return tree_.rend(); } + const_reverse_iterator rend() const { return tree_.rend(); } + const_reverse_iterator crend() const { return tree_.rend(); } + + // Lookup routines. + template <typename K = key_type> + iterator find(const key_arg<K> &key) { + return tree_.find(key); + } + template <typename K = key_type> + const_iterator find(const key_arg<K> &key) const { + return tree_.find(key); + } + template <typename K = key_type> + bool contains(const key_arg<K> &key) const { + return find(key) != end(); + } + template <typename K = key_type> + iterator lower_bound(const key_arg<K> &key) { + return tree_.lower_bound(key); + } + template <typename K = key_type> + const_iterator lower_bound(const key_arg<K> &key) const { + return tree_.lower_bound(key); + } + template <typename K = key_type> + iterator upper_bound(const key_arg<K> &key) { + return tree_.upper_bound(key); + } + template <typename K = key_type> + const_iterator upper_bound(const key_arg<K> &key) const { + return tree_.upper_bound(key); + } + template <typename K = key_type> + std::pair<iterator, iterator> equal_range(const key_arg<K> &key) { + return tree_.equal_range(key); + } + template <typename K = key_type> + std::pair<const_iterator, const_iterator> equal_range( + const key_arg<K> &key) const { + return tree_.equal_range(key); + } + + // Deletion routines. Note that there is also a deletion routine that is + // specific to btree_set_container/btree_multiset_container. + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); } + iterator erase(iterator iter) { return tree_.erase(iter); } + iterator erase(const_iterator first, const_iterator last) { + return tree_.erase_range(iterator(first), iterator(last)).second; + } + + // Extract routines. + node_type extract(iterator position) { + // Use Move instead of Transfer, because the rebalancing code expects to + // have a valid object to scribble metadata bits on top of. + auto node = CommonAccess::Move<node_type>(get_allocator(), position.slot()); + erase(position); + return node; + } + node_type extract(const_iterator position) { + return extract(iterator(position)); + } + + public: + // Utility routines. + void clear() { tree_.clear(); } + void swap(btree_container &x) { tree_.swap(x.tree_); } + void verify() const { tree_.verify(); } + + // Size routines. + size_type size() const { return tree_.size(); } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { return tree_.empty(); } + + friend bool operator==(const btree_container &x, const btree_container &y) { + if (x.size() != y.size()) return false; + return std::equal(x.begin(), x.end(), y.begin()); + } + + friend bool operator!=(const btree_container &x, const btree_container &y) { + return !(x == y); + } + + friend bool operator<(const btree_container &x, const btree_container &y) { + return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); + } + + friend bool operator>(const btree_container &x, const btree_container &y) { + return y < x; + } + + friend bool operator<=(const btree_container &x, const btree_container &y) { + return !(y < x); + } + + friend bool operator>=(const btree_container &x, const btree_container &y) { + return !(x < y); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { return tree_.get_allocator(); } + + // The key comparator used by the btree. + key_compare key_comp() const { return tree_.key_comp(); } + value_compare value_comp() const { return tree_.value_comp(); } + + // Support absl::Hash. + template <typename State> + friend State AbslHashValue(State h, const btree_container &b) { + for (const auto &v : b) { + h = State::combine(std::move(h), v); + } + return State::combine(std::move(h), b.size()); + } + + protected: + Tree tree_; +}; + +// A common base class for btree_set and btree_map. +template <typename Tree> +class btree_set_container : public btree_container<Tree> { + using super_type = btree_container<Tree>; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + friend class BtreeNodePeer; + + protected: + template <class K> + using key_arg = typename super_type::template key_arg<K>; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + using insert_return_type = InsertReturnType<iterator, node_type>; + + // Inherit constructors. + using super_type::super_type; + btree_set_container() {} + + // Range constructor. + template <class InputIterator> + btree_set_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + + // Initializer list constructor. + btree_set_container(std::initializer_list<init_type> init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_set_container(init.begin(), init.end(), comp, alloc) {} + + // Lookup routines. + template <typename K = key_type> + size_type count(const key_arg<K> &key) const { + return this->tree_.count_unique(key); + } + + // Insertion routines. + std::pair<iterator, bool> insert(const value_type &x) { + return this->tree_.insert_unique(params_type::key(x), x); + } + std::pair<iterator, bool> insert(value_type &&x) { + return this->tree_.insert_unique(params_type::key(x), std::move(x)); + } + template <typename... Args> + std::pair<iterator, bool> emplace(Args &&... args) { + init_type v(std::forward<Args>(args)...); + return this->tree_.insert_unique(params_type::key(v), std::move(v)); + } + iterator insert(const_iterator position, const value_type &x) { + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(x), x) + .first; + } + iterator insert(const_iterator position, value_type &&x) { + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(x), + std::move(x)) + .first; + } + template <typename... Args> + iterator emplace_hint(const_iterator position, Args &&... args) { + init_type v(std::forward<Args>(args)...); + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(v), + std::move(v)) + .first; + } + template <typename InputIterator> + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_unique(b, e); + } + void insert(std::initializer_list<init_type> init) { + this->tree_.insert_iterator_unique(init.begin(), init.end()); + } + insert_return_type insert(node_type &&node) { + if (!node) return {this->end(), false, node_type()}; + std::pair<iterator, bool> res = + this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) { + CommonAccess::Destroy(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + iterator insert(const_iterator hint, node_type &&node) { + if (!node) return this->end(); + std::pair<iterator, bool> res = this->tree_.insert_hint_unique( + iterator(hint), params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) CommonAccess::Destroy(&node); + return res.first; + } + + // Deletion routines. + template <typename K = key_type> + size_type erase(const key_arg<K> &key) { + return this->tree_.erase_unique(key); + } + using super_type::erase; + + // Node extraction routines. + template <typename K = key_type> + node_type extract(const key_arg<K> &key) { + auto it = this->find(key); + return it == this->end() ? node_type() : extract(it); + } + using super_type::extract; + + // Merge routines. + // Moves elements from `src` into `this`. If the element already exists in + // `this`, it is left unmodified in `src`. + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same<value_type, typename T::value_type>, + std::is_same<allocator_type, typename T::allocator_type>, + std::is_same<typename params_type::is_map_container, + typename T::params_type::is_map_container>>::value, + int> = 0> + void merge(btree_container<T> &src) { // NOLINT + for (auto src_it = src.begin(); src_it != src.end();) { + if (insert(std::move(*src_it)).second) { + src_it = src.erase(src_it); + } else { + ++src_it; + } + } + } + + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same<value_type, typename T::value_type>, + std::is_same<allocator_type, typename T::allocator_type>, + std::is_same<typename params_type::is_map_container, + typename T::params_type::is_map_container>>::value, + int> = 0> + void merge(btree_container<T> &&src) { + merge(src); + } +}; + +// Base class for btree_map. +template <typename Tree> +class btree_map_container : public btree_set_container<Tree> { + using super_type = btree_set_container<Tree>; + using params_type = typename Tree::params_type; + + private: + template <class K> + using key_arg = typename super_type::template key_arg<K>; + + public: + using key_type = typename Tree::key_type; + using mapped_type = typename params_type::mapped_type; + using value_type = typename Tree::value_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + + // Inherit constructors. + using super_type::super_type; + btree_map_container() {} + + // Insertion routines. + // Note: the nullptr template arguments and extra `const M&` overloads allow + // for supporting bitfield arguments. + // Note: when we call `std::forward<M>(obj)` twice, it's safe because + // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when + // `ret.second` is false. + template <class M> + std::pair<iterator, bool> insert_or_assign(const key_type &k, const M &obj) { + const std::pair<iterator, bool> ret = this->tree_.insert_unique(k, k, obj); + if (!ret.second) ret.first->second = obj; + return ret; + } + template <class M, key_type * = nullptr> + std::pair<iterator, bool> insert_or_assign(key_type &&k, const M &obj) { + const std::pair<iterator, bool> ret = + this->tree_.insert_unique(k, std::move(k), obj); + if (!ret.second) ret.first->second = obj; + return ret; + } + template <class M, M * = nullptr> + std::pair<iterator, bool> insert_or_assign(const key_type &k, M &&obj) { + const std::pair<iterator, bool> ret = + this->tree_.insert_unique(k, k, std::forward<M>(obj)); + if (!ret.second) ret.first->second = std::forward<M>(obj); + return ret; + } + template <class M, key_type * = nullptr, M * = nullptr> + std::pair<iterator, bool> insert_or_assign(key_type &&k, M &&obj) { + const std::pair<iterator, bool> ret = + this->tree_.insert_unique(k, std::move(k), std::forward<M>(obj)); + if (!ret.second) ret.first->second = std::forward<M>(obj); + return ret; + } + template <class M> + iterator insert_or_assign(const_iterator position, const key_type &k, + const M &obj) { + const std::pair<iterator, bool> ret = + this->tree_.insert_hint_unique(iterator(position), k, k, obj); + if (!ret.second) ret.first->second = obj; + return ret.first; + } + template <class M, key_type * = nullptr> + iterator insert_or_assign(const_iterator position, key_type &&k, + const M &obj) { + const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique( + iterator(position), k, std::move(k), obj); + if (!ret.second) ret.first->second = obj; + return ret.first; + } + template <class M, M * = nullptr> + iterator insert_or_assign(const_iterator position, const key_type &k, + M &&obj) { + const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique( + iterator(position), k, k, std::forward<M>(obj)); + if (!ret.second) ret.first->second = std::forward<M>(obj); + return ret.first; + } + template <class M, key_type * = nullptr, M * = nullptr> + iterator insert_or_assign(const_iterator position, key_type &&k, M &&obj) { + const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique( + iterator(position), k, std::move(k), std::forward<M>(obj)); + if (!ret.second) ret.first->second = std::forward<M>(obj); + return ret.first; + } + template <typename... Args> + std::pair<iterator, bool> try_emplace(const key_type &k, Args &&... args) { + return this->tree_.insert_unique( + k, std::piecewise_construct, std::forward_as_tuple(k), + std::forward_as_tuple(std::forward<Args>(args)...)); + } + template <typename... Args> + std::pair<iterator, bool> try_emplace(key_type &&k, Args &&... args) { + // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` + // and then using `k` unsequenced. This is safe because the move is into a + // forwarding reference and insert_unique guarantees that `key` is never + // referenced after consuming `args`. + const key_type &key_ref = k; + return this->tree_.insert_unique( + key_ref, std::piecewise_construct, std::forward_as_tuple(std::move(k)), + std::forward_as_tuple(std::forward<Args>(args)...)); + } + template <typename... Args> + iterator try_emplace(const_iterator hint, const key_type &k, + Args &&... args) { + return this->tree_ + .insert_hint_unique(iterator(hint), k, std::piecewise_construct, + std::forward_as_tuple(k), + std::forward_as_tuple(std::forward<Args>(args)...)) + .first; + } + template <typename... Args> + iterator try_emplace(const_iterator hint, key_type &&k, Args &&... args) { + // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` + // and then using `k` unsequenced. This is safe because the move is into a + // forwarding reference and insert_hint_unique guarantees that `key` is + // never referenced after consuming `args`. + const key_type &key_ref = k; + return this->tree_ + .insert_hint_unique(iterator(hint), key_ref, std::piecewise_construct, + std::forward_as_tuple(std::move(k)), + std::forward_as_tuple(std::forward<Args>(args)...)) + .first; + } + mapped_type &operator[](const key_type &k) { + return try_emplace(k).first->second; + } + mapped_type &operator[](key_type &&k) { + return try_emplace(std::move(k)).first->second; + } + + template <typename K = key_type> + mapped_type &at(const key_arg<K> &key) { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + return it->second; + } + template <typename K = key_type> + const mapped_type &at(const key_arg<K> &key) const { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + return it->second; + } +}; + +// A common base class for btree_multiset and btree_multimap. +template <typename Tree> +class btree_multiset_container : public btree_container<Tree> { + using super_type = btree_container<Tree>; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + + template <class K> + using key_arg = typename super_type::template key_arg<K>; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + + // Inherit constructors. + using super_type::super_type; + btree_multiset_container() {} + + // Range constructor. + template <class InputIterator> + btree_multiset_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + + // Initializer list constructor. + btree_multiset_container(std::initializer_list<init_type> init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_multiset_container(init.begin(), init.end(), comp, alloc) {} + + // Lookup routines. + template <typename K = key_type> + size_type count(const key_arg<K> &key) const { + return this->tree_.count_multi(key); + } + + // Insertion routines. + iterator insert(const value_type &x) { return this->tree_.insert_multi(x); } + iterator insert(value_type &&x) { + return this->tree_.insert_multi(std::move(x)); + } + iterator insert(const_iterator position, const value_type &x) { + return this->tree_.insert_hint_multi(iterator(position), x); + } + iterator insert(const_iterator position, value_type &&x) { + return this->tree_.insert_hint_multi(iterator(position), std::move(x)); + } + template <typename InputIterator> + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_multi(b, e); + } + void insert(std::initializer_list<init_type> init) { + this->tree_.insert_iterator_multi(init.begin(), init.end()); + } + template <typename... Args> + iterator emplace(Args &&... args) { + return this->tree_.insert_multi(init_type(std::forward<Args>(args)...)); + } + template <typename... Args> + iterator emplace_hint(const_iterator position, Args &&... args) { + return this->tree_.insert_hint_multi( + iterator(position), init_type(std::forward<Args>(args)...)); + } + iterator insert(node_type &&node) { + if (!node) return this->end(); + iterator res = + this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + CommonAccess::Destroy(&node); + return res; + } + iterator insert(const_iterator hint, node_type &&node) { + if (!node) return this->end(); + iterator res = this->tree_.insert_hint_multi( + iterator(hint), + std::move(params_type::element(CommonAccess::GetSlot(node)))); + CommonAccess::Destroy(&node); + return res; + } + + // Deletion routines. + template <typename K = key_type> + size_type erase(const key_arg<K> &key) { + return this->tree_.erase_multi(key); + } + using super_type::erase; + + // Node extraction routines. + template <typename K = key_type> + node_type extract(const key_arg<K> &key) { + auto it = this->find(key); + return it == this->end() ? node_type() : extract(it); + } + using super_type::extract; + + // Merge routines. + // Moves all elements from `src` into `this`. + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same<value_type, typename T::value_type>, + std::is_same<allocator_type, typename T::allocator_type>, + std::is_same<typename params_type::is_map_container, + typename T::params_type::is_map_container>>::value, + int> = 0> + void merge(btree_container<T> &src) { // NOLINT + insert(std::make_move_iterator(src.begin()), + std::make_move_iterator(src.end())); + src.clear(); + } + + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same<value_type, typename T::value_type>, + std::is_same<allocator_type, typename T::allocator_type>, + std::is_same<typename params_type::is_map_container, + typename T::params_type::is_map_container>>::value, + int> = 0> + void merge(btree_container<T> &&src) { + merge(src); + } +}; + +// A base class for btree_multimap. +template <typename Tree> +class btree_multimap_container : public btree_multiset_container<Tree> { + using super_type = btree_multiset_container<Tree>; + using params_type = typename Tree::params_type; + + public: + using mapped_type = typename params_type::mapped_type; + + // Inherit constructors. + using super_type::super_type; + btree_multimap_container() {} +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index a02cd5c3..5037d803 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h @@ -22,7 +22,7 @@ #include "absl/types/optional.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class, class = void> @@ -56,7 +56,7 @@ class node_handle_base { public: using allocator_type = Alloc; - constexpr node_handle_base() {} + constexpr node_handle_base() = default; node_handle_base(node_handle_base&& other) noexcept { *this = std::move(other); } @@ -109,16 +109,15 @@ class node_handle_base { 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_; + absl::optional<allocator_type> alloc_ = {}; + alignas(slot_type) mutable unsigned char slot_space_[sizeof(slot_type)] = {}; }; // 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; + using Base = node_handle_base<PolicyTraits, Alloc>; public: using value_type = typename PolicyTraits::value_type; @@ -138,7 +137,7 @@ 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; + using Base = node_handle_base<PolicyTraits, Alloc>; public: using key_type = typename Policy::key_type; @@ -168,6 +167,11 @@ struct CommonAccess { } template <typename Node> + static void Destroy(Node* node) { + node->destroy(); + } + + template <typename Node> static void Reset(Node* node) { node->reset(); } @@ -192,7 +196,7 @@ struct InsertReturnType { }; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 fbace496..4bfe92fd 100644 --- a/absl/container/internal/compressed_tuple.h +++ b/absl/container/internal/compressed_tuple.h @@ -48,7 +48,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <typename... Ts> @@ -257,7 +257,7 @@ template <> class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC diff --git a/absl/container/internal/compressed_tuple_test.cc b/absl/container/internal/compressed_tuple_test.cc index ec893b90..1dae12db 100644 --- a/absl/container/internal/compressed_tuple_test.cc +++ b/absl/container/internal/compressed_tuple_test.cc @@ -48,7 +48,7 @@ struct TwoValues { namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -333,10 +333,6 @@ TEST(CompressedTupleTest, AnyElements) { a = 0.5f; EXPECT_EQ(absl::any_cast<float>(x.get<1>()), 0.5); - - // Ensure copy construction work in the face of a type with a universal - // implicit constructor; - CompressedTuple<absl::any> c{}, d(c); // NOLINT } TEST(CompressedTupleTest, Constexpr) { @@ -409,5 +405,5 @@ TEST(CompressedTupleTest, EmptyFinalClass) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index eb6d7eb7..d24b0f84 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h @@ -34,7 +34,7 @@ #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // Allocates at least n bytes aligned to the specified alignment. @@ -434,7 +434,7 @@ struct map_slot_policy { }; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ diff --git a/absl/container/internal/container_memory_test.cc b/absl/container/internal/container_memory_test.cc index ea9568dc..7942c7be 100644 --- a/absl/container/internal/container_memory_test.cc +++ b/absl/container/internal/container_memory_test.cc @@ -23,7 +23,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -186,5 +186,5 @@ TEST(DecomposePair, NotDecomposable) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/counting_allocator.h b/absl/container/internal/counting_allocator.h index 94a457ca..9efdc662 100644 --- a/absl/container/internal/counting_allocator.h +++ b/absl/container/internal/counting_allocator.h @@ -19,8 +19,10 @@ #include <cstdint> #include <memory> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // This is a stateful allocator, but the state lives outside of the @@ -75,7 +77,7 @@ class CountingAllocator : public std::allocator<T> { }; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 2155076d..401ddf4d 100644 --- a/absl/container/internal/hash_function_defaults.h +++ b/absl/container/internal/hash_function_defaults.h @@ -56,7 +56,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // The hash of an object of type T is computed by using absl::Hash. @@ -140,7 +140,7 @@ template <class T> using hash_default_eq = typename container_internal::HashEq<T>::Eq; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ diff --git a/absl/container/internal/hash_function_defaults_test.cc b/absl/container/internal/hash_function_defaults_test.cc index ce6133f8..2eefc7e0 100644 --- a/absl/container/internal/hash_function_defaults_test.cc +++ b/absl/container/internal/hash_function_defaults_test.cc @@ -22,7 +22,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -249,7 +249,7 @@ TYPED_TEST_SUITE(StringLikeTest, StringTypesCartesianProduct); } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl enum Hash : size_t { @@ -280,7 +280,7 @@ struct hash<Hashable<H>> { } // namespace std namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -295,5 +295,5 @@ TEST(Delegate, HashDispatch) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc index 36b2571b..75c4db6c 100644 --- a/absl/container/internal/hash_generator_testing.cc +++ b/absl/container/internal/hash_generator_testing.cc @@ -17,7 +17,7 @@ #include <deque> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace hash_internal { namespace { @@ -70,5 +70,5 @@ absl::string_view Generator<absl::string_view>::operator()() const { } // namespace hash_internal } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h index 27962c35..6869fe45 100644 --- a/absl/container/internal/hash_generator_testing.h +++ b/absl/container/internal/hash_generator_testing.h @@ -19,6 +19,7 @@ #define ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ #include <stdint.h> + #include <algorithm> #include <iosfwd> #include <random> @@ -27,11 +28,12 @@ #include <utility> #include "absl/container/internal/hash_policy_testing.h" +#include "absl/memory/memory.h" #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace hash_internal { namespace generator_internal { @@ -130,6 +132,13 @@ struct Generator<std::tuple<Ts...>> { } }; +template <class T> +struct Generator<std::unique_ptr<T>> { + std::unique_ptr<T> operator()() const { + return absl::make_unique<T>(Generator<T>()()); + } +}; + template <class U> struct Generator<U, absl::void_t<decltype(std::declval<U&>().key()), decltype(std::declval<U&>().value())>> @@ -146,7 +155,7 @@ using GeneratedType = decltype( } // namespace hash_internal } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ diff --git a/absl/container/internal/hash_policy_testing.h b/absl/container/internal/hash_policy_testing.h index 8f0d2a52..01c40d2e 100644 --- a/absl/container/internal/hash_policy_testing.h +++ b/absl/container/internal/hash_policy_testing.h @@ -30,7 +30,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace hash_testing_internal { @@ -163,7 +163,7 @@ auto keys(const Set& s) } } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions diff --git a/absl/container/internal/hash_policy_testing_test.cc b/absl/container/internal/hash_policy_testing_test.cc index 8fd1df00..f0b20fe3 100644 --- a/absl/container/internal/hash_policy_testing_test.cc +++ b/absl/container/internal/hash_policy_testing_test.cc @@ -17,7 +17,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -41,5 +41,5 @@ TEST(_, Hash) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/hash_policy_traits.h b/absl/container/internal/hash_policy_traits.h index 3d87e821..3e1209c6 100644 --- a/absl/container/internal/hash_policy_traits.h +++ b/absl/container/internal/hash_policy_traits.h @@ -23,7 +23,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // Defines how slots are initialized/destroyed/moved. @@ -185,7 +185,7 @@ struct hash_policy_traits { }; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ diff --git a/absl/container/internal/hash_policy_traits_test.cc b/absl/container/internal/hash_policy_traits_test.cc index edfaf63e..6ef8b9e0 100644 --- a/absl/container/internal/hash_policy_traits_test.cc +++ b/absl/container/internal/hash_policy_traits_test.cc @@ -22,7 +22,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -140,5 +140,5 @@ TEST_F(Test, with_transfer) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h index 1d1a9c28..19d52121 100644 --- a/absl/container/internal/hashtable_debug.h +++ b/absl/container/internal/hashtable_debug.h @@ -38,7 +38,7 @@ #include "absl/container/internal/hashtable_debug_hooks.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // Returns the number of probes required to lookup `key`. Returns 0 for a @@ -104,7 +104,7 @@ size_t LowerBoundAllocatedByteSize(size_t num_elements) { } } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ diff --git a/absl/container/internal/hashtable_debug_hooks.h b/absl/container/internal/hashtable_debug_hooks.h index 7b95fcef..3e9ea595 100644 --- a/absl/container/internal/hashtable_debug_hooks.h +++ b/absl/container/internal/hashtable_debug_hooks.h @@ -23,8 +23,10 @@ #include <type_traits> #include <vector> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace hashtable_debug_internal { @@ -77,7 +79,7 @@ struct HashtableDebugAccess { } // namespace hashtable_debug_internal } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 2338045d..56447251 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -21,13 +21,14 @@ #include <limits> #include "absl/base/attributes.h" +#include "absl/base/internal/exponential_biased.h" #include "absl/container/internal/have_sse.h" #include "absl/debugging/stacktrace.h" #include "absl/memory/memory.h" #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { constexpr int HashtablezInfo::kMaxStackDepth; @@ -38,80 +39,17 @@ ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{ 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); -} +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased + g_exponential_biased_generator; +#endif } // namespace +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + HashtablezSampler& HashtablezSampler::Global() { static auto* sampler = new HashtablezSampler(); return *sampler; @@ -229,15 +167,39 @@ int64_t HashtablezSampler::Iterate( return dropped_samples_.load(std::memory_order_relaxed); } +static bool ShouldForceSampling() { + enum ForceState { + kDontForce, + kForce, + kUninitialized + }; + ABSL_CONST_INIT static std::atomic<ForceState> global_state{ + kUninitialized}; + ForceState state = global_state.load(std::memory_order_relaxed); + if (ABSL_PREDICT_TRUE(state == kDontForce)) return false; + + if (state == kUninitialized) { + state = AbslContainerInternalSampleEverything() ? kForce : kDontForce; + global_state.store(state, std::memory_order_relaxed); + } + return state == kForce; +} + HashtablezInfo* SampleSlow(int64_t* next_sample) { - if (kAbslContainerInternalSampleEverything) { + if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { *next_sample = 1; return HashtablezSampler::Global().Register(); } +#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + *next_sample = std::numeric_limits<int64_t>::max(); + return nullptr; +#else bool first = *next_sample < 0; - *next_sample = GetGeometricVariable( + *next_sample = g_exponential_biased_generator.GetStride( g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); + // Small values of interval are equivalent to just sampling next time. + ABSL_ASSERT(*next_sample >= 1); // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold // low enough that we will start sampling in a reasonable time, so we just use @@ -252,12 +214,9 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) { } return HashtablezSampler::Global().Register(); +#endif } -#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); } @@ -306,5 +265,5 @@ void SetHashtablezMaxSamples(int32_t max) { } } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index f17c425c..34d5e572 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -51,7 +51,7 @@ #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // Stores information about a sampled hashtable. All mutations to this *must* @@ -66,7 +66,7 @@ struct HashtablezInfo { // 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); + void PrepareForSampling() ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); // These fields are mutated by the various Record* APIs and need to be // thread-safe. @@ -84,7 +84,7 @@ struct HashtablezInfo { // prevents races with sampling and resurrecting an object. absl::Mutex init_mu; HashtablezInfo* next; - HashtablezInfo* dead GUARDED_BY(init_mu); + HashtablezInfo* dead ABSL_GUARDED_BY(init_mu); // All of the fields below are set by `PrepareForSampling`, they must not be // mutated in `Record*` functions. They are logically `const` in that sense. @@ -180,23 +180,30 @@ class HashtablezInfoHandle { HashtablezInfo* info_; }; -#if ABSL_PER_THREAD_TLS == 1 +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +#if (ABSL_PER_THREAD_TLS == 1) && !defined(ABSL_BUILD_DLL) && \ + !defined(ABSL_CONSUME_DLL) +#define ABSL_INTERNAL_HASHTABLEZ_SAMPLE +#endif + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) 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 defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { return HashtablezInfoHandle(nullptr); } return HashtablezInfoHandle(SampleSlow(&global_next_sample)); +#else + return HashtablezInfoHandle(nullptr); +#endif // !ABSL_PER_THREAD_TLS } // Holds samples and their associated stack traces with a soft limit of @@ -281,10 +288,10 @@ void SetHashtablezMaxSamples(int32_t max); // initialization of static storage duration objects. // The definition of this constant is weak, which allows us to inject a // different value for it at link time. -extern "C" const bool kAbslContainerInternalSampleEverything; +extern "C" bool AbslContainerInternalSampleEverything(); } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 index d3f41c7c..78b9d362 100644 --- a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc +++ b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc @@ -17,13 +17,14 @@ #include "absl/base/attributes.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // See hashtablez_sampler.h for details. -extern "C" ABSL_ATTRIBUTE_WEAK const bool - kAbslContainerInternalSampleEverything = false; +extern "C" ABSL_ATTRIBUTE_WEAK bool AbslContainerInternalSampleEverything() { + return false; +} } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index bdae75f3..36f5ccdd 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -36,7 +36,7 @@ constexpr int kProbeLength = 8; #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { class HashtablezInfoHandlePeer { public: @@ -169,6 +169,7 @@ TEST(HashtablezInfoTest, RecordRehash) { EXPECT_EQ(info.num_erases.load(), 0); } +#if defined(ABSL_HASHTABLEZ_SAMPLE) TEST(HashtablezSamplerTest, SmallSampleParameter) { SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); @@ -212,6 +213,7 @@ TEST(HashtablezSamplerTest, Sample) { } EXPECT_NEAR(sample_rate, 0.01, 0.005); } +#endif TEST(HashtablezSamplerTest, Handle) { auto& sampler = HashtablezSampler::Global(); @@ -353,5 +355,5 @@ TEST(HashtablezSamplerTest, Callback) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 123e04c9..4d80b727 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -30,7 +30,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace inlined_vector_internal { template <typename Iterator> @@ -38,16 +38,17 @@ using IsAtLeastForwardIterator = std::is_convertible< typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>; -template <typename AllocatorType> -using IsMemcpyOk = absl::conjunction< - std::is_same<std::allocator<typename AllocatorType::value_type>, - AllocatorType>, - absl::is_trivially_copy_constructible<typename AllocatorType::value_type>, - absl::is_trivially_copy_assignable<typename AllocatorType::value_type>, - absl::is_trivially_destructible<typename AllocatorType::value_type>>; - -template <typename AllocatorType, typename ValueType, typename SizeType> -void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first, +template <typename AllocatorType, + typename ValueType = + typename absl::allocator_traits<AllocatorType>::value_type> +using IsMemcpyOk = + absl::conjunction<std::is_same<AllocatorType, std::allocator<ValueType>>, + absl::is_trivially_copy_constructible<ValueType>, + absl::is_trivially_copy_assignable<ValueType>, + absl::is_trivially_destructible<ValueType>>; + +template <typename AllocatorType, typename Pointer, typename SizeType> +void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first, SizeType destroy_size) { using AllocatorTraits = absl::allocator_traits<AllocatorType>; @@ -57,21 +58,26 @@ void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first, AllocatorTraits::destroy(*alloc_ptr, destroy_first + i); } -#ifndef 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. - auto* memory_ptr = static_cast<void*>(destroy_first); - auto memory_size = sizeof(ValueType) * destroy_size; - std::memset(memory_ptr, 0xab, memory_size); -#endif // NDEBUG +#if !defined(NDEBUG) + { + using ValueType = typename AllocatorTraits::value_type; + + // Overwrite unused memory with `0xab` so we can catch uninitialized + // usage. + // + // Cast to `void*` to tell the compiler that we don't care that we might + // be scribbling on a vtable pointer. + void* memory_ptr = destroy_first; + auto memory_size = destroy_size * sizeof(ValueType); + std::memset(memory_ptr, 0xab, memory_size); + } +#endif // !defined(NDEBUG) } } -template <typename AllocatorType, typename ValueType, typename ValueAdapter, +template <typename AllocatorType, typename Pointer, typename ValueAdapter, typename SizeType> -void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first, +void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first, ValueAdapter* values_ptr, SizeType construct_size) { for (SizeType i = 0; i < construct_size; ++i) { ABSL_INTERNAL_TRY { @@ -84,8 +90,8 @@ void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first, } } -template <typename ValueType, typename ValueAdapter, typename SizeType> -void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr, +template <typename Pointer, typename ValueAdapter, typename SizeType> +void AssignElements(Pointer assign_first, ValueAdapter* values_ptr, SizeType assign_size) { for (SizeType i = 0; i < assign_size; ++i) { values_ptr->AssignNext(assign_first + i); @@ -94,28 +100,29 @@ void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr, template <typename AllocatorType> struct StorageView { - using pointer = typename AllocatorType::pointer; - using size_type = typename AllocatorType::size_type; + using AllocatorTraits = absl::allocator_traits<AllocatorType>; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; - pointer data; - size_type size; - size_type capacity; + Pointer data; + SizeType size; + SizeType capacity; }; template <typename AllocatorType, typename Iterator> class IteratorValueAdapter { - using pointer = typename AllocatorType::pointer; using AllocatorTraits = absl::allocator_traits<AllocatorType>; + using Pointer = typename AllocatorTraits::pointer; public: explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} - void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); ++it_; } - void AssignNext(pointer assign_at) { + void AssignNext(Pointer assign_at) { *assign_at = *it_; ++it_; } @@ -126,46 +133,45 @@ class IteratorValueAdapter { template <typename AllocatorType> class CopyValueAdapter { - using pointer = typename AllocatorType::pointer; - using const_pointer = typename AllocatorType::const_pointer; - using const_reference = typename AllocatorType::const_reference; using AllocatorTraits = absl::allocator_traits<AllocatorType>; + using ValueType = typename AllocatorTraits::value_type; + using Pointer = typename AllocatorTraits::pointer; + using ConstPointer = typename AllocatorTraits::const_pointer; public: - explicit CopyValueAdapter(const_reference v) : ptr_(std::addressof(v)) {} + explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {} - void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); } - void AssignNext(pointer assign_at) { *assign_at = *ptr_; } + void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } private: - const_pointer ptr_; + ConstPointer ptr_; }; template <typename AllocatorType> class DefaultValueAdapter { - using pointer = typename AllocatorType::pointer; - using value_type = typename AllocatorType::value_type; using AllocatorTraits = absl::allocator_traits<AllocatorType>; + using ValueType = typename AllocatorTraits::value_type; + using Pointer = typename AllocatorTraits::pointer; public: explicit DefaultValueAdapter() {} - void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { AllocatorTraits::construct(*alloc_ptr, construct_at); } - void AssignNext(pointer assign_at) { *assign_at = value_type(); } + void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } }; template <typename AllocatorType> class AllocationTransaction { - using value_type = typename AllocatorType::value_type; - using pointer = typename AllocatorType::pointer; - using size_type = typename AllocatorType::size_type; using AllocatorTraits = absl::allocator_traits<AllocatorType>; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; public: explicit AllocationTransaction(AllocatorType* alloc_ptr) @@ -181,25 +187,31 @@ class AllocationTransaction { void operator=(const AllocationTransaction&) = delete; AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } - pointer& GetData() { return alloc_data_.template get<1>(); } - size_type& GetCapacity() { return capacity_; } + Pointer& GetData() { return alloc_data_.template get<1>(); } + SizeType& GetCapacity() { return capacity_; } bool DidAllocate() { return GetData() != nullptr; } - pointer Allocate(size_type capacity) { + Pointer Allocate(SizeType capacity) { GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); GetCapacity() = capacity; return GetData(); } + void Reset() { + GetData() = nullptr; + GetCapacity() = 0; + } + private: - container_internal::CompressedTuple<AllocatorType, pointer> alloc_data_; - size_type capacity_ = 0; + container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_; + SizeType capacity_ = 0; }; template <typename AllocatorType> class ConstructionTransaction { - using pointer = typename AllocatorType::pointer; - using size_type = typename AllocatorType::size_type; + using AllocatorTraits = absl::allocator_traits<AllocatorType>; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; public: explicit ConstructionTransaction(AllocatorType* alloc_ptr) @@ -216,12 +228,12 @@ class ConstructionTransaction { void operator=(const ConstructionTransaction&) = delete; AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } - pointer& GetData() { return alloc_data_.template get<1>(); } - size_type& GetSize() { return size_; } + Pointer& GetData() { return alloc_data_.template get<1>(); } + SizeType& GetSize() { return size_; } bool DidConstruct() { return GetData() != nullptr; } template <typename ValueAdapter> - void Construct(pointer data, ValueAdapter* values_ptr, size_type size) { + void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) { inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), data, values_ptr, size); GetData() = data; @@ -233,28 +245,29 @@ class ConstructionTransaction { } private: - container_internal::CompressedTuple<AllocatorType, pointer> alloc_data_; - size_type size_ = 0; + container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_; + SizeType size_ = 0; }; template <typename T, size_t N, typename A> class Storage { 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 AllocatorTraits = absl::allocator_traits<A>; + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + + using reference = value_type&; + using const_reference = const value_type&; + using RValueReference = value_type&&; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; using MoveIterator = std::move_iterator<iterator>; - using AllocatorTraits = absl::allocator_traits<allocator_type>; using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<allocator_type>; using StorageView = inlined_vector_internal::StorageView<allocator_type>; @@ -287,8 +300,7 @@ class Storage { Storage() : metadata_() {} - explicit Storage(const allocator_type& alloc) - : metadata_(alloc, /* empty and inlined */ 0) {} + explicit Storage(const allocator_type& alloc) : metadata_(alloc, {}) {} ~Storage() { pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); @@ -414,8 +426,8 @@ class Storage { void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) { SetAllocatedData(allocation_tx_ptr->GetData(), allocation_tx_ptr->GetCapacity()); - allocation_tx_ptr->GetData() = nullptr; - allocation_tx_ptr->GetCapacity() = 0; + + allocation_tx_ptr->Reset(); } void MemcpyFrom(const Storage& other_storage) { @@ -442,9 +454,7 @@ class Storage { }; struct Inlined { - using InlinedDataElement = - absl::aligned_storage_t<sizeof(value_type), alignof(value_type)>; - InlinedDataElement inlined_data[N]; + alignas(value_type) char inlined_data[sizeof(value_type[N])]; }; union Data { @@ -465,18 +475,14 @@ auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size) assert(GetSize() == 0); pointer construct_data; - if (new_size > GetInlinedCapacity()) { // Because this is only called from the `InlinedVector` constructors, it's // safe to take on the allocation with size `0`. If `ConstructElements(...)` // throws, deallocation will be automatically handled by `~Storage()`. size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); - pointer new_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); - - SetAllocatedData(new_data, new_capacity); + construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); + SetAllocatedData(construct_data, new_capacity); SetIsAllocated(); - - construct_data = new_data; } else { construct_data = GetInlinedData(); } @@ -503,9 +509,7 @@ auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void { if (new_size > storage_view.capacity) { size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); - pointer new_data = allocation_tx.Allocate(new_capacity); - - construct_loop = {new_data, new_size}; + construct_loop = {allocation_tx.Allocate(new_capacity), new_size}; destroy_loop = {storage_view.data, storage_view.size}; } else if (new_size > storage_view.size) { assign_loop = {storage_view.data, storage_view.size}; @@ -539,12 +543,12 @@ template <typename ValueAdapter> auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void { StorageView storage_view = MakeStorageView(); - AllocationTransaction allocation_tx(GetAllocPtr()); - ConstructionTransaction construction_tx(GetAllocPtr()); - IteratorValueAdapter<MoveIterator> move_values( MoveIterator(storage_view.data)); + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + absl::Span<value_type> construct_loop; absl::Span<value_type> move_construct_loop; absl::Span<value_type> destroy_loop; @@ -687,19 +691,17 @@ auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference { MoveIterator(storage_view.data)); pointer construct_data; - if (storage_view.size == storage_view.capacity) { size_type new_capacity = NextCapacity(storage_view.capacity); - pointer new_data = allocation_tx.Allocate(new_capacity); - - construct_data = new_data; + construct_data = allocation_tx.Allocate(new_capacity); } else { construct_data = storage_view.data; } - pointer end = construct_data + storage_view.size; + pointer last_ptr = construct_data + storage_view.size; - AllocatorTraits::construct(*GetAllocPtr(), end, std::forward<Args>(args)...); + AllocatorTraits::construct(*GetAllocPtr(), last_ptr, + std::forward<Args>(args)...); if (allocation_tx.DidAllocate()) { ABSL_INTERNAL_TRY { @@ -708,7 +710,7 @@ auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference { storage_view.size); } ABSL_INTERNAL_CATCH_ANY { - AllocatorTraits::destroy(*GetAllocPtr(), end); + AllocatorTraits::destroy(*GetAllocPtr(), last_ptr); ABSL_INTERNAL_RETHROW; } @@ -721,14 +723,12 @@ auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference { } AddSize(1); - return *end; + return *last_ptr; } template <typename T, size_t N, typename A> auto Storage<T, N, A>::Erase(const_iterator from, const_iterator to) -> iterator { - assert(from != to); - StorageView storage_view = MakeStorageView(); size_type erase_size = std::distance(from, to); @@ -793,12 +793,9 @@ auto Storage<T, N, A>::ShrinkToFit() -> void { MoveIterator(storage_view.data)); pointer construct_data; - if (storage_view.size > GetInlinedCapacity()) { size_type new_capacity = storage_view.size; - pointer new_data = allocation_tx.Allocate(new_capacity); - - construct_data = new_data; + construct_data = allocation_tx.Allocate(new_capacity); } else { construct_data = GetInlinedData(); } @@ -889,7 +886,7 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { } } // namespace inlined_vector_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 3924b8aa..69cc85dd 100644 --- a/absl/container/internal/layout.h +++ b/absl/container/internal/layout.h @@ -188,7 +188,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // A type wrapper that instructs `Layout` to use the specific alignment for the @@ -735,7 +735,7 @@ class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> { }; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc index 44d84607..8f3628a1 100644 --- a/absl/container/internal/layout_test.cc +++ b/absl/container/internal/layout_test.cc @@ -28,7 +28,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -1563,5 +1563,5 @@ TEST(CompactString, Works) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/node_hash_policy.h b/absl/container/internal/node_hash_policy.h index d7581360..4617162f 100644 --- a/absl/container/internal/node_hash_policy.h +++ b/absl/container/internal/node_hash_policy.h @@ -39,8 +39,10 @@ #include <type_traits> #include <utility> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class Reference, class Policy> @@ -84,7 +86,7 @@ struct node_hash_policy { }; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ diff --git a/absl/container/internal/node_hash_policy_test.cc b/absl/container/internal/node_hash_policy_test.cc index d53b7364..84aabba9 100644 --- a/absl/container/internal/node_hash_policy_test.cc +++ b/absl/container/internal/node_hash_policy_test.cc @@ -21,7 +21,7 @@ #include "absl/container/internal/hash_policy_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -65,5 +65,5 @@ TEST_F(NodeTest, transfer) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h index 00caa373..0a02757d 100644 --- a/absl/container/internal/raw_hash_map.h +++ b/absl/container/internal/raw_hash_map.h @@ -24,7 +24,7 @@ #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class Policy, class Hash, class Eq, class Alloc> @@ -110,6 +110,9 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> { return insert_or_assign(k, v).first; } + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. template <class K = key_type, class... Args, typename std::enable_if< !std::is_convertible<K, const_iterator>::value, int>::type = 0, @@ -188,7 +191,7 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> { }; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index 02e74e21..919ac074 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -20,7 +20,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { constexpr size_t Group::kWidth; @@ -44,5 +44,5 @@ bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) { } } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 7b379d4f..ca7be8d8 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -118,7 +118,7 @@ #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <size_t Width> @@ -615,13 +615,17 @@ class raw_hash_set { iterator() {} // PRECONDITION: not an end() iterator. - reference operator*() const { return PolicyTraits::element(slot_); } + reference operator*() const { + assert_is_full(); + return PolicyTraits::element(slot_); + } // PRECONDITION: not an end() iterator. pointer operator->() const { return &operator*(); } // PRECONDITION: not an end() iterator. iterator& operator++() { + assert_is_full(); ++ctrl_; ++slot_; skip_empty_or_deleted(); @@ -635,6 +639,8 @@ class raw_hash_set { } friend bool operator==(const iterator& a, const iterator& b) { + a.assert_is_valid(); + b.assert_is_valid(); return a.ctrl_ == b.ctrl_; } friend bool operator!=(const iterator& a, const iterator& b) { @@ -645,6 +651,11 @@ class raw_hash_set { iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end() iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} + void assert_is_full() const { assert(IsFull(*ctrl_)); } + void assert_is_valid() const { + assert(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); + } + void skip_empty_or_deleted() { while (IsEmptyOrDeleted(*ctrl_)) { // ctrl is not necessarily aligned to Group::kWidth. It is also likely @@ -658,7 +669,7 @@ class raw_hash_set { } ctrl_t* ctrl_ = nullptr; - // To avoid uninitialized member warnigs, put slot_ in an anonymous union. + // To avoid uninitialized member warnings, put slot_ in an anonymous union. // The member is not initialized on singleton and end iterators. union { slot_type* slot_; @@ -939,8 +950,11 @@ class raw_hash_set { // // flat_hash_map<std::string, int> m; // m.insert(std::make_pair("abc", 42)); + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. template <class T, RequiresInsertable<T> = 0, - typename std::enable_if<IsDecomposable<T>::value, int>::type = 0, + class T2 = T, + typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0, T* = nullptr> std::pair<iterator, bool> insert(T&& value) { return emplace(std::forward<T>(value)); @@ -976,8 +990,10 @@ class raw_hash_set { return emplace(std::move(value)); } - template <class T, RequiresInsertable<T> = 0, - typename std::enable_if<IsDecomposable<T>::value, int>::type = 0, + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. + template <class T, RequiresInsertable<T> = 0, class T2 = T, + typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0, T* = nullptr> iterator insert(const_iterator, T&& value) { return insert(std::forward<T>(value)).first; @@ -1051,8 +1067,7 @@ class raw_hash_set { template <class... Args, typename std::enable_if< !IsDecomposable<Args...>::value, int>::type = 0> std::pair<iterator, bool> emplace(Args&&... args) { - typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type - raw; + alignas(slot_type) unsigned char raw[sizeof(slot_type)]; slot_type* slot = reinterpret_cast<slot_type*>(&raw); PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...); @@ -1068,10 +1083,15 @@ class raw_hash_set { // Extension API: support for lazy emplace. // // Looks up key in the table. If found, returns the iterator to the element. - // Otherwise calls f with one argument of type raw_hash_set::constructor. f - // MUST call raw_hash_set::constructor with arguments as if a - // raw_hash_set::value_type is constructed, otherwise the behavior is - // undefined. + // Otherwise calls `f` with one argument of type `raw_hash_set::constructor`. + // + // `f` must abide by several restrictions: + // - it MUST call `raw_hash_set::constructor` with arguments as if a + // `raw_hash_set::value_type` is constructed, + // - it MUST NOT access the container before the call to + // `raw_hash_set::constructor`, and + // - it MUST NOT erase the lazily emplaced element. + // Doing any of these is undefined behavior. // // For example: // @@ -1134,15 +1154,16 @@ class raw_hash_set { } // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`, - // this method returns void to reduce algorithmic complexity to O(1). In - // order to erase while iterating across a map, use the following idiom (which - // also works for standard containers): + // this method returns void to reduce algorithmic complexity to O(1). The + // iterator is invalidated, so any increment should be done before calling + // erase. In order to erase while iterating across a map, use the following + // idiom (which also works for standard containers): // // for (auto it = m.begin(), end = m.end(); it != end;) { + // // `erase()` will invalidate `it`, so advance `it` first. + // auto copy_it = it++; // if (<pred>) { - // m.erase(it++); - // } else { - // ++it; + // m.erase(copy_it); // } // } void erase(const_iterator cit) { erase(cit.inner_); } @@ -1150,7 +1171,7 @@ class raw_hash_set { // This overload is necessary because otherwise erase<K>(const K&) would be // a better match if non-const iterator is passed as an argument. void erase(iterator it) { - assert(it != end()); + it.assert_is_full(); PolicyTraits::destroy(&alloc_ref(), it.slot_); erase_meta_only(it); } @@ -1167,12 +1188,14 @@ class raw_hash_set { template <typename H, typename E> void merge(raw_hash_set<Policy, H, E, Alloc>& src) { // NOLINT assert(this != &src); - for (auto it = src.begin(), e = src.end(); it != e; ++it) { + for (auto it = src.begin(), e = src.end(); it != e;) { + auto next = std::next(it); if (PolicyTraits::apply(InsertSlot<false>{*this, std::move(*it.slot_)}, PolicyTraits::element(it.slot_)) .second) { src.erase_meta_only(it); } + it = next; } } @@ -1182,6 +1205,7 @@ class raw_hash_set { } node_type extract(const_iterator position) { + position.inner_.assert_is_full(); auto node = CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_); erase_meta_only(position); @@ -1531,8 +1555,7 @@ class raw_hash_set { // mark target as FULL // repeat procedure for current slot with moved from element (target) ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_); - typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type - raw; + alignas(slot_type) unsigned char raw[sizeof(slot_type)]; size_t total_probe_length = 0; slot_type* slot = reinterpret_cast<slot_type*>(&raw); for (size_t i = 0; i != capacity_; ++i) { @@ -1781,6 +1804,17 @@ class raw_hash_set { settings_{0, hasher{}, key_equal{}, allocator_type{}}; }; +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template <typename P, typename H, typename E, typename A, typename Predicate> +void EraseIf(Predicate pred, raw_hash_set<P, H, E, A>* c) { + for (auto it = c->begin(), last = c->end(); it != last;) { + auto copy_it = it++; + if (pred(*copy_it)) { + c->erase(copy_it); + } + } +} + namespace hashtable_debug_internal { template <typename Set> struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> { @@ -1842,7 +1876,7 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> { } // namespace hashtable_debug_internal } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc index 5188b3ae..7ac4b9f7 100644 --- a/absl/container/internal/raw_hash_set_allocator_test.cc +++ b/absl/container/internal/raw_hash_set_allocator_test.cc @@ -20,7 +20,7 @@ #include "absl/container/internal/tracked.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -426,5 +426,5 @@ TEST_F(PropagateOnAll, Swap) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 2783f5c4..a96ae68a 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -35,7 +35,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { struct RawHashSetTestOnlyAccess { @@ -418,53 +418,6 @@ TEST(Table, Empty) { EXPECT_TRUE(t.empty()); } -#ifdef __GNUC__ -template <class T> -ABSL_ATTRIBUTE_ALWAYS_INLINE inline void DoNotOptimize(const T& v) { - asm volatile("" : : "r,m"(v) : "memory"); -} -#endif - -TEST(Table, Prefetch) { - IntTable t; - t.emplace(1); - // Works for both present and absent keys. - t.prefetch(1); - t.prefetch(2); - - // Do not run in debug mode, when prefetch is not implemented, or when - // sanitizers are enabled, or on WebAssembly. -#if defined(NDEBUG) && defined(__GNUC__) && defined(__x86_64__) && \ - !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ - !defined(THREAD_SANITIZER) && !defined(UNDEFINED_BEHAVIOR_SANITIZER) && \ - !defined(__EMSCRIPTEN__) - const auto now = [] { return absl::base_internal::CycleClock::Now(); }; - - // 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; - for (int iter = 0; iter < 10; ++iter) { - int64_t time = now(); - for (int i = 0; i < size; ++i) { - DoNotOptimize(t.find(i)); - } - no_prefetch += now() - time; - - time = now(); - for (int i = 0; i < size; ++i) { - t.prefetch(i + 20); - DoNotOptimize(t.find(i)); - } - prefetch += now() - time; - } - - // no_prefetch is at least 30% slower. - EXPECT_GE(1.0 * no_prefetch / prefetch, 1.3); -#endif -} - TEST(Table, LookupEmpty) { IntTable t; auto it = t.find(0); @@ -1838,10 +1791,11 @@ TEST(TableDeathTest, EraseOfEndAsserts) { IntTable t; // Extra simple "regexp" as regexp support is highly varied across platforms. - constexpr char kDeathMsg[] = "it != end"; + constexpr char kDeathMsg[] = "IsFull"; EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg); } +#if defined(ABSL_HASHTABLEZ_SAMPLE) TEST(RawHashSamplerTest, Sample) { // Enable the feature even if the prod default is off. SetHashtablezEnabled(true); @@ -1862,6 +1816,7 @@ TEST(RawHashSamplerTest, Sample) { EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()), 0.01, 0.005); } +#endif // ABSL_HASHTABLEZ_SAMPLER TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) { // Enable the feature even if the prod default is off. @@ -1912,5 +1867,5 @@ TEST(Sanitizer, PoisoningOnErase) { } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/test_instance_tracker.cc b/absl/container/internal/test_instance_tracker.cc index f4b283fd..f9947f04 100644 --- a/absl/container/internal/test_instance_tracker.cc +++ b/absl/container/internal/test_instance_tracker.cc @@ -15,7 +15,7 @@ #include "absl/container/internal/test_instance_tracker.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace test_internal { int BaseCountedInstance::num_instances_ = 0; int BaseCountedInstance::num_live_instances_ = 0; @@ -25,5 +25,5 @@ int BaseCountedInstance::num_swaps_ = 0; int BaseCountedInstance::num_comparisons_ = 0; } // namespace test_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/test_instance_tracker.h b/absl/container/internal/test_instance_tracker.h index ab7f9f22..5ff6fd71 100644 --- a/absl/container/internal/test_instance_tracker.h +++ b/absl/container/internal/test_instance_tracker.h @@ -21,7 +21,7 @@ #include "absl/types/compare.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace test_internal { // A type that counts number of occurrences of the type, the live occurrences of @@ -268,7 +268,7 @@ class MovableOnlyInstance : public BaseCountedInstance { }; } // namespace test_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ diff --git a/absl/container/internal/tracked.h b/absl/container/internal/tracked.h index e9e6b95d..29f5829f 100644 --- a/absl/container/internal/tracked.h +++ b/absl/container/internal/tracked.h @@ -16,11 +16,14 @@ #define ABSL_CONTAINER_INTERNAL_TRACKED_H_ #include <stddef.h> + #include <memory> #include <utility> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { // A class that tracks its copies and moves so that it can be queried in tests. @@ -74,7 +77,7 @@ class Tracked { }; } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_TRACKED_H_ diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h index b64b5520..76ee95e6 100644 --- a/absl/container/internal/unordered_map_constructor_test.h +++ b/absl/container/internal/unordered_map_constructor_test.h @@ -24,7 +24,7 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class UnordMap> @@ -483,7 +483,7 @@ REGISTER_TYPED_TEST_CASE_P( AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf); } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 9ad78a79..e76421e5 100644 --- a/absl/container/internal/unordered_map_lookup_test.h +++ b/absl/container/internal/unordered_map_lookup_test.h @@ -21,7 +21,7 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class UnordMap> @@ -111,7 +111,7 @@ REGISTER_TYPED_TEST_CASE_P(LookupTest, At, OperatorBracket, Count, Find, EqualRange); } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 index c4600405..7d48cdb8 100644 --- a/absl/container/internal/unordered_map_members_test.h +++ b/absl/container/internal/unordered_map_members_test.h @@ -21,7 +21,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class UnordMap> @@ -81,7 +81,7 @@ TYPED_TEST_P(MembersTest, BeginEnd) { REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 89dd7894..b8c513f1 100644 --- a/absl/container/internal/unordered_map_modifiers_test.h +++ b/absl/container/internal/unordered_map_modifiers_test.h @@ -15,13 +15,15 @@ #ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ #define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ +#include <memory> + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class UnordMap> @@ -268,8 +270,47 @@ REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, Emplace, EmplaceHint, TryEmplace, TryEmplaceHint, Erase, EraseRange, EraseKey, Swap); +template <typename Type> +struct is_unique_ptr : std::false_type {}; + +template <typename Type> +struct is_unique_ptr<std::unique_ptr<Type>> : std::true_type {}; + +template <class UnordMap> +class UniquePtrModifiersTest : public ::testing::Test { + protected: + UniquePtrModifiersTest() { + static_assert(is_unique_ptr<typename UnordMap::mapped_type>::value, + "UniquePtrModifiersTyest may only be called with a " + "std::unique_ptr value type."); + } +}; + +TYPED_TEST_SUITE_P(UniquePtrModifiersTest); + +// Test that we do not move from rvalue arguments if an insertion does not +// happen. +TYPED_TEST_P(UniquePtrModifiersTest, TryEmplace) { +#ifdef UNORDERED_MAP_CXX17 + using T = hash_internal::GeneratedType<TypeParam>; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator<T>()(); + TypeParam m; + auto p = m.try_emplace(val.first, std::move(val.second)); + EXPECT_TRUE(p.second); + // A moved from std::unique_ptr is guaranteed to be nullptr. + EXPECT_EQ(val.second, nullptr); + T val2 = {val.first, hash_internal::Generator<V>()()}; + p = m.try_emplace(val2.first, std::move(val2.second)); + EXPECT_FALSE(p.second); + EXPECT_NE(val2.second, nullptr); +#endif +} + +REGISTER_TYPED_TEST_SUITE_P(UniquePtrModifiersTest, TryEmplace); + } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 51a90af8..9cbf512f 100644 --- a/absl/container/internal/unordered_map_test.cc +++ b/absl/container/internal/unordered_map_test.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <memory> #include <unordered_map> #include "absl/container/internal/unordered_map_constructor_test.h" @@ -20,7 +21,7 @@ #include "absl/container/internal/unordered_map_modifiers_test.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -36,7 +37,14 @@ INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, LookupTest, MapTypes); INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, MembersTest, MapTypes); INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ModifiersTest, MapTypes); +using UniquePtrMapTypes = ::testing::Types<std::unordered_map< + int, std::unique_ptr<int>, StatefulTestingHash, StatefulTestingEqual, + Alloc<std::pair<const int, std::unique_ptr<int>>>>>; + +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, UniquePtrModifiersTest, + UniquePtrMapTypes); + } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/unordered_set_constructor_test.h b/absl/container/internal/unordered_set_constructor_test.h index ac73a896..41165b05 100644 --- a/absl/container/internal/unordered_set_constructor_test.h +++ b/absl/container/internal/unordered_set_constructor_test.h @@ -26,7 +26,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class UnordMap> @@ -490,7 +490,7 @@ REGISTER_TYPED_TEST_CASE_P( AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf); } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 722fb1c2..8f2f4b20 100644 --- a/absl/container/internal/unordered_set_lookup_test.h +++ b/absl/container/internal/unordered_set_lookup_test.h @@ -21,7 +21,7 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class UnordSet> @@ -85,7 +85,7 @@ TYPED_TEST_P(LookupTest, EqualRange) { REGISTER_TYPED_TEST_CASE_P(LookupTest, Count, Find, EqualRange); } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 index 756a95cb..4c5e104a 100644 --- a/absl/container/internal/unordered_set_members_test.h +++ b/absl/container/internal/unordered_set_members_test.h @@ -21,7 +21,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class UnordSet> @@ -80,7 +80,7 @@ TYPED_TEST_P(MembersTest, BeginEnd) { REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 d3e534d3..26be58d9 100644 --- a/absl/container/internal/unordered_set_modifiers_test.h +++ b/absl/container/internal/unordered_set_modifiers_test.h @@ -21,7 +21,7 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class UnordSet> @@ -184,7 +184,7 @@ REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, EraseKey, Swap); } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 2356e187..a134b539 100644 --- a/absl/container/internal/unordered_set_test.cc +++ b/absl/container/internal/unordered_set_test.cc @@ -20,7 +20,7 @@ #include "absl/container/internal/unordered_set_modifiers_test.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { @@ -37,5 +37,5 @@ INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ModifiersTest, SetTypes); } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index addf120f..fccea184 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -48,7 +48,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <class Key, class Value> class NodeHashMapPolicy; @@ -352,6 +352,10 @@ class node_hash_map // Inserts (via copy or move) the element of the specified key into the // `node_hash_map` using the position of `hint` as a non-binding suggestion // for where to begin the insertion search. + // + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. using Base::try_emplace; // node_hash_map::extract() @@ -518,6 +522,15 @@ class node_hash_map void resize(typename Base::size_type hint) { this->rehash(hint); } }; +// erase_if(node_hash_map<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template <typename K, typename V, typename H, typename E, typename A, + typename Predicate> +void erase_if(node_hash_map<K, V, H, E, A>& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + namespace container_internal { template <class Key, class Value> @@ -578,7 +591,7 @@ struct IsUnorderedContainer< } // namespace container_algorithm_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 7ce7ca02..5d74b814 100644 --- a/absl/container/node_hash_map_test.cc +++ b/absl/container/node_hash_map_test.cc @@ -21,11 +21,12 @@ #include "absl/container/internal/unordered_map_modifiers_test.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { using ::testing::Field; +using ::testing::IsEmpty; using ::testing::Pair; using ::testing::UnorderedElementsAre; @@ -216,7 +217,44 @@ TEST(NodeHashMap, MergeExtractInsert) { EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70), Elem(17, 23))); } +bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; } + +TEST(NodeHashMap, EraseIf) { + // Erase all elements. + { + node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, [](std::pair<const int, int>) { return true; }); + EXPECT_THAT(s, IsEmpty()); + } + // Erase no elements. + { + node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, [](std::pair<const int, int>) { return false; }); + EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), + Pair(4, 4), Pair(5, 5))); + } + // Erase specific elements. + { + node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, + [](std::pair<const int, int> kvp) { return kvp.first % 2 == 1; }); + EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4))); + } + // Predicate is function reference. + { + node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, FirstIsEven); + EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5))); + } + // Predicate is function pointer. + { + node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + erase_if(s, &FirstIsEven); + EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5))); + } +} + } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index 103d32d2..ad54b6dc 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -44,7 +44,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { template <typename T> struct NodeHashSetPolicy; @@ -77,7 +77,7 @@ struct NodeHashSetPolicy; // // // Create a node hash set of three strings // absl::node_hash_map<std::string, std::string> ducks = -// {"huey", "dewey"}, "louie"}; +// {"huey", "dewey", "louie"}; // // // Insert a new element into the node hash map // ducks.insert("donald"}; @@ -111,7 +111,7 @@ class node_hash_set // * Initializer List constructor // // absl::node_hash_set<std::string> set2 = - // {{"huey"}, {"dewey"}, {"louie"},}; + // {{"huey"}, {"dewey"}, {"louie"}}; // // * Copy constructor // @@ -435,6 +435,14 @@ class node_hash_set void resize(typename Base::size_type hint) { this->rehash(hint); } }; +// erase_if(node_hash_set<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template <typename T, typename H, typename E, typename A, typename Predicate> +void erase_if(node_hash_set<T, H, E, A>& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + namespace container_internal { template <class T> @@ -484,7 +492,7 @@ struct IsUnorderedContainer<absl::node_hash_set<Key, Hash, KeyEqual, Allocator>> : std::true_type {}; } // namespace container_algorithm_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // 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 65d125ed..7ddad202 100644 --- a/absl/container/node_hash_set_test.cc +++ b/absl/container/node_hash_set_test.cc @@ -20,11 +20,12 @@ #include "absl/container/internal/unordered_set_modifiers_test.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { using ::absl::container_internal::hash_internal::Enum; using ::absl::container_internal::hash_internal::EnumClass; +using ::testing::IsEmpty; using ::testing::Pointee; using ::testing::UnorderedElementsAre; @@ -101,7 +102,42 @@ TEST(NodeHashSet, MergeExtractInsert) { EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23))); } +bool IsEven(int k) { return k % 2 == 0; } + +TEST(NodeHashSet, EraseIf) { + // Erase all elements. + { + node_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, [](int) { return true; }); + EXPECT_THAT(s, IsEmpty()); + } + // Erase no elements. + { + node_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, [](int) { return false; }); + EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5)); + } + // Erase specific elements. + { + node_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, [](int k) { return k % 2 == 1; }); + EXPECT_THAT(s, UnorderedElementsAre(2, 4)); + } + // Predicate is function reference. + { + node_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, IsEven); + EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5)); + } + // Predicate is function pointer. + { + node_hash_set<int> s = {1, 2, 3, 4, 5}; + erase_if(s, &IsEven); + EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5)); + } +} + } // namespace } // namespace container_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake index 4ece4c6f..77d4ace8 100644 --- a/absl/copts/AbseilConfigureCopts.cmake +++ b/absl/copts/AbseilConfigureCopts.cmake @@ -5,25 +5,46 @@ set(ABSL_LSAN_LINKOPTS "") set(ABSL_HAVE_LSAN OFF) set(ABSL_DEFAULT_LINKOPTS "") +if (BUILD_SHARED_LIBS AND MSVC) + set(ABSL_BUILD_DLL TRUE) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +else() + set(ABSL_BUILD_DLL FALSE) +endif() + +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") + if (MSVC) + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}") + else() + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}") + endif() +elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*|aarch64") + if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM64_FLAGS}") + elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM32_FLAGS}") + else() + message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.") + endif() +else() + message(WARNING "Value of CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) is unknown and cannot be used to set ABSL_RANDOM_RANDEN_COPTS") + set(ABSL_RANDOM_RANDEN_COPTS "") +endif() + + 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}") - set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_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}") set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}") - set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_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}") - set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # AppleClang doesn't have lsan # https://developer.apple.com/documentation/code_diagnostics @@ -36,20 +57,13 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") 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}") set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}") - set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_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 "") - set(ABSL_RANDOM_RANDEN_COPTS "") 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}") diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake index 01bd40b2..7ef6339b 100644 --- a/absl/copts/GENERATED_AbseilCopts.cmake +++ b/absl/copts/GENERATED_AbseilCopts.cmake @@ -3,12 +3,6 @@ # (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" @@ -29,7 +23,6 @@ list(APPEND ABSL_CLANG_CL_FLAGS "-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" @@ -80,10 +73,6 @@ list(APPEND ABSL_CLANG_CL_TEST_FLAGS "-Wno-gnu-zero-variadic-macro-arguments" ) -list(APPEND ABSL_GCC_EXCEPTIONS_FLAGS - "-fexceptions" -) - list(APPEND ABSL_GCC_FLAGS "-Wall" "-Wextra" @@ -111,10 +100,6 @@ list(APPEND ABSL_GCC_TEST_FLAGS "-Wno-unused-private-field" ) -list(APPEND ABSL_LLVM_EXCEPTIONS_FLAGS - "-fexceptions" -) - list(APPEND ABSL_LLVM_FLAGS "-Wall" "-Wextra" @@ -137,7 +122,6 @@ list(APPEND ABSL_LLVM_FLAGS "-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" @@ -183,12 +167,6 @@ list(APPEND ABSL_LLVM_TEST_FLAGS "-Wno-gnu-zero-variadic-macro-arguments" ) -list(APPEND ABSL_MSVC_EXCEPTIONS_FLAGS - "/U_HAS_EXCEPTIONS" - "/D_HAS_EXCEPTIONS=1" - "/EHsc" -) - list(APPEND ABSL_MSVC_FLAGS "/W3" "/DNOMINMAX" @@ -196,6 +174,7 @@ list(APPEND ABSL_MSVC_FLAGS "/D_CRT_SECURE_NO_WARNINGS" "/D_SCL_SECURE_NO_WARNINGS" "/D_ENABLE_EXTENDED_ALIGNED_STORAGE" + "/bigobj" "/wd4005" "/wd4068" "/wd4180" @@ -226,8 +205,6 @@ list(APPEND ABSL_RANDOM_HWAES_ARM64_FLAGS ) list(APPEND ABSL_RANDOM_HWAES_MSVC_X64_FLAGS - "/O2" - "/Ob2" ) list(APPEND ABSL_RANDOM_HWAES_X64_FLAGS diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl index 82f332f4..3cc48784 100644 --- a/absl/copts/GENERATED_copts.bzl +++ b/absl/copts/GENERATED_copts.bzl @@ -4,12 +4,6 @@ (2) Run `python <path_to_absl>/copts/generate_copts.py`. """ -ABSL_CLANG_CL_EXCEPTIONS_FLAGS = [ - "/U_HAS_EXCEPTIONS", - "/D_HAS_EXCEPTIONS=1", - "/EHsc", -] - ABSL_CLANG_CL_FLAGS = [ "/W3", "-Wno-c++98-compat-pedantic", @@ -30,7 +24,6 @@ ABSL_CLANG_CL_FLAGS = [ "-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", @@ -81,10 +74,6 @@ ABSL_CLANG_CL_TEST_FLAGS = [ "-Wno-gnu-zero-variadic-macro-arguments", ] -ABSL_GCC_EXCEPTIONS_FLAGS = [ - "-fexceptions", -] - ABSL_GCC_FLAGS = [ "-Wall", "-Wextra", @@ -112,10 +101,6 @@ ABSL_GCC_TEST_FLAGS = [ "-Wno-unused-private-field", ] -ABSL_LLVM_EXCEPTIONS_FLAGS = [ - "-fexceptions", -] - ABSL_LLVM_FLAGS = [ "-Wall", "-Wextra", @@ -138,7 +123,6 @@ ABSL_LLVM_FLAGS = [ "-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", @@ -184,12 +168,6 @@ ABSL_LLVM_TEST_FLAGS = [ "-Wno-gnu-zero-variadic-macro-arguments", ] -ABSL_MSVC_EXCEPTIONS_FLAGS = [ - "/U_HAS_EXCEPTIONS", - "/D_HAS_EXCEPTIONS=1", - "/EHsc", -] - ABSL_MSVC_FLAGS = [ "/W3", "/DNOMINMAX", @@ -197,6 +175,7 @@ ABSL_MSVC_FLAGS = [ "/D_CRT_SECURE_NO_WARNINGS", "/D_SCL_SECURE_NO_WARNINGS", "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", + "/bigobj", "/wd4005", "/wd4068", "/wd4180", @@ -227,8 +206,6 @@ ABSL_RANDOM_HWAES_ARM64_FLAGS = [ ] ABSL_RANDOM_HWAES_MSVC_X64_FLAGS = [ - "/O2", - "/Ob2", ] ABSL_RANDOM_HWAES_X64_FLAGS = [ diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl index 8c4efe77..9dd6bd0a 100644 --- a/absl/copts/configure_copts.bzl +++ b/absl/copts/configure_copts.bzl @@ -6,13 +6,12 @@ change Abseil copts, edit absl/copts/copts.py load( "//absl:copts/GENERATED_copts.bzl", - "ABSL_GCC_EXCEPTIONS_FLAGS", + "ABSL_CLANG_CL_FLAGS", + "ABSL_CLANG_CL_TEST_FLAGS", "ABSL_GCC_FLAGS", "ABSL_GCC_TEST_FLAGS", - "ABSL_LLVM_EXCEPTIONS_FLAGS", "ABSL_LLVM_FLAGS", "ABSL_LLVM_TEST_FLAGS", - "ABSL_MSVC_EXCEPTIONS_FLAGS", "ABSL_MSVC_FLAGS", "ABSL_MSVC_LINKOPTS", "ABSL_MSVC_TEST_FLAGS", @@ -36,16 +35,6 @@ ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({ "//conditions:default": ABSL_GCC_TEST_FLAGS, }) -ABSL_EXCEPTIONS_FLAG = select({ - "//absl:windows": ABSL_MSVC_EXCEPTIONS_FLAGS, - "//absl:llvm_compiler": ABSL_LLVM_EXCEPTIONS_FLAGS, - "//conditions:default": ABSL_GCC_EXCEPTIONS_FLAGS, -}) - -ABSL_EXCEPTIONS_FLAG_LINKOPTS = select({ - "//conditions:default": [], -}) - ABSL_DEFAULT_LINKOPTS = select({ "//absl:windows": ABSL_MSVC_LINKOPTS, "//conditions:default": [], diff --git a/absl/copts/copts.py b/absl/copts/copts.py index 068abceb..704ef234 100644 --- a/absl/copts/copts.py +++ b/absl/copts/copts.py @@ -56,7 +56,6 @@ LLVM_DISABLE_WARNINGS_FLAGS = [ "-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 @@ -109,12 +108,6 @@ LLVM_TEST_DISABLE_WARNINGS_FLAGS = [ "-Wno-gnu-zero-variadic-macro-arguments", ] -MSVC_STYLE_EXCEPTIONS_FLAGS = [ - "/U_HAS_EXCEPTIONS", - "/D_HAS_EXCEPTIONS=1", - "/EHsc" -] - MSVC_DEFINES = [ "/DNOMINMAX", # Don't define min and max macros (windows.h) # Don't bloat namespace with incompatible winsock versions. @@ -157,20 +150,18 @@ COPT_VARS = { "-Wno-unused-parameter", "-Wno-unused-private-field", ], - "ABSL_GCC_EXCEPTIONS_FLAGS": ["-fexceptions"], "ABSL_LLVM_FLAGS": LLVM_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS, "ABSL_LLVM_TEST_FLAGS": LLVM_TEST_DISABLE_WARNINGS_FLAGS, - "ABSL_LLVM_EXCEPTIONS_FLAGS": ["-fexceptions"], "ABSL_CLANG_CL_FLAGS": (MSVC_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS + MSVC_DEFINES), "ABSL_CLANG_CL_TEST_FLAGS": LLVM_TEST_DISABLE_WARNINGS_FLAGS, - "ABSL_CLANG_CL_EXCEPTIONS_FLAGS": - MSVC_STYLE_EXCEPTIONS_FLAGS, "ABSL_MSVC_FLAGS": MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES + [ + # Increase the number of sections available in object files + "/bigobj", "/wd4005", # macro-redefinition "/wd4068", # unknown pragma # qualifier applied to function type has no meaning; ignored @@ -191,8 +182,6 @@ COPT_VARS = { "/wd4996", # use of deprecated symbol "/DNOMINMAX", # disable the min() and max() macros from <windows.h> ], - "ABSL_MSVC_EXCEPTIONS_FLAGS": - MSVC_STYLE_EXCEPTIONS_FLAGS, "ABSL_MSVC_LINKOPTS": [ # Object file doesn't export any previously undefined symbols "-ignore:4221", @@ -207,8 +196,5 @@ COPT_VARS = { "-maes", "-msse4.1", ], - "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS": [ - "/O2", # Maximize speed - "/Ob2", # Aggressive inlining - ], + "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS": [], } diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index 913cfafb..8f521bec 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -14,6 +14,7 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -30,6 +31,14 @@ licenses(["notice"]) # Apache 2.0 cc_library( name = "stacktrace", srcs = [ + "internal/stacktrace_aarch64-inl.inc", + "internal/stacktrace_arm-inl.inc", + "internal/stacktrace_config.h", + "internal/stacktrace_generic-inl.inc", + "internal/stacktrace_powerpc-inl.inc", + "internal/stacktrace_unimplemented-inl.inc", + "internal/stacktrace_win32-inl.inc", + "internal/stacktrace_x86-inl.inc", "stacktrace.cc", ], hdrs = ["stacktrace.h"], @@ -37,7 +46,7 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":debugging_internal", - "//absl/base", + "//absl/base:config", "//absl/base:core_headers", ], ) @@ -55,27 +64,39 @@ cc_library( "symbolize.h", ], copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, + linkopts = ABSL_DEFAULT_LINKOPTS + select({ + "//absl:windows": ["-DEFAULTLIB:dbghelp.lib"], + "//conditions:default": [], + }), deps = [ ":debugging_internal", ":demangle_internal", "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:dynamic_annotations", "//absl/base:malloc_internal", + "//absl/base:raw_logging_internal", ], ) cc_test( name = "symbolize_test", srcs = ["symbolize_test.cc"], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS + select({ + "//absl:windows": ["/Z7"], + "//conditions:default": [], + }), + linkopts = ABSL_DEFAULT_LINKOPTS + select({ + "//absl:windows": ["/DEBUG"], + "//conditions:default": [], + }), deps = [ ":stack_consumption", ":symbolize", "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/memory", "@com_google_googletest//:gtest", ], @@ -95,8 +116,9 @@ cc_library( deps = [ ":stacktrace", ":symbolize", - "//absl/base", + "//absl/base:config", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", ], ) @@ -112,6 +134,8 @@ cc_library( "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:errno_saver", + "//absl/base:raw_logging_internal", ], ) @@ -127,7 +151,7 @@ cc_test( ":failure_signal_handler", ":stacktrace", ":symbolize", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/strings", "@com_google_googletest//:gtest", ], @@ -143,22 +167,16 @@ cc_library( hdrs = [ "internal/address_is_readable.h", "internal/elf_mem_image.h", - "internal/stacktrace_aarch64-inl.inc", - "internal/stacktrace_arm-inl.inc", - "internal/stacktrace_config.h", - "internal/stacktrace_generic-inl.inc", - "internal/stacktrace_powerpc-inl.inc", - "internal/stacktrace_unimplemented-inl.inc", - "internal/stacktrace_win32-inl.inc", - "internal/stacktrace_x86-inl.inc", "internal/vdso_support.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:dynamic_annotations", + "//absl/base:errno_saver", + "//absl/base:raw_logging_internal", ], ) @@ -169,6 +187,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ "//absl/base", + "//absl/base:config", "//absl/base:core_headers", ], ) @@ -181,8 +200,8 @@ cc_test( deps = [ ":demangle_internal", ":stack_consumption", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/memory", "@com_google_googletest//:gtest_main", ], @@ -193,7 +212,10 @@ cc_library( srcs = ["leak_check.cc"], hdrs = ["leak_check.h"], linkopts = ABSL_DEFAULT_LINKOPTS, - deps = ["//absl/base:core_headers"], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + ], ) # Adding a dependency to leak_check_disable will disable @@ -204,6 +226,7 @@ cc_library( srcs = ["leak_check_disable.cc"], linkopts = ABSL_DEFAULT_LINKOPTS, linkstatic = 1, + deps = ["//absl/base:config"], alwayslink = 1, ) @@ -225,6 +248,9 @@ cc_library( }), linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], + deps = [ + "//absl/base:config", + ], ) cc_library( @@ -235,6 +261,9 @@ cc_library( copts = ["-ULEAK_SANITIZER"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], + deps = [ + "//absl/base:config", + ], ) cc_test( @@ -292,8 +321,9 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ - "//absl/base", + "//absl/base:config", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", ], ) @@ -304,8 +334,8 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":stack_consumption", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 001e2727..77336159 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -19,13 +19,21 @@ absl_cc_library( stacktrace HDRS "stacktrace.h" + "internal/stacktrace_aarch64-inl.inc" + "internal/stacktrace_arm-inl.inc" + "internal/stacktrace_config.h" + "internal/stacktrace_generic-inl.inc" + "internal/stacktrace_powerpc-inl.inc" + "internal/stacktrace_unimplemented-inl.inc" + "internal/stacktrace_win32-inl.inc" + "internal/stacktrace_x86-inl.inc" SRCS "stacktrace.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS absl::debugging_internal - absl::base + absl::config absl::core_headers PUBLIC ) @@ -45,13 +53,16 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + $<$<BOOL:${MINGW}>:"dbghelp"> DEPS absl::debugging_internal absl::demangle_internal absl::base + absl::config absl::core_headers absl::dynamic_annotations absl::malloc_internal + absl::raw_logging_internal PUBLIC ) @@ -62,12 +73,16 @@ absl_cc_test( "symbolize_test.cc" COPTS ${ABSL_TEST_COPTS} + $<$<BOOL:${MSVC}>:-Z7> + LINKOPTS + $<$<BOOL:${MSVC}>:-DEBUG> DEPS absl::stack_consumption absl::symbolize absl::base absl::core_headers absl::memory + absl::raw_logging_internal gmock ) @@ -83,8 +98,9 @@ absl_cc_library( DEPS absl::stacktrace absl::symbolize - absl::base + absl::config absl::core_headers + absl::raw_logging_internal ) absl_cc_library( @@ -102,6 +118,8 @@ absl_cc_library( absl::base absl::config absl::core_headers + absl::errno_saver + absl::raw_logging_internal PUBLIC ) @@ -116,8 +134,8 @@ absl_cc_test( absl::failure_signal_handler absl::stacktrace absl::symbolize - absl::base absl::strings + absl::raw_logging_internal Threads::Threads gmock ) @@ -128,14 +146,6 @@ absl_cc_library( HDRS "internal/address_is_readable.h" "internal/elf_mem_image.h" - "internal/stacktrace_aarch64-inl.inc" - "internal/stacktrace_arm-inl.inc" - "internal/stacktrace_config.h" - "internal/stacktrace_generic-inl.inc" - "internal/stacktrace_powerpc-inl.inc" - "internal/stacktrace_unimplemented-inl.inc" - "internal/stacktrace_win32-inl.inc" - "internal/stacktrace_x86-inl.inc" "internal/vdso_support.h" SRCS "internal/address_is_readable.cc" @@ -144,9 +154,11 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS - absl::base absl::core_headers + absl::config absl::dynamic_annotations + absl::errno_saver + absl::raw_logging_internal ) absl_cc_library( @@ -174,9 +186,9 @@ absl_cc_test( DEPS absl::demangle_internal absl::stack_consumption - absl::base absl::core_headers absl::memory + absl::raw_logging_internal gmock_main ) @@ -190,6 +202,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::core_headers PUBLIC ) @@ -236,7 +249,7 @@ absl_cc_test( SRCS "leak_check_test.cc" COPTS - ${ABSL_DEFAULT_COPTS} + ${ABSL_TEST_COPTS} "$<$<BOOL:${ABSL_HAVE_LSAN}>:-DABSL_EXPECT_LEAK_SANITIZER>" LINKOPTS "${ABSL_LSAN_LINKOPTS}" @@ -273,6 +286,7 @@ absl_cc_test( absl::leak_check_api_enabled_for_testing absl::leak_check_disable absl::base + absl::raw_logging_internal gmock_main ) @@ -286,8 +300,9 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS - absl::base + absl::config absl::core_headers + absl::raw_logging_internal TESTONLY ) @@ -300,8 +315,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::stack_consumption - absl::base absl::core_headers + absl::raw_logging_internal gmock_main ) diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index af651c72..1f69bfa8 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -24,6 +24,10 @@ #include <unistd.h> #endif +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + #ifdef ABSL_HAVE_MMAP #include <sys/mman.h> #endif @@ -37,6 +41,7 @@ #include <ctime> #include "absl/base/attributes.h" +#include "absl/base/internal/errno_saver.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/sysinfo.h" #include "absl/debugging/internal/examine_stack.h" @@ -44,10 +49,15 @@ #ifndef _WIN32 #define ABSL_HAVE_SIGACTION +// Apple WatchOS and TVOS don't allow sigaltstack +#if !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \ + !(defined(TARGET_OS_TV) && TARGET_OS_TV) +#define ABSL_HAVE_SIGALTSTACK +#endif #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options; @@ -117,7 +127,7 @@ const char* FailureSignalToString(int signo) { } // namespace debugging_internal -#ifndef _WIN32 +#ifdef ABSL_HAVE_SIGALTSTACK static bool SetupAlternateStackOnce() { #if defined(__wasm__) || defined (__asjms__) @@ -169,7 +179,7 @@ static bool SetupAlternateStackOnce() { // Returns the appropriate flag for sig_action.sa_flags // if the system supports using an alternate stack. static int MaybeSetupAlternateStack() { -#ifndef _WIN32 +#ifdef ABSL_HAVE_SIGALTSTACK ABSL_ATTRIBUTE_UNUSED static const bool kOnce = SetupAlternateStackOnce(); return SA_ONSTACK; #else @@ -205,9 +215,8 @@ static void InstallOneFailureHandler(FailureSignalData* data, #endif static void WriteToStderr(const char* data) { - int old_errno = errno; + absl::base_internal::ErrnoSaver errno_saver; absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data)); - errno = old_errno; } static void WriteSignalMessage(int signo, void (*writerfn)(const char*)) { @@ -357,5 +366,5 @@ void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) { } } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h index 87b202b0..f5a83962 100644 --- a/absl/debugging/failure_signal_handler.h +++ b/absl/debugging/failure_signal_handler.h @@ -44,8 +44,10 @@ #ifndef ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ #define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // FailureSignalHandlerOptions // @@ -113,7 +115,7 @@ namespace debugging_internal { const char* FailureSignalToString(int signo); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ diff --git a/absl/debugging/failure_signal_handler_test.cc b/absl/debugging/failure_signal_handler_test.cc index bb2cc48e..863fb514 100644 --- a/absl/debugging/failure_signal_handler_test.cc +++ b/absl/debugging/failure_signal_handler_test.cc @@ -23,6 +23,7 @@ #include <fstream> #include "gtest/gtest.h" +#include "gmock/gmock.h" #include "absl/base/internal/raw_logging.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" @@ -31,6 +32,8 @@ namespace { +using testing::StartsWith; + #if GTEST_HAS_DEATH_TEST // For the parameterized death tests. GetParam() returns the signal number. @@ -113,15 +116,15 @@ TEST_P(FailureSignalHandlerDeathTest, AbslFatalSignalsWithWriterFn) { ASSERT_TRUE(error_output.is_open()) << file; std::string error_line; std::getline(error_output, error_line); - EXPECT_TRUE(absl::StartsWith( + EXPECT_THAT( error_line, - absl::StrCat("*** ", - absl::debugging_internal::FailureSignalToString(signo), - " received at "))); + StartsWith(absl::StrCat( + "*** ", absl::debugging_internal::FailureSignalToString(signo), + " received at "))); if (absl::debugging_internal::StackTraceWorksForTest()) { std::getline(error_output, error_line); - EXPECT_TRUE(absl::StartsWith(error_line, "PC: ")); + EXPECT_THAT(error_line, StartsWith("PC: ")); } } diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc index 64dd285b..65376063 100644 --- a/absl/debugging/internal/address_is_readable.cc +++ b/absl/debugging/internal/address_is_readable.cc @@ -20,14 +20,14 @@ #if !defined(__linux__) || defined(__ANDROID__) namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // On platforms other than Linux, just return true. bool AddressIsReadable(const void* /* addr */) { return true; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #else @@ -35,14 +35,16 @@ bool AddressIsReadable(const void* /* addr */) { return true; } #include <fcntl.h> #include <sys/syscall.h> #include <unistd.h> + #include <atomic> #include <cerrno> #include <cstdint> +#include "absl/base/internal/errno_saver.h" #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // Pack a pid and two file descriptors into a 64-bit word, @@ -67,7 +69,7 @@ static void Unpack(uint64_t x, int *pid, int *read_fd, int *write_fd) { // This is a namespace-scoped variable for correct zero-initialization. static std::atomic<uint64_t> pid_and_fds; // initially 0, an invalid pid. bool AddressIsReadable(const void *addr) { - int save_errno = errno; + absl::base_internal::ErrnoSaver errno_saver; // We test whether a byte is readable by using write(). Normally, this would // be done via a cached file descriptor to /dev/null, but linux fails to // check whether the byte is readable when the destination is /dev/null, so @@ -126,12 +128,11 @@ bool AddressIsReadable(const void *addr) { std::memory_order_relaxed); } } while (errno == EBADF); - errno = save_errno; return bytes_written == 1; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif diff --git a/absl/debugging/internal/address_is_readable.h b/absl/debugging/internal/address_is_readable.h index dc626e5b..4bbaf4d6 100644 --- a/absl/debugging/internal/address_is_readable.h +++ b/absl/debugging/internal/address_is_readable.h @@ -15,8 +15,10 @@ #ifndef ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ #define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // Return whether the byte at *addr is readable, without faulting. @@ -24,7 +26,7 @@ namespace debugging_internal { bool AddressIsReadable(const void *addr); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc index 976e47a6..fc615c3f 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -24,7 +24,7 @@ #include <limits> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { typedef struct { @@ -94,6 +94,8 @@ static const AbbrevPair kOperatorList[] = { }; // List of builtin types from Itanium C++ ABI. +// +// Invariant: only one- or two-character type abbreviations here. static const AbbrevPair kBuiltinTypeList[] = { {"v", "void", 0}, {"w", "wchar_t", 0}, @@ -116,6 +118,16 @@ static const AbbrevPair kBuiltinTypeList[] = { {"e", "long double", 0}, {"g", "__float128", 0}, {"z", "ellipsis", 0}, + + {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) + {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) + {"Dc", "decltype(auto)", 0}, + {"Da", "auto", 0}, + {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) + {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) + {"Di", "char32_t", 0}, + {"Ds", "char16_t", 0}, + {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) {nullptr, nullptr, 0}, }; @@ -1169,12 +1181,6 @@ static bool ParseType(State *state) { } state->parse_state = copy; - // nullptr_t, i.e. decltype(nullptr). - if (ParseTwoCharToken(state, "Dn")) { - return true; - } - state->parse_state = copy; - if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && ParseType(state)) { return true; @@ -1215,16 +1221,26 @@ static bool ParseCVQualifiers(State *state) { return num_cv_qualifiers > 0; } -// <builtin-type> ::= v, etc. +// <builtin-type> ::= v, etc. # single-character builtin types // ::= u <source-name> +// ::= Dd, etc. # two-character builtin types +// +// Not supported: +// ::= DF <number> _ # _FloatN (N bits) +// static bool ParseBuiltinType(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; const AbbrevPair *p; for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { - if (RemainingInput(state)[0] == p->abbrev[0]) { + // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. + if (p->abbrev[1] == '\0') { + if (ParseOneCharToken(state, p->abbrev[0])) { + MaybeAppend(state, p->real_name); + return true; + } + } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) { MaybeAppend(state, p->real_name); - ++state->parse_state.mangled_idx; return true; } } @@ -1875,5 +1891,5 @@ bool Demangle(const char *mangled, char *out, int out_size) { } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h index 20adbe9c..c314d9bc 100644 --- a/absl/debugging/internal/demangle.h +++ b/absl/debugging/internal/demangle.h @@ -53,8 +53,10 @@ #ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ #define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // Demangle `mangled`. On success, return true and write the @@ -63,7 +65,7 @@ namespace debugging_internal { bool Demangle(const char *mangled, char *out, int out_size); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc index 7c50fe3a..c6f1ce18 100644 --- a/absl/debugging/internal/demangle_test.cc +++ b/absl/debugging/internal/demangle_test.cc @@ -23,7 +23,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { namespace { @@ -191,5 +191,5 @@ TEST(DemangleRegression, DeeplyNestedArrayType) { } // namespace } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index a795117a..24cc0130 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -38,7 +38,7 @@ #define VERSYM_VERSION 0x7fff namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { namespace { @@ -376,7 +376,7 @@ void ElfMemImage::SymbolIterator::Update(int increment) { } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h index fcd32afe..46bfade3 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -23,6 +23,8 @@ // used. #include <climits> +#include "absl/base/config.h" + // Maybe one day we can rewrite this file not to require the elf // symbol extensions in glibc, but for right now we need them. #ifdef ABSL_HAVE_ELF_MEM_IMAGE @@ -39,7 +41,7 @@ #include <link.h> // for ElfW namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // An in-memory ELF image (may not exist on disk). @@ -124,7 +126,7 @@ class ElfMemImage { }; } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc index 4739fbc5..a3dd893a 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -30,7 +30,7 @@ #include "absl/debugging/symbolize.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // Returns the program counter from signal context, nullptr if @@ -53,6 +53,8 @@ void* GetProgramCounter(void* vuc) { return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]); #elif defined(__powerpc__) return reinterpret_cast<void*>(context->uc_mcontext.regs->nip); +#elif defined(__riscv) + return reinterpret_cast<void*>(context->uc_mcontext.__gregs[REG_PC]); #elif defined(__s390__) && !defined(__s390x__) return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff); #elif defined(__s390__) && defined(__s390x__) @@ -151,5 +153,5 @@ void DumpPCAndFrameSizesAndStackTrace( } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h index 861db75d..39336913 100644 --- a/absl/debugging/internal/examine_stack.h +++ b/absl/debugging/internal/examine_stack.h @@ -17,8 +17,10 @@ #ifndef ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ #define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // Returns the program counter from signal context, or nullptr if @@ -34,7 +36,7 @@ void DumpPCAndFrameSizesAndStackTrace( void (*writerfn)(const char*, void*), void* writerfn_arg); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc index 8dfd94aa..875ca6d9 100644 --- a/absl/debugging/internal/stack_consumption.cc +++ b/absl/debugging/internal/stack_consumption.cc @@ -27,7 +27,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { namespace { @@ -116,10 +116,11 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { // Set up the alt-signal-stack (and save the older one). stack_t sigstk; memset(&sigstk, 0, sizeof(sigstk)); - stack_t old_sigstk; sigstk.ss_sp = altstack; sigstk.ss_size = kAlternateStackSize; sigstk.ss_flags = 0; + stack_t old_sigstk; + memset(&old_sigstk, 0, sizeof(old_sigstk)); ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0, "sigaltstack() failed"); @@ -153,6 +154,15 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { int signal_handler_stack_consumption = GetStackConsumption(altstack); // Now restore the old alt-signal-stack and signal handlers. + if (old_sigstk.ss_sp == nullptr && old_sigstk.ss_size == 0 && + (old_sigstk.ss_flags & SS_DISABLE)) { + // https://git.musl-libc.org/cgit/musl/commit/src/signal/sigaltstack.c?id=7829f42a2c8944555439380498ab8b924d0f2070 + // The original stack has ss_size==0 and ss_flags==SS_DISABLE, but some + // versions of musl have a bug that rejects ss_size==0. Work around this by + // setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel + // when SS_DISABLE is set. + old_sigstk.ss_size = MINSIGSTKSZ; + } ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0, "sigaltstack() failed"); ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0, @@ -168,7 +178,7 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION diff --git a/absl/debugging/internal/stack_consumption.h b/absl/debugging/internal/stack_consumption.h index 1eb37eef..5e60ec42 100644 --- a/absl/debugging/internal/stack_consumption.h +++ b/absl/debugging/internal/stack_consumption.h @@ -18,6 +18,8 @@ #ifndef ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ #define ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ +#include "absl/base/config.h" + // The code in this module is not portable. // Use this feature test macro to detect its availability. #ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION @@ -27,7 +29,7 @@ #define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // Returns the stack consumption in bytes for the code exercised by @@ -39,7 +41,7 @@ namespace debugging_internal { int GetSignalHandlerStackConsumption(void (*signal_handler)(int)); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION diff --git a/absl/debugging/internal/stack_consumption_test.cc b/absl/debugging/internal/stack_consumption_test.cc index 1c82d18a..80445bf4 100644 --- a/absl/debugging/internal/stack_consumption_test.cc +++ b/absl/debugging/internal/stack_consumption_test.cc @@ -23,7 +23,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { namespace { @@ -44,7 +44,7 @@ TEST(SignalHandlerStackConsumptionTest, MeasuresStackConsumption) { } // namespace } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc index 14b6e6b0..411ea308 100644 --- a/absl/debugging/internal/stacktrace_aarch64-inl.inc +++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -180,13 +180,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ diff --git a/absl/debugging/internal/stacktrace_arm-inl.inc b/absl/debugging/internal/stacktrace_arm-inl.inc index 4f51d180..fffda968 100644 --- a/absl/debugging/internal/stacktrace_arm-inl.inc +++ b/absl/debugging/internal/stacktrace_arm-inl.inc @@ -113,13 +113,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ diff --git a/absl/debugging/internal/stacktrace_generic-inl.inc b/absl/debugging/internal/stacktrace_generic-inl.inc index 39c47866..ac034c9f 100644 --- a/absl/debugging/internal/stacktrace_generic-inl.inc +++ b/absl/debugging/internal/stacktrace_generic-inl.inc @@ -16,6 +16,7 @@ #include <cstring> #include "absl/debugging/stacktrace.h" +#include "absl/base/attributes.h" // Sometimes, we can try to get a stack trace from within a stack // trace, because we don't block signals inside this code (which would be too @@ -35,7 +36,7 @@ static __thread int recursive = 0; static std::atomic<bool> disable_stacktraces(true); // Disabled until healthy. // Waiting until static initializers run seems to be late enough. // This file is included into stacktrace.cc so this will only run once. -static int stacktraces_enabler = []() { +ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() { void* unused_stack[1]; // Force the first backtrace to happen early to get the one-time shared lib // loading (allocation) out of the way. After the first call it is much safer @@ -86,13 +87,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc index ee6b38ff..2e7c2f40 100644 --- a/absl/debugging/internal/stacktrace_powerpc-inl.inc +++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -236,13 +236,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ diff --git a/absl/debugging/internal/stacktrace_unimplemented-inl.inc b/absl/debugging/internal/stacktrace_unimplemented-inl.inc index b49a929a..5b8fb191 100644 --- a/absl/debugging/internal/stacktrace_unimplemented-inl.inc +++ b/absl/debugging/internal/stacktrace_unimplemented-inl.inc @@ -12,13 +12,13 @@ static int UnwindImpl(void** /* result */, int* /* sizes */, } namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_ diff --git a/absl/debugging/internal/stacktrace_win32-inl.inc b/absl/debugging/internal/stacktrace_win32-inl.inc index 0cd8c339..9c2c5580 100644 --- a/absl/debugging/internal/stacktrace_win32-inl.inc +++ b/absl/debugging/internal/stacktrace_win32-inl.inc @@ -54,7 +54,7 @@ static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, - const void *ucp, int *min_dropped_frames) { + const void*, int* min_dropped_frames) { int n = 0; if (!RtlCaptureStackBackTrace_fn) { // can't find a stacktrace with no function to call @@ -73,13 +73,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc index ff0fd31f..bc320ff7 100644 --- a/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/absl/debugging/internal/stacktrace_x86-inl.inc @@ -36,6 +36,8 @@ #include "absl/base/internal/raw_logging.h" +using absl::debugging_internal::AddressIsReadable; + #if defined(__linux__) && defined(__i386__) // Count "push %reg" instructions in VDSO __kernel_vsyscall(), // preceeding "syscall" or "sysenter". @@ -81,7 +83,7 @@ static int CountPushInstructions(const unsigned char *const addr) { // "mov reg,reg" if (addr[i + 1] == 0xE5) { // Found "mov %esp,%ebp". - return 0; + return 0; } ++i; // Skip register encoding byte. } else if (addr[i] == 0x0F && @@ -171,6 +173,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) { static const unsigned char *kernel_rt_sigreturn_address = nullptr; static const unsigned char *kernel_vsyscall_address = nullptr; if (num_push_instructions == -1) { +#ifdef ABSL_HAVE_VDSO_SUPPORT absl::debugging_internal::VDSOSupport vdso; if (vdso.IsPresent()) { absl::debugging_internal::VDSOSupport::SymbolInfo @@ -199,6 +202,9 @@ static void **NextStackFrame(void **old_fp, const void *uc) { } else { num_push_instructions = 0; } +#else // ABSL_HAVE_VDSO_SUPPORT + num_push_instructions = 0; +#endif // ABSL_HAVE_VDSO_SUPPORT } if (num_push_instructions != 0 && kernel_rt_sigreturn_address != nullptr && old_fp[1] == kernel_rt_sigreturn_address) { @@ -222,7 +228,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) { // "double fault" in case we hit the first fault due to e.g. stack // corruption. void *const reg_esp2 = reg_esp[num_push_instructions - 1]; - if (absl::debugging_internal::AddressIsReadable(reg_esp2)) { + if (AddressIsReadable(reg_esp2)) { // Alleged %esp is readable, use it for further unwinding. new_fp = reinterpret_cast<void **>(reg_esp2); } @@ -274,7 +280,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) { // Note: NextStackFrame<false>() is only called while the program // is already on its last leg, so it's ok to be slow here. - if (!absl::debugging_internal::AddressIsReadable(new_fp)) { + if (!AddressIsReadable(new_fp)) { return nullptr; } } @@ -328,13 +334,13 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_ diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h index 151bc77c..5d0858b5 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h @@ -21,6 +21,8 @@ #include <cstddef> #include <cstdint> +#include "absl/base/config.h" + #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set #elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \ @@ -33,7 +35,7 @@ #include <string> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // Iterates over all sections, invoking callback on each with the section name @@ -52,13 +54,13 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, ElfW(Shdr) *out); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { struct SymbolDecoratorArgs { @@ -120,7 +122,7 @@ bool GetFileMappingHint(const void** start, const char** filename); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index fa88e1d8..1e8a78ac 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -38,7 +38,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { ABSL_CONST_INIT @@ -188,7 +188,7 @@ static class VDSOInitHelper { } vdso_init_helper; } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HAVE_VDSO_SUPPORT diff --git a/absl/debugging/internal/vdso_support.h b/absl/debugging/internal/vdso_support.h index bc5fdb11..6562c6c2 100644 --- a/absl/debugging/internal/vdso_support.h +++ b/absl/debugging/internal/vdso_support.h @@ -53,7 +53,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { // NOTE: this class may be used from within tcmalloc, and can not @@ -150,7 +150,7 @@ class VDSOSupport { int GetCPU(); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc index 63e54ffa..ff904955 100644 --- a/absl/debugging/leak_check.cc +++ b/absl/debugging/leak_check.cc @@ -21,14 +21,14 @@ #ifndef LEAK_SANITIZER namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN bool HaveLeakSanitizer() { return false; } void DoIgnoreLeak(const void*) { } void RegisterLivePointers(const void*, size_t) { } void UnRegisterLivePointers(const void*, size_t) { } LeakCheckDisabler::LeakCheckDisabler() { } LeakCheckDisabler::~LeakCheckDisabler() { } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #else @@ -36,7 +36,7 @@ LeakCheckDisabler::~LeakCheckDisabler() { } #include <sanitizer/lsan_interface.h> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN bool HaveLeakSanitizer() { return true; } void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); } void RegisterLivePointers(const void* ptr, size_t size) { @@ -47,7 +47,7 @@ void UnRegisterLivePointers(const void* ptr, size_t size) { } LeakCheckDisabler::LeakCheckDisabler() { __lsan_disable(); } LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // LEAK_SANITIZER diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h index e1215e38..7a5a22dd 100644 --- a/absl/debugging/leak_check.h +++ b/absl/debugging/leak_check.h @@ -32,8 +32,10 @@ #include <cstddef> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // HaveLeakSanitizer() // @@ -105,7 +107,7 @@ void RegisterLivePointers(const void* ptr, size_t size); // `RegisterLivePointers()`, enabling leak checking of those pointers. void UnRegisterLivePointers(const void* ptr, size_t size); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_LEAK_CHECK_H_ diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc index 3052fb97..1f7c7d82 100644 --- a/absl/debugging/stacktrace.cc +++ b/absl/debugging/stacktrace.cc @@ -57,7 +57,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { typedef int (*Unwinder)(void**, int*, int, int, const void*, int*); @@ -136,5 +136,5 @@ int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip, return n; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/debugging/stacktrace.h b/absl/debugging/stacktrace.h index d7565aa4..0ec0ffda 100644 --- a/absl/debugging/stacktrace.h +++ b/absl/debugging/stacktrace.h @@ -31,16 +31,18 @@ #ifndef ABSL_DEBUGGING_STACKTRACE_H_ #define ABSL_DEBUGGING_STACKTRACE_H_ +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // GetStackFrames() // // Records program counter values for up to `max_depth` frames, skipping the -// most recent `skip_count` stack frames, and stores their corresponding values -// and sizes in `results` and `sizes` buffers. (Note that the frame generated -// for the `absl::GetStackFrames()` routine itself is also skipped.) -// routine itself. +// most recent `skip_count` stack frames, stores their corresponding values +// and sizes in `results` and `sizes` buffers, and returns the number of frames +// stored. (Note that the frame generated for the `absl::GetStackFrames()` +// routine itself is also skipped.) // // Example: // @@ -55,8 +57,8 @@ inline namespace lts_2019_08_08 { // The current stack frame would consist of three function calls: `bar()`, // `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets // `skip_count` to `1`, it will skip the frame for `bar()`, the most recently -// invoked function call. It will therefore return two program counters and will -// produce values that map to the following function calls: +// invoked function call. It will therefore return 2 and fill `result` with +// program counters within the following functions: // // result[0] foo() // result[1] main() @@ -83,9 +85,10 @@ extern int GetStackFrames(void** result, int* sizes, int max_depth, // // Records program counter values obtained from a signal handler. Records // program counter values for up to `max_depth` frames, skipping the most recent -// `skip_count` stack frames, and stores their corresponding values and sizes in -// `results` and `sizes` buffers. (Note that the frame generated for the -// `absl::GetStackFramesWithContext()` routine itself is also skipped.) +// `skip_count` stack frames, stores their corresponding values and sizes in +// `results` and `sizes` buffers, and returns the number of frames stored. (Note +// that the frame generated for the `absl::GetStackFramesWithContext()` routine +// itself is also skipped.) // // The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value // passed to a signal handler registered via the `sa_sigaction` field of a @@ -106,8 +109,9 @@ extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth, // GetStackTrace() // // Records program counter values for up to `max_depth` frames, skipping the -// most recent `skip_count` stack frames, and stores their corresponding values -// in `results`. Note that this function is similar to `absl::GetStackFrames()` +// most recent `skip_count` stack frames, stores their corresponding values +// in `results`, and returns the number of frames +// stored. Note that this function is similar to `absl::GetStackFrames()` // except that it returns the stack trace only, and not stack frame sizes. // // Example: @@ -132,9 +136,9 @@ extern int GetStackTrace(void** result, int max_depth, int skip_count); // // Records program counter values obtained from a signal handler. Records // program counter values for up to `max_depth` frames, skipping the most recent -// `skip_count` stack frames, and stores their corresponding values in -// `results`. (Note that the frame generated for the -// `absl::GetStackFramesWithContext()` routine itself is also skipped.) +// `skip_count` stack frames, stores their corresponding values in `results`, +// and returns the number of frames stored. (Note that the frame generated for +// the `absl::GetStackFramesWithContext()` routine itself is also skipped.) // // The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value // passed to a signal handler registered via the `sa_sigaction` field of a @@ -221,7 +225,7 @@ namespace debugging_internal { // working. extern bool StackTraceWorksForTest(); } // namespace debugging_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_STACKTRACE_H_ diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc index 24e3a7f0..54ed9700 100644 --- a/absl/debugging/symbolize.cc +++ b/absl/debugging/symbolize.cc @@ -16,12 +16,9 @@ #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) #include "absl/debugging/symbolize_elf.inc" -#elif defined(_WIN32) && defined(_DEBUG) -// The Windows Symbolizer only works in debug mode. Note that _DEBUG -// is the macro that defines whether or not MS C-Runtime debug info is -// available. Note that the PDB files containing the debug info must -// also be available to the program at runtime for the symbolizer to -// work. +#elif defined(_WIN32) +// The Windows Symbolizer only works if PDB files containing the debug info +// are available to the program at runtime. #include "absl/debugging/symbolize_win32.inc" #else #include "absl/debugging/symbolize_unimplemented.inc" diff --git a/absl/debugging/symbolize.h b/absl/debugging/symbolize.h index ab5447c4..43d93a86 100644 --- a/absl/debugging/symbolize.h +++ b/absl/debugging/symbolize.h @@ -55,7 +55,7 @@ #include "absl/debugging/internal/symbolize.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // InitializeSymbolizer() // @@ -71,7 +71,7 @@ inline namespace lts_2019_08_08 { // // Now you can use the symbolizer // } void InitializeSymbolizer(const char* argv0); - +// // Symbolize() // // Symbolizes a program counter (instruction pointer value) `pc` and, on @@ -89,11 +89,11 @@ void InitializeSymbolizer(const char* argv0); // if (absl::Symbolize(pc, tmp, sizeof(tmp))) { // symbol = tmp; // } -// absl::PrintF("%*p %s\n", pc, symbol); +// absl::PrintF("%p %s\n", pc, symbol); // } bool Symbolize(const void *pc, char *out, int out_size); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_DEBUGGING_SYMBOLIZE_H_ diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 2bd7659f..c371635f 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc @@ -76,7 +76,7 @@ #include "absl/debugging/internal/vdso_support.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Value of argv[0]. Used by MaybeInitializeObjFile(). static char *argv0_value = nullptr; @@ -625,6 +625,13 @@ static bool InSection(const void *address, const ElfW(Shdr) * section) { return start <= address && address < (start + size); } +static const char *ComputeOffset(const char *base, ptrdiff_t offset) { + // Note: cast to uintptr_t to avoid undefined behavior when base evaluates to + // zero and offset is non-zero. + return reinterpret_cast<const char *>( + reinterpret_cast<uintptr_t>(base) + offset); +} + // Read a symbol table and look for the symbol containing the // pc. Iterate over symbols in a symbol table and look for the symbol // containing "pc". If the symbol is found, and its name fits in @@ -677,7 +684,8 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( // We keep the original address for opd redirection below. const char *const original_start_address = reinterpret_cast<const char *>(symbol.st_value); - const char *start_address = original_start_address + relocation; + const char *start_address = + ComputeOffset(original_start_address, relocation); if (deref_function_descriptor_pointer && InSection(original_start_address, opd)) { @@ -689,8 +697,7 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( // If pc is inside the .opd section, it points to a function descriptor. const size_t size = pc_in_opd ? kFunctionDescriptorSize : symbol.st_size; - const void *const end_address = - reinterpret_cast<const char *>(start_address) + size; + const void *const end_address = ComputeOffset(start_address, size); if (symbol.st_value != 0 && // Skip null value symbols. symbol.st_shndx != 0 && // Skip undefined symbols. #ifdef STT_TLS @@ -1469,5 +1476,5 @@ bool Symbolize(const void *pc, char *out, int out_size) { return ok; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index 08068c30..a1d03aab 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -35,10 +35,29 @@ using testing::Contains; +#ifdef _WIN32 +#define ABSL_SYMBOLIZE_TEST_NOINLINE __declspec(noinline) +#else +#define ABSL_SYMBOLIZE_TEST_NOINLINE ABSL_ATTRIBUTE_NOINLINE +#endif + // Functions to symbolize. Use C linkage to avoid mangled names. extern "C" { -void nonstatic_func() { ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); } -static void static_func() { ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); } +ABSL_SYMBOLIZE_TEST_NOINLINE void nonstatic_func() { + // The next line makes this a unique function to prevent the compiler from + // folding identical functions together. + volatile int x = __LINE__; + static_cast<void>(x); + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); +} + +ABSL_SYMBOLIZE_TEST_NOINLINE static void static_func() { + // The next line makes this a unique function to prevent the compiler from + // folding identical functions together. + volatile int x = __LINE__; + static_cast<void>(x); + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); +} } // extern "C" struct Foo { @@ -46,7 +65,11 @@ struct Foo { }; // A C++ method that should have a mangled name. -void ABSL_ATTRIBUTE_NOINLINE Foo::func(int) { +ABSL_SYMBOLIZE_TEST_NOINLINE void Foo::func(int) { + // The next line makes this a unique function to prevent the compiler from + // folding identical functions together. + volatile int x = __LINE__; + static_cast<void>(x); ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); } @@ -80,6 +103,7 @@ static ABSL_PER_THREAD_TLS_KEYWORD char symbolize_test_thread_big[2 * 1024 * 1024]; #endif +#if !defined(__EMSCRIPTEN__) // Used below to hopefully inhibit some compiler/linker optimizations // that may remove kHpageTextPadding, kPadding0, and kPadding1 from // the binary. @@ -89,6 +113,7 @@ static volatile bool volatile_bool = false; static constexpr size_t kHpageSize = 1 << 21; const char kHpageTextPadding[kHpageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE( .text) = ""; +#endif // !defined(__EMSCRIPTEN__) static char try_symbolize_buffer[4096]; @@ -447,14 +472,15 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() { #endif } -#elif defined(_WIN32) && defined(_DEBUG) +#elif defined(_WIN32) +#if !defined(ABSL_CONSUME_DLL) TEST(Symbolize, Basics) { EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func))); // The name of an internal linkage symbol is not specified; allow either a // mangled or an unmangled name here. - const char* static_func_symbol = TrySymbolize((void *)(&static_func)); + const char *static_func_symbol = TrySymbolize((void *)(&static_func)); ASSERT_TRUE(static_func_symbol != nullptr); EXPECT_TRUE(strstr(static_func_symbol, "static_func") != nullptr); @@ -481,11 +507,12 @@ TEST(Symbolize, Truncation) { } TEST(Symbolize, SymbolizeWithDemangling) { - const char* result = TrySymbolize((void *)(&Foo::func)); + const char *result = TrySymbolize((void *)(&Foo::func)); ASSERT_TRUE(result != nullptr); EXPECT_TRUE(strstr(result, "Foo::func") != nullptr) << result; } +#endif // !defined(ABSL_CONSUME_DLL) #else // Symbolizer unimplemented TEST(Symbolize, Unimplemented) { @@ -498,10 +525,12 @@ TEST(Symbolize, Unimplemented) { #endif int main(int argc, char **argv) { +#if !defined(__EMSCRIPTEN__) // Make sure kHpageTextPadding is linked into the binary. if (volatile_bool) { ABSL_RAW_LOG(INFO, "%s", kHpageTextPadding); } +#endif // !defined(__EMSCRIPTEN__) #if ABSL_PER_THREAD_TLS // Touch the per-thread variables. diff --git a/absl/debugging/symbolize_unimplemented.inc b/absl/debugging/symbolize_unimplemented.inc index 0c1c1951..db24456b 100644 --- a/absl/debugging/symbolize_unimplemented.inc +++ b/absl/debugging/symbolize_unimplemented.inc @@ -17,7 +17,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace debugging_internal { @@ -36,5 +36,5 @@ bool GetFileMappingHint(const void **, const void **, uint64_t *, const char **) void InitializeSymbolizer(const char*) {} bool Symbolize(const void *, char *, int) { return false; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc index 498ca2db..c3df46f6 100644 --- a/absl/debugging/symbolize_win32.inc +++ b/absl/debugging/symbolize_win32.inc @@ -17,10 +17,10 @@ #include <windows.h> -// MSVC header DbgHelp.h has a warning for an ignored typedef. +// MSVC header dbghelp.h has a warning for an ignored typedef. #pragma warning(push) #pragma warning(disable:4091) -#include <DbgHelp.h> +#include <dbghelp.h> #pragma warning(pop) #pragma comment(lib, "dbghelp.lib") @@ -31,11 +31,11 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN static HANDLE process = NULL; -void InitializeSymbolizer(const char *argv0) { +void InitializeSymbolizer(const char*) { if (process != nullptr) { return; } @@ -54,13 +54,12 @@ void InitializeSymbolizer(const char *argv0) { } } -bool Symbolize(const void *pc, char *out, int out_size) { +bool Symbolize(const void* pc, char* out, int out_size) { if (out_size <= 0) { return false; } - std::aligned_storage<sizeof(SYMBOL_INFO) + MAX_SYM_NAME, - alignof(SYMBOL_INFO)>::type buf; - SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(&buf); + alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(buf); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; if (!SymFromAddr(process, reinterpret_cast<DWORD64>(pc), nullptr, symbol)) { @@ -78,5 +77,5 @@ bool Symbolize(const void *pc, char *out, int out_size) { return true; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 2fe61eaa..cdb4e7e8 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -1,5 +1,5 @@ # -# Copyright 2019 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. @@ -14,6 +14,7 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -26,12 +27,35 @@ package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 cc_library( - name = "internal", + name = "flag_internal", + srcs = [ + "internal/flag.cc", + ], + hdrs = [ + "internal/flag.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//absl/base:__subpackages__"], + deps = [ + ":config", + ":handle", + ":registry", + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/memory", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "program_name", srcs = [ "internal/program_name.cc", ], hdrs = [ - "internal/path_util.h", "internal/program_name.h", ], copts = ABSL_DEFAULT_COPTS, @@ -40,12 +64,31 @@ cc_library( "//absl/flags:__pkg__", ], deps = [ + ":path_util", + "//absl/base:config", + "//absl/base:core_headers", "//absl/strings", "//absl/synchronization", ], ) cc_library( + name = "path_util", + hdrs = [ + "internal/path_util.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + "//absl/base:config", + "//absl/strings", + ], +) + +cc_library( name = "config", srcs = [ "usage_config.cc", @@ -57,7 +100,9 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":internal", + ":path_util", + ":program_name", + "//absl/base:config", "//absl/base:core_headers", "//absl/strings", "//absl/synchronization", @@ -75,7 +120,9 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + "//absl/base:config", "//absl/base:core_headers", + "//absl/base:log_severity", "//absl/strings", "//absl/strings:str_format", ], @@ -83,9 +130,6 @@ cc_library( cc_library( name = "handle", - srcs = [ - "internal/commandlineflag.cc", - ], hdrs = [ "internal/commandlineflag.h", ], @@ -97,10 +141,9 @@ cc_library( deps = [ ":config", ":marshalling", - "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/strings", - "//absl/synchronization", "//absl/types:optional", ], ) @@ -123,9 +166,9 @@ cc_library( deps = [ ":config", ":handle", - "//absl/base", + "//absl/base:config", "//absl/base:core_headers", - "//absl/base:dynamic_annotations", + "//absl/base:raw_logging_internal", "//absl/strings", "//absl/synchronization", ], @@ -139,16 +182,17 @@ cc_library( hdrs = [ "declare.h", "flag.h", - "internal/flag.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", + ":flag_internal", ":handle", ":marshalling", ":registry", "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/strings", ], @@ -170,10 +214,14 @@ cc_library( deps = [ ":config", ":flag", + ":flag_internal", ":handle", - ":internal", + ":path_util", + ":program_name", + ":registry", + "//absl/base:config", + "//absl/base:core_headers", "//absl/strings", - "//absl/synchronization", ], ) @@ -189,6 +237,8 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":usage_internal", + "//absl/base:config", + "//absl/base:core_headers", "//absl/strings", "//absl/synchronization", ], @@ -206,18 +256,21 @@ cc_library( deps = [ ":config", ":flag", + ":flag_internal", ":handle", - ":internal", + ":program_name", ":registry", ":usage", ":usage_internal", + "//absl/base:config", + "//absl/base:core_headers", "//absl/strings", "//absl/synchronization", ], ) ############################################################################ -# Unit tests in alpahabetical order. +# Unit tests in alphabetical order. cc_test( name = "commandlineflag_test", @@ -228,6 +281,7 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":config", ":flag", ":handle", ":registry", @@ -261,12 +315,34 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":config", ":flag", + ":flag_internal", + ":handle", + ":registry", + "//absl/base:core_headers", "//absl/strings", "@com_google_googletest//:gtest_main", ], ) +cc_binary( + name = "flag_benchmark", + testonly = 1, + srcs = [ + "flag_benchmark.cc", + ], + copts = ABSL_TEST_COPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":flag", + "//absl/time", + "//absl/types:optional", + "@com_github_google_benchmark//:benchmark_main", + ], +) + cc_test( name = "marshalling_test", size = "small", @@ -290,7 +366,7 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":internal", + ":path_util", "@com_google_googletest//:gtest_main", ], ) @@ -306,7 +382,8 @@ cc_test( deps = [ ":flag", ":parse", - "//absl/base", + ":registry", + "//absl/base:raw_logging_internal", "//absl/base:scoped_set_env", "//absl/strings", "//absl/types:span", @@ -323,7 +400,7 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":internal", + ":program_name", "//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -339,9 +416,10 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":flag", + ":handle", + ":marshalling", ":registry", "//absl/memory", - "//absl/strings", "@com_google_googletest//:gtest_main", ], ) @@ -356,7 +434,8 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", - ":internal", + ":path_util", + ":program_name", "//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -373,8 +452,10 @@ cc_test( deps = [ ":config", ":flag", - ":internal", ":parse", + ":path_util", + ":program_name", + ":registry", ":usage", ":usage_internal", "//absl/memory", diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index fa1d4e17..1d25f0de 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -19,20 +19,60 @@ absl_cc_library( NAME flags_internal SRCS + "internal/flag.cc" + HDRS + "internal/flag.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::config + absl::flags_config + absl::flags_handle + absl::flags_registry + absl::synchronization + PUBLIC +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_program_name + SRCS "internal/program_name.cc" HDRS - "internal/path_util.h" "internal/program_name.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config + absl::core_headers + absl::flags_path_util absl::strings absl::synchronization PUBLIC ) +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_path_util + HDRS + "internal/path_util.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config + absl::strings + PUBLIC +) + absl_cc_library( NAME flags_config @@ -46,7 +86,9 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::flags_internal + absl::config + absl::flags_path_util + absl::flags_program_name absl::core_headers absl::strings absl::synchronization @@ -64,7 +106,9 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config absl::core_headers + absl::log_severity absl::strings absl::str_format ) @@ -73,8 +117,6 @@ absl_cc_library( absl_cc_library( NAME flags_handle - SRCS - "internal/commandlineflag.cc" HDRS "internal/commandlineflag.h" COPTS @@ -82,13 +124,14 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config absl::flags_config absl::flags_marshalling - absl::base absl::core_headers + absl::optional + absl::raw_logging_internal absl::strings absl::synchronization - absl::optional ) # Internal-only target, do not depend on directly. @@ -106,11 +149,11 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config absl::flags_config absl::flags_handle - absl::base absl::core_headers - absl::dynamic_annotations + absl::raw_logging_internal absl::strings absl::synchronization ) @@ -123,14 +166,15 @@ absl_cc_library( HDRS "declare.h" "flag.h" - "internal/flag.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config absl::flags_config absl::flags_handle + absl::flags_internal absl::flags_marshalling absl::flags_registry absl::base @@ -151,10 +195,14 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config absl::flags_config absl::flags absl::flags_handle absl::flags_internal + absl::flags_path_util + absl::flags_program_name + absl::flags_registry absl::strings absl::synchronization ) @@ -171,6 +219,8 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config + absl::core_headers absl::flags_usage_internal absl::strings absl::synchronization @@ -189,10 +239,13 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config + absl::core_headers absl::flags_config absl::flags absl::flags_handle absl::flags_internal + absl::flags_program_name absl::flags_registry absl::flags_usage absl::strings @@ -211,6 +264,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags + absl::flags_config absl::flags_handle absl::flags_registry absl::memory @@ -239,7 +293,12 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::core_headers absl::flags + absl::flags_config + absl::flags_handle + absl::flags_internal + absl::flags_registry absl::strings gtest_main ) @@ -265,8 +324,9 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags - absl::base absl::flags_parse + absl::flags_registry + absl::raw_logging_internal absl::scoped_set_env absl::span absl::strings @@ -281,7 +341,7 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS - absl::flags_internal + absl::flags_path_util gtest_main ) @@ -293,7 +353,7 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS - absl::flags_internal + absl::flags_program_name absl::strings gtest_main ) @@ -307,6 +367,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags + absl::flags_handle + absl::flags_marshalling absl::flags_registry absl::memory absl::strings @@ -322,7 +384,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags_config - absl::flags_internal + absl::flags_path_util + absl::flags_program_name absl::strings gtest_main ) @@ -337,8 +400,10 @@ absl_cc_test( DEPS absl::flags_config absl::flags - absl::flags_internal + absl::flags_path_util + absl::flags_program_name absl::flags_parse + absl::flags_registry absl::flags_usage absl::memory absl::strings diff --git a/absl/flags/config.h b/absl/flags/config.h index a9fd97ad..001f8fea 100644 --- a/absl/flags/config.h +++ b/absl/flags/config.h @@ -45,4 +45,23 @@ #define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES #endif +// ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD macro is used for using atomics with +// double words, e.g. absl::Duration. +// For reasons in bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878, modern +// versions of GCC do not support cmpxchg16b instruction in standard atomics. +#ifdef ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD +#error "ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD should not be defined." +#elif defined(__clang__) && defined(__x86_64__) && \ + defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) +#define ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD 1 +#endif + +// ABSL_FLAGS_INTERNAL_HAS_RTTI macro is used for selecting if we can use RTTI +// for flag type identification. +#ifdef ABSL_FLAGS_INTERNAL_HAS_RTTI +#error ABSL_FLAGS_INTERNAL_HAS_RTTI cannot be directly set +#elif !defined(__GNUC__) || defined(__GXX_RTTI) +#define ABSL_FLAGS_INTERNAL_HAS_RTTI 1 +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + #endif // ABSL_FLAGS_CONFIG_H_ diff --git a/absl/flags/declare.h b/absl/flags/declare.h index 7ef1d432..0f8cc6a5 100644 --- a/absl/flags/declare.h +++ b/absl/flags/declare.h @@ -25,10 +25,11 @@ #ifndef ABSL_FLAGS_DECLARE_H_ #define ABSL_FLAGS_DECLARE_H_ +#include "absl/base/config.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { // absl::Flag<T> represents a flag of type 'T' created by ABSL_FLAG. @@ -40,10 +41,15 @@ class Flag; // Flag // // Forward declaration of the `absl::Flag` type for use in defining the macro. +#if defined(_MSC_VER) && !defined(__clang__) +template <typename T> +class Flag; +#else template <typename T> using Flag = flags_internal::Flag<T>; +#endif -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // ABSL_DECLARE_FLAG() diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc index 0858259b..f7a457bf 100644 --- a/absl/flags/flag.cc +++ b/absl/flags/flag.cc @@ -15,32 +15,26 @@ #include "absl/flags/flag.h" -#include <cstring> +#include "absl/base/config.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/flag.h" namespace absl { -inline namespace lts_2019_08_08 { - -// We want to validate the type mismatch between type definition and -// declaration. The lock-free implementation does not allow us to do it, -// so in debug builds we always use the slower implementation, which always -// validates the type. -#ifndef NDEBUG -#define ABSL_FLAGS_ATOMIC_GET(T) \ - T GetFlag(const absl::Flag<T>& flag) { return flag.Get(); } -#else -#define ABSL_FLAGS_ATOMIC_GET(T) \ - T GetFlag(const absl::Flag<T>& flag) { \ - T result; \ - if (flag.AtomicGet(&result)) { \ - return result; \ - } \ - return flag.Get(); \ - } -#endif +ABSL_NAMESPACE_BEGIN + +// This global mutex protects on-demand construction of flag objects in MSVC +// builds. +#if defined(_MSC_VER) && !defined(__clang__) + +namespace flags_internal { -ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET) +ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit); -#undef ABSL_FLAGS_ATOMIC_GET +absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; } + +} // namespace flags_internal + +#endif -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 28925927..cff02c1f 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -29,16 +29,21 @@ #ifndef ABSL_FLAGS_FLAG_H_ #define ABSL_FLAGS_FLAG_H_ +#include <string> +#include <type_traits> + #include "absl/base/attributes.h" #include "absl/base/casts.h" +#include "absl/base/config.h" #include "absl/flags/config.h" #include "absl/flags/declare.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" +#include "absl/flags/internal/registry.h" #include "absl/flags/marshalling.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Flag // @@ -64,8 +69,103 @@ inline namespace lts_2019_08_08 { // ABSL_FLAG(int, count, 0, "Count of items to process"); // // No public methods of `absl::Flag<T>` are part of the Abseil Flags API. +#if !defined(_MSC_VER) || defined(__clang__) template <typename T> using Flag = flags_internal::Flag<T>; +#else +// MSVC debug builds do not implement initialization with constexpr constructors +// correctly. To work around this we add a level of indirection, so that the +// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias +// to that class) and dynamically allocates an instance when necessary. We also +// forward all calls to internal::Flag methods via trampoline methods. In this +// setup the `absl::Flag` class does not have constructor and virtual methods, +// all the data members are public and thus MSVC is able to initialize it at +// link time. To deal with multiple threads accessing the flag for the first +// time concurrently we use an atomic boolean indicating if flag object is +// initialized. We also employ the double-checked locking pattern where the +// second level of protection is a global Mutex, so if two threads attempt to +// construct the flag concurrently only one wins. +// This solution is based on a recomendation here: +// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454 + +namespace flags_internal { +absl::Mutex* GetGlobalConstructionGuard(); +} // namespace flags_internal + +template <typename T> +class Flag { + public: + // No constructor and destructor to ensure this is an aggregate type. + // Visual Studio 2015 still requires the constructor for class to be + // constexpr initializable. +#if _MSC_VER <= 1900 + constexpr Flag(const char* name, const char* filename, + const flags_internal::HelpGenFunc help_gen, + const flags_internal::FlagDfltGenFunc default_value_gen) + : name_(name), + filename_(filename), + help_gen_(help_gen), + default_value_gen_(default_value_gen), + inited_(false), + impl_(nullptr) {} +#endif + + flags_internal::Flag<T>* GetImpl() const { + if (!inited_.load(std::memory_order_acquire)) { + absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); + + if (inited_.load(std::memory_order_acquire)) { + return impl_; + } + + impl_ = + new flags_internal::Flag<T>(name_, filename_, + {flags_internal::FlagHelpMsg(help_gen_), + flags_internal::FlagHelpKind::kGenFunc}, + default_value_gen_); + inited_.store(true, std::memory_order_release); + } + + return impl_; + } + + // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API. + // See https://abseil.io/docs/cpp/guides/flags + bool IsRetired() const { return GetImpl()->IsRetired(); } + bool IsAbseilFlag() const { return GetImpl()->IsAbseilFlag(); } + absl::string_view Name() const { return GetImpl()->Name(); } + std::string Help() const { return GetImpl()->Help(); } + bool IsModified() const { return GetImpl()->IsModified(); } + bool IsSpecifiedOnCommandLine() const { + return GetImpl()->IsSpecifiedOnCommandLine(); + } + absl::string_view Typename() const { return GetImpl()->Typename(); } + std::string Filename() const { return GetImpl()->Filename(); } + std::string DefaultValue() const { return GetImpl()->DefaultValue(); } + std::string CurrentValue() const { return GetImpl()->CurrentValue(); } + template <typename U> + inline bool IsOfType() const { + return GetImpl()->template IsOfType<U>(); + } + T Get() const { return GetImpl()->Get(); } + bool AtomicGet(T* v) const { return GetImpl()->AtomicGet(v); } + void Set(const T& v) { GetImpl()->Set(v); } + void SetCallback(const flags_internal::FlagCallbackFunc mutation_callback) { + GetImpl()->SetCallback(mutation_callback); + } + void InvokeCallback() { GetImpl()->InvokeCallback(); } + + // The data members are logically private, but they need to be public for + // this to be an aggregate type. + const char* name_; + const char* filename_; + const flags_internal::HelpGenFunc help_gen_; + const flags_internal::FlagDfltGenFunc default_value_gen_; + + mutable std::atomic<bool> inited_; + mutable flags_internal::Flag<T>* impl_; +}; +#endif // GetFlag() // @@ -84,23 +184,10 @@ using Flag = flags_internal::Flag<T>; // // FLAGS_firstname is a Flag of type `std::string` // std::string first_name = absl::GetFlag(FLAGS_firstname); template <typename T> -T GetFlag(const absl::Flag<T>& flag) { -#define ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE(BIT) \ - static_assert( \ - !std::is_same<T, BIT>::value, \ - "Do not specify explicit template parameters to absl::GetFlag"); - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE) -#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE - +ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag) { return flag.Get(); } -// Overload for `GetFlag()` for types that support lock-free reads. -#define ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT(T) \ - extern T GetFlag(const absl::Flag<T>& flag); -ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT) -#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT - // SetFlag() // // Sets the value of an `absl::Flag` to the value `v`. Do not construct an @@ -122,7 +209,7 @@ void SetFlag(absl::Flag<T>* flag, const V& v) { flag->Set(value); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl @@ -186,13 +273,23 @@ void SetFlag(absl::Flag<T>* flag, const V& v) { #if ABSL_FLAGS_STRIP_NAMES #define ABSL_FLAG_IMPL_FLAGNAME(txt) "" #define ABSL_FLAG_IMPL_FILENAME() "" +#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ absl::flags_internal::FlagRegistrar<T, false>(&flag) #else +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar<T, false>(flag.GetImpl()) +#endif +#else #define ABSL_FLAG_IMPL_FLAGNAME(txt) txt #define ABSL_FLAG_IMPL_FILENAME() __FILE__ +#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ absl::flags_internal::FlagRegistrar<T, true>(&flag) +#else +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar<T, true>(flag.GetImpl()) +#endif #endif // ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP @@ -203,9 +300,19 @@ void SetFlag(absl::Flag<T>* flag, const V& v) { #define ABSL_FLAG_IMPL_FLAGHELP(txt) txt #endif -#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ - static std::string AbslFlagsWrapHelp##name() { \ - return ABSL_FLAG_IMPL_FLAGHELP(txt); \ +// AbslFlagHelpGenFor##name is used to encapsulate both immediate (method Const) +// and lazy (method NonConst) evaluation of help message expression. We choose +// between the two via the call to HelpArg in absl::Flag instantiation below. +// If help message expression is constexpr evaluable compiler will optimize +// away this whole struct. +#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ + struct AbslFlagHelpGenFor##name { \ + template <typename T = void> \ + static constexpr const char* Const() { \ + return absl::flags_internal::HelpConstexprWrap( \ + ABSL_FLAG_IMPL_FLAGHELP(txt)); \ + } \ + static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \ } #define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ @@ -218,17 +325,30 @@ void SetFlag(absl::Flag<T>* flag, const V& v) { // Note: Name of registrar object is not arbitrary. It is used to "grab" // global name for FLAGS_no<flag_name> symbol, thus preventing the possibility // of defining two flags with names foo and nofoo. +#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL(Type, name, default_value, help) \ namespace absl /* block flags in namespaces */ {} \ ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ - ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name( \ - ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagsWrapHelp##name, \ - ABSL_FLAG_IMPL_FILENAME(), \ - &absl::flags_internal::FlagMarshallingOps<Type>, \ - &AbslFlagsInitFlag##name); \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ + ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ + absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \ + &AbslFlagsInitFlag##name}; \ extern bool FLAGS_no##name; \ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) +#else +// MSVC version uses aggregate initialization. We also do not try to +// optimize away help wrapper. +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ + ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ + &AbslFlagHelpGenFor##name::NonConst, &AbslFlagsInitFlag##name}; \ + extern bool FLAGS_no##name; \ + bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) +#endif // ABSL_RETIRED_FLAG // diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc new file mode 100644 index 00000000..87f73170 --- /dev/null +++ b/absl/flags/flag_benchmark.cc @@ -0,0 +1,111 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/flag.h" +#include "absl/time/time.h" +#include "absl/types/optional.h" +#include "benchmark/benchmark.h" + +namespace { +using String = std::string; +using VectorOfStrings = std::vector<std::string>; +using AbslDuration = absl::Duration; + +// We do not want to take over marshalling for the types absl::optional<int>, +// absl::optional<std::string> which we do not own. Instead we introduce unique +// "aliases" to these types, which we do. +using AbslOptionalInt = absl::optional<int>; +struct OptionalInt : AbslOptionalInt { + using AbslOptionalInt::AbslOptionalInt; +}; +// Next two functions represent Abseil Flags marshalling for OptionalInt. +bool AbslParseFlag(absl::string_view src, OptionalInt* flag, + std::string* error) { + int val; + if (src.empty()) + flag->reset(); + else if (!absl::ParseFlag(src, &val, error)) + return false; + *flag = val; + return true; +} +std::string AbslUnparseFlag(const OptionalInt& flag) { + return !flag ? "" : absl::UnparseFlag(*flag); +} + +using AbslOptionalString = absl::optional<std::string>; +struct OptionalString : AbslOptionalString { + using AbslOptionalString::AbslOptionalString; +}; +// Next two functions represent Abseil Flags marshalling for OptionalString. +bool AbslParseFlag(absl::string_view src, OptionalString* flag, + std::string* error) { + std::string val; + if (src.empty()) + flag->reset(); + else if (!absl::ParseFlag(src, &val, error)) + return false; + *flag = val; + return true; +} +std::string AbslUnparseFlag(const OptionalString& flag) { + return !flag ? "" : absl::UnparseFlag(*flag); +} + +struct UDT { + UDT() = default; + UDT(const UDT&) {} + UDT& operator=(const UDT&) { return *this; } +}; +// Next two functions represent Abseil Flags marshalling for UDT. +bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } +std::string AbslUnparseFlag(const UDT&) { return ""; } + +} // namespace + +#define BENCHMARKED_TYPES(A) \ + A(bool) \ + A(int16_t) \ + A(uint16_t) \ + A(int32_t) \ + A(uint32_t) \ + A(int64_t) \ + A(uint64_t) \ + A(double) \ + A(float) \ + A(String) \ + A(VectorOfStrings) \ + A(OptionalInt) \ + A(OptionalString) \ + A(AbslDuration) \ + A(UDT) + +#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, ""); + +BENCHMARKED_TYPES(FLAG_DEF) + +namespace { + +#define BM_GetFlag(T) \ + void BM_GetFlag_##T(benchmark::State& state) { \ + for (auto _ : state) { \ + benchmark::DoNotOptimize(absl::GetFlag(FLAGS_##T##_flag)); \ + } \ + } \ + BENCHMARK(BM_GetFlag_##T); + +BENCHMARKED_TYPES(BM_GetFlag) + +} // namespace diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 1bcd7e96..4984d284 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -15,11 +15,25 @@ #include "absl/flags/flag.h" +#include <stdint.h> + +#include <cmath> +#include <string> +#include <vector> + #include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/flags/config.h" +#include "absl/flags/declare.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/flag.h" +#include "absl/flags/internal/registry.h" +#include "absl/flags/usage_config.h" #include "absl/strings/match.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag); ABSL_DECLARE_FLAG(std::vector<std::string>, mistyped_string_flag); @@ -28,7 +42,7 @@ namespace { namespace flags = absl::flags_internal; -std::string TestHelpMsg() { return "help"; } +std::string TestHelpMsg() { return "dynamic help"; } template <typename T> void* TestMakeDflt() { return new T{}; @@ -37,20 +51,21 @@ void TestCallback() {} template <typename T> bool TestConstructionFor() { - constexpr flags::Flag<T> f1("f1", &TestHelpMsg, "file", - &absl::flags_internal::FlagMarshallingOps<T>, - &TestMakeDflt<T>); + constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"), + flags::FlagHelpKind::kLiteral}; + constexpr flags::Flag<T> f1("f1", "file", help_arg, &TestMakeDflt<T>); EXPECT_EQ(f1.Name(), "f1"); - EXPECT_EQ(f1.Help(), "help"); + EXPECT_EQ(f1.Help(), "literal help"); EXPECT_EQ(f1.Filename(), "file"); ABSL_CONST_INIT static flags::Flag<T> f2( - "f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps<T>, + "f2", "file", + {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, &TestMakeDflt<T>); flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback); EXPECT_EQ(f2.Name(), "f2"); - EXPECT_EQ(f2.Help(), "help"); + EXPECT_EQ(f2.Help(), "dynamic help"); EXPECT_EQ(f2.Filename(), "file"); return true; @@ -63,7 +78,27 @@ struct UDT { bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } std::string AbslUnparseFlag(const UDT&) { return ""; } -TEST(FlagTest, TestConstruction) { +class FlagTest : public testing::Test { + protected: + static void SetUpTestSuite() { + // Install a function to normalize filenames before this test is run. + absl::FlagsUsageConfig default_config; + default_config.normalize_filename = &FlagTest::NormalizeFileName; + absl::SetFlagsUsageConfig(default_config); + } + + private: + static std::string NormalizeFileName(absl::string_view fname) { +#ifdef _WIN32 + std::string normalized(fname); + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + fname = normalized; +#endif + return std::string(fname); + } +}; + +TEST_F(FlagTest, TestConstruction) { TestConstructionFor<bool>(); TestConstructionFor<int16_t>(); TestConstructionFor<uint16_t>(); @@ -98,7 +133,7 @@ namespace { #if !ABSL_FLAGS_STRIP_NAMES -TEST(FlagTest, TestFlagDeclaration) { +TEST_F(FlagTest, TestFlagDeclaration) { // test that we can access flag objects. EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); @@ -133,69 +168,69 @@ ABSL_FLAG(std::string, test_flag_11, "", "test flag 11"); namespace { #if !ABSL_FLAGS_STRIP_NAMES -TEST(FlagTest, TestFlagDefinition) { +TEST_F(FlagTest, TestFlagDefinition) { absl::string_view expected_file_name = "absl/flags/flag_test.cc"; EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); EXPECT_EQ(FLAGS_test_flag_01.Help(), "test flag 01"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name)) + << FLAGS_test_flag_01.Filename(); EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); EXPECT_EQ(FLAGS_test_flag_02.Help(), "test flag 02"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name)) + << FLAGS_test_flag_02.Filename(); EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03"); EXPECT_EQ(FLAGS_test_flag_03.Help(), "test flag 03"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name)) + << FLAGS_test_flag_03.Filename(); EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04"); EXPECT_EQ(FLAGS_test_flag_04.Help(), "test flag 04"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name)) + << FLAGS_test_flag_04.Filename(); EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05"); EXPECT_EQ(FLAGS_test_flag_05.Help(), "test flag 05"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name)) + << FLAGS_test_flag_05.Filename(); EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06"); EXPECT_EQ(FLAGS_test_flag_06.Help(), "test flag 06"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name)) + << FLAGS_test_flag_06.Filename(); EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07"); EXPECT_EQ(FLAGS_test_flag_07.Help(), "test flag 07"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name)) + << FLAGS_test_flag_07.Filename(); EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08"); EXPECT_EQ(FLAGS_test_flag_08.Help(), "test flag 08"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name)) + << FLAGS_test_flag_08.Filename(); EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); EXPECT_EQ(FLAGS_test_flag_09.Help(), "test flag 09"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name)) + << FLAGS_test_flag_09.Filename(); EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); EXPECT_EQ(FLAGS_test_flag_10.Help(), "test flag 10"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name)) + << FLAGS_test_flag_10.Filename(); EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); EXPECT_EQ(FLAGS_test_flag_11.Help(), "test flag 11"); - EXPECT_TRUE( - absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name)); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name)) + << FLAGS_test_flag_11.Filename(); } #endif // !ABSL_FLAGS_STRIP_NAMES // -------------------------------------------------------------------- -TEST(FlagTest, TestDefault) { +TEST_F(FlagTest, TestDefault) { EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34); @@ -211,7 +246,7 @@ TEST(FlagTest, TestDefault) { // -------------------------------------------------------------------- -TEST(FlagTest, TestGetSet) { +TEST_F(FlagTest, TestGetSet) { absl::SetFlag(&FLAGS_test_flag_01, false); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), false); @@ -258,7 +293,7 @@ ABSL_FLAG(std::string, test_flag_13, absl::StrCat("AAA", "BBB"), namespace { -TEST(FlagTest, TestNonConstexprDefault) { +TEST_F(FlagTest, TestNonConstexprDefault) { EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), 1); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), "AAABBB"); } @@ -272,7 +307,7 @@ ABSL_FLAG(bool, test_flag_14, true, absl::StrCat("test ", "flag ", "14")); namespace { #if !ABSL_FLAGS_STRIP_HELP -TEST(FlagTest, TestNonConstexprHelp) { +TEST_F(FlagTest, TestNonConstexprHelp) { EXPECT_EQ(FLAGS_test_flag_14.Help(), "test flag 14"); } #endif //! ABSL_FLAGS_STRIP_HELP @@ -296,7 +331,7 @@ namespace { void TestFlagCB() { cb_test_value = absl::GetFlag(FLAGS_test_flag_with_cb); } // Tests side-effects of callback invocation. -TEST(FlagTest, CallbackInvocation) { +TEST_F(FlagTest, CallbackInvocation) { EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_cb), 100); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_lambda_cb), 200); EXPECT_EQ(cb_test_value, 300); @@ -343,7 +378,7 @@ ABSL_FLAG(CustomUDT, test_flag_15, CustomUDT(), "test flag 15"); namespace { -TEST(FlagTest, TestCustomUDT) { +TEST_F(FlagTest, TestCustomUDT) { EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(1, 1)); absl::SetFlag(&FLAGS_test_flag_15, CustomUDT(2, 3)); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(2, 3)); @@ -351,19 +386,20 @@ TEST(FlagTest, TestCustomUDT) { // MSVC produces link error on the type mismatch. // Linux does not have build errors and validations work as expected. -#if 0 // !defined(_WIN32) && GTEST_HAS_DEATH_TEST +#if !defined(_WIN32) && GTEST_HAS_DEATH_TEST + +using FlagDeathTest = FlagTest; -TEST(Flagtest, TestTypeMismatchValidations) { - // For builtin types, GetFlag() only does validation in debug mode. +TEST_F(FlagDeathTest, TestTypeMismatchValidations) { EXPECT_DEBUG_DEATH( - absl::GetFlag(FLAGS_mistyped_int_flag), + static_cast<void>(absl::GetFlag(FLAGS_mistyped_int_flag)), "Flag 'mistyped_int_flag' is defined as one type and declared " "as another"); - EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 0), + EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 1), "Flag 'mistyped_int_flag' is defined as one type and declared " "as another"); - EXPECT_DEATH(absl::GetFlag(FLAGS_mistyped_string_flag), + EXPECT_DEATH(static_cast<void>(absl::GetFlag(FLAGS_mistyped_string_flag)), "Flag 'mistyped_string_flag' is defined as one type and " "declared as another"); EXPECT_DEATH( @@ -409,7 +445,7 @@ ABSL_FLAG(ConversionTestVal, test_flag_16, namespace { -TEST(FlagTest, CanSetViaImplicitConversion) { +TEST_F(FlagTest, CanSetViaImplicitConversion) { EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 10); absl::SetFlag(&FLAGS_test_flag_16, ConversionTestVal::ViaImplicitConv::kEleven); @@ -447,7 +483,7 @@ ABSL_FLAG(NonDfltConstructible, ndc_flag2, 0, namespace { -TEST(FlagTest, TestNonDefaultConstructibleType) { +TEST_F(FlagTest, TestNonDefaultConstructibleType) { EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, '1' + 100); EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 0); @@ -468,7 +504,7 @@ ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); namespace { -TEST(FlagTest, TestRetiredFlagRegistration) { +TEST_F(FlagTest, TestRetiredFlagRegistration) { bool is_bool = false; EXPECT_TRUE(flags::IsRetiredFlag("old_bool_flag", &is_bool)); EXPECT_TRUE(is_bool); diff --git a/absl/flags/flag_test_defs.cc b/absl/flags/flag_test_defs.cc index 3366c580..49f91dee 100644 --- a/absl/flags/flag_test_defs.cc +++ b/absl/flags/flag_test_defs.cc @@ -20,3 +20,5 @@ ABSL_FLAG(int, mistyped_int_flag, 0, ""); ABSL_FLAG(std::string, mistyped_string_flag, "", ""); +ABSL_RETIRED_FLAG(bool, old_bool_flag, true, + "repetition of retired flag definition"); diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc deleted file mode 100644 index f964165e..00000000 --- a/absl/flags/internal/commandlineflag.cc +++ /dev/null @@ -1,496 +0,0 @@ -// -// 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/flags/internal/commandlineflag.h" - -#include <cassert> - -#include "absl/base/internal/raw_logging.h" -#include "absl/base/optimization.h" -#include "absl/flags/config.h" -#include "absl/flags/usage_config.h" -#include "absl/strings/str_cat.h" -#include "absl/synchronization/mutex.h" - -namespace absl { -inline namespace lts_2019_08_08 { -namespace flags_internal { - -// The help message indicating that the commandline flag has been -// 'stripped'. It will not show up when doing "-help" and its -// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1 -// before including absl/flags/flag.h - -// This is used by this file, and also in commandlineflags_reporting.cc -const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; - -namespace { - -// Currently we only validate flag values for user-defined flag types. -bool ShouldValidateFlagValue(const CommandLineFlag& flag) { -#define DONT_VALIDATE(T) \ - if (flag.IsOfType<T>()) return false; - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE) - DONT_VALIDATE(std::string) - DONT_VALIDATE(std::vector<std::string>) -#undef DONT_VALIDATE - - return true; -} - -} // namespace - -absl::Mutex* InitFlag(CommandLineFlag* flag) { - ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); - absl::Mutex* mu; - - { - absl::MutexLock lock(&init_lock); - - if (flag->locks == nullptr) { // Must initialize Mutexes for this flag. - flag->locks = new flags_internal::CommandLineFlagLocks; - } - - mu = &flag->locks->primary_mu; - } - - { - absl::MutexLock lock(mu); - - if (!flag->retired && flag->def == nullptr) { - // Need to initialize def and cur fields. - flag->def = (*flag->make_init_value)(); - flag->cur = Clone(flag->op, flag->def); - UpdateCopy(flag); - flag->inited.store(true, std::memory_order_release); - flag->InvokeCallback(); - } - } - - flag->inited.store(true, std::memory_order_release); - return mu; -} - -// Ensure that the lazily initialized fields of *flag have been initialized, -// and return &flag->locks->primary_mu. -absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const - LOCK_RETURNED(locks->primary_mu) { - if (!this->inited.load(std::memory_order_acquire)) { - return InitFlag(const_cast<CommandLineFlag*>(this)); - } - - // All fields initialized; this->locks is therefore safe to read. - return &this->locks->primary_mu; -} - -void CommandLineFlag::Destroy() const { - // Values are heap allocated for retired and Abseil Flags. - if (IsRetired() || IsAbseilFlag()) { - if (this->cur) Delete(this->op, this->cur); - if (this->def) Delete(this->op, this->def); - } - - delete this->locks; -} - -bool CommandLineFlag::IsModified() const { - absl::MutexLock l(InitFlagIfNecessary()); - return modified; -} - -void CommandLineFlag::SetModified(bool is_modified) { - absl::MutexLock l(InitFlagIfNecessary()); - modified = is_modified; -} - -bool CommandLineFlag::IsSpecifiedOnCommandLine() const { - absl::MutexLock l(InitFlagIfNecessary()); - return on_command_line; -} - -absl::string_view CommandLineFlag::Typename() const { - // We do not store/report type in Abseil Flags, so that user do not rely on in - // at runtime - if (IsAbseilFlag() || IsRetired()) return ""; - -#define HANDLE_V1_BUILTIN_TYPE(t) \ - if (IsOfType<t>()) { \ - return #t; \ - } - - HANDLE_V1_BUILTIN_TYPE(bool); - HANDLE_V1_BUILTIN_TYPE(int32_t); - HANDLE_V1_BUILTIN_TYPE(int64_t); - HANDLE_V1_BUILTIN_TYPE(uint64_t); - HANDLE_V1_BUILTIN_TYPE(double); -#undef HANDLE_V1_BUILTIN_TYPE - - if (IsOfType<std::string>()) { - return "string"; - } - - return ""; -} - -std::string CommandLineFlag::Filename() const { - return flags_internal::GetUsageConfig().normalize_filename(this->filename); -} - -std::string CommandLineFlag::DefaultValue() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return Unparse(this->marshalling_op, this->def); -} - -std::string CommandLineFlag::CurrentValue() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return Unparse(this->marshalling_op, this->cur); -} - -bool CommandLineFlag::HasValidatorFn() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return this->validator != nullptr; -} - -bool CommandLineFlag::SetValidatorFn(FlagValidator fn) { - absl::MutexLock l(InitFlagIfNecessary()); - - // ok to register the same function over and over again - if (fn == this->validator) return true; - - // Can't set validator to a different function, unless reset first. - if (fn != nullptr && this->validator != nullptr) { - ABSL_INTERNAL_LOG( - WARNING, absl::StrCat("Ignoring SetValidatorFn() for flag '", Name(), - "': validate-fn already registered")); - - return false; - } - - this->validator = fn; - return true; -} - -bool CommandLineFlag::InvokeValidator(const void* value) const - EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) { - if (!this->validator) { - return true; - } - - (void)value; - - ABSL_INTERNAL_LOG( - FATAL, - absl::StrCat("Flag '", Name(), - "' of encapsulated type should not have a validator")); - - return false; -} - -void CommandLineFlag::SetCallback( - const flags_internal::FlagCallback mutation_callback) { - absl::MutexLock l(InitFlagIfNecessary()); - - callback = mutation_callback; - - InvokeCallback(); -} - -// If the flag has a mutation callback this function invokes it. While the -// callback is being invoked the primary flag's mutex is unlocked and it is -// re-locked back after call to callback is completed. Callback invocation is -// guarded by flag's secondary mutex instead which prevents concurrent callback -// invocation. Note that it is possible for other thread to grab the primary -// lock and update flag's value at any time during the callback invocation. -// This is by design. Callback can get a value of the flag if necessary, but it -// might be different from the value initiated the callback and it also can be -// different by the time the callback invocation is completed. -// Requires that *primary_lock be held in exclusive mode; it may be released -// and reacquired by the implementation. -void CommandLineFlag::InvokeCallback() - EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) { - if (!this->callback) return; - - // The callback lock is guaranteed initialized, because *locks->primary_mu - // exists. - absl::Mutex* callback_mu = &this->locks->callback_mu; - - // When executing the callback we need the primary flag's mutex to be unlocked - // so that callback can retrieve the flag's value. - this->locks->primary_mu.Unlock(); - - { - absl::MutexLock lock(callback_mu); - this->callback(); - } - - this->locks->primary_mu.Lock(); -} - -// Attempts to parse supplied `value` string using parsing routine in the `flag` -// argument. If parsing is successful, it will try to validate that the parsed -// value is valid for the specified 'flag'. Finally this function stores the -// parsed value in 'dst' assuming it is a pointer to the flag's value type. In -// case if any error is encountered in either step, the error message is stored -// in 'err' -bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value, - std::string* err) - EXCLUSIVE_LOCKS_REQUIRED(flag->locks->primary_mu) { - void* tentative_value = Clone(flag->op, flag->def); - std::string parse_err; - if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) { - auto type_name = flag->Typename(); - absl::string_view err_sep = parse_err.empty() ? "" : "; "; - absl::string_view typename_sep = type_name.empty() ? "" : " "; - *err = absl::StrCat("Illegal value '", value, "' specified for", - typename_sep, type_name, " flag '", flag->Name(), "'", - err_sep, parse_err); - Delete(flag->op, tentative_value); - return false; - } - - if (!flag->InvokeValidator(tentative_value)) { - *err = absl::StrCat("Failed validation of new value '", - Unparse(flag->marshalling_op, tentative_value), - "' for flag '", flag->Name(), "'"); - Delete(flag->op, tentative_value); - return false; - } - - flag->counter++; - Copy(flag->op, tentative_value, dst); - Delete(flag->op, tentative_value); - return true; -} - -// Sets the value of the flag based on specified string `value`. If the flag -// was successfully set to new value, it returns true. Otherwise, sets `err` -// to indicate the error, leaves the flag unchanged, and returns false. There -// are three ways to set the flag's value: -// * Update the current flag value -// * Update the flag's default value -// * Update the current flag value if it was never set before -// The mode is selected based on 'set_mode' parameter. -bool CommandLineFlag::SetFromString(absl::string_view value, - FlagSettingMode set_mode, - ValueSource source, std::string* err) { - if (IsRetired()) return false; - - absl::MutexLock l(InitFlagIfNecessary()); - - // Direct-access flags can be modified without going through the - // flag API. Detect such changes and update the flag->modified bit. - if (!IsAbseilFlag()) { - if (!this->modified && ChangedDirectly(this, this->cur, this->def)) { - this->modified = true; - } - } - - switch (set_mode) { - case SET_FLAGS_VALUE: { - // set or modify the flag's value - if (!TryParseLocked(this, this->cur, value, err)) return false; - this->modified = true; - UpdateCopy(this); - InvokeCallback(); - - if (source == kCommandLine) { - this->on_command_line = true; - } - break; - } - case SET_FLAG_IF_DEFAULT: { - // set the flag's value, but only if it hasn't been set by someone else - if (!this->modified) { - if (!TryParseLocked(this, this->cur, value, err)) return false; - this->modified = true; - UpdateCopy(this); - InvokeCallback(); - } else { - // TODO(rogeeff): review and fix this semantic. Currently we do not fail - // in this case if flag is modified. This is misleading since the flag's - // value is not updated even though we return true. - // *err = absl::StrCat(this->Name(), " is already set to ", - // CurrentValue(), "\n"); - // return false; - return true; - } - break; - } - case SET_FLAGS_DEFAULT: { - // modify the flag's default-value - if (!TryParseLocked(this, this->def, value, err)) return false; - - if (!this->modified) { - // Need to set both defvalue *and* current, in this case - Copy(this->op, this->def, this->cur); - UpdateCopy(this); - InvokeCallback(); - } - break; - } - default: { - // unknown set_mode - assert(false); - return false; - } - } - - return true; -} - -void CommandLineFlag::StoreAtomic(size_t size) { - int64_t t = 0; - assert(size <= sizeof(int64_t)); - memcpy(&t, this->cur, size); - this->atomic.store(t, std::memory_order_release); -} - -void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const { - std::string v = DefaultValue(); - - absl::MutexLock lock(InitFlagIfNecessary()); - - void* dst = Clone(this->op, this->def); - std::string error; - if (!flags_internal::Parse(this->marshalling_op, v, dst, &error)) { - ABSL_INTERNAL_LOG( - FATAL, - absl::StrCat("Flag ", Name(), " (from ", Filename(), - "): std::string form of default value '", v, - "' could not be parsed; error=", error)); - } - - // We do not compare dst to def since parsing/unparsing may make - // small changes, e.g., precision loss for floating point types. - Delete(this->op, dst); -} - -bool CommandLineFlag::ValidateDefaultValue() const { - absl::MutexLock lock(InitFlagIfNecessary()); - return InvokeValidator(this->def); -} - -bool CommandLineFlag::ValidateInputValue(absl::string_view value) const { - absl::MutexLock l(InitFlagIfNecessary()); // protect default value access - - void* obj = Clone(this->op, this->def); - std::string ignored_error; - const bool result = - flags_internal::Parse(this->marshalling_op, value, obj, &ignored_error) && - InvokeValidator(obj); - Delete(this->op, obj); - return result; -} - -const int64_t CommandLineFlag::kAtomicInit; - -void CommandLineFlag::Read(void* dst, - const flags_internal::FlagOpFn dst_op) const { - absl::ReaderMutexLock l(InitFlagIfNecessary()); - - // `dst_op` is the unmarshaling operation corresponding to the declaration - // visibile at the call site. `op` is the Flag's defined unmarshalling - // operation. They must match for this operation to be well-defined. - if (ABSL_PREDICT_FALSE(dst_op != op)) { - ABSL_INTERNAL_LOG( - ERROR, - absl::StrCat("Flag '", name, - "' is defined as one type and declared as another")); - } - CopyConstruct(op, cur, dst); -} - -void CommandLineFlag::Write(const void* src, - const flags_internal::FlagOpFn src_op) { - absl::MutexLock l(InitFlagIfNecessary()); - - // `src_op` is the marshalling operation corresponding to the declaration - // visible at the call site. `op` is the Flag's defined marshalling operation. - // They must match for this operation to be well-defined. - if (ABSL_PREDICT_FALSE(src_op != op)) { - ABSL_INTERNAL_LOG( - ERROR, - absl::StrCat("Flag '", name, - "' is defined as one type and declared as another")); - } - - if (ShouldValidateFlagValue(*this)) { - void* obj = Clone(op, src); - std::string ignored_error; - std::string src_as_str = Unparse(marshalling_op, src); - if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) || - !InvokeValidator(obj)) { - ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name, - "' to invalid value ", src_as_str)); - } - Delete(op, obj); - } - - modified = true; - counter++; - Copy(op, src, cur); - - UpdateCopy(this); - InvokeCallback(); -} - -std::string HelpText::GetHelpText() const { - if (help_function_) return help_function_(); - if (help_message_) return help_message_; - - return {}; -} - -// Update any copy of the flag value that is stored in an atomic word. -// In addition if flag has a mutation callback this function invokes it. -void UpdateCopy(CommandLineFlag* flag) { -#define STORE_ATOMIC(T) \ - else if (flag->IsOfType<T>()) { \ - flag->StoreAtomic(sizeof(T)); \ - } - - if (false) { - } - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC) -#undef STORE_ATOMIC -} - -// Return true iff flag value was changed via direct-access. -bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) { - if (!flag->IsAbseilFlag()) { -// Need to compare values for direct-access flags. -#define CHANGED_FOR_TYPE(T) \ - if (flag->IsOfType<T>()) { \ - return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \ - } - - CHANGED_FOR_TYPE(bool); - CHANGED_FOR_TYPE(int32_t); - CHANGED_FOR_TYPE(int64_t); - CHANGED_FOR_TYPE(uint64_t); - CHANGED_FOR_TYPE(double); - CHANGED_FOR_TYPE(std::string); -#undef CHANGED_FOR_TYPE - } - - return false; -} - -} // namespace flags_internal -} // inline namespace lts_2019_08_08 -} // namespace absl diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index 382553d2..6363c661 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h @@ -16,30 +16,41 @@ #ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ -#include <atomic> +#include <stddef.h> +#include <stdint.h> +#include <memory> +#include <string> +#include <typeinfo> + +#include "absl/base/config.h" #include "absl/base/macros.h" +#include "absl/flags/config.h" #include "absl/flags/marshalling.h" -#include "absl/synchronization/mutex.h" +#include "absl/strings/string_view.h" #include "absl/types/optional.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { -// Type-specific operations, eg., parsing, copying, etc. are provided -// by function specific to that type with a signature matching FlagOpFn. -enum FlagOp { - kDelete, - kClone, - kCopy, - kCopyConstruct, - kSizeof, - kParse, - kUnparse -}; -using FlagOpFn = void* (*)(FlagOp, const void*, void*); -using FlagMarshallingOpFn = void* (*)(FlagOp, const void*, void*, void*); +// An alias for flag static type id. Values of type identify the flag value type +// simialarly to typeid(T), but without relying on RTTI being available. In most +// cases this id is enough to uniquely identify the flag's value type. In a few +// cases we'll have to resort to using actual RTTI implementation if it is +// available. +using FlagStaticTypeId = void* (*)(); + +// Address of this function template is used in current implementation as a flag +// static type id. +template <typename T> +void* FlagStaticTypeIdGen() { +#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) + return const_cast<std::type_info*>(&typeid(T)); +#else + return nullptr; +#endif +} // Options that control SetCommandLineOptionWithMode. enum FlagSettingMode { @@ -62,215 +73,95 @@ enum ValueSource { kProgrammaticChange, }; -// Signature for the help generation function used as an argument for the -// absl::Flag constructor. -using HelpGenFunc = std::string (*)(); - -// Signature for the function generating the initial flag value based (usually -// based on default value supplied in flag's definition) -using InitialValGenFunc = void* (*)(); - -struct CommandLineFlagInfo; - -// Signature for the mutation callback used by watched Flags -// The callback is noexcept. -// TODO(rogeeff): add noexcept after C++17 support is added. -using FlagCallback = void (*)(); - -using FlagValidator = bool (*)(); - -extern const char kStrippedFlagHelp[]; - -// The per-type function -template <typename T> -void* FlagOps(FlagOp op, const void* v1, void* v2) { - switch (op) { - case kDelete: - delete static_cast<const T*>(v1); - return nullptr; - case kClone: - return new T(*static_cast<const T*>(v1)); - case kCopy: - *static_cast<T*>(v2) = *static_cast<const T*>(v1); - return nullptr; - case kCopyConstruct: - new (v2) T(*static_cast<const T*>(v1)); - return nullptr; - case kSizeof: - return reinterpret_cast<void*>(sizeof(T)); - default: - return nullptr; - } -} - -template <typename T> -void* FlagMarshallingOps(FlagOp op, const void* v1, void* v2, void* v3) { - switch (op) { - case kParse: { - // initialize the temporary instance of type T based on current value in - // destination (which is going to be flag's default value). - T temp(*static_cast<T*>(v2)); - if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, - static_cast<std::string*>(v3))) { - return nullptr; - } - *static_cast<T*>(v2) = std::move(temp); - return v2; - } - case kUnparse: - *static_cast<std::string*>(v2) = - absl::UnparseFlag<T>(*static_cast<const T*>(v1)); - return nullptr; - default: - return nullptr; - } -} - -// Functions that invoke flag-type-specific operations. -inline void Delete(FlagOpFn op, const void* obj) { - op(flags_internal::kDelete, obj, nullptr); -} - -inline void* Clone(FlagOpFn op, const void* obj) { - return op(flags_internal::kClone, obj, nullptr); -} - -inline void Copy(FlagOpFn op, const void* src, void* dst) { - op(flags_internal::kCopy, src, dst); -} - -inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) { - op(flags_internal::kCopyConstruct, src, dst); -} - -inline bool Parse(FlagMarshallingOpFn op, absl::string_view text, void* dst, - std::string* error) { - return op(flags_internal::kParse, &text, dst, error) != nullptr; -} - -inline std::string Unparse(FlagMarshallingOpFn op, const void* val) { - std::string result; - op(flags_internal::kUnparse, val, &result, nullptr); - return result; -} - -inline size_t Sizeof(FlagOpFn op) { - // This sequence of casts reverses the sequence from base::internal::FlagOps() - return static_cast<size_t>(reinterpret_cast<intptr_t>( - op(flags_internal::kSizeof, nullptr, nullptr))); -} - -// The following struct contains the locks in a CommandLineFlag struct. -// They are in a separate struct that is lazily allocated to avoid problems -// with static initialization and to avoid multiple allocations. -struct CommandLineFlagLocks { - absl::Mutex primary_mu; // protects several fields in CommandLineFlag - absl::Mutex callback_mu; // used to serialize callbacks -}; - -// Holds either a pointer to help text or a function which produces it. This is -// needed for supporting both static initialization of Flags while supporting -// the legacy registration framework. We can't use absl::variant<const char*, -// const char*(*)()> since anybody passing 0 or nullptr in to a CommandLineFlag -// would find an ambiguity. -class HelpText { +// Handle to FlagState objects. Specific flag state objects will restore state +// of a flag produced this flag state from method CommandLineFlag::SaveState(). +class FlagStateInterface { public: - static constexpr HelpText FromFunctionPointer(const HelpGenFunc fn) { - return HelpText(fn, nullptr); - } - static constexpr HelpText FromStaticCString(const char* msg) { - return HelpText(nullptr, msg); - } - - std::string GetHelpText() const; - - HelpText() = delete; - HelpText(const HelpText&) = default; - HelpText(HelpText&&) = default; + virtual ~FlagStateInterface() {} - private: - explicit constexpr HelpText(const HelpGenFunc fn, const char* msg) - : help_function_(fn), help_message_(msg) {} - - HelpGenFunc help_function_; - const char* help_message_; + // Restores the flag originated this object to the saved state. + virtual void Restore() const = 0; }; // Holds all information for a flag. -struct CommandLineFlag { - constexpr CommandLineFlag( - const char* name_arg, HelpText help_text, const char* filename_arg, - const flags_internal::FlagOpFn op_arg, - const flags_internal::FlagMarshallingOpFn marshalling_op_arg, - const flags_internal::InitialValGenFunc initial_value_gen, - const bool retired_arg, void* def_arg, void* cur_arg) - : name(name_arg), - help(help_text), - filename(filename_arg), - op(op_arg), - marshalling_op(marshalling_op_arg), - make_init_value(initial_value_gen), - retired(retired_arg), - inited(false), - modified(false), - on_command_line(false), - validator(nullptr), - callback(nullptr), - def(def_arg), - cur(cur_arg), - counter(0), - atomic(kAtomicInit), - locks(nullptr) {} - - // Revert the init routine. - void Destroy() const; +class CommandLineFlag { + public: + constexpr CommandLineFlag() = default; // Not copyable/assignable. CommandLineFlag(const CommandLineFlag&) = delete; CommandLineFlag& operator=(const CommandLineFlag&) = delete; - absl::string_view Name() const { return name; } - std::string Help() const { return help.GetHelpText(); } - bool IsRetired() const { return this->retired; } - bool IsModified() const; - void SetModified(bool is_modified); - bool IsSpecifiedOnCommandLine() const; - // Returns true iff this is a handle to an Abseil Flag. - bool IsAbseilFlag() const { - // Set to null for V1 flags - return this->make_init_value != nullptr; - } - - absl::string_view Typename() const; - std::string Filename() const; - std::string DefaultValue() const; - std::string CurrentValue() const; - - bool HasValidatorFn() const; - bool SetValidatorFn(FlagValidator fn); - bool InvokeValidator(const void* value) const; + // Non-polymorphic access methods. // Return true iff flag has type T. template <typename T> inline bool IsOfType() const { - return this->op == &flags_internal::FlagOps<T>; + return TypeId() == &flags_internal::FlagStaticTypeIdGen<T>; } // Attempts to retrieve the flag value. Returns value on success, // absl::nullopt otherwise. template <typename T> absl::optional<T> Get() const { - if (IsRetired() || flags_internal::FlagOps<T> != this->op) + if (IsRetired() || !IsOfType<T>()) { return absl::nullopt; + } - T res; - Read(&res, flags_internal::FlagOps<T>); - - return res; + // Implementation notes: + // + // We are wrapping a union around the value of `T` to serve three purposes: + // + // 1. `U.value` has correct size and alignment for a value of type `T` + // 2. The `U.value` constructor is not invoked since U's constructor does + // not do it explicitly. + // 3. The `U.value` destructor is invoked since U's destructor does it + // explicitly. This makes `U` a kind of RAII wrapper around non default + // constructible value of T, which is destructed when we leave the + // scope. We do need to destroy U.value, which is constructed by + // CommandLineFlag::Read even though we left it in a moved-from state + // after std::move. + // + // All of this serves to avoid requiring `T` being default constructible. + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + Read(&u.value); + return std::move(u.value); } - void SetCallback(const flags_internal::FlagCallback mutation_callback); - void InvokeCallback(); + // Polymorphic access methods + + // Returns name of this flag. + virtual absl::string_view Name() const = 0; + // Returns name of the file where this flag is defined. + virtual std::string Filename() const = 0; + // Returns name of the flag's value type for some built-in types or empty + // std::string. + virtual absl::string_view Typename() const = 0; + // Returns help message associated with this flag. + virtual std::string Help() const = 0; + // Returns true iff this object corresponds to retired flag. + virtual bool IsRetired() const { return false; } + // Returns true iff this is a handle to an Abseil Flag. + virtual bool IsAbseilFlag() const { return true; } + // Returns id of the flag's value type. + virtual FlagStaticTypeId TypeId() const = 0; + virtual bool IsModified() const = 0; + virtual bool IsSpecifiedOnCommandLine() const = 0; + virtual std::string DefaultValue() const = 0; + virtual std::string CurrentValue() const = 0; + + // Interfaces to operate on validators. + virtual bool ValidateInputValue(absl::string_view value) const = 0; + + // Interface to save flag to some persistent state. Returns current flag state + // or nullptr if flag does not support saving and restoring a state. + virtual std::unique_ptr<FlagStateInterface> SaveState() = 0; // Sets the value of the flag based on specified std::string `value`. If the flag // was successfully set to new value, it returns true. Otherwise, sets `error` @@ -280,106 +171,43 @@ struct CommandLineFlag { // * Update the flag's default value // * Update the current flag value if it was never set before // The mode is selected based on `set_mode` parameter. - bool SetFromString(absl::string_view value, - flags_internal::FlagSettingMode set_mode, - flags_internal::ValueSource source, std::string* error); - - void StoreAtomic(size_t size); + virtual bool SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) = 0; - void CheckDefaultValueParsingRoundtrip() const; - // Invoke the flag validators for old flags. - // TODO(rogeeff): implement proper validators for Abseil Flags - bool ValidateDefaultValue() const; - bool ValidateInputValue(absl::string_view value) const; - - // Constant configuration for a particular flag. - private: - const char* const name; - const HelpText help; - const char* const filename; + // Checks that flags default value can be converted to std::string and back to the + // flag's value type. + virtual void CheckDefaultValueParsingRoundtrip() const = 0; protected: - const FlagOpFn op; // Type-specific handler - const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler - const InitialValGenFunc make_init_value; // Makes initial value for the flag - const bool retired; // Is the flag retired? - std::atomic<bool> inited; // fields have been lazily initialized + ~CommandLineFlag() = default; - // Mutable state (guarded by locks->primary_mu). - bool modified; // Has flag value been modified? - bool on_command_line; // Specified on command line. - FlagValidator validator; // Validator function, or nullptr - FlagCallback callback; // Mutation callback, or nullptr - void* def; // Lazily initialized pointer to default value - void* cur; // Lazily initialized pointer to current value - int64_t counter; // Mutation counter - - // For some types, a copy of the current value is kept in an atomically - // accessible field. - static const int64_t kAtomicInit = 0xababababababababll; - std::atomic<int64_t> atomic; - - // Lazily initialized mutexes for this flag value. We cannot inline a - // SpinLock or Mutex here because those have non-constexpr constructors and - // so would prevent constant initialization of this type. - // TODO(rogeeff): fix it once Mutex has constexpr constructor - struct CommandLineFlagLocks* locks; // locks, laziliy allocated. - - // Ensure that the lazily initialized fields of *flag have been initialized, - // and return the lock which should be locked when flag's state is mutated. - absl::Mutex* InitFlagIfNecessary() const; - - // copy construct new value of flag's type in a memory referenced by dst - // based on current flag's value - void Read(void* dst, const flags_internal::FlagOpFn dst_op) const; - // updates flag's value to *src (locked) - void Write(const void* src, const flags_internal::FlagOpFn src_op); - - friend class FlagRegistry; - friend class FlagPtrMap; - friend class FlagSaverImpl; - friend void FillCommandLineFlagInfo(CommandLineFlag* flag, - CommandLineFlagInfo* result); - friend bool TryParseLocked(CommandLineFlag* flag, void* dst, - absl::string_view value, std::string* err); - friend absl::Mutex* InitFlag(CommandLineFlag* flag); + private: + // Copy-construct a new value of the flag's type in a memory referenced by + // the dst based on the current flag's value. + virtual void Read(void* dst) const = 0; }; -// Update any copy of the flag value that is stored in an atomic word. -// In addition if flag has a mutation callback this function invokes it. While -// callback is being invoked the primary flag's mutex is unlocked and it is -// re-locked back after call to callback is completed. Callback invocation is -// guarded by flag's secondary mutex instead which prevents concurrent callback -// invocation. Note that it is possible for other thread to grab the primary -// lock and update flag's value at any time during the callback invocation. -// This is by design. Callback can get a value of the flag if necessary, but it -// might be different from the value initiated the callback and it also can be -// different by the time the callback invocation is completed. -// Requires that *primary_lock be held in exclusive mode; it may be released -// and reacquired by the implementation. -void UpdateCopy(CommandLineFlag* flag); -// Return true iff flag value was changed via direct-access. -bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b); - -// This macro is the "source of truth" for the list of supported flag types we -// expect to perform lock free operations on. Specifically it generates code, -// a one argument macro operating on a type, supplied as a macro argument, for -// each type in the list. -#define ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(A) \ - A(bool) \ - A(short) \ - A(unsigned short) \ - A(int) \ - A(unsigned int) \ - A(long) \ - A(unsigned long) \ - A(long long) \ - A(unsigned long long) \ - A(double) \ - A(float) +// This macro is the "source of truth" for the list of supported flag built-in +// types. +#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \ + A(bool) \ + A(short) \ + A(unsigned short) \ + A(int) \ + A(unsigned int) \ + A(long) \ + A(unsigned long) \ + A(long long) \ + A(unsigned long long) \ + A(double) \ + A(float) \ + A(std::string) \ + A(std::vector<std::string>) } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc index f0d57adb..0e8bc313 100644 --- a/absl/flags/internal/commandlineflag_test.cc +++ b/absl/flags/internal/commandlineflag_test.cc @@ -15,12 +15,17 @@ #include "absl/flags/internal/commandlineflag.h" +#include <memory> +#include <string> + #include "gtest/gtest.h" #include "absl/flags/flag.h" #include "absl/flags/internal/registry.h" +#include "absl/flags/usage_config.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" ABSL_FLAG(int, int_flag, 201, "int_flag help"); ABSL_FLAG(std::string, string_flag, "dflt", @@ -33,10 +38,26 @@ namespace flags = absl::flags_internal; class CommandLineFlagTest : public testing::Test { protected: + static void SetUpTestSuite() { + // Install a function to normalize filenames before this test is run. + absl::FlagsUsageConfig default_config; + default_config.normalize_filename = &CommandLineFlagTest::NormalizeFileName; + absl::SetFlagsUsageConfig(default_config); + } + void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); } void TearDown() override { flag_saver_.reset(); } private: + static std::string NormalizeFileName(absl::string_view fname) { +#ifdef _WIN32 + std::string normalized(fname); + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + fname = normalized; +#endif + return std::string(fname); + } + std::unique_ptr<flags::FlagSaver> flag_saver_; }; @@ -49,9 +70,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { EXPECT_EQ(flag_01->Typename(), ""); EXPECT_TRUE(!flag_01->IsRetired()); EXPECT_TRUE(flag_01->IsOfType<int>()); - EXPECT_TRUE(absl::EndsWith( - flag_01->Filename(), - "absl/flags/internal/commandlineflag_test.cc")); + EXPECT_TRUE( + absl::EndsWith(flag_01->Filename(), + "absl/flags/internal/commandlineflag_test.cc")) + << flag_01->Filename(); auto* flag_02 = flags::FindCommandLineFlag("string_flag"); @@ -61,9 +83,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { EXPECT_EQ(flag_02->Typename(), ""); EXPECT_TRUE(!flag_02->IsRetired()); EXPECT_TRUE(flag_02->IsOfType<std::string>()); - EXPECT_TRUE(absl::EndsWith( - flag_02->Filename(), - "absl/flags/internal/commandlineflag_test.cc")); + EXPECT_TRUE( + absl::EndsWith(flag_02->Filename(), + "absl/flags/internal/commandlineflag_test.cc")) + << flag_02->Filename(); auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag"); diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc new file mode 100644 index 00000000..5a921e28 --- /dev/null +++ b/absl/flags/internal/flag.cc @@ -0,0 +1,382 @@ +// +// 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/flags/internal/flag.h" + +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include <atomic> +#include <memory> +#include <string> +#include <vector> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/const_init.h" +#include "absl/base/optimization.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// The help message indicating that the commandline flag has been +// 'stripped'. It will not show up when doing "-help" and its +// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1 +// before including absl/flags/flag.h +const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; + +namespace { + +// Currently we only validate flag values for user-defined flag types. +bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) { +#define DONT_VALIDATE(T) \ + if (flag_type_id == &FlagStaticTypeIdGen<T>) return false; + ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(DONT_VALIDATE) +#undef DONT_VALIDATE + + return true; +} + +// RAII helper used to temporarily unlock and relock `absl::Mutex`. +// This is used when we need to ensure that locks are released while +// invoking user supplied callbacks and then reacquired, since callbacks may +// need to acquire these locks themselves. +class MutexRelock { + public: + explicit MutexRelock(absl::Mutex* mu) : mu_(mu) { mu_->Unlock(); } + ~MutexRelock() { mu_->Lock(); } + + MutexRelock(const MutexRelock&) = delete; + MutexRelock& operator=(const MutexRelock&) = delete; + + private: + absl::Mutex* mu_; +}; + +} // namespace + +void FlagImpl::Init() { + new (&data_guard_) absl::Mutex; + + absl::MutexLock lock(reinterpret_cast<absl::Mutex*>(&data_guard_)); + + value_.dynamic = MakeInitValue().release(); + StoreAtomic(); +} + +// Ensures that the lazily initialized data is initialized, +// and returns pointer to the mutex guarding flags data. +absl::Mutex* FlagImpl::DataGuard() const { + absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init, + const_cast<FlagImpl*>(this)); + + // data_guard_ is initialized. + return reinterpret_cast<absl::Mutex*>(&data_guard_); +} + +void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const { + FlagStaticTypeId this_type_id = flags_internal::StaticTypeId(op_); + + // `type_id` is the type id corresponding to the declaration visibile at the + // call site. `this_type_id` is the type id corresponding to the type stored + // during flag definition. They must match for this operation to be + // well-defined. + if (ABSL_PREDICT_TRUE(type_id == this_type_id)) return; + + void* lhs_runtime_type_id = type_id(); + void* rhs_runtime_type_id = this_type_id(); + + if (lhs_runtime_type_id == rhs_runtime_type_id) return; + +#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) + if (*reinterpret_cast<std::type_info*>(lhs_runtime_type_id) == + *reinterpret_cast<std::type_info*>(rhs_runtime_type_id)) + return; +#endif + + ABSL_INTERNAL_LOG( + FATAL, absl::StrCat("Flag '", Name(), + "' is defined as one type and declared as another")); +} + +std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const { + void* res = nullptr; + if (DefaultKind() == FlagDefaultKind::kDynamicValue) { + res = flags_internal::Clone(op_, default_value_.dynamic_value); + } else { + res = (*default_value_.gen_func)(); + } + return {res, DynValueDeleter{op_}}; +} + +void FlagImpl::StoreValue(const void* src) { + flags_internal::Copy(op_, src, value_.dynamic); + StoreAtomic(); + modified_ = true; + ++counter_; + InvokeCallback(); +} + +absl::string_view FlagImpl::Name() const { return name_; } + +std::string FlagImpl::Filename() const { + return flags_internal::GetUsageConfig().normalize_filename(filename_); +} + +std::string FlagImpl::Help() const { + return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal + : help_.gen_func(); +} + +bool FlagImpl::IsModified() const { + absl::MutexLock l(DataGuard()); + return modified_; +} + +bool FlagImpl::IsSpecifiedOnCommandLine() const { + absl::MutexLock l(DataGuard()); + return on_command_line_; +} + +std::string FlagImpl::DefaultValue() const { + absl::MutexLock l(DataGuard()); + + auto obj = MakeInitValue(); + return flags_internal::Unparse(op_, obj.get()); +} + +std::string FlagImpl::CurrentValue() const { + absl::MutexLock l(DataGuard()); + + return flags_internal::Unparse(op_, value_.dynamic); +} + +void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) { + absl::MutexLock l(DataGuard()); + + if (callback_ == nullptr) { + callback_ = new FlagCallback; + } + callback_->func = mutation_callback; + + InvokeCallback(); +} + +void FlagImpl::InvokeCallback() const { + if (!callback_) return; + + // Make a copy of the C-style function pointer that we are about to invoke + // before we release the lock guarding it. + FlagCallbackFunc cb = callback_->func; + + // If the flag has a mutation callback this function invokes it. While the + // callback is being invoked the primary flag's mutex is unlocked and it is + // re-locked back after call to callback is completed. Callback invocation is + // guarded by flag's secondary mutex instead which prevents concurrent + // callback invocation. Note that it is possible for other thread to grab the + // primary lock and update flag's value at any time during the callback + // invocation. This is by design. Callback can get a value of the flag if + // necessary, but it might be different from the value initiated the callback + // and it also can be different by the time the callback invocation is + // completed. Requires that *primary_lock be held in exclusive mode; it may be + // released and reacquired by the implementation. + MutexRelock relock(DataGuard()); + absl::MutexLock lock(&callback_->guard); + cb(); +} + +bool FlagImpl::RestoreState(const void* value, bool modified, + bool on_command_line, int64_t counter) { + { + absl::MutexLock l(DataGuard()); + + if (counter_ == counter) return false; + } + + Write(value); + + { + absl::MutexLock l(DataGuard()); + + modified_ = modified; + on_command_line_ = on_command_line; + } + + return true; +} + +// Attempts to parse supplied `value` string using parsing routine in the `flag` +// argument. If parsing successful, this function replaces the dst with newly +// parsed value. In case if any error is encountered in either step, the error +// message is stored in 'err' +std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse( + absl::string_view value, std::string* err) const { + std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue(); + + std::string parse_err; + if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) { + absl::string_view err_sep = parse_err.empty() ? "" : "; "; + *err = absl::StrCat("Illegal value '", value, "' specified for flag '", + Name(), "'", err_sep, parse_err); + return nullptr; + } + + return tentative_value; +} + +void FlagImpl::Read(void* dst) const { + absl::ReaderMutexLock l(DataGuard()); + + flags_internal::CopyConstruct(op_, value_.dynamic, dst); +} + +void FlagImpl::StoreAtomic() { + size_t data_size = flags_internal::Sizeof(op_); + + if (data_size <= sizeof(int64_t)) { + int64_t t = 0; + std::memcpy(&t, value_.dynamic, data_size); + value_.atomics.small_atomic.store(t, std::memory_order_release); + } +#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) + else if (data_size <= sizeof(FlagsInternalTwoWordsType)) { + FlagsInternalTwoWordsType t{0, 0}; + std::memcpy(&t, value_.dynamic, data_size); + value_.atomics.big_atomic.store(t, std::memory_order_release); + } +#endif +} + +void FlagImpl::Write(const void* src) { + absl::MutexLock l(DataGuard()); + + if (ShouldValidateFlagValue(flags_internal::StaticTypeId(op_))) { + std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src), + DynValueDeleter{op_}}; + std::string ignored_error; + std::string src_as_str = flags_internal::Unparse(op_, src); + if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) { + ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(), + "' to invalid value ", src_as_str)); + } + } + + StoreValue(src); +} + +// Sets the value of the flag based on specified string `value`. If the flag +// was successfully set to new value, it returns true. Otherwise, sets `err` +// to indicate the error, leaves the flag unchanged, and returns false. There +// are three ways to set the flag's value: +// * Update the current flag value +// * Update the flag's default value +// * Update the current flag value if it was never set before +// The mode is selected based on 'set_mode' parameter. +bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* err) { + absl::MutexLock l(DataGuard()); + + switch (set_mode) { + case SET_FLAGS_VALUE: { + // set or modify the flag's value + auto tentative_value = TryParse(value, err); + if (!tentative_value) return false; + + StoreValue(tentative_value.get()); + + if (source == kCommandLine) { + on_command_line_ = true; + } + break; + } + case SET_FLAG_IF_DEFAULT: { + // set the flag's value, but only if it hasn't been set by someone else + if (modified_) { + // TODO(rogeeff): review and fix this semantic. Currently we do not fail + // in this case if flag is modified. This is misleading since the flag's + // value is not updated even though we return true. + // *err = absl::StrCat(Name(), " is already set to ", + // CurrentValue(), "\n"); + // return false; + return true; + } + auto tentative_value = TryParse(value, err); + if (!tentative_value) return false; + + StoreValue(tentative_value.get()); + break; + } + case SET_FLAGS_DEFAULT: { + auto tentative_value = TryParse(value, err); + if (!tentative_value) return false; + + if (DefaultKind() == FlagDefaultKind::kDynamicValue) { + void* old_value = default_value_.dynamic_value; + default_value_.dynamic_value = tentative_value.release(); + tentative_value.reset(old_value); + } else { + default_value_.dynamic_value = tentative_value.release(); + def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue); + } + + if (!modified_) { + // Need to set both default value *and* current, in this case + StoreValue(default_value_.dynamic_value); + modified_ = false; + } + break; + } + } + + return true; +} + +void FlagImpl::CheckDefaultValueParsingRoundtrip() const { + std::string v = DefaultValue(); + + absl::MutexLock lock(DataGuard()); + + auto dst = MakeInitValue(); + std::string error; + if (!flags_internal::Parse(op_, v, dst.get(), &error)) { + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Flag ", Name(), " (from ", Filename(), + "): std::string form of default value '", v, + "' could not be parsed; error=", error)); + } + + // We do not compare dst to def since parsing/unparsing may make + // small changes, e.g., precision loss for floating point types. +} + +bool FlagImpl::ValidateInputValue(absl::string_view value) const { + absl::MutexLock l(DataGuard()); + + auto obj = MakeInitValue(); + std::string ignored_error; + return flags_internal::Parse(op_, value, obj.get(), &ignored_error); +} + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 9a5d9b4b..35a148cf 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -16,45 +16,547 @@ #ifndef ABSL_FLAGS_INTERNAL_FLAG_H_ #define ABSL_FLAGS_INTERNAL_FLAG_H_ +#include <stdint.h> + +#include <atomic> +#include <cstring> +#include <memory> +#include <string> +#include <type_traits> + +#include "absl/base/call_once.h" +#include "absl/base/config.h" +#include "absl/base/thread_annotations.h" +#include "absl/flags/config.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { -// This is "unspecified" implementation of absl::Flag<T> type. template <typename T> -class Flag : public flags_internal::CommandLineFlag { +class Flag; + +/////////////////////////////////////////////////////////////////////////////// +// Flag value type operations, eg., parsing, copying, etc. are provided +// by function specific to that type with a signature matching FlagOpFn. + +enum class FlagOp { + kDelete, + kClone, + kCopy, + kCopyConstruct, + kSizeof, + kStaticTypeId, + kParse, + kUnparse, +}; +using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*); + +// Flag value specific operations routine. +template <typename T> +void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { + switch (op) { + case FlagOp::kDelete: + delete static_cast<const T*>(v1); + return nullptr; + case FlagOp::kClone: + return new T(*static_cast<const T*>(v1)); + case FlagOp::kCopy: + *static_cast<T*>(v2) = *static_cast<const T*>(v1); + return nullptr; + case FlagOp::kCopyConstruct: + new (v2) T(*static_cast<const T*>(v1)); + return nullptr; + case FlagOp::kSizeof: + return reinterpret_cast<void*>(sizeof(T)); + case FlagOp::kStaticTypeId: + return reinterpret_cast<void*>(&FlagStaticTypeIdGen<T>); + case FlagOp::kParse: { + // Initialize the temporary instance of type T based on current value in + // destination (which is going to be flag's default value). + T temp(*static_cast<T*>(v2)); + if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, + static_cast<std::string*>(v3))) { + return nullptr; + } + *static_cast<T*>(v2) = std::move(temp); + return v2; + } + case FlagOp::kUnparse: + *static_cast<std::string*>(v2) = + absl::UnparseFlag<T>(*static_cast<const T*>(v1)); + return nullptr; + default: + return nullptr; + } +} + +// Deletes memory interpreting obj as flag value type pointer. +inline void Delete(FlagOpFn op, const void* obj) { + op(FlagOp::kDelete, obj, nullptr, nullptr); +} +// Makes a copy of flag value pointed by obj. +inline void* Clone(FlagOpFn op, const void* obj) { + return op(FlagOp::kClone, obj, nullptr, nullptr); +} +// Copies src to dst interpreting as flag value type pointers. +inline void Copy(FlagOpFn op, const void* src, void* dst) { + op(FlagOp::kCopy, src, dst, nullptr); +} +// Construct a copy of flag value in a location pointed by dst +// based on src - pointer to the flag's value. +inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) { + op(FlagOp::kCopyConstruct, src, dst, nullptr); +} +// Returns true if parsing of input text is successfull. +inline bool Parse(FlagOpFn op, absl::string_view text, void* dst, + std::string* error) { + return op(FlagOp::kParse, &text, dst, error) != nullptr; +} +// Returns string representing supplied value. +inline std::string Unparse(FlagOpFn op, const void* val) { + std::string result; + op(FlagOp::kUnparse, val, &result, nullptr); + return result; +} +// Returns size of flag value type. +inline size_t Sizeof(FlagOpFn op) { + // This sequence of casts reverses the sequence from + // `flags_internal::FlagOps()` + return static_cast<size_t>(reinterpret_cast<intptr_t>( + op(FlagOp::kSizeof, nullptr, nullptr, nullptr))); +} +// Returns static type id coresponding to the value type. +inline FlagStaticTypeId StaticTypeId(FlagOpFn op) { + return reinterpret_cast<FlagStaticTypeId>( + op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Persistent state of the flag data. + +template <typename T> +class FlagState : public flags_internal::FlagStateInterface { + public: + FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line, + int64_t counter) + : flag_(flag), + cur_value_(std::move(cur)), + modified_(modified), + on_command_line_(on_command_line), + counter_(counter) {} + + ~FlagState() override = default; + + private: + friend class Flag<T>; + + // Restores the flag to the saved state. + void Restore() const override; + + // Flag and saved flag data. + Flag<T>* flag_; + T cur_value_; + bool modified_; + bool on_command_line_; + int64_t counter_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag help auxiliary structs. + +// This is help argument for absl::Flag encapsulating the string literal pointer +// or pointer to function generating it as well as enum descriminating two +// cases. +using HelpGenFunc = std::string (*)(); + +union FlagHelpMsg { + constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {} + constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {} + + const char* literal; + HelpGenFunc gen_func; +}; + +enum class FlagHelpKind : uint8_t { kLiteral = 0, kGenFunc = 1 }; + +struct FlagHelpArg { + FlagHelpMsg source; + FlagHelpKind kind; +}; + +extern const char kStrippedFlagHelp[]; + +// HelpConstexprWrap is used by struct AbslFlagHelpGenFor##name generated by +// ABSL_FLAG macro. It is only used to silence the compiler in the case where +// help message expression is not constexpr and does not have type const char*. +// If help message expression is indeed constexpr const char* HelpConstexprWrap +// is just a trivial identity function. +template <typename T> +const char* HelpConstexprWrap(const T&) { + return nullptr; +} +constexpr const char* HelpConstexprWrap(const char* p) { return p; } +constexpr const char* HelpConstexprWrap(char* p) { return p; } + +// These two HelpArg overloads allows us to select at compile time one of two +// way to pass Help argument to absl::Flag. We'll be passing +// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer +// first overload if possible. If T::Const is evaluatable on constexpr +// context (see non template int parameter below) we'll choose first overload. +// In this case the help message expression is immediately evaluated and is used +// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG. +// Otherwise SFINAE kicks in and first overload is dropped from the +// consideration, in which case the second overload will be used. The second +// overload does not attempt to evaluate the help message expression +// immediately and instead delays the evaluation by returing the function +// pointer (&T::NonConst) genering the help message when necessary. This is +// evaluatable in constexpr context, but the cost is an extra function being +// generated in the ABSL_FLAG code. +template <typename T, int = (T::Const(), 1)> +constexpr FlagHelpArg HelpArg(int) { + return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral}; +} + +template <typename T> +constexpr FlagHelpArg HelpArg(char) { + return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc}; +} + +/////////////////////////////////////////////////////////////////////////////// +// Flag default value auxiliary structs. + +// Signature for the function generating the initial flag value (usually +// based on default value supplied in flag's definition) +using FlagDfltGenFunc = void* (*)(); + +union FlagDefaultSrc { + constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg) + : gen_func(gen_func_arg) {} + + void* dynamic_value; + FlagDfltGenFunc gen_func; +}; + +enum class FlagDefaultKind : uint8_t { kDynamicValue = 0, kGenFunc = 1 }; + +/////////////////////////////////////////////////////////////////////////////// +// Flag current value auxiliary structs. + +// The minimum atomic size we believe to generate lock free code, i.e. all +// trivially copyable types not bigger this size generate lock free code. +static constexpr int kMinLockFreeAtomicSize = 8; + +// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words +// might use two registers, we want to dispatch the logic for them. +#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) +static constexpr int kMaxLockFreeAtomicSize = 16; +#else +static constexpr int kMaxLockFreeAtomicSize = 8; +#endif + +// We can use atomic in cases when it fits in the register, trivially copyable +// in order to make memcpy operations. +template <typename T> +struct IsAtomicFlagTypeTrait { + static constexpr bool value = + (sizeof(T) <= kMaxLockFreeAtomicSize && + type_traits_internal::is_trivially_copyable<T>::value); +}; + +// Clang does not always produce cmpxchg16b instruction when alignment of a 16 +// bytes type is not 16. +struct alignas(16) FlagsInternalTwoWordsType { + int64_t first; + int64_t second; +}; + +constexpr bool operator==(const FlagsInternalTwoWordsType& that, + const FlagsInternalTwoWordsType& other) { + return that.first == other.first && that.second == other.second; +} +constexpr bool operator!=(const FlagsInternalTwoWordsType& that, + const FlagsInternalTwoWordsType& other) { + return !(that == other); +} + +constexpr int64_t SmallAtomicInit() { return 0xababababababababll; } + +template <typename T, typename S = void> +struct BestAtomicType { + using type = int64_t; + static constexpr int64_t AtomicInit() { return SmallAtomicInit(); } +}; + +template <typename T> +struct BestAtomicType< + T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) && + sizeof(T) <= kMaxLockFreeAtomicSize), + void>::type> { + using type = FlagsInternalTwoWordsType; + static constexpr FlagsInternalTwoWordsType AtomicInit() { + return {SmallAtomicInit(), SmallAtomicInit()}; + } +}; + +struct FlagValue { + // Heap allocated value. + void* dynamic = nullptr; + // For some types, a copy of the current value is kept in an atomically + // accessible field. + union Atomics { + // Using small atomic for small types. + std::atomic<int64_t> small_atomic; + template <typename T, + typename K = typename std::enable_if< + (sizeof(T) <= kMinLockFreeAtomicSize), void>::type> + int64_t load() const { + return small_atomic.load(std::memory_order_acquire); + } + +#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) + // Using big atomics for big types. + std::atomic<FlagsInternalTwoWordsType> big_atomic; + template <typename T, typename K = typename std::enable_if< + (kMinLockFreeAtomicSize < sizeof(T) && + sizeof(T) <= kMaxLockFreeAtomicSize), + void>::type> + FlagsInternalTwoWordsType load() const { + return big_atomic.load(std::memory_order_acquire); + } + constexpr Atomics() + : big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(), + SmallAtomicInit()}} {} +#else + constexpr Atomics() : small_atomic{SmallAtomicInit()} {} +#endif + }; + Atomics atomics{}; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag callback auxiliary structs. + +// Signature for the mutation callback used by watched Flags +// The callback is noexcept. +// TODO(rogeeff): add noexcept after C++17 support is added. +using FlagCallbackFunc = void (*)(); + +struct FlagCallback { + FlagCallbackFunc func; + absl::Mutex guard; // Guard for concurrent callback invocations. +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag implementation, which does not depend on flag value type. +// The class encapsulates the Flag's data and access to it. + +struct DynValueDeleter { + explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {} + void operator()(void* ptr) const { + if (op != nullptr) Delete(op, ptr); + } + + FlagOpFn op; +}; + +class FlagImpl { public: - constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen, - const char* filename, - const flags_internal::FlagMarshallingOpFn marshalling_op_arg, - const flags_internal::InitialValGenFunc initial_value_gen) - : flags_internal::CommandLineFlag( - name, flags_internal::HelpText::FromFunctionPointer(help_gen), - filename, &flags_internal::FlagOps<T>, marshalling_op_arg, - initial_value_gen, - /*retired_arg=*/false, /*def_arg=*/nullptr, - /*cur_arg=*/nullptr) {} + constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, + FlagHelpArg help, FlagDfltGenFunc default_value_gen) + : name_(name), + filename_(filename), + op_(op), + help_(help.source), + help_source_kind_(static_cast<uint8_t>(help.kind)), + def_kind_(static_cast<uint8_t>(FlagDefaultKind::kGenFunc)), + modified_(false), + on_command_line_(false), + counter_(0), + callback_(nullptr), + default_value_(default_value_gen), + data_guard_{} {} + + // Constant access methods + absl::string_view Name() const; + std::string Filename() const; + std::string Help() const; + bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + void Read(void* dst) const ABSL_LOCKS_EXCLUDED(*DataGuard()); + + template <typename T, typename std::enable_if< + !IsAtomicFlagTypeTrait<T>::value, int>::type = 0> + void Get(T* dst) const { + AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); + Read(dst); + } + // Overload for `GetFlag()` for types that support lock-free reads. + template <typename T, typename std::enable_if<IsAtomicFlagTypeTrait<T>::value, + int>::type = 0> + void Get(T* dst) const { + // For flags of types which can be accessed "atomically" we want to avoid + // slowing down flag value access due to type validation. That's why + // this validation is hidden behind !NDEBUG +#ifndef NDEBUG + AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); +#endif + using U = flags_internal::BestAtomicType<T>; + typename U::type r = value_.atomics.template load<T>(); + if (r != U::AtomicInit()) { + std::memcpy(static_cast<void*>(dst), &r, sizeof(T)); + } else { + Read(dst); + } + } + template <typename T> + void Set(const T& src) { + AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); + Write(&src); + } + + // Mutating access methods + void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool SetFromString(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* err) + ABSL_LOCKS_EXCLUDED(*DataGuard()); + // If possible, updates copy of the Flag's value that is stored in an + // atomic word. + void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + + // Interfaces to operate on callbacks. + void SetCallback(const FlagCallbackFunc mutation_callback) + ABSL_LOCKS_EXCLUDED(*DataGuard()); + void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + + // Interfaces to save/restore mutable flag data + template <typename T> + std::unique_ptr<FlagStateInterface> SaveState(Flag<T>* flag) const + ABSL_LOCKS_EXCLUDED(*DataGuard()) { + T&& cur_value = flag->Get(); + absl::MutexLock l(DataGuard()); + + return absl::make_unique<FlagState<T>>( + flag, std::move(cur_value), modified_, on_command_line_, counter_); + } + bool RestoreState(const void* value, bool modified, bool on_command_line, + int64_t counter) ABSL_LOCKS_EXCLUDED(*DataGuard()); + + // Value validation interfaces. + void CheckDefaultValueParsingRoundtrip() const + ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool ValidateInputValue(absl::string_view value) const + ABSL_LOCKS_EXCLUDED(*DataGuard()); + + private: + // Ensures that `data_guard_` is initialized and returns it. + absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_); + // Returns heap allocated value of type T initialized with default value. + std::unique_ptr<void, DynValueDeleter> MakeInitValue() const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + // Flag initialization called via absl::call_once. + void Init(); + // Attempts to parse supplied `value` std::string. If parsing is successful, + // returns new value. Otherwise returns nullptr. + std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value, + std::string* err) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + // Stores the flag value based on the pointer to the source. + void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + + FlagHelpKind HelpSourceKind() const { + return static_cast<FlagHelpKind>(help_source_kind_); + } + FlagDefaultKind DefaultKind() const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()) { + return static_cast<FlagDefaultKind>(def_kind_); + } + // Used in read/write operations to validate source/target has correct type. + // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to + // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed + // int. To do that we pass the "assumed" type id (which is deduced from type + // int) as an argument `op`, which is in turn is validated against the type id + // stored in flag object by flag definition statement. + void AssertValidType(FlagStaticTypeId type_id) const; + + // Immutable flag's state. + + // Flags name passed to ABSL_FLAG as second arg. + const char* const name_; + // The file name where ABSL_FLAG resides. + const char* const filename_; + // Type-specific operations "vtable". + const FlagOpFn op_; + // Help message literal or function to generate it. + const FlagHelpMsg help_; + // Indicates if help message was supplied as literal or generator func. + const uint8_t help_source_kind_ : 1; + + // ------------------------------------------------------------------------ + // The bytes containing the const bitfields must not be shared with bytes + // containing the mutable bitfields. + // ------------------------------------------------------------------------ + + // Unique tag for absl::call_once call to initialize this flag. + // + // The placement of this variable between the immutable and mutable bitfields + // is important as prevents them from occupying the same byte. If you remove + // this variable, make sure to maintain this property. + absl::once_flag init_control_; + + // Mutable flag's state (guarded by `data_guard_`). + + // If def_kind_ == kDynamicValue, default_value_ holds a dynamically allocated + // value. + uint8_t def_kind_ : 1 ABSL_GUARDED_BY(*DataGuard()); + // Has this flag's value been modified? + bool modified_ : 1 ABSL_GUARDED_BY(*DataGuard()); + // Has this flag been specified on command line. + bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard()); + + // Mutation counter + int64_t counter_ ABSL_GUARDED_BY(*DataGuard()); + // Optional flag's callback and absl::Mutex to guard the invocations. + FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard()); + // Either a pointer to the function generating the default value based on the + // value specified in ABSL_FLAG or pointer to the dynamically set default + // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish + // these two cases. + FlagDefaultSrc default_value_ ABSL_GUARDED_BY(*DataGuard()); + // Current Flag Value + FlagValue value_; + + // This is reserved space for an absl::Mutex to guard flag data. It will be + // initialized in FlagImpl::Init via placement new. + // We can't use "absl::Mutex data_guard_", since this class is not literal. + // We do not want to use "absl::Mutex* data_guard_", since this would require + // heap allocation during initialization, which is both slows program startup + // and can fail. Using reserved space + placement new allows us to avoid both + // problems. + alignas(absl::Mutex) mutable char data_guard_[sizeof(absl::Mutex)]; +}; + +/////////////////////////////////////////////////////////////////////////////// +// The Flag object parameterized by the flag's value type. This class implements +// flag reflection handle interface. + +template <typename T> +class Flag final : public flags_internal::CommandLineFlag { + public: + constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, + const FlagDfltGenFunc default_value_gen) + : impl_(name, filename, &FlagOps<T>, help, default_value_gen) {} T Get() const { - // Implementation notes: - // - // We are wrapping a union around the value of `T` to serve three purposes: - // - // 1. `U.value` has correct size and alignment for a value of type `T` - // 2. The `U.value` constructor is not invoked since U's constructor does - // not - // do it explicitly. - // 3. The `U.value` destructor is invoked since U's destructor does it - // explicitly. This makes `U` a kind of RAII wrapper around non default - // constructible value of T, which is destructed when we leave the - // scope. We do need to destroy U.value, which is constructed by - // CommandLineFlag::Read even though we left it in a moved-from state - // after std::move. - // - // All of this serves to avoid requiring `T` being default constructible. + // See implementation notes in CommandLineFlag::Get(). union U { T value; U() {} @@ -62,23 +564,69 @@ class Flag : public flags_internal::CommandLineFlag { }; U u; - this->Read(&u.value, &flags_internal::FlagOps<T>); + impl_.Get(&u.value); return std::move(u.value); } + void Set(const T& v) { impl_.Set(v); } + void SetCallback(const FlagCallbackFunc mutation_callback) { + impl_.SetCallback(mutation_callback); + } - bool AtomicGet(T* v) const { - const int64_t r = this->atomic.load(std::memory_order_acquire); - if (r != flags_internal::CommandLineFlag::kAtomicInit) { - memcpy(v, &r, sizeof(T)); - return true; - } + // CommandLineFlag interface + absl::string_view Name() const override { return impl_.Name(); } + std::string Filename() const override { return impl_.Filename(); } + absl::string_view Typename() const override { return ""; } + std::string Help() const override { return impl_.Help(); } + bool IsModified() const override { return impl_.IsModified(); } + bool IsSpecifiedOnCommandLine() const override { + return impl_.IsSpecifiedOnCommandLine(); + } + std::string DefaultValue() const override { return impl_.DefaultValue(); } + std::string CurrentValue() const override { return impl_.CurrentValue(); } + bool ValidateInputValue(absl::string_view value) const override { + return impl_.ValidateInputValue(value); + } + + // Interfaces to save and restore flags to/from persistent state. + // Returns current flag state or nullptr if flag does not support + // saving and restoring a state. + std::unique_ptr<FlagStateInterface> SaveState() override { + return impl_.SaveState(this); + } - return false; + // Restores the flag state to the supplied state object. If there is + // nothing to restore returns false. Otherwise returns true. + bool RestoreState(const FlagState<T>& flag_state) { + return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_, + flag_state.on_command_line_, flag_state.counter_); + } + bool SetFromString(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* error) override { + return impl_.SetFromString(value, set_mode, source, error); } + void CheckDefaultValueParsingRoundtrip() const override { + impl_.CheckDefaultValueParsingRoundtrip(); + } + + private: + friend class FlagState<T>; + + void Read(void* dst) const override { impl_.Read(dst); } + FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen<T>; } - void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); } + // Flag's data + FlagImpl impl_; }; +template <typename T> +inline void FlagState<T>::Restore() const { + if (flag_->RestoreState(*this)) { + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("Restore saved value of ", flag_->Name(), + " to: ", flag_->CurrentValue())); + } +} + // This class facilitates Flag object registration and tail expression-based // flag definition, for example: // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); @@ -89,7 +637,7 @@ class FlagRegistrar { if (do_register) flags_internal::RegisterCommandLineFlag(flag_); } - FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && { + FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && { flag_->SetCallback(cb); return *this; } @@ -117,7 +665,7 @@ T* MakeFromDefaultValue(EmptyBraces) { } } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_INTERNAL_FLAG_H_ diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h index 6c79dc87..03e8a07b 100644 --- a/absl/flags/internal/parse.h +++ b/absl/flags/internal/parse.h @@ -19,6 +19,7 @@ #include <string> #include <vector> +#include "absl/base/config.h" #include "absl/flags/declare.h" ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile); @@ -27,7 +28,7 @@ ABSL_DECLARE_FLAG(std::vector<std::string>, tryfromenv); ABSL_DECLARE_FLAG(std::vector<std::string>, undefok); namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs }; @@ -44,7 +45,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], OnUndefinedFlag on_undef_flag); } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_INTERNAL_PARSE_H_ diff --git a/absl/flags/internal/path_util.h b/absl/flags/internal/path_util.h index 623a7bc9..365c8305 100644 --- a/absl/flags/internal/path_util.h +++ b/absl/flags/internal/path_util.h @@ -16,11 +16,12 @@ #ifndef ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ #define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ +#include "absl/base/config.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { // A portable interface that returns the basename of the filename passed as an @@ -56,7 +57,7 @@ inline absl::string_view Package(absl::string_view filename) { } } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ diff --git a/absl/flags/internal/program_name.cc b/absl/flags/internal/program_name.cc index 28b8ed38..51d698da 100644 --- a/absl/flags/internal/program_name.cc +++ b/absl/flags/internal/program_name.cc @@ -17,16 +17,21 @@ #include <string> +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/const_init.h" +#include "absl/base/thread_annotations.h" #include "absl/flags/internal/path_util.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { ABSL_CONST_INIT static absl::Mutex program_name_guard(absl::kConstInit); ABSL_CONST_INIT static std::string* program_name - GUARDED_BY(program_name_guard) = nullptr; + ABSL_GUARDED_BY(program_name_guard) = nullptr; std::string ProgramInvocationName() { absl::MutexLock l(&program_name_guard); @@ -51,5 +56,5 @@ void SetProgramInvocationName(absl::string_view prog_name_str) { } } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/internal/program_name.h b/absl/flags/internal/program_name.h index a2f0ca10..b99b94fe 100644 --- a/absl/flags/internal/program_name.h +++ b/absl/flags/internal/program_name.h @@ -18,13 +18,14 @@ #include <string> +#include "absl/base/config.h" #include "absl/strings/string_view.h" // -------------------------------------------------------------------- // Program name namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { // Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()` @@ -43,7 +44,7 @@ std::string ShortProgramInvocationName(); void SetProgramInvocationName(absl::string_view prog_name_str); } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ diff --git a/absl/flags/internal/program_name_test.cc b/absl/flags/internal/program_name_test.cc index ed69218b..269142f2 100644 --- a/absl/flags/internal/program_name_test.cc +++ b/absl/flags/internal/program_name_test.cc @@ -15,8 +15,11 @@ #include "absl/flags/internal/program_name.h" +#include <string> + #include "gtest/gtest.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" namespace { diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index e39264e5..e434a859 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc @@ -15,9 +15,20 @@ #include "absl/flags/internal/registry.h" -#include "absl/base/dynamic_annotations.h" +#include <assert.h> +#include <stdlib.h> + +#include <functional> +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" -#include "absl/flags/config.h" +#include "absl/base/thread_annotations.h" +#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/usage_config.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -30,20 +41,8 @@ // set it. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { -namespace { - -void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS { - flag->Destroy(); - - // CommandLineFlag handle object is heap allocated for non Abseil Flags. - if (!flag->IsAbseilFlag()) { - delete flag; - } -} - -} // namespace // -------------------------------------------------------------------- // FlagRegistry @@ -58,17 +57,13 @@ void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS { class FlagRegistry { public: FlagRegistry() = default; - ~FlagRegistry() { - for (auto& p : flags_) { - DestroyFlag(p.second); - } - } + ~FlagRegistry() = default; // Store a flag in this registry. Takes ownership of *flag. void RegisterFlag(CommandLineFlag* flag); - void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); } - void Unlock() UNLOCK_FUNCTION(lock_) { lock_.Unlock(); } + void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); } + void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); } // Returns the flag object for the specified name, or nullptr if not found. // Will emit a warning if a 'retired' flag is specified. @@ -114,6 +109,7 @@ class FlagRegistryLock { FlagRegistry* const fr_; }; +void DestroyRetiredFlag(CommandLineFlag* flag); } // namespace void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { @@ -131,7 +127,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { (flag->IsRetired() ? old_flag->Filename() : flag->Filename()), "'."), true); - } else if (flag->op != old_flag->op) { + } else if (flag->TypeId() != old_flag->TypeId()) { flags_internal::ReportUsageError( absl::StrCat("Flag '", flag->Name(), "' was defined more than once but with " @@ -141,8 +137,8 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { flag->Typename(), "', respectively."), true); } else if (old_flag->IsRetired()) { - // Retired definitions are idempotent. Just keep the old one. - DestroyFlag(flag); + // Retired flag can just be deleted. + DestroyRetiredFlag(flag); return; } else if (old_flag->Filename() != flag->Filename()) { flags_internal::ReportUsageError( @@ -201,112 +197,34 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) { class FlagSaverImpl { public: - // Constructs an empty FlagSaverImpl object. - FlagSaverImpl() {} - ~FlagSaverImpl() { - // reclaim memory from each of our CommandLineFlags - for (const SavedFlag& src : backup_registry_) { - Delete(src.op, src.current); - Delete(src.op, src.default_value); - } - } + FlagSaverImpl() = default; + FlagSaverImpl(const FlagSaverImpl&) = delete; + void operator=(const FlagSaverImpl&) = delete; // Saves the flag states from the flag registry into this object. // It's an error to call this more than once. - // Must be called when the registry mutex is not held. void SaveFromRegistry() { assert(backup_registry_.empty()); // call only once! - SavedFlag saved; flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { - if (flag->IsRetired()) return; - - saved.name = flag->Name(); - saved.op = flag->op; - saved.marshalling_op = flag->marshalling_op; - { - absl::MutexLock l(flag->InitFlagIfNecessary()); - saved.validator = flag->validator; - saved.modified = flag->modified; - saved.on_command_line = flag->on_command_line; - saved.current = Clone(saved.op, flag->cur); - saved.default_value = Clone(saved.op, flag->def); - saved.counter = flag->counter; + if (auto flag_state = flag->SaveState()) { + backup_registry_.emplace_back(std::move(flag_state)); } - backup_registry_.push_back(saved); }); } - // Restores the saved flag states into the flag registry. We - // assume no flags were added or deleted from the registry since - // the SaveFromRegistry; if they were, that's trouble! Must be - // called when the registry mutex is not held. + // Restores the saved flag states into the flag registry. void RestoreToRegistry() { - FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry(); - FlagRegistryLock frl(global_registry); - for (const SavedFlag& src : backup_registry_) { - CommandLineFlag* flag = global_registry->FindFlagLocked(src.name); - // If null, flag got deleted from registry. - if (!flag) continue; - - bool restored = false; - { - absl::MutexLock l(flag->InitFlagIfNecessary()); - flag->validator = src.validator; - flag->modified = src.modified; - flag->on_command_line = src.on_command_line; - if (flag->counter != src.counter || - ChangedDirectly(flag, src.default_value, flag->def)) { - restored = true; - Copy(src.op, src.default_value, flag->def); - } - if (flag->counter != src.counter || - ChangedDirectly(flag, src.current, flag->cur)) { - restored = true; - Copy(src.op, src.current, flag->cur); - UpdateCopy(flag); - flag->InvokeCallback(); - } - } - - if (restored) { - flag->counter++; - - // Revalidate the flag because the validator might store state based - // on the flag's value, which just changed due to the restore. - // Failing validation is ignored because it's assumed that the flag - // was valid previously and there's little that can be done about it - // here, anyway. - flag->ValidateInputValue(flag->CurrentValue()); - - ABSL_INTERNAL_LOG( - INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ", - Unparse(src.marshalling_op, src.current))); - } + for (const auto& flag_state : backup_registry_) { + flag_state->Restore(); } } private: - struct SavedFlag { - absl::string_view name; - FlagOpFn op; - FlagMarshallingOpFn marshalling_op; - int64_t counter; - bool modified; - bool on_command_line; - bool (*validator)(); - const void* current; // nullptr after restore - const void* default_value; // nullptr after restore - }; - - std::vector<SavedFlag> backup_registry_; - - FlagSaverImpl(const FlagSaverImpl&); // no copying! - void operator=(const FlagSaverImpl&); + std::vector<std::unique_ptr<flags_internal::FlagStateInterface>> + backup_registry_; }; -FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) { - impl_->SaveFromRegistry(); -} +FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); } void FlagSaver::Ignore() { delete impl_; @@ -321,44 +239,6 @@ FlagSaver::~FlagSaver() { } // -------------------------------------------------------------------- -// GetAllFlags() -// The main way the FlagRegistry class exposes its data. This -// returns, as strings, all the info about all the flags in -// the main registry, sorted first by filename they are defined -// in, and then by flagname. -// -------------------------------------------------------------------- - -struct FilenameFlagnameLess { - bool operator()(const CommandLineFlagInfo& a, - const CommandLineFlagInfo& b) const { - int cmp = absl::string_view(a.filename).compare(b.filename); - if (cmp != 0) return cmp < 0; - return a.name < b.name; - } -}; - -void FillCommandLineFlagInfo(CommandLineFlag* flag, - CommandLineFlagInfo* result) { - result->name = std::string(flag->Name()); - result->type = std::string(flag->Typename()); - result->description = flag->Help(); - result->filename = flag->Filename(); - - if (!flag->IsAbseilFlag()) { - if (!flag->IsModified() && ChangedDirectly(flag, flag->cur, flag->def)) { - flag->modified = true; - } - } - - result->current_value = flag->CurrentValue(); - result->default_value = flag->DefaultValue(); - result->is_default = !flag->IsModified(); - result->has_validator_fn = flag->HasValidatorFn(); - absl::MutexLock l(flag->InitFlagIfNecessary()); - result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur; -} - -// -------------------------------------------------------------------- CommandLineFlag* FindCommandLineFlag(absl::string_view name) { if (name.empty()) return nullptr; @@ -393,21 +273,6 @@ void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) { // -------------------------------------------------------------------- -void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) { - flags_internal::ForEachFlag([&](CommandLineFlag* flag) { - if (flag->IsRetired()) return; - - CommandLineFlagInfo fi; - FillCommandLineFlagInfo(flag, &fi); - OUTPUT->push_back(fi); - }); - - // Now sort the flags, first by filename they occur in, then alphabetically - std::sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameLess()); -} - -// -------------------------------------------------------------------- - bool RegisterCommandLineFlag(CommandLineFlag* flag) { FlagRegistry::GlobalRegistry()->RegisterFlag(flag); return true; @@ -415,14 +280,55 @@ bool RegisterCommandLineFlag(CommandLineFlag* flag) { // -------------------------------------------------------------------- -bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops, - const char* name) { - auto* flag = new CommandLineFlag( - name, - /*help_text=*/absl::flags_internal::HelpText::FromStaticCString(nullptr), - /*filename_arg=*/"RETIRED", ops, marshalling_ops, - /*initial_value_gen=*/nullptr, - /*retired_arg=*/true, nullptr, nullptr); +namespace { + +class RetiredFlagObj final : public flags_internal::CommandLineFlag { + public: + constexpr RetiredFlagObj(const char* name, FlagStaticTypeId type_id) + : name_(name), type_id_(type_id) {} + + private: + absl::string_view Name() const override { return name_; } + std::string Filename() const override { return "RETIRED"; } + absl::string_view Typename() const override { return ""; } + FlagStaticTypeId TypeId() const override { return type_id_; } + std::string Help() const override { return ""; } + bool IsRetired() const override { return true; } + bool IsModified() const override { return false; } + bool IsSpecifiedOnCommandLine() const override { return false; } + std::string DefaultValue() const override { return ""; } + std::string CurrentValue() const override { return ""; } + + // Any input is valid + bool ValidateInputValue(absl::string_view) const override { return true; } + + std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override { + return nullptr; + } + + bool SetFromString(absl::string_view, flags_internal::FlagSettingMode, + flags_internal::ValueSource, std::string*) override { + return false; + } + + void CheckDefaultValueParsingRoundtrip() const override {} + + void Read(void*) const override {} + + // Data members + const char* const name_; + const FlagStaticTypeId type_id_; +}; + +void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) { + assert(flag->IsRetired()); + delete static_cast<RetiredFlagObj*>(flag); +} + +} // namespace + +bool Retire(const char* name, FlagStaticTypeId type_id) { + auto* flag = new flags_internal::RetiredFlagObj(name, type_id); FlagRegistry::GlobalRegistry()->RegisterFlag(flag); return true; } @@ -441,5 +347,5 @@ bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) { } } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h index d851d245..69ff889f 100644 --- a/absl/flags/internal/registry.h +++ b/absl/flags/internal/registry.h @@ -20,43 +20,18 @@ #include <map> #include <string> +#include "absl/base/config.h" #include "absl/base/macros.h" #include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" // -------------------------------------------------------------------- // Global flags registry API. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { -// CommandLineFlagInfo holds all information for a flag. -struct CommandLineFlagInfo { - std::string name; // the name of the flag - std::string type; // DO NOT use. Use flag->IsOfType<T>() instead. - std::string description; // the "help text" associated with the flag - std::string current_value; // the current value, as a std::string - std::string default_value; // the default value, as a std::string - std::string filename; // 'cleaned' version of filename holding the flag - bool has_validator_fn; // true if RegisterFlagValidator called on this flag - - bool is_default; // true if the flag has the default value and - // has not been set explicitly from the cmdline - // or via SetCommandLineOption. - - // nullptr for ABSL_FLAG. A pointer to the flag's current value - // otherwise. E.g., for DEFINE_int32(foo, ...), flag_ptr will be - // &FLAGS_foo. - const void* flag_ptr; -}; - -//----------------------------------------------------------------------------- - -void FillCommandLineFlagInfo(CommandLineFlag* flag, - CommandLineFlagInfo* result); - -//----------------------------------------------------------------------------- - CommandLineFlag* FindCommandLineFlag(absl::string_view name); CommandLineFlag* FindRetiredFlag(absl::string_view name); @@ -69,11 +44,6 @@ void ForEachFlag(std::function<void(CommandLineFlag*)> visitor); //----------------------------------------------------------------------------- -// Store the list of all flags in *OUTPUT, sorted by file. -void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT); - -//----------------------------------------------------------------------------- - bool RegisterCommandLineFlag(CommandLineFlag*); //----------------------------------------------------------------------------- @@ -107,29 +77,14 @@ bool RegisterCommandLineFlag(CommandLineFlag*); // 4: Remove the old_lib 'retired' registration. // 5: Eventually delete the graveyard registration entirely. // -// Returns bool to enable use in namespace-scope initializers. -// For example: -// -// static const bool dummy = base::RetiredFlag<int32_t>("myflag"); -// -// Or to declare several at once: -// -// static bool dummies[] = { -// base::RetiredFlag<std::string>("some_string_flag"), -// base::RetiredFlag<double>("some_double_flag"), -// base::RetiredFlag<int32_t>("some_int32_flag") -// }; // Retire flag with name "name" and type indicated by ops. -bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops, - const char* name); +bool Retire(const char* name, FlagStaticTypeId type_id); // Registered a retired flag with name 'flag_name' and type 'T'. template <typename T> inline bool RetiredFlag(const char* flag_name) { - return flags_internal::Retire(flags_internal::FlagOps<T>, - flags_internal::FlagMarshallingOps<T>, - flag_name); + return flags_internal::Retire(flag_name, &FlagStaticTypeIdGen<T>); } // If the flag is retired, returns true and indicates in |*type_is_bool| @@ -163,7 +118,7 @@ class FlagSaver { }; } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_INTERNAL_REGISTRY_H_ diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc index 6b91b261..490bc4eb 100644 --- a/absl/flags/internal/type_erased.cc +++ b/absl/flags/internal/type_erased.cc @@ -15,13 +15,19 @@ #include "absl/flags/internal/type_erased.h" +#include <assert.h> + +#include <string> + +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" -#include "absl/flags/config.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/registry.h" #include "absl/flags/usage_config.h" -#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { bool GetCommandLineOption(absl::string_view name, std::string* value) { @@ -37,30 +43,6 @@ bool GetCommandLineOption(absl::string_view name, std::string* value) { return true; } -bool GetCommandLineFlagInfo(absl::string_view name, - CommandLineFlagInfo* OUTPUT) { - if (name.empty()) return false; - - CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); - if (flag == nullptr || flag->IsRetired()) { - return false; - } - - assert(OUTPUT); - FillCommandLineFlagInfo(flag, OUTPUT); - return true; -} - -CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name) { - CommandLineFlagInfo info; - if (!GetCommandLineFlagInfo(name, &info)) { - ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Flag '", name, "' does not exist")); - } - return info; -} - -// -------------------------------------------------------------------- - bool SetCommandLineOption(absl::string_view name, absl::string_view value) { return SetCommandLineOptionWithMode(name, value, flags_internal::SET_FLAGS_VALUE); @@ -104,5 +86,5 @@ bool SpecifiedOnCommandLine(absl::string_view name) { } } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h index c9de6de9..188429c7 100644 --- a/absl/flags/internal/type_erased.h +++ b/absl/flags/internal/type_erased.h @@ -18,14 +18,16 @@ #include <string> +#include "absl/base/config.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" +#include "absl/strings/string_view.h" // -------------------------------------------------------------------- // Registry interfaces operating on type erased handles. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { // If a flag named "name" exists, store its current value in *OUTPUT @@ -33,17 +35,6 @@ namespace flags_internal { // Thread-safe. bool GetCommandLineOption(absl::string_view name, std::string* value); -// If a flag named "name" exists, store its information in *OUTPUT -// and return true. Else return false without changing *OUTPUT. -// Thread-safe. -bool GetCommandLineFlagInfo(absl::string_view name, - CommandLineFlagInfo* OUTPUT); - -// Returns the CommandLineFlagInfo of the flagname. exit() with an -// error code if name not found. -// Thread-safe. -CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name); - // Set the value of the flag named "name" to value. If successful, // returns true. If not successful (e.g., the flag was not found or // the value is not a valid value), returns false. @@ -93,7 +84,7 @@ inline bool GetByName(absl::string_view name, T* dst) { } } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ diff --git a/absl/flags/internal/type_erased_test.cc b/absl/flags/internal/type_erased_test.cc index ac749a60..4ce59810 100644 --- a/absl/flags/internal/type_erased_test.cc +++ b/absl/flags/internal/type_erased_test.cc @@ -15,12 +15,15 @@ #include "absl/flags/internal/type_erased.h" -#include <cmath> +#include <memory> +#include <string> #include "gtest/gtest.h" #include "absl/flags/flag.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/registry.h" +#include "absl/flags/marshalling.h" #include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" ABSL_FLAG(int, int_flag, 1, "int_flag help"); ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help"); @@ -116,6 +119,13 @@ TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) { EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", flags::SET_FLAGS_DEFAULT)); + // Set it again to ensure that resetting logic is covered. + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "102", + flags::SET_FLAGS_DEFAULT)); + + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "103", + flags::SET_FLAGS_DEFAULT)); + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", flags::SET_FLAGS_DEFAULT)); EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index a9a8a21e..ff907161 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc @@ -15,18 +15,24 @@ #include "absl/flags/internal/usage.h" +#include <functional> #include <map> +#include <ostream> #include <string> +#include <utility> +#include <vector> +#include "absl/base/config.h" #include "absl/flags/flag.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/flag.h" #include "absl/flags/internal/path_util.h" #include "absl/flags/internal/program_name.h" +#include "absl/flags/internal/registry.h" #include "absl/flags/usage_config.h" -#include "absl/strings/ascii.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" ABSL_FLAG(bool, help, false, "show help on important flags for this binary [tip: all flags can " @@ -44,10 +50,31 @@ ABSL_FLAG(std::string, helpmatch, "", "show help on modules whose name contains the specified substr"); namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { namespace { +absl::string_view TypenameForHelp(const flags_internal::CommandLineFlag& flag) { + // Only report names of v1 built-in types +#define HANDLE_V1_BUILTIN_TYPE(t) \ + if (flag.IsOfType<t>()) { \ + return #t; \ + } + + HANDLE_V1_BUILTIN_TYPE(bool); + HANDLE_V1_BUILTIN_TYPE(int32_t); + HANDLE_V1_BUILTIN_TYPE(int64_t); + HANDLE_V1_BUILTIN_TYPE(uint64_t); + HANDLE_V1_BUILTIN_TYPE(double); +#undef HANDLE_V1_BUILTIN_TYPE + + if (flag.IsOfType<std::string>()) { + return "string"; + } + + return ""; +} + // This class is used to emit an XML element with `tag` and `text`. // It adds opening and closing tags and escapes special characters in the text. // For example: @@ -180,14 +207,14 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, FlagHelpPrettyPrinter printer(80, out); // Max line length is 80. // Flag name. - printer.Write(absl::StrCat("-", flag.Name())); + printer.Write(absl::StrCat("--", flag.Name())); // Flag help. printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true); // Flag data type (for V1 flags only). if (!flag.IsAbseilFlag() && !flag.IsRetired()) { - printer.Write(absl::StrCat("type: ", flag.Typename(), ";")); + printer.Write(absl::StrCat("type: ", TypenameForHelp(flag), ";")); } // The listed default value will be the actual default from the flag @@ -224,6 +251,9 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, } else { // XML schema is not a part of our public API for now. out << "<?xml version=\"1.0\"?>\n" + << "<!-- This output should be used with care. We do not report type " + "names for flags with user defined types -->\n" + << "<!-- Prefer flag only_check_args for validating flag inputs -->\n" // The document. << "<AllFlags>\n" // The program name and usage. @@ -381,5 +411,5 @@ int HandleUsageFlags(std::ostream& out, } } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h index f3794afe..6b080fd1 100644 --- a/absl/flags/internal/usage.h +++ b/absl/flags/internal/usage.h @@ -19,6 +19,7 @@ #include <iosfwd> #include <string> +#include "absl/base/config.h" #include "absl/flags/declare.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/strings/string_view.h" @@ -27,7 +28,7 @@ // Usage reporting interfaces namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { // The format to report the help messages in. @@ -65,7 +66,7 @@ int HandleUsageFlags(std::ostream& out, absl::string_view program_usage_message); } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl ABSL_DECLARE_FLAG(bool, help); diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index 781e1d2b..e1e57e55 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -15,17 +15,23 @@ #include "absl/flags/internal/usage.h" +#include <stdint.h> + #include <sstream> +#include <string> #include "gtest/gtest.h" +#include "absl/flags/declare.h" #include "absl/flags/flag.h" +#include "absl/flags/internal/parse.h" #include "absl/flags/internal/path_util.h" #include "absl/flags/internal/program_name.h" -#include "absl/flags/parse.h" +#include "absl/flags/internal/registry.h" #include "absl/flags/usage.h" #include "absl/flags/usage_config.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" ABSL_FLAG(int, usage_reporting_test_flag_01, 101, "usage_reporting_test_flag_01 help message"); @@ -67,9 +73,9 @@ static std::string NormalizeFileName(absl::string_view fname) { fname = normalized; #endif - auto absl_pos = fname.find("/absl/"); + auto absl_pos = fname.rfind("absl/"); if (absl_pos != absl::string_view::npos) { - fname = fname.substr(absl_pos + 1); + fname = fname.substr(absl_pos); } return std::string(fname); } @@ -111,7 +117,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) { flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); EXPECT_EQ( test_buf.str(), - R"( -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + R"( --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); default: 101; )"); } @@ -123,7 +129,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) { flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); EXPECT_EQ( test_buf.str(), - R"( -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + R"( --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); default: false; )"); } @@ -135,7 +141,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) { flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); EXPECT_EQ( test_buf.str(), - R"( -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + R"( --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); default: 1.03; )"); } @@ -147,7 +153,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) { flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); EXPECT_EQ( test_buf.str(), - R"( -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + R"( --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); default: 1000000000000004; )"); } @@ -159,7 +165,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) { flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); EXPECT_EQ( test_buf.str(), - R"( -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + R"( --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); default: UDT{}; )"); } @@ -171,17 +177,17 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) { R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: - -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); default: 101; - -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); default: false; - -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); default: 1.03; - -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); default: 1000000000000004; - -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); default: UDT{}; - -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. Some more help. Even more long long long long long long long long long long long long help @@ -247,17 +253,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) { R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: - -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); default: 101; - -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); default: false; - -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); default: 1.03; - -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); default: 1000000000000004; - -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); default: UDT{}; - -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. Some more help. Even more long long long long long long long long long long long long help @@ -276,17 +282,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) { R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: - -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); default: 101; - -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); default: false; - -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); default: 1.03; - -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); default: 1000000000000004; - -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); default: UDT{}; - -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. Some more help. Even more long long long long long long long long long long long long help @@ -307,17 +313,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) { R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: - -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); default: 101; - -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); default: false; - -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); default: 1.03; - -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); default: 1000000000000004; - -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); default: UDT{}; - -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. Some more help. Even more long long long long long long long long long long long long help @@ -372,17 +378,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) { R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: - -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); default: 101; - -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); default: false; - -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); default: 1.03; - -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); default: 1000000000000004; - -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); default: UDT{}; - -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. Some more help. Even more long long long long long long long long long long long long help @@ -395,7 +401,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) { } // namespace int main(int argc, char* argv[]) { - absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc + (void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc flags::SetProgramInvocationName("usage_test"); absl::SetProgramUsageMessage(kTestUsageMessage); ::testing::InitGoogleTest(&argc, argv); diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc index 71b01d77..6f2ddda8 100644 --- a/absl/flags/marshalling.cc +++ b/absl/flags/marshalling.cc @@ -15,18 +15,28 @@ #include "absl/flags/marshalling.h" +#include <stddef.h> + +#include <cmath> #include <limits> +#include <string> +#include <type_traits> +#include <vector> +#include "absl/base/config.h" +#include "absl/base/log_severity.h" #include "absl/base/macros.h" +#include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { // -------------------------------------------------------------------- @@ -187,5 +197,44 @@ std::string AbslUnparseFlag(const std::vector<std::string>& v) { } } // namespace flags_internal -} // inline namespace lts_2019_08_08 + +bool AbslParseFlag(absl::string_view text, absl::LogSeverity* dst, + std::string* err) { + text = absl::StripAsciiWhitespace(text); + if (text.empty()) { + *err = "no value provided"; + return false; + } + if (text.front() == 'k' || text.front() == 'K') text.remove_prefix(1); + if (absl::EqualsIgnoreCase(text, "info")) { + *dst = absl::LogSeverity::kInfo; + return true; + } + if (absl::EqualsIgnoreCase(text, "warning")) { + *dst = absl::LogSeverity::kWarning; + return true; + } + if (absl::EqualsIgnoreCase(text, "error")) { + *dst = absl::LogSeverity::kError; + return true; + } + if (absl::EqualsIgnoreCase(text, "fatal")) { + *dst = absl::LogSeverity::kFatal; + return true; + } + std::underlying_type<absl::LogSeverity>::type numeric_value; + if (absl::ParseFlag(text, &numeric_value, err)) { + *dst = static_cast<absl::LogSeverity>(numeric_value); + return true; + } + *err = "only integers and absl::LogSeverity enumerators are accepted"; + return false; +} + +std::string AbslUnparseFlag(absl::LogSeverity v) { + if (v == absl::NormalizeLogSeverity(v)) return absl::LogSeverityName(v); + return absl::UnparseFlag(static_cast<int>(v)); +} + +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h index 5598d444..0b503354 100644 --- a/absl/flags/marshalling.h +++ b/absl/flags/marshalling.h @@ -33,15 +33,16 @@ // * `double` // * `std::string` // * `std::vector<std::string>` +// * `absl::LogSeverity` (provided natively for layering reasons) // // Note that support for integral types is implemented using overloads for // variable-width fundamental types (`short`, `int`, `long`, etc.). However, // you should prefer the fixed-width integral types (`int32_t`, `uint64_t`, // etc.) we've noted above within flag definitions. - // // In addition, several Abseil libraries provide their own custom support for -// Abseil flags. +// Abseil flags. Documentation for these formats is provided in the type's +// `AbslParseFlag()` definition. // // The Abseil time library provides the following support for civil time values: // @@ -164,10 +165,11 @@ #include <string> #include <vector> +#include "absl/base/config.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { // Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types. @@ -179,8 +181,8 @@ bool AbslParseFlag(absl::string_view, unsigned int*, std::string*); // NOLINT bool AbslParseFlag(absl::string_view, long*, std::string*); // NOLINT bool AbslParseFlag(absl::string_view, unsigned long*, std::string*); // NOLINT bool AbslParseFlag(absl::string_view, long long*, std::string*); // NOLINT -bool AbslParseFlag(absl::string_view, unsigned long long*, - std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned long long*, // NOLINT + std::string*); bool AbslParseFlag(absl::string_view, float*, std::string*); bool AbslParseFlag(absl::string_view, double*, std::string*); bool AbslParseFlag(absl::string_view, std::string*, std::string*); @@ -249,7 +251,14 @@ inline std::string UnparseFlag(const T& v) { return flags_internal::Unparse(v); } -} // inline namespace lts_2019_08_08 +// Overloads for `absl::LogSeverity` can't (easily) appear alongside that type's +// definition because it is layered below flags. See proper documentation in +// base/log_severity.h. +enum class LogSeverity : int; +bool AbslParseFlag(absl::string_view, absl::LogSeverity*, std::string*); +std::string AbslUnparseFlag(absl::LogSeverity); + +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_MARSHALLING_H_ diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc index 37cd1940..4a64ce11 100644 --- a/absl/flags/marshalling_test.cc +++ b/absl/flags/marshalling_test.cc @@ -15,7 +15,12 @@ #include "absl/flags/marshalling.h" +#include <stdint.h> + #include <cmath> +#include <limits> +#include <string> +#include <vector> #include "gtest/gtest.h" diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc index fd80a0c8..812e4981 100644 --- a/absl/flags/parse.cc +++ b/absl/flags/parse.cc @@ -17,43 +17,58 @@ #include <stdlib.h> +#include <algorithm> #include <fstream> #include <iostream> +#include <iterator> +#include <string> #include <tuple> +#include <utility> +#include <vector> #ifdef _WIN32 #include <windows.h> #endif +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/const_init.h" +#include "absl/base/thread_annotations.h" +#include "absl/flags/config.h" #include "absl/flags/flag.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/flag.h" +#include "absl/flags/internal/parse.h" #include "absl/flags/internal/program_name.h" #include "absl/flags/internal/registry.h" #include "absl/flags/internal/usage.h" #include "absl/flags/usage.h" #include "absl/flags/usage_config.h" +#include "absl/strings/ascii.h" #include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" #include "absl/strings/strip.h" #include "absl/synchronization/mutex.h" // -------------------------------------------------------------------- namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { namespace { ABSL_CONST_INIT absl::Mutex processing_checks_guard(absl::kConstInit); ABSL_CONST_INIT bool flagfile_needs_processing - GUARDED_BY(processing_checks_guard) = false; + ABSL_GUARDED_BY(processing_checks_guard) = false; ABSL_CONST_INIT bool fromenv_needs_processing - GUARDED_BY(processing_checks_guard) = false; + ABSL_GUARDED_BY(processing_checks_guard) = false; ABSL_CONST_INIT bool tryfromenv_needs_processing - GUARDED_BY(processing_checks_guard) = false; + ABSL_GUARDED_BY(processing_checks_guard) = false; } // namespace } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl ABSL_FLAG(std::vector<std::string>, flagfile, {}, @@ -111,7 +126,7 @@ ABSL_FLAG(std::vector<std::string>, undefok, {}, "with that name"); namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { namespace { @@ -280,9 +295,7 @@ void CheckDefaultValuesParsingRoundtrip() { #define IGNORE_TYPE(T) \ if (flag->IsOfType<T>()) return; - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(IGNORE_TYPE) - IGNORE_TYPE(std::string) - IGNORE_TYPE(std::vector<std::string>) + ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(IGNORE_TYPE) #undef IGNORE_TYPE flag->CheckDefaultValueParsingRoundtrip(); @@ -525,7 +538,7 @@ std::tuple<bool, absl::string_view> DeduceFlagValue(const CommandLineFlag& flag, // --my_string_var --foo=bar // We look for a flag of std::string type, whose value begins with a // dash and corresponds to known flag or standalone --. - if (value[0] == '-' && flag.IsOfType<std::string>()) { + if (!value.empty() && value[0] == '-' && flag.IsOfType<std::string>()) { auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1))); if (maybe_flag_name.empty() || @@ -751,5 +764,5 @@ std::vector<char*> ParseCommandLine(int argc, char* argv[]) { flags_internal::OnUndefinedFlag::kAbortIfUndefined); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/parse.h b/absl/flags/parse.h index 469bd506..f37b0602 100644 --- a/absl/flags/parse.h +++ b/absl/flags/parse.h @@ -26,10 +26,11 @@ #include <string> #include <vector> +#include "absl/base/config.h" #include "absl/flags/internal/parse.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ParseCommandLine() // @@ -54,7 +55,7 @@ inline namespace lts_2019_08_08 { // help messages and then exits the program. std::vector<char*> ParseCommandLine(int argc, char* argv[]); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_PARSE_H_ diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc index 447a3bc7..6f49377a 100644 --- a/absl/flags/parse_test.cc +++ b/absl/flags/parse_test.cc @@ -15,14 +15,22 @@ #include "absl/flags/parse.h" +#include <stdlib.h> + #include <fstream> +#include <string> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/scoped_set_env.h" +#include "absl/flags/declare.h" #include "absl/flags/flag.h" +#include "absl/flags/internal/parse.h" +#include "absl/flags/internal/registry.h" #include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "absl/types/span.h" @@ -92,8 +100,11 @@ const std::string& GetTestTempDir() { auto len = GetTempPathA(MAX_PATH, temp_path_buffer); if (len < MAX_PATH && len != 0) { - std::string temp_dir_name = absl::StrCat( - temp_path_buffer, "\\parse_test.", GetCurrentProcessId()); + std::string temp_dir_name = temp_path_buffer; + if (!absl::EndsWith(temp_dir_name, "\\")) { + temp_dir_name.push_back('\\'); + } + absl::StrAppend(&temp_dir_name, "parse_test.", GetCurrentProcessId()); if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) { *res = temp_dir_name; } @@ -104,11 +115,11 @@ const std::string& GetTestTempDir() { *res = unique_name; } #endif + } - if (res->empty()) { - ABSL_INTERNAL_LOG(FATAL, - "Failed to make temporary directory for data files"); - } + if (res->empty()) { + ABSL_INTERNAL_LOG(FATAL, + "Failed to make temporary directory for data files"); } #ifdef _WIN32 diff --git a/absl/flags/usage.cc b/absl/flags/usage.cc index 12c346b7..452f6675 100644 --- a/absl/flags/usage.cc +++ b/absl/flags/usage.cc @@ -14,18 +14,25 @@ // limitations under the License. #include "absl/flags/usage.h" +#include <stdlib.h> + #include <string> +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/const_init.h" +#include "absl/base/thread_annotations.h" #include "absl/flags/internal/usage.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { namespace { ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit); ABSL_CONST_INIT std::string* program_usage_message - GUARDED_BY(usage_message_guard) = nullptr; + ABSL_GUARDED_BY(usage_message_guard) = nullptr; } // namespace } // namespace flags_internal @@ -54,5 +61,5 @@ absl::string_view ProgramUsageMessage() { : "Warning: SetProgramUsageMessage() never called"; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/usage.h b/absl/flags/usage.h index c232a7d0..ad12ab7a 100644 --- a/absl/flags/usage.h +++ b/absl/flags/usage.h @@ -16,13 +16,14 @@ #ifndef ABSL_FLAGS_USAGE_H_ #define ABSL_FLAGS_USAGE_H_ +#include "absl/base/config.h" #include "absl/strings/string_view.h" // -------------------------------------------------------------------- // Usage reporting interfaces namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Sets the "usage" message to be used by help reporting routines. // For example: @@ -36,7 +37,7 @@ void SetProgramUsageMessage(absl::string_view new_usage_message); // Returns the usage message set by SetProgramUsageMessage(). absl::string_view ProgramUsageMessage(); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_USAGE_H_ diff --git a/absl/flags/usage_config.cc b/absl/flags/usage_config.cc index a538acd5..2d837ec5 100644 --- a/absl/flags/usage_config.cc +++ b/absl/flags/usage_config.cc @@ -16,12 +16,16 @@ #include "absl/flags/usage_config.h" #include <iostream> -#include <memory> +#include <string> #include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/const_init.h" +#include "absl/base/thread_annotations.h" #include "absl/flags/internal/path_util.h" #include "absl/flags/internal/program_name.h" -#include "absl/strings/str_cat.h" +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "absl/strings/strip.h" #include "absl/synchronization/mutex.h" @@ -34,7 +38,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {} } // extern "C" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { namespace { @@ -95,7 +99,7 @@ std::string NormalizeFilename(absl::string_view filename) { ABSL_CONST_INIT absl::Mutex custom_usage_config_guard(absl::kConstInit); ABSL_CONST_INIT FlagsUsageConfig* custom_usage_config - GUARDED_BY(custom_usage_config_guard) = nullptr; + ABSL_GUARDED_BY(custom_usage_config_guard) = nullptr; } // namespace @@ -150,5 +154,5 @@ void SetFlagsUsageConfig(FlagsUsageConfig usage_config) { flags_internal::custom_usage_config = new FlagsUsageConfig(usage_config); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/usage_config.h b/absl/flags/usage_config.h index c6d34e4a..0ed7e1b4 100644 --- a/absl/flags/usage_config.h +++ b/absl/flags/usage_config.h @@ -27,6 +27,7 @@ #include <functional> #include <string> +#include "absl/base/config.h" #include "absl/strings/string_view.h" // ----------------------------------------------------------------------------- @@ -54,7 +55,7 @@ // Shows help on modules whose name contains the specified substring namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace flags_internal { using FlagKindFilter = std::function<bool (absl::string_view)>; @@ -102,7 +103,7 @@ struct FlagsUsageConfig { // normalize_filename("/my_company/some_long_path/src/project/file.cc") // might produce // "project/file.cc". - std::function<std::string (absl::string_view)> normalize_filename; + std::function<std::string(absl::string_view)> normalize_filename; }; // SetFlagsUsageConfig() @@ -119,7 +120,7 @@ FlagsUsageConfig GetUsageConfig(); void ReportUsageError(absl::string_view msg, bool is_fatal); } // namespace flags_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl extern "C" { diff --git a/absl/flags/usage_config_test.cc b/absl/flags/usage_config_test.cc index 3bde13af..70eca30b 100644 --- a/absl/flags/usage_config_test.cc +++ b/absl/flags/usage_config_test.cc @@ -15,10 +15,13 @@ #include "absl/flags/usage_config.h" +#include <string> + #include "gtest/gtest.h" #include "absl/flags/internal/path_util.h" #include "absl/flags/internal/program_name.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" namespace { diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel new file mode 100644 index 00000000..432546ce --- /dev/null +++ b/absl/functional/BUILD.bazel @@ -0,0 +1,93 @@ +# +# Copyright 2019 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "bind_front", + srcs = ["internal/front_binder.h"], + hdrs = ["bind_front.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:base_internal", + "//absl/container:compressed_tuple", + "//absl/meta:type_traits", + "//absl/utility", + ], +) + +cc_test( + name = "bind_front_test", + srcs = ["bind_front_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bind_front", + "//absl/memory", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "function_ref", + srcs = ["internal/function_ref.h"], + hdrs = ["function_ref.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:base_internal", + "//absl/meta:type_traits", + ], +) + +cc_test( + name = "function_ref_test", + size = "small", + srcs = ["function_ref_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":function_ref", + "//absl/container:test_instance_tracker", + "//absl/memory", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "function_ref_benchmark", + srcs = [ + "function_ref_benchmark.cc", + ], + copts = ABSL_TEST_COPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":function_ref", + "//absl/base:core_headers", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt new file mode 100644 index 00000000..cda914f2 --- /dev/null +++ b/absl/functional/CMakeLists.txt @@ -0,0 +1,72 @@ +# +# 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. +# + +absl_cc_library( + NAME + bind_front + SRCS + "internal/front_binder.h" + HDRS + "bind_front.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base_internal + absl::compressed_tuple + PUBLIC +) + +absl_cc_test( + NAME + bind_front_test + SRCS + "bind_front_test.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::bind_front + absl::memory + gmock_main +) + +absl_cc_library( + NAME + function_ref + SRCS + "internal/function_ref.h" + HDRS + "function_ref.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base_internal + absl::meta + PUBLIC +) + +absl_cc_test( + NAME + function_ref_test + SRCS + "function_ref_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::function_ref + absl::memory + absl::test_instance_tracker + gmock_main +) diff --git a/absl/functional/bind_front.h b/absl/functional/bind_front.h new file mode 100644 index 00000000..5b47970e --- /dev/null +++ b/absl/functional/bind_front.h @@ -0,0 +1,184 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: bind_front.h +// ----------------------------------------------------------------------------- +// +// `absl::bind_front()` returns a functor by binding a number of arguments to +// the front of a provided (usually more generic) functor. Unlike `std::bind`, +// it does not require the use of argument placeholders. The simpler syntax of +// `absl::bind_front()` allows you to avoid known misuses with `std::bind()`. +// +// `absl::bind_front()` is meant as a drop-in replacement for C++20's upcoming +// `std::bind_front()`, which similarly resolves these issues with +// `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, allow +// partial function application. (See +// https://en.wikipedia.org/wiki/Partial_application). + +#ifndef ABSL_FUNCTIONAL_BIND_FRONT_H_ +#define ABSL_FUNCTIONAL_BIND_FRONT_H_ + +#include "absl/functional/internal/front_binder.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// bind_front() +// +// Binds the first N arguments of an invocable object and stores them by value. +// +// Like `std::bind()`, `absl::bind_front()` is implicitly convertible to +// `std::function`. In particular, it may be used as a simpler replacement for +// `std::bind()` in most cases, as it does not require placeholders to be +// specified. More importantly, it provides more reliable correctness guarantees +// than `std::bind()`; while `std::bind()` will silently ignore passing more +// parameters than expected, for example, `absl::bind_front()` will report such +// mis-uses as errors. +// +// absl::bind_front(a...) can be seen as storing the results of +// std::make_tuple(a...). +// +// Example: Binding a free function. +// +// int Minus(int a, int b) { return a - b; } +// +// assert(absl::bind_front(Minus)(3, 2) == 3 - 2); +// assert(absl::bind_front(Minus, 3)(2) == 3 - 2); +// assert(absl::bind_front(Minus, 3, 2)() == 3 - 2); +// +// Example: Binding a member function. +// +// struct Math { +// int Double(int a) const { return 2 * a; } +// }; +// +// Math math; +// +// assert(absl::bind_front(&Math::Double)(&math, 3) == 2 * 3); +// // Stores a pointer to math inside the functor. +// assert(absl::bind_front(&Math::Double, &math)(3) == 2 * 3); +// // Stores a copy of math inside the functor. +// assert(absl::bind_front(&Math::Double, math)(3) == 2 * 3); +// // Stores std::unique_ptr<Math> inside the functor. +// assert(absl::bind_front(&Math::Double, +// std::unique_ptr<Math>(new Math))(3) == 2 * 3); +// +// Example: Using `absl::bind_front()`, instead of `std::bind()`, with +// `std::function`. +// +// class FileReader { +// public: +// void ReadFileAsync(const std::string& filename, std::string* content, +// const std::function<void()>& done) { +// // Calls Executor::Schedule(std::function<void()>). +// Executor::DefaultExecutor()->Schedule( +// absl::bind_front(&FileReader::BlockingRead, this, +// filename, content, done)); +// } +// +// private: +// void BlockingRead(const std::string& filename, std::string* content, +// const std::function<void()>& done) { +// CHECK_OK(file::GetContents(filename, content, {})); +// done(); +// } +// }; +// +// `absl::bind_front()` stores bound arguments explicitly using the type passed +// rather than implicitly based on the type accepted by its functor. +// +// Example: Binding arguments explicitly. +// +// void LogStringView(absl::string_view sv) { +// LOG(INFO) << sv; +// } +// +// Executor* e = Executor::DefaultExecutor(); +// std::string s = "hello"; +// absl::string_view sv = s; +// +// // absl::bind_front(LogStringView, arg) makes a copy of arg and stores it. +// e->Schedule(absl::bind_front(LogStringView, sv)); // ERROR: dangling +// // string_view. +// +// e->Schedule(absl::bind_front(LogStringView, s)); // OK: stores a copy of +// // s. +// +// To store some of the arguments passed to `absl::bind_front()` by reference, +// use std::ref()` and `std::cref()`. +// +// Example: Storing some of the bound arguments by reference. +// +// class Service { +// public: +// void Serve(const Request& req, std::function<void()>* done) { +// // The request protocol buffer won't be deleted until done is called. +// // It's safe to store a reference to it inside the functor. +// Executor::DefaultExecutor()->Schedule( +// absl::bind_front(&Service::BlockingServe, this, std::cref(req), +// done)); +// } +// +// private: +// void BlockingServe(const Request& req, std::function<void()>* done); +// }; +// +// Example: Storing bound arguments by reference. +// +// void Print(const std::string& a, const std::string& b) { +// std::cerr << a << b; +// } +// +// std::string hi = "Hello, "; +// std::vector<std::string> names = {"Chuk", "Gek"}; +// // Doesn't copy hi. +// for_each(names.begin(), names.end(), +// absl::bind_front(Print, std::ref(hi))); +// +// // DO NOT DO THIS: the functor may outlive "hi", resulting in +// // dangling references. +// foo->DoInFuture(absl::bind_front(Print, std::ref(hi), "Guest")); // BAD! +// auto f = absl::bind_front(Print, std::ref(hi), "Guest"); // BAD! +// +// Example: Storing reference-like types. +// +// void Print(absl::string_view a, const std::string& b) { +// std::cerr << a << b; +// } +// +// std::string hi = "Hello, "; +// // Copies "hi". +// absl::bind_front(Print, hi)("Chuk"); +// +// // Compile error: std::reference_wrapper<const string> is not implicitly +// // convertible to string_view. +// // absl::bind_front(Print, std::cref(hi))("Chuk"); +// +// // Doesn't copy "hi". +// absl::bind_front(Print, absl::string_view(hi))("Chuk"); +// +template <class F, class... BoundArgs> +constexpr functional_internal::bind_front_t<F, BoundArgs...> bind_front( + F&& func, BoundArgs&&... args) { + return functional_internal::bind_front_t<F, BoundArgs...>( + absl::in_place, absl::forward<F>(func), + absl::forward<BoundArgs>(args)...); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_BIND_FRONT_H_ diff --git a/absl/functional/bind_front_test.cc b/absl/functional/bind_front_test.cc new file mode 100644 index 00000000..4801a81c --- /dev/null +++ b/absl/functional/bind_front_test.cc @@ -0,0 +1,231 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/functional/bind_front.h" + +#include <stddef.h> + +#include <functional> +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" + +namespace { + +char CharAt(const char* s, size_t index) { return s[index]; } + +TEST(BindTest, Basics) { + EXPECT_EQ('C', absl::bind_front(CharAt)("ABC", 2)); + EXPECT_EQ('C', absl::bind_front(CharAt, "ABC")(2)); + EXPECT_EQ('C', absl::bind_front(CharAt, "ABC", 2)()); +} + +TEST(BindTest, Lambda) { + auto lambda = [](int x, int y, int z) { return x + y + z; }; + EXPECT_EQ(6, absl::bind_front(lambda)(1, 2, 3)); + EXPECT_EQ(6, absl::bind_front(lambda, 1)(2, 3)); + EXPECT_EQ(6, absl::bind_front(lambda, 1, 2)(3)); + EXPECT_EQ(6, absl::bind_front(lambda, 1, 2, 3)()); +} + +struct Functor { + std::string operator()() & { return "&"; } + std::string operator()() const& { return "const&"; } + std::string operator()() && { return "&&"; } + std::string operator()() const&& { return "const&&"; } +}; + +TEST(BindTest, PerfectForwardingOfBoundArgs) { + auto f = absl::bind_front(Functor()); + const auto& cf = f; + EXPECT_EQ("&", f()); + EXPECT_EQ("const&", cf()); + EXPECT_EQ("&&", std::move(f)()); + EXPECT_EQ("const&&", std::move(cf)()); +} + +struct ArgDescribe { + std::string operator()(int&) const { return "&"; } // NOLINT + std::string operator()(const int&) const { return "const&"; } // NOLINT + std::string operator()(int&&) const { return "&&"; } + std::string operator()(const int&&) const { return "const&&"; } +}; + +TEST(BindTest, PerfectForwardingOfFreeArgs) { + ArgDescribe f; + int i; + EXPECT_EQ("&", absl::bind_front(f)(static_cast<int&>(i))); + EXPECT_EQ("const&", absl::bind_front(f)(static_cast<const int&>(i))); + EXPECT_EQ("&&", absl::bind_front(f)(static_cast<int&&>(i))); + EXPECT_EQ("const&&", absl::bind_front(f)(static_cast<const int&&>(i))); +} + +struct NonCopyableFunctor { + NonCopyableFunctor() = default; + NonCopyableFunctor(const NonCopyableFunctor&) = delete; + NonCopyableFunctor& operator=(const NonCopyableFunctor&) = delete; + const NonCopyableFunctor* operator()() const { return this; } +}; + +TEST(BindTest, RefToFunctor) { + // It won't copy/move the functor and use the original object. + NonCopyableFunctor ncf; + auto bound_ncf = absl::bind_front(std::ref(ncf)); + auto bound_ncf_copy = bound_ncf; + EXPECT_EQ(&ncf, bound_ncf_copy()); +} + +struct Struct { + std::string value; +}; + +TEST(BindTest, StoreByCopy) { + Struct s = {"hello"}; + auto f = absl::bind_front(&Struct::value, s); + auto g = f; + EXPECT_EQ("hello", f()); + EXPECT_EQ("hello", g()); + EXPECT_NE(&s.value, &f()); + EXPECT_NE(&s.value, &g()); + EXPECT_NE(&g(), &f()); +} + +struct NonCopyable { + explicit NonCopyable(const std::string& s) : value(s) {} + NonCopyable(const NonCopyable&) = delete; + NonCopyable& operator=(const NonCopyable&) = delete; + + std::string value; +}; + +const std::string& GetNonCopyableValue(const NonCopyable& n) { return n.value; } + +TEST(BindTest, StoreByRef) { + NonCopyable s("hello"); + auto f = absl::bind_front(&GetNonCopyableValue, std::ref(s)); + EXPECT_EQ("hello", f()); + EXPECT_EQ(&s.value, &f()); + auto g = std::move(f); // NOLINT + EXPECT_EQ("hello", g()); + EXPECT_EQ(&s.value, &g()); + s.value = "goodbye"; + EXPECT_EQ("goodbye", g()); +} + +TEST(BindTest, StoreByCRef) { + NonCopyable s("hello"); + auto f = absl::bind_front(&GetNonCopyableValue, std::cref(s)); + EXPECT_EQ("hello", f()); + EXPECT_EQ(&s.value, &f()); + auto g = std::move(f); // NOLINT + EXPECT_EQ("hello", g()); + EXPECT_EQ(&s.value, &g()); + s.value = "goodbye"; + EXPECT_EQ("goodbye", g()); +} + +const std::string& GetNonCopyableValueByWrapper( + std::reference_wrapper<NonCopyable> n) { + return n.get().value; +} + +TEST(BindTest, StoreByRefInvokeByWrapper) { + NonCopyable s("hello"); + auto f = absl::bind_front(GetNonCopyableValueByWrapper, std::ref(s)); + EXPECT_EQ("hello", f()); + EXPECT_EQ(&s.value, &f()); + auto g = std::move(f); + EXPECT_EQ("hello", g()); + EXPECT_EQ(&s.value, &g()); + s.value = "goodbye"; + EXPECT_EQ("goodbye", g()); +} + +TEST(BindTest, StoreByPointer) { + NonCopyable s("hello"); + auto f = absl::bind_front(&NonCopyable::value, &s); + EXPECT_EQ("hello", f()); + EXPECT_EQ(&s.value, &f()); + auto g = std::move(f); + EXPECT_EQ("hello", g()); + EXPECT_EQ(&s.value, &g()); +} + +int Sink(std::unique_ptr<int> p) { + return *p; +} + +std::unique_ptr<int> Factory(int n) { return absl::make_unique<int>(n); } + +TEST(BindTest, NonCopyableArg) { + EXPECT_EQ(42, absl::bind_front(Sink)(absl::make_unique<int>(42))); + EXPECT_EQ(42, absl::bind_front(Sink, absl::make_unique<int>(42))()); +} + +TEST(BindTest, NonCopyableResult) { + EXPECT_THAT(absl::bind_front(Factory)(42), ::testing::Pointee(42)); + EXPECT_THAT(absl::bind_front(Factory, 42)(), ::testing::Pointee(42)); +} + +// is_copy_constructible<FalseCopyable<unique_ptr<T>> is true but an attempt to +// instantiate the copy constructor leads to a compile error. This is similar +// to how standard containers behave. +template <class T> +struct FalseCopyable { + FalseCopyable() {} + FalseCopyable(const FalseCopyable& other) : m(other.m) {} + FalseCopyable(FalseCopyable&& other) : m(std::move(other.m)) {} + T m; +}; + +int GetMember(FalseCopyable<std::unique_ptr<int>> x) { return *x.m; } + +TEST(BindTest, WrappedMoveOnly) { + FalseCopyable<std::unique_ptr<int>> x; + x.m = absl::make_unique<int>(42); + auto f = absl::bind_front(&GetMember, std::move(x)); + EXPECT_EQ(42, std::move(f)()); +} + +int Plus(int a, int b) { return a + b; } + +TEST(BindTest, ConstExpr) { + constexpr auto f = absl::bind_front(CharAt); + EXPECT_EQ(f("ABC", 1), 'B'); + static constexpr int five = 5; + constexpr auto plus5 = absl::bind_front(Plus, five); + EXPECT_EQ(plus5(1), 6); + + // There seems to be a bug in MSVC dealing constexpr construction of + // char[]. Notice 'plus5' above; 'int' works just fine. +#if !(defined(_MSC_VER) && _MSC_VER < 1910) + static constexpr char data[] = "DEF"; + constexpr auto g = absl::bind_front(CharAt, data); + EXPECT_EQ(g(1), 'E'); +#endif +} + +struct ManglingCall { + int operator()(int, double, std::string) const { return 0; } +}; + +TEST(BindTest, Mangling) { + // We just want to generate a particular instantiation to see its mangling. + absl::bind_front(ManglingCall{}, 1, 3.3)("A"); +} + +} // namespace diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h new file mode 100644 index 00000000..370acc55 --- /dev/null +++ b/absl/functional/function_ref.h @@ -0,0 +1,139 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: function_ref.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::FunctionRef` type for holding a +// non-owning reference to an object of any invocable type. This function +// reference is typically most useful as a type-erased argument type for +// accepting function types that neither take ownership nor copy the type; using +// the reference type in this case avoids a copy and an allocation. Best +// practices of other non-owning reference-like objects (such as +// `absl::string_view`) apply here. +// +// An `absl::FunctionRef` is similar in usage to a `std::function` but has the +// following differences: +// +// * It doesn't own the underlying object. +// * It doesn't have a null or empty state. +// * It never performs deep copies or allocations. +// * It's much faster and cheaper to construct. +// * It's trivially copyable and destructable. +// +// Generally, `absl::FunctionRef` should not be used as a return value, data +// member, or to initialize a `std::function`. Such usages will often lead to +// problematic lifetime issues. Once you convert something to an +// `absl::FunctionRef` you cannot make a deep copy later. +// +// This class is suitable for use wherever a "const std::function<>&" +// would be used without making a copy. ForEach functions and other versions of +// the visitor pattern are a good example of when this class should be used. +// +// This class is trivial to copy and should be passed by value. +#ifndef ABSL_FUNCTIONAL_FUNCTION_REF_H_ +#define ABSL_FUNCTIONAL_FUNCTION_REF_H_ + +#include <cassert> +#include <functional> +#include <type_traits> + +#include "absl/functional/internal/function_ref.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// FunctionRef +// +// Dummy class declaration to allow the partial specialization based on function +// types below. +template <typename T> +class FunctionRef; + +// FunctionRef +// +// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with +// a compatible signature. Generally, an `absl::FunctionRef` should only be used +// as an argument type and should be preferred as an argument over a const +// reference to a `std::function`. +// +// Example: +// +// // The following function takes a function callback by const reference +// bool Visitor(const std::function<void(my_proto&, +// absl::string_view)>& callback); +// +// // Assuming that the function is not stored or otherwise copied, it can be +// // replaced by an `absl::FunctionRef`: +// bool Visitor(absl::FunctionRef<void(my_proto&, absl::string_view)> +// callback); +// +// Note: the assignment operator within an `absl::FunctionRef` is intentionally +// deleted to prevent misuse; because the `absl::FunctionRef` does not own the +// underlying type, assignment likely indicates misuse. +template <typename R, typename... Args> +class FunctionRef<R(Args...)> { + private: + // Used to disable constructors for objects that are not compatible with the + // signature of this FunctionRef. + template <typename F, + typename FR = absl::base_internal::InvokeT<F, Args&&...>> + using EnableIfCompatible = + typename std::enable_if<std::is_void<R>::value || + std::is_convertible<FR, R>::value>::type; + + public: + // Constructs a FunctionRef from any invokable type. + template <typename F, typename = EnableIfCompatible<const F&>> + FunctionRef(const F& f) // NOLINT(runtime/explicit) + : invoker_(&absl::functional_internal::InvokeObject<F, R, Args...>) { + absl::functional_internal::AssertNonNull(f); + ptr_.obj = &f; + } + + // Overload for function pointers. This eliminates a level of indirection that + // would happen if the above overload was used (it lets us store the pointer + // instead of a pointer to a pointer). + // + // This overload is also used for references to functions, since references to + // functions can decay to function pointers implicitly. + template < + typename F, typename = EnableIfCompatible<F*>, + absl::functional_internal::EnableIf<absl::is_function<F>::value> = 0> + FunctionRef(F* f) // NOLINT(runtime/explicit) + : invoker_(&absl::functional_internal::InvokeFunction<F*, R, Args...>) { + assert(f != nullptr); + ptr_.fun = reinterpret_cast<decltype(ptr_.fun)>(f); + } + + // To help prevent subtle lifetime bugs, FunctionRef is not assignable. + // Typically, it should only be used as an argument type. + FunctionRef& operator=(const FunctionRef& rhs) = delete; + + // Call the underlying object. + R operator()(Args... args) const { + return invoker_(ptr_, std::forward<Args>(args)...); + } + + private: + absl::functional_internal::VoidPtr ptr_; + absl::functional_internal::Invoker<R, Args...> invoker_; +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_FUNCTION_REF_H_ diff --git a/absl/functional/function_ref_benchmark.cc b/absl/functional/function_ref_benchmark.cc new file mode 100644 index 00000000..045305bf --- /dev/null +++ b/absl/functional/function_ref_benchmark.cc @@ -0,0 +1,142 @@ +// 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/functional/function_ref.h" + +#include <memory> + +#include "benchmark/benchmark.h" +#include "absl/base/attributes.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +int dummy = 0; + +void FreeFunction() { benchmark::DoNotOptimize(dummy); } + +struct TrivialFunctor { + void operator()() const { benchmark::DoNotOptimize(dummy); } +}; + +struct LargeFunctor { + void operator()() const { benchmark::DoNotOptimize(this); } + std::string a, b, c; +}; + +template <typename Function, typename... Args> +void ABSL_ATTRIBUTE_NOINLINE CallFunction(Function f, Args&&... args) { + f(std::forward<Args>(args)...); +} + +template <typename Function, typename Callable, typename... Args> +void ConstructAndCallFunctionBenchmark(benchmark::State& state, + const Callable& c, Args&&... args) { + for (auto _ : state) { + CallFunction<Function>(c, std::forward<Args>(args)...); + } +} + +void BM_TrivialStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark<std::function<void()>>(state, + TrivialFunctor{}); +} +BENCHMARK(BM_TrivialStdFunction); + +void BM_TrivialFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, + TrivialFunctor{}); +} +BENCHMARK(BM_TrivialFunctionRef); + +void BM_LargeStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark<std::function<void()>>(state, + LargeFunctor{}); +} +BENCHMARK(BM_LargeStdFunction); + +void BM_LargeFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, LargeFunctor{}); +} +BENCHMARK(BM_LargeFunctionRef); + +void BM_FunPtrStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark<std::function<void()>>(state, FreeFunction); +} +BENCHMARK(BM_FunPtrStdFunction); + +void BM_FunPtrFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, FreeFunction); +} +BENCHMARK(BM_FunPtrFunctionRef); + +// Doesn't include construction or copy overhead in the loop. +template <typename Function, typename Callable, typename... Args> +void CallFunctionBenchmark(benchmark::State& state, const Callable& c, + Args... args) { + Function f = c; + for (auto _ : state) { + benchmark::DoNotOptimize(&f); + f(args...); + } +} + +struct FunctorWithTrivialArgs { + void operator()(int a, int b, int c) const { + benchmark::DoNotOptimize(a); + benchmark::DoNotOptimize(b); + benchmark::DoNotOptimize(c); + } +}; + +void BM_TrivialArgsStdFunction(benchmark::State& state) { + CallFunctionBenchmark<std::function<void(int, int, int)>>( + state, FunctorWithTrivialArgs{}, 1, 2, 3); +} +BENCHMARK(BM_TrivialArgsStdFunction); + +void BM_TrivialArgsFunctionRef(benchmark::State& state) { + CallFunctionBenchmark<FunctionRef<void(int, int, int)>>( + state, FunctorWithTrivialArgs{}, 1, 2, 3); +} +BENCHMARK(BM_TrivialArgsFunctionRef); + +struct FunctorWithNonTrivialArgs { + void operator()(std::string a, std::string b, std::string c) const { + benchmark::DoNotOptimize(&a); + benchmark::DoNotOptimize(&b); + benchmark::DoNotOptimize(&c); + } +}; + +void BM_NonTrivialArgsStdFunction(benchmark::State& state) { + std::string a, b, c; + CallFunctionBenchmark< + std::function<void(std::string, std::string, std::string)>>( + state, FunctorWithNonTrivialArgs{}, a, b, c); +} +BENCHMARK(BM_NonTrivialArgsStdFunction); + +void BM_NonTrivialArgsFunctionRef(benchmark::State& state) { + std::string a, b, c; + CallFunctionBenchmark< + FunctionRef<void(std::string, std::string, std::string)>>( + state, FunctorWithNonTrivialArgs{}, a, b, c); +} +BENCHMARK(BM_NonTrivialArgsFunctionRef); + +} // namespace +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc new file mode 100644 index 00000000..3aa59745 --- /dev/null +++ b/absl/functional/function_ref_test.cc @@ -0,0 +1,257 @@ +// 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/functional/function_ref.h" + +#include <memory> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/test_instance_tracker.h" +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +void RunFun(FunctionRef<void()> f) { f(); } + +TEST(FunctionRefTest, Lambda) { + bool ran = false; + RunFun([&] { ran = true; }); + EXPECT_TRUE(ran); +} + +int Function() { return 1337; } + +TEST(FunctionRefTest, Function1) { + FunctionRef<int()> ref(&Function); + EXPECT_EQ(1337, ref()); +} + +TEST(FunctionRefTest, Function2) { + FunctionRef<int()> ref(Function); + EXPECT_EQ(1337, ref()); +} + +int NoExceptFunction() noexcept { return 1337; } + +// TODO(jdennett): Add a test for noexcept member functions. +TEST(FunctionRefTest, NoExceptFunction) { + FunctionRef<int()> ref(NoExceptFunction); + EXPECT_EQ(1337, ref()); +} + +TEST(FunctionRefTest, ForwardsArgs) { + auto l = [](std::unique_ptr<int> i) { return *i; }; + FunctionRef<int(std::unique_ptr<int>)> ref(l); + EXPECT_EQ(42, ref(absl::make_unique<int>(42))); +} + +TEST(FunctionRef, ReturnMoveOnly) { + auto l = [] { return absl::make_unique<int>(29); }; + FunctionRef<std::unique_ptr<int>()> ref(l); + EXPECT_EQ(29, *ref()); +} + +TEST(FunctionRef, ManyArgs) { + auto l = [](int a, int b, int c) { return a + b + c; }; + FunctionRef<int(int, int, int)> ref(l); + EXPECT_EQ(6, ref(1, 2, 3)); +} + +TEST(FunctionRef, VoidResultFromNonVoidFunctor) { + bool ran = false; + auto l = [&]() -> int { + ran = true; + return 2; + }; + FunctionRef<void()> ref(l); + ref(); + EXPECT_TRUE(ran); +} + +TEST(FunctionRef, CastFromDerived) { + struct Base {}; + struct Derived : public Base {}; + + Derived d; + auto l1 = [&](Base* b) { EXPECT_EQ(&d, b); }; + FunctionRef<void(Derived*)> ref1(l1); + ref1(&d); + + auto l2 = [&]() -> Derived* { return &d; }; + FunctionRef<Base*()> ref2(l2); + EXPECT_EQ(&d, ref2()); +} + +TEST(FunctionRef, VoidResultFromNonVoidFuncton) { + FunctionRef<void()> ref(Function); + ref(); +} + +TEST(FunctionRef, MemberPtr) { + struct S { + int i; + }; + + S s{1100111}; + auto mem_ptr = &S::i; + FunctionRef<int(const S& s)> ref(mem_ptr); + EXPECT_EQ(1100111, ref(s)); +} + +TEST(FunctionRef, MemberFun) { + struct S { + int i; + int get_i() const { return i; } + }; + + S s{22}; + auto mem_fun_ptr = &S::get_i; + FunctionRef<int(const S& s)> ref(mem_fun_ptr); + EXPECT_EQ(22, ref(s)); +} + +TEST(FunctionRef, MemberFunRefqualified) { + struct S { + int i; + int get_i() && { return i; } + }; + auto mem_fun_ptr = &S::get_i; + S s{22}; + FunctionRef<int(S && s)> ref(mem_fun_ptr); + EXPECT_EQ(22, ref(std::move(s))); +} + +#if !defined(_WIN32) && defined(GTEST_HAS_DEATH_TEST) + +TEST(FunctionRef, MemberFunRefqualifiedNull) { + struct S { + int i; + int get_i() && { return i; } + }; + auto mem_fun_ptr = &S::get_i; + mem_fun_ptr = nullptr; + EXPECT_DEBUG_DEATH({ FunctionRef<int(S && s)> ref(mem_fun_ptr); }, ""); +} + +TEST(FunctionRef, NullMemberPtrAssertFails) { + struct S { + int i; + }; + using MemberPtr = int S::*; + MemberPtr mem_ptr = nullptr; + EXPECT_DEBUG_DEATH({ FunctionRef<int(const S& s)> ref(mem_ptr); }, ""); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(FunctionRef, CopiesAndMovesPerPassByValue) { + absl::test_internal::InstanceTracker tracker; + absl::test_internal::CopyableMovableInstance instance(0); + auto l = [](absl::test_internal::CopyableMovableInstance) {}; + FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l); + ref(instance); + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 1); +} + +TEST(FunctionRef, CopiesAndMovesPerPassByRef) { + absl::test_internal::InstanceTracker tracker; + absl::test_internal::CopyableMovableInstance instance(0); + auto l = [](const absl::test_internal::CopyableMovableInstance&) {}; + FunctionRef<void(const absl::test_internal::CopyableMovableInstance&)> ref(l); + ref(instance); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 0); +} + +TEST(FunctionRef, CopiesAndMovesPerPassByValueCallByMove) { + absl::test_internal::InstanceTracker tracker; + absl::test_internal::CopyableMovableInstance instance(0); + auto l = [](absl::test_internal::CopyableMovableInstance) {}; + FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l); + ref(std::move(instance)); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 2); +} + +TEST(FunctionRef, CopiesAndMovesPerPassByValueToRef) { + absl::test_internal::InstanceTracker tracker; + absl::test_internal::CopyableMovableInstance instance(0); + auto l = [](const absl::test_internal::CopyableMovableInstance&) {}; + FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l); + ref(std::move(instance)); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); +} + +TEST(FunctionRef, PassByValueTypes) { + using absl::functional_internal::Invoker; + using absl::functional_internal::VoidPtr; + using absl::test_internal::CopyableMovableInstance; + struct Trivial { + void* p[2]; + }; + struct LargeTrivial { + void* p[3]; + }; + + static_assert(std::is_same<Invoker<void, int>, void (*)(VoidPtr, int)>::value, + "Scalar types should be passed by value"); + static_assert( + std::is_same<Invoker<void, Trivial>, void (*)(VoidPtr, Trivial)>::value, + "Small trivial types should be passed by value"); + static_assert(std::is_same<Invoker<void, LargeTrivial>, + void (*)(VoidPtr, LargeTrivial &&)>::value, + "Large trivial types should be passed by rvalue reference"); + static_assert( + std::is_same<Invoker<void, CopyableMovableInstance>, + void (*)(VoidPtr, CopyableMovableInstance &&)>::value, + "Types with copy/move ctor should be passed by rvalue reference"); + + // References are passed as references. + static_assert( + std::is_same<Invoker<void, int&>, void (*)(VoidPtr, int&)>::value, + "Reference types should be preserved"); + static_assert( + std::is_same<Invoker<void, CopyableMovableInstance&>, + void (*)(VoidPtr, CopyableMovableInstance&)>::value, + "Reference types should be preserved"); + static_assert( + std::is_same<Invoker<void, CopyableMovableInstance&&>, + void (*)(VoidPtr, CopyableMovableInstance &&)>::value, + "Reference types should be preserved"); + + // Make sure the address of an object received by reference is the same as the + // addess of the object passed by the caller. + { + LargeTrivial obj; + auto test = [&obj](LargeTrivial& input) { ASSERT_EQ(&input, &obj); }; + absl::FunctionRef<void(LargeTrivial&)> ref(test); + ref(obj); + } + + { + Trivial obj; + auto test = [&obj](Trivial& input) { ASSERT_EQ(&input, &obj); }; + absl::FunctionRef<void(Trivial&)> ref(test); + ref(obj); + } +} + +} // namespace +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/functional/internal/front_binder.h b/absl/functional/internal/front_binder.h new file mode 100644 index 00000000..a4d95da4 --- /dev/null +++ b/absl/functional/internal/front_binder.h @@ -0,0 +1,95 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Implementation details for `absl::bind_front()`. + +#ifndef ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ +#define ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ + +#include <cstddef> +#include <type_traits> +#include <utility> + +#include "absl/base/internal/invoke.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace functional_internal { + +// Invoke the method, expanding the tuple of bound arguments. +template <class R, class Tuple, size_t... Idx, class... Args> +R Apply(Tuple&& bound, absl::index_sequence<Idx...>, Args&&... free) { + return base_internal::Invoke( + absl::forward<Tuple>(bound).template get<Idx>()..., + absl::forward<Args>(free)...); +} + +template <class F, class... BoundArgs> +class FrontBinder { + using BoundArgsT = absl::container_internal::CompressedTuple<F, BoundArgs...>; + using Idx = absl::make_index_sequence<sizeof...(BoundArgs) + 1>; + + BoundArgsT bound_args_; + + public: + template <class... Ts> + constexpr explicit FrontBinder(absl::in_place_t, Ts&&... ts) + : bound_args_(absl::forward<Ts>(ts)...) {} + + template <class... FreeArgs, + class R = base_internal::InvokeT<F&, BoundArgs&..., FreeArgs&&...>> + R operator()(FreeArgs&&... free_args) & { + return functional_internal::Apply<R>(bound_args_, Idx(), + absl::forward<FreeArgs>(free_args)...); + } + + template <class... FreeArgs, + class R = base_internal::InvokeT<const F&, const BoundArgs&..., + FreeArgs&&...>> + R operator()(FreeArgs&&... free_args) const& { + return functional_internal::Apply<R>(bound_args_, Idx(), + absl::forward<FreeArgs>(free_args)...); + } + + template <class... FreeArgs, class R = base_internal::InvokeT< + F&&, BoundArgs&&..., FreeArgs&&...>> + R operator()(FreeArgs&&... free_args) && { + // This overload is called when *this is an rvalue. If some of the bound + // arguments are stored by value or rvalue reference, we move them. + return functional_internal::Apply<R>(absl::move(bound_args_), Idx(), + absl::forward<FreeArgs>(free_args)...); + } + + template <class... FreeArgs, + class R = base_internal::InvokeT<const F&&, const BoundArgs&&..., + FreeArgs&&...>> + R operator()(FreeArgs&&... free_args) const&& { + // This overload is called when *this is an rvalue. If some of the bound + // arguments are stored by value or rvalue reference, we move them. + return functional_internal::Apply<R>(absl::move(bound_args_), Idx(), + absl::forward<FreeArgs>(free_args)...); + } +}; + +template <class F, class... BoundArgs> +using bind_front_t = FrontBinder<decay_t<F>, absl::decay_t<BoundArgs>...>; + +} // namespace functional_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ diff --git a/absl/functional/internal/function_ref.h b/absl/functional/internal/function_ref.h new file mode 100644 index 00000000..d1575054 --- /dev/null +++ b/absl/functional/internal/function_ref.h @@ -0,0 +1,106 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ +#define ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ + +#include <cassert> +#include <functional> +#include <type_traits> + +#include "absl/base/internal/invoke.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace functional_internal { + +// Like a void* that can handle function pointers as well. The standard does not +// allow function pointers to round-trip through void*, but void(*)() is fine. +// +// Note: It's important that this class remains trivial and is the same size as +// a pointer, since this allows the compiler to perform tail-call optimizations +// when the underlying function is a callable object with a matching signature. +union VoidPtr { + const void* obj; + void (*fun)(); +}; + +// Chooses the best type for passing T as an argument. +// Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are +// passed by value. +template <typename T> +constexpr bool PassByValue() { + return !std::is_lvalue_reference<T>::value && + absl::is_trivially_copy_constructible<T>::value && + absl::is_trivially_copy_assignable< + typename std::remove_cv<T>::type>::value && + std::is_trivially_destructible<T>::value && + sizeof(T) <= 2 * sizeof(void*); +} + +template <typename T> +struct ForwardT : std::conditional<PassByValue<T>(), T, T&&> {}; + +// An Invoker takes a pointer to the type-erased invokable object, followed by +// the arguments that the invokable object expects. +// +// Note: The order of arguments here is an optimization, since member functions +// have an implicit "this" pointer as their first argument, putting VoidPtr +// first allows the compiler to perform tail-call optimization in many cases. +template <typename R, typename... Args> +using Invoker = R (*)(VoidPtr, typename ForwardT<Args>::type...); + +// +// InvokeObject and InvokeFunction provide static "Invoke" functions that can be +// used as Invokers for objects or functions respectively. +// +// static_cast<R> handles the case the return type is void. +template <typename Obj, typename R, typename... Args> +R InvokeObject(VoidPtr ptr, typename ForwardT<Args>::type... args) { + auto o = static_cast<const Obj*>(ptr.obj); + return static_cast<R>( + absl::base_internal::Invoke(*o, std::forward<Args>(args)...)); +} + +template <typename Fun, typename R, typename... Args> +R InvokeFunction(VoidPtr ptr, typename ForwardT<Args>::type... args) { + auto f = reinterpret_cast<Fun>(ptr.fun); + return static_cast<R>( + absl::base_internal::Invoke(f, std::forward<Args>(args)...)); +} + +template <typename Sig> +void AssertNonNull(const std::function<Sig>& f) { + assert(f != nullptr); + (void)f; +} + +template <typename F> +void AssertNonNull(const F&) {} + +template <typename F, typename C> +void AssertNonNull(F C::*f) { + assert(f != nullptr); + (void)f; +} + +template <bool C> +using EnableIf = typename ::std::enable_if<C, int>::type; + +} // namespace functional_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index 8c2daf70..ffe8c294 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -1,5 +1,5 @@ # -# Copyright 2018 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. @@ -14,6 +14,7 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -70,9 +71,9 @@ cc_test( deps = [ ":hash", ":hash_testing", + ":spy_hash_state", "//absl/base:core_headers", "//absl/container:flat_hash_set", - "//absl/hash:spy_hash_state", "//absl/meta:type_traits", "//absl/numeric:int128", "@com_google_googletest//:gtest_main", diff --git a/absl/hash/hash.h b/absl/hash/hash.h index d36f0960..23a65ea8 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h @@ -73,7 +73,7 @@ #include "absl/hash/internal/hash.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // `absl::Hash` @@ -318,7 +318,7 @@ class HashState : public hash_internal::HashStateBase<HashState> { void (*combine_contiguous_)(void*, const unsigned char*, size_t); }; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HASH_HASH_H_ diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 2a4e2262..f02a537a 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -274,8 +274,8 @@ TEST(HashValueTest, Strings) { const std::string small = "foo"; const std::string dup = "foofoo"; - const std::string large = "large"; - const std::string huge = std::string(5000, 'a'); + const std::string large = std::string(2048, 'x'); // multiple of chunk size + const std::string huge = std::string(5000, 'a'); // not a multiple EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( std::string(), absl::string_view(), @@ -300,6 +300,33 @@ TEST(HashValueTest, Strings) { SpyHash(absl::string_view("ABC"))); } +TEST(HashValueTest, WString) { + EXPECT_TRUE((is_hashable<std::wstring>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::wstring(), std::wstring(L"ABC"), std::wstring(L"ABC"), + std::wstring(L"Some other different string"), + std::wstring(L"Iñtërnâtiônà lizætiøn")))); +} + +TEST(HashValueTest, U16String) { + EXPECT_TRUE((is_hashable<std::u16string>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::u16string(), std::u16string(u"ABC"), std::u16string(u"ABC"), + std::u16string(u"Some other different string"), + std::u16string(u"Iñtërnâtiônà lizætiøn")))); +} + +TEST(HashValueTest, U32String) { + EXPECT_TRUE((is_hashable<std::u32string>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::u32string(), std::u32string(U"ABC"), std::u32string(U"ABC"), + std::u32string(U"Some other different string"), + std::u32string(U"Iñtërnâtiônà lizætiøn")))); +} + TEST(HashValueTest, StdArray) { EXPECT_TRUE((is_hashable<std::array<int, 3>>::value)); @@ -378,6 +405,116 @@ struct Private { } }; +// Test helper for combine_piecewise_buffer. It holds a string_view to the +// buffer-to-be-hashed. Its AbslHashValue specialization will split up its +// contents at the character offsets requested. +class PiecewiseHashTester { + public: + // Create a hash view of a buffer to be hashed contiguously. + explicit PiecewiseHashTester(absl::string_view buf) + : buf_(buf), piecewise_(false), split_locations_() {} + + // Create a hash view of a buffer to be hashed piecewise, with breaks at the + // given locations. + PiecewiseHashTester(absl::string_view buf, std::set<size_t> split_locations) + : buf_(buf), + piecewise_(true), + split_locations_(std::move(split_locations)) {} + + template <typename H> + friend H AbslHashValue(H h, const PiecewiseHashTester& p) { + if (!p.piecewise_) { + return H::combine_contiguous(std::move(h), p.buf_.data(), p.buf_.size()); + } + absl::hash_internal::PiecewiseCombiner combiner; + if (p.split_locations_.empty()) { + h = combiner.add_buffer(std::move(h), p.buf_.data(), p.buf_.size()); + return combiner.finalize(std::move(h)); + } + size_t begin = 0; + for (size_t next : p.split_locations_) { + absl::string_view chunk = p.buf_.substr(begin, next - begin); + h = combiner.add_buffer(std::move(h), chunk.data(), chunk.size()); + begin = next; + } + absl::string_view last_chunk = p.buf_.substr(begin); + if (!last_chunk.empty()) { + h = combiner.add_buffer(std::move(h), last_chunk.data(), + last_chunk.size()); + } + return combiner.finalize(std::move(h)); + } + + private: + absl::string_view buf_; + bool piecewise_; + std::set<size_t> split_locations_; +}; + +// Dummy object that hashes as two distinct contiguous buffers, "foo" followed +// by "bar" +struct DummyFooBar { + template <typename H> + friend H AbslHashValue(H h, const DummyFooBar&) { + const char* foo = "foo"; + const char* bar = "bar"; + h = H::combine_contiguous(std::move(h), foo, 3); + h = H::combine_contiguous(std::move(h), bar, 3); + return h; + } +}; + +TEST(HashValueTest, CombinePiecewiseBuffer) { + absl::Hash<PiecewiseHashTester> hash; + + // Check that hashing an empty buffer through the piecewise API works. + EXPECT_EQ(hash(PiecewiseHashTester("")), hash(PiecewiseHashTester("", {}))); + + // Similarly, small buffers should give consistent results + EXPECT_EQ(hash(PiecewiseHashTester("foobar")), + hash(PiecewiseHashTester("foobar", {}))); + EXPECT_EQ(hash(PiecewiseHashTester("foobar")), + hash(PiecewiseHashTester("foobar", {3}))); + + // But hashing "foobar" in pieces gives a different answer than hashing "foo" + // contiguously, then "bar" contiguously. + EXPECT_NE(hash(PiecewiseHashTester("foobar", {3})), + absl::Hash<DummyFooBar>()(DummyFooBar{})); + + // Test hashing a large buffer incrementally, broken up in several different + // ways. Arrange for breaks on and near the stride boundaries to look for + // off-by-one errors in the implementation. + // + // This test is run on a buffer that is a multiple of the stride size, and one + // that isn't. + for (size_t big_buffer_size : {1024 * 2 + 512, 1024 * 3}) { + SCOPED_TRACE(big_buffer_size); + std::string big_buffer; + for (int i = 0; i < big_buffer_size; ++i) { + // Arbitrary std::string + big_buffer.push_back(32 + (i * (i / 3)) % 64); + } + auto big_buffer_hash = hash(PiecewiseHashTester(big_buffer)); + + const int possible_breaks = 9; + size_t breaks[possible_breaks] = {1, 512, 1023, 1024, 1025, + 1536, 2047, 2048, 2049}; + for (unsigned test_mask = 0; test_mask < (1u << possible_breaks); + ++test_mask) { + SCOPED_TRACE(test_mask); + std::set<size_t> break_locations; + for (int j = 0; j < possible_breaks; ++j) { + if (test_mask & (1u << j)) { + break_locations.insert(breaks[j]); + } + } + EXPECT_EQ( + hash(PiecewiseHashTester(big_buffer, std::move(break_locations))), + big_buffer_hash); + } + } +} + TEST(HashValueTest, PrivateSanity) { // Sanity check that Private is working as the tests below expect it to work. EXPECT_TRUE(is_hashable<Private>::value); @@ -458,7 +595,10 @@ TEST(IsHashableTest, PoisonHash) { EXPECT_FALSE(absl::is_copy_assignable<absl::Hash<X>>::value); EXPECT_FALSE(absl::is_move_assignable<absl::Hash<X>>::value); EXPECT_FALSE(IsHashCallable<X>::value); +#if !defined(__GNUC__) || __GNUC__ < 9 + // This doesn't compile on GCC 9. EXPECT_FALSE(IsAggregateInitializable<absl::Hash<X>>::value); +#endif } #endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ @@ -544,7 +684,7 @@ H AbslHashValue(H state, CustomHashType<Tags...> t) { } // namespace namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace hash_internal { template <InvokeTag... Tags> struct is_uniquely_represented< @@ -552,7 +692,7 @@ struct is_uniquely_represented< typename EnableIfContained<InvokeTag::kUniquelyRepresented, Tags...>::type> : std::true_type {}; } // namespace hash_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ diff --git a/absl/hash/hash_testing.h b/absl/hash/hash_testing.h index 6e39028f..1e1c5741 100644 --- a/absl/hash/hash_testing.h +++ b/absl/hash/hash_testing.h @@ -28,7 +28,7 @@ #include "absl/types/variant.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Run the absl::Hash algorithm over all the elements passed in and verify that // their hash expansion is congruent with their `==` operator. @@ -372,7 +372,7 @@ VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values, equals); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HASH_HASH_TESTING_H_ diff --git a/absl/hash/internal/city.cc b/absl/hash/internal/city.cc index c7ad1591..e122c184 100644 --- a/absl/hash/internal/city.cc +++ b/absl/hash/internal/city.cc @@ -30,7 +30,7 @@ #include "absl/base/optimization.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace hash_internal { #ifdef ABSL_IS_BIG_ENDIAN @@ -342,5 +342,5 @@ uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0, } } // namespace hash_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/hash/internal/city.h b/absl/hash/internal/city.h index 7586ba08..161c7748 100644 --- a/absl/hash/internal/city.h +++ b/absl/hash/internal/city.h @@ -47,10 +47,13 @@ #include <stdint.h> #include <stdlib.h> // for size_t. + #include <utility> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace hash_internal { typedef std::pair<uint64_t, uint64_t> uint128; @@ -87,7 +90,7 @@ inline uint64_t Hash128to64(const uint128 &x) { } } // namespace hash_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HASH_INTERNAL_CITY_H_ diff --git a/absl/hash/internal/city_test.cc b/absl/hash/internal/city_test.cc index 1b9373c2..251d381d 100644 --- a/absl/hash/internal/city_test.cc +++ b/absl/hash/internal/city_test.cc @@ -20,7 +20,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace hash_internal { static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; @@ -591,5 +591,5 @@ TEST(CityHashTest, Unchanging) { } } // namespace hash_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc index 087b389b..b44ecb3a 100644 --- a/absl/hash/internal/hash.cc +++ b/absl/hash/internal/hash.cc @@ -15,11 +15,41 @@ #include "absl/hash/internal/hash.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace hash_internal { +uint64_t CityHashState::CombineLargeContiguousImpl32(uint64_t state, + const unsigned char* first, + size_t len) { + while (len >= PiecewiseChunkSize()) { + state = + Mix(state, absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), + PiecewiseChunkSize())); + len -= PiecewiseChunkSize(); + first += PiecewiseChunkSize(); + } + // Handle the remainder. + return CombineContiguousImpl(state, first, len, + std::integral_constant<int, 4>{}); +} + +uint64_t CityHashState::CombineLargeContiguousImpl64(uint64_t state, + const unsigned char* first, + size_t len) { + while (len >= PiecewiseChunkSize()) { + state = + Mix(state, absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), + PiecewiseChunkSize())); + len -= PiecewiseChunkSize(); + first += PiecewiseChunkSize(); + } + // Handle the remainder. + return CombineContiguousImpl(state, first, len, + std::integral_constant<int, 8>{}); +} + ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed; } // namespace hash_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index 81f1edf8..ae7a60cd 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -50,9 +50,15 @@ #include "absl/hash/internal/city.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace hash_internal { +class PiecewiseCombiner; + +// Internal detail: Large buffers are hashed in smaller chunks. This function +// returns the size of these chunks. +constexpr size_t PiecewiseChunkSize() { return 1024; } + // HashStateBase // // A hash state object represents an intermediate state in the computation @@ -69,7 +75,7 @@ namespace hash_internal { // // `static H combine_contiguous(H state, const unsigned char*, size_t)`. // -// `HashStateBase` will provide a complete implementations for a hash state +// `HashStateBase` will provide a complete implementation for a hash state // object in terms of this method. // // Example: @@ -118,6 +124,9 @@ class HashStateBase { // for-loop instead. template <typename T> static H combine_contiguous(H state, const T* data, size_t size); + + private: + friend class PiecewiseCombiner; }; // is_uniquely_represented @@ -188,6 +197,61 @@ H hash_bytes(H hash_state, const T& value) { return H::combine_contiguous(std::move(hash_state), start, sizeof(value)); } +// PiecewiseCombiner +// +// PiecewiseCombiner is an internal-only helper class for hashing a piecewise +// buffer of `char` or `unsigned char` as though it were contiguous. This class +// provides two methods: +// +// H add_buffer(state, data, size) +// H finalize(state) +// +// `add_buffer` can be called zero or more times, followed by a single call to +// `finalize`. This will produce the same hash expansion as concatenating each +// buffer piece into a single contiguous buffer, and passing this to +// `H::combine_contiguous`. +// +// Example usage: +// PiecewiseCombiner combiner; +// for (const auto& piece : pieces) { +// state = combiner.add_buffer(std::move(state), piece.data, piece.size); +// } +// return combiner.finalize(std::move(state)); +class PiecewiseCombiner { + public: + PiecewiseCombiner() : position_(0) {} + PiecewiseCombiner(const PiecewiseCombiner&) = delete; + PiecewiseCombiner& operator=(const PiecewiseCombiner&) = delete; + + // PiecewiseCombiner::add_buffer() + // + // Appends the given range of bytes to the sequence to be hashed, which may + // modify the provided hash state. + template <typename H> + H add_buffer(H state, const unsigned char* data, size_t size); + template <typename H> + H add_buffer(H state, const char* data, size_t size) { + return add_buffer(std::move(state), + reinterpret_cast<const unsigned char*>(data), size); + } + + // PiecewiseCombiner::finalize() + // + // Finishes combining the hash sequence, which may may modify the provided + // hash state. + // + // Once finalize() is called, add_buffer() may no longer be called. The + // resulting hash state will be the same as if the pieces passed to + // add_buffer() were concatenated into a single flat buffer, and then provided + // to H::combine_contiguous(). + template <typename H> + H finalize(H state); + + private: + unsigned char buf_[PiecewiseChunkSize()]; + size_t position_; +}; + // ----------------------------------------------------------------------------- // AbslHashValue for Basic Types // ----------------------------------------------------------------------------- @@ -364,6 +428,19 @@ H AbslHashValue(H hash_state, absl::string_view str) { str.size()); } +// Support std::wstring, std::u16string and std::u32string. +template <typename Char, typename Alloc, typename H, + typename = absl::enable_if_t<std::is_same<Char, wchar_t>::value || + std::is_same<Char, char16_t>::value || + std::is_same<Char, char32_t>::value>> +H AbslHashValue( + H hash_state, + const std::basic_string<Char, std::char_traits<Char>, Alloc>& str) { + return H::combine( + H::combine_contiguous(std::move(hash_state), str.data(), str.size()), + str.size()); +} + // ----------------------------------------------------------------------------- // AbslHashValue for Sequence Containers // ----------------------------------------------------------------------------- @@ -631,7 +708,8 @@ struct is_hashable : std::integral_constant<bool, HashSelect::template Apply<T>::value> {}; // CityHashState -class CityHashState : public HashStateBase<CityHashState> { +class ABSL_DLL CityHashState + : public HashStateBase<CityHashState> { // absl::uint128 is not an alias or a thin wrapper around the intrinsic. // We use the intrinsic when available to improve performance. #ifdef ABSL_HAVE_INTRINSIC_INT128 @@ -710,6 +788,16 @@ class CityHashState : public HashStateBase<CityHashState> { std::integral_constant<int, 8> /* sizeof_size_t*/); + // Slow dispatch path for calls to CombineContiguousImpl with a size argument + // larger than PiecewiseChunkSize(). Has the same effect as calling + // CombineContiguousImpl() repeatedly with the chunk stride size. + static uint64_t CombineLargeContiguousImpl32(uint64_t state, + const unsigned char* first, + size_t len); + static uint64_t CombineLargeContiguousImpl64(uint64_t state, + const unsigned char* first, + size_t len); + // Reads 9 to 16 bytes from p. // The first 8 bytes are in .first, the rest (zero padded) bytes are in // .second. @@ -777,6 +865,9 @@ inline uint64_t CityHashState::CombineContiguousImpl( // multiplicative hash. uint64_t v; if (len > 8) { + if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { + return CombineLargeContiguousImpl32(state, first, len); + } v = absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), len); } else if (len >= 4) { v = Read4To8(first, len); @@ -797,6 +888,9 @@ inline uint64_t CityHashState::CombineContiguousImpl( // multiplicative hash. uint64_t v; if (len > 16) { + if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { + return CombineLargeContiguousImpl64(state, first, len); + } v = absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), len); } else if (len > 8) { auto p = Read9To16(first, len); @@ -813,7 +907,6 @@ inline uint64_t CityHashState::CombineContiguousImpl( return Mix(state, v); } - struct AggregateBarrier {}; // HashImpl @@ -850,8 +943,46 @@ template <typename T> H HashStateBase<H>::combine_contiguous(H state, const T* data, size_t size) { return hash_internal::hash_range_or_bytes(std::move(state), data, size); } + +// HashStateBase::PiecewiseCombiner::add_buffer() +template <typename H> +H PiecewiseCombiner::add_buffer(H state, const unsigned char* data, + size_t size) { + if (position_ + size < PiecewiseChunkSize()) { + // This partial chunk does not fill our existing buffer + memcpy(buf_ + position_, data, size); + position_ += size; + return state; + } + + // Complete the buffer and hash it + const size_t bytes_needed = PiecewiseChunkSize() - position_; + memcpy(buf_ + position_, data, bytes_needed); + state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize()); + data += bytes_needed; + size -= bytes_needed; + + // Hash whatever chunks we can without copying + while (size >= PiecewiseChunkSize()) { + state = H::combine_contiguous(std::move(state), data, PiecewiseChunkSize()); + data += PiecewiseChunkSize(); + size -= PiecewiseChunkSize(); + } + // Fill the buffer with the remainder + memcpy(buf_, data, size); + position_ = size; + return state; +} + +// HashStateBase::PiecewiseCombiner::finalize() +template <typename H> +H PiecewiseCombiner::finalize(H state) { + // Hash the remainder left in the buffer, which may be empty + return H::combine_contiguous(std::move(state), buf_, position_); +} + } // namespace hash_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HASH_INTERNAL_HASH_H_ diff --git a/absl/hash/internal/spy_hash_state.h b/absl/hash/internal/spy_hash_state.h index 57cd70b2..c0831208 100644 --- a/absl/hash/internal/spy_hash_state.h +++ b/absl/hash/internal/spy_hash_state.h @@ -25,7 +25,7 @@ #include "absl/strings/str_join.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace hash_internal { // SpyHashState is an implementation of the HashState API that simply @@ -147,6 +147,19 @@ class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> { static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state, const unsigned char* begin, size_t size) { + const size_t large_chunk_stride = PiecewiseChunkSize(); + if (size > large_chunk_stride) { + // Combining a large contiguous buffer must have the same effect as + // doing it piecewise by the stride length, followed by the (possibly + // empty) remainder. + while (size >= large_chunk_stride) { + hash_state = SpyHashStateImpl::combine_contiguous( + std::move(hash_state), begin, large_chunk_stride); + begin += large_chunk_stride; + size -= large_chunk_stride; + } + } + hash_state.hash_representation_.emplace_back( reinterpret_cast<const char*>(begin), size); return hash_state; @@ -212,7 +225,7 @@ void AbslHashValue(SpyHashStateImpl<T>, const U&); using SpyHashState = SpyHashStateImpl<void>; } // namespace hash_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel index f815ef94..2ba9d7cb 100644 --- a/absl/memory/BUILD.bazel +++ b/absl/memory/BUILD.bazel @@ -1,5 +1,5 @@ # -# 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. @@ -14,13 +14,12 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", - "ABSL_EXCEPTIONS_FLAG", - "ABSL_EXCEPTIONS_FLAG_LINKOPTS", ) package(default_visibility = ["//visibility:public"]) @@ -45,7 +44,6 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":memory", - "//absl/base", "//absl/base:core_headers", "@com_google_googletest//:gtest_main", ], @@ -56,10 +54,11 @@ cc_test( srcs = [ "memory_exception_safety_test.cc", ], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":memory", + "//absl/base:config", "//absl/base:exception_safety_testing", "@com_google_googletest//:gtest_main", ], diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt index 0a812203..78fb7e1b 100644 --- a/absl/memory/CMakeLists.txt +++ b/absl/memory/CMakeLists.txt @@ -36,7 +36,6 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::memory - absl::base absl::core_headers gmock_main ) @@ -48,11 +47,9 @@ absl_cc_test( "memory_exception_safety_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::memory + absl::config absl::exception_safety_testing gmock_main ) diff --git a/absl/memory/memory.h b/absl/memory/memory.h index 7f525e4c..513f7103 100644 --- a/absl/memory/memory.h +++ b/absl/memory/memory.h @@ -34,7 +34,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // Function Template: WrapUnique() @@ -93,11 +93,12 @@ struct MakeUniqueResult<T[N]> { } // namespace memory_internal -// gcc 4.8 has __cplusplus at 201301 but doesn't define make_unique. Other -// supported compilers either just define __cplusplus as 201103 but have -// make_unique (msvc), or have make_unique whenever __cplusplus > 201103 (clang) +// gcc 4.8 has __cplusplus at 201301 but the libstdc++ shipped with it doesn't +// define make_unique. Other supported compilers either just define __cplusplus +// as 201103 but have make_unique (msvc), or have make_unique whenever +// __cplusplus > 201103 (clang). #if (__cplusplus > 201103L || defined(_MSC_VER)) && \ - !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8) + !(defined(__GLIBCXX__) && !defined(__cpp_lib_make_unique)) using std::make_unique; #else // ----------------------------------------------------------------------------- @@ -688,7 +689,7 @@ void CopyRange(Allocator& alloc, Iterator destination, InputIterator first, } } } // namespace memory_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_MEMORY_MEMORY_H_ diff --git a/absl/memory/memory_exception_safety_test.cc b/absl/memory/memory_exception_safety_test.cc index f5b39b34..1df72614 100644 --- a/absl/memory/memory_exception_safety_test.cc +++ b/absl/memory/memory_exception_safety_test.cc @@ -14,18 +14,19 @@ #include "absl/memory/memory.h" +#include "absl/base/config.h" + +#ifdef ABSL_HAVE_EXCEPTIONS + #include "gtest/gtest.h" #include "absl/base/internal/exception_safety_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { constexpr int kLength = 50; using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>; -using ThrowerStorage = - absl::aligned_storage_t<sizeof(Thrower), alignof(Thrower)>; -using ThrowerList = std::array<ThrowerStorage, kLength>; TEST(MakeUnique, CheckForLeaks) { constexpr int kValue = 321; @@ -50,5 +51,7 @@ TEST(MakeUnique, CheckForLeaks) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl + +#endif // ABSL_HAVE_EXCEPTIONS diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel index e004b509..c06d2d97 100644 --- a/absl/meta/BUILD.bazel +++ b/absl/meta/BUILD.bazel @@ -1,3 +1,20 @@ +# +# 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. +# + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index ae7130dd..ba87d2f0 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -41,8 +41,18 @@ #include "absl/base/config.h" +// MSVC constructibility traits do not detect destructor properties and so our +// implementations should not use them as a source-of-truth. +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) +#define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1 +#endif + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN + +// Defined and documented later on in this file. +template <typename T> +struct is_trivially_destructible; // Defined and documented later on in this file. template <typename T> @@ -67,6 +77,20 @@ union SingleMemberUnion { #endif // defined(_MSC_VER) && !defined(__GNUC__) template <class T> +struct IsTriviallyMoveConstructibleObject + : std::integral_constant< + bool, std::is_move_constructible< + type_traits_internal::SingleMemberUnion<T>>::value && + absl::is_trivially_destructible<T>::value> {}; + +template <class T> +struct IsTriviallyCopyConstructibleObject + : std::integral_constant< + bool, std::is_copy_constructible< + type_traits_internal::SingleMemberUnion<T>>::value && + absl::is_trivially_destructible<T>::value> {}; + +template <class T> struct IsTriviallyMoveAssignableReference : std::false_type {}; template <class T> @@ -146,6 +170,18 @@ using IsMoveAssignableImpl = decltype(std::declval<T&>() = std::declval<T&&>()); } // namespace type_traits_internal +// MSVC 19.20 has a regression that causes our workarounds to fail, but their +// std forms now appear to be compliant. +#if defined(_MSC_VER) && !defined(__clang__) && (_MSC_VER >= 1920) + +template <typename T> +using is_copy_assignable = std::is_copy_assignable<T>; + +template <typename T> +using is_move_assignable = std::is_move_assignable<T>; + +#else + template <typename T> struct is_copy_assignable : type_traits_internal::is_detected< type_traits_internal::IsCopyAssignableImpl, T> { @@ -156,6 +192,8 @@ struct is_move_assignable : type_traits_internal::is_detected< type_traits_internal::IsMoveAssignableImpl, T> { }; +#endif + // void_t() // // Ignores the type of any its arguments and returns `void`. In general, this @@ -244,7 +282,7 @@ struct is_function // is_trivially_destructible() // -// Determines whether the passed type `T` is trivially destructable. +// Determines whether the passed type `T` is trivially destructible. // // This metafunction is designed to be a drop-in replacement for the C++11 // `std::is_trivially_destructible()` metafunction for platforms that have @@ -310,7 +348,9 @@ struct is_trivially_default_constructible : std::integral_constant<bool, __has_trivial_constructor(T) && std::is_default_constructible<T>::value && is_trivially_destructible<T>::value> { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ + !defined( \ + ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) private: static constexpr bool compliant = std::is_trivially_default_constructible<T>::value == @@ -341,10 +381,11 @@ template <typename T> struct is_trivially_move_constructible : std::conditional< std::is_object<T>::value && !std::is_array<T>::value, - std::is_move_constructible< - type_traits_internal::SingleMemberUnion<T>>, + type_traits_internal::IsTriviallyMoveConstructibleObject<T>, std::is_reference<T>>::type::type { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ + !defined( \ + ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) private: static constexpr bool compliant = std::is_trivially_move_constructible<T>::value == @@ -375,10 +416,11 @@ template <typename T> struct is_trivially_copy_constructible : std::conditional< std::is_object<T>::value && !std::is_array<T>::value, - std::is_copy_constructible< - type_traits_internal::SingleMemberUnion<T>>, + type_traits_internal::IsTriviallyCopyConstructibleObject<T>, std::is_lvalue_reference<T>>::type::type { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ + !defined( \ + ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) private: static constexpr bool compliant = std::is_trivially_copy_constructible<T>::value == @@ -410,7 +452,8 @@ struct is_trivially_copy_constructible template <typename T> struct is_trivially_move_assignable : std::conditional< - std::is_object<T>::value && !std::is_array<T>::value, + std::is_object<T>::value && !std::is_array<T>::value && + std::is_move_assignable<T>::value, std::is_move_assignable<type_traits_internal::SingleMemberUnion<T>>, type_traits_internal::IsTriviallyMoveAssignableReference<T>>::type:: type { @@ -710,7 +753,7 @@ using swap_internal::Swap; using swap_internal::StdSwapIsUnconstrained; } // namespace type_traits_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_META_TYPE_TRAITS_H_ diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc index a7a9c5c9..1aafd0d4 100644 --- a/absl/meta/type_traits_test.cc +++ b/absl/meta/type_traits_test.cc @@ -347,21 +347,6 @@ class Base { virtual ~Base() {} }; -// In GCC/Clang, std::is_trivially_constructible requires that the destructor is -// trivial. However, MSVC doesn't require that. This results in different -// behavior when checking is_trivially_constructible on any type with -// nontrivial destructor. Since absl::is_trivially_default_constructible and -// absl::is_trivially_copy_constructible both follows Clang/GCC's interpretation -// and check is_trivially_destructible, it results in inconsistency with -// std::is_trivially_xxx_constructible on MSVC. This macro is used to work -// around this issue in test. In practice, a trivially constructible type -// should also be trivially destructible. -// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 -// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116 -#ifndef _MSC_VER -#define ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE 1 -#endif - // Old versions of libc++, around Clang 3.5 to 3.6, consider deleted destructors // as also being trivial. With the resolution of CWG 1928 and CWG 1734, this // is no longer considered true and has thus been amended. @@ -499,11 +484,9 @@ TEST(TypeTraitsTest, TestTrivialDefaultCtor) { EXPECT_FALSE( absl::is_trivially_default_constructible<DeletedDefaultCtor>::value); -#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE // types with nontrivial destructor are nontrivial EXPECT_FALSE( absl::is_trivially_default_constructible<NontrivialDestructor>::value); -#endif // types with vtables EXPECT_FALSE(absl::is_trivially_default_constructible<Base>::value); @@ -546,6 +529,28 @@ TEST(TypeTraitsTest, TestTrivialDefaultCtor) { #endif } +// GCC prior to 7.4 had a bug in its trivially-constructible traits +// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654). +// This test makes sure that we do not depend on the trait in these cases when +// implementing absl triviality traits. + +template <class T> +struct BadConstructors { + BadConstructors() { static_assert(T::value, ""); } + + BadConstructors(BadConstructors&&) { static_assert(T::value, ""); } + + BadConstructors(const BadConstructors&) { static_assert(T::value, ""); } +}; + +TEST(TypeTraitsTest, TestTrivialityBadConstructors) { + using BadType = BadConstructors<int>; + + EXPECT_FALSE(absl::is_trivially_default_constructible<BadType>::value); + EXPECT_FALSE(absl::is_trivially_move_constructible<BadType>::value); + EXPECT_FALSE(absl::is_trivially_copy_constructible<BadType>::value); +} + TEST(TypeTraitsTest, TestTrivialMoveCtor) { // Verify that arithmetic types and pointers have trivial move // constructors. @@ -585,11 +590,9 @@ TEST(TypeTraitsTest, TestTrivialMoveCtor) { EXPECT_FALSE( absl::is_trivially_move_constructible<NonCopyableOrMovable>::value); -#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE // type with nontrivial destructor are nontrivial move construbtible EXPECT_FALSE( absl::is_trivially_move_constructible<NontrivialDestructor>::value); -#endif // types with vtables EXPECT_FALSE(absl::is_trivially_move_constructible<Base>::value); @@ -660,11 +663,9 @@ TEST(TypeTraitsTest, TestTrivialCopyCtor) { EXPECT_FALSE( absl::is_trivially_copy_constructible<NonCopyableOrMovable>::value); -#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE // type with nontrivial destructor are nontrivial copy construbtible EXPECT_FALSE( absl::is_trivially_copy_constructible<NontrivialDestructor>::value); -#endif // types with vtables EXPECT_FALSE(absl::is_trivially_copy_constructible<Base>::value); diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel index d9b561df..e09e52d2 100644 --- a/absl/numeric/BUILD.bazel +++ b/absl/numeric/BUILD.bazel @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index a22f1e3e..b605a870 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -23,10 +23,10 @@ #include <type_traits> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN -const uint128 kuint128max = MakeUint128(std::numeric_limits<uint64_t>::max(), - std::numeric_limits<uint64_t>::max()); +ABSL_DLL const uint128 kuint128max = MakeUint128( + std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max()); namespace { @@ -245,7 +245,112 @@ std::ostream& operator<<(std::ostream& os, uint128 v) { return os << rep; } -} // inline namespace lts_2019_08_08 +namespace { + +uint128 UnsignedAbsoluteValue(int128 v) { + // Cast to uint128 before possibly negating because -Int128Min() is undefined. + return Int128High64(v) < 0 ? -uint128(v) : uint128(v); +} + +} // namespace + +#if !defined(ABSL_HAVE_INTRINSIC_INT128) +namespace { + +template <typename T> +int128 MakeInt128FromFloat(T v) { + // Conversion when v is NaN or cannot fit into int128 would be undefined + // behavior if using an intrinsic 128-bit integer. + assert(std::isfinite(v) && (std::numeric_limits<T>::max_exponent <= 127 || + (v >= -std::ldexp(static_cast<T>(1), 127) && + v < std::ldexp(static_cast<T>(1), 127)))); + + // We must convert the absolute value and then negate as needed, because + // floating point types are typically sign-magnitude. Otherwise, the + // difference between the high and low 64 bits when interpreted as two's + // complement overwhelms the precision of the mantissa. + uint128 result = v < 0 ? -MakeUint128FromFloat(-v) : MakeUint128FromFloat(v); + return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)), + Uint128Low64(result)); +} + +} // namespace + +int128::int128(float v) : int128(MakeInt128FromFloat(v)) {} +int128::int128(double v) : int128(MakeInt128FromFloat(v)) {} +int128::int128(long double v) : int128(MakeInt128FromFloat(v)) {} + +int128 operator/(int128 lhs, int128 rhs) { + assert(lhs != Int128Min() || rhs != -1); // UB on two's complement. + + uint128 quotient = 0; + uint128 remainder = 0; + DivModImpl(UnsignedAbsoluteValue(lhs), UnsignedAbsoluteValue(rhs), + "ient, &remainder); + if ((Int128High64(lhs) < 0) != (Int128High64(rhs) < 0)) quotient = -quotient; + return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(quotient)), + Uint128Low64(quotient)); +} + +int128 operator%(int128 lhs, int128 rhs) { + assert(lhs != Int128Min() || rhs != -1); // UB on two's complement. + + uint128 quotient = 0; + uint128 remainder = 0; + DivModImpl(UnsignedAbsoluteValue(lhs), UnsignedAbsoluteValue(rhs), + "ient, &remainder); + if (Int128High64(lhs) < 0) remainder = -remainder; + return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(remainder)), + Uint128Low64(remainder)); +} +#endif // ABSL_HAVE_INTRINSIC_INT128 + +std::ostream& operator<<(std::ostream& os, int128 v) { + std::ios_base::fmtflags flags = os.flags(); + std::string rep; + + // Add the sign if needed. + bool print_as_decimal = + (flags & std::ios::basefield) == std::ios::dec || + (flags & std::ios::basefield) == std::ios_base::fmtflags(); + if (print_as_decimal) { + if (Int128High64(v) < 0) { + rep = "-"; + } else if (flags & std::ios::showpos) { + rep = "+"; + } + } + + rep.append(Uint128ToFormattedString( + print_as_decimal ? UnsignedAbsoluteValue(v) : uint128(v), os.flags())); + + // Add the requisite padding. + std::streamsize width = os.width(0); + if (static_cast<size_t>(width) > rep.size()) { + switch (flags & std::ios::adjustfield) { + case std::ios::left: + rep.append(width - rep.size(), os.fill()); + break; + case std::ios::internal: + if (print_as_decimal && (rep[0] == '+' || rep[0] == '-')) { + rep.insert(1, width - rep.size(), os.fill()); + } else if ((flags & std::ios::basefield) == std::ios::hex && + (flags & std::ios::showbase) && v != 0) { + rep.insert(2, width - rep.size(), os.fill()); + } else { + rep.insert(0, width - rep.size(), os.fill()); + } + break; + default: // std::ios::right + rep.insert(0, width - rep.size(), os.fill()); + break; + } + } + + return os << rep; +} + +ABSL_NAMESPACE_END } // namespace absl namespace std { @@ -272,4 +377,28 @@ constexpr int numeric_limits<absl::uint128>::max_exponent; constexpr int numeric_limits<absl::uint128>::max_exponent10; constexpr bool numeric_limits<absl::uint128>::traps; constexpr bool numeric_limits<absl::uint128>::tinyness_before; + +constexpr bool numeric_limits<absl::int128>::is_specialized; +constexpr bool numeric_limits<absl::int128>::is_signed; +constexpr bool numeric_limits<absl::int128>::is_integer; +constexpr bool numeric_limits<absl::int128>::is_exact; +constexpr bool numeric_limits<absl::int128>::has_infinity; +constexpr bool numeric_limits<absl::int128>::has_quiet_NaN; +constexpr bool numeric_limits<absl::int128>::has_signaling_NaN; +constexpr float_denorm_style numeric_limits<absl::int128>::has_denorm; +constexpr bool numeric_limits<absl::int128>::has_denorm_loss; +constexpr float_round_style numeric_limits<absl::int128>::round_style; +constexpr bool numeric_limits<absl::int128>::is_iec559; +constexpr bool numeric_limits<absl::int128>::is_bounded; +constexpr bool numeric_limits<absl::int128>::is_modulo; +constexpr int numeric_limits<absl::int128>::digits; +constexpr int numeric_limits<absl::int128>::digits10; +constexpr int numeric_limits<absl::int128>::max_digits10; +constexpr int numeric_limits<absl::int128>::radix; +constexpr int numeric_limits<absl::int128>::min_exponent; +constexpr int numeric_limits<absl::int128>::min_exponent10; +constexpr int numeric_limits<absl::int128>::max_exponent; +constexpr int numeric_limits<absl::int128>::max_exponent10; +constexpr bool numeric_limits<absl::int128>::traps; +constexpr bool numeric_limits<absl::int128>::tinyness_before; } // namespace std diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index a9693a27..636e3a5b 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -17,10 +17,7 @@ // File: int128.h // ----------------------------------------------------------------------------- // -// This header file defines 128-bit integer types. -// -// Currently, this file defines `uint128`, an unsigned 128-bit integer; -// a signed 128-bit integer is forthcoming. +// This header file defines 128-bit integer types, `uint128` and `int128`. #ifndef ABSL_NUMERIC_INT128_H_ #define ABSL_NUMERIC_INT128_H_ @@ -52,7 +49,9 @@ #endif // defined(_MSC_VER) namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN + +class int128; // uint128 // @@ -117,6 +116,7 @@ class constexpr uint128(__int128 v); // NOLINT(runtime/explicit) constexpr uint128(unsigned __int128 v); // NOLINT(runtime/explicit) #endif // ABSL_HAVE_INTRINSIC_INT128 + constexpr uint128(int128 v); // NOLINT(runtime/explicit) explicit uint128(float v); explicit uint128(double v); explicit uint128(long double v); @@ -132,6 +132,7 @@ class uint128& operator=(__int128 v); uint128& operator=(unsigned __int128 v); #endif // ABSL_HAVE_INTRINSIC_INT128 + uint128& operator=(int128 v); // Conversion operators to other arithmetic types constexpr explicit operator bool() const; @@ -233,7 +234,7 @@ class // Prefer to use the constexpr `Uint128Max()`. // // TODO(absl-team) deprecate kuint128max once migration tool is released. -extern const uint128 kuint128max; +ABSL_DLL extern const uint128 kuint128max; // allow uint128 to be logged std::ostream& operator<<(std::ostream& os, uint128 v); @@ -245,7 +246,7 @@ constexpr uint128 Uint128Max() { (std::numeric_limits<uint64_t>::max)()); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // Specialized numeric_limits for uint128. @@ -293,13 +294,246 @@ class numeric_limits<absl::uint128> { }; } // namespace std -// TODO(absl-team): Implement signed 128-bit type +namespace absl { +ABSL_NAMESPACE_BEGIN + +// int128 +// +// A signed 128-bit integer type. The API is meant to mimic an intrinsic +// integral type as closely as is practical, including exhibiting undefined +// behavior in analogous cases (e.g. division by zero). +// +// An `int128` supports the following: +// +// * Implicit construction from integral types +// * Explicit conversion to integral types +// +// However, an `int128` differs from intrinsic integral types in the following +// ways: +// +// * It is not implicitly convertible to other integral types. +// * Requires explicit construction from and conversion to floating point +// types. + +// Additionally, if your compiler supports `__int128`, `int128` is +// interoperable with that type. (Abseil checks for this compatibility through +// the `ABSL_HAVE_INTRINSIC_INT128` macro.) +// +// The design goal for `int128` is that it will be compatible with a future +// `int128_t`, if that type becomes a part of the standard. +// +// Example: +// +// float y = absl::int128(17); // Error. int128 cannot be implicitly +// // converted to float. +// +// absl::int128 v; +// int64_t i = v; // Error +// int64_t i = static_cast<int64_t>(v); // OK +// +class int128 { + public: + int128() = default; + + // Constructors from arithmetic types + constexpr int128(int v); // NOLINT(runtime/explicit) + constexpr int128(unsigned int v); // NOLINT(runtime/explicit) + constexpr int128(long v); // NOLINT(runtime/int) + constexpr int128(unsigned long v); // NOLINT(runtime/int) + constexpr int128(long long v); // NOLINT(runtime/int) + constexpr int128(unsigned long long v); // NOLINT(runtime/int) +#ifdef ABSL_HAVE_INTRINSIC_INT128 + constexpr int128(__int128 v); // NOLINT(runtime/explicit) + constexpr explicit int128(unsigned __int128 v); +#endif // ABSL_HAVE_INTRINSIC_INT128 + constexpr explicit int128(uint128 v); + explicit int128(float v); + explicit int128(double v); + explicit int128(long double v); + + // Assignment operators from arithmetic types + int128& operator=(int v); + int128& operator=(unsigned int v); + int128& operator=(long v); // NOLINT(runtime/int) + int128& operator=(unsigned long v); // NOLINT(runtime/int) + int128& operator=(long long v); // NOLINT(runtime/int) + int128& operator=(unsigned long long v); // NOLINT(runtime/int) +#ifdef ABSL_HAVE_INTRINSIC_INT128 + int128& operator=(__int128 v); +#endif // ABSL_HAVE_INTRINSIC_INT128 + + // Conversion operators to other arithmetic types + constexpr explicit operator bool() const; + constexpr explicit operator char() const; + constexpr explicit operator signed char() const; + constexpr explicit operator unsigned char() const; + constexpr explicit operator char16_t() const; + constexpr explicit operator char32_t() const; + constexpr explicit operator ABSL_INTERNAL_WCHAR_T() const; + constexpr explicit operator short() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned short() const; + constexpr explicit operator int() const; + constexpr explicit operator unsigned int() const; + constexpr explicit operator long() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator long long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long long() const; +#ifdef ABSL_HAVE_INTRINSIC_INT128 + constexpr explicit operator __int128() const; + constexpr explicit operator unsigned __int128() const; +#endif // ABSL_HAVE_INTRINSIC_INT128 + explicit operator float() const; + explicit operator double() const; + explicit operator long double() const; + + // Trivial copy constructor, assignment operator and destructor. + + // Arithmetic operators + int128& operator+=(int128 other); + int128& operator-=(int128 other); + int128& operator*=(int128 other); + int128& operator/=(int128 other); + int128& operator%=(int128 other); + int128 operator++(int); // postfix increment: i++ + int128 operator--(int); // postfix decrement: i-- + int128& operator++(); // prefix increment: ++i + int128& operator--(); // prefix decrement: --i + int128& operator&=(int128 other); + int128& operator|=(int128 other); + int128& operator^=(int128 other); + int128& operator<<=(int amount); + int128& operator>>=(int amount); + + // Int128Low64() + // + // Returns the lower 64-bit value of a `int128` value. + friend constexpr uint64_t Int128Low64(int128 v); + + // Int128High64() + // + // Returns the higher 64-bit value of a `int128` value. + friend constexpr int64_t Int128High64(int128 v); + + // MakeInt128() + // + // Constructs a `int128` numeric value from two 64-bit integers. Note that + // signedness is conveyed in the upper `high` value. + // + // (absl::int128(1) << 64) * high + low + // + // Note that this factory function is the only way to construct a `int128` + // from integer values greater than 2^64 or less than -2^64. + // + // Example: + // + // absl::int128 big = absl::MakeInt128(1, 0); + // absl::int128 big_n = absl::MakeInt128(-1, 0); + friend constexpr int128 MakeInt128(int64_t high, uint64_t low); + + // Int128Max() + // + // Returns the maximum value for a 128-bit signed integer. + friend constexpr int128 Int128Max(); + + // Int128Min() + // + // Returns the minimum value for a 128-bit signed integer. + friend constexpr int128 Int128Min(); + + // Support for absl::Hash. + template <typename H> + friend H AbslHashValue(H h, int128 v) { + return H::combine(std::move(h), Int128High64(v), Int128Low64(v)); + } + + private: + constexpr int128(int64_t high, uint64_t low); + +#if defined(ABSL_HAVE_INTRINSIC_INT128) + __int128 v_; +#else // ABSL_HAVE_INTRINSIC_INT128 +#if defined(ABSL_IS_LITTLE_ENDIAN) + uint64_t lo_; + int64_t hi_; +#elif defined(ABSL_IS_BIG_ENDIAN) + int64_t hi_; + uint64_t lo_; +#else // byte order +#error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order +#endif // ABSL_HAVE_INTRINSIC_INT128 +}; + +std::ostream& operator<<(std::ostream& os, int128 v); + +// TODO(absl-team) add operator>>(std::istream&, int128) + +constexpr int128 Int128Max() { + return int128((std::numeric_limits<int64_t>::max)(), + (std::numeric_limits<uint64_t>::max)()); +} + +constexpr int128 Int128Min() { + return int128((std::numeric_limits<int64_t>::min)(), 0); +} + +ABSL_NAMESPACE_END +} // namespace absl + +// Specialized numeric_limits for int128. +namespace std { +template <> +class numeric_limits<absl::int128> { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 127; + static constexpr int digits10 = 38; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; +#ifdef ABSL_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits<__int128>::traps; +#else // ABSL_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits<uint64_t>::traps; +#endif // ABSL_HAVE_INTRINSIC_INT128 + static constexpr bool tinyness_before = false; + + static constexpr absl::int128 (min)() { return absl::Int128Min(); } + static constexpr absl::int128 lowest() { return absl::Int128Min(); } + static constexpr absl::int128 (max)() { return absl::Int128Max(); } + static constexpr absl::int128 epsilon() { return 0; } + static constexpr absl::int128 round_error() { return 0; } + static constexpr absl::int128 infinity() { return 0; } + static constexpr absl::int128 quiet_NaN() { return 0; } + static constexpr absl::int128 signaling_NaN() { return 0; } + static constexpr absl::int128 denorm_min() { return 0; } +}; +} // namespace std // -------------------------------------------------------------------------- // Implementation details follow // -------------------------------------------------------------------------- namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN constexpr uint128 MakeUint128(uint64_t high, uint64_t low) { return uint128(high, low); @@ -342,6 +576,10 @@ inline uint128& uint128::operator=(unsigned __int128 v) { } #endif // ABSL_HAVE_INTRINSIC_INT128 +inline uint128& uint128::operator=(int128 v) { + return *this = uint128(v); +} + // Arithmetic operators. uint128 operator<<(uint128 lhs, int amount); @@ -423,6 +661,9 @@ constexpr uint128::uint128(unsigned __int128 v) hi_{static_cast<uint64_t>(v >> 64)} {} #endif // ABSL_HAVE_INTRINSIC_INT128 +constexpr uint128::uint128(int128 v) + : lo_{Int128Low64(v)}, hi_{static_cast<uint64_t>(Int128High64(v))} {} + #elif defined(ABSL_IS_BIG_ENDIAN) constexpr uint128::uint128(uint64_t high, uint64_t low) @@ -453,6 +694,9 @@ constexpr uint128::uint128(unsigned __int128 v) lo_{static_cast<uint64_t>(v & ~uint64_t{0})} {} #endif // ABSL_HAVE_INTRINSIC_INT128 +constexpr uint128::uint128(int128 v) + : hi_{static_cast<uint64_t>(Int128High64(v))}, lo_{Int128Low64(v)} {} + #else // byte order #error "Unsupported byte order: must be little-endian or big-endian." #endif // byte order @@ -722,13 +966,124 @@ inline uint128& uint128::operator--() { return *this; } +constexpr int128 MakeInt128(int64_t high, uint64_t low) { + return int128(high, low); +} + +// Assignment from integer types. +inline int128& int128::operator=(int v) { + return *this = int128(v); +} + +inline int128& int128::operator=(unsigned int v) { + return *this = int128(v); +} + +inline int128& int128::operator=(long v) { // NOLINT(runtime/int) + return *this = int128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(unsigned long v) { + return *this = int128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(long long v) { + return *this = int128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(unsigned long long v) { + return *this = int128(v); +} + +// Arithmetic operators. + +int128 operator+(int128 lhs, int128 rhs); +int128 operator-(int128 lhs, int128 rhs); +int128 operator*(int128 lhs, int128 rhs); +int128 operator/(int128 lhs, int128 rhs); +int128 operator%(int128 lhs, int128 rhs); +int128 operator|(int128 lhs, int128 rhs); +int128 operator&(int128 lhs, int128 rhs); +int128 operator^(int128 lhs, int128 rhs); +int128 operator<<(int128 lhs, int amount); +int128 operator>>(int128 lhs, int amount); + +inline int128& int128::operator+=(int128 other) { + *this = *this + other; + return *this; +} + +inline int128& int128::operator-=(int128 other) { + *this = *this - other; + return *this; +} + +inline int128& int128::operator*=(int128 other) { + *this = *this * other; + return *this; +} + +inline int128& int128::operator/=(int128 other) { + *this = *this / other; + return *this; +} + +inline int128& int128::operator%=(int128 other) { + *this = *this % other; + return *this; +} + +inline int128& int128::operator|=(int128 other) { + *this = *this | other; + return *this; +} + +inline int128& int128::operator&=(int128 other) { + *this = *this & other; + return *this; +} + +inline int128& int128::operator^=(int128 other) { + *this = *this ^ other; + return *this; +} + +inline int128& int128::operator<<=(int amount) { + *this = *this << amount; + return *this; +} + +inline int128& int128::operator>>=(int amount) { + *this = *this >> amount; + return *this; +} + +namespace int128_internal { + +// Casts from unsigned to signed while preserving the underlying binary +// representation. +constexpr int64_t BitCastToSigned(uint64_t v) { + // Casting an unsigned integer to a signed integer of the same + // width is implementation defined behavior if the source value would not fit + // in the destination type. We step around it with a roundtrip bitwise not + // operation to make sure this function remains constexpr. Clang, GCC, and + // MSVC optimize this to a no-op on x86-64. + return v & (uint64_t{1} << 63) ? ~static_cast<int64_t>(~v) + : static_cast<int64_t>(v); +} + +} // namespace int128_internal + #if defined(ABSL_HAVE_INTRINSIC_INT128) -#include "absl/numeric/int128_have_intrinsic.inc" +#include "absl/numeric/int128_have_intrinsic.inc" // IWYU pragma: export #else // ABSL_HAVE_INTRINSIC_INT128 -#include "absl/numeric/int128_no_intrinsic.inc" +#include "absl/numeric/int128_no_intrinsic.inc" // IWYU pragma: export #endif // ABSL_HAVE_INTRINSIC_INT128 -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #undef ABSL_INTERNAL_WCHAR_T diff --git a/absl/numeric/int128_have_intrinsic.inc b/absl/numeric/int128_have_intrinsic.inc index c7ea6834..d6c76dd3 100644 --- a/absl/numeric/int128_have_intrinsic.inc +++ b/absl/numeric/int128_have_intrinsic.inc @@ -16,3 +16,287 @@ // This file contains :int128 implementation details that depend on internal // representation when ABSL_HAVE_INTRINSIC_INT128 is defined. This file is // included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined. + +namespace int128_internal { + +// Casts from unsigned to signed while preserving the underlying binary +// representation. +constexpr __int128 BitCastToSigned(unsigned __int128 v) { + // Casting an unsigned integer to a signed integer of the same + // width is implementation defined behavior if the source value would not fit + // in the destination type. We step around it with a roundtrip bitwise not + // operation to make sure this function remains constexpr. Clang and GCC + // optimize this to a no-op on x86-64. + return v & (static_cast<unsigned __int128>(1) << 127) + ? ~static_cast<__int128>(~v) + : static_cast<__int128>(v); +} + +} // namespace int128_internal + +inline int128& int128::operator=(__int128 v) { + v_ = v; + return *this; +} + +constexpr uint64_t Int128Low64(int128 v) { + return static_cast<uint64_t>(v.v_ & ~uint64_t{0}); +} + +constexpr int64_t Int128High64(int128 v) { + // Initially cast to unsigned to prevent a right shift on a negative value. + return int128_internal::BitCastToSigned( + static_cast<uint64_t>(static_cast<unsigned __int128>(v.v_) >> 64)); +} + +constexpr int128::int128(int64_t high, uint64_t low) + // Initially cast to unsigned to prevent a left shift that overflows. + : v_(int128_internal::BitCastToSigned(static_cast<unsigned __int128>(high) + << 64) | + low) {} + + +constexpr int128::int128(int v) : v_{v} {} + +constexpr int128::int128(long v) : v_{v} {} // NOLINT(runtime/int) + +constexpr int128::int128(long long v) : v_{v} {} // NOLINT(runtime/int) + +constexpr int128::int128(__int128 v) : v_{v} {} + +constexpr int128::int128(unsigned int v) : v_{v} {} + +constexpr int128::int128(unsigned long v) : v_{v} {} // NOLINT(runtime/int) + +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long long v) : v_{v} {} + +constexpr int128::int128(unsigned __int128 v) : v_{static_cast<__int128>(v)} {} + +inline int128::int128(float v) { + v_ = static_cast<__int128>(v); +} + +inline int128::int128(double v) { + v_ = static_cast<__int128>(v); +} + +inline int128::int128(long double v) { + v_ = static_cast<__int128>(v); +} + +constexpr int128::int128(uint128 v) : v_{static_cast<__int128>(v)} {} + +constexpr int128::operator bool() const { return static_cast<bool>(v_); } + +constexpr int128::operator char() const { return static_cast<char>(v_); } + +constexpr int128::operator signed char() const { + return static_cast<signed char>(v_); +} + +constexpr int128::operator unsigned char() const { + return static_cast<unsigned char>(v_); +} + +constexpr int128::operator char16_t() const { + return static_cast<char16_t>(v_); +} + +constexpr int128::operator char32_t() const { + return static_cast<char32_t>(v_); +} + +constexpr int128::operator ABSL_INTERNAL_WCHAR_T() const { + return static_cast<ABSL_INTERNAL_WCHAR_T>(v_); +} + +constexpr int128::operator short() const { // NOLINT(runtime/int) + return static_cast<short>(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator unsigned short() const { // NOLINT(runtime/int) + return static_cast<unsigned short>(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator int() const { + return static_cast<int>(v_); +} + +constexpr int128::operator unsigned int() const { + return static_cast<unsigned int>(v_); +} + +constexpr int128::operator long() const { // NOLINT(runtime/int) + return static_cast<long>(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator unsigned long() const { // NOLINT(runtime/int) + return static_cast<unsigned long>(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator long long() const { // NOLINT(runtime/int) + return static_cast<long long>(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int) + return static_cast<unsigned long long>(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator __int128() const { return v_; } + +constexpr int128::operator unsigned __int128() const { + return static_cast<unsigned __int128>(v_); +} + +// Clang on PowerPC sometimes produces incorrect __int128 to floating point +// conversions. In that case, we do the conversion with a similar implementation +// to the conversion operators in int128_no_intrinsic.inc. +#if defined(__clang__) && !defined(__ppc64__) +inline int128::operator float() const { return static_cast<float>(v_); } + +inline int128::operator double () const { return static_cast<double>(v_); } + +inline int128::operator long double() const { + return static_cast<long double>(v_); +} + +#else // Clang on PowerPC +// Forward declaration for conversion operators to floating point types. +int128 operator-(int128 v); +bool operator!=(int128 lhs, int128 rhs); + +inline int128::operator float() const { + // We must convert the absolute value and then negate as needed, because + // floating point types are typically sign-magnitude. Otherwise, the + // difference between the high and low 64 bits when interpreted as two's + // complement overwhelms the precision of the mantissa. + // + // Also check to make sure we don't negate Int128Min() + return v_ < 0 && *this != Int128Min() + ? -static_cast<float>(-*this) + : static_cast<float>(Int128Low64(*this)) + + std::ldexp(static_cast<float>(Int128High64(*this)), 64); +} + +inline int128::operator double() const { + // See comment in int128::operator float() above. + return v_ < 0 && *this != Int128Min() + ? -static_cast<double>(-*this) + : static_cast<double>(Int128Low64(*this)) + + std::ldexp(static_cast<double>(Int128High64(*this)), 64); +} + +inline int128::operator long double() const { + // See comment in int128::operator float() above. + return v_ < 0 && *this != Int128Min() + ? -static_cast<long double>(-*this) + : static_cast<long double>(Int128Low64(*this)) + + std::ldexp(static_cast<long double>(Int128High64(*this)), + 64); +} +#endif // Clang on PowerPC + +// Comparison operators. + +inline bool operator==(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) == static_cast<__int128>(rhs); +} + +inline bool operator!=(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) != static_cast<__int128>(rhs); +} + +inline bool operator<(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) < static_cast<__int128>(rhs); +} + +inline bool operator>(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) > static_cast<__int128>(rhs); +} + +inline bool operator<=(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) <= static_cast<__int128>(rhs); +} + +inline bool operator>=(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) >= static_cast<__int128>(rhs); +} + +// Unary operators. + +inline int128 operator-(int128 v) { + return -static_cast<__int128>(v); +} + +inline bool operator!(int128 v) { + return !static_cast<__int128>(v); +} + +inline int128 operator~(int128 val) { + return ~static_cast<__int128>(val); +} + +// Arithmetic operators. + +inline int128 operator+(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) + static_cast<__int128>(rhs); +} + +inline int128 operator-(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) - static_cast<__int128>(rhs); +} + +inline int128 operator*(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) * static_cast<__int128>(rhs); +} + +inline int128 operator/(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) / static_cast<__int128>(rhs); +} + +inline int128 operator%(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) % static_cast<__int128>(rhs); +} + +inline int128 int128::operator++(int) { + int128 tmp(*this); + ++v_; + return tmp; +} + +inline int128 int128::operator--(int) { + int128 tmp(*this); + --v_; + return tmp; +} + +inline int128& int128::operator++() { + ++v_; + return *this; +} + +inline int128& int128::operator--() { + --v_; + return *this; +} + +inline int128 operator|(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) | static_cast<__int128>(rhs); +} + +inline int128 operator&(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) & static_cast<__int128>(rhs); +} + +inline int128 operator^(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) ^ static_cast<__int128>(rhs); +} + +inline int128 operator<<(int128 lhs, int amount) { + return static_cast<__int128>(lhs) << amount; +} + +inline int128 operator>>(int128 lhs, int amount) { + return static_cast<__int128>(lhs) >> amount; +} diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc index 046cb9b3..c753771a 100644 --- a/absl/numeric/int128_no_intrinsic.inc +++ b/absl/numeric/int128_no_intrinsic.inc @@ -16,3 +16,293 @@ // This file contains :int128 implementation details that depend on internal // representation when ABSL_HAVE_INTRINSIC_INT128 is *not* defined. This file // is included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined. + +constexpr uint64_t Int128Low64(int128 v) { return v.lo_; } + +constexpr int64_t Int128High64(int128 v) { return v.hi_; } + +#if defined(ABSL_IS_LITTLE_ENDIAN) + +constexpr int128::int128(int64_t high, uint64_t low) : + lo_(low), hi_(high) {} + +constexpr int128::int128(int v) + : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {} +constexpr int128::int128(long v) // NOLINT(runtime/int) + : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {} +constexpr int128::int128(long long v) // NOLINT(runtime/int) + : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {} + +constexpr int128::int128(unsigned int v) : lo_{v}, hi_{0} {} +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long v) : lo_{v}, hi_{0} {} +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long long v) : lo_{v}, hi_{0} {} + +constexpr int128::int128(uint128 v) + : lo_{Uint128Low64(v)}, hi_{static_cast<int64_t>(Uint128High64(v))} {} + +#elif defined(ABSL_IS_BIG_ENDIAN) + +constexpr int128::int128(int64_t high, uint64_t low) : + hi_{high}, lo_{low} {} + +constexpr int128::int128(int v) + : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {} +constexpr int128::int128(long v) // NOLINT(runtime/int) + : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {} +constexpr int128::int128(long long v) // NOLINT(runtime/int) + : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {} + +constexpr int128::int128(unsigned int v) : hi_{0}, lo_{v} {} +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long v) : hi_{0}, lo_{v} {} +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long long v) : hi_{0}, lo_{v} {} + +constexpr int128::int128(uint128 v) + : hi_{static_cast<int64_t>(Uint128High64(v))}, lo_{Uint128Low64(v)} {} + +#else // byte order +#error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order + +constexpr int128::operator bool() const { return lo_ || hi_; } + +constexpr int128::operator char() const { + // NOLINTNEXTLINE(runtime/int) + return static_cast<char>(static_cast<long long>(*this)); +} + +constexpr int128::operator signed char() const { + // NOLINTNEXTLINE(runtime/int) + return static_cast<signed char>(static_cast<long long>(*this)); +} + +constexpr int128::operator unsigned char() const { + return static_cast<unsigned char>(lo_); +} + +constexpr int128::operator char16_t() const { + return static_cast<char16_t>(lo_); +} + +constexpr int128::operator char32_t() const { + return static_cast<char32_t>(lo_); +} + +constexpr int128::operator ABSL_INTERNAL_WCHAR_T() const { + // NOLINTNEXTLINE(runtime/int) + return static_cast<ABSL_INTERNAL_WCHAR_T>(static_cast<long long>(*this)); +} + +constexpr int128::operator short() const { // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + return static_cast<short>(static_cast<long long>(*this)); +} + +constexpr int128::operator unsigned short() const { // NOLINT(runtime/int) + return static_cast<unsigned short>(lo_); // NOLINT(runtime/int) +} + +constexpr int128::operator int() const { + // NOLINTNEXTLINE(runtime/int) + return static_cast<int>(static_cast<long long>(*this)); +} + +constexpr int128::operator unsigned int() const { + return static_cast<unsigned int>(lo_); +} + +constexpr int128::operator long() const { // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + return static_cast<long>(static_cast<long long>(*this)); +} + +constexpr int128::operator unsigned long() const { // NOLINT(runtime/int) + return static_cast<unsigned long>(lo_); // NOLINT(runtime/int) +} + +constexpr int128::operator long long() const { // NOLINT(runtime/int) + // We don't bother checking the value of hi_. If *this < 0, lo_'s high bit + // must be set in order for the value to fit into a long long. Conversely, if + // lo_'s high bit is set, *this must be < 0 for the value to fit. + return int128_internal::BitCastToSigned(lo_); +} + +constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int) + return static_cast<unsigned long long>(lo_); // NOLINT(runtime/int) +} + +// Forward declaration for conversion operators to floating point types. +int128 operator-(int128 v); +bool operator!=(int128 lhs, int128 rhs); + +inline int128::operator float() const { + // We must convert the absolute value and then negate as needed, because + // floating point types are typically sign-magnitude. Otherwise, the + // difference between the high and low 64 bits when interpreted as two's + // complement overwhelms the precision of the mantissa. + // + // Also check to make sure we don't negate Int128Min() + return hi_ < 0 && *this != Int128Min() + ? -static_cast<float>(-*this) + : static_cast<float>(lo_) + + std::ldexp(static_cast<float>(hi_), 64); +} + +inline int128::operator double() const { + // See comment in int128::operator float() above. + return hi_ < 0 && *this != Int128Min() + ? -static_cast<double>(-*this) + : static_cast<double>(lo_) + + std::ldexp(static_cast<double>(hi_), 64); +} + +inline int128::operator long double() const { + // See comment in int128::operator float() above. + return hi_ < 0 && *this != Int128Min() + ? -static_cast<long double>(-*this) + : static_cast<long double>(lo_) + + std::ldexp(static_cast<long double>(hi_), 64); +} + +// Comparison operators. + +inline bool operator==(int128 lhs, int128 rhs) { + return (Int128Low64(lhs) == Int128Low64(rhs) && + Int128High64(lhs) == Int128High64(rhs)); +} + +inline bool operator!=(int128 lhs, int128 rhs) { + return !(lhs == rhs); +} + +inline bool operator<(int128 lhs, int128 rhs) { + return (Int128High64(lhs) == Int128High64(rhs)) + ? (Int128Low64(lhs) < Int128Low64(rhs)) + : (Int128High64(lhs) < Int128High64(rhs)); +} + +inline bool operator>(int128 lhs, int128 rhs) { + return (Int128High64(lhs) == Int128High64(rhs)) + ? (Int128Low64(lhs) > Int128Low64(rhs)) + : (Int128High64(lhs) > Int128High64(rhs)); +} + +inline bool operator<=(int128 lhs, int128 rhs) { + return !(lhs > rhs); +} + +inline bool operator>=(int128 lhs, int128 rhs) { + return !(lhs < rhs); +} + +// Unary operators. + +inline int128 operator-(int128 v) { + int64_t hi = ~Int128High64(v); + uint64_t lo = ~Int128Low64(v) + 1; + if (lo == 0) ++hi; // carry + return MakeInt128(hi, lo); +} + +inline bool operator!(int128 v) { + return !Int128Low64(v) && !Int128High64(v); +} + +inline int128 operator~(int128 val) { + return MakeInt128(~Int128High64(val), ~Int128Low64(val)); +} + +// Arithmetic operators. + +inline int128 operator+(int128 lhs, int128 rhs) { + int128 result = MakeInt128(Int128High64(lhs) + Int128High64(rhs), + Int128Low64(lhs) + Int128Low64(rhs)); + if (Int128Low64(result) < Int128Low64(lhs)) { // check for carry + return MakeInt128(Int128High64(result) + 1, Int128Low64(result)); + } + return result; +} + +inline int128 operator-(int128 lhs, int128 rhs) { + int128 result = MakeInt128(Int128High64(lhs) - Int128High64(rhs), + Int128Low64(lhs) - Int128Low64(rhs)); + if (Int128Low64(lhs) < Int128Low64(rhs)) { // check for carry + return MakeInt128(Int128High64(result) - 1, Int128Low64(result)); + } + return result; +} + +inline int128 operator*(int128 lhs, int128 rhs) { + uint128 result = uint128(lhs) * rhs; + return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)), + Uint128Low64(result)); +} + +inline int128 int128::operator++(int) { + int128 tmp(*this); + *this += 1; + return tmp; +} + +inline int128 int128::operator--(int) { + int128 tmp(*this); + *this -= 1; + return tmp; +} + +inline int128& int128::operator++() { + *this += 1; + return *this; +} + +inline int128& int128::operator--() { + *this -= 1; + return *this; +} + +inline int128 operator|(int128 lhs, int128 rhs) { + return MakeInt128(Int128High64(lhs) | Int128High64(rhs), + Int128Low64(lhs) | Int128Low64(rhs)); +} + +inline int128 operator&(int128 lhs, int128 rhs) { + return MakeInt128(Int128High64(lhs) & Int128High64(rhs), + Int128Low64(lhs) & Int128Low64(rhs)); +} + +inline int128 operator^(int128 lhs, int128 rhs) { + return MakeInt128(Int128High64(lhs) ^ Int128High64(rhs), + Int128Low64(lhs) ^ Int128Low64(rhs)); +} + +inline int128 operator<<(int128 lhs, int amount) { + // uint64_t shifts of >= 64 are undefined, so we need some special-casing. + if (amount < 64) { + if (amount != 0) { + return MakeInt128( + (Int128High64(lhs) << amount) | + static_cast<int64_t>(Int128Low64(lhs) >> (64 - amount)), + Int128Low64(lhs) << amount); + } + return lhs; + } + return MakeInt128(static_cast<int64_t>(Int128Low64(lhs) << (amount - 64)), 0); +} + +inline int128 operator>>(int128 lhs, int amount) { + // uint64_t shifts of >= 64 are undefined, so we need some special-casing. + if (amount < 64) { + if (amount != 0) { + return MakeInt128( + Int128High64(lhs) >> amount, + (Int128Low64(lhs) >> amount) | + (static_cast<uint64_t>(Int128High64(lhs)) << (64 - amount))); + } + return lhs; + } + return MakeInt128(0, + static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64))); +} diff --git a/absl/numeric/int128_stream_test.cc b/absl/numeric/int128_stream_test.cc index 3cfa9dc1..479ad66c 100644 --- a/absl/numeric/int128_stream_test.cc +++ b/absl/numeric/int128_stream_test.cc @@ -147,6 +147,735 @@ TEST(Uint128, OStreamFormatTest) { } } +struct Int128TestCase { + absl::int128 value; + std::ios_base::fmtflags flags; + std::streamsize width; + const char* expected; +}; + +void CheckInt128Case(const Int128TestCase& test_case) { + std::ostringstream os; + os.flags(test_case.flags); + os.width(test_case.width); + os.fill(kFill); + os << test_case.value; + SCOPED_TRACE(StreamFormatToString(test_case.flags, test_case.width)); + EXPECT_EQ(test_case.expected, os.str()); +} + +TEST(Int128, OStreamValueTest) { + CheckInt128Case({1, kDec, /*width = */ 0, "1"}); + CheckInt128Case({1, kOct, /*width = */ 0, "1"}); + CheckInt128Case({1, kHex, /*width = */ 0, "1"}); + CheckInt128Case({9, kDec, /*width = */ 0, "9"}); + CheckInt128Case({9, kOct, /*width = */ 0, "11"}); + CheckInt128Case({9, kHex, /*width = */ 0, "9"}); + CheckInt128Case({12345, kDec, /*width = */ 0, "12345"}); + CheckInt128Case({12345, kOct, /*width = */ 0, "30071"}); + CheckInt128Case({12345, kHex, /*width = */ 0, "3039"}); + CheckInt128Case( + {0x8000000000000000, kDec, /*width = */ 0, "9223372036854775808"}); + CheckInt128Case( + {0x8000000000000000, kOct, /*width = */ 0, "1000000000000000000000"}); + CheckInt128Case( + {0x8000000000000000, kHex, /*width = */ 0, "8000000000000000"}); + CheckInt128Case({std::numeric_limits<uint64_t>::max(), kDec, + /*width = */ 0, "18446744073709551615"}); + CheckInt128Case({std::numeric_limits<uint64_t>::max(), kOct, + /*width = */ 0, "1777777777777777777777"}); + CheckInt128Case({std::numeric_limits<uint64_t>::max(), kHex, + /*width = */ 0, "ffffffffffffffff"}); + CheckInt128Case( + {absl::MakeInt128(1, 0), kDec, /*width = */ 0, "18446744073709551616"}); + CheckInt128Case( + {absl::MakeInt128(1, 0), kOct, /*width = */ 0, "2000000000000000000000"}); + CheckInt128Case( + {absl::MakeInt128(1, 0), kHex, /*width = */ 0, "10000000000000000"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(), + std::numeric_limits<uint64_t>::max()), + std::ios::dec, /*width = */ 0, + "170141183460469231731687303715884105727"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(), + std::numeric_limits<uint64_t>::max()), + std::ios::oct, /*width = */ 0, + "1777777777777777777777777777777777777777777"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(), + std::numeric_limits<uint64_t>::max()), + std::ios::hex, /*width = */ 0, + "7fffffffffffffffffffffffffffffff"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0), + std::ios::dec, /*width = */ 0, + "-170141183460469231731687303715884105728"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0), + std::ios::oct, /*width = */ 0, + "2000000000000000000000000000000000000000000"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0), + std::ios::hex, /*width = */ 0, + "80000000000000000000000000000000"}); + CheckInt128Case({-1, std::ios::dec, /*width = */ 0, "-1"}); + CheckInt128Case({-1, std::ios::oct, /*width = */ 0, + "3777777777777777777777777777777777777777777"}); + CheckInt128Case( + {-1, std::ios::hex, /*width = */ 0, "ffffffffffffffffffffffffffffffff"}); + CheckInt128Case({-12345, std::ios::dec, /*width = */ 0, "-12345"}); + CheckInt128Case({-12345, std::ios::oct, /*width = */ 0, + "3777777777777777777777777777777777777747707"}); + CheckInt128Case({-12345, std::ios::hex, /*width = */ 0, + "ffffffffffffffffffffffffffffcfc7"}); +} + +std::vector<Int128TestCase> GetInt128FormatCases(); +TEST(Int128, OStreamFormatTest) { + for (const Int128TestCase& test_case : GetInt128FormatCases()) { + CheckInt128Case(test_case); + } +} + +std::vector<Int128TestCase> GetInt128FormatCases() { + return { + {0, std::ios_base::fmtflags(), /*width = */ 0, "0"}, + {0, std::ios_base::fmtflags(), /*width = */ 6, "_____0"}, + {0, kPos, /*width = */ 0, "+0"}, + {0, kPos, /*width = */ 6, "____+0"}, + {0, kBase, /*width = */ 0, "0"}, + {0, kBase, /*width = */ 6, "_____0"}, + {0, kBase | kPos, /*width = */ 0, "+0"}, + {0, kBase | kPos, /*width = */ 6, "____+0"}, + {0, kUpper, /*width = */ 0, "0"}, + {0, kUpper, /*width = */ 6, "_____0"}, + {0, kUpper | kPos, /*width = */ 0, "+0"}, + {0, kUpper | kPos, /*width = */ 6, "____+0"}, + {0, kUpper | kBase, /*width = */ 0, "0"}, + {0, kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kUpper | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kLeft, /*width = */ 0, "0"}, + {0, kLeft, /*width = */ 6, "0_____"}, + {0, kLeft | kPos, /*width = */ 0, "+0"}, + {0, kLeft | kPos, /*width = */ 6, "+0____"}, + {0, kLeft | kBase, /*width = */ 0, "0"}, + {0, kLeft | kBase, /*width = */ 6, "0_____"}, + {0, kLeft | kBase | kPos, /*width = */ 0, "+0"}, + {0, kLeft | kBase | kPos, /*width = */ 6, "+0____"}, + {0, kLeft | kUpper, /*width = */ 0, "0"}, + {0, kLeft | kUpper, /*width = */ 6, "0_____"}, + {0, kLeft | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kLeft | kUpper | kPos, /*width = */ 6, "+0____"}, + {0, kLeft | kUpper | kBase, /*width = */ 0, "0"}, + {0, kLeft | kUpper | kBase, /*width = */ 6, "0_____"}, + {0, kLeft | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kLeft | kUpper | kBase | kPos, /*width = */ 6, "+0____"}, + {0, kInt, /*width = */ 0, "0"}, + {0, kInt, /*width = */ 6, "_____0"}, + {0, kInt | kPos, /*width = */ 0, "+0"}, + {0, kInt | kPos, /*width = */ 6, "+____0"}, + {0, kInt | kBase, /*width = */ 0, "0"}, + {0, kInt | kBase, /*width = */ 6, "_____0"}, + {0, kInt | kBase | kPos, /*width = */ 0, "+0"}, + {0, kInt | kBase | kPos, /*width = */ 6, "+____0"}, + {0, kInt | kUpper, /*width = */ 0, "0"}, + {0, kInt | kUpper, /*width = */ 6, "_____0"}, + {0, kInt | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kInt | kUpper | kPos, /*width = */ 6, "+____0"}, + {0, kInt | kUpper | kBase, /*width = */ 0, "0"}, + {0, kInt | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kInt | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kInt | kUpper | kBase | kPos, /*width = */ 6, "+____0"}, + {0, kRight, /*width = */ 0, "0"}, + {0, kRight, /*width = */ 6, "_____0"}, + {0, kRight | kPos, /*width = */ 0, "+0"}, + {0, kRight | kPos, /*width = */ 6, "____+0"}, + {0, kRight | kBase, /*width = */ 0, "0"}, + {0, kRight | kBase, /*width = */ 6, "_____0"}, + {0, kRight | kBase | kPos, /*width = */ 0, "+0"}, + {0, kRight | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kRight | kUpper, /*width = */ 0, "0"}, + {0, kRight | kUpper, /*width = */ 6, "_____0"}, + {0, kRight | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kRight | kUpper | kPos, /*width = */ 6, "____+0"}, + {0, kRight | kUpper | kBase, /*width = */ 0, "0"}, + {0, kRight | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kRight | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kRight | kUpper | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kDec, /*width = */ 0, "0"}, + {0, kDec, /*width = */ 6, "_____0"}, + {0, kDec | kPos, /*width = */ 0, "+0"}, + {0, kDec | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kBase, /*width = */ 0, "0"}, + {0, kDec | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kUpper, /*width = */ 0, "0"}, + {0, kDec | kUpper, /*width = */ 6, "_____0"}, + {0, kDec | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kDec | kUpper | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kUpper | kBase, /*width = */ 0, "0"}, + {0, kDec | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kUpper | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kLeft, /*width = */ 0, "0"}, + {0, kDec | kLeft, /*width = */ 6, "0_____"}, + {0, kDec | kLeft | kPos, /*width = */ 0, "+0"}, + {0, kDec | kLeft | kPos, /*width = */ 6, "+0____"}, + {0, kDec | kLeft | kBase, /*width = */ 0, "0"}, + {0, kDec | kLeft | kBase, /*width = */ 6, "0_____"}, + {0, kDec | kLeft | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kLeft | kBase | kPos, /*width = */ 6, "+0____"}, + {0, kDec | kLeft | kUpper, /*width = */ 0, "0"}, + {0, kDec | kLeft | kUpper, /*width = */ 6, "0_____"}, + {0, kDec | kLeft | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kDec | kLeft | kUpper | kPos, /*width = */ 6, "+0____"}, + {0, kDec | kLeft | kUpper | kBase, /*width = */ 0, "0"}, + {0, kDec | kLeft | kUpper | kBase, /*width = */ 6, "0_____"}, + {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "+0____"}, + {0, kDec | kInt, /*width = */ 0, "0"}, + {0, kDec | kInt, /*width = */ 6, "_____0"}, + {0, kDec | kInt | kPos, /*width = */ 0, "+0"}, + {0, kDec | kInt | kPos, /*width = */ 6, "+____0"}, + {0, kDec | kInt | kBase, /*width = */ 0, "0"}, + {0, kDec | kInt | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kInt | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kInt | kBase | kPos, /*width = */ 6, "+____0"}, + {0, kDec | kInt | kUpper, /*width = */ 0, "0"}, + {0, kDec | kInt | kUpper, /*width = */ 6, "_____0"}, + {0, kDec | kInt | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kDec | kInt | kUpper | kPos, /*width = */ 6, "+____0"}, + {0, kDec | kInt | kUpper | kBase, /*width = */ 0, "0"}, + {0, kDec | kInt | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "+____0"}, + {0, kDec | kRight, /*width = */ 0, "0"}, + {0, kDec | kRight, /*width = */ 6, "_____0"}, + {0, kDec | kRight | kPos, /*width = */ 0, "+0"}, + {0, kDec | kRight | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kRight | kBase, /*width = */ 0, "0"}, + {0, kDec | kRight | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kRight | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kRight | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kRight | kUpper, /*width = */ 0, "0"}, + {0, kDec | kRight | kUpper, /*width = */ 6, "_____0"}, + {0, kDec | kRight | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kDec | kRight | kUpper | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kRight | kUpper | kBase, /*width = */ 0, "0"}, + {0, kDec | kRight | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kOct, /*width = */ 0, "0"}, + {0, kOct, /*width = */ 6, "_____0"}, + {0, kOct | kPos, /*width = */ 0, "0"}, + {0, kOct | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kBase, /*width = */ 0, "0"}, + {0, kOct | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kUpper, /*width = */ 0, "0"}, + {0, kOct | kUpper, /*width = */ 6, "_____0"}, + {0, kOct | kUpper | kPos, /*width = */ 0, "0"}, + {0, kOct | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kUpper | kBase, /*width = */ 0, "0"}, + {0, kOct | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kLeft, /*width = */ 0, "0"}, + {0, kOct | kLeft, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kPos, /*width = */ 0, "0"}, + {0, kOct | kLeft | kPos, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kBase, /*width = */ 0, "0"}, + {0, kOct | kLeft | kBase, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kLeft | kBase | kPos, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kUpper, /*width = */ 0, "0"}, + {0, kOct | kLeft | kUpper, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kUpper | kPos, /*width = */ 0, "0"}, + {0, kOct | kLeft | kUpper | kPos, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kUpper | kBase, /*width = */ 0, "0"}, + {0, kOct | kLeft | kUpper | kBase, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"}, + {0, kOct | kInt, /*width = */ 0, "0"}, + {0, kOct | kInt, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kPos, /*width = */ 0, "0"}, + {0, kOct | kInt | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kBase, /*width = */ 0, "0"}, + {0, kOct | kInt | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kInt | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kUpper, /*width = */ 0, "0"}, + {0, kOct | kInt | kUpper, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kUpper | kPos, /*width = */ 0, "0"}, + {0, kOct | kInt | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kUpper | kBase, /*width = */ 0, "0"}, + {0, kOct | kInt | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kRight, /*width = */ 0, "0"}, + {0, kOct | kRight, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kPos, /*width = */ 0, "0"}, + {0, kOct | kRight | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kBase, /*width = */ 0, "0"}, + {0, kOct | kRight | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kRight | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kUpper, /*width = */ 0, "0"}, + {0, kOct | kRight | kUpper, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kUpper | kPos, /*width = */ 0, "0"}, + {0, kOct | kRight | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kUpper | kBase, /*width = */ 0, "0"}, + {0, kOct | kRight | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex, /*width = */ 0, "0"}, + {0, kHex, /*width = */ 6, "_____0"}, + {0, kHex | kPos, /*width = */ 0, "0"}, + {0, kHex | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kBase, /*width = */ 0, "0"}, + {0, kHex | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kUpper, /*width = */ 0, "0"}, + {0, kHex | kUpper, /*width = */ 6, "_____0"}, + {0, kHex | kUpper | kPos, /*width = */ 0, "0"}, + {0, kHex | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kUpper | kBase, /*width = */ 0, "0"}, + {0, kHex | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kLeft, /*width = */ 0, "0"}, + {0, kHex | kLeft, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kPos, /*width = */ 0, "0"}, + {0, kHex | kLeft | kPos, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kBase, /*width = */ 0, "0"}, + {0, kHex | kLeft | kBase, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kLeft | kBase | kPos, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kUpper, /*width = */ 0, "0"}, + {0, kHex | kLeft | kUpper, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kUpper | kPos, /*width = */ 0, "0"}, + {0, kHex | kLeft | kUpper | kPos, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0"}, + {0, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"}, + {0, kHex | kInt, /*width = */ 0, "0"}, + {0, kHex | kInt, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kPos, /*width = */ 0, "0"}, + {0, kHex | kInt | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kBase, /*width = */ 0, "0"}, + {0, kHex | kInt | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kInt | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kUpper, /*width = */ 0, "0"}, + {0, kHex | kInt | kUpper, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kUpper | kPos, /*width = */ 0, "0"}, + {0, kHex | kInt | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kUpper | kBase, /*width = */ 0, "0"}, + {0, kHex | kInt | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kRight, /*width = */ 0, "0"}, + {0, kHex | kRight, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kPos, /*width = */ 0, "0"}, + {0, kHex | kRight | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kBase, /*width = */ 0, "0"}, + {0, kHex | kRight | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kRight | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kUpper, /*width = */ 0, "0"}, + {0, kHex | kRight | kUpper, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kUpper | kPos, /*width = */ 0, "0"}, + {0, kHex | kRight | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kUpper | kBase, /*width = */ 0, "0"}, + {0, kHex | kRight | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {42, std::ios_base::fmtflags(), /*width = */ 0, "42"}, + {42, std::ios_base::fmtflags(), /*width = */ 6, "____42"}, + {42, kPos, /*width = */ 0, "+42"}, + {42, kPos, /*width = */ 6, "___+42"}, + {42, kBase, /*width = */ 0, "42"}, + {42, kBase, /*width = */ 6, "____42"}, + {42, kBase | kPos, /*width = */ 0, "+42"}, + {42, kBase | kPos, /*width = */ 6, "___+42"}, + {42, kUpper, /*width = */ 0, "42"}, + {42, kUpper, /*width = */ 6, "____42"}, + {42, kUpper | kPos, /*width = */ 0, "+42"}, + {42, kUpper | kPos, /*width = */ 6, "___+42"}, + {42, kUpper | kBase, /*width = */ 0, "42"}, + {42, kUpper | kBase, /*width = */ 6, "____42"}, + {42, kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kUpper | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kLeft, /*width = */ 0, "42"}, + {42, kLeft, /*width = */ 6, "42____"}, + {42, kLeft | kPos, /*width = */ 0, "+42"}, + {42, kLeft | kPos, /*width = */ 6, "+42___"}, + {42, kLeft | kBase, /*width = */ 0, "42"}, + {42, kLeft | kBase, /*width = */ 6, "42____"}, + {42, kLeft | kBase | kPos, /*width = */ 0, "+42"}, + {42, kLeft | kBase | kPos, /*width = */ 6, "+42___"}, + {42, kLeft | kUpper, /*width = */ 0, "42"}, + {42, kLeft | kUpper, /*width = */ 6, "42____"}, + {42, kLeft | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kLeft | kUpper | kPos, /*width = */ 6, "+42___"}, + {42, kLeft | kUpper | kBase, /*width = */ 0, "42"}, + {42, kLeft | kUpper | kBase, /*width = */ 6, "42____"}, + {42, kLeft | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kLeft | kUpper | kBase | kPos, /*width = */ 6, "+42___"}, + {42, kInt, /*width = */ 0, "42"}, + {42, kInt, /*width = */ 6, "____42"}, + {42, kInt | kPos, /*width = */ 0, "+42"}, + {42, kInt | kPos, /*width = */ 6, "+___42"}, + {42, kInt | kBase, /*width = */ 0, "42"}, + {42, kInt | kBase, /*width = */ 6, "____42"}, + {42, kInt | kBase | kPos, /*width = */ 0, "+42"}, + {42, kInt | kBase | kPos, /*width = */ 6, "+___42"}, + {42, kInt | kUpper, /*width = */ 0, "42"}, + {42, kInt | kUpper, /*width = */ 6, "____42"}, + {42, kInt | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kInt | kUpper | kPos, /*width = */ 6, "+___42"}, + {42, kInt | kUpper | kBase, /*width = */ 0, "42"}, + {42, kInt | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kInt | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kInt | kUpper | kBase | kPos, /*width = */ 6, "+___42"}, + {42, kRight, /*width = */ 0, "42"}, + {42, kRight, /*width = */ 6, "____42"}, + {42, kRight | kPos, /*width = */ 0, "+42"}, + {42, kRight | kPos, /*width = */ 6, "___+42"}, + {42, kRight | kBase, /*width = */ 0, "42"}, + {42, kRight | kBase, /*width = */ 6, "____42"}, + {42, kRight | kBase | kPos, /*width = */ 0, "+42"}, + {42, kRight | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kRight | kUpper, /*width = */ 0, "42"}, + {42, kRight | kUpper, /*width = */ 6, "____42"}, + {42, kRight | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kRight | kUpper | kPos, /*width = */ 6, "___+42"}, + {42, kRight | kUpper | kBase, /*width = */ 0, "42"}, + {42, kRight | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kRight | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kRight | kUpper | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kDec, /*width = */ 0, "42"}, + {42, kDec, /*width = */ 6, "____42"}, + {42, kDec | kPos, /*width = */ 0, "+42"}, + {42, kDec | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kBase, /*width = */ 0, "42"}, + {42, kDec | kBase, /*width = */ 6, "____42"}, + {42, kDec | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kUpper, /*width = */ 0, "42"}, + {42, kDec | kUpper, /*width = */ 6, "____42"}, + {42, kDec | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kDec | kUpper | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kUpper | kBase, /*width = */ 0, "42"}, + {42, kDec | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kDec | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kUpper | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kLeft, /*width = */ 0, "42"}, + {42, kDec | kLeft, /*width = */ 6, "42____"}, + {42, kDec | kLeft | kPos, /*width = */ 0, "+42"}, + {42, kDec | kLeft | kPos, /*width = */ 6, "+42___"}, + {42, kDec | kLeft | kBase, /*width = */ 0, "42"}, + {42, kDec | kLeft | kBase, /*width = */ 6, "42____"}, + {42, kDec | kLeft | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kLeft | kBase | kPos, /*width = */ 6, "+42___"}, + {42, kDec | kLeft | kUpper, /*width = */ 0, "42"}, + {42, kDec | kLeft | kUpper, /*width = */ 6, "42____"}, + {42, kDec | kLeft | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kDec | kLeft | kUpper | kPos, /*width = */ 6, "+42___"}, + {42, kDec | kLeft | kUpper | kBase, /*width = */ 0, "42"}, + {42, kDec | kLeft | kUpper | kBase, /*width = */ 6, "42____"}, + {42, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "+42___"}, + {42, kDec | kInt, /*width = */ 0, "42"}, + {42, kDec | kInt, /*width = */ 6, "____42"}, + {42, kDec | kInt | kPos, /*width = */ 0, "+42"}, + {42, kDec | kInt | kPos, /*width = */ 6, "+___42"}, + {42, kDec | kInt | kBase, /*width = */ 0, "42"}, + {42, kDec | kInt | kBase, /*width = */ 6, "____42"}, + {42, kDec | kInt | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kInt | kBase | kPos, /*width = */ 6, "+___42"}, + {42, kDec | kInt | kUpper, /*width = */ 0, "42"}, + {42, kDec | kInt | kUpper, /*width = */ 6, "____42"}, + {42, kDec | kInt | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kDec | kInt | kUpper | kPos, /*width = */ 6, "+___42"}, + {42, kDec | kInt | kUpper | kBase, /*width = */ 0, "42"}, + {42, kDec | kInt | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "+___42"}, + {42, kDec | kRight, /*width = */ 0, "42"}, + {42, kDec | kRight, /*width = */ 6, "____42"}, + {42, kDec | kRight | kPos, /*width = */ 0, "+42"}, + {42, kDec | kRight | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kRight | kBase, /*width = */ 0, "42"}, + {42, kDec | kRight | kBase, /*width = */ 6, "____42"}, + {42, kDec | kRight | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kRight | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kRight | kUpper, /*width = */ 0, "42"}, + {42, kDec | kRight | kUpper, /*width = */ 6, "____42"}, + {42, kDec | kRight | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kDec | kRight | kUpper | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kRight | kUpper | kBase, /*width = */ 0, "42"}, + {42, kDec | kRight | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kOct, /*width = */ 0, "52"}, + {42, kOct, /*width = */ 6, "____52"}, + {42, kOct | kPos, /*width = */ 0, "52"}, + {42, kOct | kPos, /*width = */ 6, "____52"}, + {42, kOct | kBase, /*width = */ 0, "052"}, + {42, kOct | kBase, /*width = */ 6, "___052"}, + {42, kOct | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kUpper, /*width = */ 0, "52"}, + {42, kOct | kUpper, /*width = */ 6, "____52"}, + {42, kOct | kUpper | kPos, /*width = */ 0, "52"}, + {42, kOct | kUpper | kPos, /*width = */ 6, "____52"}, + {42, kOct | kUpper | kBase, /*width = */ 0, "052"}, + {42, kOct | kUpper | kBase, /*width = */ 6, "___052"}, + {42, kOct | kUpper | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kUpper | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kLeft, /*width = */ 0, "52"}, + {42, kOct | kLeft, /*width = */ 6, "52____"}, + {42, kOct | kLeft | kPos, /*width = */ 0, "52"}, + {42, kOct | kLeft | kPos, /*width = */ 6, "52____"}, + {42, kOct | kLeft | kBase, /*width = */ 0, "052"}, + {42, kOct | kLeft | kBase, /*width = */ 6, "052___"}, + {42, kOct | kLeft | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kLeft | kBase | kPos, /*width = */ 6, "052___"}, + {42, kOct | kLeft | kUpper, /*width = */ 0, "52"}, + {42, kOct | kLeft | kUpper, /*width = */ 6, "52____"}, + {42, kOct | kLeft | kUpper | kPos, /*width = */ 0, "52"}, + {42, kOct | kLeft | kUpper | kPos, /*width = */ 6, "52____"}, + {42, kOct | kLeft | kUpper | kBase, /*width = */ 0, "052"}, + {42, kOct | kLeft | kUpper | kBase, /*width = */ 6, "052___"}, + {42, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "052___"}, + {42, kOct | kInt, /*width = */ 0, "52"}, + {42, kOct | kInt, /*width = */ 6, "____52"}, + {42, kOct | kInt | kPos, /*width = */ 0, "52"}, + {42, kOct | kInt | kPos, /*width = */ 6, "____52"}, + {42, kOct | kInt | kBase, /*width = */ 0, "052"}, + {42, kOct | kInt | kBase, /*width = */ 6, "___052"}, + {42, kOct | kInt | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kInt | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kInt | kUpper, /*width = */ 0, "52"}, + {42, kOct | kInt | kUpper, /*width = */ 6, "____52"}, + {42, kOct | kInt | kUpper | kPos, /*width = */ 0, "52"}, + {42, kOct | kInt | kUpper | kPos, /*width = */ 6, "____52"}, + {42, kOct | kInt | kUpper | kBase, /*width = */ 0, "052"}, + {42, kOct | kInt | kUpper | kBase, /*width = */ 6, "___052"}, + {42, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kRight, /*width = */ 0, "52"}, + {42, kOct | kRight, /*width = */ 6, "____52"}, + {42, kOct | kRight | kPos, /*width = */ 0, "52"}, + {42, kOct | kRight | kPos, /*width = */ 6, "____52"}, + {42, kOct | kRight | kBase, /*width = */ 0, "052"}, + {42, kOct | kRight | kBase, /*width = */ 6, "___052"}, + {42, kOct | kRight | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kRight | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kRight | kUpper, /*width = */ 0, "52"}, + {42, kOct | kRight | kUpper, /*width = */ 6, "____52"}, + {42, kOct | kRight | kUpper | kPos, /*width = */ 0, "52"}, + {42, kOct | kRight | kUpper | kPos, /*width = */ 6, "____52"}, + {42, kOct | kRight | kUpper | kBase, /*width = */ 0, "052"}, + {42, kOct | kRight | kUpper | kBase, /*width = */ 6, "___052"}, + {42, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "___052"}, + {42, kHex, /*width = */ 0, "2a"}, + {42, kHex, /*width = */ 6, "____2a"}, + {42, kHex | kPos, /*width = */ 0, "2a"}, + {42, kHex | kPos, /*width = */ 6, "____2a"}, + {42, kHex | kBase, /*width = */ 0, "0x2a"}, + {42, kHex | kBase, /*width = */ 6, "__0x2a"}, + {42, kHex | kBase | kPos, /*width = */ 0, "0x2a"}, + {42, kHex | kBase | kPos, /*width = */ 6, "__0x2a"}, + {42, kHex | kUpper, /*width = */ 0, "2A"}, + {42, kHex | kUpper, /*width = */ 6, "____2A"}, + {42, kHex | kUpper | kPos, /*width = */ 0, "2A"}, + {42, kHex | kUpper | kPos, /*width = */ 6, "____2A"}, + {42, kHex | kUpper | kBase, /*width = */ 0, "0X2A"}, + {42, kHex | kUpper | kBase, /*width = */ 6, "__0X2A"}, + {42, kHex | kUpper | kBase | kPos, /*width = */ 0, "0X2A"}, + {42, kHex | kUpper | kBase | kPos, /*width = */ 6, "__0X2A"}, + {42, kHex | kLeft, /*width = */ 0, "2a"}, + {42, kHex | kLeft, /*width = */ 6, "2a____"}, + {42, kHex | kLeft | kPos, /*width = */ 0, "2a"}, + {42, kHex | kLeft | kPos, /*width = */ 6, "2a____"}, + {42, kHex | kLeft | kBase, /*width = */ 0, "0x2a"}, + {42, kHex | kLeft | kBase, /*width = */ 6, "0x2a__"}, + {42, kHex | kLeft | kBase | kPos, /*width = */ 0, "0x2a"}, + {42, kHex | kLeft | kBase | kPos, /*width = */ 6, "0x2a__"}, + {42, kHex | kLeft | kUpper, /*width = */ 0, "2A"}, + {42, kHex | kLeft | kUpper, /*width = */ 6, "2A____"}, + {42, kHex | kLeft | kUpper | kPos, /*width = */ 0, "2A"}, + {42, kHex | kLeft | kUpper | kPos, /*width = */ 6, "2A____"}, + {42, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0X2A"}, + {42, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0X2A__"}, + {42, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0X2A"}, + {42, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0X2A__"}, + {42, kHex | kInt, /*width = */ 0, "2a"}, + {42, kHex | kInt, /*width = */ 6, "____2a"}, + {42, kHex | kInt | kPos, /*width = */ 0, "2a"}, + {42, kHex | kInt | kPos, /*width = */ 6, "____2a"}, + {42, kHex | kInt | kBase, /*width = */ 0, "0x2a"}, + {42, kHex | kInt | kBase, /*width = */ 6, "0x__2a"}, + {42, kHex | kInt | kBase | kPos, /*width = */ 0, "0x2a"}, + {42, kHex | kInt | kBase | kPos, /*width = */ 6, "0x__2a"}, + {42, kHex | kInt | kUpper, /*width = */ 0, "2A"}, + {42, kHex | kInt | kUpper, /*width = */ 6, "____2A"}, + {42, kHex | kInt | kUpper | kPos, /*width = */ 0, "2A"}, + {42, kHex | kInt | kUpper | kPos, /*width = */ 6, "____2A"}, + {42, kHex | kInt | kUpper | kBase, /*width = */ 0, "0X2A"}, + {42, kHex | kInt | kUpper | kBase, /*width = */ 6, "0X__2A"}, + {42, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0X2A"}, + {42, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "0X__2A"}, + {42, kHex | kRight, /*width = */ 0, "2a"}, + {42, kHex | kRight, /*width = */ 6, "____2a"}, + {42, kHex | kRight | kPos, /*width = */ 0, "2a"}, + {42, kHex | kRight | kPos, /*width = */ 6, "____2a"}, + {42, kHex | kRight | kBase, /*width = */ 0, "0x2a"}, + {42, kHex | kRight | kBase, /*width = */ 6, "__0x2a"}, + {42, kHex | kRight | kBase | kPos, /*width = */ 0, "0x2a"}, + {42, kHex | kRight | kBase | kPos, /*width = */ 6, "__0x2a"}, + {42, kHex | kRight | kUpper, /*width = */ 0, "2A"}, + {42, kHex | kRight | kUpper, /*width = */ 6, "____2A"}, + {42, kHex | kRight | kUpper | kPos, /*width = */ 0, "2A"}, + {42, kHex | kRight | kUpper | kPos, /*width = */ 6, "____2A"}, + {42, kHex | kRight | kUpper | kBase, /*width = */ 0, "0X2A"}, + {42, kHex | kRight | kUpper | kBase, /*width = */ 6, "__0X2A"}, + {42, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0X2A"}, + {42, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "__0X2A"}, + {-321, std::ios_base::fmtflags(), /*width = */ 0, "-321"}, + {-321, std::ios_base::fmtflags(), /*width = */ 6, "__-321"}, + {-321, kPos, /*width = */ 0, "-321"}, + {-321, kPos, /*width = */ 6, "__-321"}, + {-321, kBase, /*width = */ 0, "-321"}, + {-321, kBase, /*width = */ 6, "__-321"}, + {-321, kBase | kPos, /*width = */ 0, "-321"}, + {-321, kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kUpper, /*width = */ 0, "-321"}, + {-321, kUpper, /*width = */ 6, "__-321"}, + {-321, kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kUpper | kPos, /*width = */ 6, "__-321"}, + {-321, kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kUpper | kBase, /*width = */ 6, "__-321"}, + {-321, kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kUpper | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kLeft, /*width = */ 0, "-321"}, + {-321, kLeft, /*width = */ 6, "-321__"}, + {-321, kLeft | kPos, /*width = */ 0, "-321"}, + {-321, kLeft | kPos, /*width = */ 6, "-321__"}, + {-321, kLeft | kBase, /*width = */ 0, "-321"}, + {-321, kLeft | kBase, /*width = */ 6, "-321__"}, + {-321, kLeft | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kLeft | kBase | kPos, /*width = */ 6, "-321__"}, + {-321, kLeft | kUpper, /*width = */ 0, "-321"}, + {-321, kLeft | kUpper, /*width = */ 6, "-321__"}, + {-321, kLeft | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kLeft | kUpper | kPos, /*width = */ 6, "-321__"}, + {-321, kLeft | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kLeft | kUpper | kBase, /*width = */ 6, "-321__"}, + {-321, kLeft | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kLeft | kUpper | kBase | kPos, /*width = */ 6, "-321__"}, + {-321, kInt, /*width = */ 0, "-321"}, + {-321, kInt, /*width = */ 6, "-__321"}, + {-321, kInt | kPos, /*width = */ 0, "-321"}, + {-321, kInt | kPos, /*width = */ 6, "-__321"}, + {-321, kInt | kBase, /*width = */ 0, "-321"}, + {-321, kInt | kBase, /*width = */ 6, "-__321"}, + {-321, kInt | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kInt | kBase | kPos, /*width = */ 6, "-__321"}, + {-321, kInt | kUpper, /*width = */ 0, "-321"}, + {-321, kInt | kUpper, /*width = */ 6, "-__321"}, + {-321, kInt | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kInt | kUpper | kPos, /*width = */ 6, "-__321"}, + {-321, kInt | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kInt | kUpper | kBase, /*width = */ 6, "-__321"}, + {-321, kInt | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kInt | kUpper | kBase | kPos, /*width = */ 6, "-__321"}, + {-321, kRight, /*width = */ 0, "-321"}, + {-321, kRight, /*width = */ 6, "__-321"}, + {-321, kRight | kPos, /*width = */ 0, "-321"}, + {-321, kRight | kPos, /*width = */ 6, "__-321"}, + {-321, kRight | kBase, /*width = */ 0, "-321"}, + {-321, kRight | kBase, /*width = */ 6, "__-321"}, + {-321, kRight | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kRight | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kRight | kUpper, /*width = */ 0, "-321"}, + {-321, kRight | kUpper, /*width = */ 6, "__-321"}, + {-321, kRight | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kRight | kUpper | kPos, /*width = */ 6, "__-321"}, + {-321, kRight | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kRight | kUpper | kBase, /*width = */ 6, "__-321"}, + {-321, kRight | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kRight | kUpper | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kDec, /*width = */ 0, "-321"}, + {-321, kDec, /*width = */ 6, "__-321"}, + {-321, kDec | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kBase, /*width = */ 6, "__-321"}, + {-321, kDec | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kUpper, /*width = */ 0, "-321"}, + {-321, kDec | kUpper, /*width = */ 6, "__-321"}, + {-321, kDec | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kUpper | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kUpper | kBase, /*width = */ 6, "__-321"}, + {-321, kDec | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kUpper | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kLeft, /*width = */ 0, "-321"}, + {-321, kDec | kLeft, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kPos, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kBase, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kBase | kPos, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kUpper, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kUpper, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kUpper | kPos, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kUpper | kBase, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "-321__"}, + {-321, kDec | kInt, /*width = */ 0, "-321"}, + {-321, kDec | kInt, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kPos, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kBase, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kBase | kPos, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kUpper, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kUpper, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kUpper | kPos, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kUpper | kBase, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "-__321"}, + {-321, kDec | kRight, /*width = */ 0, "-321"}, + {-321, kDec | kRight, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kBase, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kUpper, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kUpper, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kUpper | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kUpper | kBase, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "__-321"}}; +} + std::vector<Uint128TestCase> GetUint128FormatCases() { return { {0, std::ios_base::fmtflags(), /*width = */ 0, "0"}, diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc index 5e1b5ec3..bc86c714 100644 --- a/absl/numeric/int128_test.cc +++ b/absl/numeric/int128_test.cc @@ -479,4 +479,747 @@ TEST(Uint128, Hash) { })); } + +TEST(Int128Uint128, ConversionTest) { + absl::int128 nonnegative_signed_values[] = { + 0, + 1, + 0xffeeddccbbaa9988, + absl::MakeInt128(0x7766554433221100, 0), + absl::MakeInt128(0x1234567890abcdef, 0xfedcba0987654321), + absl::Int128Max()}; + for (absl::int128 value : nonnegative_signed_values) { + EXPECT_EQ(value, absl::int128(absl::uint128(value))); + + absl::uint128 assigned_value; + assigned_value = value; + EXPECT_EQ(value, absl::int128(assigned_value)); + } + + absl::int128 negative_values[] = { + -1, -0x1234567890abcdef, + absl::MakeInt128(-0x5544332211ffeedd, 0), + -absl::MakeInt128(0x76543210fedcba98, 0xabcdef0123456789)}; + for (absl::int128 value : negative_values) { + EXPECT_EQ(absl::uint128(-value), -absl::uint128(value)); + + absl::uint128 assigned_value; + assigned_value = value; + EXPECT_EQ(absl::uint128(-value), -assigned_value); + } +} + +template <typename T> +class Int128IntegerTraitsTest : public ::testing::Test {}; + +TYPED_TEST_SUITE(Int128IntegerTraitsTest, IntegerTypes); + +TYPED_TEST(Int128IntegerTraitsTest, ConstructAssignTest) { + static_assert(std::is_constructible<absl::int128, TypeParam>::value, + "absl::int128 must be constructible from TypeParam"); + static_assert(std::is_assignable<absl::int128&, TypeParam>::value, + "absl::int128 must be assignable from TypeParam"); + static_assert(!std::is_assignable<TypeParam&, absl::int128>::value, + "TypeParam must not be assignable from absl::int128"); +} + +template <typename T> +class Int128FloatTraitsTest : public ::testing::Test {}; + +TYPED_TEST_SUITE(Int128FloatTraitsTest, FloatingPointTypes); + +TYPED_TEST(Int128FloatTraitsTest, ConstructAssignTest) { + static_assert(std::is_constructible<absl::int128, TypeParam>::value, + "absl::int128 must be constructible from TypeParam"); + static_assert(!std::is_assignable<absl::int128&, TypeParam>::value, + "absl::int128 must not be assignable from TypeParam"); + static_assert(!std::is_assignable<TypeParam&, absl::int128>::value, + "TypeParam must not be assignable from absl::int128"); +} + +#ifdef ABSL_HAVE_INTRINSIC_INT128 +// These type traits done separately as TYPED_TEST requires typeinfo, and not +// all platforms have this for __int128 even though they define the type. +TEST(Int128, IntrinsicTypeTraitsTest) { + static_assert(std::is_constructible<absl::int128, __int128>::value, + "absl::int128 must be constructible from __int128"); + static_assert(std::is_assignable<absl::int128&, __int128>::value, + "absl::int128 must be assignable from __int128"); + static_assert(!std::is_assignable<__int128&, absl::int128>::value, + "__int128 must not be assignable from absl::int128"); + + static_assert(std::is_constructible<absl::int128, unsigned __int128>::value, + "absl::int128 must be constructible from unsigned __int128"); + static_assert(!std::is_assignable<absl::int128&, unsigned __int128>::value, + "absl::int128 must be assignable from unsigned __int128"); + static_assert(!std::is_assignable<unsigned __int128&, absl::int128>::value, + "unsigned __int128 must not be assignable from absl::int128"); +} +#endif // ABSL_HAVE_INTRINSIC_INT128 + +TEST(Int128, TrivialTraitsTest) { + static_assert(absl::is_trivially_default_constructible<absl::int128>::value, + ""); + static_assert(absl::is_trivially_copy_constructible<absl::int128>::value, ""); + static_assert(absl::is_trivially_copy_assignable<absl::int128>::value, ""); + static_assert(std::is_trivially_destructible<absl::int128>::value, ""); +} + +TEST(Int128, BoolConversionTest) { + EXPECT_FALSE(absl::int128(0)); + for (int i = 0; i < 64; ++i) { + EXPECT_TRUE(absl::MakeInt128(0, uint64_t{1} << i)); + } + for (int i = 0; i < 63; ++i) { + EXPECT_TRUE(absl::MakeInt128(int64_t{1} << i, 0)); + } + EXPECT_TRUE(absl::Int128Min()); + + EXPECT_EQ(absl::int128(1), absl::int128(true)); + EXPECT_EQ(absl::int128(0), absl::int128(false)); +} + +template <typename T> +class Int128IntegerConversionTest : public ::testing::Test {}; + +TYPED_TEST_SUITE(Int128IntegerConversionTest, IntegerTypes); + +TYPED_TEST(Int128IntegerConversionTest, RoundTripTest) { + EXPECT_EQ(TypeParam{0}, static_cast<TypeParam>(absl::int128(0))); + EXPECT_EQ(std::numeric_limits<TypeParam>::min(), + static_cast<TypeParam>( + absl::int128(std::numeric_limits<TypeParam>::min()))); + EXPECT_EQ(std::numeric_limits<TypeParam>::max(), + static_cast<TypeParam>( + absl::int128(std::numeric_limits<TypeParam>::max()))); +} + +template <typename T> +class Int128FloatConversionTest : public ::testing::Test {}; + +TYPED_TEST_SUITE(Int128FloatConversionTest, FloatingPointTypes); + +TYPED_TEST(Int128FloatConversionTest, ConstructAndCastTest) { + // Conversions where the floating point values should be exactly the same. + // 0x9f5b is a randomly chosen small value. + for (int i = 0; i < 110; ++i) { // 110 = 126 - #bits in 0x9f5b + SCOPED_TRACE(::testing::Message() << "i = " << i); + + TypeParam float_value = std::ldexp(static_cast<TypeParam>(0x9f5b), i); + absl::int128 int_value = absl::int128(0x9f5b) << i; + + EXPECT_EQ(float_value, static_cast<TypeParam>(int_value)); + EXPECT_EQ(-float_value, static_cast<TypeParam>(-int_value)); + EXPECT_EQ(int_value, absl::int128(float_value)); + EXPECT_EQ(-int_value, absl::int128(-float_value)); + } + + // Round trip conversions with a small sample of randomly generated uint64_t + // values (less than int64_t max so that value * 2^64 fits into int128). + uint64_t values[] = {0x6d4492c24fb86199, 0x26ead65e4cb359b5, + 0x2c43407433ba3fd1, 0x3b574ec668df6b55, + 0x1c750e55a29f4f0f}; + for (uint64_t value : values) { + for (int i = 0; i <= 64; ++i) { + SCOPED_TRACE(::testing::Message() + << "value = " << value << "; i = " << i); + + TypeParam fvalue = std::ldexp(static_cast<TypeParam>(value), i); + EXPECT_DOUBLE_EQ(fvalue, static_cast<TypeParam>(absl::int128(fvalue))); + EXPECT_DOUBLE_EQ(-fvalue, static_cast<TypeParam>(-absl::int128(fvalue))); + EXPECT_DOUBLE_EQ(-fvalue, static_cast<TypeParam>(absl::int128(-fvalue))); + EXPECT_DOUBLE_EQ(fvalue, static_cast<TypeParam>(-absl::int128(-fvalue))); + } + } + + // Round trip conversions with a small sample of random large positive values. + absl::int128 large_values[] = { + absl::MakeInt128(0x5b0640d96c7b3d9f, 0xb7a7189e51d18622), + absl::MakeInt128(0x34bed042c6f65270, 0x73b236570669a089), + absl::MakeInt128(0x43deba9e6da12724, 0xf7f0f83da686797d), + absl::MakeInt128(0x71e8d383be4e5589, 0x75c3f96fb00752b6)}; + for (absl::int128 value : large_values) { + // Make value have as many significant bits as can be represented by + // the mantissa, also making sure the highest and lowest bit in the range + // are set. + value >>= (127 - std::numeric_limits<TypeParam>::digits); + value |= absl::int128(1) << (std::numeric_limits<TypeParam>::digits - 1); + value |= 1; + for (int i = 0; i < 127 - std::numeric_limits<TypeParam>::digits; ++i) { + absl::int128 int_value = value << i; + EXPECT_EQ(int_value, + static_cast<absl::int128>(static_cast<TypeParam>(int_value))); + EXPECT_EQ(-int_value, + static_cast<absl::int128>(static_cast<TypeParam>(-int_value))); + } + } + + // Small sample of checks that rounding is toward zero + EXPECT_EQ(0, absl::int128(TypeParam(0.1))); + EXPECT_EQ(17, absl::int128(TypeParam(17.8))); + EXPECT_EQ(0, absl::int128(TypeParam(-0.8))); + EXPECT_EQ(-53, absl::int128(TypeParam(-53.1))); + EXPECT_EQ(0, absl::int128(TypeParam(0.5))); + EXPECT_EQ(0, absl::int128(TypeParam(-0.5))); + TypeParam just_lt_one = std::nexttoward(TypeParam(1), TypeParam(0)); + EXPECT_EQ(0, absl::int128(just_lt_one)); + TypeParam just_gt_minus_one = std::nexttoward(TypeParam(-1), TypeParam(0)); + EXPECT_EQ(0, absl::int128(just_gt_minus_one)); + + // Check limits + EXPECT_DOUBLE_EQ(std::ldexp(static_cast<TypeParam>(1), 127), + static_cast<TypeParam>(absl::Int128Max())); + EXPECT_DOUBLE_EQ(-std::ldexp(static_cast<TypeParam>(1), 127), + static_cast<TypeParam>(absl::Int128Min())); +} + +TEST(Int128, FactoryTest) { + EXPECT_EQ(absl::int128(-1), absl::MakeInt128(-1, -1)); + EXPECT_EQ(absl::int128(-31), absl::MakeInt128(-1, -31)); + EXPECT_EQ(absl::int128(std::numeric_limits<int64_t>::min()), + absl::MakeInt128(-1, std::numeric_limits<int64_t>::min())); + EXPECT_EQ(absl::int128(0), absl::MakeInt128(0, 0)); + EXPECT_EQ(absl::int128(1), absl::MakeInt128(0, 1)); + EXPECT_EQ(absl::int128(std::numeric_limits<int64_t>::max()), + absl::MakeInt128(0, std::numeric_limits<int64_t>::max())); +} + +TEST(Int128, HighLowTest) { + struct HighLowPair { + int64_t high; + uint64_t low; + }; + HighLowPair values[]{{0, 0}, {0, 1}, {1, 0}, {123, 456}, {-654, 321}}; + for (const HighLowPair& pair : values) { + absl::int128 value = absl::MakeInt128(pair.high, pair.low); + EXPECT_EQ(pair.low, absl::Int128Low64(value)); + EXPECT_EQ(pair.high, absl::Int128High64(value)); + } +} + +TEST(Int128, LimitsTest) { + EXPECT_EQ(absl::MakeInt128(0x7fffffffffffffff, 0xffffffffffffffff), + absl::Int128Max()); + EXPECT_EQ(absl::Int128Max(), ~absl::Int128Min()); +} + +#if defined(ABSL_HAVE_INTRINSIC_INT128) +TEST(Int128, IntrinsicConversionTest) { + __int128 intrinsic = + (static_cast<__int128>(0x3a5b76c209de76f6) << 64) + 0x1f25e1d63a2b46c5; + absl::int128 custom = + absl::MakeInt128(0x3a5b76c209de76f6, 0x1f25e1d63a2b46c5); + + EXPECT_EQ(custom, absl::int128(intrinsic)); + EXPECT_EQ(intrinsic, static_cast<__int128>(custom)); +} +#endif // ABSL_HAVE_INTRINSIC_INT128 + +TEST(Int128, ConstexprTest) { + constexpr absl::int128 zero = absl::int128(); + constexpr absl::int128 one = 1; + constexpr absl::int128 minus_two = -2; + constexpr absl::int128 min = absl::Int128Min(); + constexpr absl::int128 max = absl::Int128Max(); + EXPECT_EQ(zero, absl::int128(0)); + EXPECT_EQ(one, absl::int128(1)); + EXPECT_EQ(minus_two, absl::MakeInt128(-1, -2)); + EXPECT_GT(max, one); + EXPECT_LT(min, minus_two); +} + +TEST(Int128, ComparisonTest) { + struct TestCase { + absl::int128 smaller; + absl::int128 larger; + }; + TestCase cases[] = { + {absl::int128(0), absl::int128(123)}, + {absl::MakeInt128(-12, 34), absl::MakeInt128(12, 34)}, + {absl::MakeInt128(1, 1000), absl::MakeInt128(1000, 1)}, + {absl::MakeInt128(-1000, 1000), absl::MakeInt128(-1, 1)}, + }; + for (const TestCase& pair : cases) { + SCOPED_TRACE(::testing::Message() << "pair.smaller = " << pair.smaller + << "; pair.larger = " << pair.larger); + + EXPECT_TRUE(pair.smaller == pair.smaller); // NOLINT(readability/check) + EXPECT_TRUE(pair.larger == pair.larger); // NOLINT(readability/check) + EXPECT_FALSE(pair.smaller == pair.larger); // NOLINT(readability/check) + + EXPECT_TRUE(pair.smaller != pair.larger); // NOLINT(readability/check) + EXPECT_FALSE(pair.smaller != pair.smaller); // NOLINT(readability/check) + EXPECT_FALSE(pair.larger != pair.larger); // NOLINT(readability/check) + + EXPECT_TRUE(pair.smaller < pair.larger); // NOLINT(readability/check) + EXPECT_FALSE(pair.larger < pair.smaller); // NOLINT(readability/check) + + EXPECT_TRUE(pair.larger > pair.smaller); // NOLINT(readability/check) + EXPECT_FALSE(pair.smaller > pair.larger); // NOLINT(readability/check) + + EXPECT_TRUE(pair.smaller <= pair.larger); // NOLINT(readability/check) + EXPECT_FALSE(pair.larger <= pair.smaller); // NOLINT(readability/check) + EXPECT_TRUE(pair.smaller <= pair.smaller); // NOLINT(readability/check) + EXPECT_TRUE(pair.larger <= pair.larger); // NOLINT(readability/check) + + EXPECT_TRUE(pair.larger >= pair.smaller); // NOLINT(readability/check) + EXPECT_FALSE(pair.smaller >= pair.larger); // NOLINT(readability/check) + EXPECT_TRUE(pair.smaller >= pair.smaller); // NOLINT(readability/check) + EXPECT_TRUE(pair.larger >= pair.larger); // NOLINT(readability/check) + } +} + +TEST(Int128, UnaryNegationTest) { + int64_t values64[] = {0, 1, 12345, 0x4000000000000000, + std::numeric_limits<int64_t>::max()}; + for (int64_t value : values64) { + SCOPED_TRACE(::testing::Message() << "value = " << value); + + EXPECT_EQ(absl::int128(-value), -absl::int128(value)); + EXPECT_EQ(absl::int128(value), -absl::int128(-value)); + EXPECT_EQ(absl::MakeInt128(-value, 0), -absl::MakeInt128(value, 0)); + EXPECT_EQ(absl::MakeInt128(value, 0), -absl::MakeInt128(-value, 0)); + } +} + +TEST(Int128, LogicalNotTest) { + EXPECT_TRUE(!absl::int128(0)); + for (int i = 0; i < 64; ++i) { + EXPECT_FALSE(!absl::MakeInt128(0, uint64_t{1} << i)); + } + for (int i = 0; i < 63; ++i) { + EXPECT_FALSE(!absl::MakeInt128(int64_t{1} << i, 0)); + } +} + +TEST(Int128, AdditionSubtractionTest) { + // 64 bit pairs that will not cause overflow / underflow. These test negative + // carry; positive carry must be checked separately. + std::pair<int64_t, int64_t> cases[]{ + {0, 0}, // 0, 0 + {0, 2945781290834}, // 0, + + {1908357619234, 0}, // +, 0 + {0, -1204895918245}, // 0, - + {-2957928523560, 0}, // -, 0 + {89023982312461, 98346012567134}, // +, + + {-63454234568239, -23456235230773}, // -, - + {98263457263502, -21428561935925}, // +, - + {-88235237438467, 15923659234573}, // -, + + }; + for (const auto& pair : cases) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + EXPECT_EQ(absl::int128(pair.first + pair.second), + absl::int128(pair.first) + absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.second + pair.first), + absl::int128(pair.second) += absl::int128(pair.first)); + + EXPECT_EQ(absl::int128(pair.first - pair.second), + absl::int128(pair.first) - absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.second - pair.first), + absl::int128(pair.second) -= absl::int128(pair.first)); + + EXPECT_EQ( + absl::MakeInt128(pair.second + pair.first, 0), + absl::MakeInt128(pair.second, 0) + absl::MakeInt128(pair.first, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first + pair.second, 0), + absl::MakeInt128(pair.first, 0) += absl::MakeInt128(pair.second, 0)); + + EXPECT_EQ( + absl::MakeInt128(pair.second - pair.first, 0), + absl::MakeInt128(pair.second, 0) - absl::MakeInt128(pair.first, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first - pair.second, 0), + absl::MakeInt128(pair.first, 0) -= absl::MakeInt128(pair.second, 0)); + } + + // check positive carry + EXPECT_EQ(absl::MakeInt128(31, 0), + absl::MakeInt128(20, 1) + + absl::MakeInt128(10, std::numeric_limits<uint64_t>::max())); +} + +TEST(Int128, IncrementDecrementTest) { + absl::int128 value = 0; + EXPECT_EQ(0, value++); + EXPECT_EQ(1, value); + EXPECT_EQ(1, value--); + EXPECT_EQ(0, value); + EXPECT_EQ(-1, --value); + EXPECT_EQ(-1, value); + EXPECT_EQ(0, ++value); + EXPECT_EQ(0, value); +} + +TEST(Int128, MultiplicationTest) { + // 1 bit x 1 bit, and negative combinations + for (int i = 0; i < 64; ++i) { + for (int j = 0; j < 127 - i; ++j) { + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + absl::int128 a = absl::int128(1) << i; + absl::int128 b = absl::int128(1) << j; + absl::int128 c = absl::int128(1) << (i + j); + + EXPECT_EQ(c, a * b); + EXPECT_EQ(-c, -a * b); + EXPECT_EQ(-c, a * -b); + EXPECT_EQ(c, -a * -b); + + EXPECT_EQ(c, absl::int128(a) *= b); + EXPECT_EQ(-c, absl::int128(-a) *= b); + EXPECT_EQ(-c, absl::int128(a) *= -b); + EXPECT_EQ(c, absl::int128(-a) *= -b); + } + } + + // Pairs of random values that will not overflow signed 64-bit multiplication + std::pair<int64_t, int64_t> small_values[] = { + {0x5e61, 0xf29f79ca14b4}, // +, + + {0x3e033b, -0x612c0ee549}, // +, - + {-0x052ce7e8, 0x7c728f0f}, // -, + + {-0x3af7054626, -0xfb1e1d}, // -, - + }; + for (const std::pair<int64_t, int64_t>& pair : small_values) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + EXPECT_EQ(absl::int128(pair.first * pair.second), + absl::int128(pair.first) * absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first * pair.second), + absl::int128(pair.first) *= absl::int128(pair.second)); + + EXPECT_EQ(absl::MakeInt128(pair.first * pair.second, 0), + absl::MakeInt128(pair.first, 0) * absl::int128(pair.second)); + EXPECT_EQ(absl::MakeInt128(pair.first * pair.second, 0), + absl::MakeInt128(pair.first, 0) *= absl::int128(pair.second)); + } + + // Pairs of positive random values that will not overflow 64-bit + // multiplication and can be left shifted by 32 without overflow + std::pair<int64_t, int64_t> small_values2[] = { + {0x1bb0a110, 0x31487671}, + {0x4792784e, 0x28add7d7}, + {0x7b66553a, 0x11dff8ef}, + }; + for (const std::pair<int64_t, int64_t>& pair : small_values2) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + absl::int128 a = absl::int128(pair.first << 32); + absl::int128 b = absl::int128(pair.second << 32); + absl::int128 c = absl::MakeInt128(pair.first * pair.second, 0); + + EXPECT_EQ(c, a * b); + EXPECT_EQ(-c, -a * b); + EXPECT_EQ(-c, a * -b); + EXPECT_EQ(c, -a * -b); + + EXPECT_EQ(c, absl::int128(a) *= b); + EXPECT_EQ(-c, absl::int128(-a) *= b); + EXPECT_EQ(-c, absl::int128(a) *= -b); + EXPECT_EQ(c, absl::int128(-a) *= -b); + } + + // check 0, 1, and -1 behavior with large values + absl::int128 large_values[] = { + {absl::MakeInt128(0xd66f061af02d0408, 0x727d2846cb475b53)}, + {absl::MakeInt128(0x27b8d5ed6104452d, 0x03f8a33b0ee1df4f)}, + {-absl::MakeInt128(0x621b6626b9e8d042, 0x27311ac99df00938)}, + {-absl::MakeInt128(0x34e0656f1e95fb60, 0x4281cfd731257a47)}, + }; + for (absl::int128 value : large_values) { + EXPECT_EQ(0, 0 * value); + EXPECT_EQ(0, value * 0); + EXPECT_EQ(0, absl::int128(0) *= value); + EXPECT_EQ(0, value *= 0); + + EXPECT_EQ(value, 1 * value); + EXPECT_EQ(value, value * 1); + EXPECT_EQ(value, absl::int128(1) *= value); + EXPECT_EQ(value, value *= 1); + + EXPECT_EQ(-value, -1 * value); + EXPECT_EQ(-value, value * -1); + EXPECT_EQ(-value, absl::int128(-1) *= value); + EXPECT_EQ(-value, value *= -1); + } + + // Manually calculated random large value cases + EXPECT_EQ(absl::MakeInt128(0xcd0efd3442219bb, 0xde47c05bcd9df6e1), + absl::MakeInt128(0x7c6448, 0x3bc4285c47a9d253) * 0x1a6037537b); + EXPECT_EQ(-absl::MakeInt128(0x1f8f149850b1e5e6, 0x1e50d6b52d272c3e), + -absl::MakeInt128(0x23, 0x2e68a513ca1b8859) * 0xe5a434cd14866e); + EXPECT_EQ(-absl::MakeInt128(0x55cae732029d1fce, 0xca6474b6423263e4), + 0xa9b98a8ddf66bc * -absl::MakeInt128(0x81, 0x672e58231e2469d7)); + EXPECT_EQ(absl::MakeInt128(0x19c8b7620b507dc4, 0xfec042b71a5f29a4), + -0x3e39341147 * -absl::MakeInt128(0x6a14b2, 0x5ed34cca42327b3c)); + + EXPECT_EQ(absl::MakeInt128(0xcd0efd3442219bb, 0xde47c05bcd9df6e1), + absl::MakeInt128(0x7c6448, 0x3bc4285c47a9d253) *= 0x1a6037537b); + EXPECT_EQ(-absl::MakeInt128(0x1f8f149850b1e5e6, 0x1e50d6b52d272c3e), + -absl::MakeInt128(0x23, 0x2e68a513ca1b8859) *= 0xe5a434cd14866e); + EXPECT_EQ(-absl::MakeInt128(0x55cae732029d1fce, 0xca6474b6423263e4), + absl::int128(0xa9b98a8ddf66bc) *= + -absl::MakeInt128(0x81, 0x672e58231e2469d7)); + EXPECT_EQ(absl::MakeInt128(0x19c8b7620b507dc4, 0xfec042b71a5f29a4), + absl::int128(-0x3e39341147) *= + -absl::MakeInt128(0x6a14b2, 0x5ed34cca42327b3c)); +} + +TEST(Int128, DivisionAndModuloTest) { + // Check against 64 bit division and modulo operators with a sample of + // randomly generated pairs. + std::pair<int64_t, int64_t> small_pairs[] = { + {0x15f2a64138, 0x67da05}, {0x5e56d194af43045f, 0xcf1543fb99}, + {0x15e61ed052036a, -0xc8e6}, {0x88125a341e85, -0xd23fb77683}, + {-0xc06e20, 0x5a}, {-0x4f100219aea3e85d, 0xdcc56cb4efe993}, + {-0x168d629105, -0xa7}, {-0x7b44e92f03ab2375, -0x6516}, + }; + for (const std::pair<int64_t, int64_t>& pair : small_pairs) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + absl::int128 dividend = pair.first; + absl::int128 divisor = pair.second; + int64_t quotient = pair.first / pair.second; + int64_t remainder = pair.first % pair.second; + + EXPECT_EQ(quotient, dividend / divisor); + EXPECT_EQ(quotient, absl::int128(dividend) /= divisor); + EXPECT_EQ(remainder, dividend % divisor); + EXPECT_EQ(remainder, absl::int128(dividend) %= divisor); + } + + // Test behavior with 0, 1, and -1 with a sample of randomly generated large + // values. + absl::int128 values[] = { + absl::MakeInt128(0x63d26ee688a962b2, 0x9e1411abda5c1d70), + absl::MakeInt128(0x152f385159d6f986, 0xbf8d48ef63da395d), + -absl::MakeInt128(0x3098d7567030038c, 0x14e7a8a098dc2164), + -absl::MakeInt128(0x49a037aca35c809f, 0xa6a87525480ef330), + }; + for (absl::int128 value : values) { + SCOPED_TRACE(::testing::Message() << "value = " << value); + + EXPECT_EQ(0, 0 / value); + EXPECT_EQ(0, absl::int128(0) /= value); + EXPECT_EQ(0, 0 % value); + EXPECT_EQ(0, absl::int128(0) %= value); + + EXPECT_EQ(value, value / 1); + EXPECT_EQ(value, absl::int128(value) /= 1); + EXPECT_EQ(0, value % 1); + EXPECT_EQ(0, absl::int128(value) %= 1); + + EXPECT_EQ(-value, value / -1); + EXPECT_EQ(-value, absl::int128(value) /= -1); + EXPECT_EQ(0, value % -1); + EXPECT_EQ(0, absl::int128(value) %= -1); + } + + // Min and max values + EXPECT_EQ(0, absl::Int128Max() / absl::Int128Min()); + EXPECT_EQ(absl::Int128Max(), absl::Int128Max() % absl::Int128Min()); + EXPECT_EQ(-1, absl::Int128Min() / absl::Int128Max()); + EXPECT_EQ(-1, absl::Int128Min() % absl::Int128Max()); + + // Power of two division and modulo of random large dividends + absl::int128 positive_values[] = { + absl::MakeInt128(0x21e1a1cc69574620, 0xe7ac447fab2fc869), + absl::MakeInt128(0x32c2ff3ab89e66e8, 0x03379a613fd1ce74), + absl::MakeInt128(0x6f32ca786184dcaf, 0x046f9c9ecb3a9ce1), + absl::MakeInt128(0x1aeb469dd990e0ee, 0xda2740f243cd37eb), + }; + for (absl::int128 value : positive_values) { + for (int i = 0; i < 127; ++i) { + SCOPED_TRACE(::testing::Message() + << "value = " << value << "; i = " << i); + absl::int128 power_of_two = absl::int128(1) << i; + + EXPECT_EQ(value >> i, value / power_of_two); + EXPECT_EQ(value >> i, absl::int128(value) /= power_of_two); + EXPECT_EQ(value & (power_of_two - 1), value % power_of_two); + EXPECT_EQ(value & (power_of_two - 1), + absl::int128(value) %= power_of_two); + } + } + + // Manually calculated cases with random large dividends + struct DivisionModCase { + absl::int128 dividend; + absl::int128 divisor; + absl::int128 quotient; + absl::int128 remainder; + }; + DivisionModCase manual_cases[] = { + {absl::MakeInt128(0x6ada48d489007966, 0x3c9c5c98150d5d69), + absl::MakeInt128(0x8bc308fb, 0x8cb9cc9a3b803344), 0xc3b87e08, + absl::MakeInt128(0x1b7db5e1, 0xd9eca34b7af04b49)}, + {absl::MakeInt128(0xd6946511b5b, 0x4886c5c96546bf5f), + -absl::MakeInt128(0x263b, 0xfd516279efcfe2dc), -0x59cbabf0, + absl::MakeInt128(0x622, 0xf462909155651d1f)}, + {-absl::MakeInt128(0x33db734f9e8d1399, 0x8447ac92482bca4d), 0x37495078240, + -absl::MakeInt128(0xf01f1, 0xbc0368bf9a77eae8), -0x21a508f404d}, + {-absl::MakeInt128(0x13f837b409a07e7d, 0x7fc8e248a7d73560), -0x1b9f, + absl::MakeInt128(0xb9157556d724, 0xb14f635714d7563e), -0x1ade}, + }; + for (const DivisionModCase test_case : manual_cases) { + EXPECT_EQ(test_case.quotient, test_case.dividend / test_case.divisor); + EXPECT_EQ(test_case.quotient, + absl::int128(test_case.dividend) /= test_case.divisor); + EXPECT_EQ(test_case.remainder, test_case.dividend % test_case.divisor); + EXPECT_EQ(test_case.remainder, + absl::int128(test_case.dividend) %= test_case.divisor); + } +} + +TEST(Int128, BitwiseLogicTest) { + EXPECT_EQ(absl::int128(-1), ~absl::int128(0)); + + absl::int128 values[]{ + 0, -1, 0xde400bee05c3ff6b, absl::MakeInt128(0x7f32178dd81d634a, 0), + absl::MakeInt128(0xaf539057055613a9, 0x7d104d7d946c2e4d)}; + for (absl::int128 value : values) { + EXPECT_EQ(value, ~~value); + + EXPECT_EQ(value, value | value); + EXPECT_EQ(value, value & value); + EXPECT_EQ(0, value ^ value); + + EXPECT_EQ(value, absl::int128(value) |= value); + EXPECT_EQ(value, absl::int128(value) &= value); + EXPECT_EQ(0, absl::int128(value) ^= value); + + EXPECT_EQ(value, value | 0); + EXPECT_EQ(0, value & 0); + EXPECT_EQ(value, value ^ 0); + + EXPECT_EQ(absl::int128(-1), value | absl::int128(-1)); + EXPECT_EQ(value, value & absl::int128(-1)); + EXPECT_EQ(~value, value ^ absl::int128(-1)); + } + + // small sample of randomly generated int64_t's + std::pair<int64_t, int64_t> pairs64[]{ + {0x7f86797f5e991af4, 0x1ee30494fb007c97}, + {0x0b278282bacf01af, 0x58780e0a57a49e86}, + {0x059f266ccb93a666, 0x3d5b731bae9286f5}, + {0x63c0c4820f12108c, 0x58166713c12e1c3a}, + {0x381488bb2ed2a66e, 0x2220a3eb76a3698c}, + {0x2a0a0dfb81e06f21, 0x4b60585927f5523c}, + {0x555b1c3a03698537, 0x25478cd19d8e53cb}, + {0x4750f6f27d779225, 0x16397553c6ff05fc}, + }; + for (const std::pair<int64_t, int64_t>& pair : pairs64) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + EXPECT_EQ(absl::MakeInt128(~pair.first, ~pair.second), + ~absl::MakeInt128(pair.first, pair.second)); + + EXPECT_EQ(absl::int128(pair.first & pair.second), + absl::int128(pair.first) & absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first | pair.second), + absl::int128(pair.first) | absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first ^ pair.second), + absl::int128(pair.first) ^ absl::int128(pair.second)); + + EXPECT_EQ(absl::int128(pair.first & pair.second), + absl::int128(pair.first) &= absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first | pair.second), + absl::int128(pair.first) |= absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first ^ pair.second), + absl::int128(pair.first) ^= absl::int128(pair.second)); + + EXPECT_EQ( + absl::MakeInt128(pair.first & pair.second, 0), + absl::MakeInt128(pair.first, 0) & absl::MakeInt128(pair.second, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first | pair.second, 0), + absl::MakeInt128(pair.first, 0) | absl::MakeInt128(pair.second, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first ^ pair.second, 0), + absl::MakeInt128(pair.first, 0) ^ absl::MakeInt128(pair.second, 0)); + + EXPECT_EQ( + absl::MakeInt128(pair.first & pair.second, 0), + absl::MakeInt128(pair.first, 0) &= absl::MakeInt128(pair.second, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first | pair.second, 0), + absl::MakeInt128(pair.first, 0) |= absl::MakeInt128(pair.second, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first ^ pair.second, 0), + absl::MakeInt128(pair.first, 0) ^= absl::MakeInt128(pair.second, 0)); + } +} + +TEST(Int128, BitwiseShiftTest) { + for (int i = 0; i < 64; ++i) { + for (int j = 0; j <= i; ++j) { + // Left shift from j-th bit to i-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) << (i - j)); + EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) <<= (i - j)); + } + } + for (int i = 0; i < 63; ++i) { + for (int j = 0; j < 64; ++j) { + // Left shift from j-th bit to (i + 64)-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::int128(uint64_t{1} << j) << (i + 64 - j)); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::int128(uint64_t{1} << j) <<= (i + 64 - j)); + } + for (int j = 0; j <= i; ++j) { + // Left shift from (j + 64)-th bit to (i + 64)-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::MakeInt128(uint64_t{1} << j, 0) << (i - j)); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::MakeInt128(uint64_t{1} << j, 0) <<= (i - j)); + } + } + + for (int i = 0; i < 64; ++i) { + for (int j = i; j < 64; ++j) { + // Right shift from j-th bit to i-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) >> (j - i)); + EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) >>= (j - i)); + } + for (int j = 0; j < 63; ++j) { + // Right shift from (j + 64)-th bit to i-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(uint64_t{1} << i, + absl::MakeInt128(uint64_t{1} << j, 0) >> (j + 64 - i)); + EXPECT_EQ(uint64_t{1} << i, + absl::MakeInt128(uint64_t{1} << j, 0) >>= (j + 64 - i)); + } + } + for (int i = 0; i < 63; ++i) { + for (int j = i; j < 63; ++j) { + // Right shift from (j + 64)-th bit to (i + 64)-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::MakeInt128(uint64_t{1} << j, 0) >> (j - i)); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::MakeInt128(uint64_t{1} << j, 0) >>= (j - i)); + } + } +} + +TEST(Int128, NumericLimitsTest) { + static_assert(std::numeric_limits<absl::int128>::is_specialized, ""); + static_assert(std::numeric_limits<absl::int128>::is_signed, ""); + static_assert(std::numeric_limits<absl::int128>::is_integer, ""); + EXPECT_EQ(static_cast<int>(127 * std::log10(2)), + std::numeric_limits<absl::int128>::digits10); + EXPECT_EQ(absl::Int128Min(), std::numeric_limits<absl::int128>::min()); + EXPECT_EQ(absl::Int128Min(), std::numeric_limits<absl::int128>::lowest()); + EXPECT_EQ(absl::Int128Max(), std::numeric_limits<absl::int128>::max()); +} + } // namespace diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index f7587bf9..f78fbc7e 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -1,17 +1,32 @@ +# +# 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. +# + # ABSL random-number generation libraries. +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", "ABSL_DEFAULT_LINKOPTS", - "ABSL_EXCEPTIONS_FLAG", - "ABSL_EXCEPTIONS_FLAG_LINKOPTS", "ABSL_TEST_COPTS", ) package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "random", @@ -52,15 +67,17 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:base_internal", + "//absl/base:config", "//absl/base:core_headers", "//absl/meta:type_traits", - "//absl/random/internal:distribution_impl", "//absl/random/internal:distributions", "//absl/random/internal:fast_uniform_bits", "//absl/random/internal:fastmath", + "//absl/random/internal:generate_real", "//absl/random/internal:iostream_state_saver", "//absl/random/internal:traits", "//absl/random/internal:uniform_helper", + "//absl/random/internal:wide_multiply", "//absl/strings", "//absl/types:span", ], @@ -70,8 +87,8 @@ cc_library( name = "seed_gen_exception", srcs = ["seed_gen_exception.cc"], hdrs = ["seed_gen_exception.h"], - copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = ["//absl/base:config"], ) @@ -94,6 +111,58 @@ cc_library( ], ) +cc_library( + name = "bit_gen_ref", + hdrs = ["bit_gen_ref.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/random/internal:distribution_caller", + "//absl/random/internal:fast_uniform_bits", + "//absl/random/internal:mocking_bit_gen_base", + ], +) + +cc_library( + name = "mock_distributions", + testonly = 1, + hdrs = ["mock_distributions.h"], + deps = [ + ":distributions", + ":mocking_bit_gen", + "//absl/meta:type_traits", + "//absl/random/internal:mock_overload_set", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "mocking_bit_gen", + testonly = 1, + srcs = [ + "mocking_bit_gen.cc", + ], + hdrs = [ + "mocking_bit_gen.h", + ], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + "//absl/base:raw_logging_internal", + "//absl/container:flat_hash_map", + "//absl/meta:type_traits", + "//absl/random/internal:distribution_caller", + "//absl/random/internal:mocking_bit_gen_base", + "//absl/strings", + "//absl/types:span", + "//absl/types:variant", + "//absl/utility", + "@com_google_googletest//:gtest", + ], +) + cc_test( name = "bernoulli_distribution_test", size = "small", @@ -115,11 +184,12 @@ cc_test( timeout = "eternal", # Android can take a very long time srcs = ["beta_distribution_test.cc"], copts = ABSL_TEST_COPTS, + flaky = 1, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":distributions", ":random", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -168,8 +238,8 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -189,7 +259,7 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -214,8 +284,8 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/container:flat_hash_map", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", @@ -234,8 +304,8 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -256,8 +326,8 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -278,7 +348,7 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -302,7 +372,7 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -321,7 +391,7 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -330,6 +400,46 @@ cc_test( ) cc_test( + name = "bit_gen_ref_test", + size = "small", + srcs = ["bit_gen_ref_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bit_gen_ref", + ":random", + "//absl/random/internal:sequence_urbg", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "mocking_bit_gen_test", + size = "small", + srcs = ["mocking_bit_gen_test.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bit_gen_ref", + ":mock_distributions", + ":mocking_bit_gen", + ":random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "mock_distributions_test", + size = "small", + srcs = ["mock_distributions_test.cc"], + deps = [ + ":mock_distributions", + ":mocking_bit_gen", + ":random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( name = "examples_test", size = "small", srcs = ["examples_test.cc"], diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 2d5c0658..efa55d8f 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -34,6 +34,132 @@ absl_cc_library( absl_cc_library( NAME + random_bit_gen_ref + HDRS + "bit_gen_ref.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::core_headers + absl::random_internal_distribution_caller + absl::random_internal_fast_uniform_bits + absl::random_internal_mocking_bit_gen_base + absl::type_traits +) + +absl_cc_test( + NAME + random_bit_gen_ref_test + SRCS + "bit_gen_ref_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_bit_gen_ref + absl::random_random + absl::random_internal_sequence_urbg + gmock + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_mocking_bit_gen_base + HDRS + "internal/mocking_bit_gen_base.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_random + absl::strings +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_mock_overload_set + HDRS + "internal/mock_overload_set.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_mocking_bit_gen + TESTONLY +) + +absl_cc_library( + NAME + random_mocking_bit_gen + HDRS + "mock_distributions.h" + "mocking_bit_gen.h" + SRCS + "mocking_bit_gen.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flat_hash_map + absl::raw_logging_internal + absl::random_distributions + absl::random_internal_distribution_caller + absl::random_internal_mocking_bit_gen_base + absl::random_internal_mock_overload_set + absl::strings + absl::span + absl::type_traits + absl::utility + absl::variant + gmock + gtest + TESTONLY +) + +absl_cc_test( + NAME + random_mock_distributions_test + SRCS + "mock_distributions_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_mocking_bit_gen + absl::random_random + gmock + gtest_main +) + +absl_cc_test( + NAME + random_mocking_bit_gen_test + SRCS + "mocking_bit_gen_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_bit_gen_ref + absl::random_mocking_bit_gen + absl::random_random + gmock + gtest_main +) + +absl_cc_library( + NAME random_distributions SRCS "discrete_distribution.cc" @@ -57,14 +183,16 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::base_internal + absl::config absl::core_headers - absl::random_internal_distribution_impl + absl::random_internal_generate_real absl::random_internal_distributions absl::random_internal_fast_uniform_bits absl::random_internal_fastmath absl::random_internal_iostream_state_saver absl::random_internal_traits absl::random_internal_uniform_helper + absl::random_internal_wide_multiply absl::strings absl::span absl::type_traits @@ -79,9 +207,7 @@ absl_cc_library( "seed_gen_exception.h" COPTS ${ABSL_DEFAULT_COPTS} - ${ABSL_EXCEPTIONS_FLAG} LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config @@ -137,9 +263,9 @@ absl_cc_test( DEPS absl::random_distributions absl::random_random - absl::base absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg + absl::raw_logging_internal absl::strings absl::str_format gmock @@ -174,6 +300,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} absl::random_distributions absl::random_random + absl::raw_logging_internal gmock gtest_main ) @@ -187,12 +314,12 @@ absl_cc_test( ${ABSL_TEST_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} - absl::base absl::core_headers absl::random_distributions absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg absl::random_random + absl::raw_logging_internal absl::strings absl::str_format gmock @@ -209,11 +336,11 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::random_distributions absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg absl::random_random + absl::raw_logging_internal absl::strings gmock gtest_main @@ -231,11 +358,11 @@ absl_cc_test( DEPS absl::random_distributions absl::random_random - absl::base absl::core_headers absl::flat_hash_map absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg + absl::raw_logging_internal absl::strings absl::str_format gmock @@ -252,12 +379,12 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::core_headers absl::random_distributions absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg absl::random_random + absl::raw_logging_internal absl::strings absl::str_format gmock @@ -274,12 +401,12 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::core_headers absl::random_distributions absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg absl::random_random + absl::raw_logging_internal absl::strings absl::str_format gmock @@ -296,11 +423,11 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::random_distributions absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg absl::random_random + absl::raw_logging_internal absl::strings gmock gtest_main @@ -316,7 +443,6 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::random_distributions absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg @@ -336,11 +462,11 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::random_distributions absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg absl::random_random + absl::raw_logging_internal absl::strings gmock gtest_main @@ -401,6 +527,8 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config ) # Internal-only target, do not depend on directly. @@ -434,6 +562,8 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config ) # Internal-only target, do not depend on directly. @@ -448,11 +578,12 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + $<$<BOOL:${MINGW}>:"bcrypt"> DEPS - absl::base absl::core_headers absl::optional absl::random_internal_fast_uniform_bits + absl::raw_logging_internal absl::span absl::strings ) @@ -478,6 +609,7 @@ absl_cc_library( absl::random_internal_seed_material absl::random_internal_traits absl::random_seed_gen_exception + absl::raw_logging_internal absl::span ) @@ -491,6 +623,8 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config TESTONLY ) @@ -504,6 +638,8 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config TESTONLY ) @@ -543,19 +679,34 @@ absl_cc_library( # Internal-only target, do not depend on directly. absl_cc_library( NAME - random_internal_distribution_impl + random_internal_generate_real HDRS - "internal/distribution_impl.h" + "internal/generate_real.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS absl::bits - absl::config - absl::int128 absl::random_internal_fastmath absl::random_internal_traits + absl::type_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_wide_multiply + HDRS + "internal/wide_multiply.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::bits + absl::config + absl::int128 ) # Internal-only target, do not depend on directly. @@ -624,6 +775,7 @@ absl_cc_library( DEPS absl::random_internal_iostream_state_saver absl::random_internal_randen + absl::raw_logging_internal absl::type_traits ) @@ -639,6 +791,8 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config ) # Internal-only target, do not depend on directly. @@ -654,7 +808,6 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::random_internal_platform absl::random_internal_randen_hwaes absl::random_internal_randen_slow @@ -674,6 +827,7 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_platform + absl::config ) # Internal-only target, do not depend on directly. @@ -693,6 +847,7 @@ absl_cc_library( DEPS absl::random_internal_platform absl::random_internal_randen_hwaes_impl + absl::config ) # Internal-only target, do not depend on directly. @@ -709,6 +864,7 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_platform + absl::config ) # Internal-only target, do not depend on directly. @@ -726,8 +882,9 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base + absl::config absl::core_headers + absl::raw_logging_internal absl::strings absl::str_format absl::span @@ -751,9 +908,9 @@ absl_cc_test( # Internal-only target, do not depend on directly. absl_cc_test( NAME - random_internal_distribution_impl_test + random_internal_generate_real_test SRCS - "internal/distribution_impl_test.cc" + "internal/generate_real_test.cc" COPTS ${ABSL_TEST_COPTS} LINKOPTS @@ -761,8 +918,7 @@ absl_cc_test( DEPS absl::bits absl::flags - absl::int128 - absl::random_internal_distribution_impl + absl::random_internal_generate_real gtest_main ) @@ -941,9 +1097,9 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::random_internal_explicit_seed_seq absl::random_internal_randen_engine + absl::raw_logging_internal absl::strings absl::time gmock @@ -995,7 +1151,7 @@ absl_cc_test( absl::random_internal_platform absl::random_internal_randen_hwaes absl::random_internal_randen_hwaes_impl - absl::base + absl::raw_logging_internal absl::str_format gmock gtest @@ -1013,7 +1169,6 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::core_headers - absl::random_internal_distribution_impl absl::random_internal_fast_uniform_bits absl::random_internal_iostream_state_saver absl::random_internal_traits @@ -1026,9 +1181,28 @@ absl_cc_test( random_internal_iostream_state_saver_test SRCS "internal/iostream_state_saver_test.cc" + COPTS + ${ABSL_TEST_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_iostream_state_saver gtest_main ) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_wide_multiply_test + SRCS + internal/wide_multiply_test.cc + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_wide_multiply + absl::bits + absl::int128 + gtest_main +) diff --git a/absl/random/bernoulli_distribution.h b/absl/random/bernoulli_distribution.h index 0afc2c14..25bd0d5c 100644 --- a/absl/random/bernoulli_distribution.h +++ b/absl/random/bernoulli_distribution.h @@ -24,7 +24,7 @@ #include "absl/random/internal/iostream_state_saver.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::bernoulli_distribution is a drop in replacement for // std::bernoulli_distribution. It guarantees that (given a perfect @@ -194,7 +194,7 @@ bool bernoulli_distribution::Generate(double p, } } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_ diff --git a/absl/random/beta_distribution.h b/absl/random/beta_distribution.h index ff1eba80..c154066f 100644 --- a/absl/random/beta_distribution.h +++ b/absl/random/beta_distribution.h @@ -22,13 +22,14 @@ #include <ostream> #include <type_traits> -#include "absl/random/internal/distribution_impl.h" +#include "absl/meta/type_traits.h" #include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/fastmath.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::beta_distribution: // Generate a floating-point variate conforming to a Beta distribution: @@ -130,7 +131,7 @@ class beta_distribution { private: friend class beta_distribution; -#ifdef COMPILER_MSVC +#ifdef _MSC_VER // MSVC does not have constexpr implementations for std::log and std::exp // so they are computed at runtime. #define ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR @@ -276,15 +277,21 @@ typename beta_distribution<RealType>::result_type beta_distribution<RealType>::AlgorithmJoehnk( URBG& g, // NOLINT(runtime/references) const param_type& p) { + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using real_type = + absl::conditional_t<std::is_same<RealType, float>::value, float, double>; + // Based on Joehnk, M. D. Erzeugung von betaverteilten und gammaverteilten // Zufallszahlen. Metrika 8.1 (1964): 5-15. // This method is described in Knuth, Vol 2 (Third Edition), pp 134. - using RandU64ToReal = typename random_internal::RandU64ToReal<result_type>; - using random_internal::PositiveValueT; + result_type u, v, x, y, z; for (;;) { - u = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g)); - v = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g)); + u = GenerateRealFromBits<real_type, GeneratePositiveTag, false>( + fast_u64_(g)); + v = GenerateRealFromBits<real_type, GeneratePositiveTag, false>( + fast_u64_(g)); // Direct method. std::pow is slow for float, so rely on the optimizer to // remove the std::pow() path for that case. @@ -328,12 +335,14 @@ typename beta_distribution<RealType>::result_type beta_distribution<RealType>::AlgorithmCheng( URBG& g, // NOLINT(runtime/references) const param_type& p) { + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using real_type = + absl::conditional_t<std::is_same<RealType, float>::value, float, double>; + // Based on Cheng, Russell CH. Generating beta variates with nonintegral // shape parameters. Communications of the ACM 21.4 (1978): 317-322. // (https://dl.acm.org/citation.cfm?id=359482). - using RandU64ToReal = typename random_internal::RandU64ToReal<result_type>; - using random_internal::PositiveValueT; - static constexpr result_type kLogFour = result_type(1.3862943611198906188344642429163531361); // log(4) static constexpr result_type kS = @@ -342,8 +351,10 @@ beta_distribution<RealType>::AlgorithmCheng( const bool use_algorithm_ba = (p.method_ == param_type::CHENG_BA); result_type u1, u2, v, w, z, r, s, t, bw_inv, lhs; for (;;) { - u1 = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g)); - u2 = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g)); + u1 = GenerateRealFromBits<real_type, GeneratePositiveTag, false>( + fast_u64_(g)); + u2 = GenerateRealFromBits<real_type, GeneratePositiveTag, false>( + fast_u64_(g)); v = p.y_ * std::log(u1 / (1 - u1)); w = p.a_ * std::exp(v); bw_inv = result_type(1) / (p.b_ + w); @@ -410,7 +421,7 @@ std::basic_istream<CharT, Traits>& operator>>( return is; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_BETA_DISTRIBUTION_H_ diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc index 966ad08b..d0111b3e 100644 --- a/absl/random/beta_distribution_test.cc +++ b/absl/random/beta_distribution_test.cc @@ -92,7 +92,7 @@ TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { for (TypeParam alpha : kValues) { for (TypeParam beta : kValues) { ABSL_INTERNAL_LOG( - INFO, absl::StrFormat("Smoke test for Beta(%f, %f)", alpha, beta)); + INFO, absl::StrFormat("Smoke test for Beta(%a, %a)", alpha, beta)); param_type param(alpha, beta); absl::beta_distribution<TypeParam> before(alpha, beta); diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h new file mode 100644 index 00000000..e8771162 --- /dev/null +++ b/absl/random/bit_gen_ref.h @@ -0,0 +1,153 @@ +// +// 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: bit_gen_ref.h +// ----------------------------------------------------------------------------- +// +// This header defines a bit generator "reference" class, for use in interfaces +// that take both Abseil (e.g. `absl::BitGen`) and standard library (e.g. +// `std::mt19937`) bit generators. + +#ifndef ABSL_RANDOM_BIT_GEN_REF_H_ +#define ABSL_RANDOM_BIT_GEN_REF_H_ + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" +#include "absl/random/internal/distribution_caller.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/mocking_bit_gen_base.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace random_internal { + +template <typename URBG, typename = void, typename = void, typename = void> +struct is_urbg : std::false_type {}; + +template <typename URBG> +struct is_urbg< + URBG, + absl::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype((URBG::min)())>::type>::value>, + absl::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype((URBG::max)())>::type>::value>, + absl::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> + : std::true_type {}; + +} // namespace random_internal + +// ----------------------------------------------------------------------------- +// absl::BitGenRef +// ----------------------------------------------------------------------------- +// +// `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic +// non-owning "reference" interface for use in place of any specific uniform +// random bit generator (URBG). This class may be used for both Abseil +// (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g +// `std::mt19937`, `std::minstd_rand`) bit generators. +// +// Like other reference classes, `absl::BitGenRef` does not own the +// underlying bit generator, and the underlying instance must outlive the +// `absl::BitGenRef`. +// +// `absl::BitGenRef` is particularly useful when used with an +// `absl::MockingBitGen` to test specific paths in functions which use random +// values. +// +// Example: +// void TakesBitGenRef(absl::BitGenRef gen) { +// int x = absl::Uniform<int>(gen, 0, 1000); +// } +// +class BitGenRef { + public: + using result_type = uint64_t; + + BitGenRef(const absl::BitGenRef&) = default; + BitGenRef(absl::BitGenRef&&) = default; + BitGenRef& operator=(const absl::BitGenRef&) = default; + BitGenRef& operator=(absl::BitGenRef&&) = default; + + template <typename URBG, + typename absl::enable_if_t< + (!std::is_same<URBG, BitGenRef>::value && + random_internal::is_urbg<URBG>::value)>* = nullptr> + BitGenRef(URBG& gen) // NOLINT + : mocked_gen_ptr_(MakeMockPointer(&gen)), + t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), + generate_impl_fn_(ImplFn<URBG>) { + } + + static constexpr result_type(min)() { + return (std::numeric_limits<result_type>::min)(); + } + + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); } + + private: + friend struct absl::random_internal::DistributionCaller<absl::BitGenRef>; + using impl_fn = result_type (*)(uintptr_t); + using mocker_base_t = absl::random_internal::MockingBitGenBase; + + // Convert an arbitrary URBG pointer into either a valid mocker_base_t + // pointer or a nullptr. + static inline mocker_base_t* MakeMockPointer(mocker_base_t* t) { return t; } + static inline mocker_base_t* MakeMockPointer(void*) { return nullptr; } + + template <typename URBG> + static result_type ImplFn(uintptr_t ptr) { + // Ensure that the return values from operator() fill the entire + // range promised by result_type, min() and max(). + absl::random_internal::FastUniformBits<result_type> fast_uniform_bits; + return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr)); + } + + mocker_base_t* mocked_gen_ptr_; + uintptr_t t_erased_gen_ptr_; + impl_fn generate_impl_fn_; +}; + +namespace random_internal { + +template <> +struct DistributionCaller<absl::BitGenRef> { + template <typename DistrT, typename FormatT, typename... Args> + static typename DistrT::result_type Call(absl::BitGenRef* gen_ref, + Args&&... args) { + auto* mock_ptr = gen_ref->mocked_gen_ptr_; + if (mock_ptr == nullptr) { + DistrT dist(std::forward<Args>(args)...); + return dist(*gen_ref); + } else { + return mock_ptr->template Call<DistrT, FormatT>( + std::forward<Args>(args)...); + } + } +}; + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_BIT_GEN_REF_H_ diff --git a/absl/random/bit_gen_ref_test.cc b/absl/random/bit_gen_ref_test.cc new file mode 100644 index 00000000..ca0e4d70 --- /dev/null +++ b/absl/random/bit_gen_ref_test.cc @@ -0,0 +1,101 @@ +// +// 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/random/bit_gen_ref.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class ConstBitGen : public absl::random_internal::MockingBitGenBase { + bool CallImpl(const std::type_info&, void*, void* result) override { + *static_cast<int*>(result) = 42; + return true; + } +}; + +namespace random_internal { +template <> +struct DistributionCaller<ConstBitGen> { + template <typename DistrT, typename FormatT, typename... Args> + static typename DistrT::result_type Call(ConstBitGen* gen, Args&&... args) { + return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...); + } +}; +} // namespace random_internal + +namespace { +int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); } + +template <typename T> +class BitGenRefTest : public testing::Test {}; + +using BitGenTypes = + ::testing::Types<absl::BitGen, absl::InsecureBitGen, std::mt19937, + std::mt19937_64, std::minstd_rand>; +TYPED_TEST_SUITE(BitGenRefTest, BitGenTypes); + +TYPED_TEST(BitGenRefTest, BasicTest) { + TypeParam gen; + auto x = FnTest(gen); + EXPECT_NEAR(x, 4, 3); +} + +TYPED_TEST(BitGenRefTest, Copyable) { + TypeParam gen; + absl::BitGenRef gen_ref(gen); + FnTest(gen_ref); // Copy +} + +TEST(BitGenRefTest, PassThroughEquivalence) { + // sequence_urbg returns 64-bit results. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<uint64_t> output(12); + + { + absl::BitGenRef view(urbg); + for (auto& v : output) { + v = view(); + } + } + + std::vector<uint64_t> expected( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + EXPECT_THAT(output, testing::Eq(expected)); +} + +TEST(BitGenRefTest, MockingBitGenBaseOverrides) { + ConstBitGen const_gen; + EXPECT_EQ(FnTest(const_gen), 42); + + absl::BitGenRef gen_ref(const_gen); + EXPECT_EQ(FnTest(gen_ref), 42); // Copy +} +} // namespace +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/random/discrete_distribution.cc b/absl/random/discrete_distribution.cc index 13a2dbe4..081accee 100644 --- a/absl/random/discrete_distribution.cc +++ b/absl/random/discrete_distribution.cc @@ -15,7 +15,7 @@ #include "absl/random/discrete_distribution.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // Initializes the distribution table for Walker's Aliasing algorithm, described @@ -94,5 +94,5 @@ std::vector<std::pair<double, size_t>> InitDiscreteDistribution( } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/discrete_distribution.h b/absl/random/discrete_distribution.h index 5866fb23..171aa11a 100644 --- a/absl/random/discrete_distribution.h +++ b/absl/random/discrete_distribution.h @@ -29,7 +29,7 @@ #include "absl/random/uniform_int_distribution.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::discrete_distribution // @@ -241,7 +241,7 @@ std::basic_istream<CharT, Traits>& operator>>( return is; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_ diff --git a/absl/random/distribution_format_traits.h b/absl/random/distribution_format_traits.h index 838271c7..22b358cc 100644 --- a/absl/random/distribution_format_traits.h +++ b/absl/random/distribution_format_traits.h @@ -36,7 +36,13 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN + +struct IntervalClosedClosedTag; +struct IntervalClosedOpenTag; +struct IntervalOpenClosedTag; +struct IntervalOpenOpenTag; + namespace random_internal { // ScalarTypeName defines a preferred hierarchy of preferred type names for @@ -244,8 +250,29 @@ struct DistributionFormatTraits<absl::log_uniform_int_distribution<R>> { } }; +template <typename NumType> +struct UniformDistributionWrapper; + +template <typename NumType> +struct DistributionFormatTraits<UniformDistributionWrapper<NumType>> { + using distribution_t = UniformDistributionWrapper<NumType>; + using result_t = NumType; + + static constexpr const char* Name() { return "Uniform"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<NumType>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrCat((d.min)(), ", ", (d.max)()); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ diff --git a/absl/random/distributions.h b/absl/random/distributions.h index f2ebfc2d..d026d92b 100644 --- a/absl/random/distributions.h +++ b/absl/random/distributions.h @@ -67,20 +67,15 @@ #include "absl/random/zipf_distribution.h" namespace absl { -inline namespace lts_2019_08_08 { - -ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedClosedT, - IntervalClosedClosed, {}); -ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedClosedT, - IntervalClosed, {}); -ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedOpenT, - IntervalClosedOpen, {}); -ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenOpenT, - IntervalOpenOpen, {}); -ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenOpenT, - IntervalOpen, {}); -ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenClosedT, - IntervalOpenClosed, {}); +ABSL_NAMESPACE_BEGIN + +ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedClosedTag, IntervalClosedClosed, + {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedClosedTag, IntervalClosed, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedOpenTag, IntervalClosedOpen, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenOpenTag, IntervalOpenOpen, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenOpenTag, IntervalOpen, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenClosedTag, IntervalOpenClosed, {}); // ----------------------------------------------------------------------------- // absl::Uniform<T>(tag, bitgen, lo, hi) @@ -102,7 +97,7 @@ ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenClosedT, // the return type based on the provided endpoint arguments {A lo, B hi}. // Given these endpoints, one of {A, B} will be chosen as the return type, if // a type can be implicitly converted into the other in a lossless way. The -// lack of any such implcit conversion between {A, B} will produce a +// lack of any such implicit conversion between {A, B} will produce a // compile-time error // // See https://en.wikipedia.org/wiki/Uniform_distribution_(continuous) @@ -130,7 +125,15 @@ Uniform(TagType tag, URBG&& urbg, // NOLINT(runtime/references) R lo, R hi) { using gen_t = absl::decay_t<URBG>; - return random_internal::UniformImpl<R, TagType, gen_t>(tag, urbg, lo, hi); + using distribution_t = random_internal::UniformDistributionWrapper<R>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + auto a = random_internal::uniform_lower_bound(tag, lo, hi); + auto b = random_internal::uniform_upper_bound(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, tag, lo, hi); } // absl::Uniform<T>(bitgen, lo, hi) @@ -141,11 +144,17 @@ template <typename R = void, typename URBG> typename absl::enable_if_t<!std::is_same<R, void>::value, R> // Uniform(URBG&& urbg, // NOLINT(runtime/references) R lo, R hi) { - constexpr auto tag = absl::IntervalClosedOpen; - using tag_t = decltype(tag); using gen_t = absl::decay_t<URBG>; + using distribution_t = random_internal::UniformDistributionWrapper<R>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; - return random_internal::UniformImpl<R, tag_t, gen_t>(tag, urbg, lo, hi); + constexpr auto tag = absl::IntervalClosedOpen; + auto a = random_internal::uniform_lower_bound(tag, lo, hi); + auto b = random_internal::uniform_upper_bound(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, lo, hi); } // absl::Uniform(tag, bitgen, lo, hi) @@ -162,9 +171,16 @@ Uniform(TagType tag, A lo, B hi) { using gen_t = absl::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; + using distribution_t = random_internal::UniformDistributionWrapper<return_t>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); + auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); + if (a > b) return a; - return random_internal::UniformImpl<return_t, TagType, gen_t>(tag, urbg, lo, - hi); + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, tag, static_cast<return_t>(lo), + static_cast<return_t>(hi)); } // absl::Uniform(bitgen, lo, hi) @@ -177,13 +193,19 @@ typename absl::enable_if_t<std::is_same<R, void>::value, random_internal::uniform_inferred_return_t<A, B>> Uniform(URBG&& urbg, // NOLINT(runtime/references) A lo, B hi) { - constexpr auto tag = absl::IntervalClosedOpen; - using tag_t = decltype(tag); using gen_t = absl::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; + using distribution_t = random_internal::UniformDistributionWrapper<return_t>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; - return random_internal::UniformImpl<return_t, tag_t, gen_t>(tag, urbg, lo, - hi); + constexpr auto tag = absl::IntervalClosedOpen; + auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); + auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, static_cast<return_t>(lo), + static_cast<return_t>(hi)); } // absl::Uniform<unsigned T>(bitgen) @@ -193,13 +215,12 @@ Uniform(URBG&& urbg, // NOLINT(runtime/references) template <typename R, typename URBG> typename absl::enable_if_t<!std::is_signed<R>::value, R> // Uniform(URBG&& urbg) { // NOLINT(runtime/references) - constexpr auto tag = absl::IntervalClosedClosed; - constexpr auto lo = std::numeric_limits<R>::lowest(); - constexpr auto hi = (std::numeric_limits<R>::max)(); - using tag_t = decltype(tag); using gen_t = absl::decay_t<URBG>; + using distribution_t = random_internal::UniformDistributionWrapper<R>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; - return random_internal::UniformImpl<R, tag_t, gen_t>(tag, urbg, lo, hi); + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg); } // ----------------------------------------------------------------------------- @@ -270,10 +291,10 @@ RealType Beta(URBG&& urbg, // NOLINT(runtime/references) // absl::Exponential<T>(bitgen, lambda = 1) // ----------------------------------------------------------------------------- // -// `absl::Exponential` produces a floating point number for discrete -// distributions of events occurring continuously and independently at a -// constant average rate. `T` must be a floating point type, but may be inferred -// from the type of `lambda`. +// `absl::Exponential` produces a floating point number representing the +// distance (time) between two consecutive events in a point process of events +// occurring continuously and independently at a constant average rate. `T` must +// be a floating point type, but may be inferred from the type of `lambda`. // // See https://en.wikipedia.org/wiki/Exponential_distribution. // @@ -438,7 +459,7 @@ IntType Zipf(URBG&& urbg, // NOLINT(runtime/references) distribution_t, format_t>(&urbg, hi, q, v); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_DISTRIBUTIONS_H_ diff --git a/absl/random/distributions_test.cc b/absl/random/distributions_test.cc index eb82868d..2d92723a 100644 --- a/absl/random/distributions_test.cc +++ b/absl/random/distributions_test.cc @@ -171,14 +171,11 @@ void CheckArgsInferType() { ""); static_assert( absl::conjunction< + std::is_same<Expect, decltype(InferredTaggedUniformReturnT< + absl::IntervalOpenOpenTag, A, B>(0))>, std::is_same<Expect, decltype(InferredTaggedUniformReturnT< - absl::random_internal::IntervalOpenOpenT, A, B>( - 0))>, - std::is_same<Expect, - decltype(InferredTaggedUniformReturnT< - absl::random_internal::IntervalOpenOpenT, B, A>( - 0))>>::value, + absl::IntervalOpenOpenTag, B, A>(0))>>::value, ""); } @@ -218,12 +215,10 @@ void CheckArgsReturnExpectedType() { absl::conjunction< std::is_same<Expect, decltype(ExplicitTaggedUniformReturnT< - absl::random_internal::IntervalOpenOpenT, A, B, - Expect>(0))>, - std::is_same<Expect, - decltype(ExplicitTaggedUniformReturnT< - absl::random_internal::IntervalOpenOpenT, B, A, - Expect>(0))>>::value, + absl::IntervalOpenOpenTag, A, B, Expect>(0))>, + std::is_same<Expect, decltype(ExplicitTaggedUniformReturnT< + absl::IntervalOpenOpenTag, B, A, + Expect>(0))>>::value, ""); } diff --git a/absl/random/exponential_distribution.h b/absl/random/exponential_distribution.h index ed5551ae..b5caf8a1 100644 --- a/absl/random/exponential_distribution.h +++ b/absl/random/exponential_distribution.h @@ -21,12 +21,13 @@ #include <limits> #include <type_traits> -#include "absl/random/internal/distribution_impl.h" +#include "absl/meta/type_traits.h" #include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::exponential_distribution: // Generates a number conforming to an exponential distribution and is @@ -119,9 +120,14 @@ typename exponential_distribution<RealType>::result_type exponential_distribution<RealType>::operator()( URBG& g, // NOLINT(runtime/references) const param_type& p) { - using random_internal::NegativeValueT; - const result_type u = random_internal::RandU64ToReal< - result_type>::template Value<NegativeValueT, false>(fast_u64_(g)); + using random_internal::GenerateNegativeTag; + using random_internal::GenerateRealFromBits; + using real_type = + absl::conditional_t<std::is_same<RealType, float>::value, float, double>; + + const result_type u = GenerateRealFromBits<real_type, GenerateNegativeTag, + false>(fast_u64_(g)); // U(-1, 0) + // log1p(-x) is mathematically equivalent to log(1 - x) but has more // accuracy for x near zero. return p.neg_inv_lambda_ * std::log1p(u); @@ -153,7 +159,7 @@ std::basic_istream<CharT, Traits>& operator>>( return is; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_ diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc index 6f8865c2..f3cfd764 100644 --- a/absl/random/exponential_distribution_test.cc +++ b/absl/random/exponential_distribution_test.cc @@ -46,7 +46,11 @@ using absl::random_internal::kChiSquared; template <typename RealType> class ExponentialDistributionTypedTest : public ::testing::Test {}; +#if defined(__EMSCRIPTEN__) +using RealTypes = ::testing::Types<float, double>; +#else using RealTypes = ::testing::Types<float, double, long double>; +#endif // defined(__EMSCRIPTEN__) TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes); TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) { @@ -346,7 +350,7 @@ std::string ParamName(const ::testing::TestParamInfo<Param>& info) { return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); } -INSTANTIATE_TEST_CASE_P(, ExponentialDistributionTests, +INSTANTIATE_TEST_CASE_P(All, ExponentialDistributionTests, ::testing::ValuesIn(GenParams()), ParamName); // NOTE: absl::exponential_distribution is not guaranteed to be stable. diff --git a/absl/random/gaussian_distribution.cc b/absl/random/gaussian_distribution.cc index dbc2d848..c7a72cb2 100644 --- a/absl/random/gaussian_distribution.cc +++ b/absl/random/gaussian_distribution.cc @@ -4,7 +4,7 @@ #include "absl/random/gaussian_distribution.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { const gaussian_distribution_base::Tables @@ -97,7 +97,7 @@ const gaussian_distribution_base::Tables 0.9362826816850632339, 0.9635996931270905952, 1}}; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // clang-format on diff --git a/absl/random/gaussian_distribution.h b/absl/random/gaussian_distribution.h index 8ee95148..4b07a5c0 100644 --- a/absl/random/gaussian_distribution.h +++ b/absl/random/gaussian_distribution.h @@ -28,12 +28,13 @@ #include <limits> #include <type_traits> -#include "absl/random/internal/distribution_impl.h" +#include "absl/base/config.h" #include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // absl::gaussian_distribution_base implements the underlying ziggurat algorithm @@ -43,7 +44,7 @@ namespace random_internal { // The specific algorithm has some of the improvements suggested by the // 2005 paper, "An Improved Ziggurat Method to Generate Normal Random Samples", // Jurgen A Doornik. (https://www.doornik.com/research/ziggurat.pdf) -class gaussian_distribution_base { +class ABSL_DLL gaussian_distribution_base { public: template <typename URBG> inline double zignor(URBG& g); // NOLINT(runtime/references) @@ -208,12 +209,18 @@ namespace random_internal { template <typename URBG> inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) { + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + // This fallback path happens approximately 0.05% of the time. double x, y; do { // kRInv = 1/r, U(0, 1) - x = kRInv * std::log(RandU64ToDouble<PositiveValueT, false>(fast_u64_(g))); - y = -std::log(RandU64ToDouble<PositiveValueT, false>(fast_u64_(g))); + x = kRInv * + std::log(GenerateRealFromBits<double, GeneratePositiveTag, false>( + fast_u64_(g))); + y = -std::log( + GenerateRealFromBits<double, GeneratePositiveTag, false>(fast_u64_(g))); } while ((y + y) < (x * x)); return neg ? (x - kR) : (kR - x); } @@ -221,6 +228,10 @@ inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) { template <typename URBG> inline double gaussian_distribution_base::zignor( URBG& g) { // NOLINT(runtime/references) + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using random_internal::GenerateSignedTag; + while (true) { // We use a single uint64_t to generate both a double and a strip. // These bits are unused when the generated double is > 1/2^5. @@ -228,7 +239,8 @@ inline double gaussian_distribution_base::zignor( // values (those smaller than 1/2^5, which all end up on the left tail). uint64_t bits = fast_u64_(g); int i = static_cast<int>(bits & kMask); // pick a random strip - double j = RandU64ToDouble<SignedValueT, false>(bits); // U(-1, 1) + double j = GenerateRealFromBits<double, GenerateSignedTag, false>( + bits); // U(-1, 1) const double x = j * zg_.x[i]; // Retangular box. Handles >97% of all cases. @@ -245,7 +257,8 @@ inline double gaussian_distribution_base::zignor( } // i > 0: Wedge samples using precomputed values. - double v = RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)); // U(0, 1) + double v = GenerateRealFromBits<double, GeneratePositiveTag, false>( + fast_u64_(g)); // U(0, 1) if ((zg_.f[i + 1] + v * (zg_.f[i] - zg_.f[i + 1])) < std::exp(-0.5 * x * x)) { return x; @@ -256,7 +269,7 @@ inline double gaussian_distribution_base::zignor( } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_ diff --git a/absl/random/gaussian_distribution_test.cc b/absl/random/gaussian_distribution_test.cc index 47c2989d..49c07513 100644 --- a/absl/random/gaussian_distribution_test.cc +++ b/absl/random/gaussian_distribution_test.cc @@ -394,7 +394,7 @@ std::string ParamName(const ::testing::TestParamInfo<Param>& info) { return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); } -INSTANTIATE_TEST_SUITE_P(, GaussianDistributionTests, +INSTANTIATE_TEST_SUITE_P(All, GaussianDistributionTests, ::testing::ValuesIn(GenParams()), ParamName); // NOTE: absl::gaussian_distribution is not guaranteed to be stable. diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index fd5471a6..d7ad4efe 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -1,3 +1,21 @@ +# +# 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. +# + +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") + # Internal-only implementation classes for Abseil Random load( "//absl:copts/configure_copts.bzl", @@ -33,24 +51,21 @@ cc_library( visibility = [ "//absl/random:__pkg__", ], + deps = ["//absl/base:config"], ) cc_library( name = "distributions", - hdrs = [ - "distributions.h", - ], + hdrs = ["distributions.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":distribution_caller", - ":fast_uniform_bits", - ":fastmath", ":traits", ":uniform_helper", + "//absl/base", "//absl/meta:type_traits", "//absl/strings", - "//absl/types:span", ], ) @@ -64,6 +79,7 @@ cc_library( visibility = [ "//absl/random:__pkg__", ], + deps = ["//absl/base:config"], ) cc_library( @@ -75,11 +91,14 @@ cc_library( "seed_material.h", ], copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, + linkopts = ABSL_DEFAULT_LINKOPTS + select({ + "//absl:windows": ["-DEFAULTLIB:bcrypt.lib"], + "//conditions:default": [], + }), deps = [ ":fast_uniform_bits", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/strings", "//absl/types:optional", "//absl/types:span", @@ -107,6 +126,7 @@ cc_library( "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", + "//absl/base:raw_logging_internal", "//absl/random:seed_gen_exception", "//absl/types:span", ], @@ -120,6 +140,7 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + deps = ["//absl/base:config"], ) cc_library( @@ -130,6 +151,7 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + deps = ["//absl/base:config"], ) cc_library( @@ -160,9 +182,9 @@ cc_library( ) cc_library( - name = "distribution_impl", + name = "generate_real", hdrs = [ - "distribution_impl.h", + "generate_real.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, @@ -170,8 +192,7 @@ cc_library( ":fastmath", ":traits", "//absl/base:bits", - "//absl/base:config", - "//absl/numeric:int128", + "//absl/meta:type_traits", ], ) @@ -186,6 +207,19 @@ cc_library( ) cc_library( + name = "wide_multiply", + hdrs = ["wide_multiply.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":traits", + "//absl/base:bits", + "//absl/base:config", + "//absl/numeric:int128", + ], +) + +cc_library( name = "nonsecure_base", hdrs = ["nonsecure_base.h"], copts = ABSL_DEFAULT_COPTS, @@ -239,6 +273,7 @@ cc_library( "randen-keys.inc", "platform.h", ], + deps = ["//absl/base:config"], ) cc_library( @@ -255,7 +290,7 @@ cc_library( ":platform", ":randen_hwaes", ":randen_slow", - "//absl/base", + "//absl/base:raw_logging_internal", ], ) @@ -267,6 +302,8 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":platform", + "//absl/base:config", + "//absl/base:core_headers", ], ) @@ -286,6 +323,7 @@ cc_library( deps = [ ":platform", ":randen_hwaes_impl", + "//absl/base:config", ], ) @@ -305,7 +343,11 @@ cc_library( # anyway and thus there wouldn't be any gain from using it as a module. features = ["-header_modules"], linkopts = ABSL_DEFAULT_LINKOPTS, - deps = [":platform"], + deps = [ + ":platform", + "//absl/base:config", + "//absl/base:core_headers", + ], ) cc_binary( @@ -334,8 +376,9 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - "//absl/base", + "//absl/base:config", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/strings", "//absl/strings:str_format", "//absl/types:span", @@ -367,16 +410,17 @@ cc_test( ) cc_test( - name = "distribution_impl_test", + name = "generate_real_test", size = "small", - srcs = ["distribution_impl_test.cc"], + srcs = [ + "generate_real_test.cc", + ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":distribution_impl", + ":generate_real", "//absl/base:bits", "//absl/flags:flag", - "//absl/numeric:int128", "@com_google_googletest//:gtest_main", ], ) @@ -459,6 +503,29 @@ cc_test( ], ) +cc_library( + name = "mocking_bit_gen_base", + hdrs = ["mocking_bit_gen_base.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/random", + "//absl/strings", + ], +) + +cc_library( + name = "mock_overload_set", + testonly = 1, + hdrs = ["mock_overload_set.h"], + visibility = [ + "//absl/random:__pkg__", + ], + deps = [ + "//absl/random:mocking_bit_gen", + "@com_google_googletest//:gtest", + ], +) + cc_test( name = "nonsecure_base_test", size = "small", @@ -531,7 +598,7 @@ cc_test( deps = [ ":explicit_seed_seq", ":randen_engine", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/strings", "//absl/time", "@com_google_googletest//:gtest_main", @@ -574,12 +641,26 @@ cc_test( ":platform", ":randen_hwaes", ":randen_hwaes_impl", # build_cleaner: keep - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/strings:str_format", "@com_google_googletest//:gtest", ], ) +cc_test( + name = "wide_multiply_test", + size = "small", + srcs = ["wide_multiply_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":wide_multiply", + "//absl/base:bits", + "//absl/numeric:int128", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "nanobenchmark", srcs = ["nanobenchmark.cc"], @@ -588,7 +669,8 @@ cc_library( deps = [ ":platform", ":randen_engine", - "//absl/base", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", ], ) @@ -598,11 +680,6 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":distribution_impl", - ":fast_uniform_bits", - ":iostream_state_saver", - ":traits", - "//absl/base:core_headers", "//absl/meta:type_traits", ], ) @@ -620,7 +697,7 @@ cc_test( ], deps = [ ":nanobenchmark", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/strings", ], ) @@ -641,7 +718,7 @@ cc_test( ":randen_hwaes", ":randen_hwaes_impl", ":randen_slow", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/strings", ], ) diff --git a/absl/random/internal/chi_square.cc b/absl/random/internal/chi_square.cc index 45671a3e..640d48ce 100644 --- a/absl/random/internal/chi_square.cc +++ b/absl/random/internal/chi_square.cc @@ -19,7 +19,7 @@ #include "absl/random/internal/distribution_test_util.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { namespace { @@ -228,5 +228,5 @@ double ChiSquarePValue(double chi_square, int dof) { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/chi_square.h b/absl/random/internal/chi_square.h index 002b4c95..07f4fbe5 100644 --- a/absl/random/internal/chi_square.h +++ b/absl/random/internal/chi_square.h @@ -26,8 +26,10 @@ #include <cassert> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { constexpr const char kChiSquared[] = "chi-squared"; @@ -81,7 +83,7 @@ double ChiSquareValue(int dof, double p); double ChiSquarePValue(double chi_square, int dof); } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_ diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h index dd06c985..02603cf8 100644 --- a/absl/random/internal/distribution_caller.h +++ b/absl/random/internal/distribution_caller.h @@ -19,8 +19,10 @@ #include <utility> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // DistributionCaller provides an opportunity to overload the general @@ -52,7 +54,7 @@ struct DistributionCaller { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_ diff --git a/absl/random/internal/distribution_impl.h b/absl/random/internal/distribution_impl.h deleted file mode 100644 index a8e5d61e..00000000 --- a/absl/random/internal/distribution_impl.h +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ -#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ - -// This file contains some implementation details which are used by one or more -// of the absl random number distributions. - -#include <cfloat> -#include <cstddef> -#include <cstdint> -#include <cstring> -#include <limits> -#include <type_traits> - -#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64) -#include <intrin.h> // NOLINT(build/include_order) -#pragma intrinsic(_umul128) -#define ABSL_INTERNAL_USE_UMUL128 1 -#endif - -#include "absl/base/config.h" -#include "absl/base/internal/bits.h" -#include "absl/numeric/int128.h" -#include "absl/random/internal/fastmath.h" -#include "absl/random/internal/traits.h" - -namespace absl { -inline namespace lts_2019_08_08 { -namespace random_internal { - -// Creates a double from `bits`, with the template fields controlling the -// output. -// -// RandU64To is both more efficient and generates more unique values in the -// result interval than known implementations of std::generate_canonical(). -// -// The `Signed` parameter controls whether positive, negative, or both are -// returned (thus affecting the output interval). -// When Signed == SignedValueT, range is U(-1, 1) -// When Signed == NegativeValueT, range is U(-1, 0) -// When Signed == PositiveValueT, range is U(0, 1) -// -// When the `IncludeZero` parameter is true, the function may return 0 for some -// inputs, otherwise it never returns 0. -// -// The `ExponentBias` parameter determines the scale of the output range by -// adjusting the exponent. -// -// When a value in U(0,1) is required, use: -// RandU64ToDouble<PositiveValueT, true, 0>(); -// -// When a value in U(-1,1) is required, use: -// RandU64ToDouble<SignedValueT, false, 0>() => U(-1, 1) -// This generates more distinct values than the mathematically equivalent -// expression `U(0, 1) * 2.0 - 1.0`, and is preferable. -// -// Scaling the result by powers of 2 (and avoiding a multiply) is also possible: -// RandU64ToDouble<PositiveValueT, false, 1>(); => U(0, 2) -// RandU64ToDouble<PositiveValueT, false, -1>(); => U(0, 0.5) -// - -// Tristate types controlling the output. -struct PositiveValueT {}; -struct NegativeValueT {}; -struct SignedValueT {}; - -// RandU64ToDouble is the double-result variant of RandU64To, described above. -template <typename Signed, bool IncludeZero, int ExponentBias = 0> -inline double RandU64ToDouble(uint64_t bits) { - static_assert(std::is_same<Signed, PositiveValueT>::value || - std::is_same<Signed, NegativeValueT>::value || - std::is_same<Signed, SignedValueT>::value, - ""); - - // Maybe use the left-most bit for a sign bit. - uint64_t sign = std::is_same<Signed, NegativeValueT>::value - ? 0x8000000000000000ull - : 0; // Sign bits. - - if (std::is_same<Signed, SignedValueT>::value) { - sign = bits & 0x8000000000000000ull; - bits = bits & 0x7FFFFFFFFFFFFFFFull; - } - if (IncludeZero) { - if (bits == 0u) return 0; - } - - // Number of leading zeros is mapped to the exponent: 2^-clz - int clz = base_internal::CountLeadingZeros64(bits); - // Shift number left to erase leading zeros. - bits <<= IncludeZero ? clz : (clz & 63); - - // Shift number right to remove bits that overflow double mantissa. The - // direction of the shift depends on `clz`. - bits >>= (64 - DBL_MANT_DIG); - - // Compute IEEE 754 double exponent. - // In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the - // exponent to account for that. - const uint64_t exp = - (std::is_same<Signed, SignedValueT>::value ? 1023U : 1022U) + - static_cast<uint64_t>(ExponentBias - clz); - constexpr int kExp = DBL_MANT_DIG - 1; - // Construct IEEE 754 double from exponent and mantissa. - const uint64_t val = sign | (exp << kExp) | (bits & ((1ULL << kExp) - 1U)); - - double res; - static_assert(sizeof(res) == sizeof(val), "double is not 64 bit"); - // Memcpy value from "val" to "res" to avoid aliasing problems. Assumes that - // endian-ness is same for double and uint64_t. - std::memcpy(&res, &val, sizeof(res)); - - return res; -} - -// RandU64ToFloat is the float-result variant of RandU64To, described above. -template <typename Signed, bool IncludeZero, int ExponentBias = 0> -inline float RandU64ToFloat(uint64_t bits) { - static_assert(std::is_same<Signed, PositiveValueT>::value || - std::is_same<Signed, NegativeValueT>::value || - std::is_same<Signed, SignedValueT>::value, - ""); - - // Maybe use the left-most bit for a sign bit. - uint64_t sign = std::is_same<Signed, NegativeValueT>::value - ? 0x80000000ul - : 0; // Sign bits. - - if (std::is_same<Signed, SignedValueT>::value) { - uint64_t a = bits & 0x8000000000000000ull; - sign = static_cast<uint32_t>(a >> 32); - bits = bits & 0x7FFFFFFFFFFFFFFFull; - } - if (IncludeZero) { - if (bits == 0u) return 0; - } - - // Number of leading zeros is mapped to the exponent: 2^-clz - int clz = base_internal::CountLeadingZeros64(bits); - // Shift number left to erase leading zeros. - bits <<= IncludeZero ? clz : (clz & 63); - // Shift number right to remove bits that overflow double mantissa. The - // direction of the shift depends on `clz`. - bits >>= (64 - FLT_MANT_DIG); - - // Construct IEEE 754 float exponent. - // In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the - // exponent to account for that. - const uint32_t exp = - (std::is_same<Signed, SignedValueT>::value ? 127U : 126U) + - static_cast<uint32_t>(ExponentBias - clz); - constexpr int kExp = FLT_MANT_DIG - 1; - const uint32_t val = sign | (exp << kExp) | (bits & ((1U << kExp) - 1U)); - - float res; - static_assert(sizeof(res) == sizeof(val), "float is not 32 bit"); - // Assumes that endian-ness is same for float and uint32_t. - std::memcpy(&res, &val, sizeof(res)); - - return res; -} - -template <typename Result> -struct RandU64ToReal { - template <typename Signed, bool IncludeZero, int ExponentBias = 0> - static inline Result Value(uint64_t bits) { - return RandU64ToDouble<Signed, IncludeZero, ExponentBias>(bits); - } -}; - -template <> -struct RandU64ToReal<float> { - template <typename Signed, bool IncludeZero, int ExponentBias = 0> - static inline float Value(uint64_t bits) { - return RandU64ToFloat<Signed, IncludeZero, ExponentBias>(bits); - } -}; - -inline uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return uint128(static_cast<__uint128_t>(a) * b); -#elif defined(ABSL_INTERNAL_USE_UMUL128) - // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC. - uint64_t high = 0; - const uint64_t low = _umul128(a, b, &high); - return absl::MakeUint128(high, low); -#else - // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit - // multiply. However there are many cases where that is not necessary, and it - // is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is - // for those cases. - const uint64_t a00 = static_cast<uint32_t>(a); - const uint64_t a32 = a >> 32; - const uint64_t b00 = static_cast<uint32_t>(b); - const uint64_t b32 = b >> 32; - - const uint64_t c00 = a00 * b00; - const uint64_t c32a = a00 * b32; - const uint64_t c32b = a32 * b00; - const uint64_t c64 = a32 * b32; - - const uint32_t carry = - static_cast<uint32_t>(((c00 >> 32) + static_cast<uint32_t>(c32a) + - static_cast<uint32_t>(c32b)) >> - 32); - - return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry, - c00 + (c32a << 32) + (c32b << 32)); -#endif -} - -// wide_multiply<T> multiplies two N-bit values to a 2N-bit result. -template <typename UIntType> -struct wide_multiply { - static constexpr size_t kN = std::numeric_limits<UIntType>::digits; - using input_type = UIntType; - using result_type = typename random_internal::unsigned_bits<kN * 2>::type; - - static result_type multiply(input_type a, input_type b) { - return static_cast<result_type>(a) * b; - } - - static input_type hi(result_type r) { return r >> kN; } - static input_type lo(result_type r) { return r; } - - static_assert(std::is_unsigned<UIntType>::value, - "Class-template wide_multiply<> argument must be unsigned."); -}; - -#ifndef ABSL_HAVE_INTRINSIC_INT128 -template <> -struct wide_multiply<uint64_t> { - using input_type = uint64_t; - using result_type = uint128; - - static result_type multiply(uint64_t a, uint64_t b) { - return MultiplyU64ToU128(a, b); - } - - static uint64_t hi(result_type r) { return Uint128High64(r); } - static uint64_t lo(result_type r) { return Uint128Low64(r); } -}; -#endif - -} // namespace random_internal -} // inline namespace lts_2019_08_08 -} // namespace absl - -#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ diff --git a/absl/random/internal/distribution_test_util.cc b/absl/random/internal/distribution_test_util.cc index 4fb4149d..e9005658 100644 --- a/absl/random/internal/distribution_test_util.cc +++ b/absl/random/internal/distribution_test_util.cc @@ -25,7 +25,7 @@ #include "absl/strings/str_format.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { namespace { @@ -414,5 +414,5 @@ double MaxErrorTolerance(double acceptance_probability) { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/distribution_test_util.h b/absl/random/internal/distribution_test_util.h index 56dc86ac..6d94cf6c 100644 --- a/absl/random/internal/distribution_test_util.h +++ b/absl/random/internal/distribution_test_util.h @@ -26,7 +26,7 @@ // non-test code. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // http://webspace.ship.edu/pgmarr/Geo441/Lectures/Lec%205%20-%20Normality%20Testing.pdf @@ -107,7 +107,7 @@ double BetaIncomplete(double x, double p, double q); double BetaIncompleteInv(double p, double q, double alpha); } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_ diff --git a/absl/random/internal/distributions.h b/absl/random/internal/distributions.h index 98d4f313..d7e3c016 100644 --- a/absl/random/internal/distributions.h +++ b/absl/random/internal/distributions.h @@ -23,40 +23,8 @@ #include "absl/random/internal/uniform_helper.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { -template <typename D> -struct DistributionFormatTraits; - -// UniformImpl implements the core logic of the Uniform<T> call, which is to -// select the correct distribution type, compute the bounds based on the -// interval tag, and then generate a value. -template <typename NumType, typename TagType, typename URBG> -NumType UniformImpl(TagType tag, - URBG& urbg, // NOLINT(runtime/references) - NumType lo, NumType hi) { - static_assert( - std::is_arithmetic<NumType>::value, - "absl::Uniform<T>() must use an integer or real parameter type."); - - using distribution_t = - typename std::conditional<std::is_integral<NumType>::value, - absl::uniform_int_distribution<NumType>, - absl::uniform_real_distribution<NumType>>::type; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; - - auto a = random_internal::uniform_lower_bound<NumType>(tag, lo, hi); - auto b = random_internal::uniform_upper_bound<NumType>(tag, lo, hi); - // TODO(lar): it doesn't make a lot of sense to ask for a random number in an - // empty range. Right now we just return a boundary--even though that - // boundary is not an acceptable value! Is there something better we can do - // here? - - using gen_t = absl::decay_t<URBG>; - if (a > b) return a; - return DistributionCaller<gen_t>::template Call<distribution_t, format_t>( - &urbg, a, b); -} // In the absence of an explicitly provided return-type, the template // "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on @@ -78,7 +46,7 @@ using uniform_inferred_return_t = is_widening_convertible<A, B>::value, B, A>::type>; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_ diff --git a/absl/random/internal/explicit_seed_seq.h b/absl/random/internal/explicit_seed_seq.h index 66c35f2e..6a743eaf 100644 --- a/absl/random/internal/explicit_seed_seq.h +++ b/absl/random/internal/explicit_seed_seq.h @@ -22,8 +22,10 @@ #include <iterator> #include <vector> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // This class conforms to the C++ Standard "Seed Sequence" concept @@ -83,7 +85,7 @@ class ExplicitSeedSeq { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_ diff --git a/absl/random/internal/fast_uniform_bits.h b/absl/random/internal/fast_uniform_bits.h index 5aa9de01..f13c8729 100644 --- a/absl/random/internal/fast_uniform_bits.h +++ b/absl/random/internal/fast_uniform_bits.h @@ -20,8 +20,10 @@ #include <limits> #include <type_traits> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // Returns true if the input value is zero or a power of two. Useful for // determining if the range of output values in a URBG @@ -256,7 +258,7 @@ FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references) } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_ diff --git a/absl/random/internal/fast_uniform_bits_test.cc b/absl/random/internal/fast_uniform_bits_test.cc index 35c96226..f5b837e5 100644 --- a/absl/random/internal/fast_uniform_bits_test.cc +++ b/absl/random/internal/fast_uniform_bits_test.cc @@ -19,7 +19,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { namespace { @@ -270,5 +270,5 @@ TEST(FastUniformBitsTest, URBG32bitRegression) { } // namespace } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/fastmath.h b/absl/random/internal/fastmath.h index 1dc2cd7b..6baeb5a7 100644 --- a/absl/random/internal/fastmath.h +++ b/absl/random/internal/fastmath.h @@ -25,7 +25,7 @@ #include "absl/base/internal/bits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // Returns the position of the first bit set. @@ -68,7 +68,7 @@ inline constexpr uint64_t rotr(uint64_t value, uint8_t bits) { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_FASTMATH_H_ diff --git a/absl/random/internal/gaussian_distribution_gentables.cc b/absl/random/internal/gaussian_distribution_gentables.cc index 6f9a28be..a2bf0394 100644 --- a/absl/random/internal/gaussian_distribution_gentables.cc +++ b/absl/random/internal/gaussian_distribution_gentables.cc @@ -27,7 +27,7 @@ #include "absl/base/macros.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { namespace { @@ -136,7 +136,7 @@ void TableGenerator::Print(std::ostream* os) { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl int main(int, char**) { diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h new file mode 100644 index 00000000..20f6d208 --- /dev/null +++ b/absl/random/internal/generate_real.h @@ -0,0 +1,146 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_ +#define ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_ + +// This file contains some implementation details which are used by one or more +// of the absl random number distributions. + +#include <cstdint> +#include <cstring> +#include <limits> +#include <type_traits> + +#include "absl/base/internal/bits.h" +#include "absl/meta/type_traits.h" +#include "absl/random/internal/fastmath.h" +#include "absl/random/internal/traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace random_internal { + +// Tristate tag types controlling the output of GenerateRealFromBits. +struct GeneratePositiveTag {}; +struct GenerateNegativeTag {}; +struct GenerateSignedTag {}; + +// GenerateRealFromBits generates a single real value from a single 64-bit +// `bits` with template fields controlling the output. +// +// The `SignedTag` parameter controls whether positive, negative, +// or either signed/unsigned may be returned. +// When SignedTag == GeneratePositiveTag, range is U(0, 1) +// When SignedTag == GenerateNegativeTag, range is U(-1, 0) +// When SignedTag == GenerateSignedTag, range is U(-1, 1) +// +// When the `IncludeZero` parameter is true, the function may return 0 for some +// inputs, otherwise it never returns 0. +// +// When a value in U(0,1) is required, use: +// Uniform64ToReal<double, PositiveValueT, true>; +// +// When a value in U(-1,1) is required, use: +// Uniform64ToReal<double, SignedValueT, false>; +// +// This generates more distinct values than the mathematical equivalent +// `U(0, 1) * 2.0 - 1.0`. +// +// Scaling the result by powers of 2 (and avoiding a multiply) is also possible: +// GenerateRealFromBits<double>(..., -1); => U(0, 0.5) +// GenerateRealFromBits<double>(..., 1); => U(0, 2) +// +template <typename RealType, // Real type, either float or double. + typename SignedTag = GeneratePositiveTag, // Whether a positive, + // negative, or signed + // value is generated. + bool IncludeZero = true> +inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) { + using real_type = RealType; + using uint_type = absl::conditional_t<std::is_same<real_type, float>::value, + uint32_t, uint64_t>; + + static_assert( + (std::is_same<double, real_type>::value || + std::is_same<float, real_type>::value), + "GenerateRealFromBits must be parameterized by either float or double."); + + static_assert(sizeof(uint_type) == sizeof(real_type), + "Mismatched unsinged and real types."); + + static_assert((std::numeric_limits<real_type>::is_iec559 && + std::numeric_limits<real_type>::radix == 2), + "RealType representation is not IEEE 754 binary."); + + static_assert((std::is_same<SignedTag, GeneratePositiveTag>::value || + std::is_same<SignedTag, GenerateNegativeTag>::value || + std::is_same<SignedTag, GenerateSignedTag>::value), + ""); + + static constexpr int kExp = std::numeric_limits<real_type>::digits - 1; + static constexpr uint_type kMask = (static_cast<uint_type>(1) << kExp) - 1u; + static constexpr int kUintBits = sizeof(uint_type) * 8; + + int exp = exp_bias + int{std::numeric_limits<real_type>::max_exponent - 2}; + + // Determine the sign bit. + // Depending on the SignedTag, this may use the left-most bit + // or it may be a constant value. + uint_type sign = std::is_same<SignedTag, GenerateNegativeTag>::value + ? (static_cast<uint_type>(1) << (kUintBits - 1)) + : 0; + if (std::is_same<SignedTag, GenerateSignedTag>::value) { + if (std::is_same<uint_type, uint64_t>::value) { + sign = bits & uint64_t{0x8000000000000000}; + } + if (std::is_same<uint_type, uint32_t>::value) { + const uint64_t tmp = bits & uint64_t{0x8000000000000000}; + sign = static_cast<uint32_t>(tmp >> 32); + } + // adjust the bits and the exponent to account for removing + // the leading bit. + bits = bits & uint64_t{0x7FFFFFFFFFFFFFFF}; + exp++; + } + if (IncludeZero) { + if (bits == 0u) return 0; + } + + // Number of leading zeros is mapped to the exponent: 2^-clz + // bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0 + int clz = base_internal::CountLeadingZeros64(bits); + bits <<= (IncludeZero ? clz : (clz & 63)); // remove 0-bits. + exp -= clz; // set the exponent. + bits >>= (63 - kExp); + + // Construct the 32-bit or 64-bit IEEE 754 floating-point value from + // the individual fields: sign, exp, mantissa(bits). + uint_type val = + (std::is_same<SignedTag, GeneratePositiveTag>::value ? 0u : sign) | + (static_cast<uint_type>(exp) << kExp) | + (static_cast<uint_type>(bits) & kMask); + + // bit_cast to the output-type + real_type result; + memcpy(static_cast<void*>(&result), static_cast<const void*>(&val), + sizeof(result)); + return result; +} + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_ diff --git a/absl/random/internal/distribution_impl_test.cc b/absl/random/internal/generate_real_test.cc index 09e7a318..aa02f0c2 100644 --- a/absl/random/internal/distribution_impl_test.cc +++ b/absl/random/internal/generate_real_test.cc @@ -12,57 +12,74 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/generate_real.h" + +#include <cfloat> +#include <cstddef> +#include <cstdint> +#include <string> #include "gtest/gtest.h" #include "absl/base/internal/bits.h" #include "absl/flags/flag.h" -#include "absl/numeric/int128.h" ABSL_FLAG(int64_t, absl_random_test_trials, 50000, "Number of trials for the probability tests."); -using absl::random_internal::NegativeValueT; -using absl::random_internal::PositiveValueT; -using absl::random_internal::RandU64ToDouble; -using absl::random_internal::RandU64ToFloat; -using absl::random_internal::SignedValueT; +using absl::random_internal::GenerateNegativeTag; +using absl::random_internal::GeneratePositiveTag; +using absl::random_internal::GenerateRealFromBits; +using absl::random_internal::GenerateSignedTag; namespace { -TEST(DistributionImplTest, U64ToFloat_Positive_NoZero_Test) { +TEST(GenerateRealTest, U64ToFloat_Positive_NoZero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat<PositiveValueT, false>(a); + return GenerateRealFromBits<float, GeneratePositiveTag, false>(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 2.710505431e-20f); EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f); EXPECT_EQ(ToFloat(0x8000000000000000), 0.5); + EXPECT_EQ(ToFloat(0x8000000000000001), 0.5); EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Positive_Zero_Test) { +TEST(GenerateRealTest, U64ToFloat_Positive_Zero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat<PositiveValueT, true>(a); + return GenerateRealFromBits<float, GeneratePositiveTag, true>(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 0.0); EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f); EXPECT_EQ(ToFloat(0x8000000000000000), 0.5); + EXPECT_EQ(ToFloat(0x8000000000000001), 0.5); EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Negative_NoZero_Test) { +TEST(GenerateRealTest, U64ToFloat_Negative_NoZero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat<NegativeValueT, false>(a); + return GenerateRealFromBits<float, GenerateNegativeTag, false>(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), -2.710505431e-20f); EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f); EXPECT_EQ(ToFloat(0x8000000000000000), -0.5); + EXPECT_EQ(ToFloat(0x8000000000000001), -0.5); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); +} + +TEST(GenerateRealTest, U64ToFloat_Negative_Zero_Test) { + auto ToFloat = [](uint64_t a) { + return GenerateRealFromBits<float, GenerateNegativeTag, true>(a); + }; + EXPECT_EQ(ToFloat(0x0000000000000000), 0.0); + EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f); + EXPECT_EQ(ToFloat(0x8000000000000000), -0.5); + EXPECT_EQ(ToFloat(0x8000000000000001), -0.5); EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Signed_NoZero_Test) { +TEST(GenerateRealTest, U64ToFloat_Signed_NoZero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat<SignedValueT, false>(a); + return GenerateRealFromBits<float, GenerateSignedTag, false>(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 5.421010862e-20f); EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f); @@ -72,9 +89,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_NoZero_Test) { EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Signed_Zero_Test) { +TEST(GenerateRealTest, U64ToFloat_Signed_Zero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat<SignedValueT, true>(a); + return GenerateRealFromBits<float, GenerateSignedTag, true>(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 0); EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f); @@ -84,9 +101,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_Zero_Test) { EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Signed_Bias_Test) { +TEST(GenerateRealTest, U64ToFloat_Signed_Bias_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat<SignedValueT, true, 1>(a); + return GenerateRealFromBits<float, GenerateSignedTag, true>(a, 1); }; EXPECT_EQ(ToFloat(0x0000000000000000), 0); EXPECT_EQ(ToFloat(0x0000000000000001), 2 * 1.084202172e-19f); @@ -96,9 +113,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_Bias_Test) { EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 2 * -0.9999999404f); } -TEST(DistributionImplTest, U64ToFloatTest) { +TEST(GenerateRealTest, U64ToFloatTest) { auto ToFloat = [](uint64_t a) -> float { - return RandU64ToFloat<PositiveValueT, true>(a); + return GenerateRealFromBits<float, GeneratePositiveTag, true>(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 0.0f); @@ -150,44 +167,60 @@ TEST(DistributionImplTest, U64ToFloatTest) { } } -TEST(DistributionImplTest, U64ToDouble_Positive_NoZero_Test) { +TEST(GenerateRealTest, U64ToDouble_Positive_NoZero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble<PositiveValueT, false>(a); + return GenerateRealFromBits<double, GeneratePositiveTag, false>(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 2.710505431213761085e-20); EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20); EXPECT_EQ(ToDouble(0x0000000000000002), 1.084202172485504434e-19); EXPECT_EQ(ToDouble(0x8000000000000000), 0.5); + EXPECT_EQ(ToDouble(0x8000000000000001), 0.5); EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Positive_Zero_Test) { +TEST(GenerateRealTest, U64ToDouble_Positive_Zero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble<PositiveValueT, true>(a); + return GenerateRealFromBits<double, GeneratePositiveTag, true>(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20); EXPECT_EQ(ToDouble(0x8000000000000000), 0.5); + EXPECT_EQ(ToDouble(0x8000000000000001), 0.5); EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Negative_NoZero_Test) { +TEST(GenerateRealTest, U64ToDouble_Negative_NoZero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble<NegativeValueT, false>(a); + return GenerateRealFromBits<double, GenerateNegativeTag, false>(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), -2.710505431213761085e-20); EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20); EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19); EXPECT_EQ(ToDouble(0x8000000000000000), -0.5); + EXPECT_EQ(ToDouble(0x8000000000000001), -0.5); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); +} + +TEST(GenerateRealTest, U64ToDouble_Negative_Zero_Test) { + auto ToDouble = [](uint64_t a) { + return GenerateRealFromBits<double, GenerateNegativeTag, true>(a); + }; + + EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); + EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19); + EXPECT_EQ(ToDouble(0x8000000000000000), -0.5); + EXPECT_EQ(ToDouble(0x8000000000000001), -0.5); EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Signed_NoZero_Test) { +TEST(GenerateRealTest, U64ToDouble_Signed_NoZero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble<SignedValueT, false>(a); + return GenerateRealFromBits<double, GenerateSignedTag, false>(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20); @@ -198,9 +231,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_NoZero_Test) { EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Signed_Zero_Test) { +TEST(GenerateRealTest, U64ToDouble_Signed_Zero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble<SignedValueT, true>(a); + return GenerateRealFromBits<double, GenerateSignedTag, true>(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 0); EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19); @@ -210,9 +243,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_Zero_Test) { EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Signed_Bias_Test) { +TEST(GenerateRealTest, U64ToDouble_GenerateSignedTag_Bias_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble<SignedValueT, true, -1>(a); + return GenerateRealFromBits<double, GenerateSignedTag, true>(a, -1); }; EXPECT_EQ(ToDouble(0x0000000000000000), 0); EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19 / 2); @@ -222,9 +255,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_Bias_Test) { EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978 / 2); } -TEST(DistributionImplTest, U64ToDoubleTest) { +TEST(GenerateRealTest, U64ToDoubleTest) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble<PositiveValueT, true>(a); + return GenerateRealFromBits<double, GeneratePositiveTag, true>(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); @@ -296,9 +329,9 @@ TEST(DistributionImplTest, U64ToDoubleTest) { } } -TEST(DistributionImplTest, U64ToDoubleSignedTest) { +TEST(GenerateRealTest, U64ToDoubleSignedTest) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble<SignedValueT, false>(a); + return GenerateRealFromBits<double, GenerateSignedTag, false>(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20); @@ -379,10 +412,10 @@ TEST(DistributionImplTest, U64ToDoubleSignedTest) { } } -TEST(DistributionImplTest, ExhaustiveFloat) { +TEST(GenerateRealTest, ExhaustiveFloat) { using absl::base_internal::CountLeadingZeros64; auto ToFloat = [](uint64_t a) { - return RandU64ToFloat<PositiveValueT, true>(a); + return GenerateRealFromBits<float, GeneratePositiveTag, true>(a); }; // Rely on RandU64ToFloat generating values from greatest to least when @@ -461,46 +494,4 @@ TEST(DistributionImplTest, ExhaustiveFloat) { } } -TEST(DistributionImplTest, MultiplyU64ToU128Test) { - using absl::random_internal::MultiplyU64ToU128; - constexpr uint64_t k1 = 1; - constexpr uint64_t kMax = ~static_cast<uint64_t>(0); - - EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0)); - - // Max uint64 - EXPECT_EQ(MultiplyU64ToU128(kMax, kMax), - absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001)); - EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1)); - EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(1, kMax)); - for (int i = 0; i < 64; ++i) { - EXPECT_EQ(absl::MakeUint128(0, kMax) << i, - MultiplyU64ToU128(kMax, k1 << i)); - EXPECT_EQ(absl::MakeUint128(0, kMax) << i, - MultiplyU64ToU128(k1 << i, kMax)); - } - - // 1-bit x 1-bit. - for (int i = 0; i < 64; ++i) { - for (int j = 0; j < 64; ++j) { - EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j), - MultiplyU64ToU128(k1 << i, k1 << j)); - EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j), - MultiplyU64ToU128(k1 << i, k1 << j)); - } - } - - // Verified multiplies - EXPECT_EQ(MultiplyU64ToU128(0xffffeeeeddddcccc, 0xbbbbaaaa99998888), - absl::MakeUint128(0xbbbb9e2692c5dddc, 0xc28f7531048d2c60)); - EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfedcba9876543210), - absl::MakeUint128(0x0121fa00ad77d742, 0x2236d88fe5618cf0)); - EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfdb97531eca86420), - absl::MakeUint128(0x0120ae99d26725fc, 0xce197f0ecac319e0)); - EXPECT_EQ(MultiplyU64ToU128(0x97a87f4f261ba3f2, 0xfedcba9876543210), - absl::MakeUint128(0x96fbf1a8ae78d0ba, 0x5a6dd4b71f278320)); - EXPECT_EQ(MultiplyU64ToU128(0xfedcba9876543210, 0xfdb97531eca86420), - absl::MakeUint128(0xfc98c6981a413e22, 0x342d0bbf48948200)); -} - } // namespace diff --git a/absl/random/internal/iostream_state_saver.h b/absl/random/internal/iostream_state_saver.h index b9adca86..7378829a 100644 --- a/absl/random/internal/iostream_state_saver.h +++ b/absl/random/internal/iostream_state_saver.h @@ -24,7 +24,7 @@ #include "absl/numeric/int128.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // The null_state_saver does nothing. @@ -239,7 +239,7 @@ inline FloatType read_floating_point(IStream& is) { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_ diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc index 2ecbaac1..7bb8ad95 100644 --- a/absl/random/internal/iostream_state_saver_test.cc +++ b/absl/random/internal/iostream_state_saver_test.cc @@ -196,8 +196,8 @@ TEST(IOStreamStateSaver, RoundTripFloats) { EXPECT_EQ(-d, StreamRoundTrip<double>(-d)); // Avoid undefined behavior (overflow/underflow). - if (d <= std::numeric_limits<int64_t>::max() && - d >= std::numeric_limits<int64_t>::lowest()) { + if (f <= static_cast<float>(std::numeric_limits<int64_t>::max()) && + f >= static_cast<float>(std::numeric_limits<int64_t>::lowest())) { int64_t x = static_cast<int64_t>(f); EXPECT_EQ(x, StreamRoundTrip<int64_t>(x)); } @@ -264,14 +264,15 @@ TEST(IOStreamStateSaver, RoundTripDoubles) { } // Avoid undefined behavior (overflow/underflow). - if (d <= std::numeric_limits<int64_t>::max() && - d >= std::numeric_limits<int64_t>::lowest()) { + if (d <= static_cast<double>(std::numeric_limits<int64_t>::max()) && + d >= static_cast<double>(std::numeric_limits<int64_t>::lowest())) { int64_t x = static_cast<int64_t>(d); EXPECT_EQ(x, StreamRoundTrip<int64_t>(x)); } } } +#if !defined(__EMSCRIPTEN__) TEST(IOStreamStateSaver, RoundTripLongDoubles) { // Technically, C++ only guarantees that long double is at least as large as a // double. Practically it varies from 64-bits to 128-bits. @@ -349,6 +350,7 @@ TEST(IOStreamStateSaver, RoundTripLongDoubles) { } } } +#endif // !defined(__EMSCRIPTEN__) TEST(StrToDTest, DoubleMin) { const char kV[] = "2.22507385850720138e-308"; diff --git a/absl/random/internal/mock_overload_set.h b/absl/random/internal/mock_overload_set.h new file mode 100644 index 00000000..c2a30d89 --- /dev/null +++ b/absl/random/internal/mock_overload_set.h @@ -0,0 +1,91 @@ +// +// 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_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_ +#define ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_ + +#include <type_traits> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/mocking_bit_gen.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace random_internal { + +template <typename DistrT, typename Fn> +struct MockSingleOverload; + +// MockSingleOverload +// +// MockSingleOverload hooks in to gMock's `ON_CALL` and `EXPECT_CALL` macros. +// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to +// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on +// the MockingBitGen (an argument passed inside `Call(...)`), this forwards to +// arguments to Mocking::Register. +template <typename DistrT, typename Ret, typename... Args> +struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> { + static_assert(std::is_same<typename DistrT::result_type, Ret>::value, + "Overload signature must have return type matching the " + "distributions result type."); + auto gmock_Call( + absl::MockingBitGen& gen, // NOLINT(google-runtime-references) + const ::testing::Matcher<Args>&... args) + -> decltype(gen.Register<DistrT, Args...>(args...)) { + return gen.Register<DistrT, Args...>(args...); + } +}; + +template <typename DistrT, typename Ret, typename Arg, typename... Args> +struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> { + static_assert(std::is_same<typename DistrT::result_type, Ret>::value, + "Overload signature must have return type matching the " + "distributions result type."); + auto gmock_Call( + const ::testing::Matcher<Arg>& arg, + absl::MockingBitGen& gen, // NOLINT(google-runtime-references) + const ::testing::Matcher<Args>&... args) + -> decltype(gen.Register<DistrT, Arg, Args...>(arg, args...)) { + return gen.Register<DistrT, Arg, Args...>(arg, args...); + } +}; + +// MockOverloadSet +// +// MockOverloadSet takes a distribution and a collection of signatures and +// performs overload resolution amongst all the overloads. This makes +// `EXPECT_CALL(mock_overload_set, Call(...))` expand and do overload resolution +// correctly. +template <typename DistrT, typename... Signatures> +struct MockOverloadSet; + +template <typename DistrT, typename Sig> +struct MockOverloadSet<DistrT, Sig> : public MockSingleOverload<DistrT, Sig> { + using MockSingleOverload<DistrT, Sig>::gmock_Call; +}; + +template <typename DistrT, typename FirstSig, typename... Rest> +struct MockOverloadSet<DistrT, FirstSig, Rest...> + : public MockSingleOverload<DistrT, FirstSig>, + public MockOverloadSet<DistrT, Rest...> { + using MockSingleOverload<DistrT, FirstSig>::gmock_Call; + using MockOverloadSet<DistrT, Rest...>::gmock_Call; +}; + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl +#endif // ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_ diff --git a/absl/random/internal/mocking_bit_gen_base.h b/absl/random/internal/mocking_bit_gen_base.h new file mode 100644 index 00000000..eeeae9d2 --- /dev/null +++ b/absl/random/internal/mocking_bit_gen_base.h @@ -0,0 +1,120 @@ +// +// 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_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ +#define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ + +#include <atomic> +#include <deque> +#include <string> +#include <typeinfo> + +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace random_internal { + +// MockingBitGenExpectationFormatter is invoked to format unsatisfied mocks +// and remaining results into a description string. +template <typename DistrT, typename FormatT> +struct MockingBitGenExpectationFormatter { + std::string operator()(absl::string_view args) { + return absl::StrCat(FormatT::FunctionName(), "(", args, ")"); + } +}; + +// MockingBitGenCallFormatter is invoked to format each distribution call +// into a description string for the mock log. +template <typename DistrT, typename FormatT> +struct MockingBitGenCallFormatter { + std::string operator()(const DistrT& dist, + const typename DistrT::result_type& result) { + return absl::StrCat( + FormatT::FunctionName(), "(", FormatT::FormatArgs(dist), ") => {", + FormatT::FormatResults(absl::MakeSpan(&result, 1)), "}"); + } +}; + +class MockingBitGenBase { + template <typename> + friend struct DistributionCaller; + using generator_type = absl::BitGen; + + public: + // URBG interface + using result_type = generator_type::result_type; + static constexpr result_type(min)() { return (generator_type::min)(); } + static constexpr result_type(max)() { return (generator_type::max)(); } + result_type operator()() { return gen_(); } + + MockingBitGenBase() : gen_(), observed_call_log_() {} + virtual ~MockingBitGenBase() = default; + + protected: + const std::deque<std::string>& observed_call_log() { + return observed_call_log_; + } + + // CallImpl is the type-erased virtual dispatch. + // The type of dist is always distribution<T>, + // The type of result is always distribution<T>::result_type. + virtual bool CallImpl(const std::type_info& distr_type, void* dist_args, + void* result) = 0; + + template <typename DistrT, typename ArgTupleT> + static const std::type_info& GetTypeId() { + return typeid(std::pair<absl::decay_t<DistrT>, absl::decay_t<ArgTupleT>>); + } + + // Call the generating distribution function. + // Invoked by DistributionCaller<>::Call<DistT, FormatT>. + // DistT is the distribution type. + // FormatT is the distribution formatter traits type. + template <typename DistrT, typename FormatT, typename... Args> + typename DistrT::result_type Call(Args&&... args) { + using distr_result_type = typename DistrT::result_type; + using ArgTupleT = std::tuple<absl::decay_t<Args>...>; + + ArgTupleT arg_tuple(std::forward<Args>(args)...); + auto dist = absl::make_from_tuple<DistrT>(arg_tuple); + + distr_result_type result{}; + bool found_match = + CallImpl(GetTypeId<DistrT, ArgTupleT>(), &arg_tuple, &result); + + if (!found_match) { + result = dist(gen_); + } + + // TODO(asoffer): Forwarding the args through means we no longer need to + // extract them from the from the distribution in formatter traits. We can + // just StrJoin them. + observed_call_log_.push_back( + MockingBitGenCallFormatter<DistrT, FormatT>{}(dist, result)); + return result; + } + + private: + generator_type gen_; + std::deque<std::string> observed_call_log_; +}; // namespace random_internal + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ diff --git a/absl/random/internal/nanobenchmark.cc b/absl/random/internal/nanobenchmark.cc index 4d26469b..8fee77fc 100644 --- a/absl/random/internal/nanobenchmark.cc +++ b/absl/random/internal/nanobenchmark.cc @@ -27,6 +27,7 @@ #include <utility> #include <vector> +#include "absl/base/attributes.h" #include "absl/base/internal/raw_logging.h" #include "absl/random/internal/platform.h" #include "absl/random/internal/randen_engine.h" @@ -59,15 +60,6 @@ #include <time.h> // NOLINT #endif -// ABSL_HAVE_ATTRIBUTE -#if !defined(ABSL_HAVE_ATTRIBUTE) -#ifdef __has_attribute -#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) -#else -#define ABSL_HAVE_ATTRIBUTE(x) 0 -#endif -#endif - // ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE prevents inlining of the method. #if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__)) #define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __attribute__((noinline)) @@ -78,7 +70,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal_nanobenchmark { namespace { @@ -808,5 +800,5 @@ size_t Measure(const Func func, const void* arg, const FuncInput* inputs, } } // namespace random_internal_nanobenchmark -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/nanobenchmark.h b/absl/random/internal/nanobenchmark.h index 424d2ba2..a5097ba2 100644 --- a/absl/random/internal/nanobenchmark.h +++ b/absl/random/internal/nanobenchmark.h @@ -50,8 +50,10 @@ #include <stddef.h> #include <stdint.h> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal_nanobenchmark { // Input influencing the function being measured (e.g. number of bytes to copy). @@ -164,7 +166,7 @@ static inline size_t MeasureClosure(const Closure& closure, } } // namespace random_internal_nanobenchmark -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_ diff --git a/absl/random/internal/nanobenchmark_test.cc b/absl/random/internal/nanobenchmark_test.cc index f96e0f5d..ab824ef5 100644 --- a/absl/random/internal/nanobenchmark_test.cc +++ b/absl/random/internal/nanobenchmark_test.cc @@ -18,7 +18,7 @@ #include "absl/strings/numbers.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal_nanobenchmark { namespace { @@ -68,7 +68,7 @@ void RunAll(const int argc, char* argv[]) { } // namespace } // namespace random_internal_nanobenchmark -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl int main(int argc, char* argv[]) { diff --git a/absl/random/internal/nonsecure_base.h b/absl/random/internal/nonsecure_base.h index c8af51cf..730fa2ea 100644 --- a/absl/random/internal/nonsecure_base.h +++ b/absl/random/internal/nonsecure_base.h @@ -33,7 +33,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // Each instance of NonsecureURBGBase<URBG> will be seeded by variates produced @@ -144,7 +144,7 @@ class NonsecureURBGBase { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_ diff --git a/absl/random/internal/nonsecure_base_test.cc b/absl/random/internal/nonsecure_base_test.cc index d9de9901..698027fc 100644 --- a/absl/random/internal/nonsecure_base_test.cc +++ b/absl/random/internal/nonsecure_base_test.cc @@ -154,9 +154,10 @@ TEST(NonsecureURBGBase, CompatibleWithDistributionUtils) { TEST(NonsecureURBGBase, CompatibleWithStdDistributions) { ExampleNonsecureURBG rbg; - std::uniform_int_distribution<uint32_t>(0, 100)(rbg); - std::uniform_real_distribution<float>()(rbg); - std::bernoulli_distribution(0.2)(rbg); + // Cast to void to suppress [[nodiscard]] warnings + static_cast<void>(std::uniform_int_distribution<uint32_t>(0, 100)(rbg)); + static_cast<void>(std::uniform_real_distribution<float>()(rbg)); + static_cast<void>(std::bernoulli_distribution(0.2)(rbg)); } TEST(NonsecureURBGBase, ConsecutiveDefaultInstancesYieldUniqueVariates) { diff --git a/absl/random/internal/pcg_engine.h b/absl/random/internal/pcg_engine.h index 607ac34b..53c23fe1 100644 --- a/absl/random/internal/pcg_engine.h +++ b/absl/random/internal/pcg_engine.h @@ -24,7 +24,7 @@ #include "absl/random/internal/iostream_state_saver.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // pcg_engine is a simplified implementation of Melissa O'Neil's PCG engine in @@ -301,7 +301,7 @@ using pcg32_2018_engine = pcg_engine< random_internal::pcg_xsh_rr_64_32>; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_ diff --git a/absl/random/internal/platform.h b/absl/random/internal/platform.h index a5a42cbb..bbdb4e62 100644 --- a/absl/random/internal/platform.h +++ b/absl/random/internal/platform.h @@ -162,7 +162,8 @@ // iOS does not support dispatch, even on x86, since applications // should be bundled as fat binaries, with a different build tailored for // each specific supported platform/architecture. -#if defined(__APPLE__) && (TARGET_OS_IPHONE || TARGET_OS_IPHONE_SIMULATOR) +#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ + (defined(TARGET_OS_IPHONE_SIMULATOR) && TARGET_OS_IPHONE_SIMULATOR) #undef ABSL_RANDOM_INTERNAL_AES_DISPATCH #define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0 #endif diff --git a/absl/random/internal/pool_urbg.cc b/absl/random/internal/pool_urbg.cc index 304d9b16..5bee5307 100644 --- a/absl/random/internal/pool_urbg.cc +++ b/absl/random/internal/pool_urbg.cc @@ -37,7 +37,7 @@ using absl::base_internal::SpinLock; using absl::base_internal::SpinLockHolder; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { namespace { @@ -60,13 +60,13 @@ class RandenPoolEntry { } // Copy bytes into out. - void Fill(uint8_t* out, size_t bytes) LOCKS_EXCLUDED(mu_); + void Fill(uint8_t* out, size_t bytes) ABSL_LOCKS_EXCLUDED(mu_); // Returns random bits from the buffer in units of T. template <typename T> - inline T Generate() LOCKS_EXCLUDED(mu_); + inline T Generate() ABSL_LOCKS_EXCLUDED(mu_); - inline void MaybeRefill() EXCLUSIVE_LOCKS_REQUIRED(mu_) { + inline void MaybeRefill() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) { if (next_ >= kState) { next_ = kCapacity; impl_.Generate(state_); @@ -75,10 +75,10 @@ class RandenPoolEntry { private: // Randen URBG state. - uint32_t state_[kState] GUARDED_BY(mu_); // First to satisfy alignment. + uint32_t state_[kState] ABSL_GUARDED_BY(mu_); // First to satisfy alignment. SpinLock mu_; const Randen impl_; - size_t next_ GUARDED_BY(mu_); + size_t next_ ABSL_GUARDED_BY(mu_); }; template <> @@ -250,5 +250,5 @@ template class RandenPool<uint32_t>; template class RandenPool<uint64_t>; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/pool_urbg.h b/absl/random/internal/pool_urbg.h index eac75e2c..05721929 100644 --- a/absl/random/internal/pool_urbg.h +++ b/absl/random/internal/pool_urbg.h @@ -22,7 +22,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // RandenPool is a thread-safe random number generator [random.req.urbg] that @@ -125,7 +125,7 @@ class PoolURBG { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_POOL_URBG_H_ diff --git a/absl/random/internal/randen.cc b/absl/random/internal/randen.cc index 3b087605..78a1e00c 100644 --- a/absl/random/internal/randen.cc +++ b/absl/random/internal/randen.cc @@ -41,7 +41,7 @@ // structured/low-entropy counters to digits of Pi. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { namespace { @@ -87,5 +87,5 @@ Randen::Randen() { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/randen.h b/absl/random/internal/randen.h index ef375f90..c2834aaf 100644 --- a/absl/random/internal/randen.h +++ b/absl/random/internal/randen.h @@ -23,7 +23,7 @@ #include "absl/random/internal/randen_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // RANDen = RANDom generator or beetroots in Swiss German. @@ -96,7 +96,7 @@ class Randen { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_RANDEN_H_ diff --git a/absl/random/internal/randen_detect.cc b/absl/random/internal/randen_detect.cc index 610ae319..d63230c2 100644 --- a/absl/random/internal/randen_detect.cc +++ b/absl/random/internal/randen_detect.cc @@ -95,7 +95,7 @@ static uint32_t GetAuxval(uint32_t hwcap_type) { #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // The default return at the end of the function might be unreachable depending @@ -217,5 +217,5 @@ bool CPUSupportsRandenHwAes() { #endif } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/randen_detect.h b/absl/random/internal/randen_detect.h index cb777550..f283f432 100644 --- a/absl/random/internal/randen_detect.h +++ b/absl/random/internal/randen_detect.h @@ -15,8 +15,10 @@ #ifndef ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_ #define ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_ +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // Returns whether the current CPU supports RandenHwAes implementation. @@ -25,7 +27,7 @@ namespace random_internal { bool CPUSupportsRandenHwAes(); } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_ diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h index e721559e..6b337313 100644 --- a/absl/random/internal/randen_engine.h +++ b/absl/random/internal/randen_engine.h @@ -28,7 +28,7 @@ #include "absl/random/internal/randen.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // Deterministic pseudorandom byte generator with backtracking resistance @@ -224,7 +224,7 @@ class alignas(16) randen_engine { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_ diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc index d7eed8b2..e23844f1 100644 --- a/absl/random/internal/randen_hwaes.cc +++ b/absl/random/internal/randen_hwaes.cc @@ -22,39 +22,9 @@ #include <cstdint> #include <cstring> +#include "absl/base/attributes.h" #include "absl/random/internal/platform.h" -// ABSL_HAVE_ATTRIBUTE -#if !defined(ABSL_HAVE_ATTRIBUTE) -#ifdef __has_attribute -#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) -#else -#define ABSL_HAVE_ATTRIBUTE(x) 0 -#endif -#endif - -#if ABSL_HAVE_ATTRIBUTE(always_inline) || \ - (defined(__GNUC__) && !defined(__clang__)) -#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \ - __attribute__((always_inline)) -#elif defined(_MSC_VER) -// We can achieve something similar to attribute((always_inline)) with MSVC by -// using the __forceinline keyword, however this is not perfect. MSVC is -// much less aggressive about inlining, and even with the __forceinline keyword. -#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __forceinline -#else -#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE -#endif - -// ABSL_ATTRIBUTE_FLATTEN enables much more aggressive inlining within -// the indicated function. -#undef ABSL_ATTRIBUTE_FLATTEN -#if ABSL_HAVE_ATTRIBUTE(flatten) || (defined(__GNUC__) && !defined(__clang__)) -#define ABSL_ATTRIBUTE_FLATTEN __attribute__((flatten)) -#else -#define ABSL_ATTRIBUTE_FLATTEN -#endif - // ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain // a hardware accelerated implementation of randen, or whether it // will contain stubs that exit the process. @@ -105,7 +75,7 @@ #include <cstdlib> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // No accelerated implementation. @@ -137,7 +107,7 @@ void RandenHwAes::Generate(const void*, void*) { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #else // defined(ABSL_RANDEN_HWAES_IMPL) @@ -148,18 +118,6 @@ void RandenHwAes::Generate(const void*, void*) { #include "absl/random/internal/randen_traits.h" -// ABSL_FUNCTION_ALIGN32 defines a 32-byte alignment attribute -// for the functions in this file. -// -// NOTE: Determine whether we actually have any wins from ALIGN32 -// using microbenchmarks. If not, remove. -#undef ABSL_FUNCTION_ALIGN32 -#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__)) -#define ABSL_FUNCTION_ALIGN32 __attribute__((aligned(32))) -#else -#define ABSL_FUNCTION_ALIGN32 -#endif - // TARGET_CRYPTO defines a crypto attribute for each architecture. // // NOTE: Evaluate whether we should eliminate ABSL_TARGET_CRYPTO. @@ -193,8 +151,7 @@ using Vector128 = __vector unsigned long long; // NOLINT(runtime/int) namespace { -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 -ReverseBytes(const Vector128& v) { +inline ABSL_TARGET_CRYPTO Vector128 ReverseBytes(const Vector128& v) { // Reverses the bytes of the vector. const __vector unsigned char perm = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; @@ -204,26 +161,23 @@ ReverseBytes(const Vector128& v) { // WARNING: these load/store in native byte order. It is OK to load and then // store an unchanged vector, but interpreting the bits as a number or input // to AES will have undefined results. -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 -Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { +inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) { return vec_vsx_ld(0, reinterpret_cast<const Vector128*>(from)); } -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void -Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { +inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) { vec_vsx_st(v, 0, reinterpret_cast<Vector128*>(to)); } // One round of AES. "round_key" is a public constant for breaking the // symmetry of AES (ensures previously equal columns differ afterwards). -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 -AesRound(const Vector128& state, const Vector128& round_key) { +inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, + const Vector128& round_key) { return Vector128(__builtin_crypto_vcipher(state, round_key)); } // Enables native loads in the round loop by pre-swapping. -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void -SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { +inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t* state) { using absl::random_internal::RandenTraits; constexpr size_t kLanes = 2; constexpr size_t kFeistelBlocks = RandenTraits::kFeistelBlocks; @@ -275,20 +229,18 @@ using Vector128 = uint8x16_t; namespace { -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 -Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { +inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) { return vld1q_u8(reinterpret_cast<const uint8_t*>(from)); } -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void -Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { +inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) { vst1q_u8(reinterpret_cast<uint8_t*>(to), v); } // One round of AES. "round_key" is a public constant for breaking the // symmetry of AES (ensures previously equal columns differ afterwards). -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 -AesRound(const Vector128& state, const Vector128& round_key) { +inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, + const Vector128& round_key) { // It is important to always use the full round function - omitting the // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf] // and does not help because we never decrypt. @@ -299,8 +251,7 @@ AesRound(const Vector128& state, const Vector128& round_key) { return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key; } -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void -SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT) {} +inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t*) {} } // namespace @@ -315,16 +266,11 @@ namespace { class Vector128 { public: // Convert from/to intrinsics. - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE explicit Vector128( - const __m128i& Vector128) - : data_(Vector128) {} + inline explicit Vector128(const __m128i& Vector128) : data_(Vector128) {} - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __m128i data() const { - return data_; - } + inline __m128i data() const { return data_; } - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128& operator^=( - const Vector128& other) { + inline Vector128& operator^=(const Vector128& other) { data_ = _mm_xor_si128(data_, other.data()); return *this; } @@ -333,29 +279,25 @@ class Vector128 { __m128i data_; }; -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 -Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { +inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) { return Vector128(_mm_load_si128(reinterpret_cast<const __m128i*>(from))); } -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void -Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { - _mm_store_si128(reinterpret_cast<__m128i * ABSL_RANDOM_INTERNAL_RESTRICT>(to), - v.data()); +inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) { + _mm_store_si128(reinterpret_cast<__m128i*>(to), v.data()); } // One round of AES. "round_key" is a public constant for breaking the // symmetry of AES (ensures previously equal columns differ afterwards). -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 -AesRound(const Vector128& state, const Vector128& round_key) { +inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, + const Vector128& round_key) { // It is important to always use the full round function - omitting the // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf] // and does not help because we never decrypt. return Vector128(_mm_aesenc_si128(state.data(), round_key.data())); } -inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void -SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT) {} +inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t*) {} } // namespace @@ -452,8 +394,7 @@ constexpr size_t kLanes = 2; // Block shuffles applies a shuffle to the entire state between AES rounds. // Improved odd-even shuffle from "New criterion for diffusion property". -inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO void -BlockShuffle(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { +inline ABSL_TARGET_CRYPTO void BlockShuffle(uint64_t* state) { static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); constexpr size_t shuffle[kFeistelBlocks] = {7, 2, 13, 4, 11, 8, 3, 6, @@ -501,10 +442,8 @@ BlockShuffle(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel // XORs are 'free' (included in the second AES instruction). -inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO const - u64x2* - FeistelRound(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state, - const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { +inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( + uint64_t* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); // MSVC does a horrible job at unrolling loops. @@ -563,9 +502,8 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO const // Indistinguishable from ideal by chosen-ciphertext adversaries using less than // 2^64 queries if the round function is a PRF. This is similar to the b=8 case // of Simpira v2, but more efficient than its generic construction for b=16. -inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO void -Permute(const void* ABSL_RANDOM_INTERNAL_RESTRICT keys, - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { +inline ABSL_TARGET_CRYPTO void Permute( + const void* ABSL_RANDOM_INTERNAL_RESTRICT keys, uint64_t* state) { const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys128 = static_cast<const u64x2*>(keys); @@ -582,25 +520,22 @@ Permute(const void* ABSL_RANDOM_INTERNAL_RESTRICT keys, } // namespace namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { bool HasRandenHwAesImplementation() { return true; } -const void* ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN -RandenHwAes::GetKeys() { +const void* ABSL_TARGET_CRYPTO RandenHwAes::GetKeys() { // Round keys for one AES per Feistel round and branch. // The canonical implementation uses first digits of Pi. return round_keys; } // NOLINTNEXTLINE -void ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN -RandenHwAes::Absorb(const void* seed_void, void* state_void) { - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = - reinterpret_cast<uint64_t*>(state_void); - const uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT seed = - reinterpret_cast<const uint64_t*>(seed_void); +void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void, + void* state_void) { + auto* state = static_cast<uint64_t*>(state_void); + const auto* seed = static_cast<const uint64_t*>(seed_void); constexpr size_t kCapacityBlocks = kCapacityBytes / sizeof(Vector128); constexpr size_t kStateBlocks = kStateBytes / sizeof(Vector128); @@ -672,12 +607,11 @@ RandenHwAes::Absorb(const void* seed_void, void* state_void) { } // NOLINTNEXTLINE -void ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN -RandenHwAes::Generate(const void* keys, void* state_void) { +void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys, + void* state_void) { static_assert(kCapacityBytes == sizeof(Vector128), "Capacity mismatch"); - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = - reinterpret_cast<uint64_t*>(state_void); + auto* state = static_cast<uint64_t*>(state_void); const Vector128 prev_inner = Vector128Load(state); @@ -698,7 +632,7 @@ RandenHwAes::Generate(const void* keys, void* state_void) { #endif } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // (ABSL_RANDEN_HWAES_IMPL) diff --git a/absl/random/internal/randen_hwaes.h b/absl/random/internal/randen_hwaes.h index 848bcead..bce36b52 100644 --- a/absl/random/internal/randen_hwaes.h +++ b/absl/random/internal/randen_hwaes.h @@ -15,13 +15,15 @@ #ifndef ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_ #define ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_ +#include "absl/base/config.h" + // HERMETIC NOTE: The randen_hwaes target must not introduce duplicate // symbols from arbitrary system and other headers, since it may be built // with different flags from other targets, using different levels of // optimization, potentially introducing ODR violations. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // RANDen = RANDom generator or beetroots in Swiss German. @@ -42,7 +44,7 @@ class RandenHwAes { bool HasRandenHwAesImplementation(); } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_ diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc index e2d44f88..8d074582 100644 --- a/absl/random/internal/randen_slow.cc +++ b/absl/random/internal/randen_slow.cc @@ -18,17 +18,9 @@ #include <cstdint> #include <cstring> +#include "absl/base/attributes.h" #include "absl/random/internal/platform.h" -// ABSL_HAVE_ATTRIBUTE -#if !defined(ABSL_HAVE_ATTRIBUTE) -#ifdef __has_attribute -#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) -#else -#define ABSL_HAVE_ATTRIBUTE(x) 0 -#endif -#endif - #if ABSL_HAVE_ATTRIBUTE(always_inline) || \ (defined(__GNUC__) && !defined(__clang__)) #define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \ @@ -470,7 +462,7 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute( } // namespace namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { const void* RandenSlow::GetKeys() { @@ -510,5 +502,5 @@ void RandenSlow::Generate(const void* keys, void* state_void) { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/randen_slow.h b/absl/random/internal/randen_slow.h index 2133b370..72f92b54 100644 --- a/absl/random/internal/randen_slow.h +++ b/absl/random/internal/randen_slow.h @@ -17,8 +17,10 @@ #include <cstddef> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // RANDen = RANDom generator or beetroots in Swiss German. @@ -39,7 +41,7 @@ class RandenSlow { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_ diff --git a/absl/random/internal/randen_traits.h b/absl/random/internal/randen_traits.h index d2562586..2b8bbe73 100644 --- a/absl/random/internal/randen_traits.h +++ b/absl/random/internal/randen_traits.h @@ -22,8 +22,10 @@ #include <cstddef> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // RANDen = RANDom generator or beetroots in Swiss German. @@ -55,7 +57,7 @@ struct RandenTraits { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_ diff --git a/absl/random/internal/salted_seed_seq.h b/absl/random/internal/salted_seed_seq.h index 08bf369e..5953a090 100644 --- a/absl/random/internal/salted_seed_seq.h +++ b/absl/random/internal/salted_seed_seq.h @@ -30,7 +30,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // This class conforms to the C++ Standard "Seed Sequence" concept @@ -161,7 +161,7 @@ SaltedSeedSeq<typename std::decay<SSeq>::type> MakeSaltedSeedSeq(SSeq&& seq) { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_ diff --git a/absl/random/internal/seed_material.cc b/absl/random/internal/seed_material.cc index dae7007f..4d38a574 100644 --- a/absl/random/internal/seed_material.cc +++ b/absl/random/internal/seed_material.cc @@ -45,6 +45,9 @@ #define ABSL_RANDOM_USE_BCRYPT 1 #pragma comment(lib, "bcrypt.lib") +#elif defined(__Fuchsia__) +#include <zircon/syscalls.h> + #endif #if defined(ABSL_RANDOM_USE_BCRYPT) @@ -58,7 +61,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { namespace { @@ -108,6 +111,15 @@ bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { return true; } +#elif defined(__Fuchsia__) + +bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { + auto buffer = reinterpret_cast<uint8_t*>(values.data()); + size_t buffer_size = sizeof(uint32_t) * values.size(); + zx_cprng_draw(buffer, buffer_size); + return true; +} + #else // On *nix, read entropy from /dev/urandom. @@ -203,5 +215,5 @@ absl::optional<uint32_t> GetSaltMaterial() { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/seed_material.h b/absl/random/internal/seed_material.h index 41387fe3..4be10e92 100644 --- a/absl/random/internal/seed_material.h +++ b/absl/random/internal/seed_material.h @@ -27,7 +27,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // Returns the number of 32-bit blocks needed to contain the given number of @@ -98,7 +98,7 @@ void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence, absl::optional<uint32_t> GetSaltMaterial(); } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_ diff --git a/absl/random/internal/sequence_urbg.h b/absl/random/internal/sequence_urbg.h index cec0bf9b..bc96a12c 100644 --- a/absl/random/internal/sequence_urbg.h +++ b/absl/random/internal/sequence_urbg.h @@ -21,8 +21,10 @@ #include <type_traits> #include <vector> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // `sequence_urbg` is a simple random number generator which meets the @@ -52,7 +54,7 @@ class sequence_urbg { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_ diff --git a/absl/random/internal/traits.h b/absl/random/internal/traits.h index 9f7d126c..75772bd9 100644 --- a/absl/random/internal/traits.h +++ b/absl/random/internal/traits.h @@ -22,7 +22,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace random_internal { // random_internal::is_widening_convertible<A, B> @@ -95,7 +95,7 @@ struct make_unsigned_bits { }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_TRAITS_H_ diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h index 6af053ef..663107cb 100644 --- a/absl/random/internal/uniform_helper.h +++ b/absl/random/internal/uniform_helper.h @@ -22,7 +22,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN template <typename IntType> class uniform_int_distribution; @@ -31,15 +31,33 @@ class uniform_real_distribution; // Interval tag types which specify whether the interval is open or closed // on either boundary. + namespace random_internal { -struct IntervalClosedClosedT {}; -struct IntervalClosedOpenT {}; -struct IntervalOpenClosedT {}; -struct IntervalOpenOpenT {}; +template <typename T> +struct TagTypeCompare {}; + +template <typename T> +constexpr bool operator==(TagTypeCompare<T>, TagTypeCompare<T>) { + // Tags are mono-states. They always compare equal. + return true; +} +template <typename T> +constexpr bool operator!=(TagTypeCompare<T>, TagTypeCompare<T>) { + return false; +} + } // namespace random_internal -namespace random_internal { +struct IntervalClosedClosedTag + : public random_internal::TagTypeCompare<IntervalClosedClosedTag> {}; +struct IntervalClosedOpenTag + : public random_internal::TagTypeCompare<IntervalClosedOpenTag> {}; +struct IntervalOpenClosedTag + : public random_internal::TagTypeCompare<IntervalOpenClosedTag> {}; +struct IntervalOpenOpenTag + : public random_internal::TagTypeCompare<IntervalOpenOpenTag> {}; +namespace random_internal { // The functions // uniform_lower_bound(tag, a, b) // and @@ -60,8 +78,8 @@ template <typename IntType, typename Tag> typename absl::enable_if_t< absl::conjunction< std::is_integral<IntType>, - absl::disjunction<std::is_same<Tag, IntervalOpenClosedT>, - std::is_same<Tag, IntervalOpenOpenT>>>::value, + absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>, + std::is_same<Tag, IntervalOpenOpenTag>>>::value, IntType> uniform_lower_bound(Tag, IntType a, IntType) { return a + 1; @@ -71,8 +89,8 @@ template <typename FloatType, typename Tag> typename absl::enable_if_t< absl::conjunction< std::is_floating_point<FloatType>, - absl::disjunction<std::is_same<Tag, IntervalOpenClosedT>, - std::is_same<Tag, IntervalOpenOpenT>>>::value, + absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>, + std::is_same<Tag, IntervalOpenOpenTag>>>::value, FloatType> uniform_lower_bound(Tag, FloatType a, FloatType b) { return std::nextafter(a, b); @@ -80,8 +98,8 @@ uniform_lower_bound(Tag, FloatType a, FloatType b) { template <typename NumType, typename Tag> typename absl::enable_if_t< - absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>, - std::is_same<Tag, IntervalClosedOpenT>>::value, + absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, + std::is_same<Tag, IntervalClosedOpenTag>>::value, NumType> uniform_lower_bound(Tag, NumType a, NumType) { return a; @@ -91,8 +109,8 @@ template <typename IntType, typename Tag> typename absl::enable_if_t< absl::conjunction< std::is_integral<IntType>, - absl::disjunction<std::is_same<Tag, IntervalClosedOpenT>, - std::is_same<Tag, IntervalOpenOpenT>>>::value, + absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>, + std::is_same<Tag, IntervalOpenOpenTag>>>::value, IntType> uniform_upper_bound(Tag, IntType, IntType b) { return b - 1; @@ -102,8 +120,8 @@ template <typename FloatType, typename Tag> typename absl::enable_if_t< absl::conjunction< std::is_floating_point<FloatType>, - absl::disjunction<std::is_same<Tag, IntervalClosedOpenT>, - std::is_same<Tag, IntervalOpenOpenT>>>::value, + absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>, + std::is_same<Tag, IntervalOpenOpenTag>>>::value, FloatType> uniform_upper_bound(Tag, FloatType, FloatType b) { return b; @@ -113,8 +131,8 @@ template <typename IntType, typename Tag> typename absl::enable_if_t< absl::conjunction< std::is_integral<IntType>, - absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>, - std::is_same<Tag, IntervalOpenClosedT>>>::value, + absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, + std::is_same<Tag, IntervalOpenClosedTag>>>::value, IntType> uniform_upper_bound(Tag, IntType, IntType b) { return b; @@ -124,8 +142,8 @@ template <typename FloatType, typename Tag> typename absl::enable_if_t< absl::conjunction< std::is_floating_point<FloatType>, - absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>, - std::is_same<Tag, IntervalOpenClosedT>>>::value, + absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, + std::is_same<Tag, IntervalOpenClosedTag>>>::value, FloatType> uniform_upper_bound(Tag, FloatType, FloatType b) { return std::nextafter(b, (std::numeric_limits<FloatType>::max)()); @@ -137,16 +155,26 @@ using UniformDistribution = absl::uniform_int_distribution<NumType>, absl::uniform_real_distribution<NumType>>::type; -template <typename TagType, typename NumType> +template <typename NumType> struct UniformDistributionWrapper : public UniformDistribution<NumType> { - explicit UniformDistributionWrapper(NumType lo, NumType hi) + template <typename TagType> + explicit UniformDistributionWrapper(TagType, NumType lo, NumType hi) : UniformDistribution<NumType>( uniform_lower_bound<NumType>(TagType{}, lo, hi), uniform_upper_bound<NumType>(TagType{}, lo, hi)) {} + + explicit UniformDistributionWrapper(NumType lo, NumType hi) + : UniformDistribution<NumType>( + uniform_lower_bound<NumType>(IntervalClosedOpenTag(), lo, hi), + uniform_upper_bound<NumType>(IntervalClosedOpenTag(), lo, hi)) {} + + explicit UniformDistributionWrapper() + : UniformDistribution<NumType>(std::numeric_limits<NumType>::lowest(), + (std::numeric_limits<NumType>::max)()) {} }; } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_ diff --git a/absl/random/internal/wide_multiply.h b/absl/random/internal/wide_multiply.h new file mode 100644 index 00000000..6e4cf1be --- /dev/null +++ b/absl/random/internal/wide_multiply.h @@ -0,0 +1,111 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_ +#define ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_ + +#include <cstdint> +#include <limits> +#include <type_traits> + +#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64) +#include <intrin.h> // NOLINT(build/include_order) +#pragma intrinsic(_umul128) +#define ABSL_INTERNAL_USE_UMUL128 1 +#endif + +#include "absl/base/config.h" +#include "absl/base/internal/bits.h" +#include "absl/numeric/int128.h" +#include "absl/random/internal/traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace random_internal { + +// Helper object to multiply two 64-bit values to a 128-bit value. +// MultiplyU64ToU128 multiplies two 64-bit values to a 128-bit value. +// If an intrinsic is available, it is used, otherwise use native 32-bit +// multiplies to construct the result. +inline uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return uint128(static_cast<__uint128_t>(a) * b); +#elif defined(ABSL_INTERNAL_USE_UMUL128) + // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC. + uint64_t high = 0; + const uint64_t low = _umul128(a, b, &high); + return absl::MakeUint128(high, low); +#else + // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit + // multiply. However there are many cases where that is not necessary, and it + // is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is + // for those cases. + const uint64_t a00 = static_cast<uint32_t>(a); + const uint64_t a32 = a >> 32; + const uint64_t b00 = static_cast<uint32_t>(b); + const uint64_t b32 = b >> 32; + + const uint64_t c00 = a00 * b00; + const uint64_t c32a = a00 * b32; + const uint64_t c32b = a32 * b00; + const uint64_t c64 = a32 * b32; + + const uint32_t carry = + static_cast<uint32_t>(((c00 >> 32) + static_cast<uint32_t>(c32a) + + static_cast<uint32_t>(c32b)) >> + 32); + + return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry, + c00 + (c32a << 32) + (c32b << 32)); +#endif +} + +// wide_multiply<T> multiplies two N-bit values to a 2N-bit result. +template <typename UIntType> +struct wide_multiply { + static constexpr size_t kN = std::numeric_limits<UIntType>::digits; + using input_type = UIntType; + using result_type = typename random_internal::unsigned_bits<kN * 2>::type; + + static result_type multiply(input_type a, input_type b) { + return static_cast<result_type>(a) * b; + } + + static input_type hi(result_type r) { return r >> kN; } + static input_type lo(result_type r) { return r; } + + static_assert(std::is_unsigned<UIntType>::value, + "Class-template wide_multiply<> argument must be unsigned."); +}; + +#ifndef ABSL_HAVE_INTRINSIC_INT128 +template <> +struct wide_multiply<uint64_t> { + using input_type = uint64_t; + using result_type = uint128; + + static result_type multiply(uint64_t a, uint64_t b) { + return MultiplyU64ToU128(a, b); + } + + static uint64_t hi(result_type r) { return Uint128High64(r); } + static uint64_t lo(result_type r) { return Uint128Low64(r); } +}; +#endif + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_ diff --git a/absl/random/internal/wide_multiply_test.cc b/absl/random/internal/wide_multiply_test.cc new file mode 100644 index 00000000..922603f2 --- /dev/null +++ b/absl/random/internal/wide_multiply_test.cc @@ -0,0 +1,66 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/wide_multiply.h" + +#include "gtest/gtest.h" +#include "absl/base/internal/bits.h" +#include "absl/numeric/int128.h" + +using absl::random_internal::MultiplyU64ToU128; + +namespace { + +TEST(WideMultiplyTest, MultiplyU64ToU128Test) { + constexpr uint64_t k1 = 1; + constexpr uint64_t kMax = ~static_cast<uint64_t>(0); + + EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0)); + + // Max uint64 + EXPECT_EQ(MultiplyU64ToU128(kMax, kMax), + absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001)); + EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1)); + EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(1, kMax)); + for (int i = 0; i < 64; ++i) { + EXPECT_EQ(absl::MakeUint128(0, kMax) << i, + MultiplyU64ToU128(kMax, k1 << i)); + EXPECT_EQ(absl::MakeUint128(0, kMax) << i, + MultiplyU64ToU128(k1 << i, kMax)); + } + + // 1-bit x 1-bit. + for (int i = 0; i < 64; ++i) { + for (int j = 0; j < 64; ++j) { + EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j), + MultiplyU64ToU128(k1 << i, k1 << j)); + EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j), + MultiplyU64ToU128(k1 << i, k1 << j)); + } + } + + // Verified multiplies + EXPECT_EQ(MultiplyU64ToU128(0xffffeeeeddddcccc, 0xbbbbaaaa99998888), + absl::MakeUint128(0xbbbb9e2692c5dddc, 0xc28f7531048d2c60)); + EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfedcba9876543210), + absl::MakeUint128(0x0121fa00ad77d742, 0x2236d88fe5618cf0)); + EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfdb97531eca86420), + absl::MakeUint128(0x0120ae99d26725fc, 0xce197f0ecac319e0)); + EXPECT_EQ(MultiplyU64ToU128(0x97a87f4f261ba3f2, 0xfedcba9876543210), + absl::MakeUint128(0x96fbf1a8ae78d0ba, 0x5a6dd4b71f278320)); + EXPECT_EQ(MultiplyU64ToU128(0xfedcba9876543210, 0xfdb97531eca86420), + absl::MakeUint128(0xfc98c6981a413e22, 0x342d0bbf48948200)); +} + +} // namespace diff --git a/absl/random/log_uniform_int_distribution.h b/absl/random/log_uniform_int_distribution.h index a12fa4cb..960816e2 100644 --- a/absl/random/log_uniform_int_distribution.h +++ b/absl/random/log_uniform_int_distribution.h @@ -23,14 +23,14 @@ #include <ostream> #include <type_traits> -#include "absl/random/internal/distribution_impl.h" #include "absl/random/internal/fastmath.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" #include "absl/random/internal/traits.h" #include "absl/random/uniform_int_distribution.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // log_uniform_int_distribution: // @@ -193,13 +193,15 @@ log_uniform_int_distribution<IntType>::Generate( const double r = std::pow(p.base(), d); const double s = (r * p.base()) - 1.0; - base_e = (r > (std::numeric_limits<unsigned_type>::max)()) - ? (std::numeric_limits<unsigned_type>::max)() - : static_cast<unsigned_type>(r); + base_e = + (r > static_cast<double>((std::numeric_limits<unsigned_type>::max)())) + ? (std::numeric_limits<unsigned_type>::max)() + : static_cast<unsigned_type>(r); - top_e = (s > (std::numeric_limits<unsigned_type>::max)()) - ? (std::numeric_limits<unsigned_type>::max)() - : static_cast<unsigned_type>(s); + top_e = + (s > static_cast<double>((std::numeric_limits<unsigned_type>::max)())) + ? (std::numeric_limits<unsigned_type>::max)() + : static_cast<unsigned_type>(s); } const unsigned_type lo = (base_e >= p.range()) ? p.range() : base_e; @@ -246,7 +248,7 @@ std::basic_istream<CharT, Traits>& operator>>( return is; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_ diff --git a/absl/random/log_uniform_int_distribution_test.cc b/absl/random/log_uniform_int_distribution_test.cc index 0ff4c32d..5270531d 100644 --- a/absl/random/log_uniform_int_distribution_test.cc +++ b/absl/random/log_uniform_int_distribution_test.cc @@ -243,7 +243,7 @@ std::string ParamName( return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); } -INSTANTIATE_TEST_SUITE_P(, LogUniformIntChiSquaredTest, +INSTANTIATE_TEST_SUITE_P(All, LogUniformIntChiSquaredTest, ::testing::ValuesIn(GenParams()), ParamName); // NOTE: absl::log_uniform_int_distribution is not guaranteed to be stable. diff --git a/absl/random/mock_distributions.h b/absl/random/mock_distributions.h new file mode 100644 index 00000000..d36d5ba0 --- /dev/null +++ b/absl/random/mock_distributions.h @@ -0,0 +1,261 @@ +// 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: mock_distributions.h +// ----------------------------------------------------------------------------- +// +// This file contains mock distribution functions for use alongside an +// `absl::MockingBitGen` object within the Googletest testing framework. Such +// mocks are useful to provide deterministic values as return values within +// (otherwise random) Abseil distribution functions. +// +// The return type of each function is a mock expectation object which +// is used to set the match result. +// +// More information about the Googletest testing framework is available at +// https://github.com/google/googletest +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockUniform<int>(), Call(mock, 1, 1000)) +// .WillRepeatedly(testing::ReturnRoundRobin({20, 40})); +// +// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 20); +// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 40); +// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 20); +// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 40); + +#ifndef ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ +#define ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ + +#include <limits> +#include <type_traits> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" +#include "absl/random/distributions.h" +#include "absl/random/internal/mock_overload_set.h" +#include "absl/random/mocking_bit_gen.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// ----------------------------------------------------------------------------- +// absl::MockUniform +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Uniform. +// +// `absl::MockUniform` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(mock)) +// .WillOnce(Return(123456)); +// auto x = absl::Uniform<uint32_t>(mock); +// assert(x == 123456) +// +template <typename R> +using MockUniform = random_internal::MockOverloadSet< + random_internal::UniformDistributionWrapper<R>, + R(IntervalClosedOpenTag, MockingBitGen&, R, R), + R(IntervalClosedClosedTag, MockingBitGen&, R, R), + R(IntervalOpenOpenTag, MockingBitGen&, R, R), + R(IntervalOpenClosedTag, MockingBitGen&, R, R), R(MockingBitGen&, R, R), + R(MockingBitGen&)>; + +// ----------------------------------------------------------------------------- +// absl::MockBernoulli +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Bernoulli. +// +// `absl::MockBernoulli` is a class used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockBernoulli(), Call(mock, testing::_)) +// .WillOnce(Return(false)); +// assert(absl::Bernoulli(mock, 0.5) == false); +// +using MockBernoulli = + random_internal::MockOverloadSet<absl::bernoulli_distribution, + bool(MockingBitGen&, double)>; + +// ----------------------------------------------------------------------------- +// absl::MockBeta +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Beta. +// +// `absl::MockBeta` is a class used in conjunction with Googletest's `ON_CALL()` +// and `EXPECT_CALL()` macros. To use it, default-construct an instance of it +// inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the same way one +// would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockBeta(), Call(mock, 3.0, 2.0)) +// .WillOnce(Return(0.567)); +// auto x = absl::Beta<double>(mock, 3.0, 2.0); +// assert(x == 0.567); +// +template <typename RealType> +using MockBeta = + random_internal::MockOverloadSet<absl::beta_distribution<RealType>, + RealType(MockingBitGen&, RealType, + RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockExponential +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Exponential. +// +// `absl::MockExponential` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockExponential<double>(), Call(mock, 0.5)) +// .WillOnce(Return(12.3456789)); +// auto x = absl::Exponential<double>(mock, 0.5); +// assert(x == 12.3456789) +// +template <typename RealType> +using MockExponential = + random_internal::MockOverloadSet<absl::exponential_distribution<RealType>, + RealType(MockingBitGen&, RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockGaussian +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Gaussian. +// +// `absl::MockGaussian` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockGaussian<double>(), Call(mock, 16.3, 3.3)) +// .WillOnce(Return(12.3456789)); +// auto x = absl::Gaussian<double>(mock, 16.3, 3.3); +// assert(x == 12.3456789) +// +template <typename RealType> +using MockGaussian = + random_internal::MockOverloadSet<absl::gaussian_distribution<RealType>, + RealType(MockingBitGen&, RealType, + RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockLogUniform +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::LogUniform. +// +// `absl::MockLogUniform` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockLogUniform<int>(), Call(mock, 10, 10000, 10)) +// .WillOnce(Return(1221)); +// auto x = absl::LogUniform<int>(mock, 10, 10000, 10); +// assert(x == 1221) +// +template <typename IntType> +using MockLogUniform = random_internal::MockOverloadSet< + absl::log_uniform_int_distribution<IntType>, + IntType(MockingBitGen&, IntType, IntType, IntType)>; + +// ----------------------------------------------------------------------------- +// absl::MockPoisson +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Poisson. +// +// `absl::MockPoisson` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockPoisson<int>(), Call(mock, 2.0)) +// .WillOnce(Return(1221)); +// auto x = absl::Poisson<int>(mock, 2.0); +// assert(x == 1221) +// +template <typename IntType> +using MockPoisson = + random_internal::MockOverloadSet<absl::poisson_distribution<IntType>, + IntType(MockingBitGen&, double)>; + +// ----------------------------------------------------------------------------- +// absl::MockZipf +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Zipf. +// +// `absl::MockZipf` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockZipf<int>(), Call(mock, 1000000, 2.0, 1.0)) +// .WillOnce(Return(1221)); +// auto x = absl::Zipf<int>(mock, 1000000, 2.0, 1.0); +// assert(x == 1221) +// +template <typename IntType> +using MockZipf = + random_internal::MockOverloadSet<absl::zipf_distribution<IntType>, + IntType(MockingBitGen&, IntType, double, + double)>; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ diff --git a/absl/random/mock_distributions_test.cc b/absl/random/mock_distributions_test.cc new file mode 100644 index 00000000..de23bafe --- /dev/null +++ b/absl/random/mock_distributions_test.cc @@ -0,0 +1,72 @@ +// 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/random/mock_distributions.h" + +#include "gtest/gtest.h" +#include "absl/random/mocking_bit_gen.h" +#include "absl/random/random.h" + +namespace { +using ::testing::Return; + +TEST(MockDistributions, Examples) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20); + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .WillOnce(Return(20)); + EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0)) + .WillOnce(Return(5.0)); + EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42); + EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillOnce(Return(42)); + EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500); + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(500)); + EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillOnce(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Beta<double>(gen, 3.0, 2.0), 0.567); + EXPECT_CALL(absl::MockBeta<double>(), Call(gen, 3.0, 2.0)) + .WillOnce(Return(0.567)); + EXPECT_EQ(absl::Beta<double>(gen, 3.0, 2.0), 0.567); + + EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + EXPECT_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0)) + .WillOnce(Return(1221)); + EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + EXPECT_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0)) + .WillOnce(Return(0.001)); + EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); + EXPECT_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2)) + .WillOnce(Return(2040)); + EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); +} + +} // namespace diff --git a/absl/random/internal/named_generator.cc b/absl/random/mocking_bit_gen.cc index b168a25b..6bb1e414 100644 --- a/absl/random/internal/named_generator.cc +++ b/absl/random/mocking_bit_gen.cc @@ -1,3 +1,4 @@ +// // Copyright 2018 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -11,20 +12,19 @@ // 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/random/mocking_bit_gen.h" -#include <cstddef> -#include <iostream> - -#include "absl/random/random.h" +#include <string> -// This program is used in integration tests. +namespace absl { +ABSL_NAMESPACE_BEGIN +MockingBitGen::~MockingBitGen() { -int main() { - auto seed_seq = absl::MakeTaggedSeedSeq("TEST_GENERATOR", std::cerr); - absl::BitGen rng(seed_seq); - constexpr size_t kSequenceLength = 8; - for (size_t i = 0; i < kSequenceLength; i++) { - std::cout << rng() << "\n"; + for (const auto& del : deleters_) { + del(); } - return 0; } + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h new file mode 100644 index 00000000..36cef911 --- /dev/null +++ b/absl/random/mocking_bit_gen.h @@ -0,0 +1,196 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// mocking_bit_gen.h +// ----------------------------------------------------------------------------- +// +// This file includes an `absl::MockingBitGen` class to use as a mock within the +// Googletest testing framework. Such a mock is useful to provide deterministic +// values as return values within (otherwise random) Abseil distribution +// functions. Such determinism within a mock is useful within testing frameworks +// to test otherwise indeterminate APIs. +// +// More information about the Googletest testing framework is available at +// https://github.com/google/googletest + +#ifndef ABSL_RANDOM_MOCKING_BIT_GEN_H_ +#define ABSL_RANDOM_MOCKING_BIT_GEN_H_ + +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <typeindex> +#include <typeinfo> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/flat_hash_map.h" +#include "absl/meta/type_traits.h" +#include "absl/random/distributions.h" +#include "absl/random/internal/distribution_caller.h" +#include "absl/random/internal/mocking_bit_gen_base.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/types/span.h" +#include "absl/types/variant.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace random_internal { + +template <typename, typename> +struct MockSingleOverload; + +} // namespace random_internal + +// MockingBitGen +// +// `absl::MockingBitGen` is a mock Uniform Random Bit Generator (URBG) class +// which can act in place of an `absl::BitGen` URBG within tests using the +// Googletest testing framework. +// +// Usage: +// +// Use an `absl::MockingBitGen` along with a mock distribution object (within +// mock_distributions.h) inside Googletest constructs such as ON_CALL(), +// EXPECT_TRUE(), etc. to produce deterministic results conforming to the +// distribution's API contract. +// +// Example: +// +// // Mock a call to an `absl::Bernoulli` distribution using Googletest +// absl::MockingBitGen bitgen; +// +// ON_CALL(absl::MockBernoulli(), Call(bitgen, 0.5)) +// .WillByDefault(testing::Return(true)); +// EXPECT_TRUE(absl::Bernoulli(bitgen, 0.5)); +// +// // Mock a call to an `absl::Uniform` distribution within Googletest +// absl::MockingBitGen bitgen; +// +// ON_CALL(absl::MockUniform<int>(), Call(bitgen, testing::_, testing::_)) +// .WillByDefault([] (int low, int high) { +// return (low + high) / 2; +// }); +// +// EXPECT_EQ(absl::Uniform<int>(gen, 0, 10), 5); +// EXPECT_EQ(absl::Uniform<int>(gen, 30, 40), 35); +// +// At this time, only mock distributions supplied within the Abseil random +// library are officially supported. +// +class MockingBitGen : public absl::random_internal::MockingBitGenBase { + public: + MockingBitGen() {} + + ~MockingBitGen() override; + + private: + template <typename DistrT, typename... Args> + using MockFnType = + ::testing::MockFunction<typename DistrT::result_type(Args...)>; + + // MockingBitGen::Register + // + // Register<DistrT, FormatT, ArgTupleT> is the main extension point for + // extending the MockingBitGen framework. It provides a mechanism to install a + // mock expectation for the distribution `distr_t` onto the MockingBitGen + // context. + // + // The returned MockFunction<...> type can be used to setup additional + // distribution parameters of the expectation. + template <typename DistrT, typename... Args, typename... Ms> + decltype(std::declval<MockFnType<DistrT, Args...>>().gmock_Call( + std::declval<Ms>()...)) + Register(Ms&&... matchers) { + auto& mock = + mocks_[std::type_index(GetTypeId<DistrT, std::tuple<Args...>>())]; + + if (!mock.mock_fn) { + auto* mock_fn = new MockFnType<DistrT, Args...>; + mock.mock_fn = mock_fn; + mock.match_impl = &MatchImpl<DistrT, Args...>; + deleters_.emplace_back([mock_fn] { delete mock_fn; }); + } + + return static_cast<MockFnType<DistrT, Args...>*>(mock.mock_fn) + ->gmock_Call(std::forward<Ms>(matchers)...); + } + + mutable std::vector<std::function<void()>> deleters_; + + using match_impl_fn = void (*)(void* mock_fn, void* t_erased_dist_args, + void* t_erased_result); + struct MockData { + void* mock_fn = nullptr; + match_impl_fn match_impl = nullptr; + }; + + mutable absl::flat_hash_map<std::type_index, MockData> mocks_; + + template <typename DistrT, typename... Args> + static void MatchImpl(void* mock_fn, void* dist_args, void* result) { + using result_type = typename DistrT::result_type; + *static_cast<result_type*>(result) = absl::apply( + [mock_fn](Args... args) -> result_type { + return (*static_cast<MockFnType<DistrT, Args...>*>(mock_fn)) + .Call(std::move(args)...); + }, + *static_cast<std::tuple<Args...>*>(dist_args)); + } + + // Looks for an appropriate mock - Returns the mocked result if one is found. + // Otherwise, returns a random value generated by the underlying URBG. + bool CallImpl(const std::type_info& key_type, void* dist_args, + void* result) override { + // Trigger a mock, if there exists one that matches `param`. + auto it = mocks_.find(std::type_index(key_type)); + if (it == mocks_.end()) return false; + auto* mock_data = static_cast<MockData*>(&it->second); + mock_data->match_impl(mock_data->mock_fn, dist_args, result); + return true; + } + + template <typename, typename> + friend struct ::absl::random_internal::MockSingleOverload; + friend struct ::absl::random_internal::DistributionCaller< + absl::MockingBitGen>; +}; + +// ----------------------------------------------------------------------------- +// Implementation Details Only Below +// ----------------------------------------------------------------------------- + +namespace random_internal { + +template <> +struct DistributionCaller<absl::MockingBitGen> { + template <typename DistrT, typename FormatT, typename... Args> + static typename DistrT::result_type Call(absl::MockingBitGen* gen, + Args&&... args) { + return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...); + } +}; + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_MOCKING_BIT_GEN_H_ diff --git a/absl/random/mocking_bit_gen_test.cc b/absl/random/mocking_bit_gen_test.cc new file mode 100644 index 00000000..f0ffc9ac --- /dev/null +++ b/absl/random/mocking_bit_gen_test.cc @@ -0,0 +1,347 @@ +// +// 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/random/mocking_bit_gen.h" + +#include <numeric> +#include <random> + +#include "gmock/gmock.h" +#include "gtest/gtest-spi.h" +#include "gtest/gtest.h" +#include "absl/random/bit_gen_ref.h" +#include "absl/random/mock_distributions.h" +#include "absl/random/random.h" + +namespace { +using ::testing::Ne; +using ::testing::Return; + +TEST(BasicMocking, AllDistributionsAreOverridable) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20); + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .WillOnce(Return(20)); + EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0)) + .WillOnce(Return(5.0)); + EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42); + EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillOnce(Return(42)); + EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500); + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(500)); + EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillOnce(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + EXPECT_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0)) + .WillOnce(Return(1221)); + EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + EXPECT_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0)) + .WillOnce(Return(0.001)); + EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 500000); + EXPECT_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2)) + .WillOnce(Return(500000)); + EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 500000); +} + +TEST(BasicMocking, OnDistribution) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20); + ON_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .WillByDefault(Return(20)); + EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + ON_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0)) + .WillByDefault(Return(5.0)); + EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42); + ON_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillByDefault(Return(42)); + EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500); + ON_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillByDefault(Return(500)); + EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + ON_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillByDefault(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + ON_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0)) + .WillByDefault(Return(1221)); + EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + ON_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0)) + .WillByDefault(Return(0.001)); + EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); + ON_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2)) + .WillByDefault(Return(2040)); + EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); +} + +TEST(BasicMocking, GMockMatchers) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + ON_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0)) + .WillByDefault(Return(1221)); + EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); +} + +TEST(BasicMocking, OverridesWithMultipleGMockExpectations) { + absl::MockingBitGen gen; + + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 10000)) + .WillOnce(Return(20)) + .WillOnce(Return(40)) + .WillOnce(Return(60)); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 20); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 40); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 60); +} + +TEST(BasicMocking, DefaultArgument) { + absl::MockingBitGen gen; + + ON_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillByDefault(Return(200)); + + EXPECT_EQ(absl::Exponential<double>(gen), 200); + EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 200); +} + +TEST(BasicMocking, MultipleGenerators) { + auto get_value = [](absl::BitGenRef gen_ref) { + return absl::Uniform(gen_ref, 1, 1000000); + }; + absl::MockingBitGen unmocked_generator; + absl::MockingBitGen mocked_with_3; + absl::MockingBitGen mocked_with_11; + + EXPECT_CALL(absl::MockUniform<int>(), Call(mocked_with_3, 1, 1000000)) + .WillOnce(Return(3)) + .WillRepeatedly(Return(17)); + EXPECT_CALL(absl::MockUniform<int>(), Call(mocked_with_11, 1, 1000000)) + .WillOnce(Return(11)) + .WillRepeatedly(Return(17)); + + // Ensure that unmocked generator generates neither value. + int unmocked_value = get_value(unmocked_generator); + EXPECT_NE(unmocked_value, 3); + EXPECT_NE(unmocked_value, 11); + // Mocked generators should generate their mocked values. + EXPECT_EQ(get_value(mocked_with_3), 3); + EXPECT_EQ(get_value(mocked_with_11), 11); + // Ensure that the mocks have expired. + EXPECT_NE(get_value(mocked_with_3), 3); + EXPECT_NE(get_value(mocked_with_11), 11); +} + +TEST(BasicMocking, MocksNotTrigeredForIncorrectTypes) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(gen)).WillOnce(Return(42)); + + EXPECT_NE(absl::Uniform<uint16_t>(gen), 42); // Not mocked + EXPECT_EQ(absl::Uniform<uint32_t>(gen), 42); // Mock triggered +} + +TEST(BasicMocking, FailsOnUnsatisfiedMocks) { + EXPECT_NONFATAL_FAILURE( + []() { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillOnce(Return(3.0)); + // Does not call absl::Exponential(). + }(), + "unsatisfied and active"); +} + +TEST(OnUniform, RespectsUniformIntervalSemantics) { + absl::MockingBitGen gen; + + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalClosed, gen, 1, 1000000)) + .WillOnce(Return(301)); + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 301); // Not mocked + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 1, 1000000), 301); +} + +TEST(OnUniform, RespectsNoArgUnsignedShorthand) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(gen)).WillOnce(Return(42)); + EXPECT_EQ(absl::Uniform<uint32_t>(gen), 42); +} + +TEST(RepeatedlyModifier, ForceSnakeEyesForManyDice) { + auto roll_some_dice = [](absl::BitGenRef gen_ref) { + std::vector<int> results(16); + for (auto& r : results) { + r = absl::Uniform(absl::IntervalClosed, gen_ref, 1, 6); + } + return results; + }; + std::vector<int> results; + absl::MockingBitGen gen; + + // Without any mocked calls, not all dice roll a "6". + results = roll_some_dice(gen); + EXPECT_LT(std::accumulate(std::begin(results), std::end(results), 0), + results.size() * 6); + + // Verify that we can force all "6"-rolls, with mocking. + ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosed, gen, 1, 6)) + .WillByDefault(Return(6)); + results = roll_some_dice(gen); + EXPECT_EQ(std::accumulate(std::begin(results), std::end(results), 0), + results.size() * 6); +} + +TEST(WillOnce, DistinctCounters) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .Times(3) + .WillRepeatedly(Return(0)); + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1000001, 2000000)) + .Times(3) + .WillRepeatedly(Return(1)); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); +} + +TEST(TimesModifier, ModifierSaturatesAndExpires) { + EXPECT_NONFATAL_FAILURE( + []() { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .Times(3) + .WillRepeatedly(Return(15)) + .RetiresOnSaturation(); + + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + // Times(3) has expired - Should get a different value now. + + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 15); + }(), + ""); +} + +TEST(TimesModifier, Times0) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.0)).Times(0); + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).Times(0); +} + +TEST(AnythingMatcher, MatchesAnyArgument) { + using testing::_; + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosed, gen, _, 1000)) + .WillByDefault(Return(11)); + ON_CALL(absl::MockUniform<int>(), + Call(absl::IntervalClosed, gen, _, Ne(1000))) + .WillByDefault(Return(99)); + + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000000), 99); + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000), 11); + } + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform<int>(), Call(gen, 1, _)) + .WillByDefault(Return(25)); + ON_CALL(absl::MockUniform<int>(), Call(gen, Ne(1), _)) + .WillByDefault(Return(99)); + EXPECT_EQ(absl::Uniform(gen, 3, 1000000), 99); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 25); + } + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform<int>(), Call(gen, _, _)) + .WillByDefault(Return(145)); + EXPECT_EQ(absl::Uniform(gen, 1, 1000), 145); + EXPECT_EQ(absl::Uniform(gen, 10, 1000), 145); + EXPECT_EQ(absl::Uniform(gen, 100, 1000), 145); + } +} + +TEST(AnythingMatcher, WithWillByDefault) { + using testing::_; + absl::MockingBitGen gen; + std::vector<int> values = {11, 22, 33, 44, 55, 66, 77, 88, 99, 1010}; + + ON_CALL(absl::MockUniform<size_t>(), Call(gen, 0, _)) + .WillByDefault(Return(0)); + for (int i = 0; i < 100; i++) { + auto& elem = values[absl::Uniform(gen, 0u, values.size())]; + EXPECT_EQ(elem, 11); + } +} + +TEST(BasicMocking, WillByDefaultWithArgs) { + using testing::_; + + absl::MockingBitGen gen; + ON_CALL(absl::MockPoisson<int>(), Call(gen, _)) + .WillByDefault( + [](double lambda) { return static_cast<int>(lambda * 10); }); + EXPECT_EQ(absl::Poisson<int>(gen, 1.7), 17); + EXPECT_EQ(absl::Poisson<int>(gen, 0.03), 0); +} + +TEST(MockingBitGen, InSequenceSucceedsInOrder) { + absl::MockingBitGen gen; + + testing::InSequence seq; + + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(3)); + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 2.0)).WillOnce(Return(4)); + + EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 3); + EXPECT_EQ(absl::Poisson<int>(gen, 2.0), 4); +} + +} // namespace diff --git a/absl/random/poisson_distribution.h b/absl/random/poisson_distribution.h index 66c75091..cb5f5d5d 100644 --- a/absl/random/poisson_distribution.h +++ b/absl/random/poisson_distribution.h @@ -22,13 +22,13 @@ #include <ostream> #include <type_traits> -#include "absl/random/internal/distribution_impl.h" #include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/fastmath.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::poisson_distribution: // Generates discrete variates conforming to a Poisson distribution. @@ -165,9 +165,9 @@ typename poisson_distribution<IntType>::result_type poisson_distribution<IntType>::operator()( URBG& g, // NOLINT(runtime/references) const param_type& p) { - using random_internal::PositiveValueT; - using random_internal::RandU64ToDouble; - using random_internal::SignedValueT; + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using random_internal::GenerateSignedTag; if (p.split_ != 0) { // Use Knuth's algorithm with range splitting to avoid floating-point @@ -187,7 +187,8 @@ poisson_distribution<IntType>::operator()( for (int split = p.split_; split > 0; --split) { double r = 1.0; do { - r *= RandU64ToDouble<PositiveValueT, true>(fast_u64_(g)); + r *= GenerateRealFromBits<double, GeneratePositiveTag, true>( + fast_u64_(g)); // U(-1, 0) ++n; } while (r > p.emu_); --n; @@ -206,10 +207,11 @@ poisson_distribution<IntType>::operator()( // and k = max(f). const double a = p.mean_ + 0.5; for (;;) { - const double u = - RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)); // (0, 1) - const double v = - RandU64ToDouble<SignedValueT, false>(fast_u64_(g)); // (-1, 1) + const double u = GenerateRealFromBits<double, GeneratePositiveTag, false>( + fast_u64_(g)); // U(0, 1) + const double v = GenerateRealFromBits<double, GenerateSignedTag, false>( + fast_u64_(g)); // U(-1, 1) + const double x = std::floor(p.s_ * v / u + a); if (x < 0) continue; // f(negative) = 0 const double rhs = x * p.lmu_; @@ -250,7 +252,7 @@ std::basic_istream<CharT, Traits>& operator>>( return is; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_POISSON_DISTRIBUTION_H_ diff --git a/absl/random/poisson_distribution_test.cc b/absl/random/poisson_distribution_test.cc index 6d68999a..9d215fbc 100644 --- a/absl/random/poisson_distribution_test.cc +++ b/absl/random/poisson_distribution_test.cc @@ -339,7 +339,7 @@ std::string ZParamName(const ::testing::TestParamInfo<ZParam>& info) { return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); } -INSTANTIATE_TEST_SUITE_P(, PoissonDistributionZTest, +INSTANTIATE_TEST_SUITE_P(All, PoissonDistributionZTest, ::testing::ValuesIn(GetZParams()), ZParamName); // The PoissonDistributionChiSquaredTest class provides a basic test framework @@ -468,7 +468,7 @@ TEST_P(PoissonDistributionChiSquaredTest, AbslPoissonDistribution) { EXPECT_LE(failures, 4); } -INSTANTIATE_TEST_SUITE_P(, PoissonDistributionChiSquaredTest, +INSTANTIATE_TEST_SUITE_P(All, PoissonDistributionChiSquaredTest, ::testing::Values(0.5, 1.0, 2.0, 10.0, 50.0, 51.0, 200.0)); diff --git a/absl/random/random.h b/absl/random/random.h index 72a4cf5b..c8f326e6 100644 --- a/absl/random/random.h +++ b/absl/random/random.h @@ -41,7 +41,7 @@ #include "absl/random/seed_sequences.h" // IWYU pragma: export namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // absl::BitGen @@ -183,7 +183,7 @@ using InsecureBitGen = // discards the intermediate results. // --------------------------------------------------------------------------- -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_RANDOM_H_ diff --git a/absl/random/seed_gen_exception.cc b/absl/random/seed_gen_exception.cc index 5f01a30c..fdcb54a8 100644 --- a/absl/random/seed_gen_exception.cc +++ b/absl/random/seed_gen_exception.cc @@ -19,7 +19,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN static constexpr const char kExceptionMessage[] = "Failed generating seed-material for URBG."; @@ -42,5 +42,5 @@ void ThrowSeedGenException() { } } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/seed_gen_exception.h b/absl/random/seed_gen_exception.h index 52afe6cc..53539005 100644 --- a/absl/random/seed_gen_exception.h +++ b/absl/random/seed_gen_exception.h @@ -28,8 +28,10 @@ #include <exception> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN //------------------------------------------------------------------------------ // SeedGenException @@ -47,7 +49,7 @@ namespace random_internal { [[noreturn]] void ThrowSeedGenException(); } // namespace random_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_SEED_GEN_EXCEPTION_H_ diff --git a/absl/random/seed_sequences.cc b/absl/random/seed_sequences.cc index fb7eb8d1..426eafd3 100644 --- a/absl/random/seed_sequences.cc +++ b/absl/random/seed_sequences.cc @@ -17,7 +17,7 @@ #include "absl/random/internal/pool_urbg.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN SeedSeq MakeSeedSeq() { SeedSeq::result_type seed_material[8]; @@ -25,5 +25,5 @@ SeedSeq MakeSeedSeq() { return SeedSeq(std::begin(seed_material), std::end(seed_material)); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/seed_sequences.h b/absl/random/seed_sequences.h index 73d075c0..ff1340cc 100644 --- a/absl/random/seed_sequences.h +++ b/absl/random/seed_sequences.h @@ -34,7 +34,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // absl::SeedSeq @@ -104,7 +104,7 @@ SeedSeq CreateSeedSeqFrom(URBG* urbg) { // SeedSeq MakeSeedSeq(); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_SEED_SEQUENCES_H_ diff --git a/absl/random/uniform_int_distribution.h b/absl/random/uniform_int_distribution.h index 95eb04a4..da66564a 100644 --- a/absl/random/uniform_int_distribution.h +++ b/absl/random/uniform_int_distribution.h @@ -34,13 +34,13 @@ #include <type_traits> #include "absl/base/optimization.h" -#include "absl/random/internal/distribution_impl.h" #include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/iostream_state_saver.h" #include "absl/random/internal/traits.h" +#include "absl/random/internal/wide_multiply.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::uniform_int_distribution<T> // @@ -269,7 +269,7 @@ uniform_int_distribution<IntType>::Generate( return helper::hi(product); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_UNIFORM_INT_DISTRIBUTION_H_ diff --git a/absl/random/uniform_real_distribution.h b/absl/random/uniform_real_distribution.h index 0ea3163a..5ba17b23 100644 --- a/absl/random/uniform_real_distribution.h +++ b/absl/random/uniform_real_distribution.h @@ -39,12 +39,13 @@ #include <limits> #include <type_traits> -#include "absl/random/internal/distribution_impl.h" +#include "absl/meta/type_traits.h" #include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::uniform_real_distribution<T> // @@ -57,7 +58,7 @@ inline namespace lts_2019_08_08 { // // // Use the distribution to produce a value between 0.0 (inclusive) // // and 1.0 (exclusive). -// int value = absl::uniform_real_distribution<double>(0, 1)(gen); +// double value = absl::uniform_real_distribution<double>(0, 1)(gen); // template <typename RealType = double> class uniform_real_distribution { @@ -77,6 +78,7 @@ class uniform_real_distribution { // is not possible, so value generation cannot use the full range of the // real type. assert(range_ <= (std::numeric_limits<result_type>::max)()); + assert(std::isfinite(range_)); } result_type a() const { return lo_; } @@ -152,10 +154,15 @@ template <typename URBG> typename uniform_real_distribution<RealType>::result_type uniform_real_distribution<RealType>::operator()( URBG& gen, const param_type& p) { // NOLINT(runtime/references) - using random_internal::PositiveValueT; + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using real_type = + absl::conditional_t<std::is_same<RealType, float>::value, float, double>; + while (true) { - const result_type sample = random_internal::RandU64ToReal< - result_type>::template Value<PositiveValueT, true>(fast_u64_(gen)); + const result_type sample = + GenerateRealFromBits<real_type, GeneratePositiveTag, true>( + fast_u64_(gen)); const result_type res = p.a() + (sample * p.range_); if (res < p.b() || p.range_ <= 0 || !std::isfinite(p.range_)) { return res; @@ -189,7 +196,7 @@ std::basic_istream<CharT, Traits>& operator>>( } return is; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_ diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc index 597f0ee5..a56374a6 100644 --- a/absl/random/uniform_real_distribution_test.cc +++ b/absl/random/uniform_real_distribution_test.cc @@ -54,7 +54,12 @@ namespace { template <typename RealType> class UniformRealDistributionTest : public ::testing::Test {}; +#if defined(__EMSCRIPTEN__) +using RealTypes = ::testing::Types<float, double>; +#else using RealTypes = ::testing::Types<float, double, long double>; +#endif // defined(__EMSCRIPTEN__) + TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes); TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { @@ -156,6 +161,10 @@ TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { } } +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4756) // Constant arithmetic overflow. +#endif TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) { #if GTEST_HAS_DEATH_TEST // Hi < Lo @@ -190,6 +199,9 @@ TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) { } #endif // NDEBUG } +#ifdef _MSC_VER +#pragma warning(pop) // warning(disable:4756) +#endif TYPED_TEST(UniformRealDistributionTest, TestMoments) { constexpr int kSize = 1000000; diff --git a/absl/random/zipf_distribution.h b/absl/random/zipf_distribution.h index bba98e88..22ebc756 100644 --- a/absl/random/zipf_distribution.h +++ b/absl/random/zipf_distribution.h @@ -26,7 +26,7 @@ #include "absl/random/uniform_real_distribution.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::zipf_distribution produces random integer-values in the range [0, k], // distributed according to the discrete probability function: @@ -265,7 +265,7 @@ std::basic_istream<CharT, Traits>& operator>>( return is; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_ZIPF_DISTRIBUTION_H_ diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel new file mode 100644 index 00000000..2b83077d --- /dev/null +++ b/absl/status/BUILD.bazel @@ -0,0 +1,65 @@ +# +# Copyright 2017 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This package contains `absl::Status`. +# It will expand later to have utilities around `Status` like `StatusOr`, +# `StatusBuilder` and macros. + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "status", + srcs = [ + "status.cc", + "status_payload_printer.cc", + ], + hdrs = [ + "status.h", + "status_payload_printer.h", + ], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/container:inlined_vector", + "//absl/debugging:stacktrace", + "//absl/debugging:symbolize", + "//absl/strings", + "//absl/strings:cord", + "//absl/strings:str_format", + "//absl/types:optional", + ], +) + +cc_test( + name = "status_test", + srcs = ["status_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":status", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt new file mode 100644 index 00000000..f05cee5e --- /dev/null +++ b/absl/status/CMakeLists.txt @@ -0,0 +1,52 @@ +# +# Copyright 2020 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +absl_cc_library( + NAME + status + HDRS + "status.h" + SRCS + "status.cc" + "status_payload_printer.h" + "status_payload_printer.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers + absl::raw_logging_internal + absl::inlined_vector + absl::stacktrace + absl::symbolize + absl::strings + absl::cord + absl::str_format + absl::optional + PUBLIC +) + +absl_cc_test( + NAME + status_test + SRCS + "status_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::status + absl::strings + gmock_main +) diff --git a/absl/status/status.cc b/absl/status/status.cc new file mode 100644 index 00000000..bbc1895e --- /dev/null +++ b/absl/status/status.cc @@ -0,0 +1,439 @@ +// 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/status/status.h" + +#include <cassert> + +#include "absl/base/internal/raw_logging.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" +#include "absl/status/status_payload_printer.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_split.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// The implementation was intentionally kept same as util::error::Code_Name() +// to ease the migration. +std::string StatusCodeToString(StatusCode code) { + switch (code) { + case StatusCode::kOk: + return "OK"; + case StatusCode::kCancelled: + return "CANCELLED"; + case StatusCode::kUnknown: + return "UNKNOWN"; + case StatusCode::kInvalidArgument: + return "INVALID_ARGUMENT"; + case StatusCode::kDeadlineExceeded: + return "DEADLINE_EXCEEDED"; + case StatusCode::kNotFound: + return "NOT_FOUND"; + case StatusCode::kAlreadyExists: + return "ALREADY_EXISTS"; + case StatusCode::kPermissionDenied: + return "PERMISSION_DENIED"; + case StatusCode::kUnauthenticated: + return "UNAUTHENTICATED"; + case StatusCode::kResourceExhausted: + return "RESOURCE_EXHAUSTED"; + case StatusCode::kFailedPrecondition: + return "FAILED_PRECONDITION"; + case StatusCode::kAborted: + return "ABORTED"; + case StatusCode::kOutOfRange: + return "OUT_OF_RANGE"; + case StatusCode::kUnimplemented: + return "UNIMPLEMENTED"; + case StatusCode::kInternal: + return "INTERNAL"; + case StatusCode::kUnavailable: + return "UNAVAILABLE"; + case StatusCode::kDataLoss: + return "DATA_LOSS"; + default: + return ""; + } +} + +std::ostream& operator<<(std::ostream& os, StatusCode code) { + return os << StatusCodeToString(code); +} + +namespace status_internal { + +static int FindPayloadIndexByUrl(const Payloads* payloads, + absl::string_view type_url) { + if (payloads == nullptr) return -1; + + for (int i = 0; i < payloads->size(); ++i) { + if ((*payloads)[i].type_url == type_url) return i; + } + + return -1; +} + +// Convert canonical code to a value known to this binary. +absl::StatusCode MapToLocalCode(int value) { + absl::StatusCode code = static_cast<absl::StatusCode>(value); + switch (code) { + case absl::StatusCode::kOk: + case absl::StatusCode::kCancelled: + case absl::StatusCode::kUnknown: + case absl::StatusCode::kInvalidArgument: + case absl::StatusCode::kDeadlineExceeded: + case absl::StatusCode::kNotFound: + case absl::StatusCode::kAlreadyExists: + case absl::StatusCode::kPermissionDenied: + case absl::StatusCode::kResourceExhausted: + case absl::StatusCode::kFailedPrecondition: + case absl::StatusCode::kAborted: + case absl::StatusCode::kOutOfRange: + case absl::StatusCode::kUnimplemented: + case absl::StatusCode::kInternal: + case absl::StatusCode::kUnavailable: + case absl::StatusCode::kDataLoss: + case absl::StatusCode::kUnauthenticated: + return code; + default: + return absl::StatusCode::kUnknown; + } +} +} // namespace status_internal + +absl::optional<absl::Cord> Status::GetPayload( + absl::string_view type_url) const { + const auto* payloads = GetPayloads(); + int index = status_internal::FindPayloadIndexByUrl(payloads, type_url); + if (index != -1) return (*payloads)[index].payload; + + return absl::nullopt; +} + +void Status::SetPayload(absl::string_view type_url, absl::Cord payload) { + if (ok()) return; + + PrepareToModify(); + + status_internal::StatusRep* rep = RepToPointer(rep_); + if (!rep->payloads) { + rep->payloads = absl::make_unique<status_internal::Payloads>(); + } + + int index = + status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url); + if (index != -1) { + (*rep->payloads)[index].payload = std::move(payload); + return; + } + + rep->payloads->push_back({std::string(type_url), std::move(payload)}); +} + +bool Status::ErasePayload(absl::string_view type_url) { + int index = status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url); + if (index != -1) { + GetPayloads()->erase(GetPayloads()->begin() + index); + return true; + } + + return false; +} + +void Status::ForEachPayload( + const std::function<void(absl::string_view, const absl::Cord&)>& visitor) + const { + if (auto* payloads = GetPayloads()) { + bool in_reverse = + payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6; + + for (int index = 0; index < payloads->size(); ++index) { + const auto& elem = + (*payloads)[in_reverse ? payloads->size() - 1 - index : index]; + +#ifdef NDEBUG + visitor(elem.type_url, elem.payload); +#else + // In debug mode invaldiate the type url to prevent users from relying on + // this std::string lifetime. + + // NOLINTNEXTLINE intentional extra conversion to force temporary. + visitor(std::string(elem.type_url), elem.payload); +#endif // NDEBUG + } + } +} + +const std::string* Status::EmptyString() { + static std::string* empty_string = new std::string(); + return empty_string; +} + +constexpr const char Status::kMovedFromString[]; + +const std::string* Status::MovedFromString() { + static std::string* moved_from_string = new std::string(kMovedFromString); + return moved_from_string; +} + +void Status::UnrefNonInlined(uintptr_t rep) { + status_internal::StatusRep* r = RepToPointer(rep); + // Fast path: if ref==1, there is no need for a RefCountDec (since + // this is the only reference and therefore no other thread is + // allowed to be mucking with r). + if (r->ref.load(std::memory_order_acquire) == 1 || + r->ref.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) { + delete r; + } +} + +uintptr_t Status::NewRep(absl::StatusCode code, absl::string_view msg, + std::unique_ptr<status_internal::Payloads> payloads) { + status_internal::StatusRep* rep = new status_internal::StatusRep; + rep->ref.store(1, std::memory_order_relaxed); + rep->code = code; + rep->message.assign(msg.data(), msg.size()); + rep->payloads = std::move(payloads); + return PointerToRep(rep); +} + +Status::Status(absl::StatusCode code, absl::string_view msg) + : rep_(CodeToInlinedRep(code)) { + if (code != absl::StatusCode::kOk && !msg.empty()) { + rep_ = NewRep(code, msg, nullptr); + } +} + +int Status::raw_code() const { + if (IsInlined(rep_)) { + return static_cast<int>(InlinedRepToCode(rep_)); + } + status_internal::StatusRep* rep = RepToPointer(rep_); + return static_cast<int>(rep->code); +} + +absl::StatusCode Status::code() const { + return status_internal::MapToLocalCode(raw_code()); +} + +void Status::PrepareToModify() { + ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status."); + if (IsInlined(rep_)) { + rep_ = NewRep(static_cast<absl::StatusCode>(raw_code()), + absl::string_view(), nullptr); + return; + } + + uintptr_t rep_i = rep_; + status_internal::StatusRep* rep = RepToPointer(rep_); + if (rep->ref.load(std::memory_order_acquire) != 1) { + std::unique_ptr<status_internal::Payloads> payloads; + if (rep->payloads) { + payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads); + } + rep_ = NewRep(rep->code, message(), std::move(payloads)); + UnrefNonInlined(rep_i); + } +} + +bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) { + if (IsInlined(a.rep_) != IsInlined(b.rep_)) return false; + if (a.message() != b.message()) return false; + if (a.raw_code() != b.raw_code()) return false; + if (a.GetPayloads() == b.GetPayloads()) return true; + + const status_internal::Payloads no_payloads; + const status_internal::Payloads* larger_payloads = + a.GetPayloads() ? a.GetPayloads() : &no_payloads; + const status_internal::Payloads* smaller_payloads = + b.GetPayloads() ? b.GetPayloads() : &no_payloads; + if (larger_payloads->size() < smaller_payloads->size()) { + std::swap(larger_payloads, smaller_payloads); + } + if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false; + // Payloads can be ordered differently, so we can't just compare payload + // vectors. + for (const auto& payload : *larger_payloads) { + + bool found = false; + for (const auto& other_payload : *smaller_payloads) { + if (payload.type_url == other_payload.type_url) { + if (payload.payload != other_payload.payload) { + return false; + } + found = true; + break; + } + } + if (!found) return false; + } + return true; +} + +std::string Status::ToStringSlow() const { + std::string text; + absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message()); + status_internal::StatusPayloadPrinter printer = + status_internal::GetStatusPayloadPrinter(); + this->ForEachPayload([&](absl::string_view type_url, + const absl::Cord& payload) { + absl::optional<std::string> result; + if (printer) result = printer(type_url, payload); + absl::StrAppend( + &text, " [", type_url, "='", + result.has_value() ? *result : absl::CHexEscape(std::string(payload)), + "']"); + }); + + return text; +} + +std::ostream& operator<<(std::ostream& os, const Status& x) { + os << x.ToString(); + return os; +} + +Status AbortedError(absl::string_view message) { + return Status(absl::StatusCode::kAborted, message); +} + +Status AlreadyExistsError(absl::string_view message) { + return Status(absl::StatusCode::kAlreadyExists, message); +} + +Status CancelledError(absl::string_view message) { + return Status(absl::StatusCode::kCancelled, message); +} + +Status DataLossError(absl::string_view message) { + return Status(absl::StatusCode::kDataLoss, message); +} + +Status DeadlineExceededError(absl::string_view message) { + return Status(absl::StatusCode::kDeadlineExceeded, message); +} + +Status FailedPreconditionError(absl::string_view message) { + return Status(absl::StatusCode::kFailedPrecondition, message); +} + +Status InternalError(absl::string_view message) { + return Status(absl::StatusCode::kInternal, message); +} + +Status InvalidArgumentError(absl::string_view message) { + return Status(absl::StatusCode::kInvalidArgument, message); +} + +Status NotFoundError(absl::string_view message) { + return Status(absl::StatusCode::kNotFound, message); +} + +Status OutOfRangeError(absl::string_view message) { + return Status(absl::StatusCode::kOutOfRange, message); +} + +Status PermissionDeniedError(absl::string_view message) { + return Status(absl::StatusCode::kPermissionDenied, message); +} + +Status ResourceExhaustedError(absl::string_view message) { + return Status(absl::StatusCode::kResourceExhausted, message); +} + +Status UnauthenticatedError(absl::string_view message) { + return Status(absl::StatusCode::kUnauthenticated, message); +} + +Status UnavailableError(absl::string_view message) { + return Status(absl::StatusCode::kUnavailable, message); +} + +Status UnimplementedError(absl::string_view message) { + return Status(absl::StatusCode::kUnimplemented, message); +} + +Status UnknownError(absl::string_view message) { + return Status(absl::StatusCode::kUnknown, message); +} + +bool IsAborted(const Status& status) { + return status.code() == absl::StatusCode::kAborted; +} + +bool IsAlreadyExists(const Status& status) { + return status.code() == absl::StatusCode::kAlreadyExists; +} + +bool IsCancelled(const Status& status) { + return status.code() == absl::StatusCode::kCancelled; +} + +bool IsDataLoss(const Status& status) { + return status.code() == absl::StatusCode::kDataLoss; +} + +bool IsDeadlineExceeded(const Status& status) { + return status.code() == absl::StatusCode::kDeadlineExceeded; +} + +bool IsFailedPrecondition(const Status& status) { + return status.code() == absl::StatusCode::kFailedPrecondition; +} + +bool IsInternal(const Status& status) { + return status.code() == absl::StatusCode::kInternal; +} + +bool IsInvalidArgument(const Status& status) { + return status.code() == absl::StatusCode::kInvalidArgument; +} + +bool IsNotFound(const Status& status) { + return status.code() == absl::StatusCode::kNotFound; +} + +bool IsOutOfRange(const Status& status) { + return status.code() == absl::StatusCode::kOutOfRange; +} + +bool IsPermissionDenied(const Status& status) { + return status.code() == absl::StatusCode::kPermissionDenied; +} + +bool IsResourceExhausted(const Status& status) { + return status.code() == absl::StatusCode::kResourceExhausted; +} + +bool IsUnauthenticated(const Status& status) { + return status.code() == absl::StatusCode::kUnauthenticated; +} + +bool IsUnavailable(const Status& status) { + return status.code() == absl::StatusCode::kUnavailable; +} + +bool IsUnimplemented(const Status& status) { + return status.code() == absl::StatusCode::kUnimplemented; +} + +bool IsUnknown(const Status& status) { + return status.code() == absl::StatusCode::kUnknown; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/status/status.h b/absl/status/status.h new file mode 100644 index 00000000..9706d4ba --- /dev/null +++ b/absl/status/status.h @@ -0,0 +1,428 @@ +// 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_STATUS_STATUS_H_ +#define ABSL_STATUS_STATUS_H_ + +#include <iostream> +#include <string> + +#include "absl/container/inlined_vector.h" +#include "absl/strings/cord.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +enum class StatusCode : int { + kOk = 0, + kCancelled = 1, + kUnknown = 2, + kInvalidArgument = 3, + kDeadlineExceeded = 4, + kNotFound = 5, + kAlreadyExists = 6, + kPermissionDenied = 7, + kResourceExhausted = 8, + kFailedPrecondition = 9, + kAborted = 10, + kOutOfRange = 11, + kUnimplemented = 12, + kInternal = 13, + kUnavailable = 14, + kDataLoss = 15, + kUnauthenticated = 16, + kDoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20 +}; + +// Returns the name for the status code, or "" if it is an unknown value. +std::string StatusCodeToString(StatusCode code); + +// Streams StatusCodeToString(code) to `os`. +std::ostream& operator<<(std::ostream& os, StatusCode code); + +namespace status_internal { + +// Container for status payloads. +struct Payload { + std::string type_url; + absl::Cord payload; +}; + +using Payloads = absl::InlinedVector<Payload, 1>; + +// Reference-counted representation of Status data. +struct StatusRep { + std::atomic<int32_t> ref; + absl::StatusCode code; + std::string message; + std::unique_ptr<status_internal::Payloads> payloads; +}; + +absl::StatusCode MapToLocalCode(int value); +} // namespace status_internal + +class ABSL_MUST_USE_RESULT Status final { + public: + // Creates an OK status with no message or payload. + Status(); + + // Create a status in the canonical error space with the specified code and + // error message. If `code == util::error::OK`, `msg` is ignored and an + // object identical to an OK status is constructed. + // + // `msg` must be in UTF-8. The implementation may complain (e.g., + // by printing a warning) if it is not. + Status(absl::StatusCode code, absl::string_view msg); + + Status(const Status&); + Status& operator=(const Status& x); + + // Move operations. + // The moved-from state is valid but unspecified. + Status(Status&&) noexcept; + Status& operator=(Status&&); + + ~Status(); + + // If `this->ok()`, stores `new_status` into *this. If `!this->ok()`, + // preserves the current data. May, in the future, augment the current status + // with additional information about `new_status`. + // + // Convenient way of keeping track of the first error encountered. + // Instead of: + // if (overall_status.ok()) overall_status = new_status + // Use: + // overall_status.Update(new_status); + // + // Style guide exception for rvalue reference granted in CL 153567220. + void Update(const Status& new_status); + void Update(Status&& new_status); + + // Returns true if the Status is OK. + ABSL_MUST_USE_RESULT bool ok() const; + + // Returns the (canonical) error code. + absl::StatusCode code() const; + + // Returns the raw (canonical) error code which could be out of the range of + // the local `absl::StatusCode` enum. NOTE: This should only be called when + // converting to wire format. Use `code` for error handling. + int raw_code() const; + + // Returns the error message. Note: prefer ToString() for debug logging. + // This message rarely describes the error code. It is not unusual for the + // error message to be the empty std::string. + absl::string_view message() const; + + friend bool operator==(const Status&, const Status&); + friend bool operator!=(const Status&, const Status&); + + // Returns a combination of the error code name, the message and the payloads. + // You can expect the code name and the message to be substrings of the + // result, and the payloads to be printed by the registered printer extensions + // if they are recognized. + // WARNING: Do not depend on the exact format of the result of `ToString()` + // which is subject to change. + std::string ToString() const; + + // Ignores any errors. This method does nothing except potentially suppress + // complaints from any tools that are checking that errors are not dropped on + // the floor. + void IgnoreError() const; + + // Swap the contents of `a` with `b` + friend void swap(Status& a, Status& b); + + // Payload management APIs + + // Type URL should be unique and follow the naming convention below: + // The idea of type URL comes from `google.protobuf.Any` + // (https://developers.google.com/protocol-buffers/docs/proto3#any). The + // type URL should be globally unique and follow the format of URL + // (https://en.wikipedia.org/wiki/URL). The default type URL for a given + // protobuf message type is "type.googleapis.com/packagename.messagename". For + // other custom wire formats, users should define the format of type URL in a + // similar practice so as to minimize the chance of conflict between type + // URLs. Users should make sure that the type URL can be mapped to a concrete + // C++ type if they want to deserialize the payload and read it effectively. + + // Gets the payload based for `type_url` key, if it is present. + absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const; + + // Sets the payload for `type_url` key for a non-ok status, overwriting any + // existing payload for `type_url`. + // + // NOTE: Does nothing if the Status is ok. + void SetPayload(absl::string_view type_url, absl::Cord payload); + + // Erases the payload corresponding to the `type_url` key. Returns true if + // the payload was present. + bool ErasePayload(absl::string_view type_url); + + // Iterates over the stored payloads and calls `visitor(type_key, payload)` + // for each one. + // + // NOTE: The order of calls to `visitor` is not specified and may change at + // any time. + // + // NOTE: Any mutation on the same 'Status' object during visitation is + // forbidden and could result in undefined behavior. + void ForEachPayload( + const std::function<void(absl::string_view, const absl::Cord&)>& visitor) + const; + + private: + friend Status CancelledError(); + + // Creates a status in the canonical error space with the specified + // code, and an empty error message. + explicit Status(absl::StatusCode code); + + static void UnrefNonInlined(uintptr_t rep); + static void Ref(uintptr_t rep); + static void Unref(uintptr_t rep); + + // REQUIRES: !ok() + // Ensures rep_ is not shared with any other Status. + void PrepareToModify(); + + const status_internal::Payloads* GetPayloads() const; + status_internal::Payloads* GetPayloads(); + + // Takes ownership of payload. + static uintptr_t NewRep(absl::StatusCode code, absl::string_view msg, + std::unique_ptr<status_internal::Payloads> payload); + static bool EqualsSlow(const absl::Status& a, const absl::Status& b); + + // MSVC 14.0 limitation requires the const. + static constexpr const char kMovedFromString[] = + "Status accessed after move."; + + static const std::string* EmptyString(); + static const std::string* MovedFromString(); + + // Returns whether rep contains an inlined representation. + // See rep_ for details. + static bool IsInlined(uintptr_t rep); + + // Indicates whether this Status was the rhs of a move operation. See rep_ + // for details. + static bool IsMovedFrom(uintptr_t rep); + static uintptr_t MovedFromRep(); + + // Convert between error::Code and the inlined uintptr_t representation used + // by rep_. See rep_ for details. + static uintptr_t CodeToInlinedRep(absl::StatusCode code); + static absl::StatusCode InlinedRepToCode(uintptr_t rep); + + // Converts between StatusRep* and the external uintptr_t representation used + // by rep_. See rep_ for details. + static uintptr_t PointerToRep(status_internal::StatusRep* r); + static status_internal::StatusRep* RepToPointer(uintptr_t r); + + // Returns std::string for non-ok Status. + std::string ToStringSlow() const; + + // Status supports two different representations. + // - When the low bit is off it is an inlined representation. + // It uses the canonical error space, no message or payload. + // The error code is (rep_ >> 2). + // The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom(). + // - When the low bit is on it is an external representation. + // In this case all the data comes from a heap allocated Rep object. + // (rep_ - 1) is a status_internal::StatusRep* pointer to that structure. + uintptr_t rep_; +}; + +// Returns an OK status, equivalent to a default constructed instance. +Status OkStatus(); + +// Prints a human-readable representation of `x` to `os`. +std::ostream& operator<<(std::ostream& os, const Status& x); + +// ----------------------------------------------------------------- +// Implementation details follow + +inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {} + +inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {} + +inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); } + +inline Status& Status::operator=(const Status& x) { + uintptr_t old_rep = rep_; + if (x.rep_ != old_rep) { + Ref(x.rep_); + rep_ = x.rep_; + Unref(old_rep); + } + return *this; +} + +inline Status::Status(Status&& x) noexcept : rep_(x.rep_) { + x.rep_ = MovedFromRep(); +} + +inline Status& Status::operator=(Status&& x) { + uintptr_t old_rep = rep_; + rep_ = x.rep_; + x.rep_ = MovedFromRep(); + Unref(old_rep); + return *this; +} + +inline void Status::Update(const Status& new_status) { + if (ok()) { + *this = new_status; + } +} + +inline void Status::Update(Status&& new_status) { + if (ok()) { + *this = std::move(new_status); + } +} + +inline Status::~Status() { Unref(rep_); } + +inline bool Status::ok() const { + return rep_ == CodeToInlinedRep(absl::StatusCode::kOk); +} + +inline absl::string_view Status::message() const { + return !IsInlined(rep_) + ? RepToPointer(rep_)->message + : (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString) + : absl::string_view()); +} + +inline bool operator==(const Status& lhs, const Status& rhs) { + return lhs.rep_ == rhs.rep_ || Status::EqualsSlow(lhs, rhs); +} + +inline bool operator!=(const Status& lhs, const Status& rhs) { + return !(lhs == rhs); +} + +inline std::string Status::ToString() const { + return ok() ? "OK" : ToStringSlow(); +} + +inline void Status::IgnoreError() const { + // no-op +} + +inline void swap(absl::Status& a, absl::Status& b) { + using std::swap; + swap(a.rep_, b.rep_); +} + +inline const status_internal::Payloads* Status::GetPayloads() const { + return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get(); +} + +inline status_internal::Payloads* Status::GetPayloads() { + return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get(); +} + +inline bool Status::IsInlined(uintptr_t rep) { return (rep & 1) == 0; } + +inline bool Status::IsMovedFrom(uintptr_t rep) { + return IsInlined(rep) && (rep & 2) != 0; +} + +inline uintptr_t Status::MovedFromRep() { + return CodeToInlinedRep(absl::StatusCode::kInternal) | 2; +} + +inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) { + return static_cast<uintptr_t>(code) << 2; +} + +inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) { + assert(IsInlined(rep)); + return static_cast<absl::StatusCode>(rep >> 2); +} + +inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) { + assert(!IsInlined(rep)); + return reinterpret_cast<status_internal::StatusRep*>(rep - 1); +} + +inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) { + return reinterpret_cast<uintptr_t>(rep) + 1; +} + +inline void Status::Ref(uintptr_t rep) { + if (!IsInlined(rep)) { + RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed); + } +} + +inline void Status::Unref(uintptr_t rep) { + if (!IsInlined(rep)) { + UnrefNonInlined(rep); + } +} + +inline Status OkStatus() { return Status(); } + +// Each of the functions below creates a Status object with a particular error +// code and the given message. The error code of the returned status object +// matches the name of the function. +Status AbortedError(absl::string_view message); +Status AlreadyExistsError(absl::string_view message); +Status CancelledError(absl::string_view message); +Status DataLossError(absl::string_view message); +Status DeadlineExceededError(absl::string_view message); +Status FailedPreconditionError(absl::string_view message); +Status InternalError(absl::string_view message); +Status InvalidArgumentError(absl::string_view message); +Status NotFoundError(absl::string_view message); +Status OutOfRangeError(absl::string_view message); +Status PermissionDeniedError(absl::string_view message); +Status ResourceExhaustedError(absl::string_view message); +Status UnauthenticatedError(absl::string_view message); +Status UnavailableError(absl::string_view message); +Status UnimplementedError(absl::string_view message); +Status UnknownError(absl::string_view message); + +// Creates a `Status` object with the `absl::StatusCode::kCancelled` error code +// and an empty message. It is provided only for efficiency, given that +// message-less kCancelled errors are common in the infrastructure. +inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); } + +// Each of the functions below returns true if the given status matches the +// error code implied by the function's name. +ABSL_MUST_USE_RESULT bool IsAborted(const Status& status); +ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status); +ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status); +ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status); +ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status); +ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status); +ABSL_MUST_USE_RESULT bool IsInternal(const Status& status); +ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status); +ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status); +ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status); +ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status); +ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_STATUS_H_ diff --git a/absl/status/status_payload_printer.cc b/absl/status/status_payload_printer.cc new file mode 100644 index 00000000..ad96d76a --- /dev/null +++ b/absl/status/status_payload_printer.cc @@ -0,0 +1,43 @@ +// 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/status/status_payload_printer.h" + +#include <atomic> + +#include "absl/base/attributes.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace status_internal { + +namespace { +// Tried constant initialized global variable but it doesn't work with Lexan +// (MSVC's `std::atomic` has trouble constant initializing). +std::atomic<StatusPayloadPrinter>& GetStatusPayloadPrinterStorage() { + ABSL_CONST_INIT static std::atomic<StatusPayloadPrinter> instance{nullptr}; + return instance; +} +} // namespace + +void SetStatusPayloadPrinter(StatusPayloadPrinter printer) { + GetStatusPayloadPrinterStorage().store(printer, std::memory_order_relaxed); +} + +StatusPayloadPrinter GetStatusPayloadPrinter() { + return GetStatusPayloadPrinterStorage().load(std::memory_order_relaxed); +} + +} // namespace status_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/status/status_payload_printer.h b/absl/status/status_payload_printer.h new file mode 100644 index 00000000..5e0937f6 --- /dev/null +++ b/absl/status/status_payload_printer.h @@ -0,0 +1,51 @@ +// 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_STATUS_STATUS_PAYLOAD_PRINTER_H_ +#define ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_ + +#include <string> + +#include "absl/strings/cord.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace status_internal { + +// By default, `Status::ToString` and `operator<<(Status)` print a payload by +// dumping the type URL and the raw bytes. To help debugging, we provide an +// extension point, which is a global printer function that can be set by users +// to specify how to print payloads. The function takes the type URL and the +// payload as input, and should return a valid human-readable string on success +// or `absl::nullopt` on failure (in which case it falls back to the default +// approach of printing the raw bytes). +// NOTE: This is an internal API and the design is subject to change in the +// future in a non-backward-compatible way. Since it's only meant for debugging +// purpose, you should not rely on it in any critical logic. +using StatusPayloadPrinter = absl::optional<std::string> (*)(absl::string_view, + const absl::Cord&); + +// Sets the global payload printer. Only one printer should be set per process. +// If multiple printers are set, it's undefined which one will be used. +void SetStatusPayloadPrinter(StatusPayloadPrinter); + +// Returns the global payload printer if previously set, otherwise `nullptr`. +StatusPayloadPrinter GetStatusPayloadPrinter(); + +} // namespace status_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_ diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc new file mode 100644 index 00000000..7cc65e45 --- /dev/null +++ b/absl/status/status_test.cc @@ -0,0 +1,401 @@ +// 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/status/status.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" + +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Optional; +using ::testing::UnorderedElementsAreArray; + +TEST(StatusCode, InsertionOperator) { + const absl::StatusCode code = absl::StatusCode::kUnknown; + std::ostringstream oss; + oss << code; + EXPECT_EQ(oss.str(), absl::StatusCodeToString(code)); +} + +// This structure holds the details for testing a single error code, +// its creator, and its classifier. +struct ErrorTest { + absl::StatusCode code; + using Creator = absl::Status (*)(absl::string_view); + using Classifier = bool (*)(const absl::Status&); + Creator creator; + Classifier classifier; +}; + +constexpr ErrorTest kErrorTests[]{ + {absl::StatusCode::kCancelled, absl::CancelledError, absl::IsCancelled}, + {absl::StatusCode::kUnknown, absl::UnknownError, absl::IsUnknown}, + {absl::StatusCode::kInvalidArgument, absl::InvalidArgumentError, + absl::IsInvalidArgument}, + {absl::StatusCode::kDeadlineExceeded, absl::DeadlineExceededError, + absl::IsDeadlineExceeded}, + {absl::StatusCode::kNotFound, absl::NotFoundError, absl::IsNotFound}, + {absl::StatusCode::kAlreadyExists, absl::AlreadyExistsError, + absl::IsAlreadyExists}, + {absl::StatusCode::kPermissionDenied, absl::PermissionDeniedError, + absl::IsPermissionDenied}, + {absl::StatusCode::kResourceExhausted, absl::ResourceExhaustedError, + absl::IsResourceExhausted}, + {absl::StatusCode::kFailedPrecondition, absl::FailedPreconditionError, + absl::IsFailedPrecondition}, + {absl::StatusCode::kAborted, absl::AbortedError, absl::IsAborted}, + {absl::StatusCode::kOutOfRange, absl::OutOfRangeError, absl::IsOutOfRange}, + {absl::StatusCode::kUnimplemented, absl::UnimplementedError, + absl::IsUnimplemented}, + {absl::StatusCode::kInternal, absl::InternalError, absl::IsInternal}, + {absl::StatusCode::kUnavailable, absl::UnavailableError, + absl::IsUnavailable}, + {absl::StatusCode::kDataLoss, absl::DataLossError, absl::IsDataLoss}, + {absl::StatusCode::kUnauthenticated, absl::UnauthenticatedError, + absl::IsUnauthenticated}, +}; + +TEST(Status, CreateAndClassify) { + for (const auto& test : kErrorTests) { + SCOPED_TRACE(absl::StatusCodeToString(test.code)); + + // Ensure that the creator does, in fact, create status objects with the + // expected error code and message. + std::string message = + absl::StrCat("error code ", test.code, " test message"); + absl::Status status = test.creator(message); + EXPECT_EQ(test.code, status.code()); + EXPECT_EQ(message, status.message()); + + // Ensure that the classifier returns true for a status produced by the + // creator. + EXPECT_TRUE(test.classifier(status)); + + // Ensure that the classifier returns false for status with a different + // code. + for (const auto& other : kErrorTests) { + if (other.code != test.code) { + EXPECT_FALSE(test.classifier(absl::Status(other.code, ""))) + << " other.code = " << other.code; + } + } + } +} + +TEST(Status, DefaultConstructor) { + absl::Status status; + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + EXPECT_EQ("", status.message()); +} + +TEST(Status, OkStatus) { + absl::Status status = absl::OkStatus(); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + EXPECT_EQ("", status.message()); +} + +TEST(Status, ConstructorWithCodeMessage) { + { + absl::Status status(absl::StatusCode::kCancelled, ""); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kCancelled, status.code()); + EXPECT_EQ("", status.message()); + } + { + absl::Status status(absl::StatusCode::kInternal, "message"); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + EXPECT_EQ("message", status.message()); + } +} + +TEST(Status, ConstructOutOfRangeCode) { + const int kRawCode = 9999; + absl::Status status(static_cast<absl::StatusCode>(kRawCode), ""); + EXPECT_EQ(absl::StatusCode::kUnknown, status.code()); + EXPECT_EQ(kRawCode, status.raw_code()); +} + +constexpr char kUrl1[] = "url.payload.1"; +constexpr char kUrl2[] = "url.payload.2"; +constexpr char kUrl3[] = "url.payload.3"; +constexpr char kUrl4[] = "url.payload.xx"; + +constexpr char kPayload1[] = "aaaaa"; +constexpr char kPayload2[] = "bbbbb"; +constexpr char kPayload3[] = "ccccc"; + +using PayloadsVec = std::vector<std::pair<std::string, absl::Cord>>; + +TEST(Status, TestGetSetPayload) { + absl::Status ok_status = absl::OkStatus(); + ok_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + ok_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + + EXPECT_FALSE(ok_status.GetPayload(kUrl1)); + EXPECT_FALSE(ok_status.GetPayload(kUrl2)); + + absl::Status bad_status(absl::StatusCode::kInternal, "fail"); + bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + + EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload1))); + EXPECT_THAT(bad_status.GetPayload(kUrl2), Optional(Eq(kPayload2))); + + EXPECT_FALSE(bad_status.GetPayload(kUrl3)); + + bad_status.SetPayload(kUrl1, absl::Cord(kPayload3)); + EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload3))); + + // Testing dynamically generated type_url + bad_status.SetPayload(absl::StrCat(kUrl1, ".1"), absl::Cord(kPayload1)); + EXPECT_THAT(bad_status.GetPayload(absl::StrCat(kUrl1, ".1")), + Optional(Eq(kPayload1))); +} + +TEST(Status, TestErasePayload) { + absl::Status bad_status(absl::StatusCode::kInternal, "fail"); + bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + bad_status.SetPayload(kUrl3, absl::Cord(kPayload3)); + + EXPECT_FALSE(bad_status.ErasePayload(kUrl4)); + + EXPECT_TRUE(bad_status.GetPayload(kUrl2)); + EXPECT_TRUE(bad_status.ErasePayload(kUrl2)); + EXPECT_FALSE(bad_status.GetPayload(kUrl2)); + EXPECT_FALSE(bad_status.ErasePayload(kUrl2)); + + EXPECT_TRUE(bad_status.ErasePayload(kUrl1)); + EXPECT_TRUE(bad_status.ErasePayload(kUrl3)); + + bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + EXPECT_TRUE(bad_status.ErasePayload(kUrl1)); +} + +TEST(Status, TestComparePayloads) { + absl::Status bad_status1(absl::StatusCode::kInternal, "fail"); + bad_status1.SetPayload(kUrl1, absl::Cord(kPayload1)); + bad_status1.SetPayload(kUrl2, absl::Cord(kPayload2)); + bad_status1.SetPayload(kUrl3, absl::Cord(kPayload3)); + + absl::Status bad_status2(absl::StatusCode::kInternal, "fail"); + bad_status2.SetPayload(kUrl2, absl::Cord(kPayload2)); + bad_status2.SetPayload(kUrl3, absl::Cord(kPayload3)); + bad_status2.SetPayload(kUrl1, absl::Cord(kPayload1)); + + EXPECT_EQ(bad_status1, bad_status2); +} + +PayloadsVec AllVisitedPayloads(const absl::Status& s) { + PayloadsVec result; + + s.ForEachPayload([&](absl::string_view type_url, const absl::Cord& payload) { + result.push_back(std::make_pair(std::string(type_url), payload)); + }); + + return result; +} + +TEST(Status, TestForEachPayload) { + absl::Status bad_status(absl::StatusCode::kInternal, "fail"); + bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + bad_status.SetPayload(kUrl3, absl::Cord(kPayload3)); + + int count = 0; + + bad_status.ForEachPayload( + [&count](absl::string_view, const absl::Cord&) { ++count; }); + + EXPECT_EQ(count, 3); + + PayloadsVec expected_payloads = {{kUrl1, absl::Cord(kPayload1)}, + {kUrl2, absl::Cord(kPayload2)}, + {kUrl3, absl::Cord(kPayload3)}}; + + // Test that we visit all the payloads in the status. + PayloadsVec visited_payloads = AllVisitedPayloads(bad_status); + EXPECT_THAT(visited_payloads, UnorderedElementsAreArray(expected_payloads)); + + // Test that visitation order is not consistent between run. + std::vector<absl::Status> scratch; + while (true) { + scratch.emplace_back(absl::StatusCode::kInternal, "fail"); + + scratch.back().SetPayload(kUrl1, absl::Cord(kPayload1)); + scratch.back().SetPayload(kUrl2, absl::Cord(kPayload2)); + scratch.back().SetPayload(kUrl3, absl::Cord(kPayload3)); + + if (AllVisitedPayloads(scratch.back()) != visited_payloads) { + break; + } + } +} + +TEST(Status, ToString) { + absl::Status s(absl::StatusCode::kInternal, "fail"); + EXPECT_EQ("INTERNAL: fail", s.ToString()); + s.SetPayload("foo", absl::Cord("bar")); + EXPECT_EQ("INTERNAL: fail [foo='bar']", s.ToString()); + s.SetPayload("bar", absl::Cord("\377")); + EXPECT_THAT(s.ToString(), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + HasSubstr("[bar='\\xff']"))); +} + +TEST(Status, CopyConstructor) { + { + absl::Status status; + absl::Status copy(status); + EXPECT_EQ(copy, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + absl::Status copy(status); + EXPECT_EQ(copy, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + status.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status copy(status); + EXPECT_EQ(copy, status); + } +} + +TEST(Status, CopyAssignment) { + absl::Status assignee; + { + absl::Status status; + assignee = status; + EXPECT_EQ(assignee, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + assignee = status; + EXPECT_EQ(assignee, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + status.SetPayload(kUrl1, absl::Cord(kPayload1)); + assignee = status; + EXPECT_EQ(assignee, status); + } +} + +TEST(Status, MoveConstructor) { + { + absl::Status status; + absl::Status copy(absl::Status{}); + EXPECT_EQ(copy, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + absl::Status copy( + absl::Status(absl::StatusCode::kInvalidArgument, "message")); + EXPECT_EQ(copy, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + status.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status copy1(status); + absl::Status copy2(std::move(status)); + EXPECT_EQ(copy1, copy2); + } +} + +TEST(Status, MoveAssignment) { + absl::Status assignee; + { + absl::Status status; + assignee = absl::Status(); + EXPECT_EQ(assignee, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + assignee = absl::Status(absl::StatusCode::kInvalidArgument, "message"); + EXPECT_EQ(assignee, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + status.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status copy(status); + assignee = std::move(status); + EXPECT_EQ(assignee, copy); + } +} + +TEST(Status, Update) { + absl::Status s; + s.Update(absl::OkStatus()); + EXPECT_TRUE(s.ok()); + const absl::Status a(absl::StatusCode::kCancelled, "message"); + s.Update(a); + EXPECT_EQ(s, a); + const absl::Status b(absl::StatusCode::kInternal, "other message"); + s.Update(b); + EXPECT_EQ(s, a); + s.Update(absl::OkStatus()); + EXPECT_EQ(s, a); + EXPECT_FALSE(s.ok()); +} + +TEST(Status, Equality) { + absl::Status ok; + absl::Status no_payload = absl::CancelledError("no payload"); + absl::Status one_payload = absl::InvalidArgumentError("one payload"); + one_payload.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status two_payloads = one_payload; + two_payloads.SetPayload(kUrl2, absl::Cord(kPayload2)); + const std::array<absl::Status, 4> status_arr = {ok, no_payload, one_payload, + two_payloads}; + for (int i = 0; i < status_arr.size(); i++) { + for (int j = 0; j < status_arr.size(); j++) { + if (i == j) { + EXPECT_TRUE(status_arr[i] == status_arr[j]); + EXPECT_FALSE(status_arr[i] != status_arr[j]); + } else { + EXPECT_TRUE(status_arr[i] != status_arr[j]); + EXPECT_FALSE(status_arr[i] == status_arr[j]); + } + } + } +} + +TEST(Status, Swap) { + auto test_swap = [](const absl::Status& s1, const absl::Status& s2) { + absl::Status copy1 = s1, copy2 = s2; + swap(copy1, copy2); + EXPECT_EQ(copy1, s2); + EXPECT_EQ(copy2, s1); + }; + const absl::Status ok; + const absl::Status no_payload(absl::StatusCode::kAlreadyExists, "no payload"); + absl::Status with_payload(absl::StatusCode::kInternal, "with payload"); + with_payload.SetPayload(kUrl1, absl::Cord(kPayload1)); + test_swap(ok, no_payload); + test_swap(no_payload, ok); + test_swap(ok, with_payload); + test_swap(with_payload, ok); + test_swap(no_payload, with_payload); + test_swap(with_payload, no_payload); +} + +} // namespace diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 20511a35..b950ec76 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -13,11 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", - "ABSL_EXCEPTIONS_FLAG", - "ABSL_EXCEPTIONS_FLAG_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -26,7 +25,7 @@ package( features = ["parse_headers"], ) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "strings", @@ -73,6 +72,7 @@ cc_library( "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", + "//absl/base:raw_logging_internal", "//absl/base:throw_delegate", "//absl/memory", "//absl/meta:type_traits", @@ -83,19 +83,23 @@ cc_library( cc_library( name = "internal", srcs = [ + "internal/escaping.cc", "internal/ostringstream.cc", "internal/utf8.cc", ], hdrs = [ "internal/char_map.h", + "internal/escaping.h", "internal/ostringstream.h", "internal/resize_uninitialized.h", "internal/utf8.h", ], copts = ABSL_DEFAULT_COPTS, deps = [ + "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", + "//absl/base:raw_logging_internal", "//absl/meta:type_traits", ], ) @@ -122,6 +126,7 @@ cc_test( copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], deps = [ + ":cord", ":strings", "//absl/base:core_headers", "//absl/container:fixed_array", @@ -140,7 +145,7 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":strings", - "//absl/base", + "//absl/base:raw_logging_internal", "@com_github_google_benchmark//:benchmark_main", ], ) @@ -225,8 +230,8 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":strings", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "@com_github_google_benchmark//:benchmark_main", ], ) @@ -235,8 +240,7 @@ cc_test( name = "string_view_test", size = "small", srcs = ["string_view_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], deps = [ ":strings", @@ -247,6 +251,74 @@ cc_test( ], ) +cc_library( + name = "cord_internal", + hdrs = ["internal/cord_internal.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":strings", + "//absl/meta:type_traits", + ], +) + +cc_library( + name = "cord", + srcs = [ + "cord.cc", + ], + hdrs = [ + "cord.h", + ], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord_internal", + ":internal", + ":str_format", + ":strings", + "//absl/base", + "//absl/base:base_internal", + "//absl/base:core_headers", + "//absl/base:endian", + "//absl/base:raw_logging_internal", + "//absl/container:fixed_array", + "//absl/container:inlined_vector", + "//absl/functional:function_ref", + "//absl/meta:type_traits", + ], +) + +cc_library( + name = "cord_test_helpers", + testonly = 1, + hdrs = [ + "cord_test_helpers.h", + ], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord", + ], +) + +cc_test( + name = "cord_test", + size = "medium", + srcs = ["cord_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord", + ":cord_test_helpers", + ":strings", + "//absl/base", + "//absl/base:config", + "//absl/base:endian", + "//absl/base:raw_logging_internal", + "//absl/container:fixed_array", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "substitute_test", size = "small", @@ -268,7 +340,7 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":strings", - "//absl/base", + "//absl/base:raw_logging_internal", "@com_github_google_benchmark//:benchmark_main", ], ) @@ -306,7 +378,7 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":strings", - "//absl/base", + "//absl/base:raw_logging_internal", "@com_github_google_benchmark//:benchmark_main", ], ) @@ -414,8 +486,10 @@ cc_test( deps = [ ":pow10_helper", ":strings", - "//absl/base", - "//absl/base:core_headers", + "//absl/base:config", + "//absl/base:raw_logging_internal", + "//absl/random", + "//absl/random:distributions", "@com_google_googletest//:gtest_main", ], ) @@ -428,7 +502,9 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":strings", - "//absl/base", + "//absl/base:raw_logging_internal", + "//absl/random", + "//absl/random:distributions", "@com_github_google_benchmark//:benchmark_main", ], ) @@ -474,7 +550,6 @@ cc_test( ":pow10_helper", ":str_format", ":strings", - "//absl/base", "@com_google_googletest//:gtest_main", ], ) @@ -488,7 +563,8 @@ cc_test( copts = ABSL_TEST_COPTS, deps = [ ":strings", - "//absl/base", + "//absl/base:config", + "//absl/base:raw_logging_internal", "@com_google_googletest//:gtest_main", ], ) @@ -503,7 +579,7 @@ cc_test( copts = ABSL_TEST_COPTS, deps = [ ":strings", - "//absl/base", + "//absl/base:config", "@com_google_googletest//:gtest_main", ], ) @@ -518,7 +594,6 @@ cc_test( ], deps = [ ":strings", - "//absl/base", "@com_github_google_benchmark//:benchmark_main", ], ) @@ -634,6 +709,7 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":str_format_internal", + "//absl/base:raw_logging_internal", "//absl/numeric:int128", "@com_google_googletest//:gtest_main", ], @@ -668,6 +744,7 @@ cc_library( srcs = ["internal/pow10_helper.cc"], hdrs = ["internal/pow10_helper.h"], visibility = ["//visibility:private"], + deps = ["//absl/base:config"], ) cc_test( diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index e63eec19..cebc5928 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -59,10 +59,11 @@ absl_cc_library( absl::config absl::core_headers absl::endian - absl::throw_delegate + absl::int128 absl::memory + absl::raw_logging_internal + absl::throw_delegate absl::type_traits - absl::int128 PUBLIC ) @@ -71,6 +72,8 @@ absl_cc_library( strings_internal HDRS "internal/char_map.h" + "internal/escaping.cc" + "internal/escaping.h" "internal/ostringstream.h" "internal/resize_uninitialized.h" "internal/utf8.h" @@ -80,8 +83,10 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::core_headers absl::endian + absl::raw_logging_internal absl::type_traits ) @@ -160,9 +165,6 @@ absl_cc_test( "string_view_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::strings absl::config @@ -276,9 +278,12 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings - absl::base absl::core_headers absl::pow10_helper + absl::config + absl::raw_logging_internal + absl::random_random + absl::random_distributions gmock_main ) @@ -317,7 +322,6 @@ absl_cc_test( DEPS absl::strings absl::str_format - absl::base absl::pow10_helper gmock_main ) @@ -332,7 +336,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings - absl::base + absl::config + absl::raw_logging_internal gmock_main ) @@ -347,7 +352,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings - absl::base + absl::config gmock_main ) @@ -465,6 +470,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format_internal + absl::raw_logging_internal absl::int128 gmock_main ) @@ -503,6 +509,8 @@ absl_cc_library( "internal/pow10_helper.cc" COPTS ${ABSL_TEST_COPTS} + DEPS + absl::config TESTONLY ) @@ -518,3 +526,57 @@ absl_cc_test( absl::str_format gmock_main ) + +absl_cc_library( + NAME + cord + HDRS + "cord.h" + SRCS + "cord.cc" + "internal/cord_internal.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::strings_internal + absl::base + absl::base_internal + absl::core_headers + absl::endian + absl::fixed_array + absl::function_ref + absl::inlined_vector + absl::raw_logging_internal + absl::type_traits + PUBLIC +) + +absl_cc_library( + NAME + cord_test_helpers + HDRS + "cord_test_helpers.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::cord + TESTONLY +) + +absl_cc_test( + NAME + cord_test + SRCS + "cord_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::cord + absl::strings + absl::base + absl::config + absl::endian + absl::raw_logging_internal + absl::fixed_array + gmock_main +) diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc index 045a5e21..93bb03e9 100644 --- a/absl/strings/ascii.cc +++ b/absl/strings/ascii.cc @@ -15,7 +15,7 @@ #include "absl/strings/ascii.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace ascii_internal { // # Table generated by this Python code (bit 0x02 is currently unused): @@ -57,7 +57,7 @@ namespace ascii_internal { // of these bits is tightly coupled to this implementation, the individual bits // are not named. Note that bitfields for all characters above ASCII 127 are // zero-initialized. -const unsigned char kPropertyBits[256] = { +ABSL_DLL const unsigned char kPropertyBits[256] = { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x00 0x40, 0x68, 0x48, 0x48, 0x48, 0x48, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x10 @@ -79,7 +79,7 @@ const unsigned char kPropertyBits[256] = { // Array of characters for the ascii_tolower() function. For values 'A' // through 'Z', return the lower-case character; otherwise, return the // identity of the passed character. -const char kToLower[256] = { +ABSL_DLL const char kToLower[256] = { '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', @@ -117,7 +117,7 @@ const char kToLower[256] = { // Array of characters for the ascii_toupper() function. For values 'a' // through 'z', return the upper-case character; otherwise, return the // identity of the passed character. -const char kToUpper[256] = { +ABSL_DLL const char kToUpper[256] = { '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', @@ -196,5 +196,5 @@ void RemoveExtraAsciiWhitespace(std::string* str) { str->erase(output_it - &(*str)[0]); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h index ebcbb11a..b46bc71f 100644 --- a/absl/strings/ascii.h +++ b/absl/strings/ascii.h @@ -56,20 +56,21 @@ #include <string> #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace ascii_internal { // Declaration for an array of bitfields holding character information. -extern const unsigned char kPropertyBits[256]; +ABSL_DLL extern const unsigned char kPropertyBits[256]; // Declaration for the array of characters to upper-case characters. -extern const char kToUpper[256]; +ABSL_DLL extern const char kToUpper[256]; // Declaration for the array of characters to lower-case characters. -extern const char kToLower[256]; +ABSL_DLL extern const char kToLower[256]; } // namespace ascii_internal @@ -235,7 +236,7 @@ inline void StripAsciiWhitespace(std::string* str) { // Removes leading, trailing, and consecutive internal whitespace. void RemoveExtraAsciiWhitespace(std::string*); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_ASCII_H_ diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc index 866a163e..bdba768d 100644 --- a/absl/strings/charconv.cc +++ b/absl/strings/charconv.cc @@ -57,7 +57,7 @@ // narrower mantissas. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { template <typename FloatType> @@ -673,7 +673,6 @@ from_chars_result FromCharsImpl(const char* first, const char* last, EncodeResult(calculated, negative, &result, &value); return result; } - return result; } } // namespace @@ -981,5 +980,5 @@ const int16_t kPower10ExponentTable[] = { }; } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h index 0b84ccac..e04be32f 100644 --- a/absl/strings/charconv.h +++ b/absl/strings/charconv.h @@ -17,8 +17,10 @@ #include <system_error> // NOLINT(build/c++11) +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Workalike compatibilty version of std::chars_format from C++17. // @@ -111,7 +113,7 @@ inline chars_format& operator^=(chars_format& lhs, chars_format rhs) { return lhs; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_CHARCONV_H_ diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc index b58fad26..9090e9c8 100644 --- a/absl/strings/charconv_test.cc +++ b/absl/strings/charconv_test.cc @@ -511,6 +511,13 @@ TEST(FromChars, Overflow) { EXPECT_EQ(f, std::numeric_limits<float>::max()); } +TEST(FromChars, RegressionTestsFromFuzzer) { + absl::string_view src = "0x21900000p00000000099"; + float f; + auto result = absl::from_chars(src.data(), src.data() + src.size(), f); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); +} + TEST(FromChars, ReturnValuePtr) { // Check that `ptr` points one past the number scanned, even if that number // is not representable. diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc new file mode 100644 index 00000000..d9503ae3 --- /dev/null +++ b/absl/strings/cord.cc @@ -0,0 +1,2019 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/cord.h" + +#include <algorithm> +#include <cstddef> +#include <cstdio> +#include <cstdlib> +#include <iomanip> +#include <limits> +#include <ostream> +#include <sstream> +#include <type_traits> +#include <unordered_set> +#include <vector> + +#include "absl/base/casts.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/port.h" +#include "absl/container/fixed_array.h" +#include "absl/container/inlined_vector.h" +#include "absl/strings/escaping.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/resize_uninitialized.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +using ::absl::cord_internal::CordRep; +using ::absl::cord_internal::CordRepConcat; +using ::absl::cord_internal::CordRepExternal; +using ::absl::cord_internal::CordRepSubstring; + +// Various representations that we allow +enum CordRepKind { + CONCAT = 0, + EXTERNAL = 1, + SUBSTRING = 2, + + // We have different tags for different sized flat arrays, + // starting with FLAT + FLAT = 3, +}; + +namespace { + +// Type used with std::allocator for allocating and deallocating +// `CordRepExternal`. std::allocator is used because it opaquely handles the +// different new / delete overloads available on a given platform. +struct alignas(absl::cord_internal::ExternalRepAlignment()) ExternalAllocType { + unsigned char value[absl::cord_internal::ExternalRepAlignment()]; +}; + +// Returns the number of objects to pass in to std::allocator<ExternalAllocType> +// allocate() and deallocate() to create enough room for `CordRepExternal` with +// `releaser_size` bytes on the end. +constexpr size_t GetExternalAllocNumObjects(size_t releaser_size) { + // Be sure to round up since `releaser_size` could be smaller than + // `sizeof(ExternalAllocType)`. + return (sizeof(CordRepExternal) + releaser_size + sizeof(ExternalAllocType) - + 1) / + sizeof(ExternalAllocType); +} + +// Allocates enough memory for `CordRepExternal` and a releaser with size +// `releaser_size` bytes. +void* AllocateExternal(size_t releaser_size) { + return std::allocator<ExternalAllocType>().allocate( + GetExternalAllocNumObjects(releaser_size)); +} + +// Deallocates the memory for a `CordRepExternal` assuming it was allocated with +// a releaser of given size and alignment. +void DeallocateExternal(CordRepExternal* p, size_t releaser_size) { + std::allocator<ExternalAllocType>().deallocate( + reinterpret_cast<ExternalAllocType*>(p), + GetExternalAllocNumObjects(releaser_size)); +} + +// Returns a pointer to the type erased releaser for the given CordRepExternal. +void* GetExternalReleaser(CordRepExternal* rep) { + return rep + 1; +} + +} // namespace + +namespace cord_internal { + +inline CordRepConcat* CordRep::concat() { + assert(tag == CONCAT); + return static_cast<CordRepConcat*>(this); +} + +inline const CordRepConcat* CordRep::concat() const { + assert(tag == CONCAT); + return static_cast<const CordRepConcat*>(this); +} + +inline CordRepSubstring* CordRep::substring() { + assert(tag == SUBSTRING); + return static_cast<CordRepSubstring*>(this); +} + +inline const CordRepSubstring* CordRep::substring() const { + assert(tag == SUBSTRING); + return static_cast<const CordRepSubstring*>(this); +} + +inline CordRepExternal* CordRep::external() { + assert(tag == EXTERNAL); + return static_cast<CordRepExternal*>(this); +} + +inline const CordRepExternal* CordRep::external() const { + assert(tag == EXTERNAL); + return static_cast<const CordRepExternal*>(this); +} + +} // namespace cord_internal + +static const size_t kFlatOverhead = offsetof(CordRep, data); + +static_assert(kFlatOverhead == 13, "Unittests assume kFlatOverhead == 13"); + +// Largest and smallest flat node lengths we are willing to allocate +// Flat allocation size is stored in tag, which currently can encode sizes up +// to 4K, encoded as multiple of either 8 or 32 bytes. +// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc. +static constexpr size_t kMaxFlatSize = 4096; +static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; +static constexpr size_t kMinFlatLength = 32 - kFlatOverhead; + +// Prefer copying blocks of at most this size, otherwise reference count. +static const size_t kMaxBytesToCopy = 511; + +// Helper functions for rounded div, and rounding to exact sizes. +static size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; } +static size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; } + +// Returns the size to the nearest equal or larger value that can be +// expressed exactly as a tag value. +static size_t RoundUpForTag(size_t size) { + return RoundUp(size, (size <= 1024) ? 8 : 32); +} + +// Converts the allocated size to a tag, rounding down if the size +// does not exactly match a 'tag expressible' size value. The result is +// undefined if the size exceeds the maximum size that can be encoded in +// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>). +static uint8_t AllocatedSizeToTag(size_t size) { + const size_t tag = (size <= 1024) ? size / 8 : 128 + size / 32 - 1024 / 32; + assert(tag <= std::numeric_limits<uint8_t>::max()); + return tag; +} + +// Converts the provided tag to the corresponding allocated size +static constexpr size_t TagToAllocatedSize(uint8_t tag) { + return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32); +} + +// Converts the provided tag to the corresponding available data length +static constexpr size_t TagToLength(uint8_t tag) { + return TagToAllocatedSize(tag) - kFlatOverhead; +} + +// Enforce that kMaxFlatSize maps to a well-known exact tag value. +static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic"); + +constexpr uint64_t Fibonacci(unsigned char n, uint64_t a = 0, uint64_t b = 1) { + return n == 0 ? a : Fibonacci(n - 1, b, a + b); +} + +static_assert(Fibonacci(63) == 6557470319842, + "Fibonacci values computed incorrectly"); + +// Minimum length required for a given depth tree -- a tree is considered +// balanced if +// length(t) >= min_length[depth(t)] +// The root node depth is allowed to become twice as large to reduce rebalancing +// for larger strings (see IsRootBalanced). +static constexpr uint64_t min_length[] = { + Fibonacci(2), + Fibonacci(3), + Fibonacci(4), + Fibonacci(5), + Fibonacci(6), + Fibonacci(7), + Fibonacci(8), + Fibonacci(9), + Fibonacci(10), + Fibonacci(11), + Fibonacci(12), + Fibonacci(13), + Fibonacci(14), + Fibonacci(15), + Fibonacci(16), + Fibonacci(17), + Fibonacci(18), + Fibonacci(19), + Fibonacci(20), + Fibonacci(21), + Fibonacci(22), + Fibonacci(23), + Fibonacci(24), + Fibonacci(25), + Fibonacci(26), + Fibonacci(27), + Fibonacci(28), + Fibonacci(29), + Fibonacci(30), + Fibonacci(31), + Fibonacci(32), + Fibonacci(33), + Fibonacci(34), + Fibonacci(35), + Fibonacci(36), + Fibonacci(37), + Fibonacci(38), + Fibonacci(39), + Fibonacci(40), + Fibonacci(41), + Fibonacci(42), + Fibonacci(43), + Fibonacci(44), + Fibonacci(45), + Fibonacci(46), + Fibonacci(47), + 0xffffffffffffffffull, // Avoid overflow +}; + +static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length); + +// The inlined size to use with absl::InlinedVector. +// +// Note: The InlinedVectors in this file (and in cord.h) do not need to use +// the same value for their inlined size. The fact that they do is historical. +// It may be desirable for each to use a different inlined size optimized for +// that InlinedVector's usage. +// +// TODO(jgm): Benchmark to see if there's a more optimal value than 47 for +// the inlined vector size (47 exists for backward compatibility). +static const int kInlinedVectorSize = 47; + +static inline bool IsRootBalanced(CordRep* node) { + if (node->tag != CONCAT) { + return true; + } else if (node->concat()->depth() <= 15) { + return true; + } else if (node->concat()->depth() > kMinLengthSize) { + return false; + } else { + // Allow depth to become twice as large as implied by fibonacci rule to + // reduce rebalancing for larger strings. + return (node->length >= min_length[node->concat()->depth() / 2]); + } +} + +static CordRep* Rebalance(CordRep* node); +static void DumpNode(CordRep* rep, bool include_data, std::ostream* os); +static bool VerifyNode(CordRep* root, CordRep* start_node, + bool full_validation); + +static inline CordRep* VerifyTree(CordRep* node) { + // Verification is expensive, so only do it in debug mode. + // Even in debug mode we normally do only light validation. + // If you are debugging Cord itself, you should define the + // macro EXTRA_CORD_VALIDATION, e.g. by adding + // --copt=-DEXTRA_CORD_VALIDATION to the blaze line. +#ifdef EXTRA_CORD_VALIDATION + assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/true)); +#else // EXTRA_CORD_VALIDATION + assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/false)); +#endif // EXTRA_CORD_VALIDATION + static_cast<void>(&VerifyNode); + + return node; +} + +// -------------------------------------------------------------------- +// Memory management + +inline CordRep* Ref(CordRep* rep) { + if (rep != nullptr) { + rep->refcount.Increment(); + } + return rep; +} + +// This internal routine is called from the cold path of Unref below. Keeping it +// in a separate routine allows good inlining of Unref into many profitable call +// sites. However, the call to this function can be highly disruptive to the +// register pressure in those callers. To minimize the cost to callers, we use +// a special LLVM calling convention that preserves most registers. This allows +// the call to this routine in cold paths to not disrupt the caller's register +// pressure. This calling convention is not available on all platforms; we +// intentionally allow LLVM to ignore the attribute rather than attempting to +// hardcode the list of supported platforms. +#if defined(__clang__) && !defined(__i386__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wattributes" +__attribute__((preserve_most)) +#pragma clang diagnostic pop +#endif +static void UnrefInternal(CordRep* rep) { + assert(rep != nullptr); + + absl::InlinedVector<CordRep*, kInlinedVectorSize> pending; + while (true) { + if (rep->tag == CONCAT) { + CordRepConcat* rep_concat = rep->concat(); + CordRep* right = rep_concat->right; + if (!right->refcount.Decrement()) { + pending.push_back(right); + } + CordRep* left = rep_concat->left; + delete rep_concat; + rep = nullptr; + if (!left->refcount.Decrement()) { + rep = left; + continue; + } + } else if (rep->tag == EXTERNAL) { + CordRepExternal* rep_external = rep->external(); + absl::string_view data(rep_external->base, rep->length); + void* releaser = GetExternalReleaser(rep_external); + size_t releaser_size = rep_external->releaser_invoker(releaser, data); + rep_external->~CordRepExternal(); + DeallocateExternal(rep_external, releaser_size); + rep = nullptr; + } else if (rep->tag == SUBSTRING) { + CordRepSubstring* rep_substring = rep->substring(); + CordRep* child = rep_substring->child; + delete rep_substring; + rep = nullptr; + if (!child->refcount.Decrement()) { + rep = child; + continue; + } + } else { + // Flat CordReps are allocated and constructed with raw ::operator new + // and placement new, and must be destructed and deallocated + // accordingly. +#if defined(__cpp_sized_deallocation) + size_t size = TagToAllocatedSize(rep->tag); + rep->~CordRep(); + ::operator delete(rep, size); +#else + rep->~CordRep(); + ::operator delete(rep); +#endif + rep = nullptr; + } + + if (!pending.empty()) { + rep = pending.back(); + pending.pop_back(); + } else { + break; + } + } +} + +inline void Unref(CordRep* rep) { + // Fast-path for two common, hot cases: a null rep and a shared root. + if (ABSL_PREDICT_TRUE(rep == nullptr || + rep->refcount.DecrementExpectHighRefcount())) { + return; + } + + UnrefInternal(rep); +} + +// Return the depth of a node +static int Depth(const CordRep* rep) { + if (rep->tag == CONCAT) { + return rep->concat()->depth(); + } else { + return 0; + } +} + +static void SetConcatChildren(CordRepConcat* concat, CordRep* left, + CordRep* right) { + concat->left = left; + concat->right = right; + + concat->length = left->length + right->length; + concat->set_depth(1 + std::max(Depth(left), Depth(right))); +} + +// Create a concatenation of the specified nodes. +// Does not change the refcounts of "left" and "right". +// The returned node has a refcount of 1. +static CordRep* RawConcat(CordRep* left, CordRep* right) { + // Avoid making degenerate concat nodes (one child is empty) + if (left == nullptr || left->length == 0) { + Unref(left); + return right; + } + if (right == nullptr || right->length == 0) { + Unref(right); + return left; + } + + CordRepConcat* rep = new CordRepConcat(); + rep->tag = CONCAT; + SetConcatChildren(rep, left, right); + + return rep; +} + +static CordRep* Concat(CordRep* left, CordRep* right) { + CordRep* rep = RawConcat(left, right); + if (rep != nullptr && !IsRootBalanced(rep)) { + rep = Rebalance(rep); + } + return VerifyTree(rep); +} + +// Make a balanced tree out of an array of leaf nodes. +static CordRep* MakeBalancedTree(CordRep** reps, size_t n) { + // Make repeated passes over the array, merging adjacent pairs + // until we are left with just a single node. + while (n > 1) { + size_t dst = 0; + for (size_t src = 0; src < n; src += 2) { + if (src + 1 < n) { + reps[dst] = Concat(reps[src], reps[src + 1]); + } else { + reps[dst] = reps[src]; + } + dst++; + } + n = dst; + } + + return reps[0]; +} + +// Create a new flat node. +static CordRep* NewFlat(size_t length_hint) { + if (length_hint <= kMinFlatLength) { + length_hint = kMinFlatLength; + } else if (length_hint > kMaxFlatLength) { + length_hint = kMaxFlatLength; + } + + // Round size up so it matches a size we can exactly express in a tag. + const size_t size = RoundUpForTag(length_hint + kFlatOverhead); + void* const raw_rep = ::operator new(size); + CordRep* rep = new (raw_rep) CordRep(); + rep->tag = AllocatedSizeToTag(size); + return VerifyTree(rep); +} + +// Create a new tree out of the specified array. +// The returned node has a refcount of 1. +static CordRep* NewTree(const char* data, + size_t length, + size_t alloc_hint) { + if (length == 0) return nullptr; + absl::FixedArray<CordRep*> reps((length - 1) / kMaxFlatLength + 1); + size_t n = 0; + do { + const size_t len = std::min(length, kMaxFlatLength); + CordRep* rep = NewFlat(len + alloc_hint); + rep->length = len; + memcpy(rep->data, data, len); + reps[n++] = VerifyTree(rep); + data += len; + length -= len; + } while (length != 0); + return MakeBalancedTree(reps.data(), n); +} + +namespace cord_internal { + +ExternalRepReleaserPair NewExternalWithUninitializedReleaser( + absl::string_view data, ExternalReleaserInvoker invoker, + size_t releaser_size) { + assert(!data.empty()); + + void* raw_rep = AllocateExternal(releaser_size); + auto* rep = new (raw_rep) CordRepExternal(); + rep->length = data.size(); + rep->tag = EXTERNAL; + rep->base = data.data(); + rep->releaser_invoker = invoker; + return {VerifyTree(rep), GetExternalReleaser(rep)}; +} + +} // namespace cord_internal + +static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) { + // Never create empty substring nodes + if (length == 0) { + Unref(child); + return nullptr; + } else { + CordRepSubstring* rep = new CordRepSubstring(); + assert((offset + length) <= child->length); + rep->length = length; + rep->tag = SUBSTRING; + rep->start = offset; + rep->child = child; + return VerifyTree(rep); + } +} + +// -------------------------------------------------------------------- +// Cord::InlineRep functions + +// This will trigger LNK2005 in MSVC. +#ifndef COMPILER_MSVC +const unsigned char Cord::InlineRep::kMaxInline; +#endif // COMPILER_MSVC + +inline void Cord::InlineRep::set_data(const char* data, size_t n, + bool nullify_tail) { + static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15"); + + cord_internal::SmallMemmove(data_, data, n, nullify_tail); + data_[kMaxInline] = static_cast<char>(n); +} + +inline char* Cord::InlineRep::set_data(size_t n) { + assert(n <= kMaxInline); + memset(data_, 0, sizeof(data_)); + data_[kMaxInline] = static_cast<char>(n); + return data_; +} + +inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) { + size_t len = data_[kMaxInline]; + CordRep* result; + if (len > kMaxInline) { + memcpy(&result, data_, sizeof(result)); + } else { + result = NewFlat(len + extra_hint); + result->length = len; + memcpy(result->data, data_, len); + set_tree(result); + } + return result; +} + +inline void Cord::InlineRep::reduce_size(size_t n) { + size_t tag = data_[kMaxInline]; + assert(tag <= kMaxInline); + assert(tag >= n); + tag -= n; + memset(data_ + tag, 0, n); + data_[kMaxInline] = static_cast<char>(tag); +} + +inline void Cord::InlineRep::remove_prefix(size_t n) { + cord_internal::SmallMemmove(data_, data_ + n, data_[kMaxInline] - n); + reduce_size(n); +} + +void Cord::InlineRep::AppendTree(CordRep* tree) { + if (tree == nullptr) return; + size_t len = data_[kMaxInline]; + if (len == 0) { + set_tree(tree); + } else { + set_tree(Concat(force_tree(0), tree)); + } +} + +void Cord::InlineRep::PrependTree(CordRep* tree) { + if (tree == nullptr) return; + size_t len = data_[kMaxInline]; + if (len == 0) { + set_tree(tree); + } else { + set_tree(Concat(tree, force_tree(0))); + } +} + +// Searches for a non-full flat node at the rightmost leaf of the tree. If a +// suitable leaf is found, the function will update the length field for all +// nodes to account for the size increase. The append region address will be +// written to region and the actual size increase will be written to size. +static inline bool PrepareAppendRegion(CordRep* root, char** region, + size_t* size, size_t max_length) { + // Search down the right-hand path for a non-full FLAT node. + CordRep* dst = root; + while (dst->tag == CONCAT && dst->refcount.IsOne()) { + dst = dst->concat()->right; + } + + if (dst->tag < FLAT || !dst->refcount.IsOne()) { + *region = nullptr; + *size = 0; + return false; + } + + const size_t in_use = dst->length; + const size_t capacity = TagToLength(dst->tag); + if (in_use == capacity) { + *region = nullptr; + *size = 0; + return false; + } + + size_t size_increase = std::min(capacity - in_use, max_length); + + // We need to update the length fields for all nodes, including the leaf node. + for (CordRep* rep = root; rep != dst; rep = rep->concat()->right) { + rep->length += size_increase; + } + dst->length += size_increase; + + *region = dst->data + in_use; + *size = size_increase; + return true; +} + +void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, + size_t max_length) { + if (max_length == 0) { + *region = nullptr; + *size = 0; + return; + } + + // Try to fit in the inline buffer if possible. + size_t inline_length = data_[kMaxInline]; + if (inline_length < kMaxInline && max_length <= kMaxInline - inline_length) { + *region = data_ + inline_length; + *size = max_length; + data_[kMaxInline] = static_cast<char>(inline_length + max_length); + return; + } + + CordRep* root = force_tree(max_length); + + if (PrepareAppendRegion(root, region, size, max_length)) { + return; + } + + // Allocate new node. + CordRep* new_node = + NewFlat(std::max(static_cast<size_t>(root->length), max_length)); + new_node->length = + std::min(static_cast<size_t>(TagToLength(new_node->tag)), max_length); + *region = new_node->data; + *size = new_node->length; + replace_tree(Concat(root, new_node)); +} + +void Cord::InlineRep::GetAppendRegion(char** region, size_t* size) { + const size_t max_length = std::numeric_limits<size_t>::max(); + + // Try to fit in the inline buffer if possible. + size_t inline_length = data_[kMaxInline]; + if (inline_length < kMaxInline) { + *region = data_ + inline_length; + *size = kMaxInline - inline_length; + data_[kMaxInline] = kMaxInline; + return; + } + + CordRep* root = force_tree(max_length); + + if (PrepareAppendRegion(root, region, size, max_length)) { + return; + } + + // Allocate new node. + CordRep* new_node = NewFlat(root->length); + new_node->length = TagToLength(new_node->tag); + *region = new_node->data; + *size = new_node->length; + replace_tree(Concat(root, new_node)); +} + +// If the rep is a leaf, this will increment the value at total_mem_usage and +// will return true. +static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { + if (rep->tag >= FLAT) { + *total_mem_usage += TagToAllocatedSize(rep->tag); + return true; + } + if (rep->tag == EXTERNAL) { + *total_mem_usage += sizeof(CordRepConcat) + rep->length; + return true; + } + return false; +} + +void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { + ClearSlow(); + + memcpy(data_, src.data_, sizeof(data_)); + if (is_tree()) { + Ref(tree()); + } +} + +void Cord::InlineRep::ClearSlow() { + if (is_tree()) { + Unref(tree()); + } + memset(data_, 0, sizeof(data_)); +} + +// -------------------------------------------------------------------- +// Constructors and destructors + +Cord::Cord(const Cord& src) : contents_(src.contents_) { + Ref(contents_.tree()); // Does nothing if contents_ has embedded data +} + +Cord::Cord(absl::string_view src) { + const size_t n = src.size(); + if (n <= InlineRep::kMaxInline) { + contents_.set_data(src.data(), n, false); + } else { + contents_.set_tree(NewTree(src.data(), n, 0)); + } +} + +// The destruction code is separate so that the compiler can determine +// that it does not need to call the destructor on a moved-from Cord. +void Cord::DestroyCordSlow() { + Unref(VerifyTree(contents_.tree())); +} + +// -------------------------------------------------------------------- +// Mutators + +void Cord::Clear() { + Unref(contents_.clear()); +} + +Cord& Cord::operator=(absl::string_view src) { + + const char* data = src.data(); + size_t length = src.size(); + CordRep* tree = contents_.tree(); + if (length <= InlineRep::kMaxInline) { + // Embed into this->contents_ + contents_.set_data(data, length, true); + Unref(tree); + return *this; + } + if (tree != nullptr && tree->tag >= FLAT && + TagToLength(tree->tag) >= length && tree->refcount.IsOne()) { + // Copy in place if the existing FLAT node is reusable. + memmove(tree->data, data, length); + tree->length = length; + VerifyTree(tree); + return *this; + } + contents_.set_tree(NewTree(data, length, 0)); + Unref(tree); + return *this; +} + +// TODO(sanjay): Move to Cord::InlineRep section of file. For now, +// we keep it here to make diffs easier. +void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) { + if (src_size == 0) return; // memcpy(_, nullptr, 0) is undefined. + // Try to fit in the inline buffer if possible. + size_t inline_length = data_[kMaxInline]; + if (inline_length < kMaxInline && src_size <= kMaxInline - inline_length) { + // Append new data to embedded array + data_[kMaxInline] = static_cast<char>(inline_length + src_size); + memcpy(data_ + inline_length, src_data, src_size); + return; + } + + CordRep* root = tree(); + + size_t appended = 0; + if (root) { + char* region; + if (PrepareAppendRegion(root, ®ion, &appended, src_size)) { + memcpy(region, src_data, appended); + } + } else { + // It is possible that src_data == data_, but when we transition from an + // InlineRep to a tree we need to assign data_ = root via set_tree. To + // avoid corrupting the source data before we copy it, delay calling + // set_tree until after we've copied data. + // We are going from an inline size to beyond inline size. Make the new size + // either double the inlined size, or the added size + 10%. + const size_t size1 = inline_length * 2 + src_size; + const size_t size2 = inline_length + src_size / 10; + root = NewFlat(std::max<size_t>(size1, size2)); + appended = std::min(src_size, TagToLength(root->tag) - inline_length); + memcpy(root->data, data_, inline_length); + memcpy(root->data + inline_length, src_data, appended); + root->length = inline_length + appended; + set_tree(root); + } + + src_data += appended; + src_size -= appended; + if (src_size == 0) { + return; + } + + // Use new block(s) for any remaining bytes that were not handled above. + // Alloc extra memory only if the right child of the root of the new tree is + // going to be a FLAT node, which will permit further inplace appends. + size_t length = src_size; + if (src_size < kMaxFlatLength) { + // The new length is either + // - old size + 10% + // - old_size + src_size + // This will cause a reasonable conservative step-up in size that is still + // large enough to avoid excessive amounts of small fragments being added. + length = std::max<size_t>(root->length / 10, src_size); + } + set_tree(Concat(root, NewTree(src_data, src_size, length - src_size))); +} + +inline CordRep* Cord::TakeRep() const& { + return Ref(contents_.tree()); +} + +inline CordRep* Cord::TakeRep() && { + CordRep* rep = contents_.tree(); + contents_.clear(); + return rep; +} + +template <typename C> +inline void Cord::AppendImpl(C&& src) { + if (empty()) { + // In case of an empty destination avoid allocating a new node, do not copy + // data. + *this = std::forward<C>(src); + return; + } + + // For short cords, it is faster to copy data if there is room in dst. + const size_t src_size = src.contents_.size(); + if (src_size <= kMaxBytesToCopy) { + CordRep* src_tree = src.contents_.tree(); + if (src_tree == nullptr) { + // src has embedded data. + contents_.AppendArray(src.contents_.data(), src_size); + return; + } + if (src_tree->tag >= FLAT) { + // src tree just has one flat node. + contents_.AppendArray(src_tree->data, src_size); + return; + } + if (&src == this) { + // ChunkIterator below assumes that src is not modified during traversal. + Append(Cord(src)); + return; + } + // TODO(mec): Should we only do this if "dst" has space? + for (absl::string_view chunk : src.Chunks()) { + Append(chunk); + } + return; + } + + contents_.AppendTree(std::forward<C>(src).TakeRep()); +} + +void Cord::Append(const Cord& src) { AppendImpl(src); } + +void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); } + +void Cord::Prepend(const Cord& src) { + CordRep* src_tree = src.contents_.tree(); + if (src_tree != nullptr) { + Ref(src_tree); + contents_.PrependTree(src_tree); + return; + } + + // `src` cord is inlined. + absl::string_view src_contents(src.contents_.data(), src.contents_.size()); + return Prepend(src_contents); +} + +void Cord::Prepend(absl::string_view src) { + if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined. + size_t cur_size = contents_.size(); + if (!contents_.is_tree() && cur_size + src.size() <= InlineRep::kMaxInline) { + // Use embedded storage. + char data[InlineRep::kMaxInline + 1] = {0}; + data[InlineRep::kMaxInline] = cur_size + src.size(); // set size + memcpy(data, src.data(), src.size()); + memcpy(data + src.size(), contents_.data(), cur_size); + memcpy(reinterpret_cast<void*>(&contents_), data, + InlineRep::kMaxInline + 1); + } else { + contents_.PrependTree(NewTree(src.data(), src.size(), 0)); + } +} + +static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { + if (n >= node->length) return nullptr; + if (n == 0) return Ref(node); + absl::InlinedVector<CordRep*, kInlinedVectorSize> rhs_stack; + + while (node->tag == CONCAT) { + assert(n <= node->length); + if (n < node->concat()->left->length) { + // Push right to stack, descend left. + rhs_stack.push_back(node->concat()->right); + node = node->concat()->left; + } else { + // Drop left, descend right. + n -= node->concat()->left->length; + node = node->concat()->right; + } + } + assert(n <= node->length); + + if (n == 0) { + Ref(node); + } else { + size_t start = n; + size_t len = node->length - n; + if (node->tag == SUBSTRING) { + // Consider in-place update of node, similar to in RemoveSuffixFrom(). + start += node->substring()->start; + node = node->substring()->child; + } + node = NewSubstring(Ref(node), start, len); + } + while (!rhs_stack.empty()) { + node = Concat(node, Ref(rhs_stack.back())); + rhs_stack.pop_back(); + } + return node; +} + +// RemoveSuffixFrom() is very similar to RemovePrefixFrom(), with the +// exception that removing a suffix has an optimization where a node may be +// edited in place iff that node and all its ancestors have a refcount of 1. +static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { + if (n >= node->length) return nullptr; + if (n == 0) return Ref(node); + absl::InlinedVector<CordRep*, kInlinedVectorSize> lhs_stack; + bool inplace_ok = node->refcount.IsOne(); + + while (node->tag == CONCAT) { + assert(n <= node->length); + if (n < node->concat()->right->length) { + // Push left to stack, descend right. + lhs_stack.push_back(node->concat()->left); + node = node->concat()->right; + } else { + // Drop right, descend left. + n -= node->concat()->right->length; + node = node->concat()->left; + } + inplace_ok = inplace_ok && node->refcount.IsOne(); + } + assert(n <= node->length); + + if (n == 0) { + Ref(node); + } else if (inplace_ok && node->tag != EXTERNAL) { + // Consider making a new buffer if the current node capacity is much + // larger than the new length. + Ref(node); + node->length -= n; + } else { + size_t start = 0; + size_t len = node->length - n; + if (node->tag == SUBSTRING) { + start = node->substring()->start; + node = node->substring()->child; + } + node = NewSubstring(Ref(node), start, len); + } + while (!lhs_stack.empty()) { + node = Concat(Ref(lhs_stack.back()), node); + lhs_stack.pop_back(); + } + return node; +} + +void Cord::RemovePrefix(size_t n) { + ABSL_INTERNAL_CHECK(n <= size(), + absl::StrCat("Requested prefix size ", n, + " exceeds Cord's size ", size())); + CordRep* tree = contents_.tree(); + if (tree == nullptr) { + contents_.remove_prefix(n); + } else { + CordRep* newrep = RemovePrefixFrom(tree, n); + Unref(tree); + contents_.replace_tree(VerifyTree(newrep)); + } +} + +void Cord::RemoveSuffix(size_t n) { + ABSL_INTERNAL_CHECK(n <= size(), + absl::StrCat("Requested suffix size ", n, + " exceeds Cord's size ", size())); + CordRep* tree = contents_.tree(); + if (tree == nullptr) { + contents_.reduce_size(n); + } else { + CordRep* newrep = RemoveSuffixFrom(tree, n); + Unref(tree); + contents_.replace_tree(VerifyTree(newrep)); + } +} + +// Work item for NewSubRange(). +struct SubRange { + SubRange(CordRep* a_node, size_t a_pos, size_t a_n) + : node(a_node), pos(a_pos), n(a_n) {} + CordRep* node; // nullptr means concat last 2 results. + size_t pos; + size_t n; +}; + +static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { + absl::InlinedVector<CordRep*, kInlinedVectorSize> results; + absl::InlinedVector<SubRange, kInlinedVectorSize> todo; + todo.push_back(SubRange(node, pos, n)); + do { + const SubRange& sr = todo.back(); + node = sr.node; + pos = sr.pos; + n = sr.n; + todo.pop_back(); + + if (node == nullptr) { + assert(results.size() >= 2); + CordRep* right = results.back(); + results.pop_back(); + CordRep* left = results.back(); + results.pop_back(); + results.push_back(Concat(left, right)); + } else if (pos == 0 && n == node->length) { + results.push_back(Ref(node)); + } else if (node->tag != CONCAT) { + if (node->tag == SUBSTRING) { + pos += node->substring()->start; + node = node->substring()->child; + } + results.push_back(NewSubstring(Ref(node), pos, n)); + } else if (pos + n <= node->concat()->left->length) { + todo.push_back(SubRange(node->concat()->left, pos, n)); + } else if (pos >= node->concat()->left->length) { + pos -= node->concat()->left->length; + todo.push_back(SubRange(node->concat()->right, pos, n)); + } else { + size_t left_n = node->concat()->left->length - pos; + todo.push_back(SubRange(nullptr, 0, 0)); // Concat() + todo.push_back(SubRange(node->concat()->right, 0, n - left_n)); + todo.push_back(SubRange(node->concat()->left, pos, left_n)); + } + } while (!todo.empty()); + assert(results.size() == 1); + return results[0]; +} + +Cord Cord::Subcord(size_t pos, size_t new_size) const { + Cord sub_cord; + size_t length = size(); + if (pos > length) pos = length; + if (new_size > length - pos) new_size = length - pos; + CordRep* tree = contents_.tree(); + if (tree == nullptr) { + // sub_cord is newly constructed, no need to re-zero-out the tail of + // contents_ memory. + sub_cord.contents_.set_data(contents_.data() + pos, new_size, false); + } else if (new_size == 0) { + // We want to return empty subcord, so nothing to do. + } else if (new_size <= InlineRep::kMaxInline) { + Cord::ChunkIterator it = chunk_begin(); + it.AdvanceBytes(pos); + char* dest = sub_cord.contents_.data_; + size_t remaining_size = new_size; + while (remaining_size > it->size()) { + cord_internal::SmallMemmove(dest, it->data(), it->size()); + remaining_size -= it->size(); + dest += it->size(); + ++it; + } + cord_internal::SmallMemmove(dest, it->data(), remaining_size); + sub_cord.contents_.data_[InlineRep::kMaxInline] = new_size; + } else { + sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size)); + } + return sub_cord; +} + +// -------------------------------------------------------------------- +// Balancing + +class CordForest { + public: + explicit CordForest(size_t length) + : root_length_(length), trees_(kMinLengthSize, nullptr) {} + + void Build(CordRep* cord_root) { + std::vector<CordRep*> pending = {cord_root}; + + while (!pending.empty()) { + CordRep* node = pending.back(); + pending.pop_back(); + CheckNode(node); + if (ABSL_PREDICT_FALSE(node->tag != CONCAT)) { + AddNode(node); + continue; + } + + CordRepConcat* concat_node = node->concat(); + if (concat_node->depth() >= kMinLengthSize || + concat_node->length < min_length[concat_node->depth()]) { + pending.push_back(concat_node->right); + pending.push_back(concat_node->left); + + if (concat_node->refcount.IsOne()) { + concat_node->left = concat_freelist_; + concat_freelist_ = concat_node; + } else { + Ref(concat_node->right); + Ref(concat_node->left); + Unref(concat_node); + } + } else { + AddNode(node); + } + } + } + + CordRep* ConcatNodes() { + CordRep* sum = nullptr; + for (auto* node : trees_) { + if (node == nullptr) continue; + + sum = PrependNode(node, sum); + root_length_ -= node->length; + if (root_length_ == 0) break; + } + ABSL_INTERNAL_CHECK(sum != nullptr, "Failed to locate sum node"); + return VerifyTree(sum); + } + + private: + CordRep* AppendNode(CordRep* node, CordRep* sum) { + return (sum == nullptr) ? node : MakeConcat(sum, node); + } + + CordRep* PrependNode(CordRep* node, CordRep* sum) { + return (sum == nullptr) ? node : MakeConcat(node, sum); + } + + void AddNode(CordRep* node) { + CordRep* sum = nullptr; + + // Collect together everything with which we will merge node + int i = 0; + for (; node->length > min_length[i + 1]; ++i) { + auto& tree_at_i = trees_[i]; + + if (tree_at_i == nullptr) continue; + sum = PrependNode(tree_at_i, sum); + tree_at_i = nullptr; + } + + sum = AppendNode(node, sum); + + // Insert sum into appropriate place in the forest + for (; sum->length >= min_length[i]; ++i) { + auto& tree_at_i = trees_[i]; + if (tree_at_i == nullptr) continue; + + sum = MakeConcat(tree_at_i, sum); + tree_at_i = nullptr; + } + + // min_length[0] == 1, which means sum->length >= min_length[0] + assert(i > 0); + trees_[i - 1] = sum; + } + + // Make concat node trying to resue existing CordRepConcat nodes we + // already collected in the concat_freelist_. + CordRep* MakeConcat(CordRep* left, CordRep* right) { + if (concat_freelist_ == nullptr) return RawConcat(left, right); + + CordRepConcat* rep = concat_freelist_; + if (concat_freelist_->left == nullptr) { + concat_freelist_ = nullptr; + } else { + concat_freelist_ = concat_freelist_->left->concat(); + } + SetConcatChildren(rep, left, right); + + return rep; + } + + static void CheckNode(CordRep* node) { + ABSL_INTERNAL_CHECK(node->length != 0u, ""); + if (node->tag == CONCAT) { + ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, ""); + ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, ""); + ABSL_INTERNAL_CHECK(node->length == (node->concat()->left->length + + node->concat()->right->length), + ""); + } + } + + size_t root_length_; + + // use an inlined vector instead of a flat array to get bounds checking + absl::InlinedVector<CordRep*, kInlinedVectorSize> trees_; + + // List of concat nodes we can re-use for Cord balancing. + CordRepConcat* concat_freelist_ = nullptr; +}; + +static CordRep* Rebalance(CordRep* node) { + VerifyTree(node); + assert(node->tag == CONCAT); + + if (node->length == 0) { + return nullptr; + } + + CordForest forest(node->length); + forest.Build(node); + return forest.ConcatNodes(); +} + +// -------------------------------------------------------------------- +// Comparators + +namespace { + +int ClampResult(int memcmp_res) { + return static_cast<int>(memcmp_res > 0) - static_cast<int>(memcmp_res < 0); +} + +int CompareChunks(absl::string_view* lhs, absl::string_view* rhs, + size_t* size_to_compare) { + size_t compared_size = std::min(lhs->size(), rhs->size()); + assert(*size_to_compare >= compared_size); + *size_to_compare -= compared_size; + + int memcmp_res = ::memcmp(lhs->data(), rhs->data(), compared_size); + if (memcmp_res != 0) return memcmp_res; + + lhs->remove_prefix(compared_size); + rhs->remove_prefix(compared_size); + + return 0; +} + +// This overload set computes comparison results from memcmp result. This +// interface is used inside GenericCompare below. Differet implementations +// are specialized for int and bool. For int we clamp result to {-1, 0, 1} +// set. For bool we just interested in "value == 0". +template <typename ResultType> +ResultType ComputeCompareResult(int memcmp_res) { + return ClampResult(memcmp_res); +} +template <> +bool ComputeCompareResult<bool>(int memcmp_res) { + return memcmp_res == 0; +} + +} // namespace + +// Helper routine. Locates the first flat chunk of the Cord without +// initializing the iterator. +inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { + size_t n = data_[kMaxInline]; + if (n <= kMaxInline) { + return absl::string_view(data_, n); + } + + CordRep* node = tree(); + if (node->tag >= FLAT) { + return absl::string_view(node->data, node->length); + } + + if (node->tag == EXTERNAL) { + return absl::string_view(node->external()->base, node->length); + } + + // Walk down the left branches until we hit a non-CONCAT node. + while (node->tag == CONCAT) { + node = node->concat()->left; + } + + // Get the child node if we encounter a SUBSTRING. + size_t offset = 0; + size_t length = node->length; + assert(length != 0); + + if (node->tag == SUBSTRING) { + offset = node->substring()->start; + node = node->substring()->child; + } + + if (node->tag >= FLAT) { + return absl::string_view(node->data + offset, length); + } + + assert((node->tag == EXTERNAL) && "Expect FLAT or EXTERNAL node here"); + + return absl::string_view(node->external()->base + offset, length); +} + +inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size, + size_t size_to_compare) const { + auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) { + if (!chunk->empty()) return true; + ++*it; + if (it->bytes_remaining_ == 0) return false; + *chunk = **it; + return true; + }; + + Cord::ChunkIterator lhs_it = chunk_begin(); + + // compared_size is inside first chunk. + absl::string_view lhs_chunk = + (lhs_it.bytes_remaining_ != 0) ? *lhs_it : absl::string_view(); + assert(compared_size <= lhs_chunk.size()); + assert(compared_size <= rhs.size()); + lhs_chunk.remove_prefix(compared_size); + rhs.remove_prefix(compared_size); + size_to_compare -= compared_size; // skip already compared size. + + while (advance(&lhs_it, &lhs_chunk) && !rhs.empty()) { + int comparison_result = CompareChunks(&lhs_chunk, &rhs, &size_to_compare); + if (comparison_result != 0) return comparison_result; + if (size_to_compare == 0) return 0; + } + + return static_cast<int>(rhs.empty()) - static_cast<int>(lhs_chunk.empty()); +} + +inline int Cord::CompareSlowPath(const Cord& rhs, size_t compared_size, + size_t size_to_compare) const { + auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) { + if (!chunk->empty()) return true; + ++*it; + if (it->bytes_remaining_ == 0) return false; + *chunk = **it; + return true; + }; + + Cord::ChunkIterator lhs_it = chunk_begin(); + Cord::ChunkIterator rhs_it = rhs.chunk_begin(); + + // compared_size is inside both first chunks. + absl::string_view lhs_chunk = + (lhs_it.bytes_remaining_ != 0) ? *lhs_it : absl::string_view(); + absl::string_view rhs_chunk = + (rhs_it.bytes_remaining_ != 0) ? *rhs_it : absl::string_view(); + assert(compared_size <= lhs_chunk.size()); + assert(compared_size <= rhs_chunk.size()); + lhs_chunk.remove_prefix(compared_size); + rhs_chunk.remove_prefix(compared_size); + size_to_compare -= compared_size; // skip already compared size. + + while (advance(&lhs_it, &lhs_chunk) && advance(&rhs_it, &rhs_chunk)) { + int memcmp_res = CompareChunks(&lhs_chunk, &rhs_chunk, &size_to_compare); + if (memcmp_res != 0) return memcmp_res; + if (size_to_compare == 0) return 0; + } + + return static_cast<int>(rhs_chunk.empty()) - + static_cast<int>(lhs_chunk.empty()); +} + +inline absl::string_view Cord::GetFirstChunk(const Cord& c) { + return c.contents_.FindFlatStartPiece(); +} +inline absl::string_view Cord::GetFirstChunk(absl::string_view sv) { + return sv; +} + +// Compares up to 'size_to_compare' bytes of 'lhs' with 'rhs'. It is assumed +// that 'size_to_compare' is greater that size of smallest of first chunks. +template <typename ResultType, typename RHS> +ResultType GenericCompare(const Cord& lhs, const RHS& rhs, + size_t size_to_compare) { + absl::string_view lhs_chunk = Cord::GetFirstChunk(lhs); + absl::string_view rhs_chunk = Cord::GetFirstChunk(rhs); + + size_t compared_size = std::min(lhs_chunk.size(), rhs_chunk.size()); + assert(size_to_compare >= compared_size); + int memcmp_res = ::memcmp(lhs_chunk.data(), rhs_chunk.data(), compared_size); + if (compared_size == size_to_compare || memcmp_res != 0) { + return ComputeCompareResult<ResultType>(memcmp_res); + } + + return ComputeCompareResult<ResultType>( + lhs.CompareSlowPath(rhs, compared_size, size_to_compare)); +} + +bool Cord::EqualsImpl(absl::string_view rhs, size_t size_to_compare) const { + return GenericCompare<bool>(*this, rhs, size_to_compare); +} + +bool Cord::EqualsImpl(const Cord& rhs, size_t size_to_compare) const { + return GenericCompare<bool>(*this, rhs, size_to_compare); +} + +template <typename RHS> +inline int SharedCompareImpl(const Cord& lhs, const RHS& rhs) { + size_t lhs_size = lhs.size(); + size_t rhs_size = rhs.size(); + if (lhs_size == rhs_size) { + return GenericCompare<int>(lhs, rhs, lhs_size); + } + if (lhs_size < rhs_size) { + auto data_comp_res = GenericCompare<int>(lhs, rhs, lhs_size); + return data_comp_res == 0 ? -1 : data_comp_res; + } + + auto data_comp_res = GenericCompare<int>(lhs, rhs, rhs_size); + return data_comp_res == 0 ? +1 : data_comp_res; +} + +int Cord::Compare(absl::string_view rhs) const { + return SharedCompareImpl(*this, rhs); +} + +int Cord::CompareImpl(const Cord& rhs) const { + return SharedCompareImpl(*this, rhs); +} + +bool Cord::EndsWith(absl::string_view rhs) const { + size_t my_size = size(); + size_t rhs_size = rhs.size(); + + if (my_size < rhs_size) return false; + + Cord tmp(*this); + tmp.RemovePrefix(my_size - rhs_size); + return tmp.EqualsImpl(rhs, rhs_size); +} + +bool Cord::EndsWith(const Cord& rhs) const { + size_t my_size = size(); + size_t rhs_size = rhs.size(); + + if (my_size < rhs_size) return false; + + Cord tmp(*this); + tmp.RemovePrefix(my_size - rhs_size); + return tmp.EqualsImpl(rhs, rhs_size); +} + +// -------------------------------------------------------------------- +// Misc. + +Cord::operator std::string() const { + std::string s; + absl::CopyCordToString(*this, &s); + return s; +} + +void CopyCordToString(const Cord& src, std::string* dst) { + if (!src.contents_.is_tree()) { + src.contents_.CopyTo(dst); + } else { + absl::strings_internal::STLStringResizeUninitialized(dst, src.size()); + src.CopyToArraySlowPath(&(*dst)[0]); + } +} + +void Cord::CopyToArraySlowPath(char* dst) const { + assert(contents_.is_tree()); + absl::string_view fragment; + if (GetFlatAux(contents_.tree(), &fragment)) { + memcpy(dst, fragment.data(), fragment.size()); + return; + } + for (absl::string_view chunk : Chunks()) { + memcpy(dst, chunk.data(), chunk.size()); + dst += chunk.size(); + } +} + +Cord::ChunkIterator& Cord::ChunkIterator::operator++() { + assert(bytes_remaining_ > 0 && "Attempted to iterate past `end()`"); + assert(bytes_remaining_ >= current_chunk_.size()); + bytes_remaining_ -= current_chunk_.size(); + + if (stack_of_right_children_.empty()) { + assert(!current_chunk_.empty()); // Called on invalid iterator. + // We have reached the end of the Cord. + return *this; + } + + // Process the next node on the stack. + CordRep* node = stack_of_right_children_.back(); + stack_of_right_children_.pop_back(); + + // Walk down the left branches until we hit a non-CONCAT node. Save the + // right children to the stack for subsequent traversal. + while (node->tag == CONCAT) { + stack_of_right_children_.push_back(node->concat()->right); + node = node->concat()->left; + } + + // Get the child node if we encounter a SUBSTRING. + size_t offset = 0; + size_t length = node->length; + if (node->tag == SUBSTRING) { + offset = node->substring()->start; + node = node->substring()->child; + } + + assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(length != 0); + const char* data = + node->tag == EXTERNAL ? node->external()->base : node->data; + current_chunk_ = absl::string_view(data + offset, length); + current_leaf_ = node; + return *this; +} + +Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { + assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`"); + Cord subcord; + + if (n <= InlineRep::kMaxInline) { + // Range to read fits in inline data. Flatten it. + char* data = subcord.contents_.set_data(n); + while (n > current_chunk_.size()) { + memcpy(data, current_chunk_.data(), current_chunk_.size()); + data += current_chunk_.size(); + n -= current_chunk_.size(); + ++*this; + } + memcpy(data, current_chunk_.data(), n); + if (n < current_chunk_.size()) { + RemoveChunkPrefix(n); + } else if (n > 0) { + ++*this; + } + return subcord; + } + if (n < current_chunk_.size()) { + // Range to read is a proper subrange of the current chunk. + assert(current_leaf_ != nullptr); + CordRep* subnode = Ref(current_leaf_); + const char* data = + subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data; + subnode = NewSubstring(subnode, current_chunk_.data() - data, n); + subcord.contents_.set_tree(VerifyTree(subnode)); + RemoveChunkPrefix(n); + return subcord; + } + + // Range to read begins with a proper subrange of the current chunk. + assert(!current_chunk_.empty()); + assert(current_leaf_ != nullptr); + CordRep* subnode = Ref(current_leaf_); + if (current_chunk_.size() < subnode->length) { + const char* data = + subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data; + subnode = NewSubstring(subnode, current_chunk_.data() - data, + current_chunk_.size()); + } + n -= current_chunk_.size(); + bytes_remaining_ -= current_chunk_.size(); + + // Process the next node(s) on the stack, reading whole subtrees depending on + // their length and how many bytes we are advancing. + CordRep* node = nullptr; + while (!stack_of_right_children_.empty()) { + node = stack_of_right_children_.back(); + stack_of_right_children_.pop_back(); + if (node->length > n) break; + // TODO(qrczak): This might unnecessarily recreate existing concat nodes. + // Avoiding that would need pretty complicated logic (instead of + // current_leaf_, keep current_subtree_ which points to the highest node + // such that the current leaf can be found on the path of left children + // starting from current_subtree_; delay creating subnode while node is + // below current_subtree_; find the proper node along the path of left + // children starting from current_subtree_ if this loop exits while staying + // below current_subtree_; etc.; alternatively, push parents instead of + // right children on the stack). + subnode = Concat(subnode, Ref(node)); + n -= node->length; + bytes_remaining_ -= node->length; + node = nullptr; + } + + if (node == nullptr) { + // We have reached the end of the Cord. + assert(bytes_remaining_ == 0); + subcord.contents_.set_tree(VerifyTree(subnode)); + return subcord; + } + + // Walk down the appropriate branches until we hit a non-CONCAT node. Save the + // right children to the stack for subsequent traversal. + while (node->tag == CONCAT) { + if (node->concat()->left->length > n) { + // Push right, descend left. + stack_of_right_children_.push_back(node->concat()->right); + node = node->concat()->left; + } else { + // Read left, descend right. + subnode = Concat(subnode, Ref(node->concat()->left)); + n -= node->concat()->left->length; + bytes_remaining_ -= node->concat()->left->length; + node = node->concat()->right; + } + } + + // Get the child node if we encounter a SUBSTRING. + size_t offset = 0; + size_t length = node->length; + if (node->tag == SUBSTRING) { + offset = node->substring()->start; + node = node->substring()->child; + } + + // Range to read ends with a proper (possibly empty) subrange of the current + // chunk. + assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(length > n); + if (n > 0) subnode = Concat(subnode, NewSubstring(Ref(node), offset, n)); + const char* data = + node->tag == EXTERNAL ? node->external()->base : node->data; + current_chunk_ = absl::string_view(data + offset + n, length - n); + current_leaf_ = node; + bytes_remaining_ -= n; + subcord.contents_.set_tree(VerifyTree(subnode)); + return subcord; +} + +void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { + assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`"); + assert(n >= current_chunk_.size()); // This should only be called when + // iterating to a new node. + + n -= current_chunk_.size(); + bytes_remaining_ -= current_chunk_.size(); + + // Process the next node(s) on the stack, skipping whole subtrees depending on + // their length and how many bytes we are advancing. + CordRep* node = nullptr; + while (!stack_of_right_children_.empty()) { + node = stack_of_right_children_.back(); + stack_of_right_children_.pop_back(); + if (node->length > n) break; + n -= node->length; + bytes_remaining_ -= node->length; + node = nullptr; + } + + if (node == nullptr) { + // We have reached the end of the Cord. + assert(bytes_remaining_ == 0); + return; + } + + // Walk down the appropriate branches until we hit a non-CONCAT node. Save the + // right children to the stack for subsequent traversal. + while (node->tag == CONCAT) { + if (node->concat()->left->length > n) { + // Push right, descend left. + stack_of_right_children_.push_back(node->concat()->right); + node = node->concat()->left; + } else { + // Skip left, descend right. + n -= node->concat()->left->length; + bytes_remaining_ -= node->concat()->left->length; + node = node->concat()->right; + } + } + + // Get the child node if we encounter a SUBSTRING. + size_t offset = 0; + size_t length = node->length; + if (node->tag == SUBSTRING) { + offset = node->substring()->start; + node = node->substring()->child; + } + + assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(length > n); + const char* data = + node->tag == EXTERNAL ? node->external()->base : node->data; + current_chunk_ = absl::string_view(data + offset + n, length - n); + current_leaf_ = node; + bytes_remaining_ -= n; +} + +char Cord::operator[](size_t i) const { + assert(i < size()); + size_t offset = i; + const CordRep* rep = contents_.tree(); + if (rep == nullptr) { + return contents_.data()[i]; + } + while (true) { + assert(rep != nullptr); + assert(offset < rep->length); + if (rep->tag >= FLAT) { + // Get the "i"th character directly from the flat array. + return rep->data[offset]; + } else if (rep->tag == EXTERNAL) { + // Get the "i"th character from the external array. + return rep->external()->base[offset]; + } else if (rep->tag == CONCAT) { + // Recursively branch to the side of the concatenation that the "i"th + // character is on. + size_t left_length = rep->concat()->left->length; + if (offset < left_length) { + rep = rep->concat()->left; + } else { + offset -= left_length; + rep = rep->concat()->right; + } + } else { + // This must be a substring a node, so bypass it to get to the child. + assert(rep->tag == SUBSTRING); + offset += rep->substring()->start; + rep = rep->substring()->child; + } + } +} + +absl::string_view Cord::FlattenSlowPath() { + size_t total_size = size(); + CordRep* new_rep; + char* new_buffer; + + // Try to put the contents into a new flat rep. If they won't fit in the + // biggest possible flat node, use an external rep instead. + if (total_size <= kMaxFlatLength) { + new_rep = NewFlat(total_size); + new_rep->length = total_size; + new_buffer = new_rep->data; + CopyToArraySlowPath(new_buffer); + } else { + new_buffer = std::allocator<char>().allocate(total_size); + CopyToArraySlowPath(new_buffer); + new_rep = absl::cord_internal::NewExternalRep( + absl::string_view(new_buffer, total_size), [](absl::string_view s) { + std::allocator<char>().deallocate(const_cast<char*>(s.data()), + s.size()); + }); + } + Unref(contents_.tree()); + contents_.set_tree(new_rep); + return absl::string_view(new_buffer, total_size); +} + +/* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) { + assert(rep != nullptr); + if (rep->tag >= FLAT) { + *fragment = absl::string_view(rep->data, rep->length); + return true; + } else if (rep->tag == EXTERNAL) { + *fragment = absl::string_view(rep->external()->base, rep->length); + return true; + } else if (rep->tag == SUBSTRING) { + CordRep* child = rep->substring()->child; + if (child->tag >= FLAT) { + *fragment = + absl::string_view(child->data + rep->substring()->start, rep->length); + return true; + } else if (child->tag == EXTERNAL) { + *fragment = absl::string_view( + child->external()->base + rep->substring()->start, rep->length); + return true; + } + } + return false; +} + +/* static */ void Cord::ForEachChunkAux( + absl::cord_internal::CordRep* rep, + absl::FunctionRef<void(absl::string_view)> callback) { + assert(rep != nullptr); + int stack_pos = 0; + constexpr int stack_max = 128; + // Stack of right branches for tree traversal + absl::cord_internal::CordRep* stack[stack_max]; + absl::cord_internal::CordRep* current_node = rep; + while (true) { + if (current_node->tag == CONCAT) { + if (stack_pos == stack_max) { + // There's no more room on our stack array to add another right branch, + // and the idea is to avoid allocations, so call this function + // recursively to navigate this subtree further. (This is not something + // we expect to happen in practice). + ForEachChunkAux(current_node, callback); + + // Pop the next right branch and iterate. + current_node = stack[--stack_pos]; + continue; + } else { + // Save the right branch for later traversal and continue down the left + // branch. + stack[stack_pos++] = current_node->concat()->right; + current_node = current_node->concat()->left; + continue; + } + } + // This is a leaf node, so invoke our callback. + absl::string_view chunk; + bool success = GetFlatAux(current_node, &chunk); + assert(success); + if (success) { + callback(chunk); + } + if (stack_pos == 0) { + // end of traversal + return; + } + current_node = stack[--stack_pos]; + } +} + +static void DumpNode(CordRep* rep, bool include_data, std::ostream* os) { + const int kIndentStep = 1; + int indent = 0; + absl::InlinedVector<CordRep*, kInlinedVectorSize> stack; + absl::InlinedVector<int, kInlinedVectorSize> indents; + for (;;) { + *os << std::setw(3) << rep->refcount.Get(); + *os << " " << std::setw(7) << rep->length; + *os << " ["; + if (include_data) *os << static_cast<void*>(rep); + *os << "]"; + *os << " " << (IsRootBalanced(rep) ? 'b' : 'u'); + *os << " " << std::setw(indent) << ""; + if (rep->tag == CONCAT) { + *os << "CONCAT depth=" << Depth(rep) << "\n"; + indent += kIndentStep; + indents.push_back(indent); + stack.push_back(rep->concat()->right); + rep = rep->concat()->left; + } else if (rep->tag == SUBSTRING) { + *os << "SUBSTRING @ " << rep->substring()->start << "\n"; + indent += kIndentStep; + rep = rep->substring()->child; + } else { // Leaf + if (rep->tag == EXTERNAL) { + *os << "EXTERNAL ["; + if (include_data) + *os << absl::CEscape(std::string(rep->external()->base, rep->length)); + *os << "]\n"; + } else { + *os << "FLAT cap=" << TagToLength(rep->tag) << " ["; + if (include_data) + *os << absl::CEscape(std::string(rep->data, rep->length)); + *os << "]\n"; + } + if (stack.empty()) break; + rep = stack.back(); + stack.pop_back(); + indent = indents.back(); + indents.pop_back(); + } + } + ABSL_INTERNAL_CHECK(indents.empty(), ""); +} + +static std::string ReportError(CordRep* root, CordRep* node) { + std::ostringstream buf; + buf << "Error at node " << node << " in:"; + DumpNode(root, true, &buf); + return buf.str(); +} + +static bool VerifyNode(CordRep* root, CordRep* start_node, + bool full_validation) { + absl::InlinedVector<CordRep*, 2> worklist; + worklist.push_back(start_node); + do { + CordRep* node = worklist.back(); + worklist.pop_back(); + + ABSL_INTERNAL_CHECK(node != nullptr, ReportError(root, node)); + if (node != root) { + ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node)); + } + + if (node->tag == CONCAT) { + ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, + ReportError(root, node)); + ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, + ReportError(root, node)); + ABSL_INTERNAL_CHECK((node->length == node->concat()->left->length + + node->concat()->right->length), + ReportError(root, node)); + if (full_validation) { + worklist.push_back(node->concat()->right); + worklist.push_back(node->concat()->left); + } + } else if (node->tag >= FLAT) { + ABSL_INTERNAL_CHECK(node->length <= TagToLength(node->tag), + ReportError(root, node)); + } else if (node->tag == EXTERNAL) { + ABSL_INTERNAL_CHECK(node->external()->base != nullptr, + ReportError(root, node)); + } else if (node->tag == SUBSTRING) { + ABSL_INTERNAL_CHECK( + node->substring()->start < node->substring()->child->length, + ReportError(root, node)); + ABSL_INTERNAL_CHECK(node->substring()->start + node->length <= + node->substring()->child->length, + ReportError(root, node)); + } + } while (!worklist.empty()); + return true; +} + +// Traverses the tree and computes the total memory allocated. +/* static */ size_t Cord::MemoryUsageAux(const CordRep* rep) { + size_t total_mem_usage = 0; + + // Allow a quick exit for the common case that the root is a leaf. + if (RepMemoryUsageLeaf(rep, &total_mem_usage)) { + return total_mem_usage; + } + + // Iterate over the tree. cur_node is never a leaf node and leaf nodes will + // never be appended to tree_stack. This reduces overhead from manipulating + // tree_stack. + absl::InlinedVector<const CordRep*, kInlinedVectorSize> tree_stack; + const CordRep* cur_node = rep; + while (true) { + const CordRep* next_node = nullptr; + + if (cur_node->tag == CONCAT) { + total_mem_usage += sizeof(CordRepConcat); + const CordRep* left = cur_node->concat()->left; + if (!RepMemoryUsageLeaf(left, &total_mem_usage)) { + next_node = left; + } + + const CordRep* right = cur_node->concat()->right; + if (!RepMemoryUsageLeaf(right, &total_mem_usage)) { + if (next_node) { + tree_stack.push_back(next_node); + } + next_node = right; + } + } else { + // Since cur_node is not a leaf or a concat node it must be a substring. + assert(cur_node->tag == SUBSTRING); + total_mem_usage += sizeof(CordRepSubstring); + next_node = cur_node->substring()->child; + if (RepMemoryUsageLeaf(next_node, &total_mem_usage)) { + next_node = nullptr; + } + } + + if (!next_node) { + if (tree_stack.empty()) { + return total_mem_usage; + } + next_node = tree_stack.back(); + tree_stack.pop_back(); + } + cur_node = next_node; + } +} + +std::ostream& operator<<(std::ostream& out, const Cord& cord) { + for (absl::string_view chunk : cord.Chunks()) { + out.write(chunk.data(), chunk.size()); + } + return out; +} + +namespace strings_internal { +size_t CordTestAccess::FlatOverhead() { return kFlatOverhead; } +size_t CordTestAccess::MaxFlatLength() { return kMaxFlatLength; } +size_t CordTestAccess::FlatTagToLength(uint8_t tag) { + return TagToLength(tag); +} +uint8_t CordTestAccess::LengthToTag(size_t s) { + ABSL_INTERNAL_CHECK(s <= kMaxFlatLength, absl::StrCat("Invalid length ", s)); + return AllocatedSizeToTag(s + kFlatOverhead); +} +size_t CordTestAccess::SizeofCordRepConcat() { return sizeof(CordRepConcat); } +size_t CordTestAccess::SizeofCordRepExternal() { + return sizeof(CordRepExternal); +} +size_t CordTestAccess::SizeofCordRepSubstring() { + return sizeof(CordRepSubstring); +} +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/cord.h b/absl/strings/cord.h new file mode 100644 index 00000000..40566cba --- /dev/null +++ b/absl/strings/cord.h @@ -0,0 +1,1121 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A Cord is a sequence of characters with some unusual access propreties. +// A Cord supports efficient insertions and deletions at the start and end of +// the byte sequence, but random access reads are slower, and random access +// modifications are not supported by the API. Cord also provides cheap copies +// (using a copy-on-write strategy) and cheap substring operations. +// +// Thread safety +// ------------- +// Cord has the same thread-safety properties as many other types like +// std::string, std::vector<>, int, etc -- it is thread-compatible. In +// particular, if no thread may call a non-const method, then it is safe to +// concurrently call const methods. Copying a Cord produces a new instance that +// can be used concurrently with the original in arbitrary ways. +// +// Implementation is similar to the "Ropes" described in: +// Ropes: An alternative to strings +// Hans J. Boehm, Russ Atkinson, Michael Plass +// Software Practice and Experience, December 1995 + +#ifndef ABSL_STRINGS_CORD_H_ +#define ABSL_STRINGS_CORD_H_ + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <iostream> +#include <iterator> +#include <string> + +#include "absl/base/internal/endian.h" +#include "absl/base/internal/invoke.h" +#include "absl/base/internal/per_thread_tls.h" +#include "absl/base/macros.h" +#include "absl/base/port.h" +#include "absl/container/inlined_vector.h" +#include "absl/functional/function_ref.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/resize_uninitialized.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +class Cord; +class CordTestPeer; +template <typename Releaser> +Cord MakeCordFromExternal(absl::string_view, Releaser&&); +void CopyCordToString(const Cord& src, std::string* dst); +namespace hash_internal { +template <typename H> +H HashFragmentedCord(H, const Cord&); +} + +// A Cord is a sequence of characters. +class Cord { + private: + template <typename T> + using EnableIfString = + absl::enable_if_t<std::is_same<T, std::string>::value, int>; + + public: + // -------------------------------------------------------------------- + // Constructors, destructors and helper factories + + // Create an empty cord + constexpr Cord() noexcept; + + // Cord is copyable and efficiently movable. + // The moved-from state is valid but unspecified. + Cord(const Cord& src); + Cord(Cord&& src) noexcept; + Cord& operator=(const Cord& x); + Cord& operator=(Cord&& x) noexcept; + + // Create a cord out of "src". This constructor is explicit on + // purpose so that people do not get automatic type conversions. + explicit Cord(absl::string_view src); + Cord& operator=(absl::string_view src); + + // These are templated to avoid ambiguities for types that are convertible to + // both `absl::string_view` and `std::string`, such as `const char*`. + // + // Note that these functions reserve the right to reuse the `string&&`'s + // memory and that they will do so in the future. + template <typename T, EnableIfString<T> = 0> + explicit Cord(T&& src) : Cord(absl::string_view(src)) {} + template <typename T, EnableIfString<T> = 0> + Cord& operator=(T&& src); + + // Destroy the cord + ~Cord() { + if (contents_.is_tree()) DestroyCordSlow(); + } + + // Creates a Cord that takes ownership of external memory. The contents of + // `data` are not copied. + // + // This function takes a callable that is invoked when all Cords are + // finished with `data`. The data must remain live and unchanging until the + // releaser is called. The requirements for the releaser are that it: + // * is move constructible, + // * supports `void operator()(absl::string_view) const`, + // * does not have alignment requirement greater than what is guaranteed by + // ::operator new. This is dictated by alignof(std::max_align_t) before + // C++17 and __STDCPP_DEFAULT_NEW_ALIGNMENT__ if compiling with C++17 or + // it is supported by the implementation. + // + // Example: + // + // Cord MakeCord(BlockPool* pool) { + // Block* block = pool->NewBlock(); + // FillBlock(block); + // return absl::MakeCordFromExternal( + // block->ToStringView(), + // [pool, block](absl::string_view /*ignored*/) { + // pool->FreeBlock(block); + // }); + // } + // + // WARNING: It's likely a bug if your releaser doesn't do anything. + // For example, consider the following: + // + // void Foo(const char* buffer, int len) { + // auto c = absl::MakeCordFromExternal(absl::string_view(buffer, len), + // [](absl::string_view) {}); + // + // // BUG: If Bar() copies its cord for any reason, including keeping a + // // substring of it, the lifetime of buffer might be extended beyond + // // when Foo() returns. + // Bar(c); + // } + template <typename Releaser> + friend Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser); + + // -------------------------------------------------------------------- + // Mutations + + void Clear(); + + void Append(const Cord& src); + void Append(Cord&& src); + void Append(absl::string_view src); + template <typename T, EnableIfString<T> = 0> + void Append(T&& src); + + void Prepend(const Cord& src); + void Prepend(absl::string_view src); + template <typename T, EnableIfString<T> = 0> + void Prepend(T&& src); + + void RemovePrefix(size_t n); + void RemoveSuffix(size_t n); + + // Returns a new cord representing the subrange [pos, pos + new_size) of + // *this. If pos >= size(), the result is empty(). If + // (pos + new_size) >= size(), the result is the subrange [pos, size()). + Cord Subcord(size_t pos, size_t new_size) const; + + friend void swap(Cord& x, Cord& y) noexcept; + + // -------------------------------------------------------------------- + // Accessors + + size_t size() const; + bool empty() const; + + // Returns the approximate number of bytes pinned by this Cord. Note that + // Cords that share memory could each be "charged" independently for the same + // shared memory. + size_t EstimatedMemoryUsage() const; + + // -------------------------------------------------------------------- + // Comparators + + // Compares 'this' Cord with rhs. This function and its relatives + // treat Cords as sequences of unsigned bytes. The comparison is a + // straightforward lexicographic comparison. Return value: + // -1 'this' Cord is smaller + // 0 two Cords are equal + // 1 'this' Cord is larger + int Compare(absl::string_view rhs) const; + int Compare(const Cord& rhs) const; + + // Does 'this' cord start/end with rhs + bool StartsWith(const Cord& rhs) const; + bool StartsWith(absl::string_view rhs) const; + bool EndsWith(absl::string_view rhs) const; + bool EndsWith(const Cord& rhs) const; + + // -------------------------------------------------------------------- + // Conversion to other types + + explicit operator std::string() const; + + // Copies the contents from `src` to `*dst`. + // + // This function optimizes the case of reusing the destination std::string since it + // can reuse previously allocated capacity. However, this function does not + // guarantee that pointers previously returned by `dst->data()` remain valid + // even if `*dst` had enough capacity to hold `src`. If `*dst` is a new + // object, prefer to simply use the conversion operator to `std::string`. + friend void CopyCordToString(const Cord& src, std::string* dst); + + // -------------------------------------------------------------------- + // Iteration + + class CharIterator; + + // Type for iterating over the chunks of a `Cord`. See comments for + // `Cord::chunk_begin()`, `Cord::chunk_end()` and `Cord::Chunks()` below for + // preferred usage. + // + // Additional notes: + // * The `string_view` returned by dereferencing a valid, non-`end()` + // iterator is guaranteed to be non-empty. + // * A `ChunkIterator` object is invalidated after any non-const + // operation on the `Cord` object over which it iterates. + // * Two `ChunkIterator` objects can be equality compared if and only if + // they remain valid and iterate over the same `Cord`. + // * This is a proxy iterator. This means the `string_view` returned by the + // iterator does not live inside the Cord, and its lifetime is limited to + // the lifetime of the iterator itself. To help prevent issues, + // `ChunkIterator::reference` is not a true reference type and is + // equivalent to `value_type`. + // * The iterator keeps state that can grow for `Cord`s that contain many + // nodes and are imbalanced due to sharing. Prefer to pass this type by + // const reference instead of by value. + class ChunkIterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = absl::string_view; + using difference_type = ptrdiff_t; + using pointer = const value_type*; + using reference = value_type; + + ChunkIterator() = default; + + ChunkIterator& operator++(); + ChunkIterator operator++(int); + bool operator==(const ChunkIterator& other) const; + bool operator!=(const ChunkIterator& other) const; + reference operator*() const; + pointer operator->() const; + + friend class Cord; + friend class CharIterator; + + private: + // Constructs a `begin()` iterator from `cord`. + explicit ChunkIterator(const Cord* cord); + + // Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than + // `current_chunk_.size()`. + void RemoveChunkPrefix(size_t n); + Cord AdvanceAndReadBytes(size_t n); + void AdvanceBytes(size_t n); + // Iterates `n` bytes, where `n` is expected to be greater than or equal to + // `current_chunk_.size()`. + void AdvanceBytesSlowPath(size_t n); + + // A view into bytes of the current `CordRep`. It may only be a view to a + // suffix of bytes if this is being used by `CharIterator`. + absl::string_view current_chunk_; + // The current leaf, or `nullptr` if the iterator points to short data. + // If the current chunk is a substring node, current_leaf_ points to the + // underlying flat or external node. + absl::cord_internal::CordRep* current_leaf_ = nullptr; + // The number of bytes left in the `Cord` over which we are iterating. + size_t bytes_remaining_ = 0; + absl::InlinedVector<absl::cord_internal::CordRep*, 4> + stack_of_right_children_; + }; + + // Returns an iterator to the first chunk of the `Cord`. + // + // This is useful for getting a `ChunkIterator` outside the context of a + // range-based for-loop (in which case see `Cord::Chunks()` below). + // + // Example: + // + // absl::Cord::ChunkIterator FindAsChunk(const absl::Cord& c, + // absl::string_view s) { + // return std::find(c.chunk_begin(), c.chunk_end(), s); + // } + ChunkIterator chunk_begin() const; + // Returns an iterator one increment past the last chunk of the `Cord`. + ChunkIterator chunk_end() const; + + // Convenience wrapper over `Cord::chunk_begin()` and `Cord::chunk_end()` to + // enable range-based for-loop iteration over `Cord` chunks. + // + // Prefer to use `Cord::Chunks()` below instead of constructing this directly. + class ChunkRange { + public: + explicit ChunkRange(const Cord* cord) : cord_(cord) {} + + ChunkIterator begin() const; + ChunkIterator end() const; + + private: + const Cord* cord_; + }; + + // Returns a range for iterating over the chunks of a `Cord` with a + // range-based for-loop. + // + // Example: + // + // void ProcessChunks(const Cord& cord) { + // for (absl::string_view chunk : cord.Chunks()) { ... } + // } + // + // Note that the ordinary caveats of temporary lifetime extension apply: + // + // void Process() { + // for (absl::string_view chunk : CordFactory().Chunks()) { + // // The temporary Cord returned by CordFactory has been destroyed! + // } + // } + ChunkRange Chunks() const; + + // Type for iterating over the characters of a `Cord`. See comments for + // `Cord::char_begin()`, `Cord::char_end()` and `Cord::Chars()` below for + // preferred usage. + // + // Additional notes: + // * A `CharIterator` object is invalidated after any non-const + // operation on the `Cord` object over which it iterates. + // * Two `CharIterator` objects can be equality compared if and only if + // they remain valid and iterate over the same `Cord`. + // * The iterator keeps state that can grow for `Cord`s that contain many + // nodes and are imbalanced due to sharing. Prefer to pass this type by + // const reference instead of by value. + // * This type cannot be a forward iterator because a `Cord` can reuse + // sections of memory. This violates the requirement that if dereferencing + // two iterators returns the same object, the iterators must compare + // equal. + class CharIterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = char; + using difference_type = ptrdiff_t; + using pointer = const char*; + using reference = const char&; + + CharIterator() = default; + + CharIterator& operator++(); + CharIterator operator++(int); + bool operator==(const CharIterator& other) const; + bool operator!=(const CharIterator& other) const; + reference operator*() const; + pointer operator->() const; + + friend Cord; + + private: + explicit CharIterator(const Cord* cord) : chunk_iterator_(cord) {} + + ChunkIterator chunk_iterator_; + }; + + // Advances `*it` by `n_bytes` and returns the bytes passed as a `Cord`. + // + // `n_bytes` must be less than or equal to the number of bytes remaining for + // iteration. Otherwise the behavior is undefined. It is valid to pass + // `char_end()` and 0. + static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes); + + // Advances `*it` by `n_bytes`. + // + // `n_bytes` must be less than or equal to the number of bytes remaining for + // iteration. Otherwise the behavior is undefined. It is valid to pass + // `char_end()` and 0. + static void Advance(CharIterator* it, size_t n_bytes); + + // Returns the longest contiguous view starting at the iterator's position. + // + // `it` must be dereferenceable. + static absl::string_view ChunkRemaining(const CharIterator& it); + + // Returns an iterator to the first character of the `Cord`. + CharIterator char_begin() const; + // Returns an iterator to one past the last character of the `Cord`. + CharIterator char_end() const; + + // Convenience wrapper over `Cord::char_begin()` and `Cord::char_end()` to + // enable range-based for-loop iterator over the characters of a `Cord`. + // + // Prefer to use `Cord::Chars()` below instead of constructing this directly. + class CharRange { + public: + explicit CharRange(const Cord* cord) : cord_(cord) {} + + CharIterator begin() const; + CharIterator end() const; + + private: + const Cord* cord_; + }; + + // Returns a range for iterating over the characters of a `Cord` with a + // range-based for-loop. + // + // Example: + // + // void ProcessCord(const Cord& cord) { + // for (char c : cord.Chars()) { ... } + // } + // + // Note that the ordinary caveats of temporary lifetime extension apply: + // + // void Process() { + // for (char c : CordFactory().Chars()) { + // // The temporary Cord returned by CordFactory has been destroyed! + // } + // } + CharRange Chars() const; + + // -------------------------------------------------------------------- + // Miscellaneous + + // Get the "i"th character of 'this' and return it. + // NOTE: This routine is reasonably efficient. It is roughly + // logarithmic in the number of nodes that make up the cord. Still, + // if you need to iterate over the contents of a cord, you should + // use a CharIterator/CordIterator rather than call operator[] or Get() + // repeatedly in a loop. + // + // REQUIRES: 0 <= i < size() + char operator[](size_t i) const; + + // Flattens the cord into a single array and returns a view of the data. + // + // If the cord was already flat, the contents are not modified. + absl::string_view Flatten(); + + private: + friend class CordTestPeer; + template <typename H> + friend H absl::hash_internal::HashFragmentedCord(H, const Cord&); + friend bool operator==(const Cord& lhs, const Cord& rhs); + friend bool operator==(const Cord& lhs, absl::string_view rhs); + + // Call the provided function once for each cord chunk, in order. Unlike + // Chunks(), this API will not allocate memory. + void ForEachChunk(absl::FunctionRef<void(absl::string_view)>) const; + + // Allocates new contiguous storage for the contents of the cord. This is + // called by Flatten() when the cord was not already flat. + absl::string_view FlattenSlowPath(); + + // Actual cord contents are hidden inside the following simple + // class so that we can isolate the bulk of cord.cc from changes + // to the representation. + // + // InlineRep holds either either a tree pointer, or an array of kMaxInline + // bytes. + class InlineRep { + public: + static const unsigned char kMaxInline = 15; + static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), ""); + // Tag byte & kMaxInline means we are storing a pointer. + static const unsigned char kTreeFlag = 1 << 4; + // Tag byte & kProfiledFlag means we are profiling the Cord. + static const unsigned char kProfiledFlag = 1 << 5; + + constexpr InlineRep() : data_{} {} + InlineRep(const InlineRep& src); + InlineRep(InlineRep&& src); + InlineRep& operator=(const InlineRep& src); + InlineRep& operator=(InlineRep&& src) noexcept; + + void Swap(InlineRep* rhs); + bool empty() const; + size_t size() const; + const char* data() const; // Returns nullptr if holding pointer + void set_data(const char* data, size_t n, + bool nullify_tail); // Discards pointer, if any + char* set_data(size_t n); // Write data to the result + // Returns nullptr if holding bytes + absl::cord_internal::CordRep* tree() const; + // Discards old pointer, if any + void set_tree(absl::cord_internal::CordRep* rep); + // Replaces a tree with a new root. This is faster than set_tree, but it + // should only be used when it's clear that the old rep was a tree. + void replace_tree(absl::cord_internal::CordRep* rep); + // Returns non-null iff was holding a pointer + absl::cord_internal::CordRep* clear(); + // Convert to pointer if necessary + absl::cord_internal::CordRep* force_tree(size_t extra_hint); + void reduce_size(size_t n); // REQUIRES: holding data + void remove_prefix(size_t n); // REQUIRES: holding data + void AppendArray(const char* src_data, size_t src_size); + absl::string_view FindFlatStartPiece() const; + void AppendTree(absl::cord_internal::CordRep* tree); + void PrependTree(absl::cord_internal::CordRep* tree); + void GetAppendRegion(char** region, size_t* size, size_t max_length); + void GetAppendRegion(char** region, size_t* size); + bool IsSame(const InlineRep& other) const { + return memcmp(data_, other.data_, sizeof(data_)) == 0; + } + int BitwiseCompare(const InlineRep& other) const { + uint64_t x, y; + // Use memcpy to avoid anti-aliasing issues. + memcpy(&x, data_, sizeof(x)); + memcpy(&y, other.data_, sizeof(y)); + if (x == y) { + memcpy(&x, data_ + 8, sizeof(x)); + memcpy(&y, other.data_ + 8, sizeof(y)); + if (x == y) return 0; + } + return absl::big_endian::FromHost64(x) < absl::big_endian::FromHost64(y) + ? -1 + : 1; + } + void CopyTo(std::string* dst) const { + // memcpy is much faster when operating on a known size. On most supported + // platforms, the small std::string optimization is large enough that resizing + // to 15 bytes does not cause a memory allocation. + absl::strings_internal::STLStringResizeUninitialized(dst, + sizeof(data_) - 1); + memcpy(&(*dst)[0], data_, sizeof(data_) - 1); + // erase is faster than resize because the logic for memory allocation is + // not needed. + dst->erase(data_[kMaxInline]); + } + + // Copies the inline contents into `dst`. Assumes the cord is not empty. + void CopyToArray(char* dst) const; + + bool is_tree() const { return data_[kMaxInline] > kMaxInline; } + + private: + friend class Cord; + + void AssignSlow(const InlineRep& src); + // Unrefs the tree, stops profiling, and zeroes the contents + void ClearSlow(); + + // If the data has length <= kMaxInline, we store it in data_[0..len-1], + // and store the length in data_[kMaxInline]. Else we store it in a tree + // and store a pointer to that tree in data_[0..sizeof(CordRep*)-1]. + alignas(absl::cord_internal::CordRep*) char data_[kMaxInline + 1]; + }; + InlineRep contents_; + + // Helper for MemoryUsage() + static size_t MemoryUsageAux(const absl::cord_internal::CordRep* rep); + + // Helper for GetFlat() + static bool GetFlatAux(absl::cord_internal::CordRep* rep, + absl::string_view* fragment); + + // Helper for ForEachChunk() + static void ForEachChunkAux( + absl::cord_internal::CordRep* rep, + absl::FunctionRef<void(absl::string_view)> callback); + + // The destructor for non-empty Cords. + void DestroyCordSlow(); + + // Out-of-line implementation of slower parts of logic. + void CopyToArraySlowPath(char* dst) const; + int CompareSlowPath(absl::string_view rhs, size_t compared_size, + size_t size_to_compare) const; + int CompareSlowPath(const Cord& rhs, size_t compared_size, + size_t size_to_compare) const; + bool EqualsImpl(absl::string_view rhs, size_t size_to_compare) const; + bool EqualsImpl(const Cord& rhs, size_t size_to_compare) const; + int CompareImpl(const Cord& rhs) const; + + template <typename ResultType, typename RHS> + friend ResultType GenericCompare(const Cord& lhs, const RHS& rhs, + size_t size_to_compare); + static absl::string_view GetFirstChunk(const Cord& c); + static absl::string_view GetFirstChunk(absl::string_view sv); + + // Returns a new reference to contents_.tree(), or steals an existing + // reference if called on an rvalue. + absl::cord_internal::CordRep* TakeRep() const&; + absl::cord_internal::CordRep* TakeRep() &&; + + // Helper for Append() + template <typename C> + void AppendImpl(C&& src); +}; + +ABSL_NAMESPACE_END +} // namespace absl + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// allow a Cord to be logged +extern std::ostream& operator<<(std::ostream& out, const Cord& cord); + +// ------------------------------------------------------------------ +// Internal details follow. Clients should ignore. + +namespace cord_internal { + +// Fast implementation of memmove for up to 15 bytes. This implementation is +// safe for overlapping regions. If nullify_tail is true, the destination is +// padded with '\0' up to 16 bytes. +inline void SmallMemmove(char* dst, const char* src, size_t n, + bool nullify_tail = false) { + if (n >= 8) { + assert(n <= 16); + uint64_t buf1; + uint64_t buf2; + memcpy(&buf1, src, 8); + memcpy(&buf2, src + n - 8, 8); + if (nullify_tail) { + memset(dst + 8, 0, 8); + } + memcpy(dst, &buf1, 8); + memcpy(dst + n - 8, &buf2, 8); + } else if (n >= 4) { + uint32_t buf1; + uint32_t buf2; + memcpy(&buf1, src, 4); + memcpy(&buf2, src + n - 4, 4); + if (nullify_tail) { + memset(dst + 4, 0, 4); + memset(dst + 8, 0, 8); + } + memcpy(dst, &buf1, 4); + memcpy(dst + n - 4, &buf2, 4); + } else { + if (n != 0) { + dst[0] = src[0]; + dst[n / 2] = src[n / 2]; + dst[n - 1] = src[n - 1]; + } + if (nullify_tail) { + memset(dst + 8, 0, 8); + memset(dst + n, 0, 8); + } + } +} + +struct ExternalRepReleaserPair { + CordRep* rep; + void* releaser_address; +}; + +// Allocates a new external `CordRep` and returns a pointer to it and a pointer +// to `releaser_size` bytes where the desired releaser can be constructed. +// Expects `data` to be non-empty. +ExternalRepReleaserPair NewExternalWithUninitializedReleaser( + absl::string_view data, ExternalReleaserInvoker invoker, + size_t releaser_size); + +// Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer +// to it, or `nullptr` if `data` was empty. +template <typename Releaser> +// NOLINTNEXTLINE - suppress clang-tidy raw pointer return. +CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) { + static_assert( +#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) + alignof(Releaser) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__, +#else + alignof(Releaser) <= alignof(max_align_t), +#endif + "Releasers with alignment requirement greater than what is returned by " + "default `::operator new()` are not supported."); + + using ReleaserType = absl::decay_t<Releaser>; + if (data.empty()) { + // Never create empty external nodes. + ::absl::base_internal::Invoke( + ReleaserType(std::forward<Releaser>(releaser)), data); + return nullptr; + } + + auto releaser_invoker = [](void* type_erased_releaser, absl::string_view d) { + auto* my_releaser = static_cast<ReleaserType*>(type_erased_releaser); + ::absl::base_internal::Invoke(std::move(*my_releaser), d); + my_releaser->~ReleaserType(); + return sizeof(Releaser); + }; + + ExternalRepReleaserPair external = NewExternalWithUninitializedReleaser( + data, releaser_invoker, sizeof(releaser)); + ::new (external.releaser_address) + ReleaserType(std::forward<Releaser>(releaser)); + return external.rep; +} + +// Overload for function reference types that dispatches using a function +// pointer because there are no `alignof()` or `sizeof()` a function reference. +// NOLINTNEXTLINE - suppress clang-tidy raw pointer return. +inline CordRep* NewExternalRep(absl::string_view data, + void (&releaser)(absl::string_view)) { + return NewExternalRep(data, &releaser); +} + +} // namespace cord_internal + +template <typename Releaser> +Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) { + Cord cord; + cord.contents_.set_tree(::absl::cord_internal::NewExternalRep( + data, std::forward<Releaser>(releaser))); + return cord; +} + +inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) { + cord_internal::SmallMemmove(data_, src.data_, sizeof(data_)); +} + +inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) { + memcpy(data_, src.data_, sizeof(data_)); + memset(src.data_, 0, sizeof(data_)); +} + +inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) { + if (this == &src) { + return *this; + } + if (!is_tree() && !src.is_tree()) { + cord_internal::SmallMemmove(data_, src.data_, sizeof(data_)); + return *this; + } + AssignSlow(src); + return *this; +} + +inline Cord::InlineRep& Cord::InlineRep::operator=( + Cord::InlineRep&& src) noexcept { + if (is_tree()) { + ClearSlow(); + } + memcpy(data_, src.data_, sizeof(data_)); + memset(src.data_, 0, sizeof(data_)); + return *this; +} + +inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) { + if (rhs == this) { + return; + } + + Cord::InlineRep tmp; + cord_internal::SmallMemmove(tmp.data_, data_, sizeof(data_)); + cord_internal::SmallMemmove(data_, rhs->data_, sizeof(data_)); + cord_internal::SmallMemmove(rhs->data_, tmp.data_, sizeof(data_)); +} + +inline const char* Cord::InlineRep::data() const { + return is_tree() ? nullptr : data_; +} + +inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const { + if (is_tree()) { + absl::cord_internal::CordRep* rep; + memcpy(&rep, data_, sizeof(rep)); + return rep; + } else { + return nullptr; + } +} + +inline bool Cord::InlineRep::empty() const { return data_[kMaxInline] == 0; } + +inline size_t Cord::InlineRep::size() const { + const char tag = data_[kMaxInline]; + if (tag <= kMaxInline) return tag; + return static_cast<size_t>(tree()->length); +} + +inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { + if (rep == nullptr) { + memset(data_, 0, sizeof(data_)); + } else { + bool was_tree = is_tree(); + memcpy(data_, &rep, sizeof(rep)); + memset(data_ + sizeof(rep), 0, sizeof(data_) - sizeof(rep) - 1); + if (!was_tree) { + data_[kMaxInline] = kTreeFlag; + } + } +} + +inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) { + ABSL_ASSERT(is_tree()); + if (ABSL_PREDICT_FALSE(rep == nullptr)) { + set_tree(rep); + return; + } + memcpy(data_, &rep, sizeof(rep)); + memset(data_ + sizeof(rep), 0, sizeof(data_) - sizeof(rep) - 1); +} + +inline absl::cord_internal::CordRep* Cord::InlineRep::clear() { + const char tag = data_[kMaxInline]; + absl::cord_internal::CordRep* result = nullptr; + if (tag > kMaxInline) { + memcpy(&result, data_, sizeof(result)); + } + memset(data_, 0, sizeof(data_)); // Clear the cord + return result; +} + +inline void Cord::InlineRep::CopyToArray(char* dst) const { + assert(!is_tree()); + size_t n = data_[kMaxInline]; + assert(n != 0); + cord_internal::SmallMemmove(dst, data_, n); +} + +constexpr inline Cord::Cord() noexcept {} + +inline Cord& Cord::operator=(const Cord& x) { + contents_ = x.contents_; + return *this; +} + +inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {} + +inline Cord& Cord::operator=(Cord&& x) noexcept { + contents_ = std::move(x.contents_); + return *this; +} + +template <typename T, Cord::EnableIfString<T>> +inline Cord& Cord::operator=(T&& src) { + *this = absl::string_view(src); + return *this; +} + +inline size_t Cord::size() const { + // Length is 1st field in str.rep_ + return contents_.size(); +} + +inline bool Cord::empty() const { return contents_.empty(); } + +inline size_t Cord::EstimatedMemoryUsage() const { + size_t result = sizeof(Cord); + if (const absl::cord_internal::CordRep* rep = contents_.tree()) { + result += MemoryUsageAux(rep); + } + return result; +} + +inline absl::string_view Cord::Flatten() { + absl::cord_internal::CordRep* rep = contents_.tree(); + if (rep == nullptr) { + return absl::string_view(contents_.data(), contents_.size()); + } else { + absl::string_view already_flat_contents; + if (GetFlatAux(rep, &already_flat_contents)) { + return already_flat_contents; + } + } + return FlattenSlowPath(); +} + +inline void Cord::Append(absl::string_view src) { + contents_.AppendArray(src.data(), src.size()); +} + +template <typename T, Cord::EnableIfString<T>> +inline void Cord::Append(T&& src) { + // Note that this function reserves the right to reuse the `string&&`'s + // memory and that it will do so in the future. + Append(absl::string_view(src)); +} + +template <typename T, Cord::EnableIfString<T>> +inline void Cord::Prepend(T&& src) { + // Note that this function reserves the right to reuse the `string&&`'s + // memory and that it will do so in the future. + Prepend(absl::string_view(src)); +} + +inline int Cord::Compare(const Cord& rhs) const { + if (!contents_.is_tree() && !rhs.contents_.is_tree()) { + return contents_.BitwiseCompare(rhs.contents_); + } + + return CompareImpl(rhs); +} + +// Does 'this' cord start/end with rhs +inline bool Cord::StartsWith(const Cord& rhs) const { + if (contents_.IsSame(rhs.contents_)) return true; + size_t rhs_size = rhs.size(); + if (size() < rhs_size) return false; + return EqualsImpl(rhs, rhs_size); +} + +inline bool Cord::StartsWith(absl::string_view rhs) const { + size_t rhs_size = rhs.size(); + if (size() < rhs_size) return false; + return EqualsImpl(rhs, rhs_size); +} + +inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) + : bytes_remaining_(cord->size()) { + if (cord->empty()) return; + if (cord->contents_.is_tree()) { + stack_of_right_children_.push_back(cord->contents_.tree()); + operator++(); + } else { + current_chunk_ = absl::string_view(cord->contents_.data(), cord->size()); + } +} + +inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) { + ChunkIterator tmp(*this); + operator++(); + return tmp; +} + +inline bool Cord::ChunkIterator::operator==(const ChunkIterator& other) const { + return bytes_remaining_ == other.bytes_remaining_; +} + +inline bool Cord::ChunkIterator::operator!=(const ChunkIterator& other) const { + return !(*this == other); +} + +inline Cord::ChunkIterator::reference Cord::ChunkIterator::operator*() const { + assert(bytes_remaining_ != 0); + return current_chunk_; +} + +inline Cord::ChunkIterator::pointer Cord::ChunkIterator::operator->() const { + assert(bytes_remaining_ != 0); + return ¤t_chunk_; +} + +inline void Cord::ChunkIterator::RemoveChunkPrefix(size_t n) { + assert(n < current_chunk_.size()); + current_chunk_.remove_prefix(n); + bytes_remaining_ -= n; +} + +inline void Cord::ChunkIterator::AdvanceBytes(size_t n) { + if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) { + RemoveChunkPrefix(n); + } else if (n != 0) { + AdvanceBytesSlowPath(n); + } +} + +inline Cord::ChunkIterator Cord::chunk_begin() const { + return ChunkIterator(this); +} + +inline Cord::ChunkIterator Cord::chunk_end() const { return ChunkIterator(); } + +inline Cord::ChunkIterator Cord::ChunkRange::begin() const { + return cord_->chunk_begin(); +} + +inline Cord::ChunkIterator Cord::ChunkRange::end() const { + return cord_->chunk_end(); +} + +inline Cord::ChunkRange Cord::Chunks() const { return ChunkRange(this); } + +inline Cord::CharIterator& Cord::CharIterator::operator++() { + if (ABSL_PREDICT_TRUE(chunk_iterator_->size() > 1)) { + chunk_iterator_.RemoveChunkPrefix(1); + } else { + ++chunk_iterator_; + } + return *this; +} + +inline Cord::CharIterator Cord::CharIterator::operator++(int) { + CharIterator tmp(*this); + operator++(); + return tmp; +} + +inline bool Cord::CharIterator::operator==(const CharIterator& other) const { + return chunk_iterator_ == other.chunk_iterator_; +} + +inline bool Cord::CharIterator::operator!=(const CharIterator& other) const { + return !(*this == other); +} + +inline Cord::CharIterator::reference Cord::CharIterator::operator*() const { + return *chunk_iterator_->data(); +} + +inline Cord::CharIterator::pointer Cord::CharIterator::operator->() const { + return chunk_iterator_->data(); +} + +inline Cord Cord::AdvanceAndRead(CharIterator* it, size_t n_bytes) { + assert(it != nullptr); + return it->chunk_iterator_.AdvanceAndReadBytes(n_bytes); +} + +inline void Cord::Advance(CharIterator* it, size_t n_bytes) { + assert(it != nullptr); + it->chunk_iterator_.AdvanceBytes(n_bytes); +} + +inline absl::string_view Cord::ChunkRemaining(const CharIterator& it) { + return *it.chunk_iterator_; +} + +inline Cord::CharIterator Cord::char_begin() const { + return CharIterator(this); +} + +inline Cord::CharIterator Cord::char_end() const { return CharIterator(); } + +inline Cord::CharIterator Cord::CharRange::begin() const { + return cord_->char_begin(); +} + +inline Cord::CharIterator Cord::CharRange::end() const { + return cord_->char_end(); +} + +inline Cord::CharRange Cord::Chars() const { return CharRange(this); } + +inline void Cord::ForEachChunk( + absl::FunctionRef<void(absl::string_view)> callback) const { + absl::cord_internal::CordRep* rep = contents_.tree(); + if (rep == nullptr) { + callback(absl::string_view(contents_.data(), contents_.size())); + } else { + return ForEachChunkAux(rep, callback); + } +} + +// Nonmember Cord-to-Cord relational operarators. +inline bool operator==(const Cord& lhs, const Cord& rhs) { + if (lhs.contents_.IsSame(rhs.contents_)) return true; + size_t rhs_size = rhs.size(); + if (lhs.size() != rhs_size) return false; + return lhs.EqualsImpl(rhs, rhs_size); +} + +inline bool operator!=(const Cord& x, const Cord& y) { return !(x == y); } +inline bool operator<(const Cord& x, const Cord& y) { + return x.Compare(y) < 0; +} +inline bool operator>(const Cord& x, const Cord& y) { + return x.Compare(y) > 0; +} +inline bool operator<=(const Cord& x, const Cord& y) { + return x.Compare(y) <= 0; +} +inline bool operator>=(const Cord& x, const Cord& y) { + return x.Compare(y) >= 0; +} + +// Nonmember Cord-to-absl::string_view relational operators. +// +// Due to implicit conversions, these also enable comparisons of Cord with +// with std::string, ::string, and const char*. +inline bool operator==(const Cord& lhs, absl::string_view rhs) { + size_t lhs_size = lhs.size(); + size_t rhs_size = rhs.size(); + if (lhs_size != rhs_size) return false; + return lhs.EqualsImpl(rhs, rhs_size); +} + +inline bool operator==(absl::string_view x, const Cord& y) { return y == x; } +inline bool operator!=(const Cord& x, absl::string_view y) { return !(x == y); } +inline bool operator!=(absl::string_view x, const Cord& y) { return !(x == y); } +inline bool operator<(const Cord& x, absl::string_view y) { + return x.Compare(y) < 0; +} +inline bool operator<(absl::string_view x, const Cord& y) { + return y.Compare(x) > 0; +} +inline bool operator>(const Cord& x, absl::string_view y) { return y < x; } +inline bool operator>(absl::string_view x, const Cord& y) { return y < x; } +inline bool operator<=(const Cord& x, absl::string_view y) { return !(y < x); } +inline bool operator<=(absl::string_view x, const Cord& y) { return !(y < x); } +inline bool operator>=(const Cord& x, absl::string_view y) { return !(x < y); } +inline bool operator>=(absl::string_view x, const Cord& y) { return !(x < y); } + +// Overload of swap for Cord. The use of non-const references is +// required. :( +inline void swap(Cord& x, Cord& y) noexcept { y.contents_.Swap(&x.contents_); } + +// Some internals exposed to test code. +namespace strings_internal { +class CordTestAccess { + public: + static size_t FlatOverhead(); + static size_t MaxFlatLength(); + static size_t SizeofCordRepConcat(); + static size_t SizeofCordRepExternal(); + static size_t SizeofCordRepSubstring(); + static size_t FlatTagToLength(uint8_t tag); + static uint8_t LengthToTag(size_t s); +}; +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORD_H_ diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc new file mode 100644 index 00000000..434f3a24 --- /dev/null +++ b/absl/strings/cord_test.cc @@ -0,0 +1,1526 @@ +#include "absl/strings/cord.h" + +#include <algorithm> +#include <climits> +#include <cstdio> +#include <iterator> +#include <map> +#include <numeric> +#include <random> +#include <sstream> +#include <type_traits> +#include <utility> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/casts.h" +#include "absl/base/config.h" +#include "absl/base/internal/endian.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/container/fixed_array.h" +#include "absl/strings/cord_test_helpers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +typedef std::mt19937_64 RandomEngine; + +static std::string RandomLowercaseString(RandomEngine* rng); +static std::string RandomLowercaseString(RandomEngine* rng, size_t length); + +static int GetUniformRandomUpTo(RandomEngine* rng, int upper_bound) { + if (upper_bound > 0) { + std::uniform_int_distribution<int> uniform(0, upper_bound - 1); + return uniform(*rng); + } else { + return 0; + } +} + +static size_t GetUniformRandomUpTo(RandomEngine* rng, size_t upper_bound) { + if (upper_bound > 0) { + std::uniform_int_distribution<size_t> uniform(0, upper_bound - 1); + return uniform(*rng); + } else { + return 0; + } +} + +static int32_t GenerateSkewedRandom(RandomEngine* rng, int max_log) { + const uint32_t base = (*rng)() % (max_log + 1); + const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u; + return (*rng)() & mask; +} + +static std::string RandomLowercaseString(RandomEngine* rng) { + int length; + std::bernoulli_distribution one_in_1k(0.001); + std::bernoulli_distribution one_in_10k(0.0001); + // With low probability, make a large fragment + if (one_in_10k(*rng)) { + length = GetUniformRandomUpTo(rng, 1048576); + } else if (one_in_1k(*rng)) { + length = GetUniformRandomUpTo(rng, 10000); + } else { + length = GenerateSkewedRandom(rng, 10); + } + return RandomLowercaseString(rng, length); +} + +static std::string RandomLowercaseString(RandomEngine* rng, size_t length) { + std::string result(length, '\0'); + std::uniform_int_distribution<int> chars('a', 'z'); + std::generate(result.begin(), result.end(), [&]() { + return static_cast<char>(chars(*rng)); + }); + return result; +} + +static void DoNothing(absl::string_view /* data */, void* /* arg */) {} + +static void DeleteExternalString(absl::string_view data, void* arg) { + std::string* s = reinterpret_cast<std::string*>(arg); + EXPECT_EQ(data, *s); + delete s; +} + +// Add "s" to *dst via `MakeCordFromExternal` +static void AddExternalMemory(absl::string_view s, absl::Cord* dst) { + std::string* str = new std::string(s.data(), s.size()); + dst->Append(absl::MakeCordFromExternal(*str, [str](absl::string_view data) { + DeleteExternalString(data, str); + })); +} + +static void DumpGrowth() { + absl::Cord str; + for (int i = 0; i < 1000; i++) { + char c = 'a' + i % 26; + str.Append(absl::string_view(&c, 1)); + } +} + +// Make a Cord with some number of fragments. Return the size (in bytes) +// of the smallest fragment. +static size_t AppendWithFragments(const std::string& s, RandomEngine* rng, + absl::Cord* cord) { + size_t j = 0; + const size_t max_size = s.size() / 5; // Make approx. 10 fragments + size_t min_size = max_size; // size of smallest fragment + while (j < s.size()) { + size_t N = 1 + GetUniformRandomUpTo(rng, max_size); + if (N > (s.size() - j)) { + N = s.size() - j; + } + if (N < min_size) { + min_size = N; + } + + std::bernoulli_distribution coin_flip(0.5); + if (coin_flip(*rng)) { + // Grow by adding an external-memory. + AddExternalMemory(absl::string_view(s.data() + j, N), cord); + } else { + cord->Append(absl::string_view(s.data() + j, N)); + } + j += N; + } + return min_size; +} + +// Add an external memory that contains the specified std::string to cord +static void AddNewStringBlock(const std::string& str, absl::Cord* dst) { + char* data = new char[str.size()]; + memcpy(data, str.data(), str.size()); + dst->Append(absl::MakeCordFromExternal( + absl::string_view(data, str.size()), + [](absl::string_view s) { delete[] s.data(); })); +} + +// Make a Cord out of many different types of nodes. +static absl::Cord MakeComposite() { + absl::Cord cord; + cord.Append("the"); + AddExternalMemory(" quick brown", &cord); + AddExternalMemory(" fox jumped", &cord); + + absl::Cord full(" over"); + AddExternalMemory(" the lazy", &full); + AddNewStringBlock(" dog slept the whole day away", &full); + absl::Cord substring = full.Subcord(0, 18); + + // Make substring long enough to defeat the copying fast path in Append. + substring.Append(std::string(1000, '.')); + cord.Append(substring); + cord = cord.Subcord(0, cord.size() - 998); // Remove most of extra junk + + return cord; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class CordTestPeer { + public: + static void ForEachChunk( + const Cord& c, absl::FunctionRef<void(absl::string_view)> callback) { + c.ForEachChunk(callback); + } +}; + +ABSL_NAMESPACE_END +} // namespace absl + +TEST(Cord, AllFlatSizes) { + using absl::strings_internal::CordTestAccess; + + for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) { + // Make a std::string of length s. + std::string src; + while (src.size() < s) { + src.push_back('a' + (src.size() % 26)); + } + + absl::Cord dst(src); + EXPECT_EQ(std::string(dst), src) << s; + } +} + +// We create a Cord at least 128GB in size using the fact that Cords can +// internally reference-count; thus the Cord is enormous without actually +// consuming very much memory. +TEST(GigabyteCord, FromExternal) { + const size_t one_gig = 1024U * 1024U * 1024U; + size_t max_size = 2 * one_gig; + if (sizeof(max_size) > 4) max_size = 128 * one_gig; + + size_t length = 128 * 1024; + char* data = new char[length]; + absl::Cord from = absl::MakeCordFromExternal( + absl::string_view(data, length), + [](absl::string_view sv) { delete[] sv.data(); }); + + // This loop may seem odd due to its combination of exponential doubling of + // size and incremental size increases. We do it incrementally to be sure the + // Cord will need rebalancing and will exercise code that, in the past, has + // caused crashes in production. We grow exponentially so that the code will + // execute in a reasonable amount of time. + absl::Cord c; + ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size()); + c.Append(from); + while (c.size() < max_size) { + c.Append(c); + c.Append(from); + c.Append(from); + c.Append(from); + c.Append(from); + } + + for (int i = 0; i < 1024; ++i) { + c.Append(from); + } + ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size()); + // Note: on a 32-bit build, this comes out to 2,818,048,000 bytes. + // Note: on a 64-bit build, this comes out to 171,932,385,280 bytes. +} + +static absl::Cord MakeExternalCord(int size) { + char* buffer = new char[size]; + memset(buffer, 'x', size); + absl::Cord cord; + cord.Append(absl::MakeCordFromExternal( + absl::string_view(buffer, size), + [](absl::string_view s) { delete[] s.data(); })); + return cord; +} + +// Extern to fool clang that this is not constant. Needed to suppress +// a warning of unsafe code we want to test. +extern bool my_unique_true_boolean; +bool my_unique_true_boolean = true; + +TEST(Cord, Assignment) { + absl::Cord x(absl::string_view("hi there")); + absl::Cord y(x); + ASSERT_EQ(std::string(x), "hi there"); + ASSERT_EQ(std::string(y), "hi there"); + ASSERT_TRUE(x == y); + ASSERT_TRUE(x <= y); + ASSERT_TRUE(y <= x); + + x = absl::string_view("foo"); + ASSERT_EQ(std::string(x), "foo"); + ASSERT_EQ(std::string(y), "hi there"); + ASSERT_TRUE(x < y); + ASSERT_TRUE(y > x); + ASSERT_TRUE(x != y); + ASSERT_TRUE(x <= y); + ASSERT_TRUE(y >= x); + + x = "foo"; + ASSERT_EQ(x, "foo"); + + // Test that going from inline rep to tree we don't leak memory. + std::vector<std::pair<absl::string_view, absl::string_view>> + test_string_pairs = {{"hi there", "foo"}, + {"loooooong coooooord", "short cord"}, + {"short cord", "loooooong coooooord"}, + {"loooooong coooooord1", "loooooong coooooord2"}}; + for (std::pair<absl::string_view, absl::string_view> test_strings : + test_string_pairs) { + absl::Cord tmp(test_strings.first); + absl::Cord z(std::move(tmp)); + ASSERT_EQ(std::string(z), test_strings.first); + tmp = test_strings.second; + z = std::move(tmp); + ASSERT_EQ(std::string(z), test_strings.second); + } + { + // Test that self-move assignment doesn't crash/leak. + // Do not write such code! + absl::Cord my_small_cord("foo"); + absl::Cord my_big_cord("loooooong coooooord"); + // Bypass clang's warning on self move-assignment. + absl::Cord* my_small_alias = + my_unique_true_boolean ? &my_small_cord : &my_big_cord; + absl::Cord* my_big_alias = + !my_unique_true_boolean ? &my_small_cord : &my_big_cord; + + *my_small_alias = std::move(my_small_cord); + *my_big_alias = std::move(my_big_cord); + // my_small_cord and my_big_cord are in an unspecified but valid + // state, and will be correctly destroyed here. + } +} + +TEST(Cord, StartsEndsWith) { + absl::Cord x(absl::string_view("abcde")); + absl::Cord empty(""); + + ASSERT_TRUE(x.StartsWith(absl::Cord("abcde"))); + ASSERT_TRUE(x.StartsWith(absl::Cord("abc"))); + ASSERT_TRUE(x.StartsWith(absl::Cord(""))); + ASSERT_TRUE(empty.StartsWith(absl::Cord(""))); + ASSERT_TRUE(x.EndsWith(absl::Cord("abcde"))); + ASSERT_TRUE(x.EndsWith(absl::Cord("cde"))); + ASSERT_TRUE(x.EndsWith(absl::Cord(""))); + ASSERT_TRUE(empty.EndsWith(absl::Cord(""))); + + ASSERT_TRUE(!x.StartsWith(absl::Cord("xyz"))); + ASSERT_TRUE(!empty.StartsWith(absl::Cord("xyz"))); + ASSERT_TRUE(!x.EndsWith(absl::Cord("xyz"))); + ASSERT_TRUE(!empty.EndsWith(absl::Cord("xyz"))); + + ASSERT_TRUE(x.StartsWith("abcde")); + ASSERT_TRUE(x.StartsWith("abc")); + ASSERT_TRUE(x.StartsWith("")); + ASSERT_TRUE(empty.StartsWith("")); + ASSERT_TRUE(x.EndsWith("abcde")); + ASSERT_TRUE(x.EndsWith("cde")); + ASSERT_TRUE(x.EndsWith("")); + ASSERT_TRUE(empty.EndsWith("")); + + ASSERT_TRUE(!x.StartsWith("xyz")); + ASSERT_TRUE(!empty.StartsWith("xyz")); + ASSERT_TRUE(!x.EndsWith("xyz")); + ASSERT_TRUE(!empty.EndsWith("xyz")); +} + +TEST(Cord, Subcord) { + RandomEngine rng(testing::GTEST_FLAG(random_seed)); + const std::string s = RandomLowercaseString(&rng, 1024); + + absl::Cord a; + AppendWithFragments(s, &rng, &a); + ASSERT_EQ(s.size(), a.size()); + + // Check subcords of a, from a variety of interesting points. + std::set<size_t> positions; + for (int i = 0; i <= 32; ++i) { + positions.insert(i); + positions.insert(i * 32 - 1); + positions.insert(i * 32); + positions.insert(i * 32 + 1); + positions.insert(a.size() - i); + } + positions.insert(237); + positions.insert(732); + for (size_t pos : positions) { + if (pos > a.size()) continue; + for (size_t end_pos : positions) { + if (end_pos < pos || end_pos > a.size()) continue; + absl::Cord sa = a.Subcord(pos, end_pos - pos); + EXPECT_EQ(absl::string_view(s).substr(pos, end_pos - pos), + std::string(sa)) + << a; + } + } + + // Do the same thing for an inline cord. + const std::string sh = "short"; + absl::Cord c(sh); + for (size_t pos = 0; pos <= sh.size(); ++pos) { + for (size_t n = 0; n <= sh.size() - pos; ++n) { + absl::Cord sc = c.Subcord(pos, n); + EXPECT_EQ(sh.substr(pos, n), std::string(sc)) << c; + } + } + + // Check subcords of subcords. + absl::Cord sa = a.Subcord(0, a.size()); + std::string ss = s.substr(0, s.size()); + while (sa.size() > 1) { + sa = sa.Subcord(1, sa.size() - 2); + ss = ss.substr(1, ss.size() - 2); + EXPECT_EQ(ss, std::string(sa)) << a; + if (HasFailure()) break; // halt cascade + } + + // It is OK to ask for too much. + sa = a.Subcord(0, a.size() + 1); + EXPECT_EQ(s, std::string(sa)); + + // It is OK to ask for something beyond the end. + sa = a.Subcord(a.size() + 1, 0); + EXPECT_TRUE(sa.empty()); + sa = a.Subcord(a.size() + 1, 1); + EXPECT_TRUE(sa.empty()); +} + +TEST(Cord, Swap) { + absl::string_view a("Dexter"); + absl::string_view b("Mandark"); + absl::Cord x(a); + absl::Cord y(b); + swap(x, y); + ASSERT_EQ(x, absl::Cord(b)); + ASSERT_EQ(y, absl::Cord(a)); +} + +static void VerifyCopyToString(const absl::Cord& cord) { + std::string initially_empty; + absl::CopyCordToString(cord, &initially_empty); + EXPECT_EQ(initially_empty, cord); + + constexpr size_t kInitialLength = 1024; + std::string has_initial_contents(kInitialLength, 'x'); + const char* address_before_copy = has_initial_contents.data(); + absl::CopyCordToString(cord, &has_initial_contents); + EXPECT_EQ(has_initial_contents, cord); + + if (cord.size() <= kInitialLength) { + EXPECT_EQ(has_initial_contents.data(), address_before_copy) + << "CopyCordToString allocated new std::string storage; " + "has_initial_contents = \"" + << has_initial_contents << "\""; + } +} + +TEST(Cord, CopyToString) { + VerifyCopyToString(absl::Cord()); + VerifyCopyToString(absl::Cord("small cord")); + VerifyCopyToString( + absl::MakeFragmentedCord({"fragmented ", "cord ", "to ", "test ", + "copying ", "to ", "a ", "string."})); +} + +static bool IsFlat(const absl::Cord& c) { + return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end(); +} + +static void VerifyFlatten(absl::Cord c) { + std::string old_contents(c); + absl::string_view old_flat; + bool already_flat_and_non_empty = IsFlat(c) && !c.empty(); + if (already_flat_and_non_empty) { + old_flat = *c.chunk_begin(); + } + absl::string_view new_flat = c.Flatten(); + + // Verify that the contents of the flattened Cord are correct. + EXPECT_EQ(new_flat, old_contents); + EXPECT_EQ(std::string(c), old_contents); + + // If the Cord contained data and was already flat, verify that the data + // wasn't copied. + if (already_flat_and_non_empty) { + EXPECT_EQ(old_flat.data(), new_flat.data()) + << "Allocated new memory even though the Cord was already flat."; + } + + // Verify that the flattened Cord is in fact flat. + EXPECT_TRUE(IsFlat(c)); +} + +TEST(Cord, Flatten) { + VerifyFlatten(absl::Cord()); + VerifyFlatten(absl::Cord("small cord")); + VerifyFlatten(absl::Cord("larger than small buffer optimization")); + VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"})); + + // Test with a cord that is longer than the largest flat buffer + RandomEngine rng(testing::GTEST_FLAG(random_seed)); + VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192))); +} + +// Test data +namespace { +class TestData { + private: + std::vector<std::string> data_; + + // Return a std::string of the specified length. + static std::string MakeString(int length) { + std::string result; + char buf[30]; + snprintf(buf, sizeof(buf), "(%d)", length); + while (result.size() < length) { + result += buf; + } + result.resize(length); + return result; + } + + public: + TestData() { + // short strings increasing in length by one + for (int i = 0; i < 30; i++) { + data_.push_back(MakeString(i)); + } + + // strings around half kMaxFlatLength + static const int kMaxFlatLength = 4096 - 9; + static const int kHalf = kMaxFlatLength / 2; + + for (int i = -10; i <= +10; i++) { + data_.push_back(MakeString(kHalf + i)); + } + + for (int i = -10; i <= +10; i++) { + data_.push_back(MakeString(kMaxFlatLength + i)); + } + } + + size_t size() const { return data_.size(); } + const std::string& data(size_t i) const { return data_[i]; } +}; +} // namespace + +TEST(Cord, MultipleLengths) { + TestData d; + for (size_t i = 0; i < d.size(); i++) { + std::string a = d.data(i); + + { // Construct from Cord + absl::Cord tmp(a); + absl::Cord x(tmp); + EXPECT_EQ(a, std::string(x)) << "'" << a << "'"; + } + + { // Construct from absl::string_view + absl::Cord x(a); + EXPECT_EQ(a, std::string(x)) << "'" << a << "'"; + } + + { // Append cord to self + absl::Cord self(a); + self.Append(self); + EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'"; + } + + { // Prepend cord to self + absl::Cord self(a); + self.Prepend(self); + EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'"; + } + + // Try to append/prepend others + for (size_t j = 0; j < d.size(); j++) { + std::string b = d.data(j); + + { // CopyFrom Cord + absl::Cord x(a); + absl::Cord y(b); + x = y; + EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'"; + } + + { // CopyFrom absl::string_view + absl::Cord x(a); + x = b; + EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'"; + } + + { // Cord::Append(Cord) + absl::Cord x(a); + absl::Cord y(b); + x.Append(y); + EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'"; + } + + { // Cord::Append(absl::string_view) + absl::Cord x(a); + x.Append(b); + EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'"; + } + + { // Cord::Prepend(Cord) + absl::Cord x(a); + absl::Cord y(b); + x.Prepend(y); + EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'"; + } + + { // Cord::Prepend(absl::string_view) + absl::Cord x(a); + x.Prepend(b); + EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'"; + } + } + } +} + +namespace { + +TEST(Cord, RemoveSuffixWithExternalOrSubstring) { + absl::Cord cord = absl::MakeCordFromExternal( + "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); }); + + EXPECT_EQ("foo bar baz", std::string(cord)); + + // This RemoveSuffix() will wrap the EXTERNAL node in a SUBSTRING node. + cord.RemoveSuffix(4); + EXPECT_EQ("foo bar", std::string(cord)); + + // This RemoveSuffix() will adjust the SUBSTRING node in-place. + cord.RemoveSuffix(4); + EXPECT_EQ("foo", std::string(cord)); +} + +TEST(Cord, RemoveSuffixMakesZeroLengthNode) { + absl::Cord c; + c.Append(absl::Cord(std::string(100, 'x'))); + absl::Cord other_ref = c; // Prevent inplace appends + c.Append(absl::Cord(std::string(200, 'y'))); + c.RemoveSuffix(200); + EXPECT_EQ(std::string(100, 'x'), std::string(c)); +} + +} // namespace + +// CordSpliceTest contributed by hendrie. +namespace { + +// Create a cord with an external memory block filled with 'z' +absl::Cord CordWithZedBlock(size_t size) { + char* data = new char[size]; + if (size > 0) { + memset(data, 'z', size); + } + absl::Cord cord = absl::MakeCordFromExternal( + absl::string_view(data, size), + [](absl::string_view s) { delete[] s.data(); }); + return cord; +} + +// Establish that ZedBlock does what we think it does. +TEST(CordSpliceTest, ZedBlock) { + absl::Cord blob = CordWithZedBlock(10); + EXPECT_EQ(10, blob.size()); + std::string s; + absl::CopyCordToString(blob, &s); + EXPECT_EQ("zzzzzzzzzz", s); +} + +TEST(CordSpliceTest, ZedBlock0) { + absl::Cord blob = CordWithZedBlock(0); + EXPECT_EQ(0, blob.size()); + std::string s; + absl::CopyCordToString(blob, &s); + EXPECT_EQ("", s); +} + +TEST(CordSpliceTest, ZedBlockSuffix1) { + absl::Cord blob = CordWithZedBlock(10); + EXPECT_EQ(10, blob.size()); + absl::Cord suffix(blob); + suffix.RemovePrefix(9); + EXPECT_EQ(1, suffix.size()); + std::string s; + absl::CopyCordToString(suffix, &s); + EXPECT_EQ("z", s); +} + +// Remove all of a prefix block +TEST(CordSpliceTest, ZedBlockSuffix0) { + absl::Cord blob = CordWithZedBlock(10); + EXPECT_EQ(10, blob.size()); + absl::Cord suffix(blob); + suffix.RemovePrefix(10); + EXPECT_EQ(0, suffix.size()); + std::string s; + absl::CopyCordToString(suffix, &s); + EXPECT_EQ("", s); +} + +absl::Cord BigCord(size_t len, char v) { + std::string s(len, v); + return absl::Cord(s); +} + +// Splice block into cord. +absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset, + const absl::Cord& block) { + ABSL_RAW_CHECK(offset >= 0, ""); + ABSL_RAW_CHECK(offset + block.size() <= blob.size(), ""); + absl::Cord result(blob); + result.RemoveSuffix(blob.size() - offset); + result.Append(block); + absl::Cord suffix(blob); + suffix.RemovePrefix(offset + block.size()); + result.Append(suffix); + ABSL_RAW_CHECK(blob.size() == result.size(), ""); + return result; +} + +// Taking an empty suffix of a block breaks appending. +TEST(CordSpliceTest, RemoveEntireBlock1) { + absl::Cord zero = CordWithZedBlock(10); + absl::Cord suffix(zero); + suffix.RemovePrefix(10); + absl::Cord result; + result.Append(suffix); +} + +TEST(CordSpliceTest, RemoveEntireBlock2) { + absl::Cord zero = CordWithZedBlock(10); + absl::Cord prefix(zero); + prefix.RemoveSuffix(10); + absl::Cord suffix(zero); + suffix.RemovePrefix(10); + absl::Cord result(prefix); + result.Append(suffix); +} + +TEST(CordSpliceTest, RemoveEntireBlock3) { + absl::Cord blob = CordWithZedBlock(10); + absl::Cord block = BigCord(10, 'b'); + blob = SpliceCord(blob, 0, block); +} + +struct CordCompareTestCase { + template <typename LHS, typename RHS> + CordCompareTestCase(const LHS& lhs, const RHS& rhs) + : lhs_cord(lhs), rhs_cord(rhs) {} + + absl::Cord lhs_cord; + absl::Cord rhs_cord; +}; + +const auto sign = [](int x) { return x == 0 ? 0 : (x > 0 ? 1 : -1); }; + +void VerifyComparison(const CordCompareTestCase& test_case) { + std::string lhs_string(test_case.lhs_cord); + std::string rhs_string(test_case.rhs_cord); + int expected = sign(lhs_string.compare(rhs_string)); + EXPECT_EQ(expected, test_case.lhs_cord.Compare(test_case.rhs_cord)) + << "LHS=" << lhs_string << "; RHS=" << rhs_string; + EXPECT_EQ(expected, test_case.lhs_cord.Compare(rhs_string)) + << "LHS=" << lhs_string << "; RHS=" << rhs_string; + EXPECT_EQ(-expected, test_case.rhs_cord.Compare(test_case.lhs_cord)) + << "LHS=" << rhs_string << "; RHS=" << lhs_string; + EXPECT_EQ(-expected, test_case.rhs_cord.Compare(lhs_string)) + << "LHS=" << rhs_string << "; RHS=" << lhs_string; +} + +TEST(Cord, Compare) { + absl::Cord subcord("aaaaaBBBBBcccccDDDDD"); + subcord = subcord.Subcord(3, 10); + + absl::Cord tmp("aaaaaaaaaaaaaaaa"); + tmp.Append("BBBBBBBBBBBBBBBB"); + absl::Cord concat = absl::Cord("cccccccccccccccc"); + concat.Append("DDDDDDDDDDDDDDDD"); + concat.Prepend(tmp); + + absl::Cord concat2("aaaaaaaaaaaaa"); + concat2.Append("aaaBBBBBBBBBBBBBBBBccccc"); + concat2.Append("cccccccccccDDDDDDDDDDDDDD"); + concat2.Append("DD"); + + std::vector<CordCompareTestCase> test_cases = {{ + // Inline cords + {"abcdef", "abcdef"}, + {"abcdef", "abcdee"}, + {"abcdef", "abcdeg"}, + {"bbcdef", "abcdef"}, + {"bbcdef", "abcdeg"}, + {"abcdefa", "abcdef"}, + {"abcdef", "abcdefa"}, + + // Small flat cords + {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD"}, + {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD"}, + {"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD"}, + {"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX"}, + {"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD"}, + {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa"}, + + // Subcords + {subcord, subcord}, + {subcord, "aaBBBBBccc"}, + {subcord, "aaBBBBBccd"}, + {subcord, "aaBBBBBccb"}, + {subcord, "aaBBBBBxcb"}, + {subcord, "aaBBBBBccca"}, + {subcord, "aaBBBBBcc"}, + + // Concats + {concat, concat}, + {concat, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD"}, + {concat, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD"}, + {concat, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD"}, + {concat, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD"}, + {concat, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe"}, + + {concat, concat2}, + }}; + + for (const auto& tc : test_cases) { + VerifyComparison(tc); + } +} + +TEST(Cord, CompareAfterAssign) { + absl::Cord a("aaaaaa1111111"); + absl::Cord b("aaaaaa2222222"); + a = "cccccc"; + b = "cccccc"; + EXPECT_EQ(a, b); + EXPECT_FALSE(a < b); + + a = "aaaa"; + b = "bbbbb"; + a = ""; + b = ""; + EXPECT_EQ(a, b); + EXPECT_FALSE(a < b); +} + +// Test CompareTo() and ComparePrefix() against string and substring +// comparison methods from std::basic_string. +static void TestCompare(const absl::Cord& c, const absl::Cord& d, + RandomEngine* rng) { + typedef std::basic_string<uint8_t> ustring; + ustring cs(reinterpret_cast<const uint8_t*>(std::string(c).data()), c.size()); + ustring ds(reinterpret_cast<const uint8_t*>(std::string(d).data()), d.size()); + // ustring comparison is ideal because we expect Cord comparisons to be + // based on unsigned byte comparisons regardless of whether char is signed. + int expected = sign(cs.compare(ds)); + EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d; +} + +TEST(Compare, ComparisonIsUnsigned) { + RandomEngine rng(testing::GTEST_FLAG(random_seed)); + std::uniform_int_distribution<uint32_t> uniform_uint8(0, 255); + char x = static_cast<char>(uniform_uint8(rng)); + TestCompare( + absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x)), + absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng); +} + +TEST(Compare, RandomComparisons) { + const int kIters = 5000; + RandomEngine rng(testing::GTEST_FLAG(random_seed)); + + int n = GetUniformRandomUpTo(&rng, 5000); + absl::Cord a[] = {MakeExternalCord(n), + absl::Cord("ant"), + absl::Cord("elephant"), + absl::Cord("giraffe"), + absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), + GetUniformRandomUpTo(&rng, 100))), + absl::Cord(""), + absl::Cord("x"), + absl::Cord("A"), + absl::Cord("B"), + absl::Cord("C")}; + for (int i = 0; i < kIters; i++) { + absl::Cord c, d; + for (int j = 0; j < (i % 7) + 1; j++) { + c.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]); + d.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]); + } + std::bernoulli_distribution coin_flip(0.5); + TestCompare(coin_flip(rng) ? c : absl::Cord(std::string(c)), + coin_flip(rng) ? d : absl::Cord(std::string(d)), &rng); + } +} + +template <typename T1, typename T2> +void CompareOperators() { + const T1 a("a"); + const T2 b("b"); + + EXPECT_TRUE(a == a); + // For pointer type (i.e. `const char*`), operator== compares the address + // instead of the std::string, so `a == const char*("a")` isn't necessarily true. + EXPECT_TRUE(std::is_pointer<T1>::value || a == T1("a")); + EXPECT_TRUE(std::is_pointer<T2>::value || a == T2("a")); + EXPECT_FALSE(a == b); + + EXPECT_TRUE(a != b); + EXPECT_FALSE(a != a); + + EXPECT_TRUE(a < b); + EXPECT_FALSE(b < a); + + EXPECT_TRUE(b > a); + EXPECT_FALSE(a > b); + + EXPECT_TRUE(a >= a); + EXPECT_TRUE(b >= a); + EXPECT_FALSE(a >= b); + + EXPECT_TRUE(a <= a); + EXPECT_TRUE(a <= b); + EXPECT_FALSE(b <= a); +} + +TEST(ComparisonOperators, Cord_Cord) { + CompareOperators<absl::Cord, absl::Cord>(); +} + +TEST(ComparisonOperators, Cord_StringPiece) { + CompareOperators<absl::Cord, absl::string_view>(); +} + +TEST(ComparisonOperators, StringPiece_Cord) { + CompareOperators<absl::string_view, absl::Cord>(); +} + +TEST(ComparisonOperators, Cord_string) { + CompareOperators<absl::Cord, std::string>(); +} + +TEST(ComparisonOperators, string_Cord) { + CompareOperators<std::string, absl::Cord>(); +} + +TEST(ComparisonOperators, stdstring_Cord) { + CompareOperators<std::string, absl::Cord>(); +} + +TEST(ComparisonOperators, Cord_stdstring) { + CompareOperators<absl::Cord, std::string>(); +} + +TEST(ComparisonOperators, charstar_Cord) { + CompareOperators<const char*, absl::Cord>(); +} + +TEST(ComparisonOperators, Cord_charstar) { + CompareOperators<absl::Cord, const char*>(); +} + +TEST(ConstructFromExternal, ReleaserInvoked) { + // Empty external memory means the releaser should be called immediately. + { + bool invoked = false; + auto releaser = [&invoked](absl::string_view) { invoked = true; }; + { + auto c = absl::MakeCordFromExternal("", releaser); + EXPECT_TRUE(invoked); + } + } + + // If the size of the data is small enough, a future constructor + // implementation may copy the bytes and immediately invoke the releaser + // instead of creating an external node. We make a large dummy std::string to + // make this test independent of such an optimization. + std::string large_dummy(2048, 'c'); + { + bool invoked = false; + auto releaser = [&invoked](absl::string_view) { invoked = true; }; + { + auto c = absl::MakeCordFromExternal(large_dummy, releaser); + EXPECT_FALSE(invoked); + } + EXPECT_TRUE(invoked); + } + + { + bool invoked = false; + auto releaser = [&invoked](absl::string_view) { invoked = true; }; + { + absl::Cord copy; + { + auto c = absl::MakeCordFromExternal(large_dummy, releaser); + copy = c; + EXPECT_FALSE(invoked); + } + EXPECT_FALSE(invoked); + } + EXPECT_TRUE(invoked); + } +} + +TEST(ConstructFromExternal, CompareContents) { + RandomEngine rng(testing::GTEST_FLAG(random_seed)); + + for (int length = 1; length <= 2048; length *= 2) { + std::string data = RandomLowercaseString(&rng, length); + auto* external = new std::string(data); + auto cord = + absl::MakeCordFromExternal(*external, [external](absl::string_view sv) { + EXPECT_EQ(external->data(), sv.data()); + EXPECT_EQ(external->size(), sv.size()); + delete external; + }); + EXPECT_EQ(data, cord); + } +} + +TEST(ConstructFromExternal, LargeReleaser) { + RandomEngine rng(testing::GTEST_FLAG(random_seed)); + constexpr size_t kLength = 256; + std::string data = RandomLowercaseString(&rng, kLength); + std::array<char, kLength> data_array; + for (size_t i = 0; i < kLength; ++i) data_array[i] = data[i]; + bool invoked = false; + auto releaser = [data_array, &invoked](absl::string_view data) { + EXPECT_EQ(data, absl::string_view(data_array.data(), data_array.size())); + invoked = true; + }; + (void)absl::MakeCordFromExternal(data, releaser); + EXPECT_TRUE(invoked); +} + +TEST(ConstructFromExternal, FunctionPointerReleaser) { + static absl::string_view data("hello world"); + static bool invoked; + auto* releaser = + static_cast<void (*)(absl::string_view)>([](absl::string_view sv) { + EXPECT_EQ(data, sv); + invoked = true; + }); + invoked = false; + (void)absl::MakeCordFromExternal(data, releaser); + EXPECT_TRUE(invoked); + + invoked = false; + (void)absl::MakeCordFromExternal(data, *releaser); + EXPECT_TRUE(invoked); +} + +TEST(ConstructFromExternal, MoveOnlyReleaser) { + struct Releaser { + explicit Releaser(bool* invoked) : invoked(invoked) {} + Releaser(Releaser&& other) noexcept : invoked(other.invoked) {} + void operator()(absl::string_view) const { *invoked = true; } + + bool* invoked; + }; + + bool invoked = false; + (void)absl::MakeCordFromExternal("dummy", Releaser(&invoked)); + EXPECT_TRUE(invoked); +} + +TEST(ConstructFromExternal, NonTrivialReleaserDestructor) { + struct Releaser { + explicit Releaser(bool* destroyed) : destroyed(destroyed) {} + ~Releaser() { *destroyed = true; } + void operator()(absl::string_view) const {} + + bool* destroyed; + }; + + bool destroyed = false; + Releaser releaser(&destroyed); + (void)absl::MakeCordFromExternal("dummy", releaser); + EXPECT_TRUE(destroyed); +} + +TEST(ConstructFromExternal, ReferenceQualifierOverloads) { + struct Releaser { + void operator()(absl::string_view) & { *lvalue_invoked = true; } + void operator()(absl::string_view) && { *rvalue_invoked = true; } + + bool* lvalue_invoked; + bool* rvalue_invoked; + }; + + bool lvalue_invoked = false; + bool rvalue_invoked = false; + Releaser releaser = {&lvalue_invoked, &rvalue_invoked}; + (void)absl::MakeCordFromExternal("", releaser); + EXPECT_FALSE(lvalue_invoked); + EXPECT_TRUE(rvalue_invoked); + rvalue_invoked = false; + + (void)absl::MakeCordFromExternal("dummy", releaser); + EXPECT_FALSE(lvalue_invoked); + EXPECT_TRUE(rvalue_invoked); + rvalue_invoked = false; + + // NOLINTNEXTLINE: suppress clang-tidy std::move on trivially copyable type. + (void)absl::MakeCordFromExternal("dummy", std::move(releaser)); + EXPECT_FALSE(lvalue_invoked); + EXPECT_TRUE(rvalue_invoked); +} + +TEST(ExternalMemory, BasicUsage) { + static const char* strings[] = { "", "hello", "there" }; + for (const char* str : strings) { + absl::Cord dst("(prefix)"); + AddExternalMemory(str, &dst); + dst.Append("(suffix)"); + EXPECT_EQ((std::string("(prefix)") + str + std::string("(suffix)")), + std::string(dst)); + } +} + +TEST(ExternalMemory, RemovePrefixSuffix) { + // Exhaustively try all sub-strings. + absl::Cord cord = MakeComposite(); + std::string s = std::string(cord); + for (int offset = 0; offset <= s.size(); offset++) { + for (int length = 0; length <= s.size() - offset; length++) { + absl::Cord result(cord); + result.RemovePrefix(offset); + result.RemoveSuffix(result.size() - length); + EXPECT_EQ(s.substr(offset, length), std::string(result)) + << offset << " " << length; + } + } +} + +TEST(ExternalMemory, Get) { + absl::Cord cord("hello"); + AddExternalMemory(" world!", &cord); + AddExternalMemory(" how are ", &cord); + cord.Append(" you?"); + std::string s = std::string(cord); + for (int i = 0; i < s.size(); i++) { + EXPECT_EQ(s[i], cord[i]); + } +} + +// CordMemoryUsage tests verify the correctness of the EstimatedMemoryUsage() +// These tests take into account that the reported memory usage is approximate +// and non-deterministic. For all tests, We verify that the reported memory +// usage is larger than `size()`, and less than `size() * 1.5` as a cord should +// never reserve more 'extra' capacity than half of its size as it grows. +// Additionally we have some whiteboxed expectations based on our knowledge of +// the layout and size of empty and inlined cords, and flat nodes. + +TEST(CordMemoryUsage, Empty) { + EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage()); +} + +TEST(CordMemoryUsage, Embedded) { + absl::Cord a("hello"); + EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); +} + +TEST(CordMemoryUsage, EmbeddedAppend) { + absl::Cord a("a"); + absl::Cord b("bcd"); + EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord)); + a.Append(b); + EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); +} + +TEST(CordMemoryUsage, ExternalMemory) { + static const int kLength = 1000; + absl::Cord cord; + AddExternalMemory(std::string(kLength, 'x'), &cord); + EXPECT_GT(cord.EstimatedMemoryUsage(), kLength); + EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5); +} + +TEST(CordMemoryUsage, Flat) { + static const int kLength = 125; + absl::Cord a(std::string(kLength, 'a')); + EXPECT_GT(a.EstimatedMemoryUsage(), kLength); + EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5); +} + +TEST(CordMemoryUsage, AppendFlat) { + using absl::strings_internal::CordTestAccess; + absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); + size_t length = a.EstimatedMemoryUsage(); + a.Append(std::string(CordTestAccess::MaxFlatLength(), 'b')); + size_t delta = a.EstimatedMemoryUsage() - length; + EXPECT_GT(delta, CordTestAccess::MaxFlatLength()); + EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5); +} + +// Regtest for a change that had to be rolled back because it expanded out +// of the InlineRep too soon, which was observable through MemoryUsage(). +TEST(CordMemoryUsage, InlineRep) { + constexpr size_t kMaxInline = 15; // Cord::InlineRep::N + const std::string small_string(kMaxInline, 'x'); + absl::Cord c1(small_string); + + absl::Cord c2; + c2.Append(small_string); + EXPECT_EQ(c1, c2); + EXPECT_EQ(c1.EstimatedMemoryUsage(), c2.EstimatedMemoryUsage()); +} + +} // namespace + +// Regtest for 7510292 (fix a bug introduced by 7465150) +TEST(Cord, Concat_Append) { + // Create a rep of type CONCAT + absl::Cord s1("foobarbarbarbarbar"); + s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg"); + size_t size = s1.size(); + + // Create a copy of s1 and append to it. + absl::Cord s2 = s1; + s2.Append("x"); + + // 7465150 modifies s1 when it shouldn't. + EXPECT_EQ(s1.size(), size); + EXPECT_EQ(s2.size(), size + 1); +} + +TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) { + absl::Cord fragmented = + absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"}); + + EXPECT_EQ("A fragmented Cord", fragmented); + + auto chunk_it = fragmented.chunk_begin(); + + ASSERT_TRUE(chunk_it != fragmented.chunk_end()); + EXPECT_EQ("A ", *chunk_it); + + ASSERT_TRUE(++chunk_it != fragmented.chunk_end()); + EXPECT_EQ("fragmented ", *chunk_it); + + ASSERT_TRUE(++chunk_it != fragmented.chunk_end()); + EXPECT_EQ("Cord", *chunk_it); + + ASSERT_TRUE(++chunk_it == fragmented.chunk_end()); +} + +TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) { + std::vector<absl::string_view> chunks = {"A ", "fragmented ", "Cord"}; + absl::Cord fragmented = absl::MakeFragmentedCord(chunks); + + EXPECT_EQ("A fragmented Cord", fragmented); + + auto chunk_it = fragmented.chunk_begin(); + + ASSERT_TRUE(chunk_it != fragmented.chunk_end()); + EXPECT_EQ("A ", *chunk_it); + + ASSERT_TRUE(++chunk_it != fragmented.chunk_end()); + EXPECT_EQ("fragmented ", *chunk_it); + + ASSERT_TRUE(++chunk_it != fragmented.chunk_end()); + EXPECT_EQ("Cord", *chunk_it); + + ASSERT_TRUE(++chunk_it == fragmented.chunk_end()); +} + +TEST(CordChunkIterator, Traits) { + static_assert(std::is_copy_constructible<absl::Cord::ChunkIterator>::value, + ""); + static_assert(std::is_copy_assignable<absl::Cord::ChunkIterator>::value, ""); + + // Move semantics to satisfy swappable via std::swap + static_assert(std::is_move_constructible<absl::Cord::ChunkIterator>::value, + ""); + static_assert(std::is_move_assignable<absl::Cord::ChunkIterator>::value, ""); + + static_assert( + std::is_same< + std::iterator_traits<absl::Cord::ChunkIterator>::iterator_category, + std::input_iterator_tag>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::value_type, + absl::string_view>::value, + ""); + static_assert( + std::is_same< + std::iterator_traits<absl::Cord::ChunkIterator>::difference_type, + ptrdiff_t>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::pointer, + const absl::string_view*>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::reference, + absl::string_view>::value, + ""); +} + +static void VerifyChunkIterator(const absl::Cord& cord, + size_t expected_chunks) { + EXPECT_EQ(cord.chunk_begin() == cord.chunk_end(), cord.empty()) << cord; + EXPECT_EQ(cord.chunk_begin() != cord.chunk_end(), !cord.empty()); + + absl::Cord::ChunkRange range = cord.Chunks(); + EXPECT_EQ(range.begin() == range.end(), cord.empty()); + EXPECT_EQ(range.begin() != range.end(), !cord.empty()); + + std::string content(cord); + size_t pos = 0; + auto pre_iter = cord.chunk_begin(), post_iter = cord.chunk_begin(); + size_t n_chunks = 0; + while (pre_iter != cord.chunk_end() && post_iter != cord.chunk_end()) { + EXPECT_FALSE(pre_iter == cord.chunk_end()); // NOLINT: explicitly test == + EXPECT_FALSE(post_iter == cord.chunk_end()); // NOLINT + + EXPECT_EQ(pre_iter, post_iter); + EXPECT_EQ(*pre_iter, *post_iter); + + EXPECT_EQ(pre_iter->data(), (*pre_iter).data()); + EXPECT_EQ(pre_iter->size(), (*pre_iter).size()); + + absl::string_view chunk = *pre_iter; + EXPECT_FALSE(chunk.empty()); + EXPECT_LE(pos + chunk.size(), content.size()); + EXPECT_EQ(absl::string_view(content.c_str() + pos, chunk.size()), chunk); + + int n_equal_iterators = 0; + for (absl::Cord::ChunkIterator it = range.begin(); it != range.end(); + ++it) { + n_equal_iterators += static_cast<int>(it == pre_iter); + } + EXPECT_EQ(n_equal_iterators, 1); + + ++pre_iter; + EXPECT_EQ(*post_iter++, chunk); + + pos += chunk.size(); + ++n_chunks; + } + EXPECT_EQ(expected_chunks, n_chunks); + EXPECT_EQ(pos, content.size()); + EXPECT_TRUE(pre_iter == cord.chunk_end()); // NOLINT: explicitly test == + EXPECT_TRUE(post_iter == cord.chunk_end()); // NOLINT +} + +TEST(CordChunkIterator, Operations) { + absl::Cord empty_cord; + VerifyChunkIterator(empty_cord, 0); + + absl::Cord small_buffer_cord("small cord"); + VerifyChunkIterator(small_buffer_cord, 1); + + absl::Cord flat_node_cord("larger than small buffer optimization"); + VerifyChunkIterator(flat_node_cord, 1); + + VerifyChunkIterator( + absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ", + "testing ", "chunk ", "iterations."}), + 8); + + absl::Cord reused_nodes_cord(std::string(40, 'c')); + reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'b'))); + reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'a'))); + size_t expected_chunks = 3; + for (int i = 0; i < 8; ++i) { + reused_nodes_cord.Prepend(reused_nodes_cord); + expected_chunks *= 2; + VerifyChunkIterator(reused_nodes_cord, expected_chunks); + } + + RandomEngine rng(testing::GTEST_FLAG(random_seed)); + absl::Cord flat_cord(RandomLowercaseString(&rng, 256)); + absl::Cord subcords; + for (int i = 0; i < 128; ++i) subcords.Prepend(flat_cord.Subcord(i, 128)); + VerifyChunkIterator(subcords, 128); +} + +TEST(CordCharIterator, Traits) { + static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value, + ""); + static_assert(std::is_copy_assignable<absl::Cord::CharIterator>::value, ""); + + // Move semantics to satisfy swappable via std::swap + static_assert(std::is_move_constructible<absl::Cord::CharIterator>::value, + ""); + static_assert(std::is_move_assignable<absl::Cord::CharIterator>::value, ""); + + static_assert( + std::is_same< + std::iterator_traits<absl::Cord::CharIterator>::iterator_category, + std::input_iterator_tag>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<absl::Cord::CharIterator>::value_type, + char>::value, + ""); + static_assert( + std::is_same< + std::iterator_traits<absl::Cord::CharIterator>::difference_type, + ptrdiff_t>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<absl::Cord::CharIterator>::pointer, + const char*>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<absl::Cord::CharIterator>::reference, + const char&>::value, + ""); +} + +static void VerifyCharIterator(const absl::Cord& cord) { + EXPECT_EQ(cord.char_begin() == cord.char_end(), cord.empty()); + EXPECT_EQ(cord.char_begin() != cord.char_end(), !cord.empty()); + + absl::Cord::CharRange range = cord.Chars(); + EXPECT_EQ(range.begin() == range.end(), cord.empty()); + EXPECT_EQ(range.begin() != range.end(), !cord.empty()); + + size_t i = 0; + absl::Cord::CharIterator pre_iter = cord.char_begin(); + absl::Cord::CharIterator post_iter = cord.char_begin(); + std::string content(cord); + while (pre_iter != cord.char_end() && post_iter != cord.char_end()) { + EXPECT_FALSE(pre_iter == cord.char_end()); // NOLINT: explicitly test == + EXPECT_FALSE(post_iter == cord.char_end()); // NOLINT + + EXPECT_LT(i, cord.size()); + EXPECT_EQ(content[i], *pre_iter); + + EXPECT_EQ(pre_iter, post_iter); + EXPECT_EQ(*pre_iter, *post_iter); + EXPECT_EQ(&*pre_iter, &*post_iter); + + EXPECT_EQ(&*pre_iter, pre_iter.operator->()); + + const char* character_address = &*pre_iter; + absl::Cord::CharIterator copy = pre_iter; + ++copy; + EXPECT_EQ(character_address, &*pre_iter); + + int n_equal_iterators = 0; + for (absl::Cord::CharIterator it = range.begin(); it != range.end(); ++it) { + n_equal_iterators += static_cast<int>(it == pre_iter); + } + EXPECT_EQ(n_equal_iterators, 1); + + absl::Cord::CharIterator advance_iter = range.begin(); + absl::Cord::Advance(&advance_iter, i); + EXPECT_EQ(pre_iter, advance_iter); + + advance_iter = range.begin(); + EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, i), cord.Subcord(0, i)); + EXPECT_EQ(pre_iter, advance_iter); + + advance_iter = pre_iter; + absl::Cord::Advance(&advance_iter, cord.size() - i); + EXPECT_EQ(range.end(), advance_iter); + + advance_iter = pre_iter; + EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, cord.size() - i), + cord.Subcord(i, cord.size() - i)); + EXPECT_EQ(range.end(), advance_iter); + + ++i; + ++pre_iter; + post_iter++; + } + EXPECT_EQ(i, cord.size()); + EXPECT_TRUE(pre_iter == cord.char_end()); // NOLINT: explicitly test == + EXPECT_TRUE(post_iter == cord.char_end()); // NOLINT + + absl::Cord::CharIterator zero_advanced_end = cord.char_end(); + absl::Cord::Advance(&zero_advanced_end, 0); + EXPECT_EQ(zero_advanced_end, cord.char_end()); + + absl::Cord::CharIterator it = cord.char_begin(); + for (absl::string_view chunk : cord.Chunks()) { + while (!chunk.empty()) { + EXPECT_EQ(absl::Cord::ChunkRemaining(it), chunk); + chunk.remove_prefix(1); + ++it; + } + } +} + +TEST(CordCharIterator, Operations) { + absl::Cord empty_cord; + VerifyCharIterator(empty_cord); + + absl::Cord small_buffer_cord("small cord"); + VerifyCharIterator(small_buffer_cord); + + absl::Cord flat_node_cord("larger than small buffer optimization"); + VerifyCharIterator(flat_node_cord); + + VerifyCharIterator( + absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ", + "testing ", "character ", "iteration."})); + + absl::Cord reused_nodes_cord("ghi"); + reused_nodes_cord.Prepend(absl::Cord("def")); + reused_nodes_cord.Prepend(absl::Cord("abc")); + for (int i = 0; i < 4; ++i) { + reused_nodes_cord.Prepend(reused_nodes_cord); + VerifyCharIterator(reused_nodes_cord); + } + + RandomEngine rng(testing::GTEST_FLAG(random_seed)); + absl::Cord flat_cord(RandomLowercaseString(&rng, 256)); + absl::Cord subcords; + for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128)); + VerifyCharIterator(subcords); +} + +TEST(Cord, StreamingOutput) { + absl::Cord c = + absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."}); + std::stringstream output; + output << c; + EXPECT_EQ("A small fragmented Cord.", output.str()); +} + +TEST(Cord, ForEachChunk) { + for (int num_elements : {1, 10, 200}) { + SCOPED_TRACE(num_elements); + std::vector<std::string> cord_chunks; + for (int i = 0; i < num_elements; ++i) { + cord_chunks.push_back(absl::StrCat("[", i, "]")); + } + absl::Cord c = absl::MakeFragmentedCord(cord_chunks); + + std::vector<std::string> iterated_chunks; + absl::CordTestPeer::ForEachChunk(c, + [&iterated_chunks](absl::string_view sv) { + iterated_chunks.emplace_back(sv); + }); + EXPECT_EQ(iterated_chunks, cord_chunks); + } +} + +TEST(Cord, SmallBufferAssignFromOwnData) { + constexpr size_t kMaxInline = 15; + std::string contents = "small buff cord"; + EXPECT_EQ(contents.size(), kMaxInline); + for (size_t pos = 0; pos < contents.size(); ++pos) { + for (size_t count = contents.size() - pos; count > 0; --count) { + absl::Cord c(contents); + absl::string_view flat = c.Flatten(); + c = flat.substr(pos, count); + EXPECT_EQ(c, contents.substr(pos, count)) + << "pos = " << pos << "; count = " << count; + } + } +} diff --git a/absl/strings/cord_test_helpers.h b/absl/strings/cord_test_helpers.h new file mode 100644 index 00000000..f1036e3b --- /dev/null +++ b/absl/strings/cord_test_helpers.h @@ -0,0 +1,60 @@ +// +// 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_STRINGS_CORD_TEST_HELPERS_H_ +#define ABSL_STRINGS_CORD_TEST_HELPERS_H_ + +#include "absl/strings/cord.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Creates a multi-segment Cord from an iterable container of strings. The +// resulting Cord is guaranteed to have one segment for every string in the +// container. This allows code to be unit tested with multi-segment Cord +// inputs. +// +// Example: +// +// absl::Cord c = absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"}); +// EXPECT_FALSE(c.GetFlat(&unused)); +// +// The mechanism by which this Cord is created is an implementation detail. Any +// implementation that produces a multi-segment Cord may produce a flat Cord in +// the future as new optimizations are added to the Cord class. +// MakeFragmentedCord will, however, always be updated to return a multi-segment +// Cord. +template <typename Container> +Cord MakeFragmentedCord(const Container& c) { + Cord result; + for (const auto& s : c) { + auto* external = new std::string(s); + Cord tmp = absl::MakeCordFromExternal( + *external, [external](absl::string_view) { delete external; }); + tmp.Prepend(result); + result = tmp; + } + return result; +} + +inline Cord MakeFragmentedCord(std::initializer_list<absl::string_view> list) { + return MakeFragmentedCord<std::initializer_list<absl::string_view>>(list); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORD_TEST_HELPERS_H_ diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc index eb0974dc..7adc1b65 100644 --- a/absl/strings/escaping.cc +++ b/absl/strings/escaping.cc @@ -26,6 +26,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/unaligned_access.h" #include "absl/strings/internal/char_map.h" +#include "absl/strings/internal/escaping.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/internal/utf8.h" #include "absl/strings/str_cat.h" @@ -33,30 +34,9 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { -// Digit conversion. -constexpr char kHexChar[] = "0123456789abcdef"; - -constexpr char kHexTable[513] = - "000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f" - "505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; - // These are used for the leave_nulls_escaped argument to CUnescapeInternal(). constexpr bool kUnescapeNulls = false; @@ -349,14 +329,14 @@ std::string CEscapeInternal(absl::string_view src, bool use_hex, (last_hex_escape && absl::ascii_isxdigit(c)))) { if (use_hex) { dest.append("\\" "x"); - dest.push_back(kHexChar[c / 16]); - dest.push_back(kHexChar[c % 16]); + dest.push_back(numbers_internal::kHexChar[c / 16]); + dest.push_back(numbers_internal::kHexChar[c % 16]); is_hex_escape = true; } else { dest.append("\\"); - dest.push_back(kHexChar[c / 64]); - dest.push_back(kHexChar[(c % 64) / 8]); - dest.push_back(kHexChar[c % 8]); + dest.push_back(numbers_internal::kHexChar[c / 64]); + dest.push_back(numbers_internal::kHexChar[(c % 64) / 8]); + dest.push_back(numbers_internal::kHexChar[c % 8]); } } else { dest.push_back(c); @@ -785,177 +765,10 @@ constexpr signed char kUnWebSafeBase64[] = { }; /* clang-format on */ -size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) { - // Base64 encodes three bytes of input at a time. If the input is not - // divisible by three, we pad as appropriate. - // - // (from https://tools.ietf.org/html/rfc3548) - // Special processing is performed if fewer than 24 bits are available - // at the end of the data being encoded. A full encoding quantum is - // always completed at the end of a quantity. When fewer than 24 input - // bits are available in an input group, zero bits are added (on the - // right) to form an integral number of 6-bit groups. Padding at the - // end of the data is performed using the '=' character. Since all base - // 64 input is an integral number of octets, only the following cases - // can arise: - - // Base64 encodes each three bytes of input into four bytes of output. - size_t len = (input_len / 3) * 4; - - if (input_len % 3 == 0) { - // (from https://tools.ietf.org/html/rfc3548) - // (1) the final quantum of encoding input is an integral multiple of 24 - // bits; here, the final unit of encoded output will be an integral - // multiple of 4 characters with no "=" padding, - } else if (input_len % 3 == 1) { - // (from https://tools.ietf.org/html/rfc3548) - // (2) the final quantum of encoding input is exactly 8 bits; here, the - // final unit of encoded output will be two characters followed by two - // "=" padding characters, or - len += 2; - if (do_padding) { - len += 2; - } - } else { // (input_len % 3 == 2) - // (from https://tools.ietf.org/html/rfc3548) - // (3) the final quantum of encoding input is exactly 16 bits; here, the - // final unit of encoded output will be three characters followed by one - // "=" padding character. - len += 3; - if (do_padding) { - len += 1; - } - } - - assert(len >= input_len); // make sure we didn't overflow - return len; -} - -size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, - size_t szdest, const char* base64, - bool do_padding) { - static const char kPad64 = '='; - - if (szsrc * 4 > szdest * 3) return 0; - - char* cur_dest = dest; - const unsigned char* cur_src = src; - - char* const limit_dest = dest + szdest; - const unsigned char* const limit_src = src + szsrc; - - // Three bytes of data encodes to four characters of cyphertext. - // So we can pump through three-byte chunks atomically. - if (szsrc >= 3) { // "limit_src - 3" is UB if szsrc < 3. - while (cur_src < limit_src - 3) { // While we have >= 32 bits. - uint32_t in = absl::big_endian::Load32(cur_src) >> 8; - - cur_dest[0] = base64[in >> 18]; - in &= 0x3FFFF; - cur_dest[1] = base64[in >> 12]; - in &= 0xFFF; - cur_dest[2] = base64[in >> 6]; - in &= 0x3F; - cur_dest[3] = base64[in]; - - cur_dest += 4; - cur_src += 3; - } - } - // To save time, we didn't update szdest or szsrc in the loop. So do it now. - szdest = limit_dest - cur_dest; - szsrc = limit_src - cur_src; - - /* now deal with the tail (<=3 bytes) */ - switch (szsrc) { - case 0: - // Nothing left; nothing more to do. - break; - case 1: { - // One byte left: this encodes to two characters, and (optionally) - // two pad characters to round out the four-character cypherblock. - if (szdest < 2) return 0; - uint32_t in = cur_src[0]; - cur_dest[0] = base64[in >> 2]; - in &= 0x3; - cur_dest[1] = base64[in << 4]; - cur_dest += 2; - szdest -= 2; - if (do_padding) { - if (szdest < 2) return 0; - cur_dest[0] = kPad64; - cur_dest[1] = kPad64; - cur_dest += 2; - szdest -= 2; - } - break; - } - case 2: { - // Two bytes left: this encodes to three characters, and (optionally) - // one pad character to round out the four-character cypherblock. - if (szdest < 3) return 0; - uint32_t in = absl::big_endian::Load16(cur_src); - cur_dest[0] = base64[in >> 10]; - in &= 0x3FF; - cur_dest[1] = base64[in >> 4]; - in &= 0x00F; - cur_dest[2] = base64[in << 2]; - cur_dest += 3; - szdest -= 3; - if (do_padding) { - if (szdest < 1) return 0; - cur_dest[0] = kPad64; - cur_dest += 1; - szdest -= 1; - } - break; - } - case 3: { - // Three bytes left: same as in the big loop above. We can't do this in - // the loop because the loop above always reads 4 bytes, and the fourth - // byte is past the end of the input. - if (szdest < 4) return 0; - uint32_t in = (cur_src[0] << 16) + absl::big_endian::Load16(cur_src + 1); - cur_dest[0] = base64[in >> 18]; - in &= 0x3FFFF; - cur_dest[1] = base64[in >> 12]; - in &= 0xFFF; - cur_dest[2] = base64[in >> 6]; - in &= 0x3F; - cur_dest[3] = base64[in]; - cur_dest += 4; - szdest -= 4; - break; - } - default: - // Should not be reached: blocks of 4 bytes are handled - // in the while loop before this switch statement. - ABSL_RAW_LOG(FATAL, "Logic problem? szsrc = %zu", szsrc); - break; - } - return (cur_dest - dest); -} - -constexpr char kBase64Chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - constexpr char kWebSafeBase64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; template <typename String> -void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest, - bool do_padding, const char* base64_chars) { - const size_t calc_escaped_size = - CalculateBase64EscapedLenInternal(szsrc, do_padding); - strings_internal::STLStringResizeUninitialized(dest, calc_escaped_size); - - const size_t escaped_len = Base64EscapeInternal( - src, szsrc, &(*dest)[0], dest->size(), base64_chars, do_padding); - assert(calc_escaped_size == escaped_len); - dest->erase(escaped_len); -} - -template <typename String> bool Base64UnescapeInternal(const char* src, size_t slen, String* dest, const signed char* unbase64) { // Determine the size of the output std::string. Base64 encodes every 3 bytes into @@ -983,7 +796,7 @@ bool Base64UnescapeInternal(const char* src, size_t slen, String* dest, } /* clang-format off */ -constexpr char kHexValue[256] = { +constexpr char kHexValueLenient[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -999,8 +812,9 @@ constexpr char kHexValue[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; + /* clang-format on */ // This is a templated function so that T can be either a char* @@ -1009,8 +823,8 @@ constexpr char kHexValue[256] = { template <typename T> void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) { for (int i = 0; i < num; i++) { - to[i] = (kHexValue[from[i * 2] & 0xFF] << 4) + - (kHexValue[from[i * 2 + 1] & 0xFF]); + to[i] = (kHexValueLenient[from[i * 2] & 0xFF] << 4) + + (kHexValueLenient[from[i * 2 + 1] & 0xFF]); } } @@ -1020,7 +834,7 @@ template <typename T> void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) { auto dest_ptr = &dest[0]; for (auto src_ptr = src; src_ptr != (src + num); ++src_ptr, dest_ptr += 2) { - const char* hex_p = &kHexTable[*src_ptr * 2]; + const char* hex_p = &numbers_internal::kHexTable[*src_ptr * 2]; std::copy(hex_p, hex_p + 2, dest_ptr); } } @@ -1088,26 +902,30 @@ bool WebSafeBase64Unescape(absl::string_view src, std::string* dest) { } void Base64Escape(absl::string_view src, std::string* dest) { - Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()), - src.size(), dest, true, kBase64Chars); + strings_internal::Base64EscapeInternal( + reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest, + true, strings_internal::kBase64Chars); } void WebSafeBase64Escape(absl::string_view src, std::string* dest) { - Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()), - src.size(), dest, false, kWebSafeBase64Chars); + strings_internal::Base64EscapeInternal( + reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest, + false, kWebSafeBase64Chars); } std::string Base64Escape(absl::string_view src) { std::string dest; - Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()), - src.size(), &dest, true, kBase64Chars); + strings_internal::Base64EscapeInternal( + reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest, + true, strings_internal::kBase64Chars); return dest; } std::string WebSafeBase64Escape(absl::string_view src) { std::string dest; - Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()), - src.size(), &dest, false, kWebSafeBase64Chars); + strings_internal::Base64EscapeInternal( + reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest, + false, kWebSafeBase64Chars); return dest; } @@ -1127,5 +945,5 @@ std::string BytesToHexString(absl::string_view from) { return result; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h index 7f1c5d46..f5ca26c5 100644 --- a/absl/strings/escaping.h +++ b/absl/strings/escaping.h @@ -33,7 +33,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // CUnescape() // @@ -158,7 +158,7 @@ std::string HexStringToBytes(absl::string_view from); // `2*from.size()`. std::string BytesToHexString(absl::string_view from); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_ESCAPING_H_ diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h index 772ae869..a76e6036 100644 --- a/absl/strings/internal/char_map.h +++ b/absl/strings/internal/char_map.h @@ -28,7 +28,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { class Charmap { @@ -150,7 +150,7 @@ constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); } constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_ diff --git a/absl/strings/internal/charconv_bigint.cc b/absl/strings/internal/charconv_bigint.cc index 58c909f4..66f33e72 100644 --- a/absl/strings/internal/charconv_bigint.cc +++ b/absl/strings/internal/charconv_bigint.cc @@ -19,7 +19,7 @@ #include <string> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { namespace { @@ -158,12 +158,12 @@ const uint32_t* LargePowerOfFiveData(int i) { int LargePowerOfFiveSize(int i) { return 2 * i; } } // namespace -const uint32_t kFiveToNth[14] = { +ABSL_DLL const uint32_t kFiveToNth[14] = { 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125, }; -const uint32_t kTenToNth[10] = { +ABSL_DLL const uint32_t kTenToNth[10] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, }; @@ -355,5 +355,5 @@ template class BigUnsigned<4>; template class BigUnsigned<84>; } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h index 5aef416b..999e9ae3 100644 --- a/absl/strings/internal/charconv_bigint.h +++ b/absl/strings/internal/charconv_bigint.h @@ -20,12 +20,13 @@ #include <iostream> #include <string> +#include "absl/base/config.h" #include "absl/strings/ascii.h" #include "absl/strings/internal/charconv_parse.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // The largest power that 5 that can be raised to, and still fit in a uint32_t. @@ -33,8 +34,9 @@ constexpr int kMaxSmallPowerOfFive = 13; // The largest power that 10 that can be raised to, and still fit in a uint32_t. constexpr int kMaxSmallPowerOfTen = 9; -extern const uint32_t kFiveToNth[kMaxSmallPowerOfFive + 1]; -extern const uint32_t kTenToNth[kMaxSmallPowerOfTen + 1]; +ABSL_DLL extern const uint32_t + kFiveToNth[kMaxSmallPowerOfFive + 1]; +ABSL_DLL extern const uint32_t kTenToNth[kMaxSmallPowerOfTen + 1]; // Large, fixed-width unsigned integer. // @@ -415,7 +417,7 @@ extern template class BigUnsigned<4>; extern template class BigUnsigned<84>; } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_ diff --git a/absl/strings/internal/charconv_bigint_test.cc b/absl/strings/internal/charconv_bigint_test.cc index 590511d0..363bcb03 100644 --- a/absl/strings/internal/charconv_bigint_test.cc +++ b/absl/strings/internal/charconv_bigint_test.cc @@ -19,7 +19,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { TEST(BigUnsigned, ShiftLeft) { @@ -201,5 +201,5 @@ TEST(BigUnsigned, TenToTheNth) { } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc index 4dd4ecb3..d9a57a78 100644 --- a/absl/strings/internal/charconv_parse.cc +++ b/absl/strings/internal/charconv_parse.cc @@ -22,7 +22,7 @@ #include "absl/strings/internal/memutil.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { // ParseFloat<10> will read the first 19 significant digits of the mantissa. @@ -254,6 +254,12 @@ std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits, assert(max_digits * 4 <= std::numeric_limits<T>::digits); } const char* const original_begin = begin; + + // Skip leading zeros, but only if *out is zero. + // They don't cause an overflow so we don't have to count them for + // `max_digits`. + while (!*out && end != begin && *begin == '0') ++begin; + T accumulator = *out; const char* significant_digits_end = (end - begin > max_digits) ? begin + max_digits : end; @@ -494,5 +500,5 @@ template ParsedFloat ParseFloat<16>(const char* begin, const char* end, chars_format format_flags); } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/charconv_parse.h b/absl/strings/internal/charconv_parse.h index ddfc87f8..505998b5 100644 --- a/absl/strings/internal/charconv_parse.h +++ b/absl/strings/internal/charconv_parse.h @@ -17,10 +17,11 @@ #include <cstdint> +#include "absl/base/config.h" #include "absl/strings/charconv.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // Enum indicating whether a parsed float is a number or special value. @@ -93,6 +94,6 @@ extern template ParsedFloat ParseFloat<16>(const char* begin, const char* end, absl::chars_format format_flags); } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_ diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h new file mode 100644 index 00000000..5b5d1083 --- /dev/null +++ b/absl/strings/internal/cord_internal.h @@ -0,0 +1,151 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_ +#define ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_ + +#include <atomic> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <type_traits> + +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Wraps std::atomic for reference counting. +class Refcount { + public: + Refcount() : count_{1} {} + ~Refcount() {} + + // Increments the reference count by 1. Imposes no memory ordering. + inline void Increment() { count_.fetch_add(1, std::memory_order_relaxed); } + + // Asserts that the current refcount is greater than 0. If the refcount is + // greater than 1, decrements the reference count by 1. + // + // Returns false if there are no references outstanding; true otherwise. + // Inserts barriers to ensure that state written before this method returns + // false will be visible to a thread that just observed this method returning + // false. + inline bool Decrement() { + int32_t refcount = count_.load(std::memory_order_acquire); + assert(refcount > 0); + return refcount != 1 && count_.fetch_sub(1, std::memory_order_acq_rel) != 1; + } + + // Same as Decrement but expect that refcount is greater than 1. + inline bool DecrementExpectHighRefcount() { + int32_t refcount = count_.fetch_sub(1, std::memory_order_acq_rel); + assert(refcount > 0); + return refcount != 1; + } + + // Returns the current reference count using acquire semantics. + inline int32_t Get() const { return count_.load(std::memory_order_acquire); } + + // Returns whether the atomic integer is 1. + // If the reference count is used in the conventional way, a + // reference count of 1 implies that the current thread owns the + // reference and no other thread shares it. + // This call performs the test for a reference count of one, and + // performs the memory barrier needed for the owning thread + // to act on the object, knowing that it has exclusive access to the + // object. + inline bool IsOne() { return count_.load(std::memory_order_acquire) == 1; } + + private: + std::atomic<int32_t> count_; +}; + +// The overhead of a vtable is too much for Cord, so we roll our own subclasses +// using only a single byte to differentiate classes from each other - the "tag" +// byte. Define the subclasses first so we can provide downcasting helper +// functions in the base class. + +struct CordRepConcat; +struct CordRepSubstring; +struct CordRepExternal; + +struct CordRep { + // The following three fields have to be less than 32 bytes since + // that is the smallest supported flat node size. + // We use uint64_t for the length even in 32-bit binaries. + uint64_t length; + Refcount refcount; + // If tag < FLAT, it represents CordRepKind and indicates the type of node. + // Otherwise, the node type is CordRepFlat and the tag is the encoded size. + uint8_t tag; + char data[1]; // Starting point for flat array: MUST BE LAST FIELD of CordRep + + inline CordRepConcat* concat(); + inline const CordRepConcat* concat() const; + inline CordRepSubstring* substring(); + inline const CordRepSubstring* substring() const; + inline CordRepExternal* external(); + inline const CordRepExternal* external() const; +}; + +struct CordRepConcat : public CordRep { + CordRep* left; + CordRep* right; + + uint8_t depth() const { return static_cast<uint8_t>(data[0]); } + void set_depth(uint8_t depth) { data[0] = static_cast<char>(depth); } +}; + +struct CordRepSubstring : public CordRep { + size_t start; // Starting offset of substring in child + CordRep* child; +}; + +// TODO(strel): replace the following logic (and related functions in cord.cc) +// with container_internal::Layout. + +// Alignment requirement for CordRepExternal so that the type erased releaser +// will be stored at a suitably aligned address. +constexpr size_t ExternalRepAlignment() { +#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) + return __STDCPP_DEFAULT_NEW_ALIGNMENT__; +#else + return alignof(max_align_t); +#endif +} + +// Type for function pointer that will invoke and destroy the type-erased +// releaser function object. Accepts a pointer to the releaser and the +// `string_view` that were passed in to `NewExternalRep` below. The return value +// is the size of the `Releaser` type. +using ExternalReleaserInvoker = size_t (*)(void*, absl::string_view); + +// External CordReps are allocated together with a type erased releaser. The +// releaser is stored in the memory directly following the CordRepExternal. +struct alignas(ExternalRepAlignment()) CordRepExternal : public CordRep { + const char* base; + // Pointer to function that knows how to call and destroy the releaser. + ExternalReleaserInvoker releaser_invoker; +}; + +// TODO(strel): look into removing, it doesn't seem like anything relies on this +static_assert(sizeof(CordRepConcat) == sizeof(CordRepSubstring), ""); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl +#endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_ diff --git a/absl/strings/internal/escaping.cc b/absl/strings/internal/escaping.cc new file mode 100644 index 00000000..c5271286 --- /dev/null +++ b/absl/strings/internal/escaping.cc @@ -0,0 +1,180 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/escaping.h" + +#include "absl/base/internal/endian.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace strings_internal { + +const char kBase64Chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) { + // Base64 encodes three bytes of input at a time. If the input is not + // divisible by three, we pad as appropriate. + // + // (from https://tools.ietf.org/html/rfc3548) + // Special processing is performed if fewer than 24 bits are available + // at the end of the data being encoded. A full encoding quantum is + // always completed at the end of a quantity. When fewer than 24 input + // bits are available in an input group, zero bits are added (on the + // right) to form an integral number of 6-bit groups. Padding at the + // end of the data is performed using the '=' character. Since all base + // 64 input is an integral number of octets, only the following cases + // can arise: + + // Base64 encodes each three bytes of input into four bytes of output. + size_t len = (input_len / 3) * 4; + + if (input_len % 3 == 0) { + // (from https://tools.ietf.org/html/rfc3548) + // (1) the final quantum of encoding input is an integral multiple of 24 + // bits; here, the final unit of encoded output will be an integral + // multiple of 4 characters with no "=" padding, + } else if (input_len % 3 == 1) { + // (from https://tools.ietf.org/html/rfc3548) + // (2) the final quantum of encoding input is exactly 8 bits; here, the + // final unit of encoded output will be two characters followed by two + // "=" padding characters, or + len += 2; + if (do_padding) { + len += 2; + } + } else { // (input_len % 3 == 2) + // (from https://tools.ietf.org/html/rfc3548) + // (3) the final quantum of encoding input is exactly 16 bits; here, the + // final unit of encoded output will be three characters followed by one + // "=" padding character. + len += 3; + if (do_padding) { + len += 1; + } + } + + assert(len >= input_len); // make sure we didn't overflow + return len; +} + +size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, + size_t szdest, const char* base64, + bool do_padding) { + static const char kPad64 = '='; + + if (szsrc * 4 > szdest * 3) return 0; + + char* cur_dest = dest; + const unsigned char* cur_src = src; + + char* const limit_dest = dest + szdest; + const unsigned char* const limit_src = src + szsrc; + + // Three bytes of data encodes to four characters of cyphertext. + // So we can pump through three-byte chunks atomically. + if (szsrc >= 3) { // "limit_src - 3" is UB if szsrc < 3. + while (cur_src < limit_src - 3) { // While we have >= 32 bits. + uint32_t in = absl::big_endian::Load32(cur_src) >> 8; + + cur_dest[0] = base64[in >> 18]; + in &= 0x3FFFF; + cur_dest[1] = base64[in >> 12]; + in &= 0xFFF; + cur_dest[2] = base64[in >> 6]; + in &= 0x3F; + cur_dest[3] = base64[in]; + + cur_dest += 4; + cur_src += 3; + } + } + // To save time, we didn't update szdest or szsrc in the loop. So do it now. + szdest = limit_dest - cur_dest; + szsrc = limit_src - cur_src; + + /* now deal with the tail (<=3 bytes) */ + switch (szsrc) { + case 0: + // Nothing left; nothing more to do. + break; + case 1: { + // One byte left: this encodes to two characters, and (optionally) + // two pad characters to round out the four-character cypherblock. + if (szdest < 2) return 0; + uint32_t in = cur_src[0]; + cur_dest[0] = base64[in >> 2]; + in &= 0x3; + cur_dest[1] = base64[in << 4]; + cur_dest += 2; + szdest -= 2; + if (do_padding) { + if (szdest < 2) return 0; + cur_dest[0] = kPad64; + cur_dest[1] = kPad64; + cur_dest += 2; + szdest -= 2; + } + break; + } + case 2: { + // Two bytes left: this encodes to three characters, and (optionally) + // one pad character to round out the four-character cypherblock. + if (szdest < 3) return 0; + uint32_t in = absl::big_endian::Load16(cur_src); + cur_dest[0] = base64[in >> 10]; + in &= 0x3FF; + cur_dest[1] = base64[in >> 4]; + in &= 0x00F; + cur_dest[2] = base64[in << 2]; + cur_dest += 3; + szdest -= 3; + if (do_padding) { + if (szdest < 1) return 0; + cur_dest[0] = kPad64; + cur_dest += 1; + szdest -= 1; + } + break; + } + case 3: { + // Three bytes left: same as in the big loop above. We can't do this in + // the loop because the loop above always reads 4 bytes, and the fourth + // byte is past the end of the input. + if (szdest < 4) return 0; + uint32_t in = (cur_src[0] << 16) + absl::big_endian::Load16(cur_src + 1); + cur_dest[0] = base64[in >> 18]; + in &= 0x3FFFF; + cur_dest[1] = base64[in >> 12]; + in &= 0xFFF; + cur_dest[2] = base64[in >> 6]; + in &= 0x3F; + cur_dest[3] = base64[in]; + cur_dest += 4; + szdest -= 4; + break; + } + default: + // Should not be reached: blocks of 4 bytes are handled + // in the while loop before this switch statement. + ABSL_RAW_LOG(FATAL, "Logic problem? szsrc = %zu", szsrc); + break; + } + return (cur_dest - dest); +} + +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/escaping.h b/absl/strings/internal/escaping.h new file mode 100644 index 00000000..6a9ce602 --- /dev/null +++ b/absl/strings/internal/escaping.h @@ -0,0 +1,58 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_ESCAPING_H_ +#define ABSL_STRINGS_INTERNAL_ESCAPING_H_ + +#include <cassert> + +#include "absl/strings/internal/resize_uninitialized.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace strings_internal { + +ABSL_CONST_INIT extern const char kBase64Chars[]; + +// Calculates how long a string will be when it is base64 encoded given its +// length and whether or not the result should be padded. +size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding); + +// Base64-encodes `src` using the alphabet provided in `base64` and writes the +// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars +// until its length is a multiple of 3. Returns the length of `dest`. +size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, + size_t szdest, const char* base64, bool do_padding); + +// Base64-encodes `src` using the alphabet provided in `base64` and writes the +// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars +// until its length is a multiple of 3. +template <typename String> +void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest, + bool do_padding, const char* base64_chars) { + const size_t calc_escaped_size = + CalculateBase64EscapedLenInternal(szsrc, do_padding); + STLStringResizeUninitialized(dest, calc_escaped_size); + + const size_t escaped_len = Base64EscapeInternal( + src, szsrc, &(*dest)[0], dest->size(), base64_chars, do_padding); + assert(calc_escaped_size == escaped_len); + dest->erase(escaped_len); +} + +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_ESCAPING_H_ diff --git a/absl/strings/internal/escaping_test_common.h b/absl/strings/internal/escaping_test_common.h index ecd3aa35..7b18017a 100644 --- a/absl/strings/internal/escaping_test_common.h +++ b/absl/strings/internal/escaping_test_common.h @@ -22,7 +22,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { struct base64_testcase { @@ -127,7 +127,7 @@ inline const std::array<base64_testcase, 5>& base64_strings() { } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_ESCAPING_TEST_COMMON_H_ diff --git a/absl/strings/internal/memutil.cc b/absl/strings/internal/memutil.cc index 05251377..2519c688 100644 --- a/absl/strings/internal/memutil.cc +++ b/absl/strings/internal/memutil.cc @@ -17,7 +17,7 @@ #include <cstdlib> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { int memcasecmp(const char* s1, const char* s2, size_t len) { @@ -108,5 +108,5 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/memutil.h b/absl/strings/internal/memutil.h index 4efac989..9ad05358 100644 --- a/absl/strings/internal/memutil.h +++ b/absl/strings/internal/memutil.h @@ -69,7 +69,7 @@ #include "absl/strings/ascii.h" // for absl::ascii_tolower namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { inline char* memcat(char* dest, size_t destlen, const char* src, @@ -142,7 +142,7 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, size_t neelen); } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_MEMUTIL_H_ diff --git a/absl/strings/internal/numbers_test_common.h b/absl/strings/internal/numbers_test_common.h index 3f6965f2..1a1e50c4 100644 --- a/absl/strings/internal/numbers_test_common.h +++ b/absl/strings/internal/numbers_test_common.h @@ -23,8 +23,10 @@ #include <limits> #include <string> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { template <typename IntType> @@ -43,8 +45,9 @@ inline bool Itoa(IntType value, int base, std::string* destination) { while (value != 0) { const IntType next_value = value / base; // Can't use std::abs here because of problems when IntType is unsigned. - int remainder = value > next_value * base ? value - next_value * base - : next_value * base - value; + int remainder = + static_cast<int>(value > next_value * base ? value - next_value * base + : next_value * base - value); char c = remainder < 10 ? '0' + remainder : 'A' + remainder - 10; destination->insert(0, 1, c); value = next_value; @@ -175,7 +178,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() { } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_NUMBERS_TEST_COMMON_H_ diff --git a/absl/strings/internal/ostringstream.cc b/absl/strings/internal/ostringstream.cc index ce2dd6c7..05324c78 100644 --- a/absl/strings/internal/ostringstream.cc +++ b/absl/strings/internal/ostringstream.cc @@ -15,7 +15,7 @@ #include "absl/strings/internal/ostringstream.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { OStringStream::Buf::int_type OStringStream::overflow(int c) { @@ -32,5 +32,5 @@ std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) { } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/ostringstream.h b/absl/strings/internal/ostringstream.h index 2cf65133..d25d6047 100644 --- a/absl/strings/internal/ostringstream.h +++ b/absl/strings/internal/ostringstream.h @@ -23,7 +23,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // The same as std::ostringstream but appends to a user-specified std::string, @@ -83,7 +83,7 @@ class OStringStream : private std::basic_streambuf<char>, public std::ostream { }; } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_ diff --git a/absl/strings/internal/pow10_helper.cc b/absl/strings/internal/pow10_helper.cc index 5c02ab8f..42e96c34 100644 --- a/absl/strings/internal/pow10_helper.cc +++ b/absl/strings/internal/pow10_helper.cc @@ -17,7 +17,7 @@ #include <cmath> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { namespace { @@ -118,5 +118,5 @@ double Pow10(int exp) { } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/pow10_helper.h b/absl/strings/internal/pow10_helper.h index c9a1b27f..c37c2c3f 100644 --- a/absl/strings/internal/pow10_helper.h +++ b/absl/strings/internal/pow10_helper.h @@ -22,8 +22,10 @@ #include <vector> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // Computes the precise value of 10^exp. (I.e. the nearest representable @@ -32,7 +34,7 @@ namespace strings_internal { double Pow10(int exp); } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_POW10_HELPER_H_ diff --git a/absl/strings/internal/pow10_helper_test.cc b/absl/strings/internal/pow10_helper_test.cc index 4a62a70d..a4ff76d3 100644 --- a/absl/strings/internal/pow10_helper_test.cc +++ b/absl/strings/internal/pow10_helper_test.cc @@ -20,7 +20,7 @@ #include "absl/strings/str_format.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { namespace { @@ -118,5 +118,5 @@ TEST(Pow10HelperTest, Works) { } // namespace } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h index ab1d8684..e42628e3 100644 --- a/absl/strings/internal/resize_uninitialized.h +++ b/absl/strings/internal/resize_uninitialized.h @@ -25,7 +25,7 @@ #include "absl/meta/type_traits.h" // for void_t namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // Is a subclass of true_type or false_type, depending on whether or not @@ -36,8 +36,7 @@ struct ResizeUninitializedTraits { static void Resize(string_type* s, size_t new_size) { s->resize(new_size); } }; -// __resize_default_init is provided by libc++ >= 8.0 and by Google's internal -// ::string implementation. +// __resize_default_init is provided by libc++ >= 8.0 template <typename string_type> struct ResizeUninitializedTraits< string_type, absl::void_t<decltype(std::declval<string_type&>() @@ -68,7 +67,7 @@ inline void STLStringResizeUninitialized(string_type* s, size_t new_size) { } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ diff --git a/absl/strings/internal/resize_uninitialized_test.cc b/absl/strings/internal/resize_uninitialized_test.cc index c5be0b12..0f8b3c2a 100644 --- a/absl/strings/internal/resize_uninitialized_test.cc +++ b/absl/strings/internal/resize_uninitialized_test.cc @@ -20,13 +20,27 @@ namespace { int resize_call_count = 0; +// A mock string class whose only purpose is to track how many times its +// resize() method has been called. struct resizable_string { + size_t size() const { return 0; } + char& operator[](size_t) { + static char c = '\0'; + return c; + } void resize(size_t) { resize_call_count += 1; } }; int resize_default_init_call_count = 0; +// A mock string class whose only purpose is to track how many times its +// resize() and __resize_default_init() methods have been called. struct resize_default_init_string { + size_t size() const { return 0; } + char& operator[](size_t) { + static char c = '\0'; + return c; + } void resize(size_t) { resize_call_count += 1; } void __resize_default_init(size_t) { resize_default_init_call_count += 1; } }; diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h index af50be7c..6035ca45 100644 --- a/absl/strings/internal/stl_type_traits.h +++ b/absl/strings/internal/stl_type_traits.h @@ -40,7 +40,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { template <typename C, template <typename...> class T> @@ -243,6 +243,6 @@ struct IsStrictlyBaseOfAndConvertibleToSTLContainer IsConvertibleToSTLContainer<C>> {}; } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_ diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc index 667cc133..4d0604e0 100644 --- a/absl/strings/internal/str_format/arg.cc +++ b/absl/strings/internal/str_format/arg.cc @@ -14,7 +14,7 @@ #include "absl/strings/internal/str_format/float_conversion.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -33,6 +33,10 @@ void ReducePadding(size_t n, size_t *capacity) { template <typename T> struct MakeUnsigned : std::make_unsigned<T> {}; template <> +struct MakeUnsigned<absl::int128> { + using type = absl::uint128; +}; +template <> struct MakeUnsigned<absl::uint128> { using type = absl::uint128; }; @@ -40,6 +44,8 @@ struct MakeUnsigned<absl::uint128> { template <typename T> struct IsSigned : std::is_signed<T> {}; template <> +struct IsSigned<absl::int128> : std::true_type {}; +template <> struct IsSigned<absl::uint128> : std::false_type {}; class ConvertedIntInfo { @@ -82,7 +88,7 @@ class ConvertedIntInfo { template <typename T> void UnsignedToStringRight(T u, ConversionChar conv) { char *p = end(); - switch (conv.radix()) { + switch (FormatConversionCharRadix(conv)) { default: case 10: for (; u; u /= 10) @@ -93,7 +99,7 @@ class ConvertedIntInfo { *--p = static_cast<char>('0' + static_cast<size_t>(u % 8)); break; case 16: { - const char *digits = kDigit[conv.upper() ? 1 : 0]; + const char *digits = kDigit[FormatConversionCharIsUpper(conv) ? 1 : 0]; for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)]; break; } @@ -115,21 +121,20 @@ class ConvertedIntInfo { string_view BaseIndicator(const ConvertedIntInfo &info, const ConversionSpec conv) { bool alt = conv.flags().alt; - int radix = conv.conv().radix(); - if (conv.conv().id() == ConversionChar::p) - alt = true; // always show 0x for %p. + int radix = FormatConversionCharRadix(conv.conv()); + if (conv.conv() == ConversionChar::p) alt = true; // always show 0x for %p. // From the POSIX description of '#' flag: // "For x or X conversion specifiers, a non-zero result shall have // 0x (or 0X) prefixed to it." if (alt && radix == 16 && !info.digits().empty()) { - if (conv.conv().upper()) return "0X"; + if (FormatConversionCharIsUpper(conv.conv())) return "0X"; return "0x"; } return {}; } string_view SignColumn(bool neg, const ConversionSpec conv) { - if (conv.conv().is_signed()) { + if (FormatConversionCharIsSigned(conv.conv())) { if (neg) return "-"; if (conv.flags().show_pos) return "+"; if (conv.flags().sign_col) return " "; @@ -169,7 +174,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, if (!precision_specified) precision = 1; - if (conv.flags().alt && conv.conv().id() == ConversionChar::o) { + if (conv.flags().alt && conv.conv() == ConversionChar::o) { // From POSIX description of the '#' (alt) flag: // "For o conversion, it increases the precision (if necessary) to // force the first digit of the result to be zero." @@ -205,7 +210,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, template <typename T> bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { ConvertedIntInfo info(v, conv.conv()); - if (conv.flags().basic && conv.conv().id() != ConversionChar::p) { + if (conv.flags().basic && (conv.conv() != ConversionChar::p)) { if (info.is_neg()) sink->Append(1, '-'); if (info.digits().empty()) { sink->Append(1, '0'); @@ -219,14 +224,13 @@ bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { template <typename T> bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (conv.conv().is_float()) { + if (FormatConversionCharIsFloat(conv.conv())) { return FormatConvertImpl(static_cast<double>(v), conv, sink).value; } - if (conv.conv().id() == ConversionChar::c) + if (conv.conv() == ConversionChar::c) return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); - if (!conv.conv().is_integral()) - return false; - if (!conv.conv().is_signed() && IsSigned<T>::value) { + if (!FormatConversionCharIsIntegral(conv.conv())) return false; + if (!FormatConversionCharIsSigned(conv.conv()) && IsSigned<T>::value) { using U = typename MakeUnsigned<T>::type; return FormatConvertImpl(static_cast<U>(v), conv, sink).value; } @@ -235,13 +239,13 @@ bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { template <typename T> bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { - return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink); + return FormatConversionCharIsFloat(conv.conv()) && + ConvertFloatImpl(v, conv, sink); } inline bool ConvertStringArg(string_view v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (conv.conv().id() != ConversionChar::s) - return false; + if (conv.conv() != ConversionChar::s) return false; if (conv.flags().basic) { sink->Append(v); return true; @@ -268,7 +272,7 @@ ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (conv.conv().id() == ConversionChar::p) + if (conv.conv() == ConversionChar::p) return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; size_t len; if (v == nullptr) { @@ -276,7 +280,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, } else if (conv.precision() < 0) { len = std::strlen(v); } else { - // If precision is set, we look for the null terminator on the valid range. + // If precision is set, we look for the NUL-terminator on the valid range. len = std::find(v, v + conv.precision(), '\0') - v; } return {ConvertStringArg(string_view(v, len), conv, sink)}; @@ -285,8 +289,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, // ==================== Raw pointers ==================== ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (conv.conv().id() != ConversionChar::p) - return {false}; + if (conv.conv() != ConversionChar::p) return {false}; if (!v.value) { sink->Append("(nil)"); return {true}; @@ -364,6 +367,11 @@ IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } +IntegralConvertResult FormatConvertImpl(absl::int128 v, + const ConversionSpec conv, + FormatSinkImpl *sink) { + return {ConvertIntArg(v, conv, sink)}; +} IntegralConvertResult FormatConvertImpl(absl::uint128 v, const ConversionSpec conv, FormatSinkImpl *sink) { @@ -373,7 +381,8 @@ IntegralConvertResult FormatConvertImpl(absl::uint128 v, ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); + } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index b6f708c6..7a937563 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h @@ -18,12 +18,10 @@ #include "absl/strings/internal/str_format/extension.h" #include "absl/strings/string_view.h" -class Cord; -class CordReader; - namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN +class Cord; class FormatCountCapture; class FormatSink; @@ -68,12 +66,11 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v, FormatSinkImpl* sink); template <class AbslCord, typename std::enable_if< - std::is_same<AbslCord, ::Cord>::value>::type* = nullptr, - class AbslCordReader = ::CordReader> + std::is_same<AbslCord, absl::Cord>::value>::type* = nullptr> ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, ConversionSpec conv, FormatSinkImpl* sink) { - if (conv.conv().id() != ConversionChar::s) return {false}; + if (conv.conv() != ConversionChar::s) return {false}; bool is_left = conv.flags().left; size_t space_remaining = 0; @@ -91,11 +88,17 @@ ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' '); - string_view piece; - for (AbslCordReader reader(value); - to_write > 0 && reader.ReadFragment(&piece); to_write -= piece.size()) { - if (piece.size() > to_write) piece.remove_suffix(piece.size() - to_write); + for (string_view piece : value.Chunks()) { + if (piece.size() > to_write) { + piece.remove_suffix(piece.size() - to_write); + to_write = 0; + } else { + to_write -= piece.size(); + } sink->Append(piece); + if (to_write == 0) { + break; + } } if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' '); @@ -145,6 +148,8 @@ IntegralConvertResult FormatConvertImpl(long long v, // NOLINT IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT ConversionSpec conv, FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(int128 v, ConversionSpec conv, + FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv, FormatSinkImpl* sink); template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0> @@ -180,8 +185,7 @@ struct FormatCountCaptureHelper { FormatSinkImpl* sink) { const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; - if (conv.conv().id() != str_format_internal::ConversionChar::n) - return {false}; + if (conv.conv() != str_format_internal::ConversionChar::n) return {false}; *v2.p_ = static_cast<int>(sink->size()); return {true}; } @@ -373,7 +377,7 @@ class FormatArgImpl { template <typename T> static bool Dispatch(Data arg, ConversionSpec spec, void* out) { // A `none` conv indicates that we want the `int` conversion. - if (ABSL_PREDICT_FALSE(spec.conv().id() == ConversionChar::none)) { + if (ABSL_PREDICT_FALSE(spec.conv() == ConversionChar::none)) { return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), std::is_enum<T>()); } @@ -409,6 +413,7 @@ class FormatArgImpl { __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */ \ __VA_ARGS__); \ + ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int128, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \ @@ -419,8 +424,9 @@ class FormatArgImpl { ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern); + } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc index c9c51951..8d30d8b8 100644 --- a/absl/strings/internal/str_format/arg_test.cc +++ b/absl/strings/internal/str_format/arg_test.cc @@ -14,7 +14,7 @@ #include "absl/strings/str_format.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -96,10 +96,10 @@ TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) { std::string s; FormatSinkImpl sink(&s); ConversionSpec conv; - conv.set_conv(ConversionChar::FromChar('s')); - conv.set_flags(Flags()); - conv.set_width(-1); - conv.set_precision(-1); + FormatConversionSpecImplFriend::SetConversionChar(ConversionChar::s, &conv); + FormatConversionSpecImplFriend::SetFlags(Flags(), &conv); + FormatConversionSpecImplFriend::SetWidth(-1, &conv); + FormatConversionSpecImplFriend::SetPrecision(-1, &conv); EXPECT_TRUE( FormatArgImplFriend::Convert(FormatArgImpl(kMyArray), conv, &sink)); sink.Flush(); @@ -109,5 +109,5 @@ const char kMyArray[] = "ABCDE"; } // namespace } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc index 48a306d4..27522fdb 100644 --- a/absl/strings/internal/str_format/bind.cc +++ b/absl/strings/internal/str_format/bind.cc @@ -6,7 +6,7 @@ #include <string> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -66,19 +66,22 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound, return false; } - bound->set_width(width); - bound->set_precision(precision); - bound->set_flags(unbound->flags); - if (force_left) - bound->set_left(true); + FormatConversionSpecImplFriend::SetWidth(width, bound); + FormatConversionSpecImplFriend::SetPrecision(precision, bound); + + if (force_left) { + Flags flags = unbound->flags; + flags.left = true; + FormatConversionSpecImplFriend::SetFlags(flags, bound); + } else { + FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); + } } else { - bound->set_flags(unbound->flags); - bound->set_width(-1); - bound->set_precision(-1); + FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); + FormatConversionSpecImplFriend::SetWidth(-1, bound); + FormatConversionSpecImplFriend::SetPrecision(-1, bound); } - - bound->set_length_mod(unbound->length_mod); - bound->set_conv(unbound->conv); + FormatConversionSpecImplFriend::SetConversionChar(unbound->conv, bound); bound->set_arg(arg); return true; } @@ -140,10 +143,11 @@ class SummarizingConverter { UntypedFormatSpecImpl spec("%d"); std::ostringstream ss; - ss << "{" << Streamable(spec, {*bound.arg()}) << ":" << bound.flags(); + ss << "{" << Streamable(spec, {*bound.arg()}) << ":" + << FormatConversionSpecImplFriend::FlagsToString(bound); if (bound.width() >= 0) ss << bound.width(); if (bound.precision() >= 0) ss << "." << bound.precision(); - ss << bound.length_mod() << bound.conv() << "}"; + ss << bound.conv() << "}"; Append(ss.str()); return true; } @@ -197,6 +201,15 @@ std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format, return *out; } +std::string FormatPack(const UntypedFormatSpecImpl format, + absl::Span<const FormatArgImpl> args) { + std::string out; + if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) { + out.clear(); + } + return out; +} + int FprintF(std::FILE* output, const UntypedFormatSpecImpl format, absl::Span<const FormatArgImpl> args) { FILERawSink sink(output); @@ -228,5 +241,5 @@ int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format, } } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index db79eb6c..cf41b197 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h @@ -13,7 +13,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN class UntypedFormatSpec; @@ -74,7 +74,7 @@ class FormatSpecTemplate using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type; public: -#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER +#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER // Honeypot overload for when the std::string is not constexpr. // We use the 'unavailable' attribute to give a better compiler error than @@ -122,8 +122,8 @@ class FormatSpecTemplate #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER template <Conv... C, typename = typename std::enable_if< - sizeof...(C) == sizeof...(Args) && - AllOf(Contains(ArgumentToConv<Args>(), + AllOf(sizeof...(C) == sizeof...(Args), + Contains(ArgumentToConv<Args>(), C)...)>::type> FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT : Base(&pc) {} @@ -179,12 +179,8 @@ bool FormatUntyped(FormatRawSinkImpl raw_sink, std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format, absl::Span<const FormatArgImpl> args); -inline std::string FormatPack(const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { - std::string out; - AppendPack(&out, format, args); - return out; -} +std::string FormatPack(const UntypedFormatSpecImpl format, + absl::Span<const FormatArgImpl> args); int FprintF(std::FILE* output, UntypedFormatSpecImpl format, absl::Span<const FormatArgImpl> args); @@ -207,7 +203,7 @@ class StreamedWrapper { }; } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ diff --git a/absl/strings/internal/str_format/bind_test.cc b/absl/strings/internal/str_format/bind_test.cc index 8bccc92a..64790a85 100644 --- a/absl/strings/internal/str_format/bind_test.cc +++ b/absl/strings/internal/str_format/bind_test.cc @@ -6,7 +6,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -113,18 +113,18 @@ TEST_F(FormatBindTest, FormatPack) { FormatArgImpl(ia[2]), FormatArgImpl(ia[3]), FormatArgImpl(ia[4])}; const Expectation kExpect[] = { - {__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"}, - {__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"}, - {__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"}, - {__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"}, - {__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"}, - {__LINE__, "a%.*fb", "a{20:.10f}b"}, - {__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"}, - {__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"}, - {__LINE__, "a%04ldb", "a{10:04ld}b"}, - {__LINE__, "a%-#04lldb", "a{10:-#04lld}b"}, - {__LINE__, "a%1$*5$db", "a{10:-10d}b"}, - {__LINE__, "a%1$.*5$db", "a{10:d}b"}, + {__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"}, + {__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"}, + {__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"}, + {__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"}, + {__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"}, + {__LINE__, "a%.*fb", "a{20:.10f}b"}, + {__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"}, + {__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"}, + {__LINE__, "a%04ldb", "a{10:04d}b"}, + {__LINE__, "a%-#04lldb", "a{10:-#04d}b"}, + {__LINE__, "a%1$*5$db", "a{10:-10d}b"}, + {__LINE__, "a%1$.*5$db", "a{10:d}b"}, }; for (const Expectation &e : kExpect) { absl::string_view fmt = e.fmt; @@ -139,5 +139,5 @@ TEST_F(FormatBindTest, FormatPack) { } // namespace } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h index 262887cd..8993a79b 100644 --- a/absl/strings/internal/str_format/checker.h +++ b/absl/strings/internal/str_format/checker.h @@ -1,21 +1,20 @@ #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ +#include "absl/base/attributes.h" #include "absl/strings/internal/str_format/arg.h" #include "absl/strings/internal/str_format/extension.h" // Compile time check support for entry points. #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER -#if defined(__clang__) && !defined(__native_client__) -#if __has_attribute(enable_if) +#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1 -#endif // __has_attribute(enable_if) -#endif // defined(__clang__) && !defined(__native_client__) +#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { constexpr bool AllOf() { return true; } @@ -32,7 +31,7 @@ constexpr Conv ArgumentToConv() { std::declval<FormatSinkImpl*>()))::kConv; } -#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER +#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER constexpr bool ContainsChar(const char* chars, char c) { return *chars == c || (*chars && ContainsChar(chars + 1, c)); @@ -321,7 +320,7 @@ constexpr bool ValidFormatImpl(string_view format) { #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc index 2aa3b128..ea2a7681 100644 --- a/absl/strings/internal/str_format/checker_test.cc +++ b/absl/strings/internal/str_format/checker_test.cc @@ -5,7 +5,7 @@ #include "absl/strings/str_format.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -13,7 +13,7 @@ std::string ConvToString(Conv conv) { std::string out; #define CONV_SET_CASE(c) \ if (Contains(conv, Conv::c)) out += #c; - ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) #undef CONV_SET_CASE if (Contains(conv, Conv::star)) out += "*"; return out; @@ -36,7 +36,7 @@ TEST(StrFormatChecker, ArgumentToConv) { EXPECT_EQ(ConvToString(conv), "p"); } -#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER +#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER struct Case { bool result; @@ -148,5 +148,5 @@ TEST(StrFormatChecker, LongFormat) { } // namespace } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index 751bfc34..cbcd7caf 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc @@ -5,11 +5,13 @@ #include <cmath> #include <string> +#include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" #include "absl/strings/internal/str_format/bind.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -152,18 +154,26 @@ TEST_F(FormatConvertTest, StringPrecision) { UntypedFormatSpecImpl format("%.1s"); EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)})); - // We cap at the nul terminator. + // We cap at the NUL-terminator. p = "ABC"; UntypedFormatSpecImpl format2("%.10s"); EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)})); } +// Pointer formatting is implementation defined. This checks that the argument +// can be matched to `ptr`. +MATCHER_P(MatchesPointerString, ptr, "") { + if (ptr == nullptr && arg == "(nil)") { + return true; + } + void* parsed = nullptr; + if (sscanf(arg.c_str(), "%p", &parsed) != 1) { + ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str()); + } + return ptr == parsed; +} + TEST_F(FormatConvertTest, Pointer) { -#ifdef _MSC_VER - // MSVC's printf implementation prints pointers differently. We can't easily - // compare our implementation to theirs. - return; -#endif static int x = 0; const int *xp = &x; char c = 'h'; @@ -174,48 +184,62 @@ TEST_F(FormatConvertTest, Pointer) { using VoidF = void (*)(); VoidF fp = [] {}, fnil = nullptr; volatile char vc; - volatile char* vcp = &vc; - volatile char* vcnil = nullptr; - const FormatArgImpl args[] = { + volatile char *vcp = &vc; + volatile char *vcnil = nullptr; + const FormatArgImpl args_array[] = { FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil), FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp), FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil), }; - struct Expectation { - std::string out; - const char *fmt; - }; - const Expectation kExpect[] = { - {StrPrint("%p", &x), "%p"}, - {StrPrint("%20p", &x), "%20p"}, - {StrPrint("%.1p", &x), "%.1p"}, - {StrPrint("%.20p", &x), "%.20p"}, - {StrPrint("%30.20p", &x), "%30.20p"}, - - {StrPrint("%-p", &x), "%-p"}, - {StrPrint("%-20p", &x), "%-20p"}, - {StrPrint("%-.1p", &x), "%-.1p"}, - {StrPrint("%.20p", &x), "%.20p"}, - {StrPrint("%-30.20p", &x), "%-30.20p"}, - - {StrPrint("%p", cp), "%2$p"}, // const char* - {"(nil)", "%3$p"}, // null const char * - {"(nil)", "%4$p"}, // null const int * - {StrPrint("%p", mcp), "%5$p"}, // nonconst char* - - {StrPrint("%p", fp), "%6$p"}, // function pointer - {StrPrint("%p", vcp), "%8$p"}, // function pointer - -#ifndef __APPLE__ - // Apple's printf differs here (0x0 vs. nil) - {StrPrint("%p", fnil), "%7$p"}, // null function pointer - {StrPrint("%p", vcnil), "%9$p"}, // null function pointer -#endif - }; - for (const Expectation &e : kExpect) { - UntypedFormatSpecImpl format(e.fmt); - EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))) << e.fmt; - } + auto args = absl::MakeConstSpan(args_array); + + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args), + MatchesPointerString(&x)); + + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args), + MatchesPointerString(&x)); + + // const char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args), + MatchesPointerString(cp)); + // null const int* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args), + MatchesPointerString(nullptr)); + // null const char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args), + MatchesPointerString(nullptr)); + // nonconst char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args), + MatchesPointerString(mcp)); + + // function pointers + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args), + MatchesPointerString(reinterpret_cast<const void*>(fp))); + EXPECT_THAT( + FormatPack(UntypedFormatSpecImpl("%8$p"), args), + MatchesPointerString(reinterpret_cast<volatile const void *>(vcp))); + + // null function pointers + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args), + MatchesPointerString(nullptr)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args), + MatchesPointerString(nullptr)); } struct Cardinal { @@ -367,7 +391,6 @@ typedef ::testing::Types< AllIntTypes; INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes, TypedFormatConvertTest, AllIntTypes); - TEST_F(FormatConvertTest, VectorBool) { // Make sure vector<bool>'s values behave as bools. std::vector<bool> v = {true, false}; @@ -379,6 +402,42 @@ TEST_F(FormatConvertTest, VectorBool) { FormatArgImpl(cv[0]), FormatArgImpl(cv[1])}))); } + +TEST_F(FormatConvertTest, Int128) { + absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979; + absl::int128 negative = -positive; + absl::int128 max = absl::Int128Max(), min = absl::Int128Min(); + const FormatArgImpl args[] = {FormatArgImpl(positive), + FormatArgImpl(negative), FormatArgImpl(max), + FormatArgImpl(min)}; + + struct Case { + const char* format; + const char* expected; + } cases[] = { + {"%1$d", "2595989796776606496405"}, + {"%1$30d", " 2595989796776606496405"}, + {"%1$-30d", "2595989796776606496405 "}, + {"%1$u", "2595989796776606496405"}, + {"%1$x", "8cba9876066020f695"}, + {"%2$d", "-2595989796776606496405"}, + {"%2$30d", " -2595989796776606496405"}, + {"%2$-30d", "-2595989796776606496405 "}, + {"%2$u", "340282366920938460867384810655161715051"}, + {"%2$x", "ffffffffffffff73456789f99fdf096b"}, + {"%3$d", "170141183460469231731687303715884105727"}, + {"%3$u", "170141183460469231731687303715884105727"}, + {"%3$x", "7fffffffffffffffffffffffffffffff"}, + {"%4$d", "-170141183460469231731687303715884105728"}, + {"%4$x", "80000000000000000000000000000000"}, + }; + + for (auto c : cases) { + UntypedFormatSpecImpl format(c.format); + EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args))); + } +} + TEST_F(FormatConvertTest, Uint128) { absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979; absl::uint128 max = absl::Uint128Max(); @@ -588,5 +647,5 @@ TEST_F(FormatConvertTest, ExpectedFailures) { } // namespace } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc index 1a0c4172..2e5bc2ce 100644 --- a/absl/strings/internal/str_format/extension.cc +++ b/absl/strings/internal/str_format/extension.cc @@ -20,39 +20,8 @@ #include <string> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { -namespace { -// clang-format off -#define ABSL_LENGTH_MODS_EXPAND_ \ - X_VAL(h) X_SEP \ - X_VAL(hh) X_SEP \ - X_VAL(l) X_SEP \ - X_VAL(ll) X_SEP \ - X_VAL(L) X_SEP \ - X_VAL(j) X_SEP \ - X_VAL(z) X_SEP \ - X_VAL(t) X_SEP \ - X_VAL(q) -// clang-format on -} // namespace - -const LengthMod::Spec LengthMod::kSpecs[] = { -#define X_VAL(id) { LengthMod::id, #id, strlen(#id) } -#define X_SEP , - ABSL_LENGTH_MODS_EXPAND_, {LengthMod::none, "", 0} -#undef X_VAL -#undef X_SEP -}; - -const ConversionChar::Spec ConversionChar::kSpecs[] = { -#define X_VAL(id) { ConversionChar::id, #id[0] } -#define X_SEP , - ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP), - {ConversionChar::none, '\0'}, -#undef X_VAL -#undef X_SEP -}; std::string Flags::ToString() const { std::string s; @@ -64,10 +33,6 @@ std::string Flags::ToString() const { return s; } -const size_t LengthMod::kNumValues; - -const size_t ConversionChar::kNumValues; - bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) { size_t space_remaining = 0; if (w >= 0) space_remaining = w; @@ -82,5 +47,5 @@ bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) { } } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index 4fbe4a06..d1665753 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h @@ -17,19 +17,18 @@ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ #include <limits.h> + #include <cstddef> #include <cstring> #include <ostream> +#include "absl/base/config.h" #include "absl/base/port.h" #include "absl/strings/internal/str_format/output.h" #include "absl/strings/string_view.h" -class Cord; - namespace absl { -inline namespace lts_2019_08_08 { - +ABSL_NAMESPACE_BEGIN namespace str_format_internal { class FormatRawSinkImpl { @@ -137,56 +136,8 @@ struct Flags { } }; -struct LengthMod { - public: - enum Id : uint8_t { - h, hh, l, ll, L, j, z, t, q, none - }; - static const size_t kNumValues = none + 1; - - LengthMod() : id_(none) {} - - // Index into the opaque array of LengthMod enums. - // Requires: i < kNumValues - static LengthMod FromIndex(size_t i) { - return LengthMod(kSpecs[i].value); - } - - static LengthMod FromId(Id id) { return LengthMod(id); } - - // The length modifier std::string associated with a specified LengthMod. - string_view name() const { - const Spec& spec = kSpecs[id_]; - return {spec.name, spec.name_length}; - } - - Id id() const { return id_; } - - friend bool operator==(const LengthMod& a, const LengthMod& b) { - return a.id() == b.id(); - } - friend bool operator!=(const LengthMod& a, const LengthMod& b) { - return !(a == b); - } - friend std::ostream& operator<<(std::ostream& os, const LengthMod& v) { - return os << v.name(); - } - - private: - struct Spec { - Id value; - const char *name; - size_t name_length; - }; - static const Spec kSpecs[]; - - explicit LengthMod(Id id) : id_(id) {} - - Id id_; -}; - // clang-format off -#define ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ +#define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ /* text */ \ X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \ /* ints */ \ @@ -197,121 +148,135 @@ struct LengthMod { X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \ /* misc */ \ X_VAL(n) X_SEP X_VAL(p) -// clang-format on -struct ConversionChar { - public: - enum Id : uint8_t { +enum class FormatConversionChar : uint8_t { c, C, s, S, // text d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float n, p, // misc - none - }; - static const size_t kNumValues = none + 1; - - ConversionChar() : id_(none) {} - - public: - // Index into the opaque array of ConversionChar enums. - // Requires: i < kNumValues - static ConversionChar FromIndex(size_t i) { - return ConversionChar(kSpecs[i].value); - } + kNone, + none = kNone +}; +// clang-format on - static ConversionChar FromChar(char c) { - ConversionChar::Id out_id = ConversionChar::none; - switch (c) { -#define X_VAL(id) \ - case #id[0]: \ - out_id = ConversionChar::id; \ - break; - ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, ) -#undef X_VAL - default: - break; - } - return ConversionChar(out_id); +inline FormatConversionChar FormatConversionCharFromChar(char c) { + switch (c) { +#define ABSL_INTERNAL_X_VAL(id) \ + case #id[0]: \ + return FormatConversionChar::id; + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) +#undef ABSL_INTERNAL_X_VAL } + return FormatConversionChar::kNone; +} - static ConversionChar FromId(Id id) { return ConversionChar(id); } - Id id() const { return id_; } - - int radix() const { - switch (id()) { - case x: case X: case a: case A: case p: return 16; - case o: return 8; - default: return 10; - } +inline int FormatConversionCharRadix(FormatConversionChar c) { + switch (c) { + case FormatConversionChar::x: + case FormatConversionChar::X: + case FormatConversionChar::a: + case FormatConversionChar::A: + case FormatConversionChar::p: + return 16; + case FormatConversionChar::o: + return 8; + default: + return 10; } +} - bool upper() const { - switch (id()) { - case X: case F: case E: case G: case A: return true; - default: return false; - } +inline bool FormatConversionCharIsUpper(FormatConversionChar c) { + switch (c) { + case FormatConversionChar::X: + case FormatConversionChar::F: + case FormatConversionChar::E: + case FormatConversionChar::G: + case FormatConversionChar::A: + return true; + default: + return false; } +} - bool is_signed() const { - switch (id()) { - case d: case i: return true; - default: return false; - } +inline bool FormatConversionCharIsSigned(FormatConversionChar c) { + switch (c) { + case FormatConversionChar::d: + case FormatConversionChar::i: + return true; + default: + return false; } +} - bool is_integral() const { - switch (id()) { - case d: case i: case u: case o: case x: case X: - return true; - default: return false; - } +inline bool FormatConversionCharIsIntegral(FormatConversionChar c) { + switch (c) { + case FormatConversionChar::d: + case FormatConversionChar::i: + case FormatConversionChar::u: + case FormatConversionChar::o: + case FormatConversionChar::x: + case FormatConversionChar::X: + return true; + default: + return false; } +} - bool is_float() const { - switch (id()) { - case a: case e: case f: case g: case A: case E: case F: case G: - return true; - default: return false; - } +inline bool FormatConversionCharIsFloat(FormatConversionChar c) { + switch (c) { + case FormatConversionChar::a: + case FormatConversionChar::e: + case FormatConversionChar::f: + case FormatConversionChar::g: + case FormatConversionChar::A: + case FormatConversionChar::E: + case FormatConversionChar::F: + case FormatConversionChar::G: + return true; + default: + return false; } +} - bool IsValid() const { return id() != none; } - - // The associated char. - char Char() const { return kSpecs[id_].name; } - - friend bool operator==(const ConversionChar& a, const ConversionChar& b) { - return a.id() == b.id(); - } - friend bool operator!=(const ConversionChar& a, const ConversionChar& b) { - return !(a == b); +inline char FormatConversionCharToChar(FormatConversionChar c) { + switch (c) { +#define ABSL_INTERNAL_X_VAL(e) \ + case FormatConversionChar::e: \ + return #e[0]; +#define ABSL_INTERNAL_X_SEP + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, + ABSL_INTERNAL_X_SEP) + case FormatConversionChar::kNone: + return '\0'; +#undef ABSL_INTERNAL_X_VAL +#undef ABSL_INTERNAL_X_SEP } - friend std::ostream& operator<<(std::ostream& os, const ConversionChar& v) { - char c = v.Char(); - if (!c) c = '?'; - return os << c; - } - - private: - struct Spec { - Id value; - char name; - }; - static const Spec kSpecs[]; + return '\0'; +} - explicit ConversionChar(Id id) : id_(id) {} +// The associated char. +inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) { + char c = FormatConversionCharToChar(v); + if (!c) c = '?'; + return os << c; +} - Id id_; -}; +struct FormatConversionSpecImplFriend; -class ConversionSpec { +class FormatConversionSpec { public: - Flags flags() const { return flags_; } - LengthMod length_mod() const { return length_mod_; } - ConversionChar conv() const { + // Width and precison are not specified, no flags are set. + bool is_basic() const { return flags_.basic; } + bool has_left_flag() const { return flags_.left; } + bool has_show_pos_flag() const { return flags_.show_pos; } + bool has_sign_col_flag() const { return flags_.sign_col; } + bool has_alt_flag() const { return flags_.alt; } + bool has_zero_flag() const { return flags_.zero; } + + FormatConversionChar conversion_char() const { // Keep this field first in the struct . It generates better code when // accessing it when ConversionSpec is passed by value in registers. - static_assert(offsetof(ConversionSpec, conv_) == 0, ""); + static_assert(offsetof(FormatConversionSpec, conv_) == 0, ""); return conv_; } @@ -322,46 +287,71 @@ class ConversionSpec { // negative value. int precision() const { return precision_; } - void set_flags(Flags f) { flags_ = f; } - void set_length_mod(LengthMod lm) { length_mod_ = lm; } - void set_conv(ConversionChar c) { conv_ = c; } - void set_width(int w) { width_ = w; } - void set_precision(int p) { precision_ = p; } - void set_left(bool b) { flags_.left = b; } + // Deprecated (use has_x_flag() instead). + Flags flags() const { return flags_; } + // Deprecated + FormatConversionChar conv() const { return conversion_char(); } private: - ConversionChar conv_; + friend struct str_format_internal::FormatConversionSpecImplFriend; + FormatConversionChar conv_ = FormatConversionChar::kNone; Flags flags_; - LengthMod length_mod_; int width_; int precision_; }; -constexpr uint64_t ConversionCharToConvValue(char conv) { +struct FormatConversionSpecImplFriend final { + static void SetFlags(Flags f, FormatConversionSpec* conv) { + conv->flags_ = f; + } + static void SetConversionChar(FormatConversionChar c, + FormatConversionSpec* conv) { + conv->conv_ = c; + } + static void SetWidth(int w, FormatConversionSpec* conv) { conv->width_ = w; } + static void SetPrecision(int p, FormatConversionSpec* conv) { + conv->precision_ = p; + } + static std::string FlagsToString(const FormatConversionSpec& spec) { + return spec.flags_.ToString(); + } +}; + +constexpr uint64_t FormatConversionCharToConvValue(char conv) { return -#define CONV_SET_CASE(c) \ - conv == #c[0] ? (uint64_t{1} << (1 + ConversionChar::Id::c)): - ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) -#undef CONV_SET_CASE +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + conv == #c[0] \ + ? (uint64_t{1} << (1 + static_cast<uint8_t>(FormatConversionChar::c))) \ + : + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) +#undef ABSL_INTERNAL_CHAR_SET_CASE conv == '*' ? 1 : 0; } -enum class Conv : uint64_t { -#define CONV_SET_CASE(c) c = ConversionCharToConvValue(#c[0]), - ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) -#undef CONV_SET_CASE +enum class FormatConversionCharSet : uint64_t { +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + c = FormatConversionCharToConvValue(#c[0]), + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) +#undef ABSL_INTERNAL_CHAR_SET_CASE // Used for width/precision '*' specification. - star = ConversionCharToConvValue('*'), - + kStar = FormatConversionCharToConvValue('*'), // Some predefined values: - integral = d | i | u | o | x | X, - floating = a | e | f | g | A | E | F | G, - numeric = integral | floating, - string = s, - pointer = p + kIntegral = d | i | u | o | x | X, + kFloating = a | e | f | g | A | E | F | G, + kNumeric = kIntegral | kFloating, + kString = s, + kPointer = p, + + // The following are deprecated + star = kStar, + integral = kIntegral, + floating = kFloating, + numeric = kNumeric, + string = kString, + pointer = kPointer }; // Type safe OR operator. @@ -369,45 +359,57 @@ enum class Conv : uint64_t { // 1. operator| on enums makes them decay to integers and the result is an // integer. We need the result to stay as an enum. // 2. We use "enum class" which would not work even if we accepted the decay. -constexpr Conv operator|(Conv a, Conv b) { - return Conv(static_cast<uint64_t>(a) | static_cast<uint64_t>(b)); +constexpr FormatConversionCharSet operator|(FormatConversionCharSet a, + FormatConversionCharSet b) { + return FormatConversionCharSet(static_cast<uint64_t>(a) | + static_cast<uint64_t>(b)); } // Get a conversion with a single character in it. -constexpr Conv ConversionCharToConv(char c) { - return Conv(ConversionCharToConvValue(c)); +constexpr FormatConversionCharSet ConversionCharToConv(char c) { + return FormatConversionCharSet(FormatConversionCharToConvValue(c)); } // Checks whether `c` exists in `set`. -constexpr bool Contains(Conv set, char c) { - return (static_cast<uint64_t>(set) & ConversionCharToConvValue(c)) != 0; +constexpr bool Contains(FormatConversionCharSet set, char c) { + return (static_cast<uint64_t>(set) & FormatConversionCharToConvValue(c)) != 0; } // Checks whether all the characters in `c` are contained in `set` -constexpr bool Contains(Conv set, Conv c) { +constexpr bool Contains(FormatConversionCharSet set, + FormatConversionCharSet c) { return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) == static_cast<uint64_t>(c); } // Return type of the AbslFormatConvert() functions. -// The Conv template parameter is used to inform the framework of what -// conversion characters are supported by that AbslFormatConvert routine. -template <Conv C> -struct ConvertResult { - static constexpr Conv kConv = C; +// The FormatConversionCharSet template parameter is used to inform the +// framework of what conversion characters are supported by that +// AbslFormatConvert routine. +template <FormatConversionCharSet C> +struct FormatConvertResult { + static constexpr FormatConversionCharSet kConv = C; bool value; }; -template <Conv C> -constexpr Conv ConvertResult<C>::kConv; + +template <FormatConversionCharSet C> +constexpr FormatConversionCharSet FormatConvertResult<C>::kConv; // Return capacity - used, clipped to a minimum of 0. inline size_t Excess(size_t used, size_t capacity) { return used < capacity ? capacity - used : 0; } +// Type alias for use during migration. +using ConversionChar = FormatConversionChar; +using ConversionSpec = FormatConversionSpec; +using Conv = FormatConversionCharSet; +template <FormatConversionCharSet C> +using ConvertResult = FormatConvertResult<C>; + } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc index a0484feb..d4c647c3 100644 --- a/absl/strings/internal/str_format/float_conversion.cc +++ b/absl/strings/internal/str_format/float_conversion.cc @@ -9,7 +9,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -28,12 +28,12 @@ bool FallbackToSnprintf(const Float v, const ConversionSpec &conv, { char *fp = fmt; *fp++ = '%'; - fp = CopyStringTo(conv.flags().ToString(), fp); + fp = CopyStringTo(FormatConversionSpecImplFriend::FlagsToString(conv), fp); fp = CopyStringTo("*.*", fp); if (std::is_same<long double, Float>()) { *fp++ = 'L'; } - *fp++ = conv.conv().Char(); + *fp++ = FormatConversionCharToChar(conv.conv()); *fp = 0; assert(fp < fmt + sizeof(fmt)); } @@ -100,9 +100,11 @@ bool ConvertNonNumericFloats(char sign_char, Float v, char text[4], *ptr = text; if (sign_char) *ptr++ = sign_char; if (std::isnan(v)) { - ptr = std::copy_n(conv.conv().upper() ? "NAN" : "nan", 3, ptr); + ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan", + 3, ptr); } else if (std::isinf(v)) { - ptr = std::copy_n(conv.conv().upper() ? "INF" : "inf", 3, ptr); + ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf", + 3, ptr); } else { return false; } @@ -399,7 +401,7 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, Buffer buffer; - switch (conv.conv().id()) { + switch (conv.conv()) { case ConversionChar::f: case ConversionChar::F: if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer, @@ -416,7 +418,8 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, return FallbackToSnprintf(v, conv, sink); } if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); - PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer); + PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', + &buffer); break; case ConversionChar::g: @@ -447,7 +450,10 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, while (buffer.back() == '0') buffer.pop_back(); if (buffer.back() == '.') buffer.pop_back(); } - if (exp) PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer); + if (exp) { + PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', + &buffer); + } break; case ConversionChar::a: @@ -483,5 +489,5 @@ bool ConvertFloatImpl(double v, const ConversionSpec &conv, } } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/float_conversion.h b/absl/strings/internal/str_format/float_conversion.h index 5d76ce2b..49a6a636 100644 --- a/absl/strings/internal/str_format/float_conversion.h +++ b/absl/strings/internal/str_format/float_conversion.h @@ -4,7 +4,7 @@ #include "absl/strings/internal/str_format/extension.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { bool ConvertFloatImpl(float v, const ConversionSpec &conv, @@ -17,7 +17,7 @@ bool ConvertFloatImpl(long double v, const ConversionSpec &conv, FormatSinkImpl *sink); } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ diff --git a/absl/strings/internal/str_format/output.cc b/absl/strings/internal/str_format/output.cc index d5ead8d5..c4b24706 100644 --- a/absl/strings/internal/str_format/output.cc +++ b/absl/strings/internal/str_format/output.cc @@ -18,7 +18,7 @@ #include <cstring> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -68,5 +68,5 @@ void FILERawSink::Write(string_view v) { } } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/output.h b/absl/strings/internal/str_format/output.h index ba847471..28b288b7 100644 --- a/absl/strings/internal/str_format/output.h +++ b/absl/strings/internal/str_format/output.h @@ -28,10 +28,11 @@ #include "absl/base/port.h" #include "absl/strings/string_view.h" +namespace absl { +ABSL_NAMESPACE_BEGIN + class Cord; -namespace absl { -inline namespace lts_2019_08_08 { namespace str_format_internal { // RawSink implementation that writes into a char* buffer. @@ -77,7 +78,7 @@ inline void AbslFormatFlush(std::ostream* out, string_view s) { } template <class AbslCord, typename = typename std::enable_if< - std::is_same<AbslCord, ::Cord>::value>::type> + std::is_same<AbslCord, absl::Cord>::value>::type> inline void AbslFormatFlush(AbslCord* out, string_view s) { out->Append(s); } @@ -97,7 +98,7 @@ auto InvokeFlush(T* out, string_view s) } } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_ diff --git a/absl/strings/internal/str_format/output_test.cc b/absl/strings/internal/str_format/output_test.cc index 5c3c4afb..e54e6f70 100644 --- a/absl/strings/internal/str_format/output_test.cc +++ b/absl/strings/internal/str_format/output_test.cc @@ -21,7 +21,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { TEST(InvokeFlush, String) { @@ -68,6 +68,6 @@ TEST(BufferRawSink, Limits) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc index 4b8ca4d2..aab68db9 100644 --- a/absl/strings/internal/str_format/parser.cc +++ b/absl/strings/internal/str_format/parser.cc @@ -14,11 +14,12 @@ #include <unordered_set> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { -using CC = ConversionChar::Id; -using LM = LengthMod::Id; +using CC = ConversionChar; +using LM = LengthMod; + ABSL_CONST_INIT const ConvTag kTags[256] = { {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f @@ -205,11 +206,11 @@ flags_done: using str_format_internal::LengthMod; LengthMod length_mod = tag.as_length(); ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (c == 'h' && length_mod.id() == LengthMod::h) { - conv->length_mod = LengthMod::FromId(LengthMod::hh); + if (c == 'h' && length_mod == LengthMod::h) { + conv->length_mod = LengthMod::hh; ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else if (c == 'l' && length_mod.id() == LengthMod::l) { - conv->length_mod = LengthMod::FromId(LengthMod::ll); + } else if (c == 'l' && length_mod == LengthMod::l) { + conv->length_mod = LengthMod::ll; ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); } else { conv->length_mod = length_mod; @@ -228,6 +229,32 @@ flags_done: } // namespace +std::string LengthModToString(LengthMod v) { + switch (v) { + case LengthMod::h: + return "h"; + case LengthMod::hh: + return "hh"; + case LengthMod::l: + return "l"; + case LengthMod::ll: + return "ll"; + case LengthMod::L: + return "L"; + case LengthMod::j: + return "j"; + case LengthMod::z: + return "z"; + case LengthMod::t: + return "t"; + case LengthMod::q: + return "q"; + case LengthMod::none: + return ""; + } + return ""; +} + const char *ConsumeUnboundConversion(const char *p, const char *end, UnboundConversion *conv, int *next_arg) { if (*next_arg < 0) return ConsumeConversion<true>(p, end, conv, next_arg); @@ -295,11 +322,13 @@ bool ParsedFormatBase::MatchesConversions( if (conv.width.is_from_arg() && !add_if_valid_conv(conv.width.get_from_arg(), '*')) return false; - if (!add_if_valid_conv(conv.arg_position, conv.conv.Char())) return false; + if (!add_if_valid_conv(conv.arg_position, + FormatConversionCharToChar(conv.conv))) + return false; } return used.size() == convs.size() || allow_ignored; } } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h index d3357fde..45c90d1d 100644 --- a/absl/strings/internal/str_format/parser.h +++ b/absl/strings/internal/str_format/parser.h @@ -6,19 +6,25 @@ #include <stdlib.h> #include <cassert> +#include <cstdint> #include <initializer_list> #include <iosfwd> #include <iterator> #include <memory> +#include <string> #include <vector> #include "absl/strings/internal/str_format/checker.h" #include "absl/strings/internal/str_format/extension.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { +enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none }; + +std::string LengthModToString(LengthMod v); + // The analyzed properties of a single specified conversion. struct UnboundConversion { UnboundConversion() @@ -60,8 +66,8 @@ struct UnboundConversion { InputValue precision; Flags flags; - LengthMod length_mod; - ConversionChar conv; + LengthMod length_mod = LengthMod::none; + ConversionChar conv = FormatConversionChar::kNone; }; // Consume conversion spec prefix (not including '%') of [p, end) if valid. @@ -73,13 +79,16 @@ const char* ConsumeUnboundConversion(const char* p, const char* end, UnboundConversion* conv, int* next_arg); // Helper tag class for the table below. -// It allows fast `char -> ConversionChar/LengthMod` checking and conversions. +// It allows fast `char -> ConversionChar/LengthMod` checking and +// conversions. class ConvTag { public: - constexpr ConvTag(ConversionChar::Id id) : tag_(id) {} // NOLINT + constexpr ConvTag(ConversionChar conversion_char) // NOLINT + : tag_(static_cast<int8_t>(conversion_char)) {} // We invert the length modifiers to make them negative so that we can easily // test for them. - constexpr ConvTag(LengthMod::Id id) : tag_(~id) {} // NOLINT + constexpr ConvTag(LengthMod length_mod) // NOLINT + : tag_(~static_cast<std::int8_t>(length_mod)) {} // Everything else is -128, which is negative to make is_conv() simpler. constexpr ConvTag() : tag_(-128) {} @@ -87,11 +96,11 @@ class ConvTag { bool is_length() const { return tag_ < 0 && tag_ != -128; } ConversionChar as_conv() const { assert(is_conv()); - return ConversionChar::FromId(static_cast<ConversionChar::Id>(tag_)); + return static_cast<ConversionChar>(tag_); } LengthMod as_length() const { assert(is_length()); - return LengthMod::FromId(static_cast<LengthMod::Id>(~tag_)); + return static_cast<LengthMod>(~tag_); } private: @@ -275,7 +284,7 @@ template <str_format_internal::Conv... C> class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase { public: explicit ExtendedParsedFormat(string_view format) -#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER +#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER __attribute__(( enable_if(str_format_internal::EnsureConstexpr(format), "Format std::string is not constexpr."), @@ -318,7 +327,7 @@ class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase { : ParsedFormatBase(s, allow_ignored, {C...}) {} }; } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc index d77a8ea5..1b1ee030 100644 --- a/absl/strings/internal/str_format/parser_test.cc +++ b/absl/strings/internal/str_format/parser_test.cc @@ -7,7 +7,7 @@ #include "absl/base/macros.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { @@ -17,7 +17,7 @@ using testing::Pair; TEST(LengthModTest, Names) { struct Expectation { int line; - LengthMod::Id id; + LengthMod mod; const char *name; }; const Expectation kExpect[] = { @@ -32,18 +32,16 @@ TEST(LengthModTest, Names) { {__LINE__, LengthMod::t, "t" }, {__LINE__, LengthMod::q, "q" }, }; - EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), LengthMod::kNumValues); + EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), 10); for (auto e : kExpect) { SCOPED_TRACE(e.line); - LengthMod mod = LengthMod::FromId(e.id); - EXPECT_EQ(e.id, mod.id()); - EXPECT_EQ(e.name, mod.name()); + EXPECT_EQ(e.name, LengthModToString(e.mod)); } } TEST(ConversionCharTest, Names) { struct Expectation { - ConversionChar::Id id; + ConversionChar id; char name; }; // clang-format off @@ -57,12 +55,10 @@ TEST(ConversionCharTest, Names) { {ConversionChar::none, '\0'}, }; // clang-format on - EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), ConversionChar::kNumValues); for (auto e : kExpect) { SCOPED_TRACE(e.name); - ConversionChar v = ConversionChar::FromId(e.id); - EXPECT_EQ(e.id, v.id()); - EXPECT_EQ(e.name, v.Char()); + ConversionChar v = e.id; + EXPECT_EQ(e.name, FormatConversionCharToChar(v)); } } @@ -121,13 +117,12 @@ TEST_F(ConsumeUnboundConversionTest, BasicConversion) { EXPECT_FALSE(Run("dd")); // no excess allowed EXPECT_TRUE(Run("d")); - EXPECT_EQ('d', o.conv.Char()); + EXPECT_EQ('d', FormatConversionCharToChar(o.conv)); EXPECT_FALSE(o.width.is_from_arg()); EXPECT_LT(o.width.value(), 0); EXPECT_FALSE(o.precision.is_from_arg()); EXPECT_LT(o.precision.value(), 0); EXPECT_EQ(1, o.arg_position); - EXPECT_EQ(LengthMod::none, o.length_mod.id()); } TEST_F(ConsumeUnboundConversionTest, ArgPosition) { @@ -163,7 +158,7 @@ TEST_F(ConsumeUnboundConversionTest, ArgPosition) { TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) { EXPECT_TRUE(Run("14d")); - EXPECT_EQ('d', o.conv.Char()); + EXPECT_EQ('d', FormatConversionCharToChar(o.conv)); EXPECT_FALSE(o.width.is_from_arg()); EXPECT_EQ(14, o.width.value()); EXPECT_FALSE(o.precision.is_from_arg()); @@ -290,6 +285,29 @@ TEST_F(ConsumeUnboundConversionTest, BasicFlag) { } } +TEST_F(ConsumeUnboundConversionTest, LengthMod) { + EXPECT_TRUE(Run("d")); + EXPECT_EQ(LengthMod::none, o.length_mod); + EXPECT_TRUE(Run("hd")); + EXPECT_EQ(LengthMod::h, o.length_mod); + EXPECT_TRUE(Run("hhd")); + EXPECT_EQ(LengthMod::hh, o.length_mod); + EXPECT_TRUE(Run("ld")); + EXPECT_EQ(LengthMod::l, o.length_mod); + EXPECT_TRUE(Run("lld")); + EXPECT_EQ(LengthMod::ll, o.length_mod); + EXPECT_TRUE(Run("Lf")); + EXPECT_EQ(LengthMod::L, o.length_mod); + EXPECT_TRUE(Run("qf")); + EXPECT_EQ(LengthMod::q, o.length_mod); + EXPECT_TRUE(Run("jd")); + EXPECT_EQ(LengthMod::j, o.length_mod); + EXPECT_TRUE(Run("zd")); + EXPECT_EQ(LengthMod::z, o.length_mod); + EXPECT_TRUE(Run("td")); + EXPECT_EQ(LengthMod::t, o.length_mod); +} + struct SummarizeConsumer { std::string* out; explicit SummarizeConsumer(std::string* out) : out(out) {} @@ -310,7 +328,7 @@ struct SummarizeConsumer { if (conv.precision.is_from_arg()) { *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*"; } - *out += conv.conv.Char(); + *out += FormatConversionCharToChar(conv.conv); *out += "}"; return true; } @@ -390,5 +408,5 @@ TEST_F(ParsedFormatTest, ParsingFlagOrder) { } // namespace } // namespace str_format_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h index 02787dd1..31dbf672 100644 --- a/absl/strings/internal/str_join_internal.h +++ b/absl/strings/internal/str_join_internal.h @@ -43,7 +43,7 @@ #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // @@ -308,7 +308,7 @@ std::string JoinRange(const Range& range, absl::string_view separator) { } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_ diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h index 92a678e0..b54f6ebe 100644 --- a/absl/strings/internal/str_split_internal.h +++ b/absl/strings/internal/str_split_internal.h @@ -47,7 +47,7 @@ #endif // _GLIBCXX_DEBUG namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // This class is implicitly constructible from everything that absl::string_view @@ -449,7 +449,7 @@ class Splitter { }; } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_ diff --git a/absl/strings/internal/utf8.cc b/absl/strings/internal/utf8.cc index fe4a9beb..8fd8edc1 100644 --- a/absl/strings/internal/utf8.cc +++ b/absl/strings/internal/utf8.cc @@ -17,7 +17,7 @@ #include "absl/strings/internal/utf8.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) { @@ -49,5 +49,5 @@ size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) { } } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/utf8.h b/absl/strings/internal/utf8.h index b1bb954e..32fb1093 100644 --- a/absl/strings/internal/utf8.h +++ b/absl/strings/internal/utf8.h @@ -20,8 +20,10 @@ #include <cstddef> #include <cstdint> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // For Unicode code points 0 through 0x10FFFF, EncodeUTF8Char writes @@ -42,7 +44,7 @@ enum { kMaxEncodedUTF8Size = 4 }; size_t EncodeUTF8Char(char *buffer, char32_t utf8_char); } // namespace strings_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_UTF8_H_ diff --git a/absl/strings/match.cc b/absl/strings/match.cc index 01ed1f15..8127cb0c 100644 --- a/absl/strings/match.cc +++ b/absl/strings/match.cc @@ -17,7 +17,7 @@ #include "absl/strings/internal/memutil.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) { return (piece1.size() == piece2.size() && @@ -36,5 +36,5 @@ bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) { EqualsIgnoreCase(text.substr(text.size() - suffix.size()), suffix); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/match.h b/absl/strings/match.h index 15fdc0c8..90fca98a 100644 --- a/absl/strings/match.h +++ b/absl/strings/match.h @@ -20,7 +20,7 @@ // This file contains simple utilities for performing string matching checks. // All of these function parameters are specified as `absl::string_view`, // meaning that these functions can accept `std::string`, `absl::string_view` or -// nul-terminated C-style strings. +// NUL-terminated C-style strings. // // Examples: // std::string s = "foo"; @@ -38,7 +38,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // StrContains() // @@ -84,7 +84,7 @@ bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix); // case in the comparison. bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_MATCH_H_ diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc index 2bac4adc..68c26dd6 100644 --- a/absl/strings/numbers.cc +++ b/absl/strings/numbers.cc @@ -19,8 +19,8 @@ #include <algorithm> #include <cassert> -#include <cfloat> // for DBL_DIG and FLT_DIG -#include <cmath> // for HUGE_VAL +#include <cfloat> // for DBL_DIG and FLT_DIG +#include <cmath> // for HUGE_VAL #include <cstdint> #include <cstdio> #include <cstdlib> @@ -30,16 +30,18 @@ #include <memory> #include <utility> +#include "absl/base/attributes.h" #include "absl/base/internal/bits.h" #include "absl/base/internal/raw_logging.h" #include "absl/strings/ascii.h" #include "absl/strings/charconv.h" +#include "absl/strings/escaping.h" #include "absl/strings/internal/memutil.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN bool SimpleAtof(absl::string_view str, float* out) { *out = 0.0; @@ -93,43 +95,6 @@ bool SimpleAtod(absl::string_view str, double* out) { return true; } -namespace { - -// Writes a two-character representation of 'i' to 'buf'. 'i' must be in the -// range 0 <= i < 100, and buf must have space for two characters. Example: -// char buf[2]; -// PutTwoDigits(42, buf); -// // buf[0] == '4' -// // buf[1] == '2' -inline void PutTwoDigits(size_t i, char* buf) { - static const char two_ASCII_digits[100][2] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, - {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, - {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, - {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'}, - {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, - {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, - {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, - {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, - {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'}, - {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, - {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, - {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, - {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, - {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'}, - {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'}, - {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, - {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'} - }; - assert(i < 100); - memcpy(buf, two_ASCII_digits[i], 2); -} - -} // namespace - bool SimpleAtob(absl::string_view str, bool* out) { ABSL_RAW_CHECK(out != nullptr, "Output pointer must not be nullptr."); if (EqualsIgnoreCase(str, "true") || EqualsIgnoreCase(str, "t") || @@ -496,13 +461,13 @@ static ExpDigits SplitToSix(const double value) { int two_digits = dddddd / 10000; dddddd -= two_digits * 10000; - PutTwoDigits(two_digits, &exp_dig.digits[0]); + numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[0]); two_digits = dddddd / 100; dddddd -= two_digits * 100; - PutTwoDigits(two_digits, &exp_dig.digits[2]); + numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[2]); - PutTwoDigits(dddddd, &exp_dig.digits[4]); + numbers_internal::PutTwoDigits(dddddd, &exp_dig.digits[4]); return exp_dig; } @@ -755,8 +720,8 @@ inline bool safe_parse_sign_and_base(absl::string_view* text /*inout*/, // commonly used bases. template <typename IntType> struct LookupTables { - static const IntType kVmaxOverBase[]; - static const IntType kVminOverBase[]; + ABSL_CONST_INIT static const IntType kVmaxOverBase[]; + ABSL_CONST_INIT static const IntType kVminOverBase[]; }; // An array initializer macro for X/base where base in [0, 36]. @@ -771,6 +736,49 @@ struct LookupTables { X / 35, X / 36, \ } +// uint128& operator/=(uint128) is not constexpr, so hardcode the resulting +// array to avoid a static initializer. +template <> +const uint128 LookupTables<uint128>::kVmaxOverBase[] = { + 0, + 0, + MakeUint128(9223372036854775807u, 18446744073709551615u), + MakeUint128(6148914691236517205u, 6148914691236517205u), + MakeUint128(4611686018427387903u, 18446744073709551615u), + MakeUint128(3689348814741910323u, 3689348814741910323u), + MakeUint128(3074457345618258602u, 12297829382473034410u), + MakeUint128(2635249153387078802u, 5270498306774157604u), + MakeUint128(2305843009213693951u, 18446744073709551615u), + MakeUint128(2049638230412172401u, 14347467612885206812u), + MakeUint128(1844674407370955161u, 11068046444225730969u), + MakeUint128(1676976733973595601u, 8384883669867978007u), + MakeUint128(1537228672809129301u, 6148914691236517205u), + MakeUint128(1418980313362273201u, 4256940940086819603u), + MakeUint128(1317624576693539401u, 2635249153387078802u), + MakeUint128(1229782938247303441u, 1229782938247303441u), + MakeUint128(1152921504606846975u, 18446744073709551615u), + MakeUint128(1085102592571150095u, 1085102592571150095u), + MakeUint128(1024819115206086200u, 16397105843297379214u), + MakeUint128(970881267037344821u, 16504981539634861972u), + MakeUint128(922337203685477580u, 14757395258967641292u), + MakeUint128(878416384462359600u, 14054662151397753612u), + MakeUint128(838488366986797800u, 13415813871788764811u), + MakeUint128(802032351030850070u, 4812194106185100421u), + MakeUint128(768614336404564650u, 12297829382473034410u), + MakeUint128(737869762948382064u, 11805916207174113034u), + MakeUint128(709490156681136600u, 11351842506898185609u), + MakeUint128(683212743470724133u, 17080318586768103348u), + MakeUint128(658812288346769700u, 10540996613548315209u), + MakeUint128(636094623231363848u, 15266270957552732371u), + MakeUint128(614891469123651720u, 9838263505978427528u), + MakeUint128(595056260442243600u, 9520900167075897608u), + MakeUint128(576460752303423487u, 18446744073709551615u), + MakeUint128(558992244657865200u, 8943875914525843207u), + MakeUint128(542551296285575047u, 9765923333140350855u), + MakeUint128(527049830677415760u, 8432797290838652167u), + MakeUint128(512409557603043100u, 8198552921648689607u), +}; + template <typename IntType> const IntType LookupTables<IntType>::kVmaxOverBase[] = X_OVER_BASE_INITIALIZER(std::numeric_limits<IntType>::max()); @@ -790,6 +798,8 @@ inline bool safe_parse_positive_int(absl::string_view text, int base, assert(base >= 0); assert(vmax >= static_cast<IntType>(base)); const IntType vmax_over_base = LookupTables<IntType>::kVmaxOverBase[base]; + assert(base < 2 || + std::numeric_limits<IntType>::max() / base == vmax_over_base); const char* start = text.data(); const char* end = start + text.size(); // loop over digits @@ -823,6 +833,8 @@ inline bool safe_parse_negative_int(absl::string_view text, int base, assert(vmin < 0); assert(vmin <= 0 - base); IntType vmin_over_base = LookupTables<IntType>::kVminOverBase[base]; + assert(base < 2 || + std::numeric_limits<IntType>::min() / base == vmin_over_base); // 2003 c++ standard [expr.mul] // "... the sign of the remainder is implementation-defined." // Although (vmin/base)*base + vmin%base is always vmin. @@ -886,6 +898,48 @@ inline bool safe_uint_internal(absl::string_view text, IntType* value_p, } // anonymous namespace namespace numbers_internal { + +// Digit conversion. +ABSL_CONST_INIT ABSL_DLL const char kHexChar[] = + "0123456789abcdef"; + +ABSL_CONST_INIT ABSL_DLL const char kHexTable[513] = + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + +ABSL_CONST_INIT ABSL_DLL const char two_ASCII_digits[100][2] = { + {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, + {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, + {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, + {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, + {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, + {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, + {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, + {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, + {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, + {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, + {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, + {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, + {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, + {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, + {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, + {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, + {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; + bool safe_strto32_base(absl::string_view text, int32_t* value, int base) { return safe_int_internal<int32_t>(text, value, base); } @@ -901,7 +955,11 @@ bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base) { bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) { return safe_uint_internal<uint64_t>(text, value, base); } -} // namespace numbers_internal -} // inline namespace lts_2019_08_08 +bool safe_strtou128_base(absl::string_view text, uint128* value, int base) { + return safe_uint_internal<absl::uint128>(text, value, base); +} + +} // namespace numbers_internal +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index e9878016..d872cca5 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -24,6 +24,10 @@ #ifndef ABSL_STRINGS_NUMBERS_H_ #define ABSL_STRINGS_NUMBERS_H_ +#ifdef __SSE4_2__ +#include <x86intrin.h> +#endif + #include <cstddef> #include <cstdlib> #include <cstring> @@ -32,39 +36,54 @@ #include <string> #include <type_traits> +#include "absl/base/config.h" +#include "absl/base/internal/bits.h" +#ifdef __SSE4_2__ +// TODO(jorg): Remove this when we figure out the right way +// to swap bytes on SSE 4.2 that works with the compilers +// we claim to support. Also, add tests for the compiler +// that doesn't support the Intel _bswap64 intrinsic but +// does support all the SSE 4.2 intrinsics +#include "absl/base/internal/endian.h" +#endif #include "absl/base/macros.h" #include "absl/base/port.h" #include "absl/numeric/int128.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // SimpleAtoi() // -// Converts the given string into an integer value, returning `true` if -// successful. The string must reflect a base-10 integer (optionally followed or -// preceded by ASCII whitespace) whose value falls within the range of the -// integer type. If any errors are encountered, this function returns `false`, -// leaving `out` in an unspecified state. +// Converts the given string (optionally followed or preceded by ASCII +// whitespace) into an integer value, returning `true` if successful. The string +// must reflect a base-10 integer whose value falls within the range of the +// integer type (optionally preceded by a `+` or `-`). If any errors are +// encountered, this function returns `false`, leaving `out` in an unspecified +// state. template <typename int_type> ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out); // SimpleAtof() // // Converts the given string (optionally followed or preceded by ASCII -// whitespace) into a float, which may be rounded on overflow or underflow. +// whitespace) into a float, which may be rounded on overflow or underflow, +// returning `true` if successful. // See https://en.cppreference.com/w/c/string/byte/strtof for details about the -// allowed formats for `str`. If any errors are encountered, this function +// allowed formats for `str`, except SimpleAtof() is locale-independent and will +// always use the "C" locale. If any errors are encountered, this function // returns `false`, leaving `out` in an unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out); // SimpleAtod() // // Converts the given string (optionally followed or preceded by ASCII -// whitespace) into a double, which may be rounded on overflow or underflow. +// whitespace) into a double, which may be rounded on overflow or underflow, +// returning `true` if successful. // See https://en.cppreference.com/w/c/string/byte/strtof for details about the -// allowed formats for `str`. If any errors are encountered, this function +// allowed formats for `str`, except SimpleAtod is locale-independent and will +// always use the "C" locale. If any errors are encountered, this function // returns `false`, leaving `out` in an unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out); @@ -78,20 +97,40 @@ ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out); // unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // End of public API. Implementation details follow. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace numbers_internal { +// Digit conversion. +ABSL_DLL extern const char kHexChar[17]; // 0123456789abcdef +ABSL_DLL extern const char + kHexTable[513]; // 000102030405060708090a0b0c0d0e0f1011... +ABSL_DLL extern const char + two_ASCII_digits[100][2]; // 00, 01, 02, 03... + +// Writes a two-character representation of 'i' to 'buf'. 'i' must be in the +// range 0 <= i < 100, and buf must have space for two characters. Example: +// char buf[2]; +// PutTwoDigits(42, buf); +// // buf[0] == '4' +// // buf[1] == '2' +inline void PutTwoDigits(size_t i, char* buf) { + assert(i < 100); + memcpy(buf, two_ASCII_digits[i], 2); +} + // safe_strto?() functions for implementing SimpleAtoi() bool safe_strto32_base(absl::string_view text, int32_t* value, int base); bool safe_strto64_base(absl::string_view text, int64_t* value, int base); bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base); bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base); +bool safe_strtou128_base(absl::string_view text, absl::uint128* value, + int base); static const int kFastToBufferSize = 32; static const int kSixDigitsToBufferSize = 16; @@ -173,6 +212,35 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, return parsed; } +// FastHexToBufferZeroPad16() +// +// Outputs `val` into `out` as if by `snprintf(out, 17, "%016x", val)` but +// without the terminating null character. Thus `out` must be of length >= 16. +// Returns the number of non-pad digits of the output (it can never be zero +// since 0 has one digit). +inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { +#ifdef __SSE4_2__ + uint64_t be = absl::big_endian::FromHost64(val); + const auto kNibbleMask = _mm_set1_epi8(0xf); + const auto kHexDigits = _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); + auto v = _mm_loadl_epi64(reinterpret_cast<__m128i*>(&be)); // load lo dword + auto v4 = _mm_srli_epi64(v, 4); // shift 4 right + auto il = _mm_unpacklo_epi8(v4, v); // interleave bytes + auto m = _mm_and_si128(il, kNibbleMask); // mask out nibbles + auto hexchars = _mm_shuffle_epi8(kHexDigits, m); // hex chars + _mm_storeu_si128(reinterpret_cast<__m128i*>(out), hexchars); +#else + for (int i = 0; i < 8; ++i) { + auto byte = (val >> (56 - 8 * i)) & 0xFF; + auto* hex = &absl::numbers_internal::kHexTable[byte * 2]; + std::memcpy(out + 2 * i, hex, 2); + } +#endif + // | 0x1 so that even 0 has 1 digit. + return 16 - absl::base_internal::CountLeadingZeros64(val | 0x1) / 4; +} + } // namespace numbers_internal // SimpleAtoi() @@ -187,7 +255,12 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) { return numbers_internal::safe_strtoi_base(str, out, 10); } -} // inline namespace lts_2019_08_08 +ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, + absl::uint128* out) { + return numbers_internal::safe_strtou128_base(str, out, 10); +} + +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_NUMBERS_H_ diff --git a/absl/strings/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc index 54dbedd3..6e79b3e8 100644 --- a/absl/strings/numbers_benchmark.cc +++ b/absl/strings/numbers_benchmark.cc @@ -20,6 +20,8 @@ #include "benchmark/benchmark.h" #include "absl/base/internal/raw_logging.h" +#include "absl/random/distributions.h" +#include "absl/random/random.h" #include "absl/strings/numbers.h" namespace { @@ -260,4 +262,25 @@ BENCHMARK_TEMPLATE(BM_SimpleAtod, std::string) ->ArgPair(10, 4) ->ArgPair(10, 8); +void BM_FastHexToBufferZeroPad16(benchmark::State& state) { + absl::BitGen rng; + std::vector<uint64_t> nums; + nums.resize(1000); + auto min = std::numeric_limits<uint64_t>::min(); + auto max = std::numeric_limits<uint64_t>::max(); + for (auto& num : nums) { + num = absl::LogUniform(rng, min, max); + } + + char buf[16]; + while (state.KeepRunningBatch(nums.size())) { + for (auto num : nums) { + auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(num, buf); + benchmark::DoNotOptimize(digits); + benchmark::DoNotOptimize(buf); + } + } +} +BENCHMARK(BM_FastHexToBufferZeroPad16); + } // namespace diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index 77d7e783..68229b15 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -17,6 +17,7 @@ #include "absl/strings/numbers.h" #include <sys/types.h> + #include <cfenv> // NOLINT(build/c++11) #include <cinttypes> #include <climits> @@ -36,10 +37,11 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" -#include "absl/strings/str_cat.h" - +#include "absl/random/distributions.h" +#include "absl/random/random.h" #include "absl/strings/internal/numbers_test_common.h" #include "absl/strings/internal/pow10_helper.h" +#include "absl/strings/str_cat.h" namespace { @@ -204,6 +206,9 @@ void CheckHex64(uint64_t v) { std::string actual = absl::StrCat(absl::Hex(v, absl::kZeroPad16)); snprintf(expected, sizeof(expected), "%016" PRIx64, static_cast<uint64_t>(v)); EXPECT_EQ(expected, actual) << " Input " << v; + actual = absl::StrCat(absl::Hex(v, absl::kSpacePad16)); + snprintf(expected, sizeof(expected), "%16" PRIx64, static_cast<uint64_t>(v)); + EXPECT_EQ(expected, actual) << " Input " << v; } TEST(Numbers, TestFastPrints) { @@ -244,7 +249,9 @@ TEST(Numbers, TestFastPrints) { template <typename int_type, typename in_val_type> void VerifySimpleAtoiGood(in_val_type in_value, int_type exp_value) { - std::string s = absl::StrCat(in_value); + std::string s; + // uint128 can be streamed but not StrCat'd + absl::strings_internal::OStringStream(&s) << in_value; int_type x = static_cast<int_type>(~exp_value); EXPECT_TRUE(SimpleAtoi(s, &x)) << "in_value=" << in_value << " s=" << s << " x=" << x; @@ -320,6 +327,25 @@ TEST(NumbersTest, Atoi) { VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max()); + // SimpleAtoi(absl::string_view, absl::uint128) + VerifySimpleAtoiGood<absl::uint128>(0, 0); + VerifySimpleAtoiGood<absl::uint128>(42, 42); + VerifySimpleAtoiBad<absl::uint128>(-42); + + VerifySimpleAtoiBad<absl::uint128>(std::numeric_limits<int32_t>::min()); + VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<int32_t>::max(), + std::numeric_limits<int32_t>::max()); + VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint32_t>::max()); + VerifySimpleAtoiBad<absl::uint128>(std::numeric_limits<int64_t>::min()); + VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<int64_t>::max(), + std::numeric_limits<int64_t>::max()); + VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<uint64_t>::max(), + std::numeric_limits<uint64_t>::max()); + VerifySimpleAtoiGood<absl::uint128>( + std::numeric_limits<absl::uint128>::max(), + std::numeric_limits<absl::uint128>::max()); + // Some other types VerifySimpleAtoiGood<int>(-42, -42); VerifySimpleAtoiGood<int32_t>(-42, -42); @@ -652,6 +678,46 @@ TEST(stringtest, safe_strtou32_random) { TEST(stringtest, safe_strtou64_random) { test_random_integer_parse_base<uint64_t>(&safe_strtou64_base); } +TEST(stringtest, safe_strtou128_random) { + // random number generators don't work for uint128, and + // uint128 can be streamed but not StrCat'd, so this code must be custom + // implemented for uint128, but is generally the same as what's above. + // test_random_integer_parse_base<absl::uint128>( + // &absl::numbers_internal::safe_strtou128_base); + using RandomEngine = std::minstd_rand0; + using IntType = absl::uint128; + constexpr auto parse_func = &absl::numbers_internal::safe_strtou128_base; + + std::random_device rd; + RandomEngine rng(rd()); + std::uniform_int_distribution<uint64_t> random_uint64( + std::numeric_limits<uint64_t>::min()); + std::uniform_int_distribution<int> random_base(2, 35); + + for (size_t i = 0; i < kNumRandomTests; i++) { + IntType value = random_uint64(rng); + value = (value << 64) + random_uint64(rng); + int base = random_base(rng); + std::string str_value; + EXPECT_TRUE(Itoa<IntType>(value, base, &str_value)); + IntType parsed_value; + + // Test successful parse + EXPECT_TRUE(parse_func(str_value, &parsed_value, base)); + EXPECT_EQ(parsed_value, value); + + // Test overflow + std::string s; + absl::strings_internal::OStringStream(&s) + << std::numeric_limits<IntType>::max() << value; + EXPECT_FALSE(parse_func(s, &parsed_value, base)); + + // Test underflow + s.clear(); + absl::strings_internal::OStringStream(&s) << "-" << value; + EXPECT_FALSE(parse_func(s, &parsed_value, base)); + } +} TEST(stringtest, safe_strtou32_base) { for (int i = 0; strtouint32_test_cases()[i].str != nullptr; ++i) { @@ -713,11 +779,11 @@ TEST(stringtest, safe_strtou64_base_length_delimited) { } } -// feenableexcept() and fedisableexcept() are missing on macOS, MSVC, -// and WebAssembly. -#if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__) -#define ABSL_MISSING_FEENABLEEXCEPT 1 -#define ABSL_MISSING_FEDISABLEEXCEPT 1 +// feenableexcept() and fedisableexcept() are extensions supported by some libc +// implementations. +#if defined(__GLIBC__) || defined(__BIONIC__) +#define ABSL_HAVE_FEENABLEEXCEPT 1 +#define ABSL_HAVE_FEDISABLEEXCEPT 1 #endif class SimpleDtoaTest : public testing::Test { @@ -725,7 +791,7 @@ class SimpleDtoaTest : public testing::Test { void SetUp() override { // Store the current floating point env & clear away any pending exceptions. feholdexcept(&fp_env_); -#ifndef ABSL_MISSING_FEENABLEEXCEPT +#ifdef ABSL_HAVE_FEENABLEEXCEPT // Turn on floating point exceptions. feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif @@ -735,7 +801,7 @@ class SimpleDtoaTest : public testing::Test { // Restore the floating point environment to the original state. // In theory fedisableexcept is unnecessary; fesetenv will also do it. // In practice, our toolchains have subtle bugs. -#ifndef ABSL_MISSING_FEDISABLEEXCEPT +#ifdef ABSL_HAVE_FEDISABLEEXCEPT fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif fesetenv(&fp_env_); @@ -1184,4 +1250,28 @@ TEST(StrToUint64Base, PrefixOnly) { } } +void TestFastHexToBufferZeroPad16(uint64_t v) { + char buf[16]; + auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(v, buf); + absl::string_view res(buf, 16); + char buf2[17]; + snprintf(buf2, sizeof(buf2), "%016" PRIx64, v); + EXPECT_EQ(res, buf2) << v; + size_t expected_digits = snprintf(buf2, sizeof(buf2), "%" PRIx64, v); + EXPECT_EQ(digits, expected_digits) << v; +} + +TEST(FastHexToBufferZeroPad16, Smoke) { + TestFastHexToBufferZeroPad16(std::numeric_limits<uint64_t>::min()); + TestFastHexToBufferZeroPad16(std::numeric_limits<uint64_t>::max()); + TestFastHexToBufferZeroPad16(std::numeric_limits<int64_t>::min()); + TestFastHexToBufferZeroPad16(std::numeric_limits<int64_t>::max()); + absl::BitGen rng; + for (int i = 0; i < 100000; ++i) { + TestFastHexToBufferZeroPad16( + absl::LogUniform(rng, std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max())); + } +} + } // namespace diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index 73b9e0ba..d9afe2f3 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc @@ -15,35 +15,34 @@ #include "absl/strings/str_cat.h" #include <assert.h> + #include <algorithm> #include <cstdint> #include <cstring> #include "absl/strings/ascii.h" #include "absl/strings/internal/resize_uninitialized.h" +#include "absl/strings/numbers.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN AlphaNum::AlphaNum(Hex hex) { + static_assert(numbers_internal::kFastToBufferSize >= 32, + "This function only works when output buffer >= 32 bytes long"); char* const end = &digits_[numbers_internal::kFastToBufferSize]; - char* writer = end; - uint64_t value = hex.value; - static const char hexdigits[] = "0123456789abcdef"; - do { - *--writer = hexdigits[value & 0xF]; - value >>= 4; - } while (value != 0); - - char* beg; - if (end - writer < hex.width) { - beg = end - hex.width; - std::fill_n(beg, writer - beg, hex.fill); + auto real_width = + absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16); + if (real_width >= hex.width) { + piece_ = absl::string_view(end - real_width, real_width); } else { - beg = writer; + // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and + // max pad width can be up to 20. + std::memset(end - 32, hex.fill, 16); + // Patch up everything else up to the real_width. + std::memset(end - real_width - 16, hex.fill, 16); + piece_ = absl::string_view(end - hex.width, hex.width); } - - piece_ = absl::string_view(beg, end - beg); } AlphaNum::AlphaNum(Dec dec) { @@ -100,7 +99,7 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b) { std::string result; absl::strings_internal::STLStringResizeUninitialized(&result, a.size() + b.size()); - char* const begin = &*result.begin(); + char* const begin = &result[0]; char* out = begin; out = Append(out, a); out = Append(out, b); @@ -112,7 +111,7 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) { std::string result; strings_internal::STLStringResizeUninitialized( &result, a.size() + b.size() + c.size()); - char* const begin = &*result.begin(); + char* const begin = &result[0]; char* out = begin; out = Append(out, a); out = Append(out, b); @@ -126,7 +125,7 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, std::string result; strings_internal::STLStringResizeUninitialized( &result, a.size() + b.size() + c.size() + d.size()); - char* const begin = &*result.begin(); + char* const begin = &result[0]; char* out = begin; out = Append(out, a); out = Append(out, b); @@ -145,7 +144,7 @@ std::string CatPieces(std::initializer_list<absl::string_view> pieces) { for (const absl::string_view piece : pieces) total_size += piece.size(); strings_internal::STLStringResizeUninitialized(&result, total_size); - char* const begin = &*result.begin(); + char* const begin = &result[0]; char* out = begin; for (const absl::string_view piece : pieces) { const size_t this_size = piece.size(); @@ -177,7 +176,7 @@ void AppendPieces(std::string* dest, } strings_internal::STLStringResizeUninitialized(dest, total_size); - char* const begin = &*dest->begin(); + char* const begin = &(*dest)[0]; char* out = begin + old_size; for (const absl::string_view piece : pieces) { const size_t this_size = piece.size(); @@ -202,7 +201,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) { std::string::size_type old_size = dest->size(); strings_internal::STLStringResizeUninitialized( dest, old_size + a.size() + b.size()); - char* const begin = &*dest->begin(); + char* const begin = &(*dest)[0]; char* out = begin + old_size; out = Append(out, a); out = Append(out, b); @@ -217,7 +216,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, std::string::size_type old_size = dest->size(); strings_internal::STLStringResizeUninitialized( dest, old_size + a.size() + b.size() + c.size()); - char* const begin = &*dest->begin(); + char* const begin = &(*dest)[0]; char* out = begin + old_size; out = Append(out, a); out = Append(out, b); @@ -234,7 +233,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, std::string::size_type old_size = dest->size(); strings_internal::STLStringResizeUninitialized( dest, old_size + a.size() + b.size() + c.size() + d.size()); - char* const begin = &*dest->begin(); + char* const begin = &(*dest)[0]; char* out = begin + old_size; out = Append(out, a); out = Append(out, b); @@ -243,5 +242,5 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, assert(out == begin + dest->size()); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index c2700475..292fa235 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -64,7 +64,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { // AlphaNumBuffer allows a way to pass a string to StrCat without having to do @@ -291,7 +291,8 @@ class AlphaNum { // StrCat() // ----------------------------------------------------------------------------- // -// Merges given strings or numbers, using no delimiter(s). +// Merges given strings or numbers, using no delimiter(s), returning the merged +// result as a string. // // `StrCat()` is designed to be the fastest possible way to construct a string // out of a mix of raw C strings, string_views, strings, bool values, @@ -401,7 +402,7 @@ SixDigits(double d) { return result; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_STR_CAT_H_ diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc index 29db9c02..be39880b 100644 --- a/absl/strings/str_cat_test.cc +++ b/absl/strings/str_cat_test.cc @@ -195,6 +195,21 @@ TEST(StrCat, Basics) { EXPECT_EQ(result, "12333444455555666666777777788888888999999999"); } +TEST(StrCat, CornerCases) { + std::string result; + + result = absl::StrCat(""); // NOLINT + EXPECT_EQ(result, ""); + result = absl::StrCat("", ""); + EXPECT_EQ(result, ""); + result = absl::StrCat("", "", ""); + EXPECT_EQ(result, ""); + result = absl::StrCat("", "", "", ""); + EXPECT_EQ(result, ""); + result = absl::StrCat("", "", "", "", ""); + EXPECT_EQ(result, ""); +} + // A minimal allocator that uses malloc(). template <typename T> struct Mallocator { @@ -433,10 +448,34 @@ TEST(StrAppend, Death) { } #endif // GTEST_HAS_DEATH_TEST -TEST(StrAppend, EmptyString) { - std::string s = ""; - absl::StrAppend(&s, s); - EXPECT_EQ(s, ""); +TEST(StrAppend, CornerCases) { + std::string result; + absl::StrAppend(&result, ""); + EXPECT_EQ(result, ""); + absl::StrAppend(&result, "", ""); + EXPECT_EQ(result, ""); + absl::StrAppend(&result, "", "", ""); + EXPECT_EQ(result, ""); + absl::StrAppend(&result, "", "", "", ""); + EXPECT_EQ(result, ""); + absl::StrAppend(&result, "", "", "", "", ""); + EXPECT_EQ(result, ""); +} + +TEST(StrAppend, CornerCasesNonEmptyAppend) { + for (std::string result : {"hello", "a std::string too long to fit in the SSO"}) { + const std::string expected = result; + absl::StrAppend(&result, ""); + EXPECT_EQ(result, expected); + absl::StrAppend(&result, "", ""); + EXPECT_EQ(result, expected); + absl::StrAppend(&result, "", "", ""); + EXPECT_EQ(result, expected); + absl::StrAppend(&result, "", "", "", ""); + EXPECT_EQ(result, expected); + absl::StrAppend(&result, "", "", "", "", ""); + EXPECT_EQ(result, expected); + } } template <typename IntType> diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h index b4d1b7bd..2f9b4b27 100644 --- a/absl/strings/str_format.h +++ b/absl/strings/str_format.h @@ -82,7 +82,7 @@ #include "absl/strings/internal/str_format/parser.h" // IWYU pragma: export namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // UntypedFormatSpec // @@ -401,6 +401,12 @@ int FPrintF(std::FILE* output, const FormatSpec<Args...>& format, // This function is functionally equivalent to `std::snprintf()` (and // type-safe); prefer `absl::SNPrintF()` over `std::snprintf()`. // +// In particular, a successful call to `absl::SNPrintF()` writes at most `size` +// bytes of the formatted output to `output`, including a NUL-terminator, and +// returns the number of bytes that would have been written if truncation did +// not occur. In the event of an error, a negative value is returned and `errno` +// is set. +// // Example: // // std::string_view s = "Ulaanbaatar"; @@ -525,7 +531,7 @@ ABSL_MUST_USE_RESULT inline bool FormatUntyped( str_format_internal::UntypedFormatSpecImpl::Extract(format), args); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_STR_FORMAT_H_ diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index bb0f58bf..acbdbf4a 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc @@ -10,7 +10,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { using str_format_internal::FormatArgImpl; @@ -450,7 +450,7 @@ struct SummarizeConsumer { if (conv.precision.is_from_arg()) { *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*"; } - *out += conv.conv.Char(); + *out += FormatConversionCharToChar(conv.conv); *out += "}"; return true; } @@ -623,7 +623,7 @@ TEST_F(FormatWrapperTest, ParsedFormat) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // Some codegen thunks that we can use to easily dump the generated assembly for diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h index c6c0c98a..ae5731a4 100644 --- a/absl/strings/str_join.h +++ b/absl/strings/str_join.h @@ -60,7 +60,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // Concept: Formatter @@ -287,7 +287,7 @@ std::string StrJoin(const std::tuple<T...>& value, return strings_internal::JoinAlgorithm(value, separator, AlphaNumFormatter()); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_STR_JOIN_H_ diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc index 3bd8bae1..2bd5fa98 100644 --- a/absl/strings/str_replace.cc +++ b/absl/strings/str_replace.cc @@ -17,7 +17,7 @@ #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace strings_internal { using FixedMapping = @@ -78,5 +78,5 @@ int StrReplaceAll(strings_internal::FixedMapping replacements, return StrReplaceAll<strings_internal::FixedMapping>(replacements, target); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h index cb995456..273c7077 100644 --- a/absl/strings/str_replace.h +++ b/absl/strings/str_replace.h @@ -46,7 +46,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // StrReplaceAll() // @@ -213,7 +213,7 @@ int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) { return substitutions; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_STR_REPLACE_H_ diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc index f1e03717..d0f86669 100644 --- a/absl/strings/str_split.cc +++ b/absl/strings/str_split.cc @@ -27,7 +27,7 @@ #include "absl/strings/ascii.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { @@ -135,5 +135,5 @@ absl::string_view ByLength::Find(absl::string_view text, return absl::string_view(substr.data() + length_, 0); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h index 463ca008..a79cd4a0 100644 --- a/absl/strings/str_split.h +++ b/absl/strings/str_split.h @@ -49,7 +49,7 @@ #include "absl/strings/strip.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN //------------------------------------------------------------------------------ // Delimiters @@ -507,7 +507,7 @@ StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d, std::move(text), DelimiterType(d), std::move(p)); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_STR_SPLIT_H_ diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc index 9d241e51..c5f5de93 100644 --- a/absl/strings/string_view.cc +++ b/absl/strings/string_view.cc @@ -14,7 +14,7 @@ #include "absl/strings/string_view.h" -#ifndef ABSL_HAVE_STD_STRING_VIEW +#ifndef ABSL_USES_STD_STRING_VIEW #include <algorithm> #include <climits> @@ -24,7 +24,7 @@ #include "absl/strings/internal/memutil.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { void WritePadding(std::ostream& o, size_t pad) { @@ -229,7 +229,7 @@ constexpr string_view::size_type string_view::npos; ABSL_STRING_VIEW_SELECTANY constexpr string_view::size_type string_view::kMaxSize; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_HAVE_STD_STRING_VIEW +#endif // ABSL_USES_STD_STRING_VIEW diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index 3a0a4609..1861ea62 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -28,20 +28,6 @@ #define ABSL_STRINGS_STRING_VIEW_H_ #include <algorithm> -#include "absl/base/config.h" - -#ifdef ABSL_HAVE_STD_STRING_VIEW - -#include <string_view> // IWYU pragma: export - -namespace absl { -inline namespace lts_2019_08_08 { -using std::string_view; -} // inline namespace lts_2019_08_08 -} // namespace absl - -#else // ABSL_HAVE_STD_STRING_VIEW - #include <cassert> #include <cstddef> #include <cstring> @@ -50,13 +36,33 @@ using std::string_view; #include <limits> #include <string> +#include "absl/base/config.h" #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/base/port.h" +#ifdef ABSL_USES_STD_STRING_VIEW + +#include <string_view> // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN +using std::string_view; +ABSL_NAMESPACE_END +} // namespace absl + +#else // ABSL_USES_STD_STRING_VIEW + +#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp +#else // ABSL_HAVE_BUILTIN(__builtin_memcmp) +#define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp +#endif // ABSL_HAVE_BUILTIN(__builtin_memcmp) + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // absl::string_view // @@ -105,17 +111,17 @@ inline namespace lts_2019_08_08 { // example, when splitting a string, `std::vector<absl::string_view>` is a // natural data type for the output. // -// When constructed from a source which is nul-terminated, the `string_view` -// itself will not include the nul-terminator unless a specific size (including -// the nul) is passed to the constructor. As a result, common idioms that work -// on nul-terminated strings do not work on `string_view` objects. If you write +// When constructed from a source which is NUL-terminated, the `string_view` +// itself will not include the NUL-terminator unless a specific size (including +// the NUL) is passed to the constructor. As a result, common idioms that work +// on NUL-terminated strings do not work on `string_view` objects. If you write // code that scans a `string_view`, you must check its length rather than test // for nul, for example. Note, however, that nuls may still be embedded within // a `string_view` explicitly. // // You may create a null `string_view` in two ways: // -// absl::string_view sv(); +// absl::string_view sv; // absl::string_view sv(nullptr, 0); // // For the above, `sv.data() == nullptr`, `sv.length() == 0`, and @@ -171,9 +177,11 @@ class string_view { string_view( // NOLINT(runtime/explicit) const std::basic_string<char, std::char_traits<char>, Allocator>& str) noexcept - : ptr_(str.data()), length_(CheckLengthInternal(str.size())) {} + // This is implemented in terms of `string_view(p, n)` so `str.size()` + // doesn't need to be reevaluated after `ptr_` is set. + : string_view(str.data(), str.size()) {} - // Implicit constructor of a `string_view` from nul-terminated `str`. When + // Implicit constructor of a `string_view` from NUL-terminated `str`. When // accepting possibly null strings, use `absl::NullSafeStringView(str)` // instead (see below). constexpr string_view(const char* str) // NOLINT(runtime/explicit) @@ -272,26 +280,45 @@ class string_view { // string_view::operator[] // - // Returns the ith element of an `string_view` using the array operator. + // Returns the ith element of the `string_view` using the array operator. // Note that this operator does not perform any bounds checking. - constexpr const_reference operator[](size_type i) const { return ptr_[i]; } + constexpr const_reference operator[](size_type i) const { + return ABSL_ASSERT(i < size()), ptr_[i]; + } + + // string_view::at() + // + // Returns the ith element of the `string_view`. Bounds checking is performed, + // and an exception of type `std::out_of_range` will be thrown on invalid + // access. + constexpr const_reference at(size_type i) const { + return ABSL_PREDICT_TRUE(i < size()) + ? ptr_[i] + : ((void)base_internal::ThrowStdOutOfRange( + "absl::string_view::at"), + ptr_[i]); + } // string_view::front() // // Returns the first element of a `string_view`. - constexpr const_reference front() const { return ptr_[0]; } + constexpr const_reference front() const { + return ABSL_ASSERT(!empty()), ptr_[0]; + } // string_view::back() // // Returns the last element of a `string_view`. - constexpr const_reference back() const { return ptr_[size() - 1]; } + constexpr const_reference back() const { + return ABSL_ASSERT(!empty()), ptr_[size() - 1]; + } // string_view::data() // // Returns a pointer to the underlying character array (which is of course // stored elsewhere). Note that `string_view::data()` may contain embedded nul - // characters, but the returned buffer may or may not be nul-terminated; - // therefore, do not pass `data()` to a routine that expects a nul-terminated + // characters, but the returned buffer may or may not be NUL-terminated; + // therefore, do not pass `data()` to a routine that expects a NUL-terminated // std::string. constexpr const_pointer data() const noexcept { return ptr_; } @@ -345,7 +372,7 @@ class string_view { size_type rlen = (std::min)(length_ - pos, n); if (rlen > 0) { const char* start = ptr_ + pos; - std::copy(start, start + rlen, buf); + traits_type::copy(buf, start, rlen); } return rlen; } @@ -370,16 +397,12 @@ class string_view { // view. Note that in the case of data equality, a further comparison is made // on the respective sizes of the two `string_view`s to determine which is // smaller, equal, or greater. - int compare(string_view x) const noexcept { - auto min_length = (std::min)(length_, x.length_); - if (min_length > 0) { - int r = memcmp(ptr_, x.ptr_, min_length); - if (r < 0) return -1; - if (r > 0) return 1; - } - if (length_ < x.length_) return -1; - if (length_ > x.length_) return 1; - return 0; + constexpr int compare(string_view x) const noexcept { + return CompareImpl(length_, x.length_, + Min(length_, x.length_) == 0 + ? 0 + : ABSL_INTERNAL_STRING_VIEW_MEMCMP( + ptr_, x.ptr_, Min(length_, x.length_))); } // Overload of `string_view::compare()` for comparing a substring of the @@ -496,7 +519,7 @@ class string_view { (std::numeric_limits<difference_type>::max)(); static constexpr size_type CheckLengthInternal(size_type len) { - return ABSL_ASSERT(len <= kMaxSize), len; + return (void)ABSL_ASSERT(len <= kMaxSize), len; } static constexpr size_type StrlenInternal(const char* str) { @@ -517,6 +540,17 @@ class string_view { #endif } + static constexpr size_t Min(size_type length_a, size_type length_b) { + return length_a < length_b ? length_a : length_b; + } + + static constexpr int CompareImpl(size_type length_a, size_type length_b, + int compare_result) { + return compare_result == 0 ? static_cast<int>(length_a > length_b) - + static_cast<int>(length_a < length_b) + : (compare_result < 0 ? -1 : 1); + } + const char* ptr_; size_type length_; }; @@ -524,46 +558,44 @@ class string_view { // This large function is defined inline so that in a fairly common case where // one of the arguments is a literal, the compiler can elide a lot of the // following comparisons. -inline bool operator==(string_view x, string_view y) noexcept { - auto len = x.size(); - if (len != y.size()) { - return false; - } - - return x.data() == y.data() || len <= 0 || - memcmp(x.data(), y.data(), len) == 0; +constexpr bool operator==(string_view x, string_view y) noexcept { + return x.size() == y.size() && + (x.empty() || + ABSL_INTERNAL_STRING_VIEW_MEMCMP(x.data(), y.data(), x.size()) == 0); } -inline bool operator!=(string_view x, string_view y) noexcept { +constexpr bool operator!=(string_view x, string_view y) noexcept { return !(x == y); } -inline bool operator<(string_view x, string_view y) noexcept { - auto min_size = (std::min)(x.size(), y.size()); - const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size); - return (r < 0) || (r == 0 && x.size() < y.size()); +constexpr bool operator<(string_view x, string_view y) noexcept { + return x.compare(y) < 0; } -inline bool operator>(string_view x, string_view y) noexcept { return y < x; } +constexpr bool operator>(string_view x, string_view y) noexcept { + return y < x; +} -inline bool operator<=(string_view x, string_view y) noexcept { +constexpr bool operator<=(string_view x, string_view y) noexcept { return !(y < x); } -inline bool operator>=(string_view x, string_view y) noexcept { +constexpr bool operator>=(string_view x, string_view y) noexcept { return !(x < y); } // IO Insertion Operator std::ostream& operator<<(std::ostream& o, string_view piece); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_HAVE_STD_STRING_VIEW +#undef ABSL_INTERNAL_STRING_VIEW_MEMCMP + +#endif // ABSL_USES_STD_STRING_VIEW namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ClippedSubstr() // @@ -580,11 +612,11 @@ inline string_view ClippedSubstr(string_view s, size_t pos, // Creates an `absl::string_view` from a pointer `p` even if it's null-valued. // This function should be used where an `absl::string_view` can be created from // a possibly-null pointer. -inline string_view NullSafeStringView(const char* p) { +constexpr string_view NullSafeStringView(const char* p) { return p ? string_view(p) : string_view(); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_STRING_VIEW_H_ diff --git a/absl/strings/string_view_benchmark.cc b/absl/strings/string_view_benchmark.cc index 46909cb0..0d74e23e 100644 --- a/absl/strings/string_view_benchmark.cc +++ b/absl/strings/string_view_benchmark.cc @@ -30,6 +30,24 @@ namespace { +void BM_StringViewFromString(benchmark::State& state) { + std::string s(state.range(0), 'x'); + std::string* ps = &s; + struct SV { + SV() = default; + explicit SV(const std::string& s) : sv(s) {} + absl::string_view sv; + } sv; + SV* psv = &sv; + benchmark::DoNotOptimize(ps); + benchmark::DoNotOptimize(psv); + for (auto _ : state) { + new (psv) SV(*ps); + benchmark::DoNotOptimize(sv); + } +} +BENCHMARK(BM_StringViewFromString)->Arg(12)->Arg(128); + // Provide a forcibly out-of-line wrapper for operator== that can be used in // benchmarks to measure the impact of inlining. ABSL_ATTRIBUTE_NOINLINE @@ -142,11 +160,45 @@ void BM_CompareSame(benchmark::State& state) { absl::string_view b = y; for (auto _ : state) { + benchmark::DoNotOptimize(a); + benchmark::DoNotOptimize(b); benchmark::DoNotOptimize(a.compare(b)); } } BENCHMARK(BM_CompareSame)->DenseRange(0, 3)->Range(4, 1 << 10); +void BM_CompareFirstOneLess(benchmark::State& state) { + const int len = state.range(0); + std::string x(len, 'a'); + std::string y = x; + y.back() = 'b'; + absl::string_view a = x; + absl::string_view b = y; + + for (auto _ : state) { + benchmark::DoNotOptimize(a); + benchmark::DoNotOptimize(b); + benchmark::DoNotOptimize(a.compare(b)); + } +} +BENCHMARK(BM_CompareFirstOneLess)->DenseRange(1, 3)->Range(4, 1 << 10); + +void BM_CompareSecondOneLess(benchmark::State& state) { + const int len = state.range(0); + std::string x(len, 'a'); + std::string y = x; + x.back() = 'b'; + absl::string_view a = x; + absl::string_view b = y; + + for (auto _ : state) { + benchmark::DoNotOptimize(a); + benchmark::DoNotOptimize(b); + benchmark::DoNotOptimize(a.compare(b)); + } +} +BENCHMARK(BM_CompareSecondOneLess)->DenseRange(1, 3)->Range(4, 1 << 10); + void BM_find_string_view_len_one(benchmark::State& state) { std::string haystack(state.range(0), '0'); absl::string_view s(haystack); diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index 22d43536..7b1d56fa 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc @@ -29,7 +29,8 @@ #include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" -#ifdef __ANDROID__ +#if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__) +// We don't control the death messaging when using std::string_view. // Android assert messages only go to system log, so death tests cannot inspect // the message for matching. #define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ @@ -372,7 +373,7 @@ TEST(StringViewTest, STL1) { #ifdef ABSL_HAVE_EXCEPTIONS EXPECT_THROW(a.copy(buf, 1, 27), std::out_of_range); #else - EXPECT_DEATH(a.copy(buf, 1, 27), "absl::string_view::copy"); + ABSL_EXPECT_DEATH_IF_SUPPORTED(a.copy(buf, 1, 27), "absl::string_view::copy"); #endif } @@ -686,7 +687,8 @@ TEST(StringViewTest, STL2Substr) { #ifdef ABSL_HAVE_EXCEPTIONS EXPECT_THROW((void)a.substr(99, 2), std::out_of_range); #else - EXPECT_DEATH((void)a.substr(99, 2), "absl::string_view::substr"); + ABSL_EXPECT_DEATH_IF_SUPPORTED((void)a.substr(99, 2), + "absl::string_view::substr"); #endif } @@ -816,21 +818,35 @@ TEST(StringViewTest, FrontBackSingleChar) { EXPECT_EQ(&c, &csp.back()); } +TEST(StringViewTest, FrontBackEmpty) { +#ifndef ABSL_USES_STD_STRING_VIEW +#ifndef NDEBUG + // Abseil's string_view implementation has debug assertions that check that + // front() and back() are not called on an empty string_view. + absl::string_view sv; + ABSL_EXPECT_DEATH_IF_SUPPORTED(sv.front(), ""); + ABSL_EXPECT_DEATH_IF_SUPPORTED(sv.back(), ""); +#endif +#endif +} + // `std::string_view::string_view(const char*)` calls // `std::char_traits<char>::length(const char*)` to get the string length. In // libc++, it doesn't allow `nullptr` in the constexpr context, with the error // "read of dereferenced null pointer is not allowed in a constant expression". // At run time, the behavior of `std::char_traits::length()` on `nullptr` is // undefined by the standard and usually results in crash with libc++. +// GCC also started rejected this in libstdc++ starting in GCC9. // In MSVC, creating a constexpr string_view from nullptr also triggers an // "unevaluable pointer value" error. This compiler implementation conforms // to the standard, but `absl::string_view` implements a different // behavior for historical reasons. We work around tests that construct // `string_view` from `nullptr` when using libc++. -#if !defined(ABSL_HAVE_STD_STRING_VIEW) || \ - (!defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +#if !defined(ABSL_USES_STD_STRING_VIEW) || \ + (!(defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 9) && \ + !defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) #define ABSL_HAVE_STRING_VIEW_FROM_NULLPTR 1 -#endif // !defined(ABSL_HAVE_STD_STRING_VIEW) || !defined(_LIBCPP_VERSION) +#endif TEST(StringViewTest, NULLInput) { absl::string_view s; @@ -892,6 +908,18 @@ TEST(StringViewTest, Comparisons2) { EXPECT_LT(digits.compare(0, npos, "0123456789", 3, 5), 0); // 6 } +TEST(StringViewTest, At) { + absl::string_view abc = "abc"; + EXPECT_EQ(abc.at(0), 'a'); + EXPECT_EQ(abc.at(1), 'b'); + EXPECT_EQ(abc.at(2), 'c'); +#ifdef ABSL_HAVE_EXCEPTIONS + EXPECT_THROW(abc.at(3), std::out_of_range); +#else + ABSL_EXPECT_DEATH_IF_SUPPORTED(abc.at(3), "absl::string_view::at"); +#endif +} + struct MyCharAlloc : std::allocator<char> {}; TEST(StringViewTest, ExplicitConversionOperator) { @@ -915,6 +943,31 @@ TEST(StringViewTest, NullSafeStringView) { } } +TEST(StringViewTest, ConstexprNullSafeStringView) { + { + constexpr absl::string_view s = absl::NullSafeStringView(nullptr); + EXPECT_EQ(nullptr, s.data()); + EXPECT_EQ(0, s.size()); + EXPECT_EQ(absl::string_view(), s); + } +#if !defined(_MSC_VER) || _MSC_VER >= 1910 + // MSVC 2017+ is required for good constexpr string_view support. + // See the implementation of `absl::string_view::StrlenInternal()`. + { + static constexpr char kHi[] = "hi"; + absl::string_view s = absl::NullSafeStringView(kHi); + EXPECT_EQ(kHi, s.data()); + EXPECT_EQ(strlen(kHi), s.size()); + EXPECT_EQ(absl::string_view("hi"), s); + } + { + constexpr absl::string_view s = absl::NullSafeStringView("hello"); + EXPECT_EQ(s.size(), 5); + EXPECT_EQ("hello", s); + } +#endif +} + TEST(StringViewTest, ConstexprCompiles) { constexpr absl::string_view sp; #ifdef ABSL_HAVE_STRING_VIEW_FROM_NULLPTR @@ -922,7 +975,7 @@ TEST(StringViewTest, ConstexprCompiles) { #endif constexpr absl::string_view cstr_len("cstr", 4); -#if defined(ABSL_HAVE_STD_STRING_VIEW) +#if defined(ABSL_USES_STD_STRING_VIEW) // In libstdc++ (as of 7.2), `std::string_view::string_view(const char*)` // calls `std::char_traits<char>::length(const char*)` to get the std::string // length, but it is not marked constexpr yet. See GCC bug: @@ -936,7 +989,7 @@ TEST(StringViewTest, ConstexprCompiles) { #define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1 #endif // !__GLIBCXX__ -#else // ABSL_HAVE_STD_STRING_VIEW +#else // ABSL_USES_STD_STRING_VIEW // This duplicates the check for __builtin_strlen in the header. #if ABSL_HAVE_BUILTIN(__builtin_strlen) || \ @@ -951,13 +1004,36 @@ TEST(StringViewTest, ConstexprCompiles) { #define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1 #endif -#endif // ABSL_HAVE_STD_STRING_VIEW +#endif // ABSL_USES_STD_STRING_VIEW #ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR constexpr absl::string_view cstr_strlen("foo"); EXPECT_EQ(cstr_strlen.length(), 3); constexpr absl::string_view cstr_strlen2 = "bar"; EXPECT_EQ(cstr_strlen2, "bar"); + +#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON 1 +#endif +#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON + constexpr absl::string_view foo = "foo"; + constexpr absl::string_view bar = "bar"; + constexpr bool foo_eq_bar = foo == bar; + constexpr bool foo_ne_bar = foo != bar; + constexpr bool foo_lt_bar = foo < bar; + constexpr bool foo_le_bar = foo <= bar; + constexpr bool foo_gt_bar = foo > bar; + constexpr bool foo_ge_bar = foo >= bar; + constexpr int foo_compare_bar = foo.compare(bar); + EXPECT_FALSE(foo_eq_bar); + EXPECT_TRUE(foo_ne_bar); + EXPECT_FALSE(foo_lt_bar); + EXPECT_FALSE(foo_le_bar); + EXPECT_TRUE(foo_gt_bar); + EXPECT_TRUE(foo_ge_bar); + EXPECT_GT(foo_compare_bar, 0); +#endif #endif #if !defined(__clang__) || 3 < __clang_major__ || \ @@ -979,8 +1055,16 @@ TEST(StringViewTest, ConstexprCompiles) { constexpr absl::string_view::iterator const_end = cstr_len.end(); constexpr absl::string_view::size_type const_size = cstr_len.size(); constexpr absl::string_view::size_type const_length = cstr_len.length(); + static_assert(const_begin + const_size == const_end, + "pointer arithmetic check"); + static_assert(const_begin + const_length == const_end, + "pointer arithmetic check"); +#ifndef _MSC_VER + // MSVC has bugs doing constexpr pointer arithmetic. + // https://developercommunity.visualstudio.com/content/problem/482192/bad-pointer-arithmetic-in-constepxr-2019-rc1-svc1.html EXPECT_EQ(const_begin + const_size, const_end); EXPECT_EQ(const_begin + const_length, const_end); +#endif constexpr bool isempty = sp.empty(); EXPECT_TRUE(isempty); @@ -1036,6 +1120,17 @@ TEST(StringViewTest, Noexcept) { EXPECT_TRUE(noexcept(sp.find_last_not_of('f'))); } +TEST(StringViewTest, BoundsCheck) { +#ifndef ABSL_USES_STD_STRING_VIEW +#ifndef NDEBUG + // Abseil's string_view implementation has bounds-checking in debug mode. + absl::string_view h = "hello"; + ABSL_EXPECT_DEATH_IF_SUPPORTED(h[5], ""); + ABSL_EXPECT_DEATH_IF_SUPPORTED(h[-1], ""); +#endif +#endif +} + TEST(ComparisonOpsTest, StringCompareNotAmbiguous) { EXPECT_EQ("hello", std::string("hello")); EXPECT_LT("hello", std::string("world")); @@ -1089,7 +1184,7 @@ TEST(HugeStringView, TwoPointTwoGB) { } #endif // THREAD_SANITIZER -#if !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW) +#if !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW) TEST(NonNegativeLenTest, NonNegativeLen) { ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("xyz", -1), "len <= kMaxSize"); @@ -1105,7 +1200,7 @@ TEST(LenExceedsMaxSizeTest, LenExceedsMaxSize) { ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("", max_size + 1), "len <= kMaxSize"); } -#endif // !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW) +#endif // !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW) class StringViewStreamTest : public ::testing::Test { public: diff --git a/absl/strings/strip.h b/absl/strings/strip.h index 8e2ae422..111872ca 100644 --- a/absl/strings/strip.h +++ b/absl/strings/strip.h @@ -30,7 +30,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ConsumePrefix() // @@ -85,7 +85,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix( return str; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_STRIP_H_ diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc index 6bc9641d..5b69a3ef 100644 --- a/absl/strings/substitute.cc +++ b/absl/strings/substitute.cc @@ -23,7 +23,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace substitute_internal { void SubstituteAndAppendArray(std::string* output, absl::string_view format, @@ -36,7 +36,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, if (i + 1 >= format.size()) { #ifndef NDEBUG ABSL_RAW_LOG(FATAL, - "Invalid strings::Substitute() format std::string: \"%s\".", + "Invalid absl::Substitute() format std::string: \"%s\".", absl::CEscape(format).c_str()); #endif return; @@ -46,7 +46,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, #ifndef NDEBUG ABSL_RAW_LOG( FATAL, - "Invalid strings::Substitute() format std::string: asked for \"$" + "Invalid absl::Substitute() format std::string: asked for \"$" "%d\", but only %d args were given. Full format std::string was: " "\"%s\".", index, static_cast<int>(num_args), absl::CEscape(format).c_str()); @@ -61,7 +61,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, } else { #ifndef NDEBUG ABSL_RAW_LOG(FATAL, - "Invalid strings::Substitute() format std::string: \"%s\".", + "Invalid absl::Substitute() format std::string: \"%s\".", absl::CEscape(format).c_str()); #endif return; @@ -95,7 +95,6 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, assert(target == output->data() + output->size()); } -static const char kHexDigits[] = "0123456789abcdef"; Arg::Arg(const void* value) { static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2, "fix sizeof(scratch_)"); @@ -105,7 +104,7 @@ Arg::Arg(const void* value) { char* ptr = scratch_ + sizeof(scratch_); uintptr_t num = reinterpret_cast<uintptr_t>(value); do { - *--ptr = kHexDigits[num & 0xf]; + *--ptr = absl::numbers_internal::kHexChar[num & 0xf]; num >>= 4; } while (num != 0); *--ptr = 'x'; @@ -120,7 +119,7 @@ Arg::Arg(Hex hex) { char* writer = end; uint64_t value = hex.value; do { - *--writer = kHexDigits[value & 0xF]; + *--writer = absl::numbers_internal::kHexChar[value & 0xF]; value >>= 4; } while (value != 0); @@ -168,5 +167,5 @@ Arg::Arg(Dec dec) { } } // namespace substitute_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index 3ba7f4d3..4d0984d3 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h @@ -86,7 +86,7 @@ #include "absl/strings/strip.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace substitute_internal { // Arg @@ -190,7 +190,12 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, #if defined(ABSL_BAD_CALL_IF) constexpr int CalculateOneBit(const char* format) { - return (*format < '0' || *format > '9') ? 0 : (1 << (*format - '0')); + // Returns: + // * 2^N for '$N' when N is in [0-9] + // * 0 for correct '$' escaping: '$$'. + // * -1 otherwise. + return (*format < '0' || *format > '9') ? (*format == '$' ? 0 : -1) + : (1 << (*format - '0')); } constexpr const char* SkipNumber(const char* format) { @@ -682,7 +687,7 @@ std::string Substitute( "format std::string doesn't contain all of $0 through $9"); #endif // ABSL_BAD_CALL_IF -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_SUBSTITUTE_H_ diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc index e27abb17..450cd2bc 100644 --- a/absl/strings/substitute_test.cc +++ b/absl/strings/substitute_test.cc @@ -189,14 +189,14 @@ TEST(SubstituteTest, VectorBoolRef) { TEST(SubstituteDeathTest, SubstituteDeath) { EXPECT_DEBUG_DEATH( static_cast<void>(absl::Substitute(absl::string_view("-$2"), "a", "b")), - "Invalid strings::Substitute\\(\\) format std::string: asked for \"\\$2\", " + "Invalid absl::Substitute\\(\\) format std::string: asked for \"\\$2\", " "but only 2 args were given."); EXPECT_DEBUG_DEATH( - static_cast<void>(absl::Substitute("-$z-")), - "Invalid strings::Substitute\\(\\) format std::string: \"-\\$z-\""); + static_cast<void>(absl::Substitute(absl::string_view("-$z-"))), + "Invalid absl::Substitute\\(\\) format std::string: \"-\\$z-\""); EXPECT_DEBUG_DEATH( - static_cast<void>(absl::Substitute("-$")), - "Invalid strings::Substitute\\(\\) format std::string: \"-\\$\""); + static_cast<void>(absl::Substitute(absl::string_view("-$"))), + "Invalid absl::Substitute\\(\\) format std::string: \"-\\$\""); } #endif // GTEST_HAS_DEATH_TEST diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index fca8cb69..3f876b9f 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -14,6 +14,7 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -42,8 +43,25 @@ cc_library( deps = [ "//absl/base", "//absl/base:base_internal", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:malloc_internal", + "//absl/base:raw_logging_internal", + ], +) + +cc_library( + name = "kernel_timeout_internal", + hdrs = ["internal/kernel_timeout.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/synchronization:__pkg__", + ], + deps = [ + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/time", ], ) @@ -63,7 +81,6 @@ cc_library( "barrier.h", "blocking_counter.h", "internal/create_thread_identity.h", - "internal/kernel_timeout.h", "internal/mutex_nonprod.inc", "internal/per_thread_sem.h", "internal/waiter.h", @@ -77,6 +94,7 @@ cc_library( }) + ABSL_DEFAULT_LINKOPTS, deps = [ ":graphcycles_internal", + ":kernel_timeout_internal", "//absl/base", "//absl/base:atomic_hook", "//absl/base:base_internal", @@ -84,6 +102,7 @@ cc_library( "//absl/base:core_headers", "//absl/base:dynamic_annotations", "//absl/base:malloc_internal", + "//absl/base:raw_logging_internal", "//absl/debugging:stacktrace", "//absl/debugging:symbolize", "//absl/time", @@ -124,8 +143,8 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":graphcycles_internal", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "@com_google_googletest//:gtest_main", ], ) @@ -140,7 +159,7 @@ cc_test( ], deps = [ ":graphcycles_internal", - "//absl/base", + "//absl/base:raw_logging_internal", "@com_github_google_benchmark//:benchmark_main", ], ) @@ -171,6 +190,7 @@ cc_test( ":thread_pool", "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/memory", "//absl/time", "@com_google_googletest//:gtest_main", @@ -243,7 +263,6 @@ cc_test( deps = [ ":per_thread_sem_test_common", ":synchronization", - "//absl/base", "//absl/strings", "//absl/time", "@com_google_googletest//:gtest_main", @@ -260,7 +279,7 @@ cc_test( tags = ["no_test_ios_x86_64"], deps = [ ":synchronization", - "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", ], ) diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index 4b708823..dfe5d05d 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -26,8 +26,23 @@ absl_cc_library( DEPS absl::base absl::base_internal + absl::config absl::core_headers absl::malloc_internal + absl::raw_logging_internal +) + +absl_cc_library( + NAME + kernel_timeout_internal + HDRS + "internal/kernel_timeout.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::raw_logging_internal + absl::time ) absl_cc_library( @@ -37,7 +52,6 @@ absl_cc_library( "barrier.h" "blocking_counter.h" "internal/create_thread_identity.h" - "internal/kernel_timeout.h" "internal/mutex_nonprod.inc" "internal/per_thread_sem.h" "internal/waiter.h" @@ -55,6 +69,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::graphcycles_internal + absl::kernel_timeout_internal absl::atomic_hook absl::base absl::base_internal @@ -62,6 +77,7 @@ absl_cc_library( absl::core_headers absl::dynamic_annotations absl::malloc_internal + absl::raw_logging_internal absl::stacktrace absl::symbolize absl::time @@ -104,8 +120,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::graphcycles_internal - absl::base absl::core_headers + absl::raw_logging_internal gmock_main ) @@ -135,6 +151,7 @@ absl_cc_test( absl::base absl::core_headers absl::memory + absl::raw_logging_internal absl::time gmock_main ) @@ -178,7 +195,6 @@ absl_cc_test( DEPS absl::per_thread_sem_test_common absl::synchronization - absl::base absl::strings absl::time gmock_main @@ -193,6 +209,6 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::synchronization - absl::base absl::core_headers + absl::raw_logging_internal ) diff --git a/absl/synchronization/barrier.cc b/absl/synchronization/barrier.cc index 72089c52..0dfd795e 100644 --- a/absl/synchronization/barrier.cc +++ b/absl/synchronization/barrier.cc @@ -18,7 +18,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Return whether int *arg is zero. static bool IsZero(void *arg) { @@ -48,5 +48,5 @@ bool Barrier::Block() { return this->num_to_exit_ == 0; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/barrier.h b/absl/synchronization/barrier.h index 53d5ca26..d8e75440 100644 --- a/absl/synchronization/barrier.h +++ b/absl/synchronization/barrier.h @@ -23,7 +23,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Barrier // @@ -70,10 +70,10 @@ class Barrier { private: Mutex lock_; - int num_to_block_ GUARDED_BY(lock_); - int num_to_exit_ GUARDED_BY(lock_); + int num_to_block_ ABSL_GUARDED_BY(lock_); + int num_to_exit_ ABSL_GUARDED_BY(lock_); }; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_SYNCHRONIZATION_BARRIER_H_ diff --git a/absl/synchronization/blocking_counter.cc b/absl/synchronization/blocking_counter.cc index c6968973..3cea7aed 100644 --- a/absl/synchronization/blocking_counter.cc +++ b/absl/synchronization/blocking_counter.cc @@ -17,7 +17,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Return whether int *arg is zero. static bool IsZero(void *arg) { @@ -53,5 +53,5 @@ void BlockingCounter::Wait() { // after we return from this method. } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/blocking_counter.h b/absl/synchronization/blocking_counter.h index 5dab5a94..1f53f9f2 100644 --- a/absl/synchronization/blocking_counter.h +++ b/absl/synchronization/blocking_counter.h @@ -24,7 +24,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // BlockingCounter // @@ -89,11 +89,11 @@ class BlockingCounter { private: Mutex lock_; - int count_ GUARDED_BY(lock_); - int num_waiting_ GUARDED_BY(lock_); + int count_ ABSL_GUARDED_BY(lock_); + int num_waiting_ ABSL_GUARDED_BY(lock_); }; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ diff --git a/absl/synchronization/blocking_counter_test.cc b/absl/synchronization/blocking_counter_test.cc index 62d98738..2926224a 100644 --- a/absl/synchronization/blocking_counter_test.cc +++ b/absl/synchronization/blocking_counter_test.cc @@ -22,7 +22,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { void PauseAndDecreaseCounter(BlockingCounter* counter, int* done) { @@ -64,5 +64,5 @@ TEST(BlockingCounterTest, BasicFunctionality) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc index 65f6d8fc..fa0070a9 100644 --- a/absl/synchronization/internal/create_thread_identity.cc +++ b/absl/synchronization/internal/create_thread_identity.cc @@ -27,7 +27,7 @@ #include "absl/synchronization/internal/per_thread_sem.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { // ThreadIdentity storage is persistent, we maintain a free-list of previously @@ -38,7 +38,7 @@ static base_internal::ThreadIdentity* thread_identity_freelist; // A per-thread destructor for reclaiming associated ThreadIdentity objects. // Since we must preserve their storage we cache them for re-use. -static void ReclaimThreadIdentity(void* v) { +void ReclaimThreadIdentity(void* v) { base_internal::ThreadIdentity* identity = static_cast<base_internal::ThreadIdentity*>(v); @@ -48,6 +48,8 @@ static void ReclaimThreadIdentity(void* v) { base_internal::LowLevelAlloc::Free(identity->per_thread_synch.all_locks); } + PerThreadSem::Destroy(identity); + // We must explicitly clear the current thread's identity: // (a) Subsequent (unrelated) per-thread destructors may require an identity. // We must guarantee a new identity is used in this case (this instructor @@ -85,7 +87,6 @@ static void ResetThreadIdentity(base_internal::ThreadIdentity* identity) { pts->wake = false; pts->cond_waiter = false; pts->all_locks = nullptr; - identity->waiter_state = {}; identity->blocked_count_ptr = nullptr; identity->ticker.store(0, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed); @@ -133,7 +134,7 @@ base_internal::ThreadIdentity* CreateThreadIdentity() { } } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/absl/synchronization/internal/create_thread_identity.h b/absl/synchronization/internal/create_thread_identity.h index d743cc3b..e121f683 100644 --- a/absl/synchronization/internal/create_thread_identity.h +++ b/absl/synchronization/internal/create_thread_identity.h @@ -29,13 +29,17 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { // Allocates and attaches a ThreadIdentity object for the calling thread. // For private use only. base_internal::ThreadIdentity* CreateThreadIdentity(); +// A per-thread destructor for reclaiming associated ThreadIdentity objects. +// For private use only. +void ReclaimThreadIdentity(void* v); + // Returns the ThreadIdentity object representing the calling thread; guaranteed // to be unique for its lifetime. The returned object will remain valid for the // program's lifetime; although it may be re-assigned to a subsequent thread. @@ -50,7 +54,7 @@ inline base_internal::ThreadIdentity* GetOrCreateCurrentThreadIdentity() { } } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_CREATE_THREAD_IDENTITY_H_ diff --git a/absl/synchronization/internal/graphcycles.cc b/absl/synchronization/internal/graphcycles.cc index f4fbeadd..6a2bcdf6 100644 --- a/absl/synchronization/internal/graphcycles.cc +++ b/absl/synchronization/internal/graphcycles.cc @@ -44,7 +44,7 @@ // Do not use STL. This module does not use standard memory allocation. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { namespace { @@ -691,7 +691,7 @@ int GraphCycles::GetStackTrace(GraphId id, void*** ptr) { } } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/absl/synchronization/internal/graphcycles.h b/absl/synchronization/internal/graphcycles.h index 208527c3..ceba33e4 100644 --- a/absl/synchronization/internal/graphcycles.h +++ b/absl/synchronization/internal/graphcycles.h @@ -36,12 +36,14 @@ // InsertEdge() is very fast when the edge already exists, and reasonably fast // otherwise. // FindPath() is linear in the size of the graph. -// The current implemenation uses O(|V|+|E|) space. +// The current implementation uses O(|V|+|E|) space. #include <cstdint> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { // Opaque identifier for a graph node. @@ -133,7 +135,7 @@ class GraphCycles { }; } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif diff --git a/absl/synchronization/internal/graphcycles_test.cc b/absl/synchronization/internal/graphcycles_test.cc index fca86219..74eaffe7 100644 --- a/absl/synchronization/internal/graphcycles_test.cc +++ b/absl/synchronization/internal/graphcycles_test.cc @@ -25,7 +25,7 @@ #include "absl/base/macros.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { // We emulate a GraphCycles object with a node vector and an edge vector. @@ -460,5 +460,5 @@ TEST_F(GraphCyclesTest, ManyEdges) { } } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h index e0f01e06..d6ac5db0 100644 --- a/absl/synchronization/internal/kernel_timeout.h +++ b/absl/synchronization/internal/kernel_timeout.h @@ -34,7 +34,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { class Futex; @@ -149,7 +149,7 @@ class KernelTimeout { }; } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ diff --git a/absl/synchronization/internal/mutex_nonprod.cc b/absl/synchronization/internal/mutex_nonprod.cc index aa1ed83b..4590b98d 100644 --- a/absl/synchronization/internal/mutex_nonprod.cc +++ b/absl/synchronization/internal/mutex_nonprod.cc @@ -31,7 +31,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { namespace { @@ -316,5 +316,5 @@ bool Condition::Eval() const { void RegisterSymbolizer(bool (*)(const void*, char*, int)) {} -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/internal/mutex_nonprod.inc b/absl/synchronization/internal/mutex_nonprod.inc index ac10879b..a1502e72 100644 --- a/absl/synchronization/internal/mutex_nonprod.inc +++ b/absl/synchronization/internal/mutex_nonprod.inc @@ -36,7 +36,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN class Condition; namespace synchronization_internal { @@ -252,10 +252,10 @@ class SynchronizationStorage { absl::once_flag once_; - // An aligned space for T. - typename std::aligned_storage<sizeof(T), alignof(T)>::type space_; + // An aligned space for the T. + alignas(T) unsigned char space_[sizeof(T)]; }; } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc index 284a5df4..821ca9b4 100644 --- a/absl/synchronization/internal/per_thread_sem.cc +++ b/absl/synchronization/internal/per_thread_sem.cc @@ -25,7 +25,7 @@ #include "absl/synchronization/internal/waiter.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { void PerThreadSem::SetThreadBlockedCounter(std::atomic<int> *counter) { @@ -41,12 +41,16 @@ std::atomic<int> *PerThreadSem::GetThreadBlockedCounter() { } void PerThreadSem::Init(base_internal::ThreadIdentity *identity) { - Waiter::GetWaiter(identity)->Init(); + new (Waiter::GetWaiter(identity)) Waiter(); identity->ticker.store(0, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed); identity->is_idle.store(false, std::memory_order_relaxed); } +void PerThreadSem::Destroy(base_internal::ThreadIdentity *identity) { + Waiter::GetWaiter(identity)->~Waiter(); +} + void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) { const int ticker = identity->ticker.fetch_add(1, std::memory_order_relaxed) + 1; @@ -59,7 +63,7 @@ void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) { } } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl extern "C" { diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h index 5bb0978b..8ab43915 100644 --- a/absl/synchronization/internal/per_thread_sem.h +++ b/absl/synchronization/internal/per_thread_sem.h @@ -32,7 +32,7 @@ #include "absl/synchronization/internal/kernel_timeout.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN class Mutex; @@ -66,6 +66,10 @@ class PerThreadSem { // REQUIRES: May only be called by ThreadIdentity. static void Init(base_internal::ThreadIdentity* identity); + // Destroy the PerThreadSem associated with "identity". + // REQUIRES: May only be called by ThreadIdentity. + static void Destroy(base_internal::ThreadIdentity* identity); + // Increments "identity"'s count. static inline void Post(base_internal::ThreadIdentity* identity); @@ -78,10 +82,11 @@ class PerThreadSem { friend class PerThreadSemTest; friend class absl::Mutex; friend absl::base_internal::ThreadIdentity* CreateThreadIdentity(); + friend void ReclaimThreadIdentity(void* v); }; } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // In some build configurations we pass --detect-odr-violations to the diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc index 93bc4244..b5a2f6d4 100644 --- a/absl/synchronization/internal/per_thread_sem_test.cc +++ b/absl/synchronization/internal/per_thread_sem_test.cc @@ -33,7 +33,7 @@ // primitives which might use PerThreadSem, most notably absl::Mutex. namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { class SimpleSemaphore { @@ -176,5 +176,5 @@ TEST_F(PerThreadSemTest, Timeouts) { } // namespace } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/internal/thread_pool.h b/absl/synchronization/internal/thread_pool.h index 8941be68..0cb96dac 100644 --- a/absl/synchronization/internal/thread_pool.h +++ b/absl/synchronization/internal/thread_pool.h @@ -26,7 +26,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { // A simple ThreadPool implementation for tests. @@ -61,7 +61,7 @@ class ThreadPool { } private: - bool WorkAvailable() const EXCLUSIVE_LOCKS_REQUIRED(mu_) { + bool WorkAvailable() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) { return !queue_.empty(); } @@ -82,12 +82,12 @@ class ThreadPool { } absl::Mutex mu_; - std::queue<std::function<void()>> queue_ GUARDED_BY(mu_); + std::queue<std::function<void()>> queue_ ABSL_GUARDED_BY(mu_); std::vector<std::thread> threads_; }; } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_ diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 17c6a506..2949f5a8 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -49,7 +49,7 @@ #include "absl/synchronization/internal/kernel_timeout.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { static void MaybeBecomeIdle() { @@ -123,16 +123,21 @@ class Futex { } }; -void Waiter::Init() { +Waiter::Waiter() { futex_.store(0, std::memory_order_relaxed); } +Waiter::~Waiter() = default; + bool Waiter::Wait(KernelTimeout t) { // Loop until we can atomically decrement futex from a positive // value, waiting on a futex while we believe it is zero. + // Note that, since the thread ticker is just reset, we don't need to check + // whether the thread is idle on the very first pass of the loop. + bool first_pass = true; while (true) { int32_t x = futex_.load(std::memory_order_relaxed); - if (x != 0) { + while (x != 0) { if (!futex_.compare_exchange_weak(x, x - 1, std::memory_order_acquire, std::memory_order_relaxed)) { @@ -141,6 +146,8 @@ bool Waiter::Wait(KernelTimeout t) { return true; // Consumed a wakeup, we are done. } + + if (!first_pass) MaybeBecomeIdle(); const int err = Futex::WaitUntil(&futex_, 0, t); if (err != 0) { if (err == -EINTR || err == -EWOULDBLOCK) { @@ -151,14 +158,13 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err); } } - - MaybeBecomeIdle(); + first_pass = false; } } void Waiter::Post() { if (futex_.fetch_add(1, std::memory_order_release) == 0) { - // We incremented from 0, need to wake a potential waker. + // We incremented from 0, need to wake a potential waiter. Poke(); } } @@ -196,7 +202,7 @@ class PthreadMutexHolder { pthread_mutex_t *mu_; }; -void Waiter::Init() { +Waiter::Waiter() { const int err = pthread_mutex_init(&mu_, 0); if (err != 0) { ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err); @@ -207,8 +213,20 @@ void Waiter::Init() { ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2); } - waiter_count_.store(0, std::memory_order_relaxed); - wakeup_count_.store(0, std::memory_order_relaxed); + waiter_count_ = 0; + wakeup_count_ = 0; +} + +Waiter::~Waiter() { + const int err = pthread_mutex_destroy(&mu_); + if (err != 0) { + ABSL_RAW_LOG(FATAL, "pthread_mutex_destroy failed: %d", err); + } + + const int err2 = pthread_cond_destroy(&cv_); + if (err2 != 0) { + ABSL_RAW_LOG(FATAL, "pthread_cond_destroy failed: %d", err2); + } } bool Waiter::Wait(KernelTimeout t) { @@ -218,21 +236,13 @@ bool Waiter::Wait(KernelTimeout t) { } PthreadMutexHolder h(&mu_); - waiter_count_.fetch_add(1, std::memory_order_relaxed); + ++waiter_count_; // Loop until we find a wakeup to consume or timeout. - while (true) { - int x = wakeup_count_.load(std::memory_order_relaxed); - if (x != 0) { - if (!wakeup_count_.compare_exchange_weak(x, x - 1, - std::memory_order_acquire, - std::memory_order_relaxed)) { - continue; // Raced with someone, retry. - } - // Successfully consumed a wakeup, we're done. - waiter_count_.fetch_sub(1, std::memory_order_relaxed); - return true; - } - + // Note that, since the thread ticker is just reset, we don't need to check + // whether the thread is idle on the very first pass of the loop. + bool first_pass = true; + while (wakeup_count_ == 0) { + if (!first_pass) MaybeBecomeIdle(); // No wakeups available, time to wait. if (!t.has_timeout()) { const int err = pthread_cond_wait(&cv_, &mu_); @@ -242,46 +252,56 @@ bool Waiter::Wait(KernelTimeout t) { } else { const int err = pthread_cond_timedwait(&cv_, &mu_, &abs_timeout); if (err == ETIMEDOUT) { - waiter_count_.fetch_sub(1, std::memory_order_relaxed); + --waiter_count_; return false; } if (err != 0) { - ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err); + ABSL_RAW_LOG(FATAL, "pthread_cond_timedwait failed: %d", err); } } - MaybeBecomeIdle(); + first_pass = false; } + // Consume a wakeup and we're done. + --wakeup_count_; + --waiter_count_; + return true; } void Waiter::Post() { - wakeup_count_.fetch_add(1, std::memory_order_release); - Poke(); + PthreadMutexHolder h(&mu_); + ++wakeup_count_; + InternalCondVarPoke(); } void Waiter::Poke() { - if (waiter_count_.load(std::memory_order_relaxed) == 0) { - return; - } - // Potentially a waker. Take the lock and check again. PthreadMutexHolder h(&mu_); - if (waiter_count_.load(std::memory_order_relaxed) == 0) { - return; - } - const int err = pthread_cond_signal(&cv_); - if (err != 0) { - ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err); + InternalCondVarPoke(); +} + +void Waiter::InternalCondVarPoke() { + if (waiter_count_ != 0) { + const int err = pthread_cond_signal(&cv_); + if (ABSL_PREDICT_FALSE(err != 0)) { + ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err); + } } } #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM -void Waiter::Init() { +Waiter::Waiter() { if (sem_init(&sem_, 0, 0) != 0) { ABSL_RAW_LOG(FATAL, "sem_init failed with errno %d\n", errno); } wakeups_.store(0, std::memory_order_relaxed); } +Waiter::~Waiter() { + if (sem_destroy(&sem_) != 0) { + ABSL_RAW_LOG(FATAL, "sem_destroy failed with errno %d\n", errno); + } +} + bool Waiter::Wait(KernelTimeout t) { struct timespec abs_timeout; if (t.has_timeout()) { @@ -289,9 +309,12 @@ bool Waiter::Wait(KernelTimeout t) { } // Loop until we timeout or consume a wakeup. + // Note that, since the thread ticker is just reset, we don't need to check + // whether the thread is idle on the very first pass of the loop. + bool first_pass = true; while (true) { int x = wakeups_.load(std::memory_order_relaxed); - if (x != 0) { + while (x != 0) { if (!wakeups_.compare_exchange_weak(x, x - 1, std::memory_order_acquire, std::memory_order_relaxed)) { @@ -301,6 +324,7 @@ bool Waiter::Wait(KernelTimeout t) { return true; } + if (!first_pass) MaybeBecomeIdle(); // Nothing to consume, wait (looping on EINTR). while (true) { if (!t.has_timeout()) { @@ -314,13 +338,16 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "sem_timedwait failed: %d", errno); } } - MaybeBecomeIdle(); + first_pass = false; } } void Waiter::Post() { - wakeups_.fetch_add(1, std::memory_order_release); // Post a wakeup. - Poke(); + // Post a wakeup. + if (wakeups_.fetch_add(1, std::memory_order_release) == 0) { + // We incremented from 0, need to wake a potential waiter. + Poke(); + } } void Waiter::Poke() { @@ -341,31 +368,29 @@ class Waiter::WinHelper { return reinterpret_cast<CONDITION_VARIABLE *>(&w->cv_storage_); } - static_assert(sizeof(SRWLOCK) == sizeof(Waiter::SRWLockStorage), - "SRWLockStorage does not have the same size as SRWLOCK"); + static_assert(sizeof(SRWLOCK) == sizeof(void *), + "`mu_storage_` does not have the same size as SRWLOCK"); + static_assert(alignof(SRWLOCK) == alignof(void *), + "`mu_storage_` does not have the same alignment as SRWLOCK"); + + static_assert(sizeof(CONDITION_VARIABLE) == sizeof(void *), + "`ABSL_CONDITION_VARIABLE_STORAGE` does not have the same size " + "as `CONDITION_VARIABLE`"); static_assert( - alignof(SRWLOCK) == alignof(Waiter::SRWLockStorage), - "SRWLockStorage does not have the same alignment as SRWLOCK"); - - static_assert(sizeof(CONDITION_VARIABLE) == - sizeof(Waiter::ConditionVariableStorage), - "ABSL_CONDITION_VARIABLE_STORAGE does not have the same size " - "as CONDITION_VARIABLE"); - static_assert(alignof(CONDITION_VARIABLE) == - alignof(Waiter::ConditionVariableStorage), - "ConditionVariableStorage does not have the same " - "alignment as CONDITION_VARIABLE"); - - // The SRWLOCK and CONDITION_VARIABLE types must be trivially constuctible + alignof(CONDITION_VARIABLE) == alignof(void *), + "`cv_storage_` does not have the same alignment as `CONDITION_VARIABLE`"); + + // The SRWLOCK and CONDITION_VARIABLE types must be trivially constructible // and destructible because we never call their constructors or destructors. static_assert(std::is_trivially_constructible<SRWLOCK>::value, - "The SRWLOCK type must be trivially constructible"); - static_assert(std::is_trivially_constructible<CONDITION_VARIABLE>::value, - "The CONDITION_VARIABLE type must be trivially constructible"); + "The `SRWLOCK` type must be trivially constructible"); + static_assert( + std::is_trivially_constructible<CONDITION_VARIABLE>::value, + "The `CONDITION_VARIABLE` type must be trivially constructible"); static_assert(std::is_trivially_destructible<SRWLOCK>::value, - "The SRWLOCK type must be trivially destructible"); + "The `SRWLOCK` type must be trivially destructible"); static_assert(std::is_trivially_destructible<CONDITION_VARIABLE>::value, - "The CONDITION_VARIABLE type must be trivially destructible"); + "The `CONDITION_VARIABLE` type must be trivially destructible"); }; class LockHolder { @@ -385,36 +410,33 @@ class LockHolder { SRWLOCK* mu_; }; -void Waiter::Init() { +Waiter::Waiter() { auto *mu = ::new (static_cast<void *>(&mu_storage_)) SRWLOCK; auto *cv = ::new (static_cast<void *>(&cv_storage_)) CONDITION_VARIABLE; InitializeSRWLock(mu); InitializeConditionVariable(cv); - waiter_count_.store(0, std::memory_order_relaxed); - wakeup_count_.store(0, std::memory_order_relaxed); + waiter_count_ = 0; + wakeup_count_ = 0; } +// SRW locks and condition variables do not need to be explicitly destroyed. +// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock +// https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with +Waiter::~Waiter() = default; + bool Waiter::Wait(KernelTimeout t) { SRWLOCK *mu = WinHelper::GetLock(this); CONDITION_VARIABLE *cv = WinHelper::GetCond(this); LockHolder h(mu); - waiter_count_.fetch_add(1, std::memory_order_relaxed); + ++waiter_count_; // Loop until we find a wakeup to consume or timeout. - while (true) { - int x = wakeup_count_.load(std::memory_order_relaxed); - if (x != 0) { - if (!wakeup_count_.compare_exchange_weak(x, x - 1, - std::memory_order_acquire, - std::memory_order_relaxed)) { - continue; // Raced with someone, retry. - } - // Successfully consumed a wakeup, we're done. - waiter_count_.fetch_sub(1, std::memory_order_relaxed); - return true; - } - + // Note that, since the thread ticker is just reset, we don't need to check + // whether the thread is idle on the very first pass of the loop. + bool first_pass = true; + while (wakeup_count_ == 0) { + if (!first_pass) MaybeBecomeIdle(); // No wakeups available, time to wait. if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) { // GetLastError() returns a Win32 DWORD, but we assign to @@ -422,32 +444,35 @@ bool Waiter::Wait(KernelTimeout t) { // initialization guarantees this is not a narrowing conversion. const unsigned long err{GetLastError()}; // NOLINT(runtime/int) if (err == ERROR_TIMEOUT) { - waiter_count_.fetch_sub(1, std::memory_order_relaxed); + --waiter_count_; return false; } else { ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err); } } - - MaybeBecomeIdle(); + first_pass = false; } + // Consume a wakeup and we're done. + --wakeup_count_; + --waiter_count_; + return true; } void Waiter::Post() { - wakeup_count_.fetch_add(1, std::memory_order_release); - Poke(); + LockHolder h(WinHelper::GetLock(this)); + ++wakeup_count_; + InternalCondVarPoke(); } void Waiter::Poke() { - if (waiter_count_.load(std::memory_order_relaxed) == 0) { - return; - } - // Potentially a waker. Take the lock and check again. LockHolder h(WinHelper::GetLock(this)); - if (waiter_count_.load(std::memory_order_relaxed) == 0) { - return; + InternalCondVarPoke(); +} + +void Waiter::InternalCondVarPoke() { + if (waiter_count_ != 0) { + WakeConditionVariable(WinHelper::GetCond(this)); } - WakeConditionVariable(WinHelper::GetCond(this)); } #else @@ -455,5 +480,5 @@ void Waiter::Poke() { #endif } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index 06032642..a6e6d4c7 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -18,10 +18,16 @@ #include "absl/base/config.h" -#ifndef _WIN32 +#ifdef _WIN32 +#include <sdkddkver.h> +#else #include <pthread.h> #endif +#ifdef __linux__ +#include <linux/futex.h> +#endif + #ifdef ABSL_HAVE_SEMAPHORE_H #include <semaphore.h> #endif @@ -40,9 +46,14 @@ #if defined(ABSL_FORCE_WAITER_MODE) #define ABSL_WAITER_MODE ABSL_FORCE_WAITER_MODE -#elif defined(_WIN32) +#elif defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA #define ABSL_WAITER_MODE ABSL_WAITER_MODE_WIN32 -#elif defined(__linux__) +#elif defined(__BIONIC__) +// Bionic supports all the futex operations we need even when some of the futex +// definitions are missing. +#define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX +#elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME) +// FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28. #define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX #elif defined(ABSL_HAVE_SEMAPHORE_H) #define ABSL_WAITER_MODE ABSL_WAITER_MODE_SEM @@ -51,20 +62,21 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace synchronization_internal { // Waiter is an OS-specific semaphore. class Waiter { public: - // No constructor, instances use the reserved space in ThreadIdentity. - // All initialization logic belongs in `Init()`. - Waiter() = delete; + // Prepare any data to track waits. + Waiter(); + + // Not copyable or movable Waiter(const Waiter&) = delete; Waiter& operator=(const Waiter&) = delete; - // Prepare any data to track waits. - void Init(); + // Destroy any data to track waits. + ~Waiter(); // Blocks the calling thread until a matching call to `Post()` or // `t` has passed. Returns `true` if woken (`Post()` called), @@ -105,10 +117,13 @@ class Waiter { static_assert(sizeof(int32_t) == sizeof(futex_), "Wrong size for futex"); #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_CONDVAR + // REQUIRES: mu_ must be held. + void InternalCondVarPoke(); + pthread_mutex_t mu_; pthread_cond_t cv_; - std::atomic<int> waiter_count_; - std::atomic<int> wakeup_count_; // Unclaimed wakeups, written under lock. + int waiter_count_; + int wakeup_count_; // Unclaimed wakeups. #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM sem_t sem_; @@ -118,26 +133,19 @@ class Waiter { std::atomic<int> wakeups_; #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32 - // The Windows API has lots of choices for synchronization - // primivitives. We are using SRWLOCK and CONDITION_VARIABLE - // because they don't require a destructor to release system - // resources. - // - // However, we can't include Windows.h in our headers, so we use aligned - // storage buffers to define the storage. - using SRWLockStorage = - typename std::aligned_storage<sizeof(void*), alignof(void*)>::type; - using ConditionVariableStorage = - typename std::aligned_storage<sizeof(void*), alignof(void*)>::type; - // WinHelper - Used to define utilities for accessing the lock and // condition variable storage once the types are complete. class WinHelper; - SRWLockStorage mu_storage_; - ConditionVariableStorage cv_storage_; - std::atomic<int> waiter_count_; - std::atomic<int> wakeup_count_; + // REQUIRES: WinHelper::GetLock(this) must be held. + void InternalCondVarPoke(); + + // We can't include Windows.h in our headers, so we use aligned charachter + // buffers to define the storage of SRWLOCK and CONDITION_VARIABLE. + alignas(void*) unsigned char mu_storage_[sizeof(void*)]; + alignas(void*) unsigned char cv_storage_[sizeof(void*)]; + int waiter_count_; + int wakeup_count_; #else #error Unknown ABSL_WAITER_MODE @@ -145,7 +153,7 @@ class Waiter { }; } // namespace synchronization_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_ diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc index 0279c8f8..cc973a32 100644 --- a/absl/synchronization/lifetime_test.cc +++ b/absl/synchronization/lifetime_test.cc @@ -122,6 +122,11 @@ class OnDestruction { Function fn_; }; +// These tests require that the compiler correctly supports C++11 constant +// initialization... but MSVC has a known regression since v19.10: +// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html +// TODO(epastor): Limit the affected range once MSVC fixes this bug. +#if defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900) // kConstInit // Test early usage. (Declaration comes first; definitions must appear after // the test runner.) @@ -143,14 +148,15 @@ ABSL_CONST_INIT absl::Mutex early_const_init_mutex(absl::kConstInit); // constructors of globals "happen at link time"; memory is pre-initialized, // before the constructors of either grab_lock or check_still_locked are run.) extern absl::Mutex const_init_sanity_mutex; -OnConstruction grab_lock([]() NO_THREAD_SAFETY_ANALYSIS { +OnConstruction grab_lock([]() ABSL_NO_THREAD_SAFETY_ANALYSIS { const_init_sanity_mutex.Lock(); }); ABSL_CONST_INIT absl::Mutex const_init_sanity_mutex(absl::kConstInit); -OnConstruction check_still_locked([]() NO_THREAD_SAFETY_ANALYSIS { +OnConstruction check_still_locked([]() ABSL_NO_THREAD_SAFETY_ANALYSIS { const_init_sanity_mutex.AssertHeld(); const_init_sanity_mutex.Unlock(); }); +#endif // defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900) // Test shutdown usage. (Declarations come first; definitions must appear after // the test runner.) diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 07f220f5..e0879b05 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc @@ -71,7 +71,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalMutexYield() { std::this_thread::yield(); } } // extern "C" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { @@ -107,13 +107,16 @@ static_assert( sizeof(MutexGlobals) == ABSL_CACHELINE_SIZE, "MutexGlobals must occupy an entire cacheline to prevent false sharing"); -ABSL_CONST_INIT absl::base_internal::AtomicHook<void (*)(int64_t wait_cycles)> - submit_profile_data; -ABSL_CONST_INIT absl::base_internal::AtomicHook< - void (*)(const char *msg, const void *obj, int64_t wait_cycles)> mutex_tracer; -ABSL_CONST_INIT absl::base_internal::AtomicHook< - void (*)(const char *msg, const void *cv)> cond_var_tracer; -ABSL_CONST_INIT absl::base_internal::AtomicHook< +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES + absl::base_internal::AtomicHook<void (*)(int64_t wait_cycles)> + submit_profile_data; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<void (*)( + const char *msg, const void *obj, int64_t wait_cycles)> + mutex_tracer; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES + absl::base_internal::AtomicHook<void (*)(const char *msg, const void *cv)> + cond_var_tracer; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook< bool (*)(const void *pc, char *out, int out_size)> symbolizer(absl::Symbolize); @@ -208,8 +211,8 @@ static absl::base_internal::SpinLock deadlock_graph_mu( absl::base_internal::kLinkerInitialized); // graph used to detect deadlocks. -static GraphCycles *deadlock_graph GUARDED_BY(deadlock_graph_mu) - PT_GUARDED_BY(deadlock_graph_mu); +static GraphCycles *deadlock_graph ABSL_GUARDED_BY(deadlock_graph_mu) + ABSL_PT_GUARDED_BY(deadlock_graph_mu); //------------------------------------------------------------------ // An event mechanism for debugging mutex use. @@ -280,10 +283,10 @@ static const uint32_t kNSynchEvent = 1031; static struct SynchEvent { // this is a trivial hash table for the events // struct is freed when refcount reaches 0 - int refcount GUARDED_BY(synch_event_mu); + int refcount ABSL_GUARDED_BY(synch_event_mu); // buckets have linear, 0-terminated chains - SynchEvent *next GUARDED_BY(synch_event_mu); + SynchEvent *next ABSL_GUARDED_BY(synch_event_mu); // Constant after initialization uintptr_t masked_addr; // object at this address is called "name" @@ -296,8 +299,8 @@ static struct SynchEvent { // this is a trivial hash table for the events bool log; // logging turned on // Constant after initialization - char name[1]; // actually longer---null-terminated std::string -} *synch_event[kNSynchEvent] GUARDED_BY(synch_event_mu); + char name[1]; // actually longer---NUL-terminated std::string +} * synch_event[kNSynchEvent] ABSL_GUARDED_BY(synch_event_mu); // Ensure that the object at "addr" has a SynchEvent struct associated with it, // set "bits" in the word there (waiting until lockbit is clear before doing @@ -1144,7 +1147,7 @@ PerThreadSynch *Mutex::Wakeup(PerThreadSynch *w) { } static GraphId GetGraphIdLocked(Mutex *mu) - EXCLUSIVE_LOCKS_REQUIRED(deadlock_graph_mu) { + ABSL_EXCLUSIVE_LOCKS_REQUIRED(deadlock_graph_mu) { if (!deadlock_graph) { // (re)create the deadlock graph. deadlock_graph = new (base_internal::LowLevelAlloc::Alloc(sizeof(*deadlock_graph))) @@ -1153,7 +1156,7 @@ static GraphId GetGraphIdLocked(Mutex *mu) return deadlock_graph->GetId(mu); } -static GraphId GetGraphId(Mutex *mu) LOCKS_EXCLUDED(deadlock_graph_mu) { +static GraphId GetGraphId(Mutex *mu) ABSL_LOCKS_EXCLUDED(deadlock_graph_mu) { deadlock_graph_mu.Lock(); GraphId id = GetGraphIdLocked(mu); deadlock_graph_mu.Unlock(); @@ -2721,5 +2724,5 @@ bool Condition::GuaranteedEqual(const Condition *a, const Condition *b) { a->arg_ == b->arg_ && a->method_ == b->method_; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index d6890099..8c70c4ce 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -82,7 +82,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN class Condition; struct SynchWaitParams; @@ -136,7 +136,7 @@ struct SynchWaitParams; // // See also `MutexLock`, below, for scoped `Mutex` acquisition. -class LOCKABLE Mutex { +class ABSL_LOCKABLE Mutex { public: // Creates a `Mutex` that is not held by anyone. This constructor is // typically used for Mutexes allocated on the heap or the stack. @@ -165,27 +165,27 @@ class LOCKABLE Mutex { // // Blocks the calling thread, if necessary, until this `Mutex` is free, and // then acquires it exclusively. (This lock is also known as a "write lock.") - void Lock() EXCLUSIVE_LOCK_FUNCTION(); + void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(); // Mutex::Unlock() // // Releases this `Mutex` and returns it from the exclusive/write state to the // free state. Caller must hold the `Mutex` exclusively. - void Unlock() UNLOCK_FUNCTION(); + void Unlock() ABSL_UNLOCK_FUNCTION(); // Mutex::TryLock() // // If the mutex can be acquired without blocking, does so exclusively and // returns `true`. Otherwise, returns `false`. Returns `true` with high // probability if the `Mutex` was free. - bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true); + bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true); // Mutex::AssertHeld() // // Return immediately if this thread holds the `Mutex` exclusively (in write // mode). Otherwise, may report an error (typically by crashing with a // diagnostic), or may return immediately. - void AssertHeld() const ASSERT_EXCLUSIVE_LOCK(); + void AssertHeld() const ABSL_ASSERT_EXCLUSIVE_LOCK(); // --------------------------------------------------------------------------- // Reader-Writer Locking @@ -226,28 +226,28 @@ class LOCKABLE Mutex { // `ReaderLock()` will block if some other thread has an exclusive/writer lock // on the mutex. - void ReaderLock() SHARED_LOCK_FUNCTION(); + void ReaderLock() ABSL_SHARED_LOCK_FUNCTION(); // Mutex::ReaderUnlock() // // Releases a read share of this `Mutex`. `ReaderUnlock` may return a mutex to // the free state if this thread holds the last reader lock on the mutex. Note // that you cannot call `ReaderUnlock()` on a mutex held in write mode. - void ReaderUnlock() UNLOCK_FUNCTION(); + void ReaderUnlock() ABSL_UNLOCK_FUNCTION(); // Mutex::ReaderTryLock() // // If the mutex can be acquired without blocking, acquires this mutex for // shared access and returns `true`. Otherwise, returns `false`. Returns // `true` with high probability if the `Mutex` was free or shared. - bool ReaderTryLock() SHARED_TRYLOCK_FUNCTION(true); + bool ReaderTryLock() ABSL_SHARED_TRYLOCK_FUNCTION(true); // Mutex::AssertReaderHeld() // // Returns immediately if this thread holds the `Mutex` in at least shared // mode (read mode). Otherwise, may report an error (typically by // crashing with a diagnostic), or may return immediately. - void AssertReaderHeld() const ASSERT_SHARED_LOCK(); + void AssertReaderHeld() const ABSL_ASSERT_SHARED_LOCK(); // Mutex::WriterLock() // Mutex::WriterUnlock() @@ -258,11 +258,11 @@ class LOCKABLE Mutex { // These methods may be used (along with the complementary `Reader*()` // methods) to distingish simple exclusive `Mutex` usage (`Lock()`, // etc.) from reader/writer lock usage. - void WriterLock() EXCLUSIVE_LOCK_FUNCTION() { this->Lock(); } + void WriterLock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { this->Lock(); } - void WriterUnlock() UNLOCK_FUNCTION() { this->Unlock(); } + void WriterUnlock() ABSL_UNLOCK_FUNCTION() { this->Unlock(); } - bool WriterTryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) { + bool WriterTryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) { return this->TryLock(); } @@ -316,11 +316,11 @@ class LOCKABLE Mutex { // be acquired, then atomically acquires this `Mutex`. `LockWhen()` is // logically equivalent to `*Lock(); Await();` though they may have different // performance characteristics. - void LockWhen(const Condition &cond) EXCLUSIVE_LOCK_FUNCTION(); + void LockWhen(const Condition &cond) ABSL_EXCLUSIVE_LOCK_FUNCTION(); - void ReaderLockWhen(const Condition &cond) SHARED_LOCK_FUNCTION(); + void ReaderLockWhen(const Condition &cond) ABSL_SHARED_LOCK_FUNCTION(); - void WriterLockWhen(const Condition &cond) EXCLUSIVE_LOCK_FUNCTION() { + void WriterLockWhen(const Condition &cond) ABSL_EXCLUSIVE_LOCK_FUNCTION() { this->LockWhen(cond); } @@ -362,11 +362,11 @@ class LOCKABLE Mutex { // // Negative timeouts are equivalent to a zero timeout. bool LockWhenWithTimeout(const Condition &cond, absl::Duration timeout) - EXCLUSIVE_LOCK_FUNCTION(); + ABSL_EXCLUSIVE_LOCK_FUNCTION(); bool ReaderLockWhenWithTimeout(const Condition &cond, absl::Duration timeout) - SHARED_LOCK_FUNCTION(); + ABSL_SHARED_LOCK_FUNCTION(); bool WriterLockWhenWithTimeout(const Condition &cond, absl::Duration timeout) - EXCLUSIVE_LOCK_FUNCTION() { + ABSL_EXCLUSIVE_LOCK_FUNCTION() { return this->LockWhenWithTimeout(cond, timeout); } @@ -382,11 +382,11 @@ class LOCKABLE Mutex { // // Deadlines in the past are equivalent to an immediate deadline. bool LockWhenWithDeadline(const Condition &cond, absl::Time deadline) - EXCLUSIVE_LOCK_FUNCTION(); + ABSL_EXCLUSIVE_LOCK_FUNCTION(); bool ReaderLockWhenWithDeadline(const Condition &cond, absl::Time deadline) - SHARED_LOCK_FUNCTION(); + ABSL_SHARED_LOCK_FUNCTION(); bool WriterLockWhenWithDeadline(const Condition &cond, absl::Time deadline) - EXCLUSIVE_LOCK_FUNCTION() { + ABSL_EXCLUSIVE_LOCK_FUNCTION() { return this->LockWhenWithDeadline(cond, deadline); } @@ -536,9 +536,9 @@ class LOCKABLE Mutex { // private: // Mutex lock_; // }; -class SCOPED_LOCKABLE MutexLock { +class ABSL_SCOPED_LOCKABLE MutexLock { public: - explicit MutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { + explicit MutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { this->mu_->Lock(); } @@ -547,7 +547,7 @@ class SCOPED_LOCKABLE MutexLock { MutexLock& operator=(const MutexLock&) = delete; MutexLock& operator=(MutexLock&&) = delete; - ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } + ~MutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->Unlock(); } private: Mutex *const mu_; @@ -557,10 +557,9 @@ class SCOPED_LOCKABLE MutexLock { // // The `ReaderMutexLock` is a helper class, like `MutexLock`, which acquires and // releases a shared lock on a `Mutex` via RAII. -class SCOPED_LOCKABLE ReaderMutexLock { +class ABSL_SCOPED_LOCKABLE ReaderMutexLock { public: - explicit ReaderMutexLock(Mutex *mu) SHARED_LOCK_FUNCTION(mu) - : mu_(mu) { + explicit ReaderMutexLock(Mutex *mu) ABSL_SHARED_LOCK_FUNCTION(mu) : mu_(mu) { mu->ReaderLock(); } @@ -569,9 +568,7 @@ class SCOPED_LOCKABLE ReaderMutexLock { ReaderMutexLock& operator=(const ReaderMutexLock&) = delete; ReaderMutexLock& operator=(ReaderMutexLock&&) = delete; - ~ReaderMutexLock() UNLOCK_FUNCTION() { - this->mu_->ReaderUnlock(); - } + ~ReaderMutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->ReaderUnlock(); } private: Mutex *const mu_; @@ -581,9 +578,9 @@ class SCOPED_LOCKABLE ReaderMutexLock { // // The `WriterMutexLock` is a helper class, like `MutexLock`, which acquires and // releases a write (exclusive) lock on a `Mutex` via RAII. -class SCOPED_LOCKABLE WriterMutexLock { +class ABSL_SCOPED_LOCKABLE WriterMutexLock { public: - explicit WriterMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) + explicit WriterMutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { mu->WriterLock(); } @@ -593,9 +590,7 @@ class SCOPED_LOCKABLE WriterMutexLock { WriterMutexLock& operator=(const WriterMutexLock&) = delete; WriterMutexLock& operator=(WriterMutexLock&&) = delete; - ~WriterMutexLock() UNLOCK_FUNCTION() { - this->mu_->WriterUnlock(); - } + ~WriterMutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->WriterUnlock(); } private: Mutex *const mu_; @@ -634,7 +629,7 @@ class SCOPED_LOCKABLE WriterMutexLock { // Example: // // // assume count_ is not internal reference count -// int count_ GUARDED_BY(mu_); +// int count_ ABSL_GUARDED_BY(mu_); // // mu_.LockWhen(Condition(+[](int* count) { return *count == 0; }, // &count_)); @@ -861,13 +856,18 @@ class CondVar { // MutexLockMaybe // // MutexLockMaybe is like MutexLock, but is a no-op when mu is null. -class SCOPED_LOCKABLE MutexLockMaybe { +class ABSL_SCOPED_LOCKABLE MutexLockMaybe { public: - explicit MutexLockMaybe(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) - : mu_(mu) { if (this->mu_ != nullptr) { this->mu_->Lock(); } } - ~MutexLockMaybe() UNLOCK_FUNCTION() { + explicit MutexLockMaybe(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + if (this->mu_ != nullptr) { + this->mu_->Lock(); + } + } + ~MutexLockMaybe() ABSL_UNLOCK_FUNCTION() { if (this->mu_ != nullptr) { this->mu_->Unlock(); } } + private: Mutex *const mu_; MutexLockMaybe(const MutexLockMaybe&) = delete; @@ -880,17 +880,17 @@ class SCOPED_LOCKABLE MutexLockMaybe { // // ReleasableMutexLock is like MutexLock, but permits `Release()` of its // mutex before destruction. `Release()` may be called at most once. -class SCOPED_LOCKABLE ReleasableMutexLock { +class ABSL_SCOPED_LOCKABLE ReleasableMutexLock { public: - explicit ReleasableMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) + explicit ReleasableMutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { this->mu_->Lock(); } - ~ReleasableMutexLock() UNLOCK_FUNCTION() { + ~ReleasableMutexLock() ABSL_UNLOCK_FUNCTION() { if (this->mu_ != nullptr) { this->mu_->Unlock(); } } - void Release() UNLOCK_FUNCTION(); + void Release() ABSL_UNLOCK_FUNCTION(); private: Mutex *mu_; @@ -1001,7 +1001,7 @@ void RegisterCondVarTracer(void (*fn)(const char *msg, const void *cv)); // // 'pc' is the program counter being symbolized, 'out' is the buffer to write // into, and 'out_size' is the size of the buffer. This function can return -// false if symbolizing failed, or true if a null-terminated symbol was written +// false if symbolizing failed, or true if a NUL-terminated symbol was written // to 'out.' // // This has the same memory ordering concerns as RegisterMutexProfiler() above. @@ -1040,7 +1040,7 @@ enum class OnDeadlockCycle { // the manner chosen here. void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // In some build configurations we pass --detect-odr-violations to the diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc index 9851ac19..afb363af 100644 --- a/absl/synchronization/mutex_test.cc +++ b/absl/synchronization/mutex_test.cc @@ -425,10 +425,10 @@ TEST(Mutex, CondVarWaitSignalsAwait) { // Use a struct so the lock annotations apply. struct { absl::Mutex barrier_mu; - bool barrier GUARDED_BY(barrier_mu) = false; + bool barrier ABSL_GUARDED_BY(barrier_mu) = false; absl::Mutex release_mu; - bool release GUARDED_BY(release_mu) = false; + bool release ABSL_GUARDED_BY(release_mu) = false; absl::CondVar released_cv; } state; @@ -466,10 +466,10 @@ TEST(Mutex, CondVarWaitWithTimeoutSignalsAwait) { // Use a struct so the lock annotations apply. struct { absl::Mutex barrier_mu; - bool barrier GUARDED_BY(barrier_mu) = false; + bool barrier ABSL_GUARDED_BY(barrier_mu) = false; absl::Mutex release_mu; - bool release GUARDED_BY(release_mu) = false; + bool release ABSL_GUARDED_BY(release_mu) = false; absl::CondVar released_cv; } state; @@ -770,7 +770,7 @@ static void GetReadLock(ReaderDecrementBugStruct *x) { // Test for reader counter being decremented incorrectly by waiter // with false condition. -TEST(Mutex, MutexReaderDecrementBug) NO_THREAD_SAFETY_ANALYSIS { +TEST(Mutex, MutexReaderDecrementBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { ReaderDecrementBugStruct x; x.cond = false; x.waiting_on_cond = false; @@ -819,7 +819,7 @@ TEST(Mutex, MutexReaderDecrementBug) NO_THREAD_SAFETY_ANALYSIS { // TSAN reports errors when locked Mutexes are destroyed. TEST(Mutex, DISABLED_LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS { #else -TEST(Mutex, LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS { +TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { #endif for (int i = 0; i != 10; i++) { // Create, lock and destroy 10 locks. @@ -1101,7 +1101,7 @@ TEST(Mutex, DeadlockDetectorBazelWarning) { // annotation-based static thread-safety analysis is not currently // predicate-aware and cannot tell if the two for-loops that acquire and // release the locks have the same predicates. -TEST(Mutex, DeadlockDetectorStessTest) NO_THREAD_SAFETY_ANALYSIS { +TEST(Mutex, DeadlockDetectorStessTest) ABSL_NO_THREAD_SAFETY_ANALYSIS { // Stress test: Here we create a large number of locks and use all of them. // If a deadlock detector keeps a full graph of lock acquisition order, // it will likely be too slow for this test to pass. @@ -1123,7 +1123,7 @@ TEST(Mutex, DeadlockDetectorStessTest) NO_THREAD_SAFETY_ANALYSIS { // TSAN reports errors when locked Mutexes are destroyed. TEST(Mutex, DISABLED_DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS { #else -TEST(Mutex, DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS { +TEST(Mutex, DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { #endif // Test a scenario where a cached deadlock graph node id in the // list of held locks is not invalidated when the corresponding diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc index d691cfca..e91b9038 100644 --- a/absl/synchronization/notification.cc +++ b/absl/synchronization/notification.cc @@ -22,7 +22,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN void Notification::Notify() { MutexLock l(&this->mutex_); @@ -45,15 +45,6 @@ Notification::~Notification() { MutexLock l(&this->mutex_); } -static inline bool HasBeenNotifiedInternal( - const std::atomic<bool> *notified_yet) { - return notified_yet->load(std::memory_order_acquire); -} - -bool Notification::HasBeenNotified() const { - return HasBeenNotifiedInternal(&this->notified_yet_); -} - void Notification::WaitForNotification() const { if (!HasBeenNotifiedInternal(&this->notified_yet_)) { this->mutex_.LockWhen(Condition(&HasBeenNotifiedInternal, @@ -83,5 +74,5 @@ bool Notification::WaitForNotificationWithDeadline(absl::Time deadline) const { return notified; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h index 8ed7f12a..9a354ca2 100644 --- a/absl/synchronization/notification.h +++ b/absl/synchronization/notification.h @@ -57,7 +57,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // Notification @@ -74,7 +74,9 @@ class Notification { // Notification::HasBeenNotified() // // Returns the value of the notification's internal "notified" state. - bool HasBeenNotified() const; + bool HasBeenNotified() const { + return HasBeenNotifiedInternal(&this->notified_yet_); + } // Notification::WaitForNotification() // @@ -106,11 +108,16 @@ class Notification { void Notify(); private: + static inline bool HasBeenNotifiedInternal( + const std::atomic<bool>* notified_yet) { + return notified_yet->load(std::memory_order_acquire); + } + mutable Mutex mutex_; std::atomic<bool> notified_yet_; // written under mutex_ }; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_SYNCHRONIZATION_NOTIFICATION_H_ diff --git a/absl/synchronization/notification_test.cc b/absl/synchronization/notification_test.cc index a64674ca..100ea76f 100644 --- a/absl/synchronization/notification_test.cc +++ b/absl/synchronization/notification_test.cc @@ -21,7 +21,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // A thread-safe class that holds a counter. class ThreadSafeCounter { @@ -129,5 +129,5 @@ TEST(NotificationTest, SanityTest) { BasicTests(true, &local_notification2); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index 55e83a8c..9ab2adb8 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -14,6 +14,7 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -46,6 +47,7 @@ cc_library( deps = [ "//absl/base", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/numeric:int128", "//absl/strings", "//absl/time/internal/cctz:civil_time", @@ -68,7 +70,7 @@ cc_library( ], deps = [ ":time", - "//absl/base", + "//absl/base:raw_logging_internal", "//absl/time/internal/cctz:time_zone", "@com_google_googletest//:gtest", ], @@ -89,9 +91,9 @@ cc_test( deps = [ ":test_util", ":time", - "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/numeric:int128", "//absl/time/internal/cctz:time_zone", "@com_google_googletest//:gtest_main", ], diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt index 59098321..853563e8 100644 --- a/absl/time/CMakeLists.txt +++ b/absl/time/CMakeLists.txt @@ -22,21 +22,22 @@ absl_cc_library( "clock.h" "time.h" SRCS - "civil_time.cc" - "clock.cc" - "duration.cc" - "format.cc" - "internal/get_current_time_chrono.inc" - "internal/get_current_time_posix.inc" - "time.cc" + "civil_time.cc" + "clock.cc" + "duration.cc" + "format.cc" + "internal/get_current_time_chrono.inc" + "internal/get_current_time_posix.inc" + "time.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS absl::base + absl::civil_time absl::core_headers absl::int128 + absl::raw_logging_internal absl::strings - absl::civil_time absl::time_zone PUBLIC ) @@ -88,7 +89,7 @@ absl_cc_library( absl_cc_library( NAME - test_util + time_internal_test_util HDRS "internal/test_util.h" SRCS @@ -98,7 +99,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::time - absl::base + absl::raw_logging_internal absl::time_zone gmock TESTONLY @@ -117,9 +118,8 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS - absl::test_util + absl::time_internal_test_util absl::time - absl::base absl::config absl::core_headers absl::time_zone diff --git a/absl/time/civil_time.cc b/absl/time/civil_time.cc index bf7ba5ab..ada82cbc 100644 --- a/absl/time/civil_time.cc +++ b/absl/time/civil_time.cc @@ -21,7 +21,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { @@ -43,6 +43,58 @@ std::string FormatYearAnd(string_view fmt, CivilSecond cs) { FormatTime(std::string(fmt), FromCivil(ncs, utc), utc)); } +template <typename CivilT> +bool ParseYearAnd(string_view fmt, string_view s, CivilT* c) { + // Civil times support a larger year range than absl::Time, so we need to + // parse the year separately, normalize it, then use absl::ParseTime on the + // normalized std::string. + const std::string ss = std::string(s); // TODO(absl-team): Avoid conversion. + const char* const np = ss.c_str(); + char* endp; + errno = 0; + const civil_year_t y = + std::strtoll(np, &endp, 10); // NOLINT(runtime/deprecated_fn) + if (endp == np || errno == ERANGE) return false; + const std::string norm = StrCat(NormalizeYear(y), endp); + + const TimeZone utc = UTCTimeZone(); + Time t; + if (ParseTime(StrCat("%Y", fmt), norm, utc, &t, nullptr)) { + const auto cs = ToCivilSecond(t, utc); + *c = CivilT(y, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second()); + return true; + } + + return false; +} + +// Tries to parse the type as a CivilT1, but then assigns the result to the +// argument of type CivilT2. +template <typename CivilT1, typename CivilT2> +bool ParseAs(string_view s, CivilT2* c) { + CivilT1 t1; + if (ParseCivilTime(s, &t1)) { + *c = CivilT2(t1); + return true; + } + return false; +} + +template <typename CivilT> +bool ParseLenient(string_view s, CivilT* c) { + // A fastpath for when the given std::string data parses exactly into the given + // type T (e.g., s="YYYY-MM-DD" and CivilT=CivilDay). + if (ParseCivilTime(s, c)) return true; + // Try parsing as each of the 6 types, trying the most common types first + // (based on csearch results). + if (ParseAs<CivilDay>(s, c)) return true; + if (ParseAs<CivilSecond>(s, c)) return true; + if (ParseAs<CivilHour>(s, c)) return true; + if (ParseAs<CivilMonth>(s, c)) return true; + if (ParseAs<CivilMinute>(s, c)) return true; + if (ParseAs<CivilYear>(s, c)) return true; + return false; +} } // namespace std::string FormatCivilTime(CivilSecond c) { @@ -58,6 +110,44 @@ std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); } std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); } std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); } +bool ParseCivilTime(string_view s, CivilSecond* c) { + return ParseYearAnd("-%m-%dT%H:%M:%S", s, c); +} +bool ParseCivilTime(string_view s, CivilMinute* c) { + return ParseYearAnd("-%m-%dT%H:%M", s, c); +} +bool ParseCivilTime(string_view s, CivilHour* c) { + return ParseYearAnd("-%m-%dT%H", s, c); +} +bool ParseCivilTime(string_view s, CivilDay* c) { + return ParseYearAnd("-%m-%d", s, c); +} +bool ParseCivilTime(string_view s, CivilMonth* c) { + return ParseYearAnd("-%m", s, c); +} +bool ParseCivilTime(string_view s, CivilYear* c) { + return ParseYearAnd("", s, c); +} + +bool ParseLenientCivilTime(string_view s, CivilSecond* c) { + return ParseLenient(s, c); +} +bool ParseLenientCivilTime(string_view s, CivilMinute* c) { + return ParseLenient(s, c); +} +bool ParseLenientCivilTime(string_view s, CivilHour* c) { + return ParseLenient(s, c); +} +bool ParseLenientCivilTime(string_view s, CivilDay* c) { + return ParseLenient(s, c); +} +bool ParseLenientCivilTime(string_view s, CivilMonth* c) { + return ParseLenient(s, c); +} +bool ParseLenientCivilTime(string_view s, CivilYear* c) { + return ParseLenient(s, c); +} + namespace time_internal { std::ostream& operator<<(std::ostream& os, CivilYear y) { @@ -81,5 +171,5 @@ std::ostream& operator<<(std::ostream& os, CivilSecond s) { } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/civil_time.h b/absl/time/civil_time.h index f0be303c..bb460044 100644 --- a/absl/time/civil_time.h +++ b/absl/time/civil_time.h @@ -76,7 +76,7 @@ #include "absl/time/internal/cctz/include/cctz/civil_time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { struct second_tag : cctz::detail::second_tag {}; @@ -248,7 +248,7 @@ struct year_tag : month_tag, cctz::detail::year_tag {}; // int minute() // int second() // -// Recall that fields inferior to the type's aligment will be set to their +// Recall that fields inferior to the type's alignment will be set to their // minimum valid value. // // Example: @@ -460,6 +460,57 @@ std::string FormatCivilTime(CivilDay c); std::string FormatCivilTime(CivilMonth c); std::string FormatCivilTime(CivilYear c); +// absl::ParseCivilTime() +// +// Parses a civil-time value from the specified `absl::string_view` into the +// passed output parameter. Returns `true` upon successful parsing. +// +// The expected form of the input string is as follows: +// +// Type | Format +// --------------------------------- +// CivilSecond | YYYY-MM-DDTHH:MM:SS +// CivilMinute | YYYY-MM-DDTHH:MM +// CivilHour | YYYY-MM-DDTHH +// CivilDay | YYYY-MM-DD +// CivilMonth | YYYY-MM +// CivilYear | YYYY +// +// Example: +// +// absl::CivilDay d; +// bool ok = absl::ParseCivilTime("2018-01-02", &d); // OK +// +// Note that parsing will fail if the string's format does not match the +// expected type exactly. `ParseLenientCivilTime()` below is more lenient. +// +bool ParseCivilTime(absl::string_view s, CivilSecond* c); +bool ParseCivilTime(absl::string_view s, CivilMinute* c); +bool ParseCivilTime(absl::string_view s, CivilHour* c); +bool ParseCivilTime(absl::string_view s, CivilDay* c); +bool ParseCivilTime(absl::string_view s, CivilMonth* c); +bool ParseCivilTime(absl::string_view s, CivilYear* c); + +// ParseLenientCivilTime() +// +// Parses any of the formats accepted by `absl::ParseCivilTime()`, but is more +// lenient if the format of the string does not exactly match the associated +// type. +// +// Example: +// +// absl::CivilDay d; +// bool ok = absl::ParseLenientCivilTime("1969-07-20", &d); // OK +// ok = absl::ParseLenientCivilTime("1969-07-20T10", &d); // OK: T10 floored +// ok = absl::ParseLenientCivilTime("1969-07", &d); // OK: day defaults to 1 +// +bool ParseLenientCivilTime(absl::string_view s, CivilSecond* c); +bool ParseLenientCivilTime(absl::string_view s, CivilMinute* c); +bool ParseLenientCivilTime(absl::string_view s, CivilHour* c); +bool ParseLenientCivilTime(absl::string_view s, CivilDay* c); +bool ParseLenientCivilTime(absl::string_view s, CivilMonth* c); +bool ParseLenientCivilTime(absl::string_view s, CivilYear* c); + namespace time_internal { // For functions found via ADL on civil-time tags. // Streaming Operators @@ -469,7 +520,7 @@ namespace time_internal { // For functions found via ADL on civil-time tags. // // Example: // -// absl::CivilDay d = absl::CivilDay("1969-07-20"); +// absl::CivilDay d = absl::CivilDay(1969, 7, 20); // std::cout << "Date is: " << d << "\n"; // std::ostream& operator<<(std::ostream& os, CivilYear y); @@ -481,7 +532,7 @@ std::ostream& operator<<(std::ostream& os, CivilSecond s); } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_CIVIL_TIME_H_ diff --git a/absl/time/civil_time_benchmark.cc b/absl/time/civil_time_benchmark.cc index 40869835..f04dbe20 100644 --- a/absl/time/civil_time_benchmark.cc +++ b/absl/time/civil_time_benchmark.cc @@ -66,6 +66,26 @@ void BM_Format(benchmark::State& state) { } BENCHMARK(BM_Format); +void BM_Parse(benchmark::State& state) { + const std::string f = "2014-01-02T03:04:05"; + absl::CivilSecond c; + while (state.KeepRunning()) { + const bool b = absl::ParseCivilTime(f, &c); + benchmark::DoNotOptimize(b); + } +} +BENCHMARK(BM_Parse); + +void BM_RoundTripFormatParse(benchmark::State& state) { + const absl::CivilSecond c(2014, 1, 2, 3, 4, 5); + absl::CivilSecond out; + while (state.KeepRunning()) { + const bool b = absl::ParseCivilTime(absl::FormatCivilTime(c), &out); + benchmark::DoNotOptimize(b); + } +} +BENCHMARK(BM_RoundTripFormatParse); + template <typename T> void BM_CivilTimeAbslHash(benchmark::State& state) { const int kSize = 100000; diff --git a/absl/time/civil_time_test.cc b/absl/time/civil_time_test.cc index 03cd1f12..0ebd97ad 100644 --- a/absl/time/civil_time_test.cc +++ b/absl/time/civil_time_test.cc @@ -690,6 +690,69 @@ TEST(CivilTime, Format) { EXPECT_EQ("1970", absl::FormatCivilTime(y)); } +TEST(CivilTime, Parse) { + absl::CivilSecond ss; + absl::CivilMinute mm; + absl::CivilHour hh; + absl::CivilDay d; + absl::CivilMonth m; + absl::CivilYear y; + + // CivilSecond OK; others fail + EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03:04:05", &ss)); + EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(ss)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &mm)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &hh)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &d)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &m)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &y)); + + // CivilMinute OK; others fail + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &ss)); + EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03:04", &mm)); + EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(mm)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &hh)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &d)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &m)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &y)); + + // CivilHour OK; others fail + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &ss)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &mm)); + EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03", &hh)); + EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hh)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &d)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &m)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &y)); + + // CivilDay OK; others fail + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &ss)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &mm)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &hh)); + EXPECT_TRUE(absl::ParseCivilTime("2015-01-02", &d)); + EXPECT_EQ("2015-01-02", absl::FormatCivilTime(d)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &m)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &y)); + + // CivilMonth OK; others fail + EXPECT_FALSE(absl::ParseCivilTime("2015-01", &ss)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01", &mm)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01", &hh)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01", &d)); + EXPECT_TRUE(absl::ParseCivilTime("2015-01", &m)); + EXPECT_EQ("2015-01", absl::FormatCivilTime(m)); + EXPECT_FALSE(absl::ParseCivilTime("2015-01", &y)); + + // CivilYear OK; others fail + EXPECT_FALSE(absl::ParseCivilTime("2015", &ss)); + EXPECT_FALSE(absl::ParseCivilTime("2015", &mm)); + EXPECT_FALSE(absl::ParseCivilTime("2015", &hh)); + EXPECT_FALSE(absl::ParseCivilTime("2015", &d)); + EXPECT_FALSE(absl::ParseCivilTime("2015", &m)); + EXPECT_TRUE(absl::ParseCivilTime("2015", &y)); + EXPECT_EQ("2015", absl::FormatCivilTime(y)); +} + TEST(CivilTime, FormatAndParseLenient) { absl::CivilSecond ss; EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(ss)); @@ -708,6 +771,101 @@ TEST(CivilTime, FormatAndParseLenient) { absl::CivilYear y; EXPECT_EQ("1970", absl::FormatCivilTime(y)); + + EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &ss)); + EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(ss)); + + EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &mm)); + EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(mm)); + + EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &hh)); + EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hh)); + + EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &d)); + EXPECT_EQ("2015-01-02", absl::FormatCivilTime(d)); + + EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &m)); + EXPECT_EQ("2015-01", absl::FormatCivilTime(m)); + + EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &y)); + EXPECT_EQ("2015", absl::FormatCivilTime(y)); +} + +TEST(CivilTime, ParseEdgeCases) { + absl::CivilSecond ss; + EXPECT_TRUE( + absl::ParseLenientCivilTime("9223372036854775807-12-31T23:59:59", &ss)); + EXPECT_EQ("9223372036854775807-12-31T23:59:59", absl::FormatCivilTime(ss)); + EXPECT_TRUE( + absl::ParseLenientCivilTime("-9223372036854775808-01-01T00:00:00", &ss)); + EXPECT_EQ("-9223372036854775808-01-01T00:00:00", absl::FormatCivilTime(ss)); + + absl::CivilMinute mm; + EXPECT_TRUE( + absl::ParseLenientCivilTime("9223372036854775807-12-31T23:59", &mm)); + EXPECT_EQ("9223372036854775807-12-31T23:59", absl::FormatCivilTime(mm)); + EXPECT_TRUE( + absl::ParseLenientCivilTime("-9223372036854775808-01-01T00:00", &mm)); + EXPECT_EQ("-9223372036854775808-01-01T00:00", absl::FormatCivilTime(mm)); + + absl::CivilHour hh; + EXPECT_TRUE( + absl::ParseLenientCivilTime("9223372036854775807-12-31T23", &hh)); + EXPECT_EQ("9223372036854775807-12-31T23", absl::FormatCivilTime(hh)); + EXPECT_TRUE( + absl::ParseLenientCivilTime("-9223372036854775808-01-01T00", &hh)); + EXPECT_EQ("-9223372036854775808-01-01T00", absl::FormatCivilTime(hh)); + + absl::CivilDay d; + EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807-12-31", &d)); + EXPECT_EQ("9223372036854775807-12-31", absl::FormatCivilTime(d)); + EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808-01-01", &d)); + EXPECT_EQ("-9223372036854775808-01-01", absl::FormatCivilTime(d)); + + absl::CivilMonth m; + EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807-12", &m)); + EXPECT_EQ("9223372036854775807-12", absl::FormatCivilTime(m)); + EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808-01", &m)); + EXPECT_EQ("-9223372036854775808-01", absl::FormatCivilTime(m)); + + absl::CivilYear y; + EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807", &y)); + EXPECT_EQ("9223372036854775807", absl::FormatCivilTime(y)); + EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808", &y)); + EXPECT_EQ("-9223372036854775808", absl::FormatCivilTime(y)); + + // Tests some valid, but interesting, cases + EXPECT_TRUE(absl::ParseLenientCivilTime("0", &ss)) << ss; + EXPECT_EQ(absl::CivilYear(0), ss); + EXPECT_TRUE(absl::ParseLenientCivilTime("0-1", &ss)) << ss; + EXPECT_EQ(absl::CivilMonth(0, 1), ss); + EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015 ", &ss)) << ss; + EXPECT_EQ(absl::CivilYear(2015), ss); + EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-6 ", &ss)) << ss; + EXPECT_EQ(absl::CivilMonth(2015, 6), ss); + EXPECT_TRUE(absl::ParseLenientCivilTime("2015-6-7", &ss)) << ss; + EXPECT_EQ(absl::CivilDay(2015, 6, 7), ss); + EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-6-7 ", &ss)) << ss; + EXPECT_EQ(absl::CivilDay(2015, 6, 7), ss); + EXPECT_TRUE(absl::ParseLenientCivilTime("2015-06-07T10:11:12 ", &ss)) << ss; + EXPECT_EQ(absl::CivilSecond(2015, 6, 7, 10, 11, 12), ss); + EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-06-07T10:11:12 ", &ss)) << ss; + EXPECT_EQ(absl::CivilSecond(2015, 6, 7, 10, 11, 12), ss); + EXPECT_TRUE(absl::ParseLenientCivilTime("-01-01", &ss)) << ss; + EXPECT_EQ(absl::CivilMonth(-1, 1), ss); + + // Tests some invalid cases + EXPECT_FALSE(absl::ParseLenientCivilTime("01-01-2015", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("2015-", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("0xff-01", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-30T04:05:06", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03T04:05:96", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("X2015-02-03T04:05:06", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03T04:05:003", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("2015 -02-03T04:05:06", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03-04:05:06", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("2015:02:03T04-05-06", &ss)) << ss; + EXPECT_FALSE(absl::ParseLenientCivilTime("9223372036854775808", &y)) << y; } TEST(CivilTime, OutputStream) { diff --git a/absl/time/clock.cc b/absl/time/clock.cc index 48dc4450..3b895c38 100644 --- a/absl/time/clock.cc +++ b/absl/time/clock.cc @@ -34,7 +34,7 @@ #include "absl/base/thread_annotations.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN Time Now() { // TODO(bww): Get a timespec instead so we don't have to divide. int64_t n = absl::GetCurrentTimeNanos(); @@ -44,7 +44,7 @@ Time Now() { } return time_internal::FromUnixDuration(absl::Nanoseconds(n)); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // Decide if we should use the fast GetCurrentTimeNanos() algorithm @@ -73,11 +73,11 @@ Time Now() { #if !ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN int64_t GetCurrentTimeNanos() { return GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #else // Use the cyclecounter-based implementation below. @@ -95,7 +95,7 @@ static int64_t stats_slow_paths; static int64_t stats_fast_slow_paths; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { // This is a friend wrapper around UnscaledCycleClock::Now() // (needed to access UnscaledCycleClock). @@ -390,7 +390,7 @@ static uint64_t UpdateLastSample( // TODO(absl-team): Remove this attribute when our compiler is smart enough // to do the right thing. ABSL_ATTRIBUTE_NOINLINE -static int64_t GetCurrentTimeNanosSlowPath() LOCKS_EXCLUDED(lock) { +static int64_t GetCurrentTimeNanosSlowPath() ABSL_LOCKS_EXCLUDED(lock) { // Serialize access to slow-path. Fast-path readers are not blocked yet, and // code below must not modify last_sample until the seqlock is acquired. lock.Lock(); @@ -435,7 +435,7 @@ static int64_t GetCurrentTimeNanosSlowPath() LOCKS_EXCLUDED(lock) { static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, uint64_t delta_cycles, const struct TimeSample *sample) - EXCLUSIVE_LOCKS_REQUIRED(lock) { + ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock) { uint64_t estimated_base_ns = now_ns; uint64_t lock_value = SeqAcquire(&seq); // acquire seqlock to block readers @@ -454,7 +454,7 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed); stats_initializations++; } else if (sample->raw_ns + 500 * 1000 * 1000 < now_ns && - sample->base_cycles + 100 < now_cycles) { + sample->base_cycles + 50 < now_cycles) { // Enough time has passed to compute the cycle time. if (sample->nsscaled_per_cycle != 0) { // Have a cycle time estimate. // Compute time from counter reading, but avoiding overflow @@ -520,12 +520,12 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, return estimated_base_ns; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { // Returns the maximum duration that SleepOnce() can sleep for. @@ -553,7 +553,7 @@ void SleepOnce(absl::Duration to_sleep) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl extern "C" { diff --git a/absl/time/clock.h b/absl/time/clock.h index a3b9ffe9..27764a92 100644 --- a/absl/time/clock.h +++ b/absl/time/clock.h @@ -26,7 +26,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Now() // @@ -50,7 +50,7 @@ int64_t GetCurrentTimeNanos(); // * Returns immediately when passed a nonpositive duration. void SleepFor(absl::Duration duration); -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl // ----------------------------------------------------------------------------- diff --git a/absl/time/duration.cc b/absl/time/duration.cc index 6a51baff..b1af8406 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -71,7 +71,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { @@ -198,11 +198,11 @@ inline int64_t DecodeTwosComp(uint64_t v) { return absl::bit_cast<int64_t>(v); } // double as overflow cases. inline bool SafeAddRepHi(double a_hi, double b_hi, Duration* d) { double c = a_hi + b_hi; - if (c >= kint64max) { + if (c >= static_cast<double>(kint64max)) { *d = InfiniteDuration(); return false; } - if (c <= kint64min) { + if (c <= static_cast<double>(kint64min)) { *d = -InfiniteDuration(); return false; } @@ -907,11 +907,16 @@ bool ParseDuration(const std::string& dur_string, Duration* d) { return true; } +bool AbslParseFlag(absl::string_view text, Duration* dst, std::string*) { + return ParseDuration(std::string(text), dst); +} + +std::string AbslUnparseFlag(Duration d) { return FormatDuration(d); } bool ParseFlag(const std::string& text, Duration* dst, std::string* ) { return ParseDuration(text, dst); } std::string UnparseFlag(Duration d) { return FormatDuration(d); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index 5dce9ac8..4d85a2c4 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -762,11 +762,6 @@ TEST(Duration, DivisionByZero) { const double dbl_inf = std::numeric_limits<double>::infinity(); const double dbl_denorm = std::numeric_limits<double>::denorm_min(); - // IEEE 754 behavior - double z = 0.0, two = 2.0; - EXPECT_TRUE(std::isinf(two / z)); - EXPECT_TRUE(std::isnan(z / z)); // We'll return inf - // Operator/(Duration, double) EXPECT_EQ(inf, zero / 0.0); EXPECT_EQ(-inf, zero / -0.0); @@ -1050,7 +1045,7 @@ TEST(Duration, Multiplication) { EXPECT_EQ(absl::Seconds(666666666) + absl::Nanoseconds(666666667) + absl::Nanoseconds(1) / 2, sigfigs / 3); - sigfigs = absl::Seconds(7000000000LL); + sigfigs = absl::Seconds(int64_t{7000000000}); EXPECT_EQ(absl::Seconds(2333333333) + absl::Nanoseconds(333333333) + absl::Nanoseconds(1) / 4, sigfigs / 3); diff --git a/absl/time/format.cc b/absl/time/format.cc index 6eb83d7a..ee088f33 100644 --- a/absl/time/format.cc +++ b/absl/time/format.cc @@ -22,13 +22,16 @@ namespace cctz = absl::time_internal::cctz; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN -extern const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez"; -extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; +ABSL_DLL extern const char RFC3339_full[] = + "%Y-%m-%dT%H:%M:%E*S%Ez"; +ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; -extern const char RFC1123_full[] = "%a, %d %b %E4Y %H:%M:%S %z"; -extern const char RFC1123_no_wday[] = "%d %b %E4Y %H:%M:%S %z"; +ABSL_DLL extern const char RFC1123_full[] = + "%a, %d %b %E4Y %H:%M:%S %z"; +ABSL_DLL extern const char RFC1123_no_wday[] = + "%d %b %E4Y %H:%M:%S %z"; namespace { @@ -130,6 +133,14 @@ bool ParseTime(const std::string& format, const std::string& input, } // Functions required to support absl::Time flags. +bool AbslParseFlag(absl::string_view text, absl::Time* t, std::string* error) { + return absl::ParseTime(RFC3339_full, std::string(text), absl::UTCTimeZone(), + t, error); +} + +std::string AbslUnparseFlag(absl::Time t) { + return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone()); +} bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) { return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error); } @@ -138,5 +149,5 @@ std::string UnparseFlag(absl::Time t) { return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone()); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index b05c2347..7a53c815 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -12,10 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") + package(features = ["-parse_headers"]) licenses(["notice"]) # Apache License +filegroup( + name = "zoneinfo", + srcs = glob(["testdata/zoneinfo/**"]), +) + config_setting( name = "osx", constraint_values = [ @@ -33,16 +40,6 @@ config_setting( ### libraries cc_library( - name = "includes", - textual_hdrs = [ - "include/cctz/civil_time.h", - "include/cctz/civil_time_detail.h", - "include/cctz/time_zone.h", - ], - visibility = ["//absl/time:__pkg__"], -) - -cc_library( name = "civil_time", srcs = ["src/civil_time_detail.cc"], hdrs = [ @@ -50,6 +47,7 @@ cc_library( ], textual_hdrs = ["include/cctz/civil_time_detail.h"], visibility = ["//visibility:public"], + deps = ["//absl/base:config"], ) cc_library( @@ -86,7 +84,10 @@ cc_library( "//conditions:default": [], }), visibility = ["//visibility:public"], - deps = [":civil_time"], + deps = [ + ":civil_time", + "//absl/base:config", + ], ) ### tests @@ -97,6 +98,7 @@ cc_test( srcs = ["src/civil_time_test.cc"], deps = [ ":civil_time", + "//absl/base:config", "@com_google_googletest//:gtest_main", ], ) @@ -114,6 +116,7 @@ cc_test( deps = [ ":civil_time", ":time_zone", + "//absl/base:config", "@com_google_googletest//:gtest_main", ], ) @@ -132,6 +135,7 @@ cc_test( deps = [ ":civil_time", ":time_zone", + "//absl/base:config", "@com_google_googletest//:gtest_main", ], ) @@ -152,6 +156,7 @@ cc_test( deps = [ ":civil_time", ":time_zone", + "//absl/base:config", "@com_github_google_benchmark//:benchmark_main", ], ) @@ -159,8 +164,3 @@ cc_test( ### examples ### binaries - -filegroup( - name = "zoneinfo", - srcs = glob(["testdata/zoneinfo/**"]), -) diff --git a/absl/time/internal/cctz/include/cctz/civil_time.h b/absl/time/internal/cctz/include/cctz/civil_time.h index df25db07..d47ff86f 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time.h +++ b/absl/time/internal/cctz/include/cctz/civil_time.h @@ -15,10 +15,11 @@ #ifndef ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_ #define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_ +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time_detail.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -151,7 +152,7 @@ namespace cctz { // // All civil-time types have accessors for all six of the civil-time fields: // year, month, day, hour, minute, and second. Recall that fields inferior to -// the type's aligment will be set to their minimum valid value. +// the type's alignment will be set to their minimum valid value. // // civil_day d(2015, 6, 28); // // d.year() == 2015 @@ -325,7 +326,7 @@ using detail::get_yearday; } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_ diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h index 53e087a2..4cde96f1 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h +++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h @@ -20,6 +20,8 @@ #include <ostream> #include <type_traits> +#include "absl/base/config.h" + // Disable constexpr support unless we are in C++14 mode. #if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) #define CONSTEXPR_D constexpr // data @@ -32,7 +34,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -54,8 +56,8 @@ using second_t = std::int_fast8_t; // [0:59] // Normalized civil-time fields: Y-M-D HH:MM:SS. struct fields { - CONSTEXPR_M fields(year_t year, month_t month, day_t day, - hour_t hour, minute_t minute, second_t second) + CONSTEXPR_M fields(year_t year, month_t month, day_t day, hour_t hour, + minute_t minute, second_t second) : y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {} std::int_least64_t y; std::int_least8_t m; @@ -102,8 +104,8 @@ CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept { return k_days_per_month[m] + (m == 2 && is_leap_year(y)); } -CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, - hour_t hh, minute_t mm, second_t ss) noexcept { +CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh, + minute_t mm, second_t ss) noexcept { y += (cd / 146097) * 400; cd %= 146097; if (cd < 0) { @@ -153,8 +155,8 @@ CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, } return fields(y, m, static_cast<day_t>(d), hh, mm, ss); } -CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, - hour_t hh, minute_t mm, second_t ss) noexcept { +CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh, + minute_t mm, second_t ss) noexcept { if (m != 12) { y += m / 12; m %= 12; @@ -165,8 +167,8 @@ CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, } return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss); } -CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, - diff_t hh, minute_t mm, second_t ss) noexcept { +CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, diff_t hh, + minute_t mm, second_t ss) noexcept { cd += hh / 24; hh %= 24; if (hh < 0) { @@ -265,8 +267,8 @@ CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept { // yet the difference between two such extreme values may actually be // small, so we take a little care to avoid overflow when possible by // exploiting the 146097-day cycle. -CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, - year_t y2, month_t m2, day_t d2) noexcept { +CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, year_t y2, + month_t m2, day_t d2) noexcept { const diff_t a_c4_off = y1 % 400; const diff_t b_c4_off = y2 % 400; diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off); @@ -306,9 +308,7 @@ CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept { //////////////////////////////////////////////////////////////////////// // Aligns the (normalized) fields struct to the indicated field. -CONSTEXPR_F fields align(second_tag, fields f) noexcept { - return f; -} +CONSTEXPR_F fields align(second_tag, fields f) noexcept { return f; } CONSTEXPR_F fields align(minute_tag, fields f) noexcept { return fields{f.y, f.m, f.d, f.hh, f.mm, 0}; } @@ -387,11 +387,11 @@ class civil_time { : civil_time(ct.f_) {} // Factories for the maximum/minimum representable civil_time. - static CONSTEXPR_F civil_time (max)() { + static CONSTEXPR_F civil_time(max)() { const auto max_year = (std::numeric_limits<std::int_least64_t>::max)(); return civil_time(max_year, 12, 31, 23, 59, 59); } - static CONSTEXPR_F civil_time (min)() { + static CONSTEXPR_F civil_time(min)() { const auto min_year = (std::numeric_limits<std::int_least64_t>::min)(); return civil_time(min_year, 1, 1, 0, 0, 0); } @@ -417,17 +417,13 @@ class civil_time { } return *this; } - CONSTEXPR_M civil_time& operator++() noexcept { - return *this += 1; - } + CONSTEXPR_M civil_time& operator++() noexcept { return *this += 1; } CONSTEXPR_M civil_time operator++(int) noexcept { const civil_time a = *this; ++*this; return a; } - CONSTEXPR_M civil_time& operator--() noexcept { - return *this -= 1; - } + CONSTEXPR_M civil_time& operator--() noexcept { return *this -= 1; } CONSTEXPR_M civil_time operator--(int) noexcept { const civil_time a = *this; --*this; @@ -484,17 +480,17 @@ using civil_second = civil_time<second_tag>; template <typename T1, typename T2> CONSTEXPR_F bool operator<(const civil_time<T1>& lhs, const civil_time<T2>& rhs) noexcept { - return (lhs.year() < rhs.year() || - (lhs.year() == rhs.year() && - (lhs.month() < rhs.month() || - (lhs.month() == rhs.month() && - (lhs.day() < rhs.day() || - (lhs.day() == rhs.day() && - (lhs.hour() < rhs.hour() || - (lhs.hour() == rhs.hour() && - (lhs.minute() < rhs.minute() || - (lhs.minute() == rhs.minute() && - (lhs.second() < rhs.second()))))))))))); + return ( + lhs.year() < rhs.year() || + (lhs.year() == rhs.year() && + (lhs.month() < rhs.month() || + (lhs.month() == rhs.month() && + (lhs.day() < rhs.day() || (lhs.day() == rhs.day() && + (lhs.hour() < rhs.hour() || + (lhs.hour() == rhs.hour() && + (lhs.minute() < rhs.minute() || + (lhs.minute() == rhs.minute() && + (lhs.second() < rhs.second()))))))))))); } template <typename T1, typename T2> CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs, @@ -616,7 +612,7 @@ std::ostream& operator<<(std::ostream& os, weekday wd); } // namespace detail } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #undef CONSTEXPR_M diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h index f9769c0c..d05147a1 100644 --- a/absl/time/internal/cctz/include/cctz/time_zone.h +++ b/absl/time/internal/cctz/include/cctz/time_zone.h @@ -25,10 +25,11 @@ #include <string> #include <utility> +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -40,8 +41,8 @@ using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead. namespace detail { template <typename D> -inline std::pair<time_point<seconds>, D> -split_seconds(const time_point<D>& tp) { +inline std::pair<time_point<seconds>, D> split_seconds( + const time_point<D>& tp) { auto sec = std::chrono::time_point_cast<seconds>(tp); auto sub = tp - sec; if (sub.count() < 0) { @@ -50,8 +51,8 @@ split_seconds(const time_point<D>& tp) { } return {sec, std::chrono::duration_cast<D>(sub)}; } -inline std::pair<time_point<seconds>, seconds> -split_seconds(const time_point<seconds>& tp) { +inline std::pair<time_point<seconds>, seconds> split_seconds( + const time_point<seconds>& tp) { return {tp, seconds::zero()}; } } // namespace detail @@ -195,15 +196,13 @@ class time_zone { bool next_transition(const time_point<seconds>& tp, civil_transition* trans) const; template <typename D> - bool next_transition(const time_point<D>& tp, - civil_transition* trans) const { + bool next_transition(const time_point<D>& tp, civil_transition* trans) const { return next_transition(detail::split_seconds(tp).first, trans); } bool prev_transition(const time_point<seconds>& tp, civil_transition* trans) const; template <typename D> - bool prev_transition(const time_point<D>& tp, - civil_transition* trans) const { + bool prev_transition(const time_point<D>& tp, civil_transition* trans) const { return prev_transition(detail::split_seconds(tp).first, trans); } @@ -221,9 +220,7 @@ class time_zone { friend bool operator==(time_zone lhs, time_zone rhs) { return &lhs.effective_impl() == &rhs.effective_impl(); } - friend bool operator!=(time_zone lhs, time_zone rhs) { - return !(lhs == rhs); - } + friend bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); } template <typename H> friend H AbslHashValue(H h, time_zone tz) { @@ -381,7 +378,7 @@ inline bool parse(const std::string& fmt, const std::string& input, } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_ diff --git a/absl/time/internal/cctz/include/cctz/zone_info_source.h b/absl/time/internal/cctz/include/cctz/zone_info_source.h index 7372c5de..912b44ba 100644 --- a/absl/time/internal/cctz/include/cctz/zone_info_source.h +++ b/absl/time/internal/cctz/include/cctz/zone_info_source.h @@ -20,8 +20,10 @@ #include <memory> #include <string> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -31,7 +33,7 @@ class ZoneInfoSource { virtual ~ZoneInfoSource(); virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread() - virtual int Skip(std::size_t offset) = 0; // like fseek() + virtual int Skip(std::size_t offset) = 0; // like fseek() // Until the zoneinfo data supports versioning information, we provide // a way for a ZoneInfoSource to indicate it out-of-band. The default @@ -41,11 +43,11 @@ class ZoneInfoSource { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz_extension { @@ -55,8 +57,8 @@ namespace cctz_extension { using ZoneInfoSourceFactory = std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> (*)( const std::string&, - const std::function<std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>( - const std::string&)>&); + const std::function<std::unique_ptr< + absl::time_internal::cctz::ZoneInfoSource>(const std::string&)>&); // The user can control the mapping of zone names to zoneinfo data by // providing a definition for cctz_extension::zone_info_source_factory. @@ -94,7 +96,7 @@ extern ZoneInfoSourceFactory zone_info_source_factory; } // namespace cctz_extension } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_ diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc index a40f504e..d30a644e 100644 --- a/absl/time/internal/cctz/src/cctz_benchmark.cc +++ b/absl/time/internal/cctz/src/cctz_benchmark.cc @@ -105,601 +105,599 @@ const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; // A list of known time-zone names. // TODO: Refactor with src/time_zone_lookup_test.cc. -const char* const kTimeZoneNames[] = { - "Africa/Abidjan", - "Africa/Accra", - "Africa/Addis_Ababa", - "Africa/Algiers", - "Africa/Asmara", - "Africa/Asmera", - "Africa/Bamako", - "Africa/Bangui", - "Africa/Banjul", - "Africa/Bissau", - "Africa/Blantyre", - "Africa/Brazzaville", - "Africa/Bujumbura", - "Africa/Cairo", - "Africa/Casablanca", - "Africa/Ceuta", - "Africa/Conakry", - "Africa/Dakar", - "Africa/Dar_es_Salaam", - "Africa/Djibouti", - "Africa/Douala", - "Africa/El_Aaiun", - "Africa/Freetown", - "Africa/Gaborone", - "Africa/Harare", - "Africa/Johannesburg", - "Africa/Juba", - "Africa/Kampala", - "Africa/Khartoum", - "Africa/Kigali", - "Africa/Kinshasa", - "Africa/Lagos", - "Africa/Libreville", - "Africa/Lome", - "Africa/Luanda", - "Africa/Lubumbashi", - "Africa/Lusaka", - "Africa/Malabo", - "Africa/Maputo", - "Africa/Maseru", - "Africa/Mbabane", - "Africa/Mogadishu", - "Africa/Monrovia", - "Africa/Nairobi", - "Africa/Ndjamena", - "Africa/Niamey", - "Africa/Nouakchott", - "Africa/Ouagadougou", - "Africa/Porto-Novo", - "Africa/Sao_Tome", - "Africa/Timbuktu", - "Africa/Tripoli", - "Africa/Tunis", - "Africa/Windhoek", - "America/Adak", - "America/Anchorage", - "America/Anguilla", - "America/Antigua", - "America/Araguaina", - "America/Argentina/Buenos_Aires", - "America/Argentina/Catamarca", - "America/Argentina/ComodRivadavia", - "America/Argentina/Cordoba", - "America/Argentina/Jujuy", - "America/Argentina/La_Rioja", - "America/Argentina/Mendoza", - "America/Argentina/Rio_Gallegos", - "America/Argentina/Salta", - "America/Argentina/San_Juan", - "America/Argentina/San_Luis", - "America/Argentina/Tucuman", - "America/Argentina/Ushuaia", - "America/Aruba", - "America/Asuncion", - "America/Atikokan", - "America/Atka", - "America/Bahia", - "America/Bahia_Banderas", - "America/Barbados", - "America/Belem", - "America/Belize", - "America/Blanc-Sablon", - "America/Boa_Vista", - "America/Bogota", - "America/Boise", - "America/Buenos_Aires", - "America/Cambridge_Bay", - "America/Campo_Grande", - "America/Cancun", - "America/Caracas", - "America/Catamarca", - "America/Cayenne", - "America/Cayman", - "America/Chicago", - "America/Chihuahua", - "America/Coral_Harbour", - "America/Cordoba", - "America/Costa_Rica", - "America/Creston", - "America/Cuiaba", - "America/Curacao", - "America/Danmarkshavn", - "America/Dawson", - "America/Dawson_Creek", - "America/Denver", - "America/Detroit", - "America/Dominica", - "America/Edmonton", - "America/Eirunepe", - "America/El_Salvador", - "America/Ensenada", - "America/Fort_Nelson", - "America/Fort_Wayne", - "America/Fortaleza", - "America/Glace_Bay", - "America/Godthab", - "America/Goose_Bay", - "America/Grand_Turk", - "America/Grenada", - "America/Guadeloupe", - "America/Guatemala", - "America/Guayaquil", - "America/Guyana", - "America/Halifax", - "America/Havana", - "America/Hermosillo", - "America/Indiana/Indianapolis", - "America/Indiana/Knox", - "America/Indiana/Marengo", - "America/Indiana/Petersburg", - "America/Indiana/Tell_City", - "America/Indiana/Vevay", - "America/Indiana/Vincennes", - "America/Indiana/Winamac", - "America/Indianapolis", - "America/Inuvik", - "America/Iqaluit", - "America/Jamaica", - "America/Jujuy", - "America/Juneau", - "America/Kentucky/Louisville", - "America/Kentucky/Monticello", - "America/Knox_IN", - "America/Kralendijk", - "America/La_Paz", - "America/Lima", - "America/Los_Angeles", - "America/Louisville", - "America/Lower_Princes", - "America/Maceio", - "America/Managua", - "America/Manaus", - "America/Marigot", - "America/Martinique", - "America/Matamoros", - "America/Mazatlan", - "America/Mendoza", - "America/Menominee", - "America/Merida", - "America/Metlakatla", - "America/Mexico_City", - "America/Miquelon", - "America/Moncton", - "America/Monterrey", - "America/Montevideo", - "America/Montreal", - "America/Montserrat", - "America/Nassau", - "America/New_York", - "America/Nipigon", - "America/Nome", - "America/Noronha", - "America/North_Dakota/Beulah", - "America/North_Dakota/Center", - "America/North_Dakota/New_Salem", - "America/Ojinaga", - "America/Panama", - "America/Pangnirtung", - "America/Paramaribo", - "America/Phoenix", - "America/Port-au-Prince", - "America/Port_of_Spain", - "America/Porto_Acre", - "America/Porto_Velho", - "America/Puerto_Rico", - "America/Punta_Arenas", - "America/Rainy_River", - "America/Rankin_Inlet", - "America/Recife", - "America/Regina", - "America/Resolute", - "America/Rio_Branco", - "America/Rosario", - "America/Santa_Isabel", - "America/Santarem", - "America/Santiago", - "America/Santo_Domingo", - "America/Sao_Paulo", - "America/Scoresbysund", - "America/Shiprock", - "America/Sitka", - "America/St_Barthelemy", - "America/St_Johns", - "America/St_Kitts", - "America/St_Lucia", - "America/St_Thomas", - "America/St_Vincent", - "America/Swift_Current", - "America/Tegucigalpa", - "America/Thule", - "America/Thunder_Bay", - "America/Tijuana", - "America/Toronto", - "America/Tortola", - "America/Vancouver", - "America/Virgin", - "America/Whitehorse", - "America/Winnipeg", - "America/Yakutat", - "America/Yellowknife", - "Antarctica/Casey", - "Antarctica/Davis", - "Antarctica/DumontDUrville", - "Antarctica/Macquarie", - "Antarctica/Mawson", - "Antarctica/McMurdo", - "Antarctica/Palmer", - "Antarctica/Rothera", - "Antarctica/South_Pole", - "Antarctica/Syowa", - "Antarctica/Troll", - "Antarctica/Vostok", - "Arctic/Longyearbyen", - "Asia/Aden", - "Asia/Almaty", - "Asia/Amman", - "Asia/Anadyr", - "Asia/Aqtau", - "Asia/Aqtobe", - "Asia/Ashgabat", - "Asia/Ashkhabad", - "Asia/Atyrau", - "Asia/Baghdad", - "Asia/Bahrain", - "Asia/Baku", - "Asia/Bangkok", - "Asia/Barnaul", - "Asia/Beirut", - "Asia/Bishkek", - "Asia/Brunei", - "Asia/Calcutta", - "Asia/Chita", - "Asia/Choibalsan", - "Asia/Chongqing", - "Asia/Chungking", - "Asia/Colombo", - "Asia/Dacca", - "Asia/Damascus", - "Asia/Dhaka", - "Asia/Dili", - "Asia/Dubai", - "Asia/Dushanbe", - "Asia/Famagusta", - "Asia/Gaza", - "Asia/Harbin", - "Asia/Hebron", - "Asia/Ho_Chi_Minh", - "Asia/Hong_Kong", - "Asia/Hovd", - "Asia/Irkutsk", - "Asia/Istanbul", - "Asia/Jakarta", - "Asia/Jayapura", - "Asia/Jerusalem", - "Asia/Kabul", - "Asia/Kamchatka", - "Asia/Karachi", - "Asia/Kashgar", - "Asia/Kathmandu", - "Asia/Katmandu", - "Asia/Khandyga", - "Asia/Kolkata", - "Asia/Krasnoyarsk", - "Asia/Kuala_Lumpur", - "Asia/Kuching", - "Asia/Kuwait", - "Asia/Macao", - "Asia/Macau", - "Asia/Magadan", - "Asia/Makassar", - "Asia/Manila", - "Asia/Muscat", - "Asia/Nicosia", - "Asia/Novokuznetsk", - "Asia/Novosibirsk", - "Asia/Omsk", - "Asia/Oral", - "Asia/Phnom_Penh", - "Asia/Pontianak", - "Asia/Pyongyang", - "Asia/Qatar", - "Asia/Qostanay", - "Asia/Qyzylorda", - "Asia/Rangoon", - "Asia/Riyadh", - "Asia/Saigon", - "Asia/Sakhalin", - "Asia/Samarkand", - "Asia/Seoul", - "Asia/Shanghai", - "Asia/Singapore", - "Asia/Srednekolymsk", - "Asia/Taipei", - "Asia/Tashkent", - "Asia/Tbilisi", - "Asia/Tehran", - "Asia/Tel_Aviv", - "Asia/Thimbu", - "Asia/Thimphu", - "Asia/Tokyo", - "Asia/Tomsk", - "Asia/Ujung_Pandang", - "Asia/Ulaanbaatar", - "Asia/Ulan_Bator", - "Asia/Urumqi", - "Asia/Ust-Nera", - "Asia/Vientiane", - "Asia/Vladivostok", - "Asia/Yakutsk", - "Asia/Yangon", - "Asia/Yekaterinburg", - "Asia/Yerevan", - "Atlantic/Azores", - "Atlantic/Bermuda", - "Atlantic/Canary", - "Atlantic/Cape_Verde", - "Atlantic/Faeroe", - "Atlantic/Faroe", - "Atlantic/Jan_Mayen", - "Atlantic/Madeira", - "Atlantic/Reykjavik", - "Atlantic/South_Georgia", - "Atlantic/St_Helena", - "Atlantic/Stanley", - "Australia/ACT", - "Australia/Adelaide", - "Australia/Brisbane", - "Australia/Broken_Hill", - "Australia/Canberra", - "Australia/Currie", - "Australia/Darwin", - "Australia/Eucla", - "Australia/Hobart", - "Australia/LHI", - "Australia/Lindeman", - "Australia/Lord_Howe", - "Australia/Melbourne", - "Australia/NSW", - "Australia/North", - "Australia/Perth", - "Australia/Queensland", - "Australia/South", - "Australia/Sydney", - "Australia/Tasmania", - "Australia/Victoria", - "Australia/West", - "Australia/Yancowinna", - "Brazil/Acre", - "Brazil/DeNoronha", - "Brazil/East", - "Brazil/West", - "CET", - "CST6CDT", - "Canada/Atlantic", - "Canada/Central", - "Canada/Eastern", - "Canada/Mountain", - "Canada/Newfoundland", - "Canada/Pacific", - "Canada/Saskatchewan", - "Canada/Yukon", - "Chile/Continental", - "Chile/EasterIsland", - "Cuba", - "EET", - "EST", - "EST5EDT", - "Egypt", - "Eire", - "Etc/GMT", - "Etc/GMT+0", - "Etc/GMT+1", - "Etc/GMT+10", - "Etc/GMT+11", - "Etc/GMT+12", - "Etc/GMT+2", - "Etc/GMT+3", - "Etc/GMT+4", - "Etc/GMT+5", - "Etc/GMT+6", - "Etc/GMT+7", - "Etc/GMT+8", - "Etc/GMT+9", - "Etc/GMT-0", - "Etc/GMT-1", - "Etc/GMT-10", - "Etc/GMT-11", - "Etc/GMT-12", - "Etc/GMT-13", - "Etc/GMT-14", - "Etc/GMT-2", - "Etc/GMT-3", - "Etc/GMT-4", - "Etc/GMT-5", - "Etc/GMT-6", - "Etc/GMT-7", - "Etc/GMT-8", - "Etc/GMT-9", - "Etc/GMT0", - "Etc/Greenwich", - "Etc/UCT", - "Etc/UTC", - "Etc/Universal", - "Etc/Zulu", - "Europe/Amsterdam", - "Europe/Andorra", - "Europe/Astrakhan", - "Europe/Athens", - "Europe/Belfast", - "Europe/Belgrade", - "Europe/Berlin", - "Europe/Bratislava", - "Europe/Brussels", - "Europe/Bucharest", - "Europe/Budapest", - "Europe/Busingen", - "Europe/Chisinau", - "Europe/Copenhagen", - "Europe/Dublin", - "Europe/Gibraltar", - "Europe/Guernsey", - "Europe/Helsinki", - "Europe/Isle_of_Man", - "Europe/Istanbul", - "Europe/Jersey", - "Europe/Kaliningrad", - "Europe/Kiev", - "Europe/Kirov", - "Europe/Lisbon", - "Europe/Ljubljana", - "Europe/London", - "Europe/Luxembourg", - "Europe/Madrid", - "Europe/Malta", - "Europe/Mariehamn", - "Europe/Minsk", - "Europe/Monaco", - "Europe/Moscow", - "Europe/Nicosia", - "Europe/Oslo", - "Europe/Paris", - "Europe/Podgorica", - "Europe/Prague", - "Europe/Riga", - "Europe/Rome", - "Europe/Samara", - "Europe/San_Marino", - "Europe/Sarajevo", - "Europe/Saratov", - "Europe/Simferopol", - "Europe/Skopje", - "Europe/Sofia", - "Europe/Stockholm", - "Europe/Tallinn", - "Europe/Tirane", - "Europe/Tiraspol", - "Europe/Ulyanovsk", - "Europe/Uzhgorod", - "Europe/Vaduz", - "Europe/Vatican", - "Europe/Vienna", - "Europe/Vilnius", - "Europe/Volgograd", - "Europe/Warsaw", - "Europe/Zagreb", - "Europe/Zaporozhye", - "Europe/Zurich", - "GB", - "GB-Eire", - "GMT", - "GMT+0", - "GMT-0", - "GMT0", - "Greenwich", - "HST", - "Hongkong", - "Iceland", - "Indian/Antananarivo", - "Indian/Chagos", - "Indian/Christmas", - "Indian/Cocos", - "Indian/Comoro", - "Indian/Kerguelen", - "Indian/Mahe", - "Indian/Maldives", - "Indian/Mauritius", - "Indian/Mayotte", - "Indian/Reunion", - "Iran", - "Israel", - "Jamaica", - "Japan", - "Kwajalein", - "Libya", - "MET", - "MST", - "MST7MDT", - "Mexico/BajaNorte", - "Mexico/BajaSur", - "Mexico/General", - "NZ", - "NZ-CHAT", - "Navajo", - "PRC", - "PST8PDT", - "Pacific/Apia", - "Pacific/Auckland", - "Pacific/Bougainville", - "Pacific/Chatham", - "Pacific/Chuuk", - "Pacific/Easter", - "Pacific/Efate", - "Pacific/Enderbury", - "Pacific/Fakaofo", - "Pacific/Fiji", - "Pacific/Funafuti", - "Pacific/Galapagos", - "Pacific/Gambier", - "Pacific/Guadalcanal", - "Pacific/Guam", - "Pacific/Honolulu", - "Pacific/Johnston", - "Pacific/Kiritimati", - "Pacific/Kosrae", - "Pacific/Kwajalein", - "Pacific/Majuro", - "Pacific/Marquesas", - "Pacific/Midway", - "Pacific/Nauru", - "Pacific/Niue", - "Pacific/Norfolk", - "Pacific/Noumea", - "Pacific/Pago_Pago", - "Pacific/Palau", - "Pacific/Pitcairn", - "Pacific/Pohnpei", - "Pacific/Ponape", - "Pacific/Port_Moresby", - "Pacific/Rarotonga", - "Pacific/Saipan", - "Pacific/Samoa", - "Pacific/Tahiti", - "Pacific/Tarawa", - "Pacific/Tongatapu", - "Pacific/Truk", - "Pacific/Wake", - "Pacific/Wallis", - "Pacific/Yap", - "Poland", - "Portugal", - "ROC", - "ROK", - "Singapore", - "Turkey", - "UCT", - "US/Alaska", - "US/Aleutian", - "US/Arizona", - "US/Central", - "US/East-Indiana", - "US/Eastern", - "US/Hawaii", - "US/Indiana-Starke", - "US/Michigan", - "US/Mountain", - "US/Pacific", - "US/Samoa", - "UTC", - "Universal", - "W-SU", - "WET", - "Zulu", - nullptr -}; +const char* const kTimeZoneNames[] = {"Africa/Abidjan", + "Africa/Accra", + "Africa/Addis_Ababa", + "Africa/Algiers", + "Africa/Asmara", + "Africa/Asmera", + "Africa/Bamako", + "Africa/Bangui", + "Africa/Banjul", + "Africa/Bissau", + "Africa/Blantyre", + "Africa/Brazzaville", + "Africa/Bujumbura", + "Africa/Cairo", + "Africa/Casablanca", + "Africa/Ceuta", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Douala", + "Africa/El_Aaiun", + "Africa/Freetown", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Juba", + "Africa/Kampala", + "Africa/Khartoum", + "Africa/Kigali", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Lome", + "Africa/Luanda", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Malabo", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Africa/Mogadishu", + "Africa/Monrovia", + "Africa/Nairobi", + "Africa/Ndjamena", + "Africa/Niamey", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Porto-Novo", + "Africa/Sao_Tome", + "Africa/Timbuktu", + "Africa/Tripoli", + "Africa/Tunis", + "Africa/Windhoek", + "America/Adak", + "America/Anchorage", + "America/Anguilla", + "America/Antigua", + "America/Araguaina", + "America/Argentina/Buenos_Aires", + "America/Argentina/Catamarca", + "America/Argentina/ComodRivadavia", + "America/Argentina/Cordoba", + "America/Argentina/Jujuy", + "America/Argentina/La_Rioja", + "America/Argentina/Mendoza", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Aruba", + "America/Asuncion", + "America/Atikokan", + "America/Atka", + "America/Bahia", + "America/Bahia_Banderas", + "America/Barbados", + "America/Belem", + "America/Belize", + "America/Blanc-Sablon", + "America/Boa_Vista", + "America/Bogota", + "America/Boise", + "America/Buenos_Aires", + "America/Cambridge_Bay", + "America/Campo_Grande", + "America/Cancun", + "America/Caracas", + "America/Catamarca", + "America/Cayenne", + "America/Cayman", + "America/Chicago", + "America/Chihuahua", + "America/Coral_Harbour", + "America/Cordoba", + "America/Costa_Rica", + "America/Creston", + "America/Cuiaba", + "America/Curacao", + "America/Danmarkshavn", + "America/Dawson", + "America/Dawson_Creek", + "America/Denver", + "America/Detroit", + "America/Dominica", + "America/Edmonton", + "America/Eirunepe", + "America/El_Salvador", + "America/Ensenada", + "America/Fort_Nelson", + "America/Fort_Wayne", + "America/Fortaleza", + "America/Glace_Bay", + "America/Godthab", + "America/Goose_Bay", + "America/Grand_Turk", + "America/Grenada", + "America/Guadeloupe", + "America/Guatemala", + "America/Guayaquil", + "America/Guyana", + "America/Halifax", + "America/Havana", + "America/Hermosillo", + "America/Indiana/Indianapolis", + "America/Indiana/Knox", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Tell_City", + "America/Indiana/Vevay", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Indianapolis", + "America/Inuvik", + "America/Iqaluit", + "America/Jamaica", + "America/Jujuy", + "America/Juneau", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/Knox_IN", + "America/Kralendijk", + "America/La_Paz", + "America/Lima", + "America/Los_Angeles", + "America/Louisville", + "America/Lower_Princes", + "America/Maceio", + "America/Managua", + "America/Manaus", + "America/Marigot", + "America/Martinique", + "America/Matamoros", + "America/Mazatlan", + "America/Mendoza", + "America/Menominee", + "America/Merida", + "America/Metlakatla", + "America/Mexico_City", + "America/Miquelon", + "America/Moncton", + "America/Monterrey", + "America/Montevideo", + "America/Montreal", + "America/Montserrat", + "America/Nassau", + "America/New_York", + "America/Nipigon", + "America/Nome", + "America/Noronha", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Ojinaga", + "America/Panama", + "America/Pangnirtung", + "America/Paramaribo", + "America/Phoenix", + "America/Port-au-Prince", + "America/Port_of_Spain", + "America/Porto_Acre", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Punta_Arenas", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Recife", + "America/Regina", + "America/Resolute", + "America/Rio_Branco", + "America/Rosario", + "America/Santa_Isabel", + "America/Santarem", + "America/Santiago", + "America/Santo_Domingo", + "America/Sao_Paulo", + "America/Scoresbysund", + "America/Shiprock", + "America/Sitka", + "America/St_Barthelemy", + "America/St_Johns", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Swift_Current", + "America/Tegucigalpa", + "America/Thule", + "America/Thunder_Bay", + "America/Tijuana", + "America/Toronto", + "America/Tortola", + "America/Vancouver", + "America/Virgin", + "America/Whitehorse", + "America/Winnipeg", + "America/Yakutat", + "America/Yellowknife", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/DumontDUrville", + "Antarctica/Macquarie", + "Antarctica/Mawson", + "Antarctica/McMurdo", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/South_Pole", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok", + "Arctic/Longyearbyen", + "Asia/Aden", + "Asia/Almaty", + "Asia/Amman", + "Asia/Anadyr", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Ashkhabad", + "Asia/Atyrau", + "Asia/Baghdad", + "Asia/Bahrain", + "Asia/Baku", + "Asia/Bangkok", + "Asia/Barnaul", + "Asia/Beirut", + "Asia/Bishkek", + "Asia/Brunei", + "Asia/Calcutta", + "Asia/Chita", + "Asia/Choibalsan", + "Asia/Chongqing", + "Asia/Chungking", + "Asia/Colombo", + "Asia/Dacca", + "Asia/Damascus", + "Asia/Dhaka", + "Asia/Dili", + "Asia/Dubai", + "Asia/Dushanbe", + "Asia/Famagusta", + "Asia/Gaza", + "Asia/Harbin", + "Asia/Hebron", + "Asia/Ho_Chi_Minh", + "Asia/Hong_Kong", + "Asia/Hovd", + "Asia/Irkutsk", + "Asia/Istanbul", + "Asia/Jakarta", + "Asia/Jayapura", + "Asia/Jerusalem", + "Asia/Kabul", + "Asia/Kamchatka", + "Asia/Karachi", + "Asia/Kashgar", + "Asia/Kathmandu", + "Asia/Katmandu", + "Asia/Khandyga", + "Asia/Kolkata", + "Asia/Krasnoyarsk", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Kuwait", + "Asia/Macao", + "Asia/Macau", + "Asia/Magadan", + "Asia/Makassar", + "Asia/Manila", + "Asia/Muscat", + "Asia/Nicosia", + "Asia/Novokuznetsk", + "Asia/Novosibirsk", + "Asia/Omsk", + "Asia/Oral", + "Asia/Phnom_Penh", + "Asia/Pontianak", + "Asia/Pyongyang", + "Asia/Qatar", + "Asia/Qostanay", + "Asia/Qyzylorda", + "Asia/Rangoon", + "Asia/Riyadh", + "Asia/Saigon", + "Asia/Sakhalin", + "Asia/Samarkand", + "Asia/Seoul", + "Asia/Shanghai", + "Asia/Singapore", + "Asia/Srednekolymsk", + "Asia/Taipei", + "Asia/Tashkent", + "Asia/Tbilisi", + "Asia/Tehran", + "Asia/Tel_Aviv", + "Asia/Thimbu", + "Asia/Thimphu", + "Asia/Tokyo", + "Asia/Tomsk", + "Asia/Ujung_Pandang", + "Asia/Ulaanbaatar", + "Asia/Ulan_Bator", + "Asia/Urumqi", + "Asia/Ust-Nera", + "Asia/Vientiane", + "Asia/Vladivostok", + "Asia/Yakutsk", + "Asia/Yangon", + "Asia/Yekaterinburg", + "Asia/Yerevan", + "Atlantic/Azores", + "Atlantic/Bermuda", + "Atlantic/Canary", + "Atlantic/Cape_Verde", + "Atlantic/Faeroe", + "Atlantic/Faroe", + "Atlantic/Jan_Mayen", + "Atlantic/Madeira", + "Atlantic/Reykjavik", + "Atlantic/South_Georgia", + "Atlantic/St_Helena", + "Atlantic/Stanley", + "Australia/ACT", + "Australia/Adelaide", + "Australia/Brisbane", + "Australia/Broken_Hill", + "Australia/Canberra", + "Australia/Currie", + "Australia/Darwin", + "Australia/Eucla", + "Australia/Hobart", + "Australia/LHI", + "Australia/Lindeman", + "Australia/Lord_Howe", + "Australia/Melbourne", + "Australia/NSW", + "Australia/North", + "Australia/Perth", + "Australia/Queensland", + "Australia/South", + "Australia/Sydney", + "Australia/Tasmania", + "Australia/Victoria", + "Australia/West", + "Australia/Yancowinna", + "Brazil/Acre", + "Brazil/DeNoronha", + "Brazil/East", + "Brazil/West", + "CET", + "CST6CDT", + "Canada/Atlantic", + "Canada/Central", + "Canada/Eastern", + "Canada/Mountain", + "Canada/Newfoundland", + "Canada/Pacific", + "Canada/Saskatchewan", + "Canada/Yukon", + "Chile/Continental", + "Chile/EasterIsland", + "Cuba", + "EET", + "EST", + "EST5EDT", + "Egypt", + "Eire", + "Etc/GMT", + "Etc/GMT+0", + "Etc/GMT+1", + "Etc/GMT+10", + "Etc/GMT+11", + "Etc/GMT+12", + "Etc/GMT+2", + "Etc/GMT+3", + "Etc/GMT+4", + "Etc/GMT+5", + "Etc/GMT+6", + "Etc/GMT+7", + "Etc/GMT+8", + "Etc/GMT+9", + "Etc/GMT-0", + "Etc/GMT-1", + "Etc/GMT-10", + "Etc/GMT-11", + "Etc/GMT-12", + "Etc/GMT-13", + "Etc/GMT-14", + "Etc/GMT-2", + "Etc/GMT-3", + "Etc/GMT-4", + "Etc/GMT-5", + "Etc/GMT-6", + "Etc/GMT-7", + "Etc/GMT-8", + "Etc/GMT-9", + "Etc/GMT0", + "Etc/Greenwich", + "Etc/UCT", + "Etc/UTC", + "Etc/Universal", + "Etc/Zulu", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Astrakhan", + "Europe/Athens", + "Europe/Belfast", + "Europe/Belgrade", + "Europe/Berlin", + "Europe/Bratislava", + "Europe/Brussels", + "Europe/Bucharest", + "Europe/Budapest", + "Europe/Busingen", + "Europe/Chisinau", + "Europe/Copenhagen", + "Europe/Dublin", + "Europe/Gibraltar", + "Europe/Guernsey", + "Europe/Helsinki", + "Europe/Isle_of_Man", + "Europe/Istanbul", + "Europe/Jersey", + "Europe/Kaliningrad", + "Europe/Kiev", + "Europe/Kirov", + "Europe/Lisbon", + "Europe/Ljubljana", + "Europe/London", + "Europe/Luxembourg", + "Europe/Madrid", + "Europe/Malta", + "Europe/Mariehamn", + "Europe/Minsk", + "Europe/Monaco", + "Europe/Moscow", + "Europe/Nicosia", + "Europe/Oslo", + "Europe/Paris", + "Europe/Podgorica", + "Europe/Prague", + "Europe/Riga", + "Europe/Rome", + "Europe/Samara", + "Europe/San_Marino", + "Europe/Sarajevo", + "Europe/Saratov", + "Europe/Simferopol", + "Europe/Skopje", + "Europe/Sofia", + "Europe/Stockholm", + "Europe/Tallinn", + "Europe/Tirane", + "Europe/Tiraspol", + "Europe/Ulyanovsk", + "Europe/Uzhgorod", + "Europe/Vaduz", + "Europe/Vatican", + "Europe/Vienna", + "Europe/Vilnius", + "Europe/Volgograd", + "Europe/Warsaw", + "Europe/Zagreb", + "Europe/Zaporozhye", + "Europe/Zurich", + "GB", + "GB-Eire", + "GMT", + "GMT+0", + "GMT-0", + "GMT0", + "Greenwich", + "HST", + "Hongkong", + "Iceland", + "Indian/Antananarivo", + "Indian/Chagos", + "Indian/Christmas", + "Indian/Cocos", + "Indian/Comoro", + "Indian/Kerguelen", + "Indian/Mahe", + "Indian/Maldives", + "Indian/Mauritius", + "Indian/Mayotte", + "Indian/Reunion", + "Iran", + "Israel", + "Jamaica", + "Japan", + "Kwajalein", + "Libya", + "MET", + "MST", + "MST7MDT", + "Mexico/BajaNorte", + "Mexico/BajaSur", + "Mexico/General", + "NZ", + "NZ-CHAT", + "Navajo", + "PRC", + "PST8PDT", + "Pacific/Apia", + "Pacific/Auckland", + "Pacific/Bougainville", + "Pacific/Chatham", + "Pacific/Chuuk", + "Pacific/Easter", + "Pacific/Efate", + "Pacific/Enderbury", + "Pacific/Fakaofo", + "Pacific/Fiji", + "Pacific/Funafuti", + "Pacific/Galapagos", + "Pacific/Gambier", + "Pacific/Guadalcanal", + "Pacific/Guam", + "Pacific/Honolulu", + "Pacific/Johnston", + "Pacific/Kiritimati", + "Pacific/Kosrae", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Marquesas", + "Pacific/Midway", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Norfolk", + "Pacific/Noumea", + "Pacific/Pago_Pago", + "Pacific/Palau", + "Pacific/Pitcairn", + "Pacific/Pohnpei", + "Pacific/Ponape", + "Pacific/Port_Moresby", + "Pacific/Rarotonga", + "Pacific/Saipan", + "Pacific/Samoa", + "Pacific/Tahiti", + "Pacific/Tarawa", + "Pacific/Tongatapu", + "Pacific/Truk", + "Pacific/Wake", + "Pacific/Wallis", + "Pacific/Yap", + "Poland", + "Portugal", + "ROC", + "ROK", + "Singapore", + "Turkey", + "UCT", + "US/Alaska", + "US/Aleutian", + "US/Arizona", + "US/Central", + "US/East-Indiana", + "US/Eastern", + "US/Hawaii", + "US/Indiana-Starke", + "US/Michigan", + "US/Mountain", + "US/Pacific", + "US/Samoa", + "UTC", + "Universal", + "W-SU", + "WET", + "Zulu", + nullptr}; std::vector<std::string> AllTimeZoneNames() { std::vector<std::string> names; diff --git a/absl/time/internal/cctz/src/civil_time_detail.cc b/absl/time/internal/cctz/src/civil_time_detail.cc index 4df15d55..0b07e397 100644 --- a/absl/time/internal/cctz/src/civil_time_detail.cc +++ b/absl/time/internal/cctz/src/civil_time_detail.cc @@ -18,8 +18,10 @@ #include <ostream> #include <sstream> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { namespace detail { @@ -88,5 +90,5 @@ std::ostream& operator<<(std::ostream& os, weekday wd) { } // namespace detail } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/civil_time_test.cc b/absl/time/internal/cctz/src/civil_time_test.cc index 7d9a1834..be894d70 100644 --- a/absl/time/internal/cctz/src/civil_time_test.cc +++ b/absl/time/internal/cctz/src/civil_time_test.cc @@ -21,9 +21,10 @@ #include <type_traits> #include "gtest/gtest.h" +#include "absl/base/config.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -1015,19 +1016,13 @@ TEST(CivilTime, LeapYears) { int day; } leap_day; // The date of the day after Feb 28. } kLeapYearTable[]{ - {1900, 365, {3, 1}}, - {1999, 365, {3, 1}}, + {1900, 365, {3, 1}}, {1999, 365, {3, 1}}, {2000, 366, {2, 29}}, // leap year - {2001, 365, {3, 1}}, - {2002, 365, {3, 1}}, - {2003, 365, {3, 1}}, - {2004, 366, {2, 29}}, // leap year - {2005, 365, {3, 1}}, - {2006, 365, {3, 1}}, - {2007, 365, {3, 1}}, - {2008, 366, {2, 29}}, // leap year - {2009, 365, {3, 1}}, - {2100, 365, {3, 1}}, + {2001, 365, {3, 1}}, {2002, 365, {3, 1}}, + {2003, 365, {3, 1}}, {2004, 366, {2, 29}}, // leap year + {2005, 365, {3, 1}}, {2006, 365, {3, 1}}, + {2007, 365, {3, 1}}, {2008, 366, {2, 29}}, // leap year + {2009, 365, {3, 1}}, {2100, 365, {3, 1}}, }; for (const auto& e : kLeapYearTable) { @@ -1057,5 +1052,5 @@ TEST(CivilTime, FirstThursdayInMonth) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc index aa5af020..303c0244 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.cc +++ b/absl/time/internal/cctz/src/time_zone_fixed.cc @@ -20,8 +20,10 @@ #include <cstring> #include <string> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -60,11 +62,9 @@ bool FixedOffsetFromName(const std::string& name, seconds* offset) { const char* const ep = kFixedZonePrefix + prefix_len; if (name.size() != prefix_len + 9) // <prefix>+99:99:99 return false; - if (!std::equal(kFixedZonePrefix, ep, name.begin())) - return false; + if (!std::equal(kFixedZonePrefix, ep, name.begin())) return false; const char* np = name.data() + prefix_len; - if (np[0] != '+' && np[0] != '-') - return false; + if (np[0] != '+' && np[0] != '-') return false; if (np[3] != ':' || np[6] != ':') // see note below about large offsets return false; @@ -89,29 +89,29 @@ std::string FixedOffsetToName(const seconds& offset) { // offsets and to (somewhat) limit the total number of zones. return "UTC"; } - int seconds = static_cast<int>(offset.count()); - const char sign = (seconds < 0 ? '-' : '+'); - int minutes = seconds / 60; - seconds %= 60; + int offset_seconds = static_cast<int>(offset.count()); + const char sign = (offset_seconds < 0 ? '-' : '+'); + int offset_minutes = offset_seconds / 60; + offset_seconds %= 60; if (sign == '-') { - if (seconds > 0) { - seconds -= 60; - minutes += 1; + if (offset_seconds > 0) { + offset_seconds -= 60; + offset_minutes += 1; } - seconds = -seconds; - minutes = -minutes; + offset_seconds = -offset_seconds; + offset_minutes = -offset_minutes; } - int hours = minutes / 60; - minutes %= 60; + int offset_hours = offset_minutes / 60; + offset_minutes %= 60; const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; char buf[prefix_len + sizeof("-24:00:00")]; char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf); *ep++ = sign; - ep = Format02d(ep, hours); + ep = Format02d(ep, offset_hours); *ep++ = ':'; - ep = Format02d(ep, minutes); + ep = Format02d(ep, offset_minutes); *ep++ = ':'; - ep = Format02d(ep, seconds); + ep = Format02d(ep, offset_seconds); *ep++ = '\0'; assert(ep == buf + sizeof(buf)); return buf; @@ -136,5 +136,5 @@ std::string FixedOffsetToAbbr(const seconds& offset) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_fixed.h b/absl/time/internal/cctz/src/time_zone_fixed.h index 15ea3bdf..e74a0bbd 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.h +++ b/absl/time/internal/cctz/src/time_zone_fixed.h @@ -17,10 +17,11 @@ #include <string> +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -45,7 +46,7 @@ std::string FixedOffsetToAbbr(const seconds& offset); } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_ diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc index ee2ffac0..950b23a1 100644 --- a/absl/time/internal/cctz/src/time_zone_format.cc +++ b/absl/time/internal/cctz/src/time_zone_format.cc @@ -13,17 +13,18 @@ // limitations under the License. #if !defined(HAS_STRPTIME) -# if !defined(_MSC_VER) && !defined(__MINGW32__) -# define HAS_STRPTIME 1 // assume everyone has strptime() except windows -# endif +#if !defined(_MSC_VER) && !defined(__MINGW32__) +#define HAS_STRPTIME 1 // assume everyone has strptime() except windows +#endif #endif #if defined(HAS_STRPTIME) && HAS_STRPTIME -# if !defined(_XOPEN_SOURCE) -# define _XOPEN_SOURCE // Definedness suffices for strptime. -# endif +#if !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE // Definedness suffices for strptime. +#endif #endif +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" // Include time.h directly since, by C++ standards, ctime doesn't have to @@ -48,7 +49,7 @@ #include "time_zone_if.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { namespace detail { @@ -503,8 +504,9 @@ std::string format(const std::string& format, const time_point<seconds>& tp, bp = ep; if (n > 0) { if (n > kDigits10_64) n = kDigits10_64; - bp = Format64(bp, n, (n > 15) ? fs.count() * kExp10[n - 15] - : fs.count() / kExp10[15 - n]); + bp = Format64(bp, n, + (n > 15) ? fs.count() * kExp10[n - 15] + : fs.count() / kExp10[15 - n]); if (*np == 'S') *--bp = '.'; } if (*np == 'S') bp = Format02d(bp, al.cs.second()); @@ -721,10 +723,9 @@ bool parse(const std::string& format, const std::string& input, data = ParseZone(data, &zone); continue; case 's': - data = ParseInt(data, 0, - std::numeric_limits<std::int_fast64_t>::min(), - std::numeric_limits<std::int_fast64_t>::max(), - &percent_s); + data = + ParseInt(data, 0, std::numeric_limits<std::int_fast64_t>::min(), + std::numeric_limits<std::int_fast64_t>::max(), &percent_s); if (data != nullptr) saw_percent_s = true; continue; case ':': @@ -917,5 +918,5 @@ bool parse(const std::string& format, const std::string& input, } // namespace detail } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc index 49737445..caebcc4d 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -12,21 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/time/internal/cctz/include/cctz/time_zone.h" - #include <chrono> #include <iomanip> #include <sstream> #include <string> -#include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/time/internal/cctz/include/cctz/civil_time.h" +#include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace chrono = std::chrono; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -49,10 +49,10 @@ namespace { } while (0) const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez"; -const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; +const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z"; -const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; +const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; // A helper that tests the given format specifier by itself, and with leading // and trailing characters. For example: TestFormatSpecifier(tp, "%a", "Thu"). @@ -89,8 +89,11 @@ TEST(Format, TimePointResolution) { format(kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc)); EXPECT_EQ("03:04:05", format(kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc)); - EXPECT_EQ("03:04:05", - format(kFmt, chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), utc)); + EXPECT_EQ( + "03:04:05", + format(kFmt, + chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), + utc)); EXPECT_EQ("03:04:00", format(kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc)); EXPECT_EQ("03:00:00", @@ -111,12 +114,10 @@ TEST(Format, TimePointExtendedResolution) { EXPECT_EQ( "12:34:56.012345678901234", detail::format(kFmt, tp, detail::femtoseconds(12345678901234), utc)); - EXPECT_EQ( - "12:34:56.001234567890123", - detail::format(kFmt, tp, detail::femtoseconds(1234567890123), utc)); - EXPECT_EQ( - "12:34:56.000123456789012", - detail::format(kFmt, tp, detail::femtoseconds(123456789012), utc)); + EXPECT_EQ("12:34:56.001234567890123", + detail::format(kFmt, tp, detail::femtoseconds(1234567890123), utc)); + EXPECT_EQ("12:34:56.000123456789012", + detail::format(kFmt, tp, detail::femtoseconds(123456789012), utc)); EXPECT_EQ("12:34:56.000000000000123", detail::format(kFmt, tp, detail::femtoseconds(123), utc)); @@ -1251,9 +1252,9 @@ TEST(Parse, ExtendedSubecondsScan) { const auto expected = chrono::system_clock::from_time_t(0) + chrono::nanoseconds(micros * 1000 + ns); for (int ps = 0; ps < 1000; ps += 250) { - std::ostringstream oss; + std::ostringstream ps_oss; oss << std::setfill('0') << std::setw(3) << ps; - const std::string input = nanos + oss.str() + "999"; + const std::string input = nanos + ps_oss.str() + "999"; EXPECT_TRUE(parse("%E*f", input, tz, &tp)); EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input; } @@ -1417,8 +1418,8 @@ TEST(Parse, MaxRange) { parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp)); // tests max/min civil-second overflow - EXPECT_FALSE(parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", - utc, &tp)); + EXPECT_FALSE( + parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp)); EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01", utc, &tp)); @@ -1475,7 +1476,8 @@ TEST(FormatParse, RoundTrip) { TEST(FormatParse, RoundTripDistantFuture) { const time_zone utc = utc_time_zone(); - const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::max(); + const time_point<absl::time_internal::cctz::seconds> in = + time_point<absl::time_internal::cctz::seconds>::max(); const std::string s = format(RFC3339_full, in, utc); time_point<absl::time_internal::cctz::seconds> out; EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; @@ -1484,7 +1486,8 @@ TEST(FormatParse, RoundTripDistantFuture) { TEST(FormatParse, RoundTripDistantPast) { const time_zone utc = utc_time_zone(); - const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::min(); + const time_point<absl::time_internal::cctz::seconds> in = + time_point<absl::time_internal::cctz::seconds>::min(); const std::string s = format(RFC3339_full, in, utc); time_point<absl::time_internal::cctz::seconds> out; EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; @@ -1493,5 +1496,5 @@ TEST(FormatParse, RoundTripDistantPast) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_if.cc b/absl/time/internal/cctz/src/time_zone_if.cc index 75d23da5..0319b2f9 100644 --- a/absl/time/internal/cctz/src/time_zone_if.cc +++ b/absl/time/internal/cctz/src/time_zone_if.cc @@ -13,11 +13,13 @@ // limitations under the License. #include "time_zone_if.h" + +#include "absl/base/config.h" #include "time_zone_info.h" #include "time_zone_libc.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -39,5 +41,5 @@ TimeZoneIf::~TimeZoneIf() {} } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h index 0081d990..32c0891c 100644 --- a/absl/time/internal/cctz/src/time_zone_if.h +++ b/absl/time/internal/cctz/src/time_zone_if.h @@ -20,11 +20,12 @@ #include <memory> #include <string> +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -39,8 +40,7 @@ class TimeZoneIf { virtual time_zone::absolute_lookup BreakTime( const time_point<seconds>& tp) const = 0; - virtual time_zone::civil_lookup MakeTime( - const civil_second& cs) const = 0; + virtual time_zone::civil_lookup MakeTime(const civil_second& cs) const = 0; virtual bool NextTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const = 0; @@ -59,16 +59,18 @@ class TimeZoneIf { // Unix clock are second aligned, but not that they share an epoch. inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) { return (tp - std::chrono::time_point_cast<seconds>( - std::chrono::system_clock::from_time_t(0))).count(); + std::chrono::system_clock::from_time_t(0))) + .count(); } inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) { return std::chrono::time_point_cast<seconds>( - std::chrono::system_clock::from_time_t(0)) + seconds(t); + std::chrono::system_clock::from_time_t(0)) + + seconds(t); } } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_ diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc index 60911f0b..030ae0e1 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.cc +++ b/absl/time/internal/cctz/src/time_zone_impl.cc @@ -14,15 +14,17 @@ #include "time_zone_impl.h" +#include <deque> #include <mutex> #include <string> #include <unordered_map> #include <utility> +#include "absl/base/config.h" #include "time_zone_fixed.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -34,13 +36,16 @@ using TimeZoneImplByName = TimeZoneImplByName* time_zone_map = nullptr; // Mutual exclusion for time_zone_map. -std::mutex time_zone_mutex; +std::mutex& TimeZoneMutex() { + // This mutex is intentionally "leaked" to avoid the static deinitialization + // order fiasco (std::mutex's destructor is not trivial on many platforms). + static std::mutex* time_zone_mutex = new std::mutex; + return *time_zone_mutex; +} } // namespace -time_zone time_zone::Impl::UTC() { - return time_zone(UTCImpl()); -} +time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); } bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { const time_zone::Impl* const utc_impl = UTCImpl(); @@ -55,7 +60,7 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { // Then check, under a shared lock, whether the time zone has already // been loaded. This is the common path. TODO: Move to shared_mutex. { - std::lock_guard<std::mutex> lock(time_zone_mutex); + std::lock_guard<std::mutex> lock(TimeZoneMutex()); if (time_zone_map != nullptr) { TimeZoneImplByName::const_iterator itr = time_zone_map->find(name); if (itr != time_zone_map->end()) { @@ -66,7 +71,7 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { } // Now check again, under an exclusive lock. - std::lock_guard<std::mutex> lock(time_zone_mutex); + std::lock_guard<std::mutex> lock(TimeZoneMutex()); if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName; const Impl*& impl = (*time_zone_map)[name]; if (impl == nullptr) { @@ -85,10 +90,16 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { } void time_zone::Impl::ClearTimeZoneMapTestOnly() { - std::lock_guard<std::mutex> lock(time_zone_mutex); + std::lock_guard<std::mutex> lock(TimeZoneMutex()); if (time_zone_map != nullptr) { - // Existing time_zone::Impl* entries are in the wild, so we simply - // leak them. Future requests will result in reloading the data. + // Existing time_zone::Impl* entries are in the wild, so we can't delete + // them. Instead, we move them to a private container, where they are + // logically unreachable but not "leaked". Future requests will result + // in reloading the data. + static auto* cleared = new std::deque<const time_zone::Impl*>; + for (const auto& element : *time_zone_map) { + cleared->push_back(element.second); + } time_zone_map->clear(); } } @@ -106,5 +117,5 @@ const time_zone::Impl* time_zone::Impl::UTCImpl() { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_impl.h b/absl/time/internal/cctz/src/time_zone_impl.h index 33d3f6bd..69806c10 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.h +++ b/absl/time/internal/cctz/src/time_zone_impl.h @@ -18,13 +18,14 @@ #include <memory> #include <string> +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" #include "time_zone_if.h" #include "time_zone_info.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -86,7 +87,7 @@ class time_zone::Impl { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_ diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc index b45d82fa..f1697cdf 100644 --- a/absl/time/internal/cctz/src/time_zone_info.cc +++ b/absl/time/internal/cctz/src/time_zone_info.cc @@ -45,12 +45,13 @@ #include <sstream> #include <string> +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "time_zone_fixed.h" #include "time_zone_posix.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -66,8 +67,8 @@ const std::int_least32_t kDaysPerYear[2] = {365, 366}; // The day offsets of the beginning of each (1-based) month in non-leap and // leap years respectively (e.g., 335 days before December in a leap year). const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = { - {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, - {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, + {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, }; // We reject leap-second encoded zoneinfo and so assume 60-second minutes. @@ -78,8 +79,8 @@ const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay; // Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay. const std::int_least32_t kSecsPerYear[2] = { - 365 * kSecsPerDay, - 366 * kSecsPerDay, + 365 * kSecsPerDay, + 366 * kSecsPerDay, }; // Single-byte, unsigned numeric values are encoded directly. @@ -173,8 +174,8 @@ inline time_zone::civil_lookup MakeRepeated(const Transition& tr, } inline civil_second YearShift(const civil_second& cs, year_t shift) { - return civil_second(cs.year() + shift, cs.month(), cs.day(), - cs.hour(), cs.minute(), cs.second()); + return civil_second(cs.year() + shift, cs.month(), cs.day(), cs.hour(), + cs.minute(), cs.second()); } } // namespace @@ -217,7 +218,7 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { default_transition_type_ = 0; abbreviations_ = FixedOffsetToAbbr(offset); abbreviations_.append(1, '\0'); // add NUL - future_spec_.clear(); // never needed for a fixed-offset zone + future_spec_.clear(); // never needed for a fixed-offset zone extended_ = false; tt.civil_max = LocalTime(seconds::max().count(), tt).cs; @@ -394,31 +395,24 @@ void TimeZoneInfo::ExtendTransitions(const std::string& name, bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { // Read and validate the header. tzhead tzh; - if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) - return false; + if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) return false; Header hdr; - if (!hdr.Build(tzh)) - return false; + if (!hdr.Build(tzh)) return false; std::size_t time_len = 4; if (tzh.tzh_version[0] != '\0') { // Skip the 4-byte data. - if (zip->Skip(hdr.DataLength(time_len)) != 0) - return false; + if (zip->Skip(hdr.DataLength(time_len)) != 0) return false; // Read and validate the header for the 8-byte data. - if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) - return false; + if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) return false; - if (tzh.tzh_version[0] == '\0') - return false; - if (!hdr.Build(tzh)) - return false; + if (tzh.tzh_version[0] == '\0') return false; + if (!hdr.Build(tzh)) return false; time_len = 8; } - if (hdr.typecnt == 0) - return false; + if (hdr.typecnt == 0) return false; if (hdr.leapcnt != 0) { // This code assumes 60-second minutes so we do not want // the leap-second encoded zoneinfo. We could reverse the @@ -426,16 +420,13 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { // so currently we simply reject such data. return false; } - if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) - return false; - if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) - return false; + if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false; + if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false; // Read the data into a local buffer. std::size_t len = hdr.DataLength(time_len); std::vector<char> tbuf(len); - if (zip->Read(tbuf.data(), len) != len) - return false; + if (zip->Read(tbuf.data(), len) != len) return false; const char* bp = tbuf.data(); // Decode and validate the transitions. @@ -453,10 +444,8 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { bool seen_type_0 = false; for (std::size_t i = 0; i != hdr.timecnt; ++i) { transitions_[i].type_index = Decode8(bp++); - if (transitions_[i].type_index >= hdr.typecnt) - return false; - if (transitions_[i].type_index == 0) - seen_type_0 = true; + if (transitions_[i].type_index >= hdr.typecnt) return false; + if (transitions_[i].type_index == 0) seen_type_0 = true; } // Decode and validate the transition types. @@ -470,8 +459,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { bp += 4; transition_types_[i].is_dst = (Decode8(bp++) != 0); transition_types_[i].abbr_index = Decode8(bp++); - if (transition_types_[i].abbr_index >= hdr.charcnt) - return false; + if (transition_types_[i].abbr_index >= hdr.charcnt) return false; } // Determine the before-first-transition type. @@ -480,13 +468,10 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { std::uint_fast8_t index = 0; if (transition_types_[0].is_dst) { index = transitions_[0].type_index; - while (index != 0 && transition_types_[index].is_dst) - --index; + while (index != 0 && transition_types_[index].is_dst) --index; } - while (index != hdr.typecnt && transition_types_[index].is_dst) - ++index; - if (index != hdr.typecnt) - default_transition_type_ = index; + while (index != hdr.typecnt && transition_types_[index].is_dst) ++index; + if (index != hdr.typecnt) default_transition_type_ = index; } // Copy all the abbreviations. @@ -510,11 +495,9 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { unsigned char ch; // all non-EOF results are positive return (azip->Read(&ch, 1) == 1) ? ch : EOF; }; - if (get_char(zip) != '\n') - return false; + if (get_char(zip) != '\n') return false; for (int c = get_char(zip); c != '\n'; c = get_char(zip)) { - if (c == EOF) - return false; + if (c == EOF) return false; future_spec_.push_back(static_cast<char>(c)); } } @@ -625,18 +608,18 @@ class FileZoneInfoSource : public ZoneInfoSource { : fp_(fp, fclose), len_(len) {} private: - std::unique_ptr<FILE, int(*)(FILE*)> fp_; + std::unique_ptr<FILE, int (*)(FILE*)> fp_; std::size_t len_; }; std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( const std::string& name) { // Use of the "file:" prefix is intended for testing purposes only. - if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); + const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; // Map the time-zone name to a path name. std::string path; - if (name.empty() || name[0] != '/') { + if (pos == name.size() || name[pos] != '/') { const char* tzdir = "/usr/share/zoneinfo"; char* tzdir_env = nullptr; #if defined(_MSC_VER) @@ -651,16 +634,16 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( free(tzdir_env); #endif } - path += name; + path.append(name, pos, std::string::npos); // Open the zoneinfo file. FILE* fp = FOpen(path.c_str(), "rb"); if (fp == nullptr) return nullptr; std::size_t length = 0; if (fseek(fp, 0, SEEK_END) == 0) { - long pos = ftell(fp); - if (pos >= 0) { - length = static_cast<std::size_t>(pos); + long offset = ftell(fp); + if (offset >= 0) { + length = static_cast<std::size_t>(offset); } rewind(fp); } @@ -681,7 +664,7 @@ class AndroidZoneInfoSource : public FileZoneInfoSource { std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( const std::string& name) { // Use of the "file:" prefix is intended for testing purposes only. - if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); + const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; // See Android's libc/tzcode/bionic.cpp for additional information. for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", @@ -710,7 +693,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( const std::int_fast32_t length = Decode32(ebuf + 44); if (start < 0 || length < 0) break; ebuf[40] = '\0'; // ensure zone name is NUL terminated - if (strcmp(name.c_str(), ebuf) == 0) { + if (strcmp(name.c_str() + pos, ebuf) == 0) { if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break; return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource( fp.release(), static_cast<std::size_t>(length), vers)); @@ -749,13 +732,13 @@ time_zone::absolute_lookup TimeZoneInfo::LocalTime( // A civil time in "+offset" looks like (time+offset) in UTC. // Note: We perform two additions in the civil_second domain to // sidestep the chance of overflow in (unix_time + tt.utc_offset). - return {(civil_second() + unix_time) + tt.utc_offset, - tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]}; + return {(civil_second() + unix_time) + tt.utc_offset, tt.utc_offset, + tt.is_dst, &abbreviations_[tt.abbr_index]}; } // BreakTime() translation for a particular transition. -time_zone::absolute_lookup TimeZoneInfo::LocalTime( - std::int_fast64_t unix_time, const Transition& tr) const { +time_zone::absolute_lookup TimeZoneInfo::LocalTime(std::int_fast64_t unix_time, + const Transition& tr) const { const TransitionType& tt = transition_types_[tr.type_index]; // Note: (unix_time - tr.unix_time) will never overflow as we // have ensured that there is always a "nearby" transition. @@ -898,9 +881,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); } -std::string TimeZoneInfo::Version() const { - return version_; -} +std::string TimeZoneInfo::Version() const { return version_; } std::string TimeZoneInfo::Description() const { std::ostringstream oss; @@ -922,8 +903,8 @@ bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp, } std::int_fast64_t unix_time = ToUnixSeconds(tp); const Transition target = {unix_time, 0, civil_second(), civil_second()}; - const Transition* tr = std::upper_bound(begin, end, target, - Transition::ByUnixTime()); + const Transition* tr = + std::upper_bound(begin, end, target, Transition::ByUnixTime()); for (; tr != end; ++tr) { // skip no-op transitions std::uint_fast8_t prev_type_index = (tr == begin) ? default_transition_type_ : tr[-1].type_index; @@ -957,8 +938,8 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp, unix_time += 1; // ceils } const Transition target = {unix_time, 0, civil_second(), civil_second()}; - const Transition* tr = std::lower_bound(begin, end, target, - Transition::ByUnixTime()); + const Transition* tr = + std::lower_bound(begin, end, target, Transition::ByUnixTime()); for (; tr != begin; --tr) { // skip no-op transitions std::uint_fast8_t prev_type_index = (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index; @@ -973,5 +954,5 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp, } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_info.h b/absl/time/internal/cctz/src/time_zone_info.h index c724fa8f..2a10c06c 100644 --- a/absl/time/internal/cctz/src/time_zone_info.h +++ b/absl/time/internal/cctz/src/time_zone_info.h @@ -21,6 +21,7 @@ #include <string> #include <vector> +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" #include "absl/time/internal/cctz/include/cctz/zone_info_source.h" @@ -28,7 +29,7 @@ #include "tzfile.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -73,8 +74,7 @@ class TimeZoneInfo : public TimeZoneIf { // TimeZoneIf implementations. time_zone::absolute_lookup BreakTime( const time_point<seconds>& tp) const override; - time_zone::civil_lookup MakeTime( - const civil_second& cs) const override; + time_zone::civil_lookup MakeTime(const civil_second& cs) const override; bool NextTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const override; bool PrevTransition(const time_point<seconds>& tp, @@ -83,7 +83,7 @@ class TimeZoneInfo : public TimeZoneIf { std::string Description() const override; private: - struct Header { // counts of: + struct Header { // counts of: std::size_t timecnt; // transition times std::size_t typecnt; // transition types std::size_t charcnt; // zone abbreviation characters @@ -115,7 +115,7 @@ class TimeZoneInfo : public TimeZoneIf { std::vector<Transition> transitions_; // ordered by unix_time and civil_sec std::vector<TransitionType> transition_types_; // distinct transition types - std::uint_fast8_t default_transition_type_; // for before first transition + std::uint_fast8_t default_transition_type_; // for before first transition std::string abbreviations_; // all the NUL-terminated abbreviations std::string version_; // the tzdata version if available @@ -132,7 +132,7 @@ class TimeZoneInfo : public TimeZoneIf { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_ diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc index cfe7d55c..47cf84c6 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.cc +++ b/absl/time/internal/cctz/src/time_zone_libc.cc @@ -23,11 +23,12 @@ #include <limits> #include <utility> +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -85,9 +86,7 @@ auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) { } #endif // tm_gmtoff #if defined(tm_zone) -auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { - return tm.tm_zone; -} +auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; } #elif defined(__tm_zone) auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) { return tm.__tm_zone; @@ -104,19 +103,19 @@ auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) { #endif // tm_zone #endif -inline std::tm* gm_time(const std::time_t *timep, std::tm *result) { +inline std::tm* gm_time(const std::time_t* timep, std::tm* result) { #if defined(_WIN32) || defined(_WIN64) - return gmtime_s(result, timep) ? nullptr : result; + return gmtime_s(result, timep) ? nullptr : result; #else - return gmtime_r(timep, result); + return gmtime_r(timep, result); #endif } -inline std::tm* local_time(const std::time_t *timep, std::tm *result) { +inline std::tm* local_time(const std::time_t* timep, std::tm* result) { #if defined(_WIN32) || defined(_WIN64) - return localtime_s(result, timep) ? nullptr : result; + return localtime_s(result, timep) ? nullptr : result; #else - return localtime_r(timep, result); + return localtime_r(timep, result); #endif } @@ -209,8 +208,8 @@ time_zone::absolute_lookup TimeZoneLibC::BreakTime( } const year_t year = tmp->tm_year + year_t{1900}; - al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, + tmp->tm_min, tmp->tm_sec); al.offset = static_cast<int>(tm_gmtoff(*tmp)); al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz al.is_dst = tmp->tm_isdst > 0; @@ -305,5 +304,5 @@ std::string TimeZoneLibC::Description() const { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_libc.h b/absl/time/internal/cctz/src/time_zone_libc.h index 4b1ccb06..1da9039a 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.h +++ b/absl/time/internal/cctz/src/time_zone_libc.h @@ -17,10 +17,11 @@ #include <string> +#include "absl/base/config.h" #include "time_zone_if.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -34,8 +35,7 @@ class TimeZoneLibC : public TimeZoneIf { // TimeZoneIf implementations. time_zone::absolute_lookup BreakTime( const time_point<seconds>& tp) const override; - time_zone::civil_lookup MakeTime( - const civil_second& cs) const override; + time_zone::civil_lookup MakeTime(const civil_second& cs) const override; bool NextTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const override; bool PrevTransition(const time_point<seconds>& tp, @@ -49,7 +49,7 @@ class TimeZoneLibC : public TimeZoneIf { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_ diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc index 772a9375..efdea64b 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" #if defined(__ANDROID__) @@ -23,6 +24,7 @@ #if defined(__APPLE__) #include <CoreFoundation/CFTimeZone.h> + #include <vector> #endif @@ -34,7 +36,7 @@ #include "time_zone_impl.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -67,9 +69,7 @@ int __system_property_get(const char* name, char* value) { } // namespace #endif -std::string time_zone::name() const { - return effective_impl().Name(); -} +std::string time_zone::name() const { return effective_impl().Name(); } time_zone::absolute_lookup time_zone::lookup( const time_point<seconds>& tp) const { @@ -90,9 +90,7 @@ bool time_zone::prev_transition(const time_point<seconds>& tp, return effective_impl().PrevTransition(tp, trans); } -std::string time_zone::version() const { - return effective_impl().Version(); -} +std::string time_zone::version() const { return effective_impl().Version(); } std::string time_zone::description() const { return effective_impl().Description(); @@ -185,5 +183,5 @@ time_zone local_time_zone() { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 1bef7708..99137a08 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/time/internal/cctz/include/cctz/time_zone.h" - #include <chrono> #include <cstddef> #include <cstdlib> @@ -23,614 +21,614 @@ #include <thread> #include <vector> -#include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/time/internal/cctz/include/cctz/civil_time.h" +#include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace chrono = std::chrono; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { namespace { // A list of known time-zone names. -const char* const kTimeZoneNames[] = { - "Africa/Abidjan", - "Africa/Accra", - "Africa/Addis_Ababa", - "Africa/Algiers", - "Africa/Asmara", - "Africa/Asmera", - "Africa/Bamako", - "Africa/Bangui", - "Africa/Banjul", - "Africa/Bissau", - "Africa/Blantyre", - "Africa/Brazzaville", - "Africa/Bujumbura", - "Africa/Cairo", - "Africa/Casablanca", - "Africa/Ceuta", - "Africa/Conakry", - "Africa/Dakar", - "Africa/Dar_es_Salaam", - "Africa/Djibouti", - "Africa/Douala", - "Africa/El_Aaiun", - "Africa/Freetown", - "Africa/Gaborone", - "Africa/Harare", - "Africa/Johannesburg", - "Africa/Juba", - "Africa/Kampala", - "Africa/Khartoum", - "Africa/Kigali", - "Africa/Kinshasa", - "Africa/Lagos", - "Africa/Libreville", - "Africa/Lome", - "Africa/Luanda", - "Africa/Lubumbashi", - "Africa/Lusaka", - "Africa/Malabo", - "Africa/Maputo", - "Africa/Maseru", - "Africa/Mbabane", - "Africa/Mogadishu", - "Africa/Monrovia", - "Africa/Nairobi", - "Africa/Ndjamena", - "Africa/Niamey", - "Africa/Nouakchott", - "Africa/Ouagadougou", - "Africa/Porto-Novo", - "Africa/Sao_Tome", - "Africa/Timbuktu", - "Africa/Tripoli", - "Africa/Tunis", - "Africa/Windhoek", - "America/Adak", - "America/Anchorage", - "America/Anguilla", - "America/Antigua", - "America/Araguaina", - "America/Argentina/Buenos_Aires", - "America/Argentina/Catamarca", - "America/Argentina/ComodRivadavia", - "America/Argentina/Cordoba", - "America/Argentina/Jujuy", - "America/Argentina/La_Rioja", - "America/Argentina/Mendoza", - "America/Argentina/Rio_Gallegos", - "America/Argentina/Salta", - "America/Argentina/San_Juan", - "America/Argentina/San_Luis", - "America/Argentina/Tucuman", - "America/Argentina/Ushuaia", - "America/Aruba", - "America/Asuncion", - "America/Atikokan", - "America/Atka", - "America/Bahia", - "America/Bahia_Banderas", - "America/Barbados", - "America/Belem", - "America/Belize", - "America/Blanc-Sablon", - "America/Boa_Vista", - "America/Bogota", - "America/Boise", - "America/Buenos_Aires", - "America/Cambridge_Bay", - "America/Campo_Grande", - "America/Cancun", - "America/Caracas", - "America/Catamarca", - "America/Cayenne", - "America/Cayman", - "America/Chicago", - "America/Chihuahua", - "America/Coral_Harbour", - "America/Cordoba", - "America/Costa_Rica", - "America/Creston", - "America/Cuiaba", - "America/Curacao", - "America/Danmarkshavn", - "America/Dawson", - "America/Dawson_Creek", - "America/Denver", - "America/Detroit", - "America/Dominica", - "America/Edmonton", - "America/Eirunepe", - "America/El_Salvador", - "America/Ensenada", - "America/Fort_Nelson", - "America/Fort_Wayne", - "America/Fortaleza", - "America/Glace_Bay", - "America/Godthab", - "America/Goose_Bay", - "America/Grand_Turk", - "America/Grenada", - "America/Guadeloupe", - "America/Guatemala", - "America/Guayaquil", - "America/Guyana", - "America/Halifax", - "America/Havana", - "America/Hermosillo", - "America/Indiana/Indianapolis", - "America/Indiana/Knox", - "America/Indiana/Marengo", - "America/Indiana/Petersburg", - "America/Indiana/Tell_City", - "America/Indiana/Vevay", - "America/Indiana/Vincennes", - "America/Indiana/Winamac", - "America/Indianapolis", - "America/Inuvik", - "America/Iqaluit", - "America/Jamaica", - "America/Jujuy", - "America/Juneau", - "America/Kentucky/Louisville", - "America/Kentucky/Monticello", - "America/Knox_IN", - "America/Kralendijk", - "America/La_Paz", - "America/Lima", - "America/Los_Angeles", - "America/Louisville", - "America/Lower_Princes", - "America/Maceio", - "America/Managua", - "America/Manaus", - "America/Marigot", - "America/Martinique", - "America/Matamoros", - "America/Mazatlan", - "America/Mendoza", - "America/Menominee", - "America/Merida", - "America/Metlakatla", - "America/Mexico_City", - "America/Miquelon", - "America/Moncton", - "America/Monterrey", - "America/Montevideo", - "America/Montreal", - "America/Montserrat", - "America/Nassau", - "America/New_York", - "America/Nipigon", - "America/Nome", - "America/Noronha", - "America/North_Dakota/Beulah", - "America/North_Dakota/Center", - "America/North_Dakota/New_Salem", - "America/Ojinaga", - "America/Panama", - "America/Pangnirtung", - "America/Paramaribo", - "America/Phoenix", - "America/Port-au-Prince", - "America/Port_of_Spain", - "America/Porto_Acre", - "America/Porto_Velho", - "America/Puerto_Rico", - "America/Punta_Arenas", - "America/Rainy_River", - "America/Rankin_Inlet", - "America/Recife", - "America/Regina", - "America/Resolute", - "America/Rio_Branco", - "America/Rosario", - "America/Santa_Isabel", - "America/Santarem", - "America/Santiago", - "America/Santo_Domingo", - "America/Sao_Paulo", - "America/Scoresbysund", - "America/Shiprock", - "America/Sitka", - "America/St_Barthelemy", - "America/St_Johns", - "America/St_Kitts", - "America/St_Lucia", - "America/St_Thomas", - "America/St_Vincent", - "America/Swift_Current", - "America/Tegucigalpa", - "America/Thule", - "America/Thunder_Bay", - "America/Tijuana", - "America/Toronto", - "America/Tortola", - "America/Vancouver", - "America/Virgin", - "America/Whitehorse", - "America/Winnipeg", - "America/Yakutat", - "America/Yellowknife", - "Antarctica/Casey", - "Antarctica/Davis", - "Antarctica/DumontDUrville", - "Antarctica/Macquarie", - "Antarctica/Mawson", - "Antarctica/McMurdo", - "Antarctica/Palmer", - "Antarctica/Rothera", - "Antarctica/South_Pole", - "Antarctica/Syowa", - "Antarctica/Troll", - "Antarctica/Vostok", - "Arctic/Longyearbyen", - "Asia/Aden", - "Asia/Almaty", - "Asia/Amman", - "Asia/Anadyr", - "Asia/Aqtau", - "Asia/Aqtobe", - "Asia/Ashgabat", - "Asia/Ashkhabad", - "Asia/Atyrau", - "Asia/Baghdad", - "Asia/Bahrain", - "Asia/Baku", - "Asia/Bangkok", - "Asia/Barnaul", - "Asia/Beirut", - "Asia/Bishkek", - "Asia/Brunei", - "Asia/Calcutta", - "Asia/Chita", - "Asia/Choibalsan", - "Asia/Chongqing", - "Asia/Chungking", - "Asia/Colombo", - "Asia/Dacca", - "Asia/Damascus", - "Asia/Dhaka", - "Asia/Dili", - "Asia/Dubai", - "Asia/Dushanbe", - "Asia/Famagusta", - "Asia/Gaza", - "Asia/Harbin", - "Asia/Hebron", - "Asia/Ho_Chi_Minh", - "Asia/Hong_Kong", - "Asia/Hovd", - "Asia/Irkutsk", - "Asia/Istanbul", - "Asia/Jakarta", - "Asia/Jayapura", - "Asia/Jerusalem", - "Asia/Kabul", - "Asia/Kamchatka", - "Asia/Karachi", - "Asia/Kashgar", - "Asia/Kathmandu", - "Asia/Katmandu", - "Asia/Khandyga", - "Asia/Kolkata", - "Asia/Krasnoyarsk", - "Asia/Kuala_Lumpur", - "Asia/Kuching", - "Asia/Kuwait", - "Asia/Macao", - "Asia/Macau", - "Asia/Magadan", - "Asia/Makassar", - "Asia/Manila", - "Asia/Muscat", - "Asia/Nicosia", - "Asia/Novokuznetsk", - "Asia/Novosibirsk", - "Asia/Omsk", - "Asia/Oral", - "Asia/Phnom_Penh", - "Asia/Pontianak", - "Asia/Pyongyang", - "Asia/Qatar", - "Asia/Qostanay", - "Asia/Qyzylorda", - "Asia/Rangoon", - "Asia/Riyadh", - "Asia/Saigon", - "Asia/Sakhalin", - "Asia/Samarkand", - "Asia/Seoul", - "Asia/Shanghai", - "Asia/Singapore", - "Asia/Srednekolymsk", - "Asia/Taipei", - "Asia/Tashkent", - "Asia/Tbilisi", - "Asia/Tehran", - "Asia/Tel_Aviv", - "Asia/Thimbu", - "Asia/Thimphu", - "Asia/Tokyo", - "Asia/Tomsk", - "Asia/Ujung_Pandang", - "Asia/Ulaanbaatar", - "Asia/Ulan_Bator", - "Asia/Urumqi", - "Asia/Ust-Nera", - "Asia/Vientiane", - "Asia/Vladivostok", - "Asia/Yakutsk", - "Asia/Yangon", - "Asia/Yekaterinburg", - "Asia/Yerevan", - "Atlantic/Azores", - "Atlantic/Bermuda", - "Atlantic/Canary", - "Atlantic/Cape_Verde", - "Atlantic/Faeroe", - "Atlantic/Faroe", - "Atlantic/Jan_Mayen", - "Atlantic/Madeira", - "Atlantic/Reykjavik", - "Atlantic/South_Georgia", - "Atlantic/St_Helena", - "Atlantic/Stanley", - "Australia/ACT", - "Australia/Adelaide", - "Australia/Brisbane", - "Australia/Broken_Hill", - "Australia/Canberra", - "Australia/Currie", - "Australia/Darwin", - "Australia/Eucla", - "Australia/Hobart", - "Australia/LHI", - "Australia/Lindeman", - "Australia/Lord_Howe", - "Australia/Melbourne", - "Australia/NSW", - "Australia/North", - "Australia/Perth", - "Australia/Queensland", - "Australia/South", - "Australia/Sydney", - "Australia/Tasmania", - "Australia/Victoria", - "Australia/West", - "Australia/Yancowinna", - "Brazil/Acre", - "Brazil/DeNoronha", - "Brazil/East", - "Brazil/West", - "CET", - "CST6CDT", - "Canada/Atlantic", - "Canada/Central", - "Canada/Eastern", - "Canada/Mountain", - "Canada/Newfoundland", - "Canada/Pacific", - "Canada/Saskatchewan", - "Canada/Yukon", - "Chile/Continental", - "Chile/EasterIsland", - "Cuba", - "EET", - "EST", - "EST5EDT", - "Egypt", - "Eire", - "Etc/GMT", - "Etc/GMT+0", - "Etc/GMT+1", - "Etc/GMT+10", - "Etc/GMT+11", - "Etc/GMT+12", - "Etc/GMT+2", - "Etc/GMT+3", - "Etc/GMT+4", - "Etc/GMT+5", - "Etc/GMT+6", - "Etc/GMT+7", - "Etc/GMT+8", - "Etc/GMT+9", - "Etc/GMT-0", - "Etc/GMT-1", - "Etc/GMT-10", - "Etc/GMT-11", - "Etc/GMT-12", - "Etc/GMT-13", - "Etc/GMT-14", - "Etc/GMT-2", - "Etc/GMT-3", - "Etc/GMT-4", - "Etc/GMT-5", - "Etc/GMT-6", - "Etc/GMT-7", - "Etc/GMT-8", - "Etc/GMT-9", - "Etc/GMT0", - "Etc/Greenwich", - "Etc/UCT", - "Etc/UTC", - "Etc/Universal", - "Etc/Zulu", - "Europe/Amsterdam", - "Europe/Andorra", - "Europe/Astrakhan", - "Europe/Athens", - "Europe/Belfast", - "Europe/Belgrade", - "Europe/Berlin", - "Europe/Bratislava", - "Europe/Brussels", - "Europe/Bucharest", - "Europe/Budapest", - "Europe/Busingen", - "Europe/Chisinau", - "Europe/Copenhagen", - "Europe/Dublin", - "Europe/Gibraltar", - "Europe/Guernsey", - "Europe/Helsinki", - "Europe/Isle_of_Man", - "Europe/Istanbul", - "Europe/Jersey", - "Europe/Kaliningrad", - "Europe/Kiev", - "Europe/Kirov", - "Europe/Lisbon", - "Europe/Ljubljana", - "Europe/London", - "Europe/Luxembourg", - "Europe/Madrid", - "Europe/Malta", - "Europe/Mariehamn", - "Europe/Minsk", - "Europe/Monaco", - "Europe/Moscow", - "Europe/Nicosia", - "Europe/Oslo", - "Europe/Paris", - "Europe/Podgorica", - "Europe/Prague", - "Europe/Riga", - "Europe/Rome", - "Europe/Samara", - "Europe/San_Marino", - "Europe/Sarajevo", - "Europe/Saratov", - "Europe/Simferopol", - "Europe/Skopje", - "Europe/Sofia", - "Europe/Stockholm", - "Europe/Tallinn", - "Europe/Tirane", - "Europe/Tiraspol", - "Europe/Ulyanovsk", - "Europe/Uzhgorod", - "Europe/Vaduz", - "Europe/Vatican", - "Europe/Vienna", - "Europe/Vilnius", - "Europe/Volgograd", - "Europe/Warsaw", - "Europe/Zagreb", - "Europe/Zaporozhye", - "Europe/Zurich", - "GB", - "GB-Eire", - "GMT", - "GMT+0", - "GMT-0", - "GMT0", - "Greenwich", - "HST", - "Hongkong", - "Iceland", - "Indian/Antananarivo", - "Indian/Chagos", - "Indian/Christmas", - "Indian/Cocos", - "Indian/Comoro", - "Indian/Kerguelen", - "Indian/Mahe", - "Indian/Maldives", - "Indian/Mauritius", - "Indian/Mayotte", - "Indian/Reunion", - "Iran", - "Israel", - "Jamaica", - "Japan", - "Kwajalein", - "Libya", - "MET", - "MST", - "MST7MDT", - "Mexico/BajaNorte", - "Mexico/BajaSur", - "Mexico/General", - "NZ", - "NZ-CHAT", - "Navajo", - "PRC", - "PST8PDT", - "Pacific/Apia", - "Pacific/Auckland", - "Pacific/Bougainville", - "Pacific/Chatham", - "Pacific/Chuuk", - "Pacific/Easter", - "Pacific/Efate", - "Pacific/Enderbury", - "Pacific/Fakaofo", - "Pacific/Fiji", - "Pacific/Funafuti", - "Pacific/Galapagos", - "Pacific/Gambier", - "Pacific/Guadalcanal", - "Pacific/Guam", - "Pacific/Honolulu", - "Pacific/Johnston", - "Pacific/Kiritimati", - "Pacific/Kosrae", - "Pacific/Kwajalein", - "Pacific/Majuro", - "Pacific/Marquesas", - "Pacific/Midway", - "Pacific/Nauru", - "Pacific/Niue", - "Pacific/Norfolk", - "Pacific/Noumea", - "Pacific/Pago_Pago", - "Pacific/Palau", - "Pacific/Pitcairn", - "Pacific/Pohnpei", - "Pacific/Ponape", - "Pacific/Port_Moresby", - "Pacific/Rarotonga", - "Pacific/Saipan", - "Pacific/Samoa", - "Pacific/Tahiti", - "Pacific/Tarawa", - "Pacific/Tongatapu", - "Pacific/Truk", - "Pacific/Wake", - "Pacific/Wallis", - "Pacific/Yap", - "Poland", - "Portugal", - "ROC", - "ROK", - "Singapore", - "Turkey", - "UCT", - "US/Alaska", - "US/Aleutian", - "US/Arizona", - "US/Central", - "US/East-Indiana", - "US/Eastern", - "US/Hawaii", - "US/Indiana-Starke", - "US/Michigan", - "US/Mountain", - "US/Pacific", - "US/Samoa", - "UTC", - "Universal", - "W-SU", - "WET", - "Zulu", - nullptr -}; +const char* const kTimeZoneNames[] = {"Africa/Abidjan", + "Africa/Accra", + "Africa/Addis_Ababa", + "Africa/Algiers", + "Africa/Asmara", + "Africa/Asmera", + "Africa/Bamako", + "Africa/Bangui", + "Africa/Banjul", + "Africa/Bissau", + "Africa/Blantyre", + "Africa/Brazzaville", + "Africa/Bujumbura", + "Africa/Cairo", + "Africa/Casablanca", + "Africa/Ceuta", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Douala", + "Africa/El_Aaiun", + "Africa/Freetown", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Juba", + "Africa/Kampala", + "Africa/Khartoum", + "Africa/Kigali", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Lome", + "Africa/Luanda", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Malabo", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Africa/Mogadishu", + "Africa/Monrovia", + "Africa/Nairobi", + "Africa/Ndjamena", + "Africa/Niamey", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Porto-Novo", + "Africa/Sao_Tome", + "Africa/Timbuktu", + "Africa/Tripoli", + "Africa/Tunis", + "Africa/Windhoek", + "America/Adak", + "America/Anchorage", + "America/Anguilla", + "America/Antigua", + "America/Araguaina", + "America/Argentina/Buenos_Aires", + "America/Argentina/Catamarca", + "America/Argentina/ComodRivadavia", + "America/Argentina/Cordoba", + "America/Argentina/Jujuy", + "America/Argentina/La_Rioja", + "America/Argentina/Mendoza", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Aruba", + "America/Asuncion", + "America/Atikokan", + "America/Atka", + "America/Bahia", + "America/Bahia_Banderas", + "America/Barbados", + "America/Belem", + "America/Belize", + "America/Blanc-Sablon", + "America/Boa_Vista", + "America/Bogota", + "America/Boise", + "America/Buenos_Aires", + "America/Cambridge_Bay", + "America/Campo_Grande", + "America/Cancun", + "America/Caracas", + "America/Catamarca", + "America/Cayenne", + "America/Cayman", + "America/Chicago", + "America/Chihuahua", + "America/Coral_Harbour", + "America/Cordoba", + "America/Costa_Rica", + "America/Creston", + "America/Cuiaba", + "America/Curacao", + "America/Danmarkshavn", + "America/Dawson", + "America/Dawson_Creek", + "America/Denver", + "America/Detroit", + "America/Dominica", + "America/Edmonton", + "America/Eirunepe", + "America/El_Salvador", + "America/Ensenada", + "America/Fort_Nelson", + "America/Fort_Wayne", + "America/Fortaleza", + "America/Glace_Bay", + "America/Godthab", + "America/Goose_Bay", + "America/Grand_Turk", + "America/Grenada", + "America/Guadeloupe", + "America/Guatemala", + "America/Guayaquil", + "America/Guyana", + "America/Halifax", + "America/Havana", + "America/Hermosillo", + "America/Indiana/Indianapolis", + "America/Indiana/Knox", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Tell_City", + "America/Indiana/Vevay", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Indianapolis", + "America/Inuvik", + "America/Iqaluit", + "America/Jamaica", + "America/Jujuy", + "America/Juneau", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/Knox_IN", + "America/Kralendijk", + "America/La_Paz", + "America/Lima", + "America/Los_Angeles", + "America/Louisville", + "America/Lower_Princes", + "America/Maceio", + "America/Managua", + "America/Manaus", + "America/Marigot", + "America/Martinique", + "America/Matamoros", + "America/Mazatlan", + "America/Mendoza", + "America/Menominee", + "America/Merida", + "America/Metlakatla", + "America/Mexico_City", + "America/Miquelon", + "America/Moncton", + "America/Monterrey", + "America/Montevideo", + "America/Montreal", + "America/Montserrat", + "America/Nassau", + "America/New_York", + "America/Nipigon", + "America/Nome", + "America/Noronha", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Ojinaga", + "America/Panama", + "America/Pangnirtung", + "America/Paramaribo", + "America/Phoenix", + "America/Port-au-Prince", + "America/Port_of_Spain", + "America/Porto_Acre", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Punta_Arenas", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Recife", + "America/Regina", + "America/Resolute", + "America/Rio_Branco", + "America/Rosario", + "America/Santa_Isabel", + "America/Santarem", + "America/Santiago", + "America/Santo_Domingo", + "America/Sao_Paulo", + "America/Scoresbysund", + "America/Shiprock", + "America/Sitka", + "America/St_Barthelemy", + "America/St_Johns", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Swift_Current", + "America/Tegucigalpa", + "America/Thule", + "America/Thunder_Bay", + "America/Tijuana", + "America/Toronto", + "America/Tortola", + "America/Vancouver", + "America/Virgin", + "America/Whitehorse", + "America/Winnipeg", + "America/Yakutat", + "America/Yellowknife", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/DumontDUrville", + "Antarctica/Macquarie", + "Antarctica/Mawson", + "Antarctica/McMurdo", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/South_Pole", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok", + "Arctic/Longyearbyen", + "Asia/Aden", + "Asia/Almaty", + "Asia/Amman", + "Asia/Anadyr", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Ashkhabad", + "Asia/Atyrau", + "Asia/Baghdad", + "Asia/Bahrain", + "Asia/Baku", + "Asia/Bangkok", + "Asia/Barnaul", + "Asia/Beirut", + "Asia/Bishkek", + "Asia/Brunei", + "Asia/Calcutta", + "Asia/Chita", + "Asia/Choibalsan", + "Asia/Chongqing", + "Asia/Chungking", + "Asia/Colombo", + "Asia/Dacca", + "Asia/Damascus", + "Asia/Dhaka", + "Asia/Dili", + "Asia/Dubai", + "Asia/Dushanbe", + "Asia/Famagusta", + "Asia/Gaza", + "Asia/Harbin", + "Asia/Hebron", + "Asia/Ho_Chi_Minh", + "Asia/Hong_Kong", + "Asia/Hovd", + "Asia/Irkutsk", + "Asia/Istanbul", + "Asia/Jakarta", + "Asia/Jayapura", + "Asia/Jerusalem", + "Asia/Kabul", + "Asia/Kamchatka", + "Asia/Karachi", + "Asia/Kashgar", + "Asia/Kathmandu", + "Asia/Katmandu", + "Asia/Khandyga", + "Asia/Kolkata", + "Asia/Krasnoyarsk", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Kuwait", + "Asia/Macao", + "Asia/Macau", + "Asia/Magadan", + "Asia/Makassar", + "Asia/Manila", + "Asia/Muscat", + "Asia/Nicosia", + "Asia/Novokuznetsk", + "Asia/Novosibirsk", + "Asia/Omsk", + "Asia/Oral", + "Asia/Phnom_Penh", + "Asia/Pontianak", + "Asia/Pyongyang", + "Asia/Qatar", + "Asia/Qostanay", + "Asia/Qyzylorda", + "Asia/Rangoon", + "Asia/Riyadh", + "Asia/Saigon", + "Asia/Sakhalin", + "Asia/Samarkand", + "Asia/Seoul", + "Asia/Shanghai", + "Asia/Singapore", + "Asia/Srednekolymsk", + "Asia/Taipei", + "Asia/Tashkent", + "Asia/Tbilisi", + "Asia/Tehran", + "Asia/Tel_Aviv", + "Asia/Thimbu", + "Asia/Thimphu", + "Asia/Tokyo", + "Asia/Tomsk", + "Asia/Ujung_Pandang", + "Asia/Ulaanbaatar", + "Asia/Ulan_Bator", + "Asia/Urumqi", + "Asia/Ust-Nera", + "Asia/Vientiane", + "Asia/Vladivostok", + "Asia/Yakutsk", + "Asia/Yangon", + "Asia/Yekaterinburg", + "Asia/Yerevan", + "Atlantic/Azores", + "Atlantic/Bermuda", + "Atlantic/Canary", + "Atlantic/Cape_Verde", + "Atlantic/Faeroe", + "Atlantic/Faroe", + "Atlantic/Jan_Mayen", + "Atlantic/Madeira", + "Atlantic/Reykjavik", + "Atlantic/South_Georgia", + "Atlantic/St_Helena", + "Atlantic/Stanley", + "Australia/ACT", + "Australia/Adelaide", + "Australia/Brisbane", + "Australia/Broken_Hill", + "Australia/Canberra", + "Australia/Currie", + "Australia/Darwin", + "Australia/Eucla", + "Australia/Hobart", + "Australia/LHI", + "Australia/Lindeman", + "Australia/Lord_Howe", + "Australia/Melbourne", + "Australia/NSW", + "Australia/North", + "Australia/Perth", + "Australia/Queensland", + "Australia/South", + "Australia/Sydney", + "Australia/Tasmania", + "Australia/Victoria", + "Australia/West", + "Australia/Yancowinna", + "Brazil/Acre", + "Brazil/DeNoronha", + "Brazil/East", + "Brazil/West", + "CET", + "CST6CDT", + "Canada/Atlantic", + "Canada/Central", + "Canada/Eastern", + "Canada/Mountain", + "Canada/Newfoundland", + "Canada/Pacific", + "Canada/Saskatchewan", + "Canada/Yukon", + "Chile/Continental", + "Chile/EasterIsland", + "Cuba", + "EET", + "EST", + "EST5EDT", + "Egypt", + "Eire", + "Etc/GMT", + "Etc/GMT+0", + "Etc/GMT+1", + "Etc/GMT+10", + "Etc/GMT+11", + "Etc/GMT+12", + "Etc/GMT+2", + "Etc/GMT+3", + "Etc/GMT+4", + "Etc/GMT+5", + "Etc/GMT+6", + "Etc/GMT+7", + "Etc/GMT+8", + "Etc/GMT+9", + "Etc/GMT-0", + "Etc/GMT-1", + "Etc/GMT-10", + "Etc/GMT-11", + "Etc/GMT-12", + "Etc/GMT-13", + "Etc/GMT-14", + "Etc/GMT-2", + "Etc/GMT-3", + "Etc/GMT-4", + "Etc/GMT-5", + "Etc/GMT-6", + "Etc/GMT-7", + "Etc/GMT-8", + "Etc/GMT-9", + "Etc/GMT0", + "Etc/Greenwich", + "Etc/UCT", + "Etc/UTC", + "Etc/Universal", + "Etc/Zulu", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Astrakhan", + "Europe/Athens", + "Europe/Belfast", + "Europe/Belgrade", + "Europe/Berlin", + "Europe/Bratislava", + "Europe/Brussels", + "Europe/Bucharest", + "Europe/Budapest", + "Europe/Busingen", + "Europe/Chisinau", + "Europe/Copenhagen", + "Europe/Dublin", + "Europe/Gibraltar", + "Europe/Guernsey", + "Europe/Helsinki", + "Europe/Isle_of_Man", + "Europe/Istanbul", + "Europe/Jersey", + "Europe/Kaliningrad", + "Europe/Kiev", + "Europe/Kirov", + "Europe/Lisbon", + "Europe/Ljubljana", + "Europe/London", + "Europe/Luxembourg", + "Europe/Madrid", + "Europe/Malta", + "Europe/Mariehamn", + "Europe/Minsk", + "Europe/Monaco", + "Europe/Moscow", + "Europe/Nicosia", + "Europe/Oslo", + "Europe/Paris", + "Europe/Podgorica", + "Europe/Prague", + "Europe/Riga", + "Europe/Rome", + "Europe/Samara", + "Europe/San_Marino", + "Europe/Sarajevo", + "Europe/Saratov", + "Europe/Simferopol", + "Europe/Skopje", + "Europe/Sofia", + "Europe/Stockholm", + "Europe/Tallinn", + "Europe/Tirane", + "Europe/Tiraspol", + "Europe/Ulyanovsk", + "Europe/Uzhgorod", + "Europe/Vaduz", + "Europe/Vatican", + "Europe/Vienna", + "Europe/Vilnius", + "Europe/Volgograd", + "Europe/Warsaw", + "Europe/Zagreb", + "Europe/Zaporozhye", + "Europe/Zurich", + "GB", + "GB-Eire", + "GMT", + "GMT+0", + "GMT-0", + "GMT0", + "Greenwich", + "HST", + "Hongkong", + "Iceland", + "Indian/Antananarivo", + "Indian/Chagos", + "Indian/Christmas", + "Indian/Cocos", + "Indian/Comoro", + "Indian/Kerguelen", + "Indian/Mahe", + "Indian/Maldives", + "Indian/Mauritius", + "Indian/Mayotte", + "Indian/Reunion", + "Iran", + "Israel", + "Jamaica", + "Japan", + "Kwajalein", + "Libya", + "MET", + "MST", + "MST7MDT", + "Mexico/BajaNorte", + "Mexico/BajaSur", + "Mexico/General", + "NZ", + "NZ-CHAT", + "Navajo", + "PRC", + "PST8PDT", + "Pacific/Apia", + "Pacific/Auckland", + "Pacific/Bougainville", + "Pacific/Chatham", + "Pacific/Chuuk", + "Pacific/Easter", + "Pacific/Efate", + "Pacific/Enderbury", + "Pacific/Fakaofo", + "Pacific/Fiji", + "Pacific/Funafuti", + "Pacific/Galapagos", + "Pacific/Gambier", + "Pacific/Guadalcanal", + "Pacific/Guam", + "Pacific/Honolulu", + "Pacific/Johnston", + "Pacific/Kiritimati", + "Pacific/Kosrae", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Marquesas", + "Pacific/Midway", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Norfolk", + "Pacific/Noumea", + "Pacific/Pago_Pago", + "Pacific/Palau", + "Pacific/Pitcairn", + "Pacific/Pohnpei", + "Pacific/Ponape", + "Pacific/Port_Moresby", + "Pacific/Rarotonga", + "Pacific/Saipan", + "Pacific/Samoa", + "Pacific/Tahiti", + "Pacific/Tarawa", + "Pacific/Tongatapu", + "Pacific/Truk", + "Pacific/Wake", + "Pacific/Wallis", + "Pacific/Yap", + "Poland", + "Portugal", + "ROC", + "ROK", + "Singapore", + "Turkey", + "UCT", + "US/Alaska", + "US/Aleutian", + "US/Arizona", + "US/Central", + "US/East-Indiana", + "US/Eastern", + "US/Hawaii", + "US/Indiana-Starke", + "US/Michigan", + "US/Mountain", + "US/Pacific", + "US/Samoa", + "UTC", + "Universal", + "W-SU", + "WET", + "Zulu", + nullptr}; // Helper to return a loaded time zone by value (UTC on error). time_zone LoadZone(const std::string& name) { @@ -725,7 +723,8 @@ TEST(TimeZone, NamedTimeZones) { EXPECT_EQ("America/New_York", nyc.name()); const time_zone syd = LoadZone("Australia/Sydney"); EXPECT_EQ("Australia/Sydney", syd.name()); - const time_zone fixed0 = fixed_time_zone(absl::time_internal::cctz::seconds::zero()); + const time_zone fixed0 = + fixed_time_zone(absl::time_internal::cctz::seconds::zero()); EXPECT_EQ("UTC", fixed0.name()); const time_zone fixed_pos = fixed_time_zone( chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45)); @@ -768,7 +767,8 @@ TEST(TimeZone, Equality) { EXPECT_EQ(implicit_utc, explicit_utc); EXPECT_EQ(implicit_utc.name(), explicit_utc.name()); - const time_zone fixed_zero = fixed_time_zone(absl::time_internal::cctz::seconds::zero()); + const time_zone fixed_zero = + fixed_time_zone(absl::time_internal::cctz::seconds::zero()); EXPECT_EQ(fixed_zero, LoadZone(fixed_zero.name())); EXPECT_EQ(fixed_zero, explicit_utc); @@ -807,8 +807,8 @@ TEST(TimeZone, Equality) { TEST(StdChronoTimePoint, TimeTAlignment) { // Ensures that the Unix epoch and the system clock epoch are an integral // number of seconds apart. This simplifies conversions to/from time_t. - auto diff = chrono::system_clock::time_point() - - chrono::system_clock::from_time_t(0); + auto diff = + chrono::system_clock::time_point() - chrono::system_clock::from_time_t(0); EXPECT_EQ(chrono::system_clock::time_point::duration::zero(), diff % chrono::seconds(1)); } @@ -817,20 +817,20 @@ TEST(BreakTime, TimePointResolution) { const time_zone utc = utc_time_zone(); const auto t0 = chrono::system_clock::from_time_t(0); - ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc, - 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc, - 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc, - 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc, - 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), utc, - 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc, - 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc, - 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); + ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc, 1970, 1, 1, + 0, 0, 0, 0, false, "UTC"); + ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc, 1970, 1, 1, + 0, 0, 0, 0, false, "UTC"); + ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc, 1970, 1, 1, + 0, 0, 0, 0, false, "UTC"); + ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc, 1970, 1, 1, 0, + 0, 0, 0, false, "UTC"); + ExpectTime(chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), + utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); + ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc, 1970, 1, 1, 0, + 0, 0, 0, false, "UTC"); + ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc, 1970, 1, 1, 0, 0, + 0, 0, false, "UTC"); } TEST(BreakTime, LocalTimeInUTC) { @@ -912,9 +912,8 @@ TEST(MakeTime, TimePointResolution) { chrono::time_point_cast<chrono::minutes>( convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc)); - const time_point<chrono::hours> tp_h = - chrono::time_point_cast<chrono::hours>( - convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); + const time_point<chrono::hours> tp_h = chrono::time_point_cast<chrono::hours>( + convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc)); } @@ -933,7 +932,7 @@ TEST(MakeTime, Normalization) { // NOTE: Run this with -ftrapv to detect overflow problems. TEST(MakeTime, SysSecondsLimits) { - const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez"; + const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez"; const time_zone utc = utc_time_zone(); const time_zone east = fixed_time_zone(chrono::hours(14)); const time_zone west = fixed_time_zone(-chrono::hours(14)); @@ -1029,17 +1028,17 @@ TEST(MakeTime, LocalTimeLibC) { ASSERT_EQ(0, setenv("TZ", *np, 1)); // change what "localtime" means const auto zi = local_time_zone(); const auto lc = LoadZone("libc:localtime"); - time_zone::civil_transition trans; + time_zone::civil_transition transition; for (auto tp = zi.lookup(civil_second()).trans; - zi.next_transition(tp, &trans); - tp = zi.lookup(trans.to).trans) { - const auto fcl = zi.lookup(trans.from); - const auto tcl = zi.lookup(trans.to); + zi.next_transition(tp, &transition); + tp = zi.lookup(transition.to).trans) { + const auto fcl = zi.lookup(transition.from); + const auto tcl = zi.lookup(transition.to); civil_second cs; // compare cs in zi and lc if (fcl.kind == time_zone::civil_lookup::UNIQUE) { if (tcl.kind == time_zone::civil_lookup::UNIQUE) { // Both unique; must be an is_dst or abbr change. - ASSERT_EQ(trans.from, trans.to); + ASSERT_EQ(transition.from, transition.to); const auto trans = fcl.trans; const auto tal = zi.lookup(trans); const auto tprev = trans - absl::time_internal::cctz::seconds(1); @@ -1050,11 +1049,11 @@ TEST(MakeTime, LocalTimeLibC) { continue; } ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind); - cs = trans.to; + cs = transition.to; } else { ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind); ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind); - cs = trans.from; + cs = transition.from; } if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t) const auto cl_zi = zi.lookup(cs); @@ -1434,5 +1433,5 @@ TEST(TimeZoneEdgeCase, UTC5DigitYear) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_posix.cc b/absl/time/internal/cctz/src/time_zone_posix.cc index d1f8ecbe..5cdd09e8 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.cc +++ b/absl/time/internal/cctz/src/time_zone_posix.cc @@ -19,8 +19,10 @@ #include <limits> #include <string> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -153,5 +155,5 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_posix.h b/absl/time/internal/cctz/src/time_zone_posix.h index 07e3427a..0cf29055 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.h +++ b/absl/time/internal/cctz/src/time_zone_posix.h @@ -55,8 +55,10 @@ #include <cstdint> #include <string> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -124,7 +126,7 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res); } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_ diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h index 51b1f1f3..1ed55e0f 100644 --- a/absl/time/internal/cctz/src/tzfile.h +++ b/absl/time/internal/cctz/src/tzfile.h @@ -22,36 +22,35 @@ */ #ifndef TZDIR -#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */ -#endif /* !defined TZDIR */ +#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */ +#endif /* !defined TZDIR */ #ifndef TZDEFAULT -#define TZDEFAULT "/etc/localtime" +#define TZDEFAULT "/etc/localtime" #endif /* !defined TZDEFAULT */ #ifndef TZDEFRULES -#define TZDEFRULES "posixrules" +#define TZDEFRULES "posixrules" #endif /* !defined TZDEFRULES */ - /* See Internet RFC 8536 for more details about the following format. */ /* ** Each file begins with. . . */ -#define TZ_MAGIC "TZif" +#define TZ_MAGIC "TZif" struct tzhead { - char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ - char tzh_reserved[15]; /* reserved; must be zero */ - char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ - char tzh_leapcnt[4]; /* coded number of leap seconds */ - char tzh_timecnt[4]; /* coded number of transition times */ - char tzh_typecnt[4]; /* coded number of local time types */ - char tzh_charcnt[4]; /* coded number of abbr. chars */ + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ + char tzh_reserved[15]; /* reserved; must be zero */ + char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ }; /* @@ -103,21 +102,21 @@ struct tzhead { */ #ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 2000 +#define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES /* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ -#endif /* !defined TZ_MAX_TYPES */ +#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ +#endif /* !defined TZ_MAX_TYPES */ #ifndef TZ_MAX_CHARS -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ - /* (limited by what unsigned chars can hold) */ -#endif /* !defined TZ_MAX_CHARS */ +#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ + /* (limited by what unsigned chars can hold) */ +#endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ -#endif /* !defined TZ_MAX_LEAPS */ +#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ +#endif /* !defined TZ_MAX_LEAPS */ #endif /* !defined TZFILE_H */ diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc index b64950cb..98ea1612 100644 --- a/absl/time/internal/cctz/src/zone_info_source.cc +++ b/absl/time/internal/cctz/src/zone_info_source.cc @@ -14,8 +14,10 @@ #include "absl/time/internal/cctz/include/cctz/zone_info_source.h" +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { @@ -25,11 +27,11 @@ std::string ZoneInfoSource::Version() const { return std::string(); } } // namespace cctz } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz_extension { @@ -39,8 +41,9 @@ namespace { // defers to the fallback factory. std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory( const std::string& name, - const std::function<std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>( - const std::string& name)>& fallback_factory) { + const std::function< + std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>( + const std::string& name)>& fallback_factory) { return fallback_factory(name); } @@ -52,21 +55,52 @@ std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory( #if !defined(__has_attribute) #define __has_attribute(x) 0 #endif -#if __has_attribute(weak) || defined(__GNUC__) -ZoneInfoSourceFactory zone_info_source_factory - __attribute__((weak)) = DefaultFactory; -#elif defined(_MSC_VER) && !defined(_LIBCPP_VERSION) +// MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the +// Windows linker cannot handle that. Nor does the MinGW compiler know how to +// pass "#pragma comment(linker, ...)" to the Windows linker. +#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__) +ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) = + DefaultFactory; +#elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION) extern ZoneInfoSourceFactory zone_info_source_factory; extern ZoneInfoSourceFactory default_factory; ZoneInfoSourceFactory default_factory = DefaultFactory; #if defined(_M_IX86) -#pragma comment( \ - linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA=?default_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA") +#pragma comment( \ + linker, \ + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZA") #elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64) -#pragma comment( \ - linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA=?default_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA") +#pragma comment( \ + linker, \ + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZEA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZEA") #else #error Unsupported MSVC platform #endif // _M_<PLATFORM> @@ -77,5 +111,5 @@ ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory; } // namespace cctz_extension } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index 8d4a43ba..db18f831 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version @@ -1 +1 @@ -2019b +2019c diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit b/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit Binary files differindex 5e022605..e104faa4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton b/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton Binary files differindex 3fa05798..cd78a6f8 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City Binary files differindex 4ce95c15..7bbb653c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville b/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville Binary files differindex f4c4cf96..177836e4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville b/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville Binary files differindex f4c4cf96..177836e4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver b/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver Binary files differindex 0f9f8328..bb60cbce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong Binary files differindex 378a37f9..23d0375f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul Binary files differindex 10d4b21b..508446bb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul Binary files differindex 73182cf6..96199e73 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain Binary files differindex 3fa05798..cd78a6f8 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain +++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific Binary files differindex 0f9f8328..bb60cbce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific +++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels Binary files differindex 9613c981..40d7124e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul Binary files differindex 10d4b21b..508446bb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad Binary files differindex f774ffdb..cc99beab 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna Binary files differindex 696f359d..3582bb15 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Hongkong b/absl/time/internal/cctz/testdata/zoneinfo/Hongkong Binary files differindex 378a37f9..23d0375f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Hongkong +++ b/absl/time/internal/cctz/testdata/zoneinfo/Hongkong diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji Binary files differindex 6bf667d4..d39bf536 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk Binary files differindex 1f6d610e..53c1aad4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/ROK b/absl/time/internal/cctz/testdata/zoneinfo/ROK Binary files differindex 73182cf6..96199e73 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/ROK +++ b/absl/time/internal/cctz/testdata/zoneinfo/ROK diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Turkey b/absl/time/internal/cctz/testdata/zoneinfo/Turkey Binary files differindex 10d4b21b..508446bb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Turkey +++ b/absl/time/internal/cctz/testdata/zoneinfo/Turkey diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan b/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan Binary files differindex 5e022605..e104faa4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan +++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan diff --git a/absl/time/internal/get_current_time_chrono.inc b/absl/time/internal/get_current_time_chrono.inc index d294b3a6..5eeb6406 100644 --- a/absl/time/internal/get_current_time_chrono.inc +++ b/absl/time/internal/get_current_time_chrono.inc @@ -16,7 +16,7 @@ #include <cstdint> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { static int64_t GetCurrentTimeNanosFromSystem() { @@ -27,5 +27,5 @@ static int64_t GetCurrentTimeNanosFromSystem() { } } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/get_current_time_posix.inc b/absl/time/internal/get_current_time_posix.inc index 0ce2a9a9..42072000 100644 --- a/absl/time/internal/get_current_time_posix.inc +++ b/absl/time/internal/get_current_time_posix.inc @@ -7,7 +7,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { static int64_t GetCurrentTimeNanosFromSystem() { @@ -20,5 +20,5 @@ static int64_t GetCurrentTimeNanosFromSystem() { } } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc index 2bc449fe..9bffe121 100644 --- a/absl/time/internal/test_util.cc +++ b/absl/time/internal/test_util.cc @@ -24,7 +24,7 @@ namespace cctz = absl::time_internal::cctz; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { TimeZone LoadTimeZone(const std::string& name) { @@ -34,11 +34,11 @@ TimeZone LoadTimeZone(const std::string& name) { } } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz_extension { namespace { @@ -119,9 +119,12 @@ std::unique_ptr<cctz::ZoneInfoSource> TestFactory( } // namespace +#if !defined(__MINGW32__) +// MinGW does not support the weak symbol extension mechanism. ZoneInfoSourceFactory zone_info_source_factory = TestFactory; +#endif } // namespace cctz_extension } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/internal/test_util.h b/absl/time/internal/test_util.h index 1a320cab..5c4bf1f6 100644 --- a/absl/time/internal/test_util.h +++ b/absl/time/internal/test_util.h @@ -20,14 +20,14 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace time_internal { // Loads the named timezone, but dies on any failure. absl::TimeZone LoadTimeZone(const std::string& name); } // namespace time_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_INTERNAL_TEST_UTIL_H_ diff --git a/absl/time/time.cc b/absl/time/time.cc index 2c7da3ad..6bb36cb3 100644 --- a/absl/time/time.cc +++ b/absl/time/time.cc @@ -47,7 +47,7 @@ namespace cctz = absl::time_internal::cctz; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { @@ -431,9 +431,17 @@ absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour, } absl::Time FromTM(const struct tm& tm, absl::TimeZone tz) { - const CivilSecond cs(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - const auto ti = tz.At(cs); + civil_year_t tm_year = tm.tm_year; + // Avoids years that are too extreme for CivilSecond to normalize. + if (tm_year > 300000000000ll) return InfiniteFuture(); + if (tm_year < -300000000000ll) return InfinitePast(); + int tm_mon = tm.tm_mon; + if (tm_mon == std::numeric_limits<int>::max()) { + tm_mon -= 12; + tm_year += 1; + } + const auto ti = tz.At(CivilSecond(tm_year + 1900, tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec)); return tm.tm_isdst == 0 ? ti.post : ti.pre; } @@ -487,5 +495,5 @@ struct tm ToTM(absl::Time t, absl::TimeZone tz) { return tm; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/time/time.h b/absl/time/time.h index 1f6500e8..33a4a630 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -83,12 +83,13 @@ struct timeval; #include <type_traits> #include <utility> +#include "absl/base/macros.h" #include "absl/strings/string_view.h" #include "absl/time/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN class Duration; // Defined below class Time; // Defined below @@ -381,11 +382,11 @@ constexpr Duration InfiniteDuration(); // of the unit indicated by the factory function's name. The number must be // representable as int64_t. // -// Note: no "Days()" factory function exists because "a day" is ambiguous. +// NOTE: no "Days()" factory function exists because "a day" is ambiguous. // Civil days are not always 24 hours long, and a 24-hour duration often does // not correspond with a civil day. If a 24-hour duration is needed, use -// `absl::Hours(24)`. (If you actually want a civil day, use absl::CivilDay -// from civil_time.h.) +// `absl::Hours(24)`. If you actually want a civil day, use absl::CivilDay +// from civil_time.h. // // Example: // @@ -422,7 +423,9 @@ Duration Milliseconds(T n) { template <typename T, time_internal::EnableIfFloat<T> = 0> Duration Seconds(T n) { if (n >= 0) { // Note: `NaN >= 0` is false. - if (n >= (std::numeric_limits<int64_t>::max)()) return InfiniteDuration(); + if (n >= static_cast<T>((std::numeric_limits<int64_t>::max)())) { + return InfiniteDuration(); + } return time_internal::MakePosDoubleDuration(n); } else { if (std::isnan(n)) @@ -546,7 +549,11 @@ bool ParseDuration(const std::string& dur_string, Duration* d); // Support for flag values of type Duration. Duration flags must be specified // in a format that is valid input for absl::ParseDuration(). +bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error); +std::string AbslUnparseFlag(Duration d); +ABSL_DEPRECATED("Use AbslParseFlag() instead.") bool ParseFlag(const std::string& text, Duration* dst, std::string* error); +ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") std::string UnparseFlag(Duration d); // Time @@ -816,7 +823,11 @@ std::chrono::system_clock::time_point ToChronoTime(Time); // Additionally, if you'd like to specify a time as a count of // seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag // and add that duration to absl::UnixEpoch() to get an absl::Time. +bool AbslParseFlag(absl::string_view text, Time* t, std::string* error); +std::string AbslUnparseFlag(Time t); +ABSL_DEPRECATED("Use AbslParseFlag() instead.") bool ParseFlag(const std::string& text, Time* t, std::string* error); +ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") std::string UnparseFlag(Time t); // TimeZone @@ -1104,7 +1115,7 @@ inline Time FromCivil(CivilSecond ct, TimeZone tz) { // An `absl::TimeConversion` represents the conversion of year, month, day, // hour, minute, and second values (i.e., a civil time), in a particular // `absl::TimeZone`, to a time instant (an absolute time), as returned by -// `absl::ConvertDateTime()`. Lecacy version of `absl::TimeZone::TimeInfo`. +// `absl::ConvertDateTime()`. Legacy version of `absl::TimeZone::TimeInfo`. // // Deprecated. Use `absl::TimeZone::TimeInfo`. struct @@ -1192,15 +1203,18 @@ struct tm ToTM(Time t, TimeZone tz); // time with UTC offset. Also note the use of "%Y": RFC3339 mandates that // years have exactly four digits, but we allow them to take their natural // width. -extern const char RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez -extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez +ABSL_DLL extern const char + RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez +ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez // RFC1123_full // RFC1123_no_wday // // FormatTime()/ParseTime() format specifiers for RFC1123 date/time strings. -extern const char RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z -extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z +ABSL_DLL extern const char + RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z +ABSL_DLL extern const char + RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z // FormatTime() // @@ -1402,8 +1416,7 @@ constexpr Duration FromInt64(int64_t v, std::ratio<3600>) { // IsValidRep64<T>(0) is true if the expression `int64_t{std::declval<T>()}` is // valid. That is, if a T can be assigned to an int64_t without narrowing. template <typename T> -constexpr auto IsValidRep64(int) - -> decltype(int64_t{std::declval<T>()}, bool()) { +constexpr auto IsValidRep64(int) -> decltype(int64_t{std::declval<T>()} == 0) { return true; } template <typename T> @@ -1565,7 +1578,7 @@ constexpr Time FromTimeT(time_t t) { return time_internal::FromUnixDuration(Seconds(t)); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TIME_TIME_H_ diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc index 37af39d9..6f89672c 100644 --- a/absl/time/time_test.cc +++ b/absl/time/time_test.cc @@ -27,6 +27,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/numeric/int128.h" #include "absl/time/clock.h" #include "absl/time/internal/test_util.h" @@ -575,6 +576,50 @@ TEST(Time, ToChronoTime) { absl::ToChronoTime(absl::UnixEpoch() - tick)); } +// Check that absl::int128 works as a std::chrono::duration representation. +TEST(Time, Chrono128) { + // Define a std::chrono::time_point type whose time[sic]_since_epoch() is + // a signed 128-bit count of attoseconds. This has a range and resolution + // (currently) beyond those of absl::Time, and undoubtedly also beyond those + // of std::chrono::system_clock::time_point. + // + // Note: The to/from-chrono support should probably be updated to handle + // such wide representations. + using Timestamp = + std::chrono::time_point<std::chrono::system_clock, + std::chrono::duration<absl::int128, std::atto>>; + + // Expect that we can round-trip the std::chrono::system_clock::time_point + // extremes through both absl::Time and Timestamp, and that Timestamp can + // handle the (current) absl::Time extremes. + // + // Note: We should use std::chrono::floor() instead of time_point_cast(), + // but floor() is only available since c++17. + for (const auto tp : {std::chrono::system_clock::time_point::min(), + std::chrono::system_clock::time_point::max()}) { + EXPECT_EQ(tp, absl::ToChronoTime(absl::FromChrono(tp))); + EXPECT_EQ(tp, std::chrono::time_point_cast< + std::chrono::system_clock::time_point::duration>( + std::chrono::time_point_cast<Timestamp::duration>(tp))); + } + Timestamp::duration::rep v = std::numeric_limits<int64_t>::min(); + v *= Timestamp::duration::period::den; + auto ts = Timestamp(Timestamp::duration(v)); + ts += std::chrono::duration<int64_t, std::atto>(0); + EXPECT_EQ(std::numeric_limits<int64_t>::min(), + ts.time_since_epoch().count() / Timestamp::duration::period::den); + EXPECT_EQ(0, + ts.time_since_epoch().count() % Timestamp::duration::period::den); + v = std::numeric_limits<int64_t>::max(); + v *= Timestamp::duration::period::den; + ts = Timestamp(Timestamp::duration(v)); + ts += std::chrono::duration<int64_t, std::atto>(999999999750000000); + EXPECT_EQ(std::numeric_limits<int64_t>::max(), + ts.time_since_epoch().count() / Timestamp::duration::period::den); + EXPECT_EQ(999999999750000000, + ts.time_since_epoch().count() % Timestamp::duration::period::den); +} + TEST(Time, TimeZoneAt) { const absl::TimeZone nyc = absl::time_internal::LoadTimeZone("America/New_York"); @@ -795,6 +840,30 @@ TEST(Time, FromTM) { tm.tm_isdst = 1; t = FromTM(tm, nyc); EXPECT_EQ("2014-03-09T03:30:42-04:00", absl::FormatTime(t, nyc)); // DST + + // Adjusts tm to refer to a time with a year larger than 2147483647. + tm.tm_year = 2147483647 - 1900 + 1; + tm.tm_mon = 6 - 1; + tm.tm_mday = 28; + tm.tm_hour = 1; + tm.tm_min = 2; + tm.tm_sec = 3; + tm.tm_isdst = -1; + t = FromTM(tm, absl::UTCTimeZone()); + EXPECT_EQ("2147483648-06-28T01:02:03+00:00", + absl::FormatTime(t, absl::UTCTimeZone())); + + // Adjusts tm to refer to a time with a very large month. + tm.tm_year = 2019 - 1900; + tm.tm_mon = 2147483647; + tm.tm_mday = 28; + tm.tm_hour = 1; + tm.tm_min = 2; + tm.tm_sec = 3; + tm.tm_isdst = -1; + t = FromTM(tm, absl::UTCTimeZone()); + EXPECT_EQ("178958989-08-28T01:02:03+00:00", + absl::FormatTime(t, absl::UTCTimeZone())); } TEST(Time, TMRoundTrip) { diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 66ecb044..f2ea9f39 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel @@ -14,12 +14,11 @@ # limitations under the License. # +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", "ABSL_DEFAULT_LINKOPTS", - "ABSL_EXCEPTIONS_FLAG", - "ABSL_EXCEPTIONS_FLAG_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -58,12 +57,12 @@ cc_library( "bad_any_cast.cc", "bad_any_cast.h", ], - copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ - "//absl/base", "//absl/base:config", + "//absl/base:raw_logging_internal", ], ) @@ -73,31 +72,13 @@ cc_test( srcs = [ "any_test.cc", ], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, - deps = [ - ":any", - "//absl/base", - "//absl/base:config", - "//absl/base:exception_testing", - "//absl/container:test_instance_tracker", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "any_test_noexceptions", - size = "small", - srcs = [ - "any_test.cc", - ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":any", - "//absl/base", "//absl/base:config", "//absl/base:exception_testing", + "//absl/base:raw_logging_internal", "//absl/container:test_instance_tracker", "@com_google_googletest//:gtest_main", ], @@ -106,10 +87,11 @@ cc_test( cc_test( name = "any_exception_safety_test", srcs = ["any_exception_safety_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":any", + "//absl/base:config", "//absl/base:exception_safety_testing", "@com_google_googletest//:gtest_main", ], @@ -137,25 +119,6 @@ cc_test( name = "span_test", size = "small", srcs = ["span_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, - deps = [ - ":span", - "//absl/base:config", - "//absl/base:core_headers", - "//absl/base:exception_testing", - "//absl/container:fixed_array", - "//absl/container:inlined_vector", - "//absl/hash:hash_testing", - "//absl/strings", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "span_test_noexceptions", - size = "small", - srcs = ["span_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ @@ -192,11 +155,11 @@ cc_library( name = "bad_optional_access", srcs = ["bad_optional_access.cc"], hdrs = ["bad_optional_access.h"], - copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - "//absl/base", "//absl/base:config", + "//absl/base:raw_logging_internal", ], ) @@ -204,11 +167,11 @@ cc_library( name = "bad_variant_access", srcs = ["bad_variant_access.cc"], hdrs = ["bad_variant_access.h"], - copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - "//absl/base", "//absl/base:config", + "//absl/base:raw_logging_internal", ], ) @@ -218,12 +181,12 @@ cc_test( srcs = [ "optional_test.cc", ], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":optional", - "//absl/base", "//absl/base:config", + "//absl/base:raw_logging_internal", "//absl/meta:type_traits", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -235,16 +198,51 @@ cc_test( srcs = [ "optional_exception_safety_test.cc", ], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":optional", + "//absl/base:config", "//absl/base:exception_safety_testing", "@com_google_googletest//:gtest_main", ], ) cc_library( + name = "conformance_testing", + testonly = 1, + hdrs = [ + "internal/conformance_aliases.h", + "internal/conformance_archetype.h", + "internal/conformance_profile.h", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/debugging:demangle_internal", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/utility", + "@com_google_googletest//:gtest", + ], +) + +cc_test( + name = "conformance_testing_test", + size = "small", + srcs = [ + "internal/conformance_testing_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":conformance_testing", + "//absl/meta:type_traits", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( name = "variant", srcs = ["internal/variant.h"], hdrs = ["variant.h"], @@ -264,8 +262,8 @@ cc_test( name = "variant_test", size = "small", srcs = ["variant_test.cc"], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":variant", "//absl/base:config", @@ -298,8 +296,8 @@ cc_test( srcs = [ "variant_exception_safety_test.cc", ], - copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":variant", "//absl/base:config", diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index 4ce685da..c7c88250 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt @@ -50,12 +50,9 @@ absl_cc_library( "bad_any_cast.cc" COPTS ${ABSL_DEFAULT_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS - absl::base absl::config + absl::raw_logging_internal ) absl_cc_test( @@ -65,14 +62,11 @@ absl_cc_test( "any_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::any - absl::base absl::config absl::exception_testing + absl::raw_logging_internal absl::test_instance_tracker gmock_main ) @@ -86,9 +80,9 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::any - absl::base absl::config absl::exception_testing + absl::raw_logging_internal absl::test_instance_tracker gmock_main ) @@ -100,11 +94,9 @@ absl_cc_test( "any_exception_safety_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::any + absl::config absl::exception_safety_testing gmock_main ) @@ -133,9 +125,6 @@ absl_cc_test( "span_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::span absl::base @@ -198,12 +187,9 @@ absl_cc_library( "bad_optional_access.cc" COPTS ${ABSL_DEFAULT_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS - absl::base absl::config + absl::raw_logging_internal PUBLIC ) @@ -216,12 +202,9 @@ absl_cc_library( "bad_variant_access.cc" COPTS ${ABSL_DEFAULT_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS - absl::base absl::config + absl::raw_logging_internal PUBLIC ) @@ -232,15 +215,12 @@ absl_cc_test( "optional_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::optional - absl::base absl::config - absl::type_traits + absl::raw_logging_internal absl::strings + absl::type_traits gmock_main ) @@ -251,12 +231,56 @@ absl_cc_test( "optional_exception_safety_test.cc" COPTS ${ABSL_TEST_COPTS} + DEPS + absl::optional + absl::config + absl::exception_safety_testing + gmock_main +) + +absl_cc_library( + NAME + conformance_testing + HDRS + "internal/conformance_aliases.h" + "internal/conformance_archetype.h" + "internal/conformance_profile.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::debugging + absl::type_traits + absl::strings + absl::utility + gmock_main + PUBLIC +) + +absl_cc_test( + NAME + conformance_testing_test + SRCS + "internal/conformance_testing_test.cc" + COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} LINKOPTS ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS - absl::optional - absl::exception_safety_testing + absl::conformance_testing + absl::type_traits + gmock_main +) + +absl_cc_test( + NAME + conformance_testing_test_no_exceptions + SRCS + "internal/conformance_testing_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::conformance_testing gmock_main ) @@ -286,9 +310,6 @@ absl_cc_test( "variant_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::variant absl::config @@ -335,9 +356,6 @@ absl_cc_test( "variant_exception_safety_test.cc" COPTS ${ABSL_TEST_COPTS} - ${ABSL_EXCEPTIONS_FLAG} - LINKOPTS - ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS absl::variant absl::config diff --git a/absl/types/any.h b/absl/types/any.h index 7cae9dd0..16bda79c 100644 --- a/absl/types/any.h +++ b/absl/types/any.h @@ -56,20 +56,20 @@ #include "absl/base/config.h" #include "absl/utility/utility.h" -#ifdef ABSL_HAVE_STD_ANY +#ifdef ABSL_USES_STD_ANY #include <any> // IWYU pragma: export namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN using std::any; using std::any_cast; using std::bad_any_cast; using std::make_any; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#else // ABSL_HAVE_STD_ANY +#else // ABSL_USES_STD_ANY #include <algorithm> #include <cstddef> @@ -93,7 +93,7 @@ using std::make_any; #endif // !defined(__GNUC__) || defined(__GXX_RTTI) namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace any_internal { @@ -185,7 +185,7 @@ ValueType* any_cast(any* operand) noexcept; // object, when containing a value, must contain a value type; storing a // reference type is neither desired nor supported. // -// An `absl::any` can only store a type that is copy-constructable; move-only +// An `absl::any` can only store a type that is copy-constructible; move-only // types are not allowed within an `any` object. // // Example: @@ -193,7 +193,7 @@ ValueType* any_cast(any* operand) noexcept; // auto a = absl::any(65); // Literal, copyable // auto b = absl::any(std::vector<int>()); // Default-initialized, copyable // std::unique_ptr<Foo> my_foo; -// auto c = absl::any(std::move(my_foo)); // Error, not copy-constructable +// auto c = absl::any(std::move(my_foo)); // Error, not copy-constructible // // Note that `absl::any` makes use of decayed types (`absl::decay_t` in this // context) to remove const-volatile qualifiers (known as "cv qualifiers"), @@ -518,26 +518,30 @@ ValueType any_cast(any&& operand) { // Description at the declaration site (top of file). template <typename T> const T* any_cast(const any* operand) noexcept { - return operand && operand->GetObjTypeId() == any::IdForType<T>() + using U = + typename std::remove_cv<typename std::remove_reference<T>::type>::type; + return operand && operand->GetObjTypeId() == any::IdForType<U>() ? std::addressof( - static_cast<const any::Obj<T>*>(operand->obj_.get())->value) + static_cast<const any::Obj<U>*>(operand->obj_.get())->value) : nullptr; } // Description at the declaration site (top of file). template <typename T> T* any_cast(any* operand) noexcept { - return operand && operand->GetObjTypeId() == any::IdForType<T>() + using U = + typename std::remove_cv<typename std::remove_reference<T>::type>::type; + return operand && operand->GetObjTypeId() == any::IdForType<U>() ? std::addressof( - static_cast<any::Obj<T>*>(operand->obj_.get())->value) + static_cast<any::Obj<U>*>(operand->obj_.get())->value) : nullptr; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #undef ABSL_ANY_DETAIL_HAS_RTTI -#endif // ABSL_HAVE_STD_ANY +#endif // ABSL_USES_STD_ANY #endif // ABSL_TYPES_ANY_H_ diff --git a/absl/types/any_exception_safety_test.cc b/absl/types/any_exception_safety_test.cc index 5d7d8a5c..31c11401 100644 --- a/absl/types/any_exception_safety_test.cc +++ b/absl/types/any_exception_safety_test.cc @@ -14,6 +14,12 @@ #include "absl/types/any.h" +#include "absl/base/config.h" + +// This test is a no-op when absl::any is an alias for std::any and when +// exceptions are not enabled. +#if !defined(ABSL_USES_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS) + #include <typeinfo> #include <vector> @@ -136,8 +142,6 @@ TEST(AnyExceptionSafety, Assignment) { EXPECT_TRUE(strong_empty_any_tester.Test(move)); } -// libstdc++ std::any fails this test -#if !defined(ABSL_HAVE_STD_ANY) TEST(AnyExceptionSafety, Emplace) { auto initial_val = absl::any{absl::in_place_type_t<Thrower>(), 1, testing::nothrow_ctor}; @@ -163,6 +167,7 @@ TEST(AnyExceptionSafety, Emplace) { EXPECT_TRUE(empty_tester.Test(emp_thrower)); EXPECT_TRUE(empty_tester.Test(emp_throwervec)); } -#endif // ABSL_HAVE_STD_ANY } // namespace + +#endif // #if !defined(ABSL_USES_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/types/any_test.cc b/absl/types/any_test.cc index 87104721..70e4ba22 100644 --- a/absl/types/any_test.cc +++ b/absl/types/any_test.cc @@ -14,6 +14,9 @@ #include "absl/types/any.h" +// This test is a no-op when absl::any is an alias for std::any. +#if !defined(ABSL_USES_STD_ANY) + #include <initializer_list> #include <type_traits> #include <utility> @@ -639,7 +642,7 @@ TEST(AnyTest, ConversionConstructionCausesOneCopy) { // Tests for Exception Behavior // ////////////////////////////////// -#if defined(ABSL_HAVE_STD_ANY) +#if defined(ABSL_USES_STD_ANY) // If using a std `any` implementation, we can't check for a specific message. #define ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(...) \ @@ -653,7 +656,7 @@ TEST(AnyTest, ConversionConstructionCausesOneCopy) { ABSL_BASE_INTERNAL_EXPECT_FAIL((__VA_ARGS__), absl::bad_any_cast, \ "Bad any cast") -#endif // defined(ABSL_HAVE_STD_ANY) +#endif // defined(ABSL_USES_STD_ANY) TEST(AnyTest, ThrowBadAlloc) { { @@ -761,7 +764,7 @@ TEST(AnyTest, FailedEmplace) { BadCopyable bad; absl::any target(absl::in_place_type<int>); ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace<BadCopyable>(bad)); -#if defined(ABSL_HAVE_STD_ANY) && defined(__GLIBCXX__) +#if defined(ABSL_USES_STD_ANY) && defined(__GLIBCXX__) // libstdc++ std::any::emplace() implementation (as of 7.2) has a bug: if an // exception is thrown, *this contains a value. #define ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG 1 @@ -774,3 +777,5 @@ TEST(AnyTest, FailedEmplace) { } } // namespace + +#endif // #if !defined(ABSL_USES_STD_ANY) diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc index 062675df..b0592cc9 100644 --- a/absl/types/bad_any_cast.cc +++ b/absl/types/bad_any_cast.cc @@ -14,7 +14,7 @@ #include "absl/types/bad_any_cast.h" -#ifndef ABSL_HAVE_STD_ANY +#ifndef ABSL_USES_STD_ANY #include <cstdlib> @@ -22,7 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN bad_any_cast::~bad_any_cast() = default; @@ -40,7 +40,7 @@ void ThrowBadAnyCast() { } } // namespace any_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_HAVE_STD_ANY +#endif // ABSL_USES_STD_ANY diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h index 8a8476ce..114cef80 100644 --- a/absl/types/bad_any_cast.h +++ b/absl/types/bad_any_cast.h @@ -25,20 +25,20 @@ #include "absl/base/config.h" -#ifdef ABSL_HAVE_STD_ANY +#ifdef ABSL_USES_STD_ANY #include <any> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN using std::bad_any_cast; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#else // ABSL_HAVE_STD_ANY +#else // ABSL_USES_STD_ANY namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // bad_any_cast @@ -67,9 +67,9 @@ namespace any_internal { [[noreturn]] void ThrowBadAnyCast(); } // namespace any_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_HAVE_STD_ANY +#endif // ABSL_USES_STD_ANY #endif // ABSL_TYPES_BAD_ANY_CAST_H_ diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc index 440adac2..26aca70d 100644 --- a/absl/types/bad_optional_access.cc +++ b/absl/types/bad_optional_access.cc @@ -14,7 +14,7 @@ #include "absl/types/bad_optional_access.h" -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL #include <cstdlib> @@ -22,7 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN bad_optional_access::~bad_optional_access() = default; @@ -42,7 +42,7 @@ void throw_bad_optional_access() { } } // namespace optional_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h index 585c7c77..a500286a 100644 --- a/absl/types/bad_optional_access.h +++ b/absl/types/bad_optional_access.h @@ -25,20 +25,20 @@ #include "absl/base/config.h" -#ifdef ABSL_HAVE_STD_OPTIONAL +#ifdef ABSL_USES_STD_OPTIONAL #include <optional> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN using std::bad_optional_access; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#else // ABSL_HAVE_STD_OPTIONAL +#else // ABSL_USES_STD_OPTIONAL namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // bad_optional_access @@ -70,9 +70,9 @@ namespace optional_internal { [[noreturn]] void throw_bad_optional_access(); } // namespace optional_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL #endif // ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_ diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc index d60dae9a..3dc88cc0 100644 --- a/absl/types/bad_variant_access.cc +++ b/absl/types/bad_variant_access.cc @@ -14,7 +14,7 @@ #include "absl/types/bad_variant_access.h" -#ifndef ABSL_HAVE_STD_VARIANT +#ifndef ABSL_USES_STD_VARIANT #include <cstdlib> #include <stdexcept> @@ -23,7 +23,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN ////////////////////////// // [variant.bad.access] // @@ -58,7 +58,7 @@ void Rethrow() { } } // namespace variant_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h index 8d635b57..095969f9 100644 --- a/absl/types/bad_variant_access.h +++ b/absl/types/bad_variant_access.h @@ -25,20 +25,20 @@ #include "absl/base/config.h" -#ifdef ABSL_HAVE_STD_VARIANT +#ifdef ABSL_USES_STD_VARIANT #include <variant> namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN using std::bad_variant_access; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#else // ABSL_HAVE_STD_VARIANT +#else // ABSL_USES_STD_VARIANT namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // bad_variant_access @@ -74,9 +74,9 @@ namespace variant_internal { [[noreturn]] void Rethrow(); } // namespace variant_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT #endif // ABSL_TYPES_BAD_VARIANT_ACCESS_H_ diff --git a/absl/types/compare.h b/absl/types/compare.h index 6f371be7..62ca70f9 100644 --- a/absl/types/compare.h +++ b/absl/types/compare.h @@ -39,7 +39,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace compare_internal { using value_type = int8_t; @@ -80,79 +80,72 @@ enum class ord : value_type { less = -1, greater = 1 }; enum class ncmp : value_type { unordered = -127 }; +// Define macros to allow for creation or emulation of C++17 inline variables +// based on whether the feature is supported. Note: we can't use +// ABSL_INTERNAL_INLINE_CONSTEXPR here because the variables here are of +// incomplete types so they need to be defined after the types are complete. +#ifdef __cpp_inline_variables + +#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) + +#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) \ + static const type name + +#define ABSL_COMPARE_INLINE_INIT(type, name, init) \ + inline constexpr type type::name(init) + +#else // __cpp_inline_variables + +#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) \ + ABSL_CONST_INIT static const T name + +#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) + +#define ABSL_COMPARE_INLINE_INIT(type, name, init) \ + template <typename T> \ + const T compare_internal::type##_base<T>::name(init) + +#endif // __cpp_inline_variables + // These template base classes allow for defining the values of the constants // in the header file (for performance) without using inline variables (which // aren't available in C++11). template <typename T> struct weak_equality_base { - ABSL_CONST_INIT static const T equivalent; - ABSL_CONST_INIT static const T nonequivalent; + ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); + ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent); }; -template <typename T> -const T weak_equality_base<T>::equivalent(eq::equivalent); -template <typename T> -const T weak_equality_base<T>::nonequivalent(eq::nonequivalent); template <typename T> struct strong_equality_base { - ABSL_CONST_INIT static const T equal; - ABSL_CONST_INIT static const T nonequal; - ABSL_CONST_INIT static const T equivalent; - ABSL_CONST_INIT static const T nonequivalent; + ABSL_COMPARE_INLINE_BASECLASS_DECL(equal); + ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequal); + ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); + ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent); }; -template <typename T> -const T strong_equality_base<T>::equal(eq::equal); -template <typename T> -const T strong_equality_base<T>::nonequal(eq::nonequal); -template <typename T> -const T strong_equality_base<T>::equivalent(eq::equivalent); -template <typename T> -const T strong_equality_base<T>::nonequivalent(eq::nonequivalent); template <typename T> struct partial_ordering_base { - ABSL_CONST_INIT static const T less; - ABSL_CONST_INIT static const T equivalent; - ABSL_CONST_INIT static const T greater; - ABSL_CONST_INIT static const T unordered; + ABSL_COMPARE_INLINE_BASECLASS_DECL(less); + ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); + ABSL_COMPARE_INLINE_BASECLASS_DECL(greater); + ABSL_COMPARE_INLINE_BASECLASS_DECL(unordered); }; -template <typename T> -const T partial_ordering_base<T>::less(ord::less); -template <typename T> -const T partial_ordering_base<T>::equivalent(eq::equivalent); -template <typename T> -const T partial_ordering_base<T>::greater(ord::greater); -template <typename T> -const T partial_ordering_base<T>::unordered(ncmp::unordered); template <typename T> struct weak_ordering_base { - ABSL_CONST_INIT static const T less; - ABSL_CONST_INIT static const T equivalent; - ABSL_CONST_INIT static const T greater; + ABSL_COMPARE_INLINE_BASECLASS_DECL(less); + ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); + ABSL_COMPARE_INLINE_BASECLASS_DECL(greater); }; -template <typename T> -const T weak_ordering_base<T>::less(ord::less); -template <typename T> -const T weak_ordering_base<T>::equivalent(eq::equivalent); -template <typename T> -const T weak_ordering_base<T>::greater(ord::greater); template <typename T> struct strong_ordering_base { - ABSL_CONST_INIT static const T less; - ABSL_CONST_INIT static const T equal; - ABSL_CONST_INIT static const T equivalent; - ABSL_CONST_INIT static const T greater; + ABSL_COMPARE_INLINE_BASECLASS_DECL(less); + ABSL_COMPARE_INLINE_BASECLASS_DECL(equal); + ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); + ABSL_COMPARE_INLINE_BASECLASS_DECL(greater); }; -template <typename T> -const T strong_ordering_base<T>::less(ord::less); -template <typename T> -const T strong_ordering_base<T>::equal(eq::equal); -template <typename T> -const T strong_ordering_base<T>::equivalent(eq::equivalent); -template <typename T> -const T strong_ordering_base<T>::greater(ord::greater); } // namespace compare_internal @@ -163,6 +156,9 @@ class weak_equality friend struct compare_internal::weak_equality_base<weak_equality>; public: + ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, equivalent); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, nonequivalent); + // Comparisons friend constexpr bool operator==( weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { @@ -180,10 +176,22 @@ class weak_equality weak_equality v) noexcept { return 0 != v.value_; } + friend constexpr bool operator==(weak_equality v1, + weak_equality v2) noexcept { + return v1.value_ == v2.value_; + } + friend constexpr bool operator!=(weak_equality v1, + weak_equality v2) noexcept { + return v1.value_ != v2.value_; + } private: compare_internal::value_type value_; }; +ABSL_COMPARE_INLINE_INIT(weak_equality, equivalent, + compare_internal::eq::equivalent); +ABSL_COMPARE_INLINE_INIT(weak_equality, nonequivalent, + compare_internal::eq::nonequivalent); class strong_equality : public compare_internal::strong_equality_base<strong_equality> { @@ -192,6 +200,11 @@ class strong_equality friend struct compare_internal::strong_equality_base<strong_equality>; public: + ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equal); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequal); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equivalent); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequivalent); + // Conversion constexpr operator weak_equality() const noexcept { // NOLINT return value_ == 0 ? weak_equality::equivalent @@ -214,10 +227,25 @@ class strong_equality strong_equality v) noexcept { return 0 != v.value_; } + friend constexpr bool operator==(strong_equality v1, + strong_equality v2) noexcept { + return v1.value_ == v2.value_; + } + friend constexpr bool operator!=(strong_equality v1, + strong_equality v2) noexcept { + return v1.value_ != v2.value_; + } private: compare_internal::value_type value_; }; +ABSL_COMPARE_INLINE_INIT(strong_equality, equal, compare_internal::eq::equal); +ABSL_COMPARE_INLINE_INIT(strong_equality, nonequal, + compare_internal::eq::nonequal); +ABSL_COMPARE_INLINE_INIT(strong_equality, equivalent, + compare_internal::eq::equivalent); +ABSL_COMPARE_INLINE_INIT(strong_equality, nonequivalent, + compare_internal::eq::nonequivalent); class partial_ordering : public compare_internal::partial_ordering_base<partial_ordering> { @@ -235,6 +263,11 @@ class partial_ordering } public: + ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, less); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, equivalent); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, greater); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, unordered); + // Conversion constexpr operator weak_equality() const noexcept { // NOLINT return value_ == 0 ? weak_equality::equivalent @@ -289,10 +322,25 @@ class partial_ordering partial_ordering v) noexcept { return v.is_ordered() && 0 >= v.value_; } + friend constexpr bool operator==(partial_ordering v1, + partial_ordering v2) noexcept { + return v1.value_ == v2.value_; + } + friend constexpr bool operator!=(partial_ordering v1, + partial_ordering v2) noexcept { + return v1.value_ != v2.value_; + } private: compare_internal::value_type value_; }; +ABSL_COMPARE_INLINE_INIT(partial_ordering, less, compare_internal::ord::less); +ABSL_COMPARE_INLINE_INIT(partial_ordering, equivalent, + compare_internal::eq::equivalent); +ABSL_COMPARE_INLINE_INIT(partial_ordering, greater, + compare_internal::ord::greater); +ABSL_COMPARE_INLINE_INIT(partial_ordering, unordered, + compare_internal::ncmp::unordered); class weak_ordering : public compare_internal::weak_ordering_base<weak_ordering> { @@ -303,6 +351,10 @@ class weak_ordering friend struct compare_internal::weak_ordering_base<weak_ordering>; public: + ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, less); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, equivalent); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater); + // Conversions constexpr operator weak_equality() const noexcept { // NOLINT return value_ == 0 ? weak_equality::equivalent @@ -362,10 +414,23 @@ class weak_ordering weak_ordering v) noexcept { return 0 >= v.value_; } + friend constexpr bool operator==(weak_ordering v1, + weak_ordering v2) noexcept { + return v1.value_ == v2.value_; + } + friend constexpr bool operator!=(weak_ordering v1, + weak_ordering v2) noexcept { + return v1.value_ != v2.value_; + } private: compare_internal::value_type value_; }; +ABSL_COMPARE_INLINE_INIT(weak_ordering, less, compare_internal::ord::less); +ABSL_COMPARE_INLINE_INIT(weak_ordering, equivalent, + compare_internal::eq::equivalent); +ABSL_COMPARE_INLINE_INIT(weak_ordering, greater, + compare_internal::ord::greater); class strong_ordering : public compare_internal::strong_ordering_base<strong_ordering> { @@ -376,6 +441,11 @@ class strong_ordering friend struct compare_internal::strong_ordering_base<strong_ordering>; public: + ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, less); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equal); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equivalent); + ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater); + // Conversions constexpr operator weak_equality() const noexcept { // NOLINT return value_ == 0 ? weak_equality::equivalent @@ -443,10 +513,28 @@ class strong_ordering strong_ordering v) noexcept { return 0 >= v.value_; } + friend constexpr bool operator==(strong_ordering v1, + strong_ordering v2) noexcept { + return v1.value_ == v2.value_; + } + friend constexpr bool operator!=(strong_ordering v1, + strong_ordering v2) noexcept { + return v1.value_ != v2.value_; + } private: compare_internal::value_type value_; }; +ABSL_COMPARE_INLINE_INIT(strong_ordering, less, compare_internal::ord::less); +ABSL_COMPARE_INLINE_INIT(strong_ordering, equal, compare_internal::eq::equal); +ABSL_COMPARE_INLINE_INIT(strong_ordering, equivalent, + compare_internal::eq::equivalent); +ABSL_COMPARE_INLINE_INIT(strong_ordering, greater, + compare_internal::ord::greater); + +#undef ABSL_COMPARE_INLINE_BASECLASS_DECL +#undef ABSL_COMPARE_INLINE_SUBCLASS_DECL +#undef ABSL_COMPARE_INLINE_INIT namespace compare_internal { // We also provide these comparator adapter functions for internal absl use. @@ -504,7 +592,7 @@ constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare, } } // namespace compare_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TYPES_COMPARE_H_ diff --git a/absl/types/compare_test.cc b/absl/types/compare_test.cc index 11b3ad40..8095baf9 100644 --- a/absl/types/compare_test.cc +++ b/absl/types/compare_test.cc @@ -18,7 +18,7 @@ #include "absl/base/casts.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { // This is necessary to avoid a bunch of lint warnings suggesting that we use @@ -31,6 +31,15 @@ TEST(Compare, WeakEquality) { EXPECT_TRUE(Identity(0 == weak_equality::equivalent)); EXPECT_TRUE(Identity(weak_equality::nonequivalent != 0)); EXPECT_TRUE(Identity(0 != weak_equality::nonequivalent)); + const weak_equality values[] = {weak_equality::equivalent, + weak_equality::nonequivalent}; + for (const auto& lhs : values) { + for (const auto& rhs : values) { + const bool are_equal = &lhs == &rhs; + EXPECT_EQ(lhs == rhs, are_equal); + EXPECT_EQ(lhs != rhs, !are_equal); + } + } } TEST(Compare, StrongEquality) { @@ -42,6 +51,18 @@ TEST(Compare, StrongEquality) { EXPECT_TRUE(Identity(0 == strong_equality::equivalent)); EXPECT_TRUE(Identity(strong_equality::nonequivalent != 0)); EXPECT_TRUE(Identity(0 != strong_equality::nonequivalent)); + const strong_equality values[] = {strong_equality::equal, + strong_equality::nonequal}; + for (const auto& lhs : values) { + for (const auto& rhs : values) { + const bool are_equal = &lhs == &rhs; + EXPECT_EQ(lhs == rhs, are_equal); + EXPECT_EQ(lhs != rhs, !are_equal); + } + } + EXPECT_TRUE(Identity(strong_equality::equivalent == strong_equality::equal)); + EXPECT_TRUE( + Identity(strong_equality::nonequivalent == strong_equality::nonequal)); } TEST(Compare, PartialOrdering) { @@ -65,6 +86,16 @@ TEST(Compare, PartialOrdering) { EXPECT_FALSE(Identity(0 > partial_ordering::unordered)); EXPECT_FALSE(Identity(partial_ordering::unordered >= 0)); EXPECT_FALSE(Identity(0 >= partial_ordering::unordered)); + const partial_ordering values[] = { + partial_ordering::less, partial_ordering::equivalent, + partial_ordering::greater, partial_ordering::unordered}; + for (const auto& lhs : values) { + for (const auto& rhs : values) { + const bool are_equal = &lhs == &rhs; + EXPECT_EQ(lhs == rhs, are_equal); + EXPECT_EQ(lhs != rhs, !are_equal); + } + } } TEST(Compare, WeakOrdering) { @@ -78,6 +109,15 @@ TEST(Compare, WeakOrdering) { EXPECT_TRUE(Identity(0 < weak_ordering::greater)); EXPECT_TRUE(Identity(weak_ordering::greater >= 0)); EXPECT_TRUE(Identity(0 <= weak_ordering::greater)); + const weak_ordering values[] = { + weak_ordering::less, weak_ordering::equivalent, weak_ordering::greater}; + for (const auto& lhs : values) { + for (const auto& rhs : values) { + const bool are_equal = &lhs == &rhs; + EXPECT_EQ(lhs == rhs, are_equal); + EXPECT_EQ(lhs != rhs, !are_equal); + } + } } TEST(Compare, StrongOrdering) { @@ -93,6 +133,16 @@ TEST(Compare, StrongOrdering) { EXPECT_TRUE(Identity(0 < strong_ordering::greater)); EXPECT_TRUE(Identity(strong_ordering::greater >= 0)); EXPECT_TRUE(Identity(0 <= strong_ordering::greater)); + const strong_ordering values[] = { + strong_ordering::less, strong_ordering::equal, strong_ordering::greater}; + for (const auto& lhs : values) { + for (const auto& rhs : values) { + const bool are_equal = &lhs == &rhs; + EXPECT_EQ(lhs == rhs, are_equal); + EXPECT_EQ(lhs != rhs, !are_equal); + } + } + EXPECT_TRUE(Identity(strong_ordering::equivalent == strong_ordering::equal)); } TEST(Compare, Conversions) { @@ -190,7 +240,7 @@ TEST(Compare, Conversions) { struct WeakOrderingLess { template <typename T> - absl::weak_ordering operator()(const T &a, const T &b) const { + absl::weak_ordering operator()(const T& a, const T& b) const { return a < b ? absl::weak_ordering::less : a == b ? absl::weak_ordering::equivalent : absl::weak_ordering::greater; @@ -224,10 +274,10 @@ TEST(DoLessThanComparison, SanityTest) { } TEST(CompareResultAsOrdering, SanityTest) { - EXPECT_TRUE(Identity( - absl::compare_internal::compare_result_as_ordering(-1) < 0)); - EXPECT_FALSE(Identity( - absl::compare_internal::compare_result_as_ordering(-1) == 0)); + EXPECT_TRUE( + Identity(absl::compare_internal::compare_result_as_ordering(-1) < 0)); + EXPECT_FALSE( + Identity(absl::compare_internal::compare_result_as_ordering(-1) == 0)); EXPECT_FALSE( Identity(absl::compare_internal::compare_result_as_ordering(-1) > 0)); EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering( @@ -237,31 +287,31 @@ TEST(CompareResultAsOrdering, SanityTest) { EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( weak_ordering::less) > 0)); - EXPECT_FALSE(Identity( - absl::compare_internal::compare_result_as_ordering(0) < 0)); - EXPECT_TRUE(Identity( - absl::compare_internal::compare_result_as_ordering(0) == 0)); - EXPECT_FALSE(Identity( - absl::compare_internal::compare_result_as_ordering(0) > 0)); + EXPECT_FALSE( + Identity(absl::compare_internal::compare_result_as_ordering(0) < 0)); + EXPECT_TRUE( + Identity(absl::compare_internal::compare_result_as_ordering(0) == 0)); + EXPECT_FALSE( + Identity(absl::compare_internal::compare_result_as_ordering(0) > 0)); EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( - weak_ordering::equivalent) < 0)); + weak_ordering::equivalent) < 0)); EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering( - weak_ordering::equivalent) == 0)); + weak_ordering::equivalent) == 0)); EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( weak_ordering::equivalent) > 0)); - EXPECT_FALSE(Identity( - absl::compare_internal::compare_result_as_ordering(1) < 0)); - EXPECT_FALSE(Identity( - absl::compare_internal::compare_result_as_ordering(1) == 0)); - EXPECT_TRUE(Identity( - absl::compare_internal::compare_result_as_ordering(1) > 0)); + EXPECT_FALSE( + Identity(absl::compare_internal::compare_result_as_ordering(1) < 0)); + EXPECT_FALSE( + Identity(absl::compare_internal::compare_result_as_ordering(1) == 0)); + EXPECT_TRUE( + Identity(absl::compare_internal::compare_result_as_ordering(1) > 0)); EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( - weak_ordering::greater) < 0)); + weak_ordering::greater) < 0)); EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( weak_ordering::greater) == 0)); EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering( - weak_ordering::greater) > 0)); + weak_ordering::greater) > 0)); } TEST(DoThreeWayComparison, SanityTest) { @@ -308,6 +358,32 @@ TEST(DoThreeWayComparison, SanityTest) { absl::compare_internal::do_three_way_comparison(weak, 10, 5) > 0)); } +#ifdef __cpp_inline_variables +TEST(Compare, StaticAsserts) { + static_assert(weak_equality::equivalent == 0, ""); + static_assert(weak_equality::nonequivalent != 0, ""); + + static_assert(strong_equality::equal == 0, ""); + static_assert(strong_equality::nonequal != 0, ""); + static_assert(strong_equality::equivalent == 0, ""); + static_assert(strong_equality::nonequivalent != 0, ""); + + static_assert(partial_ordering::less < 0, ""); + static_assert(partial_ordering::equivalent == 0, ""); + static_assert(partial_ordering::greater > 0, ""); + static_assert(partial_ordering::unordered != 0, ""); + + static_assert(weak_ordering::less < 0, ""); + static_assert(weak_ordering::equivalent == 0, ""); + static_assert(weak_ordering::greater > 0, ""); + + static_assert(strong_ordering::less < 0, ""); + static_assert(strong_ordering::equal == 0, ""); + static_assert(strong_ordering::equivalent == 0, ""); + static_assert(strong_ordering::greater > 0, ""); +} +#endif // __cpp_inline_variables + } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/types/internal/conformance_aliases.h b/absl/types/internal/conformance_aliases.h new file mode 100644 index 00000000..0cc6884e --- /dev/null +++ b/absl/types/internal/conformance_aliases.h @@ -0,0 +1,447 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// regularity_aliases.h +// ----------------------------------------------------------------------------- +// +// This file contains type aliases of common ConformanceProfiles and Archetypes +// so that they can be directly used by name without creating them from scratch. + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_ + +#include "absl/types/internal/conformance_archetype.h" +#include "absl/types/internal/conformance_profile.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace types_internal { + +// Creates both a Profile and a corresponding Archetype with root name "name". +#define ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(name, ...) \ + struct name##Profile : __VA_ARGS__ {}; \ + \ + using name##Archetype = ::absl::types_internal::Archetype<name##Profile>; \ + \ + template <class AbslInternalProfileTag> \ + using name##Archetype##_ = ::absl::types_internal::Archetype< \ + ::absl::types_internal::StrongProfileTypedef<name##Profile, \ + AbslInternalProfileTag>> + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialDefaultConstructor, + ConformanceProfile<default_constructible::trivial>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowDefaultConstructor, + ConformanceProfile<default_constructible::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasDefaultConstructor, ConformanceProfile<default_constructible::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialMoveConstructor, ConformanceProfile<default_constructible::maybe, + move_constructible::trivial>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowMoveConstructor, ConformanceProfile<default_constructible::maybe, + move_constructible::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasMoveConstructor, + ConformanceProfile<default_constructible::maybe, move_constructible::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialCopyConstructor, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::trivial>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowCopyConstructor, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasCopyConstructor, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialMoveAssign, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::trivial>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowMoveAssign, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasMoveAssign, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialCopyAssign, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::trivial>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowCopyAssign, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasCopyAssign, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialDestructor, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::trivial>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowDestructor, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasDestructor, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowEquality, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, + equality_comparable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasEquality, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, + equality_comparable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowInequality, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, + equality_comparable::maybe, + inequality_comparable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasInequality, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, + equality_comparable::maybe, inequality_comparable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowLessThan, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, + equality_comparable::maybe, inequality_comparable::maybe, + less_than_comparable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasLessThan, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, + equality_comparable::maybe, inequality_comparable::maybe, + less_than_comparable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowLessEqual, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, + equality_comparable::maybe, inequality_comparable::maybe, + less_than_comparable::maybe, + less_equal_comparable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasLessEqual, + ConformanceProfile<default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, + equality_comparable::maybe, inequality_comparable::maybe, + less_than_comparable::maybe, + less_equal_comparable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowGreaterEqual, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasGreaterEqual, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowGreaterThan, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasGreaterThan, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowSwap, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::maybe, swappable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasSwap, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::maybe, swappable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasStdHashSpecialization, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::maybe, swappable::maybe, hashable::yes>); + +//////////////////////////////////////////////////////////////////////////////// +//// The remaining aliases are combinations of the previous aliases. //// +//////////////////////////////////////////////////////////////////////////////// + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + Equatable, CombineProfiles<HasEqualityProfile, HasInequalityProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + Comparable, + CombineProfiles<EquatableProfile, HasLessThanProfile, HasLessEqualProfile, + HasGreaterEqualProfile, HasGreaterThanProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + NothrowEquatable, + CombineProfiles<HasNothrowEqualityProfile, HasNothrowInequalityProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + NothrowComparable, + CombineProfiles<NothrowEquatableProfile, HasNothrowLessThanProfile, + HasNothrowLessEqualProfile, HasNothrowGreaterEqualProfile, + HasNothrowGreaterThanProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + Value, + CombineProfiles<HasNothrowMoveConstructorProfile, HasCopyConstructorProfile, + HasNothrowMoveAssignProfile, HasCopyAssignProfile, + HasNothrowDestructorProfile, HasNothrowSwapProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + EquatableValue, CombineProfiles<EquatableProfile, ValueProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableValue, CombineProfiles<ComparableProfile, ValueProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + DefaultConstructibleValue, + CombineProfiles<HasDefaultConstructorProfile, ValueProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + NothrowMoveConstructible, CombineProfiles<HasNothrowMoveConstructorProfile, + HasNothrowDestructorProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + EquatableNothrowMoveConstructible, + CombineProfiles<EquatableProfile, NothrowMoveConstructibleProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableNothrowMoveConstructible, + CombineProfiles<ComparableProfile, NothrowMoveConstructibleProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + DefaultConstructibleNothrowMoveConstructible, + CombineProfiles<HasDefaultConstructorProfile, + NothrowMoveConstructibleProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + CopyConstructible, + CombineProfiles<HasNothrowMoveConstructorProfile, HasCopyConstructorProfile, + HasNothrowDestructorProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + EquatableCopyConstructible, + CombineProfiles<EquatableProfile, CopyConstructibleProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableCopyConstructible, + CombineProfiles<ComparableProfile, CopyConstructibleProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + DefaultConstructibleCopyConstructible, + CombineProfiles<HasDefaultConstructorProfile, CopyConstructibleProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + NothrowMovable, + CombineProfiles<HasNothrowMoveConstructorProfile, + HasNothrowMoveAssignProfile, HasNothrowDestructorProfile, + HasNothrowSwapProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + EquatableNothrowMovable, + CombineProfiles<EquatableProfile, NothrowMovableProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableNothrowMovable, + CombineProfiles<ComparableProfile, NothrowMovableProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + DefaultConstructibleNothrowMovable, + CombineProfiles<HasDefaultConstructorProfile, NothrowMovableProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + TrivialSpecialMemberFunctions, + CombineProfiles<HasTrivialDefaultConstructorProfile, + HasTrivialMoveConstructorProfile, + HasTrivialCopyConstructorProfile, + HasTrivialMoveAssignProfile, HasTrivialCopyAssignProfile, + HasTrivialDestructorProfile, HasNothrowSwapProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + TriviallyComplete, + CombineProfiles<TrivialSpecialMemberFunctionsProfile, ComparableProfile, + HasStdHashSpecializationProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HashableNothrowMoveConstructible, + CombineProfiles<HasStdHashSpecializationProfile, + NothrowMoveConstructibleProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HashableCopyConstructible, + CombineProfiles<HasStdHashSpecializationProfile, CopyConstructibleProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HashableNothrowMovable, + CombineProfiles<HasStdHashSpecializationProfile, NothrowMovableProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HashableValue, + CombineProfiles<HasStdHashSpecializationProfile, ValueProfile>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableHashableValue, + CombineProfiles<HashableValueProfile, ComparableProfile>); + +// The "preferred" profiles that we support in Abseil. +template <template <class...> class Receiver> +using ExpandBasicProfiles = + Receiver<NothrowMoveConstructibleProfile, CopyConstructibleProfile, + NothrowMovableProfile, ValueProfile>; + +// The basic profiles except that they are also all Equatable. +template <template <class...> class Receiver> +using ExpandBasicEquatableProfiles = + Receiver<EquatableNothrowMoveConstructibleProfile, + EquatableCopyConstructibleProfile, EquatableNothrowMovableProfile, + EquatableValueProfile>; + +// The basic profiles except that they are also all Comparable. +template <template <class...> class Receiver> +using ExpandBasicComparableProfiles = + Receiver<ComparableNothrowMoveConstructibleProfile, + ComparableCopyConstructibleProfile, + ComparableNothrowMovableProfile, ComparableValueProfile>; + +// The basic profiles except that they are also all Hashable. +template <template <class...> class Receiver> +using ExpandBasicHashableProfiles = + Receiver<HashableNothrowMoveConstructibleProfile, + HashableCopyConstructibleProfile, HashableNothrowMovableProfile, + HashableValueProfile>; + +// The basic profiles except that they are also all DefaultConstructible. +template <template <class...> class Receiver> +using ExpandBasicDefaultConstructibleProfiles = + Receiver<DefaultConstructibleNothrowMoveConstructibleProfile, + DefaultConstructibleCopyConstructibleProfile, + DefaultConstructibleNothrowMovableProfile, + DefaultConstructibleValueProfile>; + +// The type profiles that we support in Abseil (all of the previous lists). +template <template <class...> class Receiver> +using ExpandSupportedProfiles = Receiver< + NothrowMoveConstructibleProfile, CopyConstructibleProfile, + NothrowMovableProfile, ValueProfile, + EquatableNothrowMoveConstructibleProfile, EquatableCopyConstructibleProfile, + EquatableNothrowMovableProfile, EquatableValueProfile, + ComparableNothrowMoveConstructibleProfile, + ComparableCopyConstructibleProfile, ComparableNothrowMovableProfile, + ComparableValueProfile, DefaultConstructibleNothrowMoveConstructibleProfile, + DefaultConstructibleCopyConstructibleProfile, + DefaultConstructibleNothrowMovableProfile, DefaultConstructibleValueProfile, + HashableNothrowMoveConstructibleProfile, HashableCopyConstructibleProfile, + HashableNothrowMovableProfile, HashableValueProfile>; + +// TODO(calabrese) Include types that have throwing move constructors, since in +// practice we still need to support them because of standard library types with +// (potentially) non-noexcept moves. + +} // namespace types_internal +ABSL_NAMESPACE_END +} // namespace absl + +#undef ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS + +#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_ diff --git a/absl/types/internal/conformance_archetype.h b/absl/types/internal/conformance_archetype.h new file mode 100644 index 00000000..2349e0f7 --- /dev/null +++ b/absl/types/internal/conformance_archetype.h @@ -0,0 +1,978 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// conformance_archetype.h +// ----------------------------------------------------------------------------- +// +// This file contains a facility for generating "archetypes" of out of +// "Conformance Profiles" (see "conformance_profiles.h" for more information +// about Conformance Profiles). An archetype is a type that aims to support the +// bare minimum requirements of a given Conformance Profile. For instance, an +// archetype that corresponds to an ImmutableProfile has exactly a nothrow +// move-constructor, a potentially-throwing copy constructor, a nothrow +// destructor, with all other special-member-functions deleted. These archetypes +// are useful for testing to make sure that templates are able to work with the +// kinds of types that they claim to support (i.e. that they do not accidentally +// under-constrain), +// +// The main type template in this file is the Archetype template, which takes +// a Conformance Profile as a template argument and its instantiations are a +// minimum-conforming model of that profile. + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_ + +#include <cstddef> +#include <functional> +#include <type_traits> +#include <utility> + +#include "absl/meta/type_traits.h" +#include "absl/types/internal/conformance_profile.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace types_internal { + +// A minimum-conforming implementation of a type with properties specified in +// `Prof`, where `Prof` is a valid Conformance Profile. +template <class Prof, class /*Enabler*/ = void> +class Archetype; + +// Given an Archetype, obtain the properties of the profile associated with that +// archetype. +template <class Archetype> +struct PropertiesOfArchetype; + +template <class Prof> +struct PropertiesOfArchetype<Archetype<Prof>> { + using type = PropertiesOfT<Prof>; +}; + +template <class Archetype> +using PropertiesOfArchetypeT = typename PropertiesOfArchetype<Archetype>::type; + +// A metafunction to determine if a type is an `Archetype`. +template <class T> +struct IsArchetype : std::false_type {}; + +template <class Prof> +struct IsArchetype<Archetype<Prof>> : std::true_type {}; + +// A constructor tag type used when creating an Archetype with internal state. +struct MakeArchetypeState {}; + +// Data stored within an archetype that is copied/compared/hashed when the +// corresponding operations are used. +using ArchetypeState = std::size_t; + +//////////////////////////////////////////////////////////////////////////////// +// This section of the file defines a chain of base classes for Archetype, // +// where each base defines a specific special member function with the // +// appropriate properties (deleted, noexcept(false), noexcept, or trivial). // +//////////////////////////////////////////////////////////////////////////////// + +// The bottom-most base, which contains the state and the default constructor. +template <default_constructible DefaultConstructibleValue> +struct ArchetypeStateBase { + static_assert(DefaultConstructibleValue == default_constructible::yes || + DefaultConstructibleValue == default_constructible::nothrow, + ""); + + ArchetypeStateBase() noexcept( + DefaultConstructibleValue == + default_constructible:: + nothrow) /*Vacuous archetype_state initialization*/ {} + explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept + : archetype_state(state) {} + + ArchetypeState archetype_state; +}; + +template <> +struct ArchetypeStateBase<default_constructible::maybe> { + explicit ArchetypeStateBase() = delete; + explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept + : archetype_state(state) {} + + ArchetypeState archetype_state; +}; + +template <> +struct ArchetypeStateBase<default_constructible::trivial> { + ArchetypeStateBase() = default; + explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept + : archetype_state(state) {} + + ArchetypeState archetype_state; +}; + +// The move-constructor base +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue> +struct ArchetypeMoveConstructor + : ArchetypeStateBase<DefaultConstructibleValue> { + static_assert(MoveConstructibleValue == move_constructible::yes || + MoveConstructibleValue == move_constructible::nothrow, + ""); + + explicit ArchetypeMoveConstructor(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(), + state) {} + + ArchetypeMoveConstructor() = default; + ArchetypeMoveConstructor(ArchetypeMoveConstructor&& other) noexcept( + MoveConstructibleValue == move_constructible::nothrow) + : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(), + other.archetype_state) {} + ArchetypeMoveConstructor(const ArchetypeMoveConstructor&) = default; + ArchetypeMoveConstructor& operator=(ArchetypeMoveConstructor&&) = default; + ArchetypeMoveConstructor& operator=(const ArchetypeMoveConstructor&) = + default; +}; + +template <default_constructible DefaultConstructibleValue> +struct ArchetypeMoveConstructor<DefaultConstructibleValue, + move_constructible::trivial> + : ArchetypeStateBase<DefaultConstructibleValue> { + explicit ArchetypeMoveConstructor(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(), + state) {} + + ArchetypeMoveConstructor() = default; +}; + +// The copy-constructor base +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue> +struct ArchetypeCopyConstructor + : ArchetypeMoveConstructor<DefaultConstructibleValue, + MoveConstructibleValue> { + static_assert(CopyConstructibleValue == copy_constructible::yes || + CopyConstructibleValue == copy_constructible::nothrow, + ""); + explicit ArchetypeCopyConstructor(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeMoveConstructor<DefaultConstructibleValue, + MoveConstructibleValue>(MakeArchetypeState(), + state) {} + + ArchetypeCopyConstructor() = default; + ArchetypeCopyConstructor(ArchetypeCopyConstructor&&) = default; + ArchetypeCopyConstructor(const ArchetypeCopyConstructor& other) noexcept( + CopyConstructibleValue == copy_constructible::nothrow) + : ArchetypeMoveConstructor<DefaultConstructibleValue, + MoveConstructibleValue>( + MakeArchetypeState(), other.archetype_state) {} + ArchetypeCopyConstructor& operator=(ArchetypeCopyConstructor&&) = default; + ArchetypeCopyConstructor& operator=(const ArchetypeCopyConstructor&) = + default; +}; + +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue> +struct ArchetypeCopyConstructor<DefaultConstructibleValue, + MoveConstructibleValue, + copy_constructible::maybe> + : ArchetypeMoveConstructor<DefaultConstructibleValue, + MoveConstructibleValue> { + explicit ArchetypeCopyConstructor(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeMoveConstructor<DefaultConstructibleValue, + MoveConstructibleValue>(MakeArchetypeState(), + state) {} + + ArchetypeCopyConstructor() = default; + ArchetypeCopyConstructor(ArchetypeCopyConstructor&&) = default; + ArchetypeCopyConstructor(const ArchetypeCopyConstructor&) = delete; + ArchetypeCopyConstructor& operator=(ArchetypeCopyConstructor&&) = default; + ArchetypeCopyConstructor& operator=(const ArchetypeCopyConstructor&) = + default; +}; + +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue> +struct ArchetypeCopyConstructor<DefaultConstructibleValue, + MoveConstructibleValue, + copy_constructible::trivial> + : ArchetypeMoveConstructor<DefaultConstructibleValue, + MoveConstructibleValue> { + explicit ArchetypeCopyConstructor(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeMoveConstructor<DefaultConstructibleValue, + MoveConstructibleValue>(MakeArchetypeState(), + state) {} + + ArchetypeCopyConstructor() = default; +}; + +// The move-assign base +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue, + move_assignable MoveAssignableValue> +struct ArchetypeMoveAssign + : ArchetypeCopyConstructor<DefaultConstructibleValue, + MoveConstructibleValue, CopyConstructibleValue> { + static_assert(MoveAssignableValue == move_assignable::yes || + MoveAssignableValue == move_assignable::nothrow, + ""); + explicit ArchetypeMoveAssign(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeCopyConstructor<DefaultConstructibleValue, + MoveConstructibleValue, + CopyConstructibleValue>(MakeArchetypeState(), + state) {} + + ArchetypeMoveAssign() = default; + ArchetypeMoveAssign(ArchetypeMoveAssign&&) = default; + ArchetypeMoveAssign(const ArchetypeMoveAssign&) = default; + ArchetypeMoveAssign& operator=(ArchetypeMoveAssign&& other) noexcept( + MoveAssignableValue == move_assignable::nothrow) { + this->archetype_state = other.archetype_state; + return *this; + } + + ArchetypeMoveAssign& operator=(const ArchetypeMoveAssign&) = default; +}; + +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue> +struct ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, move_assignable::trivial> + : ArchetypeCopyConstructor<DefaultConstructibleValue, + MoveConstructibleValue, CopyConstructibleValue> { + explicit ArchetypeMoveAssign(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeCopyConstructor<DefaultConstructibleValue, + MoveConstructibleValue, + CopyConstructibleValue>(MakeArchetypeState(), + state) {} + + ArchetypeMoveAssign() = default; +}; + +// The copy-assign base +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue, + move_assignable MoveAssignableValue, + copy_assignable CopyAssignableValue> +struct ArchetypeCopyAssign + : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue> { + static_assert(CopyAssignableValue == copy_assignable::yes || + CopyAssignableValue == copy_assignable::nothrow, + ""); + explicit ArchetypeCopyAssign(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue>( + MakeArchetypeState(), state) {} + + ArchetypeCopyAssign() = default; + ArchetypeCopyAssign(ArchetypeCopyAssign&&) = default; + ArchetypeCopyAssign(const ArchetypeCopyAssign&) = default; + ArchetypeCopyAssign& operator=(ArchetypeCopyAssign&&) = default; + + ArchetypeCopyAssign& operator=(const ArchetypeCopyAssign& other) noexcept( + CopyAssignableValue == copy_assignable::nothrow) { + this->archetype_state = other.archetype_state; + return *this; + } +}; + +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue, + move_assignable MoveAssignableValue> +struct ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue, + copy_assignable::maybe> + : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue> { + explicit ArchetypeCopyAssign(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue>( + MakeArchetypeState(), state) {} + + ArchetypeCopyAssign() = default; + ArchetypeCopyAssign(ArchetypeCopyAssign&&) = default; + ArchetypeCopyAssign(const ArchetypeCopyAssign&) = default; + ArchetypeCopyAssign& operator=(ArchetypeCopyAssign&&) = default; + ArchetypeCopyAssign& operator=(const ArchetypeCopyAssign&) = delete; +}; + +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue, + move_assignable MoveAssignableValue> +struct ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue, + copy_assignable::trivial> + : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue> { + explicit ArchetypeCopyAssign(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue>( + MakeArchetypeState(), state) {} + + ArchetypeCopyAssign() = default; +}; + +// The destructor base +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue, + move_assignable MoveAssignableValue, + copy_assignable CopyAssignableValue, destructible DestructibleValue> +struct ArchetypeDestructor + : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue, + CopyAssignableValue> { + static_assert(DestructibleValue == destructible::yes || + DestructibleValue == destructible::nothrow, + ""); + + explicit ArchetypeDestructor(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue, + CopyAssignableValue>(MakeArchetypeState(), state) {} + + ArchetypeDestructor() = default; + ArchetypeDestructor(ArchetypeDestructor&&) = default; + ArchetypeDestructor(const ArchetypeDestructor&) = default; + ArchetypeDestructor& operator=(ArchetypeDestructor&&) = default; + ArchetypeDestructor& operator=(const ArchetypeDestructor&) = default; + ~ArchetypeDestructor() noexcept(DestructibleValue == destructible::nothrow) {} +}; + +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue, + move_assignable MoveAssignableValue, + copy_assignable CopyAssignableValue> +struct ArchetypeDestructor<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue, + CopyAssignableValue, destructible::trivial> + : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue, + CopyAssignableValue> { + explicit ArchetypeDestructor(MakeArchetypeState, + ArchetypeState state) noexcept + : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue, + CopyConstructibleValue, MoveAssignableValue, + CopyAssignableValue>(MakeArchetypeState(), state) {} + + ArchetypeDestructor() = default; +}; + +// An alias to the top of the chain of bases for special-member functions. +// NOTE: move_constructible::maybe, move_assignable::maybe, and +// destructible::maybe are handled in the top-level type by way of SFINAE. +// Because of this, we never instantiate the base classes with +// move_constructible::maybe, move_assignable::maybe, or destructible::maybe so +// that we minimize the number of different possible type-template +// instantiations. +template <default_constructible DefaultConstructibleValue, + move_constructible MoveConstructibleValue, + copy_constructible CopyConstructibleValue, + move_assignable MoveAssignableValue, + copy_assignable CopyAssignableValue, destructible DestructibleValue> +using ArchetypeSpecialMembersBase = ArchetypeDestructor< + DefaultConstructibleValue, + MoveConstructibleValue != move_constructible::maybe + ? MoveConstructibleValue + : move_constructible::nothrow, + CopyConstructibleValue, + MoveAssignableValue != move_assignable::maybe ? MoveAssignableValue + : move_assignable::nothrow, + CopyAssignableValue, + DestructibleValue != destructible::maybe ? DestructibleValue + : destructible::nothrow>; + +// A function that is used to create an archetype with some associated state. +template <class Arch> +Arch MakeArchetype(ArchetypeState state) noexcept { + static_assert(IsArchetype<Arch>::value, + "The explicit template argument to MakeArchetype is required " + "to be an Archetype."); + return Arch(MakeArchetypeState(), state); +} + +// This is used to conditionally delete "copy" and "move" constructors in a way +// that is consistent with what the ConformanceProfile requires and that also +// strictly enforces the arguments to the copy/move to not come from implicit +// conversions when dealing with the Archetype. +template <class Prof, class T> +constexpr bool ShouldDeleteConstructor() { + return !((PropertiesOfT<Prof>::move_constructible_support != + move_constructible::maybe && + std::is_same<T, Archetype<Prof>>::value) || + (PropertiesOfT<Prof>::copy_constructible_support != + copy_constructible::maybe && + (std::is_same<T, const Archetype<Prof>&>::value || + std::is_same<T, Archetype<Prof>&>::value || + std::is_same<T, const Archetype<Prof>>::value))); +} + +// This is used to conditionally delete "copy" and "move" assigns in a way +// that is consistent with what the ConformanceProfile requires and that also +// strictly enforces the arguments to the copy/move to not come from implicit +// conversions when dealing with the Archetype. +template <class Prof, class T> +constexpr bool ShouldDeleteAssign() { + return !( + (PropertiesOfT<Prof>::move_assignable_support != move_assignable::maybe && + std::is_same<T, Archetype<Prof>>::value) || + (PropertiesOfT<Prof>::copy_assignable_support != copy_assignable::maybe && + (std::is_same<T, const Archetype<Prof>&>::value || + std::is_same<T, Archetype<Prof>&>::value || + std::is_same<T, const Archetype<Prof>>::value))); +} + +// TODO(calabrese) Inherit from a chain of secondary bases to pull in the +// associated functions of other concepts. +template <class Prof, class Enabler> +class Archetype : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support> { + static_assert(std::is_same<Enabler, void>::value, + "An explicit type must not be passed as the second template " + "argument to 'Archetype`."); + + // The cases mentioned in these static_asserts are expected to be handled in + // the partial template specializations of Archetype that follow this + // definition. + static_assert(PropertiesOfT<Prof>::destructible_support != + destructible::maybe, + ""); + static_assert(PropertiesOfT<Prof>::move_constructible_support != + move_constructible::maybe || + PropertiesOfT<Prof>::copy_constructible_support == + copy_constructible::maybe, + ""); + static_assert(PropertiesOfT<Prof>::move_assignable_support != + move_assignable::maybe || + PropertiesOfT<Prof>::copy_assignable_support == + copy_assignable::maybe, + ""); + + public: + Archetype() = default; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteConstructor<Prof, T>()>::type* = nullptr> + Archetype(T&&) = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteAssign<Prof, T>()>::type* = nullptr> + Archetype& operator=(T&&) = delete; + + using ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>::archetype_state; + + private: + explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(), + state) {} + + friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept; +}; + +template <class Prof> +class Archetype<Prof, typename std::enable_if< + PropertiesOfT<Prof>::move_constructible_support != + move_constructible::maybe && + PropertiesOfT<Prof>::move_assignable_support == + move_assignable::maybe && + PropertiesOfT<Prof>::destructible_support != + destructible::maybe>::type> + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support> { + public: + Archetype() = default; + Archetype(Archetype&&) = default; + Archetype(const Archetype&) = default; + Archetype& operator=(Archetype&&) = delete; + Archetype& operator=(const Archetype&) = default; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteConstructor<Prof, T>()>::type* = nullptr> + Archetype(T&&) = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteAssign<Prof, T>()>::type* = nullptr> + Archetype& operator=(T&&) = delete; + + using ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>::archetype_state; + + private: + explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(), + state) {} + + friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept; +}; + +template <class Prof> +class Archetype<Prof, typename std::enable_if< + PropertiesOfT<Prof>::move_constructible_support == + move_constructible::maybe && + PropertiesOfT<Prof>::move_assignable_support == + move_assignable::maybe && + PropertiesOfT<Prof>::destructible_support != + destructible::maybe>::type> + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support> { + public: + Archetype() = default; + Archetype(Archetype&&) = delete; + Archetype(const Archetype&) = default; + Archetype& operator=(Archetype&&) = delete; + Archetype& operator=(const Archetype&) = default; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteConstructor<Prof, T>()>::type* = nullptr> + Archetype(T&&) = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteAssign<Prof, T>()>::type* = nullptr> + Archetype& operator=(T&&) = delete; + + using ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>::archetype_state; + + private: + explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(), + state) {} + + friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept; +}; + +template <class Prof> +class Archetype<Prof, typename std::enable_if< + PropertiesOfT<Prof>::move_constructible_support == + move_constructible::maybe && + PropertiesOfT<Prof>::move_assignable_support != + move_assignable::maybe && + PropertiesOfT<Prof>::destructible_support != + destructible::maybe>::type> + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support> { + public: + Archetype() = default; + Archetype(Archetype&&) = delete; + Archetype(const Archetype&) = default; + Archetype& operator=(Archetype&&) = default; + Archetype& operator=(const Archetype&) = default; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteConstructor<Prof, T>()>::type* = nullptr> + Archetype(T&&) = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteAssign<Prof, T>()>::type* = nullptr> + Archetype& operator=(T&&) = delete; + + using ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>::archetype_state; + + private: + explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(), + state) {} + + friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept; +}; + +template <class Prof> +class Archetype<Prof, typename std::enable_if< + PropertiesOfT<Prof>::move_constructible_support != + move_constructible::maybe && + PropertiesOfT<Prof>::move_assignable_support == + move_assignable::maybe && + PropertiesOfT<Prof>::destructible_support == + destructible::maybe>::type> + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support> { + public: + Archetype() = default; + Archetype(Archetype&&) = default; + Archetype(const Archetype&) = default; + Archetype& operator=(Archetype&&) = delete; + Archetype& operator=(const Archetype&) = default; + ~Archetype() = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteConstructor<Prof, T>()>::type* = nullptr> + Archetype(T&&) = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteAssign<Prof, T>()>::type* = nullptr> + Archetype& operator=(T&&) = delete; + + using ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>::archetype_state; + + private: + explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(), + state) {} + + friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept; +}; + +template <class Prof> +class Archetype<Prof, typename std::enable_if< + PropertiesOfT<Prof>::move_constructible_support == + move_constructible::maybe && + PropertiesOfT<Prof>::move_assignable_support == + move_assignable::maybe && + PropertiesOfT<Prof>::destructible_support == + destructible::maybe>::type> + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support> { + public: + Archetype() = default; + Archetype(Archetype&&) = delete; + Archetype(const Archetype&) = default; + Archetype& operator=(Archetype&&) = delete; + Archetype& operator=(const Archetype&) = default; + ~Archetype() = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteConstructor<Prof, T>()>::type* = nullptr> + Archetype(T&&) = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteAssign<Prof, T>()>::type* = nullptr> + Archetype& operator=(T&&) = delete; + + using ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>::archetype_state; + + private: + explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(), + state) {} + + friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept; +}; + +template <class Prof> +class Archetype<Prof, typename std::enable_if< + PropertiesOfT<Prof>::move_constructible_support == + move_constructible::maybe && + PropertiesOfT<Prof>::move_assignable_support != + move_assignable::maybe && + PropertiesOfT<Prof>::destructible_support == + destructible::maybe>::type> + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support> { + public: + Archetype() = default; + Archetype(Archetype&&) = delete; + Archetype(const Archetype&) = default; + Archetype& operator=(Archetype&&) = default; + Archetype& operator=(const Archetype&) = default; + ~Archetype() = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteConstructor<Prof, T>()>::type* = nullptr> + Archetype(T&&) = delete; + + // Disallow moves when requested, and disallow implicit conversions. + template <class T, typename std::enable_if< + ShouldDeleteAssign<Prof, T>()>::type* = nullptr> + Archetype& operator=(T&&) = delete; + + using ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>::archetype_state; + + private: + explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept + : ArchetypeSpecialMembersBase< + PropertiesOfT<Prof>::default_constructible_support, + PropertiesOfT<Prof>::move_constructible_support, + PropertiesOfT<Prof>::copy_constructible_support, + PropertiesOfT<Prof>::move_assignable_support, + PropertiesOfT<Prof>::copy_assignable_support, + PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(), + state) {} + + friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept; +}; + +// Explicitly deleted swap for Archetype if the profile does not require swap. +// It is important to delete it rather than simply leave it out so that the +// "using std::swap;" idiom will result in this deleted overload being picked. +template <class Prof, + absl::enable_if_t<!PropertiesOfT<Prof>::is_swappable, int> = 0> +void swap(Archetype<Prof>&, Archetype<Prof>&) = delete; // NOLINT + +// A conditionally-noexcept swap implementation for Archetype when the profile +// supports swap. +template <class Prof, + absl::enable_if_t<PropertiesOfT<Prof>::is_swappable, int> = 0> +void swap(Archetype<Prof>& lhs, Archetype<Prof>& rhs) // NOLINT + noexcept(PropertiesOfT<Prof>::swappable_support != swappable::yes) { + std::swap(lhs.archetype_state, rhs.archetype_state); +} + +// A convertible-to-bool type that is used as the return type of comparison +// operators since the standard doesn't always require exactly bool. +struct NothrowBool { + explicit NothrowBool() = delete; + ~NothrowBool() = default; + + // TODO(calabrese) Delete the copy constructor in C++17 mode since guaranteed + // elision makes it not required when returning from a function. + // NothrowBool(NothrowBool const&) = delete; + + NothrowBool& operator=(NothrowBool const&) = delete; + + explicit operator bool() const noexcept { return value; } + + static NothrowBool make(bool const value) noexcept { + return NothrowBool(value); + } + + private: + explicit NothrowBool(bool const value) noexcept : value(value) {} + + bool value; +}; + +// A convertible-to-bool type that is used as the return type of comparison +// operators since the standard doesn't always require exactly bool. +// Note: ExceptionalBool has a conversion operator that is not noexcept, so +// that even when a comparison operator is noexcept, that operation may still +// potentially throw when converted to bool. +struct ExceptionalBool { + explicit ExceptionalBool() = delete; + ~ExceptionalBool() = default; + + // TODO(calabrese) Delete the copy constructor in C++17 mode since guaranteed + // elision makes it not required when returning from a function. + // ExceptionalBool(ExceptionalBool const&) = delete; + + ExceptionalBool& operator=(ExceptionalBool const&) = delete; + + explicit operator bool() const { return value; } // NOLINT + + static ExceptionalBool make(bool const value) noexcept { + return ExceptionalBool(value); + } + + private: + explicit ExceptionalBool(bool const value) noexcept : value(value) {} + + bool value; +}; + +// The following macro is only used as a helper in this file to stamp out +// comparison operator definitions. It is undefined after usage. +// +// NOTE: Non-nothrow operators throw via their result's conversion to bool even +// though the operation itself is noexcept. +#define ABSL_TYPES_INTERNAL_OP(enum_name, op) \ + template <class Prof> \ + absl::enable_if_t<!PropertiesOfT<Prof>::is_##enum_name, bool> operator op( \ + const Archetype<Prof>&, const Archetype<Prof>&) = delete; \ + \ + template <class Prof> \ + typename absl::enable_if_t< \ + PropertiesOfT<Prof>::is_##enum_name, \ + std::conditional<PropertiesOfT<Prof>::enum_name##_support == \ + enum_name::nothrow, \ + NothrowBool, ExceptionalBool>>::type \ + operator op(const Archetype<Prof>& lhs, \ + const Archetype<Prof>& rhs) noexcept { \ + return absl::conditional_t< \ + PropertiesOfT<Prof>::enum_name##_support == enum_name::nothrow, \ + NothrowBool, ExceptionalBool>::make(lhs.archetype_state op \ + rhs.archetype_state); \ + } + +ABSL_TYPES_INTERNAL_OP(equality_comparable, ==); +ABSL_TYPES_INTERNAL_OP(inequality_comparable, !=); +ABSL_TYPES_INTERNAL_OP(less_than_comparable, <); +ABSL_TYPES_INTERNAL_OP(less_equal_comparable, <=); +ABSL_TYPES_INTERNAL_OP(greater_equal_comparable, >=); +ABSL_TYPES_INTERNAL_OP(greater_than_comparable, >); + +#undef ABSL_TYPES_INTERNAL_OP + +// Base class for std::hash specializations when an Archetype doesn't support +// hashing. +struct PoisonedHash { + PoisonedHash() = delete; + PoisonedHash(const PoisonedHash&) = delete; + PoisonedHash& operator=(const PoisonedHash&) = delete; +}; + +// Base class for std::hash specializations when an Archetype supports hashing. +template <class Prof> +struct EnabledHash { + using argument_type = Archetype<Prof>; + using result_type = std::size_t; + result_type operator()(const argument_type& arg) const { + return std::hash<ArchetypeState>()(arg.archetype_state); + } +}; + +} // namespace types_internal +ABSL_NAMESPACE_END +} // namespace absl + +namespace std { + +template <class Prof> // NOLINT +struct hash<::absl::types_internal::Archetype<Prof>> + : conditional<::absl::types_internal::PropertiesOfT<Prof>::is_hashable, + ::absl::types_internal::EnabledHash<Prof>, + ::absl::types_internal::PoisonedHash>::type {}; + +} // namespace std + +#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_ diff --git a/absl/types/internal/conformance_profile.h b/absl/types/internal/conformance_profile.h new file mode 100644 index 00000000..e62004fd --- /dev/null +++ b/absl/types/internal/conformance_profile.h @@ -0,0 +1,376 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// conformance_profiles.h +// ----------------------------------------------------------------------------- +// +// This file contains templates for representing "Regularity Profiles" and +// concisely-named versions of commonly used Regularity Profiles. +// +// A Regularity Profile is a compile-time description of the types of operations +// that a given type supports, along with properties of those operations when +// they do exist. For instance, a Regularity Profile may describe a type that +// has a move-constructor that is noexcept and a copy constructor that is not +// noexcept. This description can then be examined and passed around to other +// templates for the purposes of asserting expectations on user-defined types +// via a series trait checks, or for determining what kinds of run-time tests +// are able to be performed. +// +// Regularity Profiles are also used when creating "archetypes," which are +// minimum-conforming types that meet all of the requirements of a given +// Regularity Profile. For more information regarding archetypes, see +// "conformance_archetypes.h". + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_ + +#include <type_traits> +#include <utility> + +#include "absl/meta/type_traits.h" + +// TODO(calabrese) Add support for extending profiles. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace types_internal { + +template <class T, class /*Enabler*/ = void> +struct PropertiesOfImpl {}; + +template <class T> +struct PropertiesOfImpl<T, absl::void_t<typename T::properties>> { + using type = typename T::properties; +}; + +template <class T> +struct PropertiesOfImpl<T, absl::void_t<typename T::profile_alias_of>> { + using type = typename PropertiesOfImpl<typename T::profile_alias_of>::type; +}; + +template <class T> +struct PropertiesOf : PropertiesOfImpl<T> {}; + +template <class T> +using PropertiesOfT = typename PropertiesOf<T>::type; + +// NOTE: These enums use this naming convention to be consistent with the +// standard trait names, which is useful since it allows us to match up each +// enum name with a corresponding trait name in macro definitions. + +enum class function_kind { maybe, yes, nothrow, trivial }; + +#define ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(name) \ + enum class name { maybe, yes, nothrow, trivial } + +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(default_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(move_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(copy_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(move_assignable); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(copy_assignable); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(destructible); + +#undef ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM + +#define ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(name) \ + enum class name { maybe, yes, nothrow } + +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(equality_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(inequality_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(less_than_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(less_equal_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(greater_equal_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(greater_than_comparable); + +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(swappable); + +#undef ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM + +enum class hashable { maybe, yes }; + +constexpr const char* PropertyName(hashable v) { + return "support for std::hash"; +} + +template < + default_constructible DefaultConstructibleValue = + default_constructible::maybe, + move_constructible MoveConstructibleValue = move_constructible::maybe, + copy_constructible CopyConstructibleValue = copy_constructible::maybe, + move_assignable MoveAssignableValue = move_assignable::maybe, + copy_assignable CopyAssignableValue = copy_assignable::maybe, + destructible DestructibleValue = destructible::maybe, + equality_comparable EqualityComparableValue = equality_comparable::maybe, + inequality_comparable InequalityComparableValue = + inequality_comparable::maybe, + less_than_comparable LessThanComparableValue = less_than_comparable::maybe, + less_equal_comparable LessEqualComparableValue = + less_equal_comparable::maybe, + greater_equal_comparable GreaterEqualComparableValue = + greater_equal_comparable::maybe, + greater_than_comparable GreaterThanComparableValue = + greater_than_comparable::maybe, + swappable SwappableValue = swappable::maybe, + hashable HashableValue = hashable::maybe> +struct ConformanceProfile { + using properties = ConformanceProfile; + + static constexpr default_constructible + default_constructible_support = // NOLINT + DefaultConstructibleValue; + + static constexpr move_constructible move_constructible_support = // NOLINT + MoveConstructibleValue; + + static constexpr copy_constructible copy_constructible_support = // NOLINT + CopyConstructibleValue; + + static constexpr move_assignable move_assignable_support = // NOLINT + MoveAssignableValue; + + static constexpr copy_assignable copy_assignable_support = // NOLINT + CopyAssignableValue; + + static constexpr destructible destructible_support = // NOLINT + DestructibleValue; + + static constexpr equality_comparable equality_comparable_support = // NOLINT + EqualityComparableValue; + + static constexpr inequality_comparable + inequality_comparable_support = // NOLINT + InequalityComparableValue; + + static constexpr less_than_comparable + less_than_comparable_support = // NOLINT + LessThanComparableValue; + + static constexpr less_equal_comparable + less_equal_comparable_support = // NOLINT + LessEqualComparableValue; + + static constexpr greater_equal_comparable + greater_equal_comparable_support = // NOLINT + GreaterEqualComparableValue; + + static constexpr greater_than_comparable + greater_than_comparable_support = // NOLINT + GreaterThanComparableValue; + + static constexpr swappable swappable_support = SwappableValue; // NOLINT + + static constexpr hashable hashable_support = HashableValue; // NOLINT + + static constexpr bool is_default_constructible = // NOLINT + DefaultConstructibleValue != default_constructible::maybe; + + static constexpr bool is_move_constructible = // NOLINT + MoveConstructibleValue != move_constructible::maybe; + + static constexpr bool is_copy_constructible = // NOLINT + CopyConstructibleValue != copy_constructible::maybe; + + static constexpr bool is_move_assignable = // NOLINT + MoveAssignableValue != move_assignable::maybe; + + static constexpr bool is_copy_assignable = // NOLINT + CopyAssignableValue != copy_assignable::maybe; + + static constexpr bool is_destructible = // NOLINT + DestructibleValue != destructible::maybe; + + static constexpr bool is_equality_comparable = // NOLINT + EqualityComparableValue != equality_comparable::maybe; + + static constexpr bool is_inequality_comparable = // NOLINT + InequalityComparableValue != inequality_comparable::maybe; + + static constexpr bool is_less_than_comparable = // NOLINT + LessThanComparableValue != less_than_comparable::maybe; + + static constexpr bool is_less_equal_comparable = // NOLINT + LessEqualComparableValue != less_equal_comparable::maybe; + + static constexpr bool is_greater_equal_comparable = // NOLINT + GreaterEqualComparableValue != greater_equal_comparable::maybe; + + static constexpr bool is_greater_than_comparable = // NOLINT + GreaterThanComparableValue != greater_than_comparable::maybe; + + static constexpr bool is_swappable = // NOLINT + SwappableValue != swappable::maybe; + + static constexpr bool is_hashable = // NOLINT + HashableValue != hashable::maybe; +}; + +#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type, name) \ + template <default_constructible DefaultConstructibleValue, \ + move_constructible MoveConstructibleValue, \ + copy_constructible CopyConstructibleValue, \ + move_assignable MoveAssignableValue, \ + copy_assignable CopyAssignableValue, \ + destructible DestructibleValue, \ + equality_comparable EqualityComparableValue, \ + inequality_comparable InequalityComparableValue, \ + less_than_comparable LessThanComparableValue, \ + less_equal_comparable LessEqualComparableValue, \ + greater_equal_comparable GreaterEqualComparableValue, \ + greater_than_comparable GreaterThanComparableValue, \ + swappable SwappableValue, hashable HashableValue> \ + constexpr type ConformanceProfile< \ + DefaultConstructibleValue, MoveConstructibleValue, \ + CopyConstructibleValue, MoveAssignableValue, CopyAssignableValue, \ + DestructibleValue, EqualityComparableValue, InequalityComparableValue, \ + LessThanComparableValue, LessEqualComparableValue, \ + GreaterEqualComparableValue, GreaterThanComparableValue, SwappableValue, \ + HashableValue>::name + +#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(type) \ + ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type, \ + type##_support); \ + ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(bool, is_##type) + +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(default_constructible); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_constructible); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_constructible); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_assignable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_assignable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(destructible); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(equality_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(inequality_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_than_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_equal_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_equal_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_than_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(swappable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(hashable); + +#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF +#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL + +// Converts an enum to its underlying integral value. +template <class Enum> +constexpr absl::underlying_type_t<Enum> UnderlyingValue(Enum value) { + return static_cast<absl::underlying_type_t<Enum>>(value); +} + +// Retrieve the enum with the greatest underlying value. +// Note: std::max is not constexpr in C++11, which is why this is necessary. +template <class H> +constexpr H MaxEnum(H head) { + return head; +} + +template <class H, class N, class... T> +constexpr H MaxEnum(H head, N next, T... tail) { + return (UnderlyingValue)(next) < (UnderlyingValue)(head) + ? (MaxEnum)(head, tail...) + : (MaxEnum)(next, tail...); +} + +template <class... Profs> +struct CombineProfilesImpl { + static constexpr default_constructible + default_constructible_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::default_constructible_support...); + + static constexpr move_constructible move_constructible_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::move_constructible_support...); + + static constexpr copy_constructible copy_constructible_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::copy_constructible_support...); + + static constexpr move_assignable move_assignable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::move_assignable_support...); + + static constexpr copy_assignable copy_assignable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::copy_assignable_support...); + + static constexpr destructible destructible_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::destructible_support...); + + static constexpr equality_comparable equality_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::equality_comparable_support...); + + static constexpr inequality_comparable + inequality_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::inequality_comparable_support...); + + static constexpr less_than_comparable + less_than_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::less_than_comparable_support...); + + static constexpr less_equal_comparable + less_equal_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...); + + static constexpr greater_equal_comparable + greater_equal_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...); + + static constexpr greater_than_comparable + greater_than_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...); + + static constexpr swappable swappable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::swappable_support...); + + static constexpr hashable hashable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::hashable_support...); + + using properties = ConformanceProfile< + default_constructible_support, move_constructible_support, + copy_constructible_support, move_assignable_support, + copy_assignable_support, destructible_support, + equality_comparable_support, inequality_comparable_support, + less_than_comparable_support, less_equal_comparable_support, + greater_equal_comparable_support, greater_than_comparable_support, + swappable_support, hashable_support>; +}; + +// NOTE: We use this as opposed to a direct alias of CombineProfilesImpl so that +// when named aliases of CombineProfiles are created (such as in +// conformance_aliases.h), we only pay for the combination algorithm on the +// profiles that are actually used. +template <class... Profs> +struct CombineProfiles { + using profile_alias_of = CombineProfilesImpl<Profs...>; +}; + +template <> +struct CombineProfiles<> { + using properties = ConformanceProfile<>; +}; + +template <class Profile, class Tag> +struct StrongProfileTypedef { + using properties = PropertiesOfT<Profile>; +}; + +template <class T, class /*Enabler*/ = void> +struct IsProfileImpl : std::false_type {}; + +template <class T> +struct IsProfileImpl<T, absl::void_t<PropertiesOfT<T>>> : std::true_type {}; + +template <class T> +struct IsProfile : IsProfileImpl<T>::type {}; + +} // namespace types_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_ diff --git a/absl/types/internal/conformance_testing_test.cc b/absl/types/internal/conformance_testing_test.cc new file mode 100644 index 00000000..3dcf5305 --- /dev/null +++ b/absl/types/internal/conformance_testing_test.cc @@ -0,0 +1,1186 @@ +// 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 <new> +#include <type_traits> +#include <utility> + +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" +#include "absl/types/internal/conformance_aliases.h" + +namespace { + +namespace ti = absl::types_internal; + +template <class T> +using DefaultConstructibleWithNewImpl = decltype(::new (std::nothrow) T); + +template <class T> +using DefaultConstructibleWithNew = + absl::type_traits_internal::is_detected<DefaultConstructibleWithNewImpl, T>; + +template <class T> +using MoveConstructibleWithNewImpl = + decltype(::new (std::nothrow) T(std::declval<T>())); + +template <class T> +using MoveConstructibleWithNew = + absl::type_traits_internal::is_detected<MoveConstructibleWithNewImpl, T>; + +template <class T> +using CopyConstructibleWithNewImpl = + decltype(::new (std::nothrow) T(std::declval<const T&>())); + +template <class T> +using CopyConstructibleWithNew = + absl::type_traits_internal::is_detected<CopyConstructibleWithNewImpl, T>; + +template <class T, + class Result = + std::integral_constant<bool, noexcept(::new (std::nothrow) T)>> +using NothrowDefaultConstructibleWithNewImpl = + typename std::enable_if<Result::value>::type; + +template <class T> +using NothrowDefaultConstructibleWithNew = + absl::type_traits_internal::is_detected< + NothrowDefaultConstructibleWithNewImpl, T>; + +template <class T, + class Result = std::integral_constant< + bool, noexcept(::new (std::nothrow) T(std::declval<T>()))>> +using NothrowMoveConstructibleWithNewImpl = + typename std::enable_if<Result::value>::type; + +template <class T> +using NothrowMoveConstructibleWithNew = + absl::type_traits_internal::is_detected<NothrowMoveConstructibleWithNewImpl, + T>; + +template <class T, + class Result = std::integral_constant< + bool, noexcept(::new (std::nothrow) T(std::declval<const T&>()))>> +using NothrowCopyConstructibleWithNewImpl = + typename std::enable_if<Result::value>::type; + +template <class T> +using NothrowCopyConstructibleWithNew = + absl::type_traits_internal::is_detected<NothrowCopyConstructibleWithNewImpl, + T>; + +// NOTE: ?: is used to verify contextually-convertible to bool and not simply +// implicit or explicit convertibility. +#define ABSL_INTERNAL_COMPARISON_OP_EXPR(op) \ + ((std::declval<const T&>() op std::declval<const T&>()) ? true : true) + +#define ABSL_INTERNAL_COMPARISON_OP_TRAIT(name, op) \ + template <class T> \ + using name##Impl = decltype(ABSL_INTERNAL_COMPARISON_OP_EXPR(op)); \ + \ + template <class T> \ + using name = absl::type_traits_internal::is_detected<name##Impl, T>; \ + \ + template <class T, \ + class Result = std::integral_constant< \ + bool, noexcept(ABSL_INTERNAL_COMPARISON_OP_EXPR(op))>> \ + using Nothrow##name##Impl = typename std::enable_if<Result::value>::type; \ + \ + template <class T> \ + using Nothrow##name = \ + absl::type_traits_internal::is_detected<Nothrow##name##Impl, T> + +ABSL_INTERNAL_COMPARISON_OP_TRAIT(EqualityComparable, ==); +ABSL_INTERNAL_COMPARISON_OP_TRAIT(InequalityComparable, !=); +ABSL_INTERNAL_COMPARISON_OP_TRAIT(LessThanComparable, <); +ABSL_INTERNAL_COMPARISON_OP_TRAIT(LessEqualComparable, <=); +ABSL_INTERNAL_COMPARISON_OP_TRAIT(GreaterEqualComparable, >=); +ABSL_INTERNAL_COMPARISON_OP_TRAIT(GreaterThanComparable, >); + +#undef ABSL_INTERNAL_COMPARISON_OP_TRAIT + +template <class T> +class ProfileTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(ProfileTest); + +TYPED_TEST_P(ProfileTest, HasAppropriateConstructionProperties) { + using profile = typename TypeParam::profile; + using arch = typename TypeParam::arch; + using expected_profile = typename TypeParam::expected_profile; + + using props = ti::PropertiesOfT<profile>; + using arch_props = ti::PropertiesOfArchetypeT<arch>; + using expected_props = ti::PropertiesOfT<expected_profile>; + + // Make sure all of the properties are as expected. + // There are seemingly redundant tests here to make it easier to diagnose + // the specifics of the failure if something were to go wrong. + EXPECT_TRUE((std::is_same<props, arch_props>::value)); + EXPECT_TRUE((std::is_same<props, expected_props>::value)); + EXPECT_TRUE((std::is_same<arch_props, expected_props>::value)); + + EXPECT_EQ(props::default_constructible_support, + expected_props::default_constructible_support); + + EXPECT_EQ(props::move_constructible_support, + expected_props::move_constructible_support); + + EXPECT_EQ(props::copy_constructible_support, + expected_props::copy_constructible_support); + + EXPECT_EQ(props::destructible_support, expected_props::destructible_support); + + // Avoid additional error message noise when profile and archetype match with + // each other but were not what was expected. + if (!std::is_same<props, arch_props>::value) { + EXPECT_EQ(arch_props::default_constructible_support, + expected_props::default_constructible_support); + + EXPECT_EQ(arch_props::move_constructible_support, + expected_props::move_constructible_support); + + EXPECT_EQ(arch_props::copy_constructible_support, + expected_props::copy_constructible_support); + + EXPECT_EQ(arch_props::destructible_support, + expected_props::destructible_support); + } + + ////////////////////////////////////////////////////////////////////////////// + // Default constructor checks // + ////////////////////////////////////////////////////////////////////////////// + EXPECT_EQ(props::default_constructible_support, + expected_props::default_constructible_support); + + switch (expected_props::default_constructible_support) { + case ti::default_constructible::maybe: + EXPECT_FALSE(DefaultConstructibleWithNew<arch>::value); + EXPECT_FALSE(NothrowDefaultConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_FALSE(std::is_default_constructible<arch>::value); + EXPECT_FALSE(std::is_nothrow_default_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value); + } + break; + case ti::default_constructible::yes: + EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value); + EXPECT_FALSE(NothrowDefaultConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_default_constructible<arch>::value); + EXPECT_FALSE(std::is_nothrow_default_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value); + } + break; + case ti::default_constructible::nothrow: + EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value); + EXPECT_TRUE(NothrowDefaultConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_default_constructible<arch>::value); + EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value); + + // Constructor traits also check the destructor. + if (std::is_nothrow_destructible<arch>::value) { + EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value); + } + } + break; + case ti::default_constructible::trivial: + EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value); + EXPECT_TRUE(NothrowDefaultConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_default_constructible<arch>::value); + EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value); + + // Constructor triviality traits require trivially destructible types. + if (absl::is_trivially_destructible<arch>::value) { + EXPECT_TRUE(absl::is_trivially_default_constructible<arch>::value); + } + } + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Move constructor checks // + ////////////////////////////////////////////////////////////////////////////// + EXPECT_EQ(props::move_constructible_support, + expected_props::move_constructible_support); + + switch (expected_props::move_constructible_support) { + case ti::move_constructible::maybe: + EXPECT_FALSE(MoveConstructibleWithNew<arch>::value); + EXPECT_FALSE(NothrowMoveConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_FALSE(std::is_move_constructible<arch>::value); + EXPECT_FALSE(std::is_nothrow_move_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value); + } + break; + case ti::move_constructible::yes: + EXPECT_TRUE(MoveConstructibleWithNew<arch>::value); + EXPECT_FALSE(NothrowMoveConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_move_constructible<arch>::value); + EXPECT_FALSE(std::is_nothrow_move_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value); + } + break; + case ti::move_constructible::nothrow: + EXPECT_TRUE(MoveConstructibleWithNew<arch>::value); + EXPECT_TRUE(NothrowMoveConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_move_constructible<arch>::value); + EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value); + + // Constructor traits also check the destructor. + if (std::is_nothrow_destructible<arch>::value) { + EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value); + } + } + break; + case ti::move_constructible::trivial: + EXPECT_TRUE(MoveConstructibleWithNew<arch>::value); + EXPECT_TRUE(NothrowMoveConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_move_constructible<arch>::value); + EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value); + + // Constructor triviality traits require trivially destructible types. + if (absl::is_trivially_destructible<arch>::value) { + EXPECT_TRUE(absl::is_trivially_move_constructible<arch>::value); + } + } + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Copy constructor checks // + ////////////////////////////////////////////////////////////////////////////// + EXPECT_EQ(props::copy_constructible_support, + expected_props::copy_constructible_support); + + switch (expected_props::copy_constructible_support) { + case ti::copy_constructible::maybe: + EXPECT_FALSE(CopyConstructibleWithNew<arch>::value); + EXPECT_FALSE(NothrowCopyConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_FALSE(std::is_copy_constructible<arch>::value); + EXPECT_FALSE(std::is_nothrow_copy_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value); + } + break; + case ti::copy_constructible::yes: + EXPECT_TRUE(CopyConstructibleWithNew<arch>::value); + EXPECT_FALSE(NothrowCopyConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_copy_constructible<arch>::value); + EXPECT_FALSE(std::is_nothrow_copy_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value); + } + break; + case ti::copy_constructible::nothrow: + EXPECT_TRUE(CopyConstructibleWithNew<arch>::value); + EXPECT_TRUE(NothrowCopyConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_copy_constructible<arch>::value); + EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value); + + // Constructor traits also check the destructor. + if (std::is_nothrow_destructible<arch>::value) { + EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value); + } + } + break; + case ti::copy_constructible::trivial: + EXPECT_TRUE(CopyConstructibleWithNew<arch>::value); + EXPECT_TRUE(NothrowCopyConstructibleWithNew<arch>::value); + + // Standard constructible traits depend on the destructor. + if (std::is_destructible<arch>::value) { + EXPECT_TRUE(std::is_copy_constructible<arch>::value); + EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value); + + // Constructor triviality traits require trivially destructible types. + if (absl::is_trivially_destructible<arch>::value) { + EXPECT_TRUE(absl::is_trivially_copy_constructible<arch>::value); + } + } + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Destructible checks // + ////////////////////////////////////////////////////////////////////////////// + EXPECT_EQ(props::destructible_support, expected_props::destructible_support); + + switch (expected_props::destructible_support) { + case ti::destructible::maybe: + EXPECT_FALSE(std::is_destructible<arch>::value); + EXPECT_FALSE(std::is_nothrow_destructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_destructible<arch>::value); + break; + case ti::destructible::yes: + EXPECT_TRUE(std::is_destructible<arch>::value); + EXPECT_FALSE(std::is_nothrow_destructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_destructible<arch>::value); + break; + case ti::destructible::nothrow: + EXPECT_TRUE(std::is_destructible<arch>::value); + EXPECT_TRUE(std::is_nothrow_destructible<arch>::value); + EXPECT_FALSE(absl::is_trivially_destructible<arch>::value); + break; + case ti::destructible::trivial: + EXPECT_TRUE(std::is_destructible<arch>::value); + EXPECT_TRUE(std::is_nothrow_destructible<arch>::value); + EXPECT_TRUE(absl::is_trivially_destructible<arch>::value); + break; + } +} + +TYPED_TEST_P(ProfileTest, HasAppropriateAssignmentProperties) { + using profile = typename TypeParam::profile; + using arch = typename TypeParam::arch; + using expected_profile = typename TypeParam::expected_profile; + + using props = ti::PropertiesOfT<profile>; + using arch_props = ti::PropertiesOfArchetypeT<arch>; + using expected_props = ti::PropertiesOfT<expected_profile>; + + // Make sure all of the properties are as expected. + // There are seemingly redundant tests here to make it easier to diagnose + // the specifics of the failure if something were to go wrong. + EXPECT_TRUE((std::is_same<props, arch_props>::value)); + EXPECT_TRUE((std::is_same<props, expected_props>::value)); + EXPECT_TRUE((std::is_same<arch_props, expected_props>::value)); + + EXPECT_EQ(props::move_assignable_support, + expected_props::move_assignable_support); + + EXPECT_EQ(props::copy_assignable_support, + expected_props::copy_assignable_support); + + // Avoid additional error message noise when profile and archetype match with + // each other but were not what was expected. + if (!std::is_same<props, arch_props>::value) { + EXPECT_EQ(arch_props::move_assignable_support, + expected_props::move_assignable_support); + + EXPECT_EQ(arch_props::copy_assignable_support, + expected_props::copy_assignable_support); + } + + ////////////////////////////////////////////////////////////////////////////// + // Move assignment checks // + ////////////////////////////////////////////////////////////////////////////// + EXPECT_EQ(props::move_assignable_support, + expected_props::move_assignable_support); + + switch (expected_props::move_assignable_support) { + case ti::move_assignable::maybe: + EXPECT_FALSE(std::is_move_assignable<arch>::value); + EXPECT_FALSE(std::is_nothrow_move_assignable<arch>::value); + EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value); + break; + case ti::move_assignable::yes: + EXPECT_TRUE(std::is_move_assignable<arch>::value); + EXPECT_FALSE(std::is_nothrow_move_assignable<arch>::value); + EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value); + break; + case ti::move_assignable::nothrow: + EXPECT_TRUE(std::is_move_assignable<arch>::value); + EXPECT_TRUE(std::is_nothrow_move_assignable<arch>::value); + EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value); + break; + case ti::move_assignable::trivial: + EXPECT_TRUE(std::is_move_assignable<arch>::value); + EXPECT_TRUE(std::is_nothrow_move_assignable<arch>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<arch>::value); + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Copy assignment checks // + ////////////////////////////////////////////////////////////////////////////// + EXPECT_EQ(props::copy_assignable_support, + expected_props::copy_assignable_support); + + switch (expected_props::copy_assignable_support) { + case ti::copy_assignable::maybe: + EXPECT_FALSE(std::is_copy_assignable<arch>::value); + EXPECT_FALSE(std::is_nothrow_copy_assignable<arch>::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value); + break; + case ti::copy_assignable::yes: + EXPECT_TRUE(std::is_copy_assignable<arch>::value); + EXPECT_FALSE(std::is_nothrow_copy_assignable<arch>::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value); + break; + case ti::copy_assignable::nothrow: + EXPECT_TRUE(std::is_copy_assignable<arch>::value); + EXPECT_TRUE(std::is_nothrow_copy_assignable<arch>::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value); + break; + case ti::copy_assignable::trivial: + EXPECT_TRUE(std::is_copy_assignable<arch>::value); + EXPECT_TRUE(std::is_nothrow_copy_assignable<arch>::value); + EXPECT_TRUE(absl::is_trivially_copy_assignable<arch>::value); + break; + } +} + +TYPED_TEST_P(ProfileTest, HasAppropriateComparisonProperties) { + using profile = typename TypeParam::profile; + using arch = typename TypeParam::arch; + using expected_profile = typename TypeParam::expected_profile; + + using props = ti::PropertiesOfT<profile>; + using arch_props = ti::PropertiesOfArchetypeT<arch>; + using expected_props = ti::PropertiesOfT<expected_profile>; + + // Make sure all of the properties are as expected. + // There are seemingly redundant tests here to make it easier to diagnose + // the specifics of the failure if something were to go wrong. + EXPECT_TRUE((std::is_same<props, arch_props>::value)); + EXPECT_TRUE((std::is_same<props, expected_props>::value)); + EXPECT_TRUE((std::is_same<arch_props, expected_props>::value)); + + EXPECT_EQ(props::equality_comparable_support, + expected_props::equality_comparable_support); + + EXPECT_EQ(props::inequality_comparable_support, + expected_props::inequality_comparable_support); + + EXPECT_EQ(props::less_than_comparable_support, + expected_props::less_than_comparable_support); + + EXPECT_EQ(props::less_equal_comparable_support, + expected_props::less_equal_comparable_support); + + EXPECT_EQ(props::greater_equal_comparable_support, + expected_props::greater_equal_comparable_support); + + EXPECT_EQ(props::greater_than_comparable_support, + expected_props::greater_than_comparable_support); + + // Avoid additional error message noise when profile and archetype match with + // each other but were not what was expected. + if (!std::is_same<props, arch_props>::value) { + EXPECT_EQ(arch_props::equality_comparable_support, + expected_props::equality_comparable_support); + + EXPECT_EQ(arch_props::inequality_comparable_support, + expected_props::inequality_comparable_support); + + EXPECT_EQ(arch_props::less_than_comparable_support, + expected_props::less_than_comparable_support); + + EXPECT_EQ(arch_props::less_equal_comparable_support, + expected_props::less_equal_comparable_support); + + EXPECT_EQ(arch_props::greater_equal_comparable_support, + expected_props::greater_equal_comparable_support); + + EXPECT_EQ(arch_props::greater_than_comparable_support, + expected_props::greater_than_comparable_support); + } + + ////////////////////////////////////////////////////////////////////////////// + // Equality comparable checks // + ////////////////////////////////////////////////////////////////////////////// + switch (expected_props::equality_comparable_support) { + case ti::equality_comparable::maybe: + EXPECT_FALSE(EqualityComparable<arch>::value); + EXPECT_FALSE(NothrowEqualityComparable<arch>::value); + break; + case ti::equality_comparable::yes: + EXPECT_TRUE(EqualityComparable<arch>::value); + EXPECT_FALSE(NothrowEqualityComparable<arch>::value); + break; + case ti::equality_comparable::nothrow: + EXPECT_TRUE(EqualityComparable<arch>::value); + EXPECT_TRUE(NothrowEqualityComparable<arch>::value); + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Inequality comparable checks // + ////////////////////////////////////////////////////////////////////////////// + switch (expected_props::inequality_comparable_support) { + case ti::inequality_comparable::maybe: + EXPECT_FALSE(InequalityComparable<arch>::value); + EXPECT_FALSE(NothrowInequalityComparable<arch>::value); + break; + case ti::inequality_comparable::yes: + EXPECT_TRUE(InequalityComparable<arch>::value); + EXPECT_FALSE(NothrowInequalityComparable<arch>::value); + break; + case ti::inequality_comparable::nothrow: + EXPECT_TRUE(InequalityComparable<arch>::value); + EXPECT_TRUE(NothrowInequalityComparable<arch>::value); + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Less than comparable checks // + ////////////////////////////////////////////////////////////////////////////// + switch (expected_props::less_than_comparable_support) { + case ti::less_than_comparable::maybe: + EXPECT_FALSE(LessThanComparable<arch>::value); + EXPECT_FALSE(NothrowLessThanComparable<arch>::value); + break; + case ti::less_than_comparable::yes: + EXPECT_TRUE(LessThanComparable<arch>::value); + EXPECT_FALSE(NothrowLessThanComparable<arch>::value); + break; + case ti::less_than_comparable::nothrow: + EXPECT_TRUE(LessThanComparable<arch>::value); + EXPECT_TRUE(NothrowLessThanComparable<arch>::value); + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Less equal comparable checks // + ////////////////////////////////////////////////////////////////////////////// + switch (expected_props::less_equal_comparable_support) { + case ti::less_equal_comparable::maybe: + EXPECT_FALSE(LessEqualComparable<arch>::value); + EXPECT_FALSE(NothrowLessEqualComparable<arch>::value); + break; + case ti::less_equal_comparable::yes: + EXPECT_TRUE(LessEqualComparable<arch>::value); + EXPECT_FALSE(NothrowLessEqualComparable<arch>::value); + break; + case ti::less_equal_comparable::nothrow: + EXPECT_TRUE(LessEqualComparable<arch>::value); + EXPECT_TRUE(NothrowLessEqualComparable<arch>::value); + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Greater equal comparable checks // + ////////////////////////////////////////////////////////////////////////////// + switch (expected_props::greater_equal_comparable_support) { + case ti::greater_equal_comparable::maybe: + EXPECT_FALSE(GreaterEqualComparable<arch>::value); + EXPECT_FALSE(NothrowGreaterEqualComparable<arch>::value); + break; + case ti::greater_equal_comparable::yes: + EXPECT_TRUE(GreaterEqualComparable<arch>::value); + EXPECT_FALSE(NothrowGreaterEqualComparable<arch>::value); + break; + case ti::greater_equal_comparable::nothrow: + EXPECT_TRUE(GreaterEqualComparable<arch>::value); + EXPECT_TRUE(NothrowGreaterEqualComparable<arch>::value); + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Greater than comparable checks // + ////////////////////////////////////////////////////////////////////////////// + switch (expected_props::greater_than_comparable_support) { + case ti::greater_than_comparable::maybe: + EXPECT_FALSE(GreaterThanComparable<arch>::value); + EXPECT_FALSE(NothrowGreaterThanComparable<arch>::value); + break; + case ti::greater_than_comparable::yes: + EXPECT_TRUE(GreaterThanComparable<arch>::value); + EXPECT_FALSE(NothrowGreaterThanComparable<arch>::value); + break; + case ti::greater_than_comparable::nothrow: + EXPECT_TRUE(GreaterThanComparable<arch>::value); + EXPECT_TRUE(NothrowGreaterThanComparable<arch>::value); + break; + } +} + +TYPED_TEST_P(ProfileTest, HasAppropriateAuxilliaryProperties) { + using profile = typename TypeParam::profile; + using arch = typename TypeParam::arch; + using expected_profile = typename TypeParam::expected_profile; + + using props = ti::PropertiesOfT<profile>; + using arch_props = ti::PropertiesOfArchetypeT<arch>; + using expected_props = ti::PropertiesOfT<expected_profile>; + + // Make sure all of the properties are as expected. + // There are seemingly redundant tests here to make it easier to diagnose + // the specifics of the failure if something were to go wrong. + EXPECT_TRUE((std::is_same<props, arch_props>::value)); + EXPECT_TRUE((std::is_same<props, expected_props>::value)); + EXPECT_TRUE((std::is_same<arch_props, expected_props>::value)); + + EXPECT_EQ(props::swappable_support, expected_props::swappable_support); + + EXPECT_EQ(props::hashable_support, expected_props::hashable_support); + + // Avoid additional error message noise when profile and archetype match with + // each other but were not what was expected. + if (!std::is_same<props, arch_props>::value) { + EXPECT_EQ(arch_props::swappable_support, expected_props::swappable_support); + + EXPECT_EQ(arch_props::hashable_support, expected_props::hashable_support); + } + + ////////////////////////////////////////////////////////////////////////////// + // Swappable checks // + ////////////////////////////////////////////////////////////////////////////// + switch (expected_props::swappable_support) { + case ti::swappable::maybe: + EXPECT_FALSE(absl::type_traits_internal::IsSwappable<arch>::value); + EXPECT_FALSE(absl::type_traits_internal::IsNothrowSwappable<arch>::value); + break; + case ti::swappable::yes: + EXPECT_TRUE(absl::type_traits_internal::IsSwappable<arch>::value); + EXPECT_FALSE(absl::type_traits_internal::IsNothrowSwappable<arch>::value); + break; + case ti::swappable::nothrow: + EXPECT_TRUE(absl::type_traits_internal::IsSwappable<arch>::value); + EXPECT_TRUE(absl::type_traits_internal::IsNothrowSwappable<arch>::value); + break; + } + + ////////////////////////////////////////////////////////////////////////////// + // Hashable checks // + ////////////////////////////////////////////////////////////////////////////// + switch (expected_props::hashable_support) { + case ti::hashable::maybe: +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ + EXPECT_FALSE(absl::type_traits_internal::IsHashable<arch>::value); +#endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ + break; + case ti::hashable::yes: + EXPECT_TRUE(absl::type_traits_internal::IsHashable<arch>::value); + break; + } +} + +REGISTER_TYPED_TEST_SUITE_P(ProfileTest, HasAppropriateConstructionProperties, + HasAppropriateAssignmentProperties, + HasAppropriateComparisonProperties, + HasAppropriateAuxilliaryProperties); + +template <class Profile, class Arch, class ExpectedProfile> +struct ProfileAndExpectation { + using profile = Profile; + using arch = Arch; + using expected_profile = ExpectedProfile; +}; + +using CoreProfilesToTest = ::testing::Types< + // The terminating case of combine (all properties are "maybe"). + ProfileAndExpectation<ti::CombineProfiles<>, + ti::Archetype<ti::CombineProfiles<>>, + ti::ConformanceProfile<>>, + + // Core default constructor profiles + ProfileAndExpectation< + ti::HasDefaultConstructorProfile, ti::HasDefaultConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::yes>>, + ProfileAndExpectation< + ti::HasNothrowDefaultConstructorProfile, + ti::HasNothrowDefaultConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::nothrow>>, + ProfileAndExpectation< + ti::HasTrivialDefaultConstructorProfile, + ti::HasTrivialDefaultConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::trivial>>, + + // Core move constructor profiles + ProfileAndExpectation< + ti::HasMoveConstructorProfile, ti::HasMoveConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::maybe, + ti::move_constructible::yes>>, + ProfileAndExpectation< + ti::HasNothrowMoveConstructorProfile, + ti::HasNothrowMoveConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::maybe, + ti::move_constructible::nothrow>>, + ProfileAndExpectation< + ti::HasTrivialMoveConstructorProfile, + ti::HasTrivialMoveConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::maybe, + ti::move_constructible::trivial>>, + + // Core copy constructor profiles + ProfileAndExpectation< + ti::HasCopyConstructorProfile, ti::HasCopyConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::maybe, + ti::move_constructible::maybe, + ti::copy_constructible::yes>>, + ProfileAndExpectation< + ti::HasNothrowCopyConstructorProfile, + ti::HasNothrowCopyConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::maybe, + ti::move_constructible::maybe, + ti::copy_constructible::nothrow>>, + ProfileAndExpectation< + ti::HasTrivialCopyConstructorProfile, + ti::HasTrivialCopyConstructorArchetype, + ti::ConformanceProfile<ti::default_constructible::maybe, + ti::move_constructible::maybe, + ti::copy_constructible::trivial>>, + + // Core move assignment profiles + ProfileAndExpectation< + ti::HasMoveAssignProfile, ti::HasMoveAssignArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::yes>>, + ProfileAndExpectation< + ti::HasNothrowMoveAssignProfile, ti::HasNothrowMoveAssignArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::nothrow>>, + ProfileAndExpectation< + ti::HasTrivialMoveAssignProfile, ti::HasTrivialMoveAssignArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::trivial>>, + + // Core copy assignment profiles + ProfileAndExpectation< + ti::HasCopyAssignProfile, ti::HasCopyAssignArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::yes>>, + ProfileAndExpectation< + ti::HasNothrowCopyAssignProfile, ti::HasNothrowCopyAssignArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::nothrow>>, + ProfileAndExpectation< + ti::HasTrivialCopyAssignProfile, ti::HasTrivialCopyAssignArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::trivial>>, + + // Core destructor profiles + ProfileAndExpectation< + ti::HasDestructorProfile, ti::HasDestructorArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::yes>>, + ProfileAndExpectation< + ti::HasNothrowDestructorProfile, ti::HasNothrowDestructorArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::nothrow>>, + ProfileAndExpectation< + ti::HasTrivialDestructorProfile, ti::HasTrivialDestructorArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::trivial>>, + + // Core equality comparable profiles + ProfileAndExpectation< + ti::HasEqualityProfile, ti::HasEqualityArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::yes>>, + ProfileAndExpectation< + ti::HasNothrowEqualityProfile, ti::HasNothrowEqualityArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::nothrow>>, + + // Core inequality comparable profiles + ProfileAndExpectation< + ti::HasInequalityProfile, ti::HasInequalityArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::yes>>, + ProfileAndExpectation< + ti::HasNothrowInequalityProfile, ti::HasNothrowInequalityArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, + ti::inequality_comparable::nothrow>>, + + // Core less than comparable profiles + ProfileAndExpectation< + ti::HasLessThanProfile, ti::HasLessThanArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::yes>>, + ProfileAndExpectation< + ti::HasNothrowLessThanProfile, ti::HasNothrowLessThanArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::nothrow>>, + + // Core less equal comparable profiles + ProfileAndExpectation< + ti::HasLessEqualProfile, ti::HasLessEqualArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::yes>>, + ProfileAndExpectation< + ti::HasNothrowLessEqualProfile, ti::HasNothrowLessEqualArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, + ti::less_equal_comparable::nothrow>>, + + // Core greater equal comparable profiles + ProfileAndExpectation< + ti::HasGreaterEqualProfile, ti::HasGreaterEqualArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::yes>>, + ProfileAndExpectation< + ti::HasNothrowGreaterEqualProfile, ti::HasNothrowGreaterEqualArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::nothrow>>, + + // Core greater than comparable profiles + ProfileAndExpectation< + ti::HasGreaterThanProfile, ti::HasGreaterThanArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::yes>>, + ProfileAndExpectation< + ti::HasNothrowGreaterThanProfile, ti::HasNothrowGreaterThanArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::nothrow>>, + + // Core swappable profiles + ProfileAndExpectation< + ti::HasSwapProfile, ti::HasSwapArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::maybe, ti::swappable::yes>>, + ProfileAndExpectation< + ti::HasNothrowSwapProfile, ti::HasNothrowSwapArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::maybe, ti::swappable::nothrow>>, + + // Core hashable profiles + ProfileAndExpectation< + ti::HasStdHashSpecializationProfile, + ti::HasStdHashSpecializationArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::maybe, ti::swappable::maybe, + ti::hashable::yes>>>; + +using CommonProfilesToTest = ::testing::Types< + // NothrowMoveConstructible + ProfileAndExpectation< + ti::NothrowMoveConstructibleProfile, + ti::NothrowMoveConstructibleArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::nothrow, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::nothrow>>, + + // CopyConstructible + ProfileAndExpectation< + ti::CopyConstructibleProfile, ti::CopyConstructibleArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::nothrow, + ti::copy_constructible::yes, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::nothrow>>, + + // NothrowMovable + ProfileAndExpectation< + ti::NothrowMovableProfile, ti::NothrowMovableArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::nothrow, + ti::copy_constructible::maybe, ti::move_assignable::nothrow, + ti::copy_assignable::maybe, ti::destructible::nothrow, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::maybe, ti::swappable::nothrow>>, + + // Value + ProfileAndExpectation< + ti::ValueProfile, ti::ValueArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::nothrow, + ti::copy_constructible::yes, ti::move_assignable::nothrow, + ti::copy_assignable::yes, ti::destructible::nothrow, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::maybe, ti::swappable::nothrow>>, + + //////////////////////////////////////////////////////////////////////////// + // Common but also DefaultConstructible // + //////////////////////////////////////////////////////////////////////////// + + // DefaultConstructibleNothrowMoveConstructible + ProfileAndExpectation< + ti::DefaultConstructibleNothrowMoveConstructibleProfile, + ti::DefaultConstructibleNothrowMoveConstructibleArchetype, + ti::ConformanceProfile< + ti::default_constructible::yes, ti::move_constructible::nothrow, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::nothrow>>, + + // DefaultConstructibleCopyConstructible + ProfileAndExpectation< + ti::DefaultConstructibleCopyConstructibleProfile, + ti::DefaultConstructibleCopyConstructibleArchetype, + ti::ConformanceProfile< + ti::default_constructible::yes, ti::move_constructible::nothrow, + ti::copy_constructible::yes, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::nothrow>>, + + // DefaultConstructibleNothrowMovable + ProfileAndExpectation< + ti::DefaultConstructibleNothrowMovableProfile, + ti::DefaultConstructibleNothrowMovableArchetype, + ti::ConformanceProfile< + ti::default_constructible::yes, ti::move_constructible::nothrow, + ti::copy_constructible::maybe, ti::move_assignable::nothrow, + ti::copy_assignable::maybe, ti::destructible::nothrow, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::maybe, ti::swappable::nothrow>>, + + // DefaultConstructibleValue + ProfileAndExpectation< + ti::DefaultConstructibleValueProfile, + ti::DefaultConstructibleValueArchetype, + ti::ConformanceProfile< + ti::default_constructible::yes, ti::move_constructible::nothrow, + ti::copy_constructible::yes, ti::move_assignable::nothrow, + ti::copy_assignable::yes, ti::destructible::nothrow, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::maybe, ti::swappable::nothrow>>>; + +using ComparableHelpersProfilesToTest = ::testing::Types< + // Equatable + ProfileAndExpectation< + ti::EquatableProfile, ti::EquatableArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::yes, ti::inequality_comparable::yes>>, + + // Comparable + ProfileAndExpectation< + ti::ComparableProfile, ti::ComparableArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::yes, ti::inequality_comparable::yes, + ti::less_than_comparable::yes, ti::less_equal_comparable::yes, + ti::greater_equal_comparable::yes, + ti::greater_than_comparable::yes>>, + + // NothrowEquatable + ProfileAndExpectation< + ti::NothrowEquatableProfile, ti::NothrowEquatableArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::nothrow, + ti::inequality_comparable::nothrow>>, + + // NothrowComparable + ProfileAndExpectation< + ti::NothrowComparableProfile, ti::NothrowComparableArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::maybe, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::maybe, + ti::equality_comparable::nothrow, + ti::inequality_comparable::nothrow, + ti::less_than_comparable::nothrow, + ti::less_equal_comparable::nothrow, + ti::greater_equal_comparable::nothrow, + ti::greater_than_comparable::nothrow>>>; + +using CommonComparableProfilesToTest = ::testing::Types< + // ComparableNothrowMoveConstructible + ProfileAndExpectation< + ti::ComparableNothrowMoveConstructibleProfile, + ti::ComparableNothrowMoveConstructibleArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::nothrow, + ti::copy_constructible::maybe, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::nothrow, + ti::equality_comparable::yes, ti::inequality_comparable::yes, + ti::less_than_comparable::yes, ti::less_equal_comparable::yes, + ti::greater_equal_comparable::yes, + ti::greater_than_comparable::yes>>, + + // ComparableCopyConstructible + ProfileAndExpectation< + ti::ComparableCopyConstructibleProfile, + ti::ComparableCopyConstructibleArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::nothrow, + ti::copy_constructible::yes, ti::move_assignable::maybe, + ti::copy_assignable::maybe, ti::destructible::nothrow, + ti::equality_comparable::yes, ti::inequality_comparable::yes, + ti::less_than_comparable::yes, ti::less_equal_comparable::yes, + ti::greater_equal_comparable::yes, + ti::greater_than_comparable::yes>>, + + // ComparableNothrowMovable + ProfileAndExpectation< + ti::ComparableNothrowMovableProfile, + ti::ComparableNothrowMovableArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::nothrow, + ti::copy_constructible::maybe, ti::move_assignable::nothrow, + ti::copy_assignable::maybe, ti::destructible::nothrow, + ti::equality_comparable::yes, ti::inequality_comparable::yes, + ti::less_than_comparable::yes, ti::less_equal_comparable::yes, + ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes, + ti::swappable::nothrow>>, + + // ComparableValue + ProfileAndExpectation< + ti::ComparableValueProfile, ti::ComparableValueArchetype, + ti::ConformanceProfile< + ti::default_constructible::maybe, ti::move_constructible::nothrow, + ti::copy_constructible::yes, ti::move_assignable::nothrow, + ti::copy_assignable::yes, ti::destructible::nothrow, + ti::equality_comparable::yes, ti::inequality_comparable::yes, + ti::less_than_comparable::yes, ti::less_equal_comparable::yes, + ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes, + ti::swappable::nothrow>>>; + +using TrivialProfilesToTest = ::testing::Types< + ProfileAndExpectation< + ti::TrivialSpecialMemberFunctionsProfile, + ti::TrivialSpecialMemberFunctionsArchetype, + ti::ConformanceProfile< + ti::default_constructible::trivial, ti::move_constructible::trivial, + ti::copy_constructible::trivial, ti::move_assignable::trivial, + ti::copy_assignable::trivial, ti::destructible::trivial, + ti::equality_comparable::maybe, ti::inequality_comparable::maybe, + ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe, + ti::greater_equal_comparable::maybe, + ti::greater_than_comparable::maybe, ti::swappable::nothrow>>, + + ProfileAndExpectation< + ti::TriviallyCompleteProfile, ti::TriviallyCompleteArchetype, + ti::ConformanceProfile< + ti::default_constructible::trivial, ti::move_constructible::trivial, + ti::copy_constructible::trivial, ti::move_assignable::trivial, + ti::copy_assignable::trivial, ti::destructible::trivial, + ti::equality_comparable::yes, ti::inequality_comparable::yes, + ti::less_than_comparable::yes, ti::less_equal_comparable::yes, + ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes, + ti::swappable::nothrow, ti::hashable::yes>>>; + +INSTANTIATE_TYPED_TEST_SUITE_P(Core, ProfileTest, CoreProfilesToTest); +INSTANTIATE_TYPED_TEST_SUITE_P(Common, ProfileTest, CommonProfilesToTest); +INSTANTIATE_TYPED_TEST_SUITE_P(ComparableHelpers, ProfileTest, + ComparableHelpersProfilesToTest); +INSTANTIATE_TYPED_TEST_SUITE_P(CommonComparable, ProfileTest, + CommonComparableProfilesToTest); +INSTANTIATE_TYPED_TEST_SUITE_P(Trivial, ProfileTest, TrivialProfilesToTest); + +// TODO(calabrese) Test runtime results + +} // namespace diff --git a/absl/types/internal/optional.h b/absl/types/internal/optional.h index 3c8e7cca..92932b60 100644 --- a/absl/types/internal/optional.h +++ b/absl/types/internal/optional.h @@ -54,7 +54,7 @@ #endif namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // Forward declaration template <typename T> @@ -85,8 +85,8 @@ class optional_data_dtor_base { bool engaged_; // Data storage union { - dummy_type dummy_; T data_; + dummy_type dummy_; }; void destruct() noexcept { @@ -120,8 +120,8 @@ class optional_data_dtor_base<T, true> { bool engaged_; // Data storage union { - dummy_type dummy_; T data_; + dummy_type dummy_; }; void destruct() noexcept { engaged_ = false; } @@ -388,7 +388,7 @@ struct optional_hash_base<T, decltype(std::hash<absl::remove_const_t<T> >()( }; } // namespace optional_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS diff --git a/absl/types/internal/span.h b/absl/types/internal/span.h index 873ae160..112612f4 100644 --- a/absl/types/internal/span.h +++ b/absl/types/internal/span.h @@ -26,7 +26,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace span_internal { // A constexpr min function @@ -122,7 +122,7 @@ template <typename From, typename To> using EnableIfConvertibleTo = typename std::enable_if<IsConvertible<From, To>::value>::type; } // namespace span_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TYPES_INTERNAL_SPAN_H_ diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h index 4f29f617..71bd3adf 100644 --- a/absl/types/internal/variant.h +++ b/absl/types/internal/variant.h @@ -37,10 +37,10 @@ #include "absl/types/bad_variant_access.h" #include "absl/utility/utility.h" -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN template <class... Types> class variant; @@ -1639,8 +1639,8 @@ struct VariantHashBase<Variant, }; } // namespace variant_internal -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#endif // !defined(ABSL_HAVE_STD_VARIANT) +#endif // !defined(ABSL_USES_STD_VARIANT) #endif // ABSL_TYPES_variant_internal_H_ diff --git a/absl/types/optional.h b/absl/types/optional.h index 6614d7bd..2025e29f 100644 --- a/absl/types/optional.h +++ b/absl/types/optional.h @@ -38,21 +38,21 @@ #include "absl/base/config.h" // TODO(calabrese) IWYU removal? #include "absl/utility/utility.h" -#ifdef ABSL_HAVE_STD_OPTIONAL +#ifdef ABSL_USES_STD_OPTIONAL #include <optional> // IWYU pragma: export namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN using std::bad_optional_access; using std::optional; using std::make_optional; using std::nullopt_t; using std::nullopt; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#else // ABSL_HAVE_STD_OPTIONAL +#else // ABSL_USES_STD_OPTIONAL #include <cassert> #include <functional> @@ -67,7 +67,7 @@ using std::nullopt; #include "absl/types/internal/optional.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // nullopt_t // @@ -757,7 +757,7 @@ constexpr auto operator>=(const U& v, const optional<T>& x) return static_cast<bool>(x) ? static_cast<bool>(v >= *x) : true; } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl namespace std { @@ -771,6 +771,6 @@ struct hash<absl::optional<T> > #undef ABSL_MSVC_CONSTEXPR_BUG_IN_UNION_LIKE_CLASS -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL #endif // ABSL_TYPES_OPTIONAL_H_ diff --git a/absl/types/optional_exception_safety_test.cc b/absl/types/optional_exception_safety_test.cc index 056ced42..8e5fe851 100644 --- a/absl/types/optional_exception_safety_test.cc +++ b/absl/types/optional_exception_safety_test.cc @@ -14,11 +14,17 @@ #include "absl/types/optional.h" +#include "absl/base/config.h" + +// This test is a no-op when absl::optional is an alias for std::optional and +// when exceptions are not enabled. +#if !defined(ABSL_USES_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS) + #include "gtest/gtest.h" #include "absl/base/internal/exception_safety_testing.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { @@ -280,5 +286,7 @@ TEST(OptionalExceptionSafety, NothrowMoveAssign) { } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl + +#endif // #if !defined(ABSL_USES_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc index e6a36eb8..47d5c8a2 100644 --- a/absl/types/optional_test.cc +++ b/absl/types/optional_test.cc @@ -14,6 +14,9 @@ #include "absl/types/optional.h" +// This test is a no-op when absl::optional is an alias for std::optional. +#if !defined(ABSL_USES_STD_OPTIONAL) + #include <string> #include <type_traits> #include <utility> @@ -221,7 +224,7 @@ TEST(optionalTest, CopyConstructor) { EXPECT_FALSE( absl::is_trivially_copy_constructible<absl::optional<Copyable>>::value); -#if defined(ABSL_HAVE_STD_OPTIONAL) && defined(__GLIBCXX__) +#if defined(ABSL_USES_STD_OPTIONAL) && defined(__GLIBCXX__) // libstdc++ std::optional implementation (as of 7.2) has a bug: when T is // trivially copyable, optional<T> is not trivially copyable (due to one of // its base class is unconditionally nontrivial). @@ -276,7 +279,7 @@ TEST(optionalTest, CopyConstructor) { // std::optional when T is volatile-qualified. So skipping this test. // Bug report: // https://connect.microsoft.com/VisualStudio/feedback/details/3142534 -#if defined(ABSL_HAVE_STD_OPTIONAL) && defined(_MSC_VER) && _MSC_VER >= 1911 +#if defined(ABSL_USES_STD_OPTIONAL) && defined(_MSC_VER) && _MSC_VER >= 1911 #define ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG 1 #endif #ifndef ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG @@ -302,7 +305,7 @@ TEST(optionalTest, MoveConstructor) { EXPECT_FALSE(std::is_move_constructible<absl::optional<NonMovable>>::value); // test noexcept EXPECT_TRUE(std::is_nothrow_move_constructible<absl::optional<int>>::value); -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL EXPECT_EQ( absl::default_allocator_is_nothrow::value, std::is_nothrow_move_constructible<absl::optional<MoveableThrow>>::value); @@ -636,7 +639,7 @@ TEST(optionalTest, CopyAssignment) { EXPECT_FALSE(absl::is_trivially_copy_assignable<NonTrivial>::value); // std::optional doesn't support volatile nontrivial types. -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL { StructorListener listener; Listenable::listener = &listener; @@ -655,7 +658,7 @@ TEST(optionalTest, CopyAssignment) { EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.volatile_copy_assign); } -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL } TEST(optionalTest, MoveAssignment) { @@ -679,7 +682,7 @@ TEST(optionalTest, MoveAssignment) { EXPECT_EQ(1, listener.move_assign); } // std::optional doesn't support volatile nontrivial types. -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL { StructorListener listener; Listenable::listener = &listener; @@ -699,7 +702,7 @@ TEST(optionalTest, MoveAssignment) { EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.volatile_move_assign); } -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL EXPECT_FALSE(absl::is_move_assignable<absl::optional<const int>>::value); EXPECT_TRUE(absl::is_move_assignable<absl::optional<Copyable>>::value); EXPECT_TRUE(absl::is_move_assignable<absl::optional<MoveableThrow>>::value); @@ -941,7 +944,7 @@ TEST(optionalTest, Swap) { template <int v> struct DeletedOpAddr { - constexpr static const int value = v; + int value = v; constexpr DeletedOpAddr() = default; constexpr const DeletedOpAddr<v>* operator&() const = delete; // NOLINT DeletedOpAddr<v>* operator&() = delete; // NOLINT @@ -951,9 +954,9 @@ struct DeletedOpAddr { // to document the fact that the current implementation of absl::optional<T> // expects such usecases to be malformed and not compile. TEST(optionalTest, OperatorAddr) { - constexpr const int v = -1; + constexpr int v = -1; { // constexpr - constexpr const absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{}); + constexpr absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{}); static_assert(opt.has_value(), ""); // static_assert(opt->value == v, ""); static_assert((*opt).value == v, ""); @@ -1557,7 +1560,7 @@ TEST(optionalTest, NoExcept) { static_assert( std::is_nothrow_move_constructible<absl::optional<MoveMeNoThrow>>::value, ""); -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL static_assert(absl::default_allocator_is_nothrow::value == std::is_nothrow_move_constructible< absl::optional<MoveMeThrow>>::value, @@ -1654,3 +1657,5 @@ TEST(optionalTest, InPlaceTSFINAEBug) { #endif // !defined(__EMSCRIPTEN__) } // namespace + +#endif // #if !defined(ABSL_USES_STD_OPTIONAL) diff --git a/absl/types/span.h b/absl/types/span.h index 98c6cdc8..3283145a 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -71,7 +71,7 @@ #include "absl/types/internal/span.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN //------------------------------------------------------------------------------ // Span @@ -708,6 +708,6 @@ template <int&... ExplicitArgumentBarrier, typename T, size_t N> constexpr Span<const T> MakeConstSpan(const T (&array)[N]) noexcept { return Span<const T>(array, N); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TYPES_SPAN_H_ diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc index 9269f911..22467a0a 100644 --- a/absl/types/span_test.cc +++ b/absl/types/span_test.cc @@ -35,7 +35,7 @@ namespace { MATCHER_P(DataIs, data, - absl::StrCat("data() is ", negation ? "is " : "isn't ", + absl::StrCat("data() ", negation ? "isn't " : "is ", testing::PrintToString(data))) { return arg.data() == data; } diff --git a/absl/types/variant.h b/absl/types/variant.h index 1c1962b1..776d19a1 100644 --- a/absl/types/variant.h +++ b/absl/types/variant.h @@ -24,7 +24,7 @@ // should always hold a value of one of its alternative types (except in the // "valueless by exception state" -- see below). A default-constructed // `absl::variant` will hold the value of its first alternative type, provided -// it is default-constructable. +// it is default-constructible. // // In exceptional cases due to error, an `absl::variant` can hold no // value (known as a "valueless by exception" state), though this is not the @@ -45,12 +45,12 @@ #include "absl/base/config.h" #include "absl/utility/utility.h" -#ifdef ABSL_HAVE_STD_VARIANT +#ifdef ABSL_USES_STD_VARIANT #include <variant> // IWYU pragma: export namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN using std::bad_variant_access; using std::get; using std::get_if; @@ -63,10 +63,10 @@ using std::variant_npos; using std::variant_size; using std::variant_size_v; using std::visit; -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl -#else // ABSL_HAVE_STD_VARIANT +#else // ABSL_USES_STD_VARIANT #include <functional> #include <new> @@ -79,7 +79,7 @@ using std::visit; #include "absl/types/internal/variant.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // ----------------------------------------------------------------------------- // absl::variant @@ -95,7 +95,7 @@ inline namespace lts_2019_08_08 { // // assign it to a std::string. // absl::variant<int, std::string> v = std::string("abc"); // -// // A default-contructed variant will hold a value-initialized value of +// // A default-constructed variant will hold a value-initialized value of // // the first alternative type. // auto a = absl::variant<int, std::string>(); // Holds an int of value '0'. // @@ -798,7 +798,7 @@ operator>=(const variant<Types...>& a, const variant<Types...>& b) { a.index()); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl namespace std { @@ -816,10 +816,10 @@ struct hash<absl::variant<T...>> } // namespace std -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace variant_internal { // Helper visitor for converting a variant<Ts...>` into another type (mostly @@ -855,7 +855,7 @@ To ConvertVariantTo(Variant&& variant) { std::forward<Variant>(variant)); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TYPES_VARIANT_H_ diff --git a/absl/types/variant_benchmark.cc b/absl/types/variant_benchmark.cc index efe02310..350b1753 100644 --- a/absl/types/variant_benchmark.cc +++ b/absl/types/variant_benchmark.cc @@ -28,7 +28,7 @@ #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { template <std::size_t I> @@ -218,5 +218,5 @@ BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2) ->DenseRange(0, integral_pow(4, 2) - 1); } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc index 31662545..439c6e1d 100644 --- a/absl/types/variant_exception_safety_test.cc +++ b/absl/types/variant_exception_safety_test.cc @@ -14,6 +14,12 @@ #include "absl/types/variant.h" +#include "absl/base/config.h" + +// This test is a no-op when absl::variant is an alias for std::variant and when +// exceptions are not enabled. +#if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) + #include <iostream> #include <memory> #include <utility> @@ -21,7 +27,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "absl/base/config.h" #include "absl/base/internal/exception_safety_testing.h" #include "absl/memory/memory.h" @@ -29,7 +34,7 @@ #if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { using ::testing::MakeExceptionSafetyTester; @@ -233,7 +238,7 @@ TEST(VariantExceptionSafetyTest, CopyAssign) { } // libstdc++ std::variant has bugs on copy assignment regarding exception // safety. -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) // index() != j // if is_nothrow_copy_constructible_v<Tj> or // !is_nothrow_move_constructible<Tj> is true, equivalent to @@ -264,7 +269,7 @@ TEST(VariantExceptionSafetyTest, CopyAssign) { .Test()); EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); } -#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) { // is_nothrow_copy_constructible_v<Tj> == false && // is_nothrow_move_constructible_v<Tj> == true @@ -321,7 +326,7 @@ TEST(VariantExceptionSafetyTest, MoveAssign) { // The fix is targeted for gcc-9. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c7 // https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267614 -#if !(defined(ABSL_HAVE_STD_VARIANT) && \ +#if !(defined(ABSL_USES_STD_VARIANT) && \ defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) // - otherwise (index() != j), equivalent to // emplace<j>(get<j>(std::move(rhs))) @@ -338,7 +343,7 @@ TEST(VariantExceptionSafetyTest, MoveAssign) { auto copy = rhs; *lhs = std::move(copy); })); -#endif // !(defined(ABSL_HAVE_STD_VARIANT) && +#endif // !(defined(ABSL_USES_STD_VARIANT) && // defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) } } @@ -441,7 +446,7 @@ TEST(VariantExceptionSafetyTest, ValueAssign) { // and operator=(variant&&) invokes Tj's move ctor which doesn't throw. // libstdc++ std::variant has bugs on conversion assignment regarding // exception safety. -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) { MoveNothrow rhs; EXPECT_TRUE(MakeExceptionSafetyTester() @@ -449,7 +454,7 @@ TEST(VariantExceptionSafetyTest, ValueAssign) { .WithContracts(VariantInvariants, strong_guarantee) .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); } -#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) } TEST(VariantExceptionSafetyTest, Emplace) { @@ -519,7 +524,9 @@ TEST(VariantExceptionSafetyTest, Swap) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) + +#endif // #if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc index ff0f187a..96393333 100644 --- a/absl/types/variant_test.cc +++ b/absl/types/variant_test.cc @@ -19,6 +19,9 @@ #include "absl/types/variant.h" +// This test is a no-op when absl::variant is an alias for std::variant. +#if !defined(ABSL_USES_STD_VARIANT) + #include <algorithm> #include <cstddef> #include <functional> @@ -67,7 +70,7 @@ struct hash<Hashable> { struct NonHashable {}; namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN namespace { using ::testing::DoubleEq; @@ -840,7 +843,7 @@ TEST(VariantTest, TestBackupAssign) { } // libstdc++ doesn't pass this test -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) EXPECT_EQ(3, counter[0]); EXPECT_EQ(2, counter[1]); EXPECT_EQ(2, counter[2]); @@ -1040,8 +1043,6 @@ TEST(VariantTest, MemberSwap) { using V = variant<MoveCanThrow, std::string, int>; int i = 33; std::string s = "abc"; - V valueless(in_place_index<0>); - ToValuelessByException(valueless); { // lhs and rhs holds different alternative V lhs(i), rhs(s); @@ -1049,6 +1050,9 @@ TEST(VariantTest, MemberSwap) { EXPECT_THAT(lhs, VariantWith<std::string>(s)); EXPECT_THAT(rhs, VariantWith<int>(i)); } +#ifdef ABSL_HAVE_EXCEPTIONS + V valueless(in_place_index<0>); + ToValuelessByException(valueless); { // lhs is valueless V lhs(valueless), rhs(i); @@ -1070,6 +1074,7 @@ TEST(VariantTest, MemberSwap) { EXPECT_TRUE(lhs.valueless_by_exception()); EXPECT_TRUE(rhs.valueless_by_exception()); } +#endif // ABSL_HAVE_EXCEPTIONS } ////////////////////// @@ -1929,7 +1934,7 @@ TEST(VariantTest, VisitReferenceWrapper) { } // libstdc++ std::variant doesn't support the INVOKE semantics. -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) TEST(VariantTest, VisitMemberFunction) { absl::variant<std::unique_ptr<Class>> p(absl::make_unique<Class>()); absl::variant<std::unique_ptr<const Class>> cp( @@ -1953,7 +1958,7 @@ TEST(VariantTest, VisitDataMember) { EXPECT_EQ(42, absl::visit(&Class::member, cp)); } -#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) ///////////////////////// // [variant.monostate] // @@ -2031,7 +2036,7 @@ TEST(VariantTest, NonmemberSwap) { std::swap(a, b); EXPECT_THAT(a, VariantWith<SpecialSwap>(v2)); EXPECT_THAT(b, VariantWith<SpecialSwap>(v1)); -#ifndef ABSL_HAVE_STD_VARIANT +#ifndef ABSL_USES_STD_VARIANT EXPECT_FALSE(absl::get<SpecialSwap>(a).special_swap); #endif @@ -2079,7 +2084,7 @@ TEST(VariantTest, Hash) { // MSVC std::hash<std::variant> does not use the index, thus produce the same // result on the same value as different alternative. -#if !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT)) +#if !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT)) { // same value as different alternative variant<int, int> v0(in_place_index<0>, 42); @@ -2087,7 +2092,7 @@ TEST(VariantTest, Hash) { std::hash<variant<int, int>> hash; EXPECT_NE(hash(v0), hash(v1)); } -#endif // !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT)) +#endif // !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT)) { std::hash<variant<int>> hash; @@ -2114,7 +2119,7 @@ TEST(VariantTest, Hash) { //////////////////////////////////////// // Test that a set requiring a basic type conversion works correctly -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) TEST(VariantTest, TestConvertingSet) { typedef variant<double> Variant; Variant v(1.0); @@ -2124,7 +2129,7 @@ TEST(VariantTest, TestConvertingSet) { ASSERT_TRUE(nullptr != absl::get_if<double>(&v)); EXPECT_DOUBLE_EQ(2, absl::get<double>(v)); } -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT // Test that a vector of variants behaves reasonably. TEST(VariantTest, Container) { @@ -2276,7 +2281,7 @@ struct Convertible2 { }; TEST(VariantTest, TestRvalueConversion) { -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) variant<double, std::string> var( ConvertVariantTo<variant<double, std::string>>( variant<std::string, int>(0))); @@ -2309,7 +2314,7 @@ TEST(VariantTest, TestRvalueConversion) { variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42)); ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2)); EXPECT_EQ(42, absl::get<uint32_t>(variant2)); -#endif // !ABSL_HAVE_STD_VARIANT +#endif // !ABSL_USES_STD_VARIANT variant<Convertible1, Convertible2> variant3( ConvertVariantTo<variant<Convertible1, Convertible2>>( @@ -2322,7 +2327,7 @@ TEST(VariantTest, TestRvalueConversion) { } TEST(VariantTest, TestLvalueConversion) { -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) variant<std::string, int> source1 = 0; variant<double, std::string> destination( ConvertVariantTo<variant<double, std::string>>(source1)); @@ -2424,7 +2429,7 @@ TEST(VariantTest, DoesNotMoveFromLvalues) { } TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) variant<double, std::string> var( ConvertVariantTo<variant<double, std::string>>( variant<std::string, int>(3))); @@ -2463,7 +2468,7 @@ TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { } TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) variant<std::string, int> source1 = 3; variant<double, std::string> destination( ConvertVariantTo<variant<double, std::string>>(source1)); @@ -2495,7 +2500,7 @@ TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { variant<uint32_t> source6(42); variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6); EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42)); -#endif // !ABSL_HAVE_STD_VARIANT +#endif // !ABSL_USES_STD_VARIANT variant<Convertible2, Convertible1> source7((Convertible1())); variant<Convertible1, Convertible2> variant3( @@ -2529,7 +2534,7 @@ TEST(VariantTest, TestMoveConversionViaConvertVariantTo) { // standard and we know that libstdc++ variant doesn't have this feature. // For more details see the paper: // http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0602r0.html -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) #define ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY 1 #endif @@ -2705,5 +2710,7 @@ TEST(VariantTest, MoveCtorBug) { } } // namespace -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl + +#endif // #if !defined(ABSL_USES_STD_VARIANT) diff --git a/absl/utility/BUILD.bazel b/absl/utility/BUILD.bazel index d41317e3..6881f939 100644 --- a/absl/utility/BUILD.bazel +++ b/absl/utility/BUILD.bazel @@ -1,3 +1,20 @@ +# +# 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. +# + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -11,7 +28,9 @@ licenses(["notice"]) # Apache 2.0 cc_library( name = "utility", - hdrs = ["utility.h"], + hdrs = [ + "utility.h", + ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ diff --git a/absl/utility/utility.h b/absl/utility/utility.h index bc9af048..e6647c7b 100644 --- a/absl/utility/utility.h +++ b/absl/utility/utility.h @@ -51,7 +51,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2019_08_08 { +ABSL_NAMESPACE_BEGIN // integer_sequence // @@ -159,12 +159,12 @@ using index_sequence_for = make_index_sequence<sizeof...(Ts)>; // Tag types -#ifdef ABSL_HAVE_STD_OPTIONAL +#ifdef ABSL_USES_STD_OPTIONAL using std::in_place_t; using std::in_place; -#else // ABSL_HAVE_STD_OPTIONAL +#else // ABSL_USES_STD_OPTIONAL // in_place_t // @@ -175,9 +175,9 @@ struct in_place_t {}; ABSL_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {}); -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL -#if defined(ABSL_HAVE_STD_ANY) || defined(ABSL_HAVE_STD_VARIANT) +#if defined(ABSL_USES_STD_ANY) || defined(ABSL_USES_STD_VARIANT) using std::in_place_type; using std::in_place_type_t; #else @@ -192,9 +192,9 @@ using in_place_type_t = void (*)(utility_internal::InPlaceTypeTag<T>); template <typename T> void in_place_type(utility_internal::InPlaceTypeTag<T>) {} -#endif // ABSL_HAVE_STD_ANY || ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_ANY || ABSL_USES_STD_VARIANT -#ifdef ABSL_HAVE_STD_VARIANT +#ifdef ABSL_USES_STD_VARIANT using std::in_place_index; using std::in_place_index_t; #else @@ -209,7 +209,7 @@ using in_place_index_t = void (*)(utility_internal::InPlaceIndexTag<I>); template <size_t I> void in_place_index(utility_internal::InPlaceIndexTag<I>) {} -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT // Constexpr move and forward @@ -344,7 +344,7 @@ constexpr T make_from_tuple(Tuple&& tup) { std::tuple_size<absl::decay_t<Tuple>>::value>{}); } -} // inline namespace lts_2019_08_08 +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_UTILITY_UTILITY_H_ |