diff options
author | thegeorg <thegeorg@yandex-team.com> | 2023-03-25 20:23:17 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2023-03-25 20:23:17 +0300 |
commit | a50a4399c2600b05a086acdca3ba56c957d62196 (patch) | |
tree | 2cf3f6cc37ccc6bd19c33a928e07dd6c083cea72 /contrib | |
parent | 76f3ccf647d9cff0e38a7989dc89480854107b78 (diff) | |
download | ydb-a50a4399c2600b05a086acdca3ba56c957d62196.tar.gz |
Update contrib/restricted/abseil-cpp-tstring to 20230125.1
Diffstat (limited to 'contrib')
170 files changed, 9415 insertions, 3515 deletions
diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/algorithm/container.h b/contrib/restricted/abseil-cpp-tstring/y_absl/algorithm/container.h index b34b4f498b..0a5a253c2d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/algorithm/container.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/algorithm/container.h @@ -77,9 +77,8 @@ using ContainerIterPairType = decltype(std::make_pair(ContainerIter<C1>(), ContainerIter<C2>())); template <typename C> -using ContainerDifferenceType = - decltype(std::distance(std::declval<ContainerIter<C>>(), - std::declval<ContainerIter<C>>())); +using ContainerDifferenceType = decltype(std::distance( + std::declval<ContainerIter<C>>(), std::declval<ContainerIter<C>>())); template <typename C> using ContainerPointerType = @@ -97,10 +96,14 @@ using ContainerPointerType = // These are meant for internal use only. template <typename C> -ContainerIter<C> c_begin(C& c) { return begin(c); } +ContainerIter<C> c_begin(C& c) { + return begin(c); +} template <typename C> -ContainerIter<C> c_end(C& c) { return end(c); } +ContainerIter<C> c_end(C& c) { + return end(c); +} template <typename T> struct IsUnorderedContainer : std::false_type {}; @@ -343,8 +346,8 @@ container_algorithm_internal::ContainerDifferenceType<const C> c_count_if( // return the first element where two ordered containers differ. Applies `==` to // the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template <typename C1, typename C2> -container_algorithm_internal::ContainerIterPairType<C1, C2> -c_mismatch(C1& c1, C2& c2) { +container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(C1& c1, + C2& c2) { auto first1 = container_algorithm_internal::c_begin(c1); auto last1 = container_algorithm_internal::c_end(c1); auto first2 = container_algorithm_internal::c_begin(c2); @@ -365,8 +368,8 @@ c_mismatch(C1& c1, C2& c2) { // the function's test condition. Applies `pred`to the first N elements of `c1` // and `c2`, where N = min(size(c1), size(c2)). template <typename C1, typename C2, typename BinaryPredicate> -container_algorithm_internal::ContainerIterPairType<C1, C2> -c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) { +container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch( + C1& c1, C2& c2, BinaryPredicate pred) { auto first1 = container_algorithm_internal::c_begin(c1); auto last1 = container_algorithm_internal::c_end(c1); auto first2 = container_algorithm_internal::c_begin(c2); @@ -655,11 +658,10 @@ OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, // some condition, and return the results within an iterator. template <typename C, typename OutputIterator, typename Pred, typename T> OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, - T&& new_value) { + const T& new_value) { return std::replace_copy_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, - std::forward<Pred>(pred), - std::forward<T>(new_value)); + std::forward<Pred>(pred), new_value); } // c_fill() @@ -667,9 +669,9 @@ OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, // Container-based version of the <algorithm> `std::fill()` function to fill a // container with some value. template <typename C, typename T> -void c_fill(C& c, T&& value) { +void c_fill(C& c, const T& value) { std::fill(container_algorithm_internal::c_begin(c), - container_algorithm_internal::c_end(c), std::forward<T>(value)); + container_algorithm_internal::c_end(c), value); } // c_fill_n() @@ -677,9 +679,8 @@ void c_fill(C& c, T&& value) { // Container-based version of the <algorithm> `std::fill_n()` function to fill // the first N elements in a container with some value. template <typename C, typename Size, typename T> -void c_fill_n(C& c, Size n, T&& value) { - std::fill_n(container_algorithm_internal::c_begin(c), n, - std::forward<T>(value)); +void c_fill_n(C& c, Size n, const T& value) { + std::fill_n(container_algorithm_internal::c_begin(c), n, value); } // c_generate() @@ -716,10 +717,11 @@ container_algorithm_internal::ContainerIter<C> c_generate_n(C& c, Size n, // copy a container's elements while removing any elements matching the given // `value`. template <typename C, typename OutputIterator, typename T> -OutputIterator c_remove_copy(const C& c, OutputIterator result, T&& value) { +OutputIterator c_remove_copy(const C& c, OutputIterator result, + const T& value) { return std::remove_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, - std::forward<T>(value)); + value); } // c_remove_copy_if() @@ -1064,20 +1066,19 @@ void c_nth_element( // which does not compare less than `value`. template <typename Sequence, typename T> container_algorithm_internal::ContainerIter<Sequence> c_lower_bound( - Sequence& sequence, T&& value) { + Sequence& sequence, const T& value) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward<T>(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_lower_bound() for performing a `comp` comparison other than // the default `operator<`. template <typename Sequence, typename T, typename LessThan> container_algorithm_internal::ContainerIter<Sequence> c_lower_bound( - Sequence& sequence, T&& value, LessThan&& comp) { + Sequence& sequence, const T& value, LessThan&& comp) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward<T>(value), std::forward<LessThan>(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward<LessThan>(comp)); } // c_upper_bound() @@ -1087,20 +1088,19 @@ container_algorithm_internal::ContainerIter<Sequence> c_lower_bound( // which is greater than `value`. template <typename Sequence, typename T> container_algorithm_internal::ContainerIter<Sequence> c_upper_bound( - Sequence& sequence, T&& value) { + Sequence& sequence, const T& value) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward<T>(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_upper_bound() for performing a `comp` comparison other than // the default `operator<`. template <typename Sequence, typename T, typename LessThan> container_algorithm_internal::ContainerIter<Sequence> c_upper_bound( - Sequence& sequence, T&& value, LessThan&& comp) { + Sequence& sequence, const T& value, LessThan&& comp) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward<T>(value), std::forward<LessThan>(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward<LessThan>(comp)); } // c_equal_range() @@ -1110,20 +1110,19 @@ container_algorithm_internal::ContainerIter<Sequence> c_upper_bound( // sorted container which compare equal to `value`. template <typename Sequence, typename T> container_algorithm_internal::ContainerIterPairType<Sequence, Sequence> -c_equal_range(Sequence& sequence, T&& value) { +c_equal_range(Sequence& sequence, const T& value) { return std::equal_range(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward<T>(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_equal_range() for performing a `comp` comparison other than // the default `operator<`. template <typename Sequence, typename T, typename LessThan> container_algorithm_internal::ContainerIterPairType<Sequence, Sequence> -c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) { +c_equal_range(Sequence& sequence, const T& value, LessThan&& comp) { return std::equal_range(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward<T>(value), std::forward<LessThan>(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward<LessThan>(comp)); } // c_binary_search() @@ -1132,20 +1131,19 @@ c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) { // to test if any element in the sorted container contains a value equivalent to // 'value'. template <typename Sequence, typename T> -bool c_binary_search(Sequence&& sequence, T&& value) { +bool c_binary_search(Sequence&& sequence, const T& value) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<T>(value)); + value); } // Overload of c_binary_search() for performing a `comp` comparison other than // the default `operator<`. template <typename Sequence, typename T, typename LessThan> -bool c_binary_search(Sequence&& sequence, T&& value, LessThan&& comp) { +bool c_binary_search(Sequence&& sequence, const T& value, LessThan&& comp) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<T>(value), - std::forward<LessThan>(comp)); + value, std::forward<LessThan>(comp)); } //------------------------------------------------------------------------------ @@ -1560,8 +1558,8 @@ container_algorithm_internal::ContainerIter<Sequence> c_max_element( // smallest and largest values, respectively, using `operator<` to make the // comparisons. template <typename C> -container_algorithm_internal::ContainerIterPairType<C, C> -c_minmax_element(C& c) { +container_algorithm_internal::ContainerIterPairType<C, C> c_minmax_element( + C& c) { return std::minmax_element(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } @@ -1569,8 +1567,8 @@ c_minmax_element(C& c) { // Overload of c_minmax_element() for performing `comp` comparisons other than // `operator<`. template <typename C, typename LessThan> -container_algorithm_internal::ContainerIterPairType<C, C> -c_minmax_element(C& c, LessThan&& comp) { +container_algorithm_internal::ContainerIterPairType<C, C> c_minmax_element( + C& c, LessThan&& comp) { return std::minmax_element(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward<LessThan>(comp)); @@ -1588,7 +1586,8 @@ c_minmax_element(C& c, LessThan&& comp) { // that capital letters ("A-Z") have ASCII values less than lowercase letters // ("a-z"). template <typename Sequence1, typename Sequence2> -bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { +bool c_lexicographical_compare(const Sequence1& sequence1, + const Sequence2& sequence2) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), @@ -1599,8 +1598,8 @@ bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { // Overload of c_lexicographical_compare() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. template <typename Sequence1, typename Sequence2, typename LessThan> -bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, - LessThan&& comp) { +bool c_lexicographical_compare(const Sequence1& sequence1, + const Sequence2& sequence2, LessThan&& comp) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), @@ -1655,18 +1654,18 @@ bool c_prev_permutation(C& c, LessThan&& comp) { // c_iota() // -// Container-based version of the <algorithm> `std::iota()` function +// Container-based version of the <numeric> `std::iota()` function // to compute successive values of `value`, as if incremented with `++value` // after each element is written. and write them to the container. template <typename Sequence, typename T> -void c_iota(Sequence& sequence, T&& value) { +void c_iota(Sequence& sequence, const T& value) { std::iota(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward<T>(value)); + container_algorithm_internal::c_end(sequence), value); } + // c_accumulate() // -// Container-based version of the <algorithm> `std::accumulate()` function +// Container-based version of the <numeric> `std::accumulate()` function // to accumulate the element values of a container to `init` and return that // accumulation by value. // @@ -1693,7 +1692,7 @@ decay_t<T> c_accumulate(const Sequence& sequence, T&& init, // c_inner_product() // -// Container-based version of the <algorithm> `std::inner_product()` function +// Container-based version of the <numeric> `std::inner_product()` function // to compute the cumulative inner product of container element pairs. // // Note: Due to a language technicality this function has return type @@ -1724,7 +1723,7 @@ decay_t<T> c_inner_product(const Sequence1& factors1, const Sequence2& factors2, // c_adjacent_difference() // -// Container-based version of the <algorithm> `std::adjacent_difference()` +// Container-based version of the <numeric> `std::adjacent_difference()` // function to compute the difference between each element and the one preceding // it and write it to an iterator. template <typename InputSequence, typename OutputIt> @@ -1747,7 +1746,7 @@ OutputIt c_adjacent_difference(const InputSequence& input, // c_partial_sum() // -// Container-based version of the <algorithm> `std::partial_sum()` function +// Container-based version of the <numeric> `std::partial_sum()` function // to compute the partial sum of the elements in a sequence and write them // to an iterator. The partial sum is the sum of all element values so far in // the sequence. diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/algorithm/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/algorithm/ya.make index d53dc4c83a..41ce8373c6 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/algorithm/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/algorithm/ya.make @@ -6,9 +6,9 @@ LICENSE(Apache-2.0) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -VERSION(20220623.1) +VERSION(20230125.1) -ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20220623.1.tar.gz) +ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20230125.1.tar.gz) NO_RUNTIME() diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/attributes.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/attributes.h index 21e64f43b3..420da2603d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/attributes.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/attributes.h @@ -716,26 +716,9 @@ #define Y_ABSL_CONST_INIT #endif -// Y_ABSL_ATTRIBUTE_PURE_FUNCTION -// -// Y_ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure" -// functions. A function is pure if its return value is only a function of its -// arguments. The pure attribute prohibits a function from modifying the state -// of the program that is observable by means other than inspecting the -// function's return value. Declaring such functions with the pure attribute -// allows the compiler to avoid emitting some calls in repeated invocations of -// the function with the same argument values. -// -// Example: -// -// Y_ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d); -#if Y_ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure) -#define Y_ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]] -#elif Y_ABSL_HAVE_ATTRIBUTE(pure) -#define Y_ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure)) -#else +// These annotations are not available yet due to fear of breaking code. #define Y_ABSL_ATTRIBUTE_PURE_FUNCTION -#endif +#define Y_ABSL_ATTRIBUTE_CONST_FUNCTION // Y_ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function // parameter or implicit object parameter is retained by the return value of the @@ -759,4 +742,41 @@ #define Y_ABSL_ATTRIBUTE_LIFETIME_BOUND #endif +// Y_ABSL_ATTRIBUTE_TRIVIAL_ABI +// Indicates that a type is "trivially relocatable" -- meaning it can be +// relocated without invoking the constructor/destructor, using a form of move +// elision. +// +// From a memory safety point of view, putting aside destructor ordering, it's +// safe to apply Y_ABSL_ATTRIBUTE_TRIVIAL_ABI if an object's location +// can change over the course of its lifetime: if a constructor can be run one +// place, and then the object magically teleports to another place where some +// methods are run, and then the object teleports to yet another place where it +// is destroyed. This is notably not true for self-referential types, where the +// move-constructor must keep the self-reference up to date. If the type changed +// location without invoking the move constructor, it would have a dangling +// self-reference. +// +// The use of this teleporting machinery means that the number of paired +// move/destroy operations can change, and so it is a bad idea to apply this to +// a type meant to count the number of moves. +// +// Warning: applying this can, rarely, break callers. Objects passed by value +// will be destroyed at the end of the call, instead of the end of the +// full-expression containing the call. In addition, it changes the ABI +// of functions accepting this type by value (e.g. to pass in registers). +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// +#if Y_ABSL_HAVE_CPP_ATTRIBUTE(clang::trivial_abi) +#define Y_ABSL_ATTRIBUTE_TRIVIAL_ABI [[clang::trivial_abi]] +#define Y_ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1 +#elif Y_ABSL_HAVE_ATTRIBUTE(trivial_abi) +#define Y_ABSL_ATTRIBUTE_TRIVIAL_ABI __attribute__((trivial_abi)) +#define Y_ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1 +#else +#define Y_ABSL_ATTRIBUTE_TRIVIAL_ABI +#endif + #endif // Y_ABSL_BASE_ATTRIBUTES_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/config.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/config.h index 6ffa20f4f0..2a96715cae 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/config.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/config.h @@ -111,7 +111,7 @@ // // LTS releases can be obtained from // https://github.com/abseil/abseil-cpp/releases. -#define Y_ABSL_LTS_RELEASE_VERSION 20220623 +#define Y_ABSL_LTS_RELEASE_VERSION 20230125 #define Y_ABSL_LTS_RELEASE_PATCH_LEVEL 1 // Helper macro to convert a CPP variable to a string literal. @@ -243,6 +243,7 @@ static_assert(Y_ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #ifdef Y_ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE #error Y_ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set #elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \ + (defined(__clang__) && __clang_major__ >= 15) || \ (!defined(__clang__) && defined(__GLIBCXX__) && \ Y_ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8)) #define Y_ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1 @@ -264,15 +265,27 @@ static_assert(Y_ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #elif defined(Y_ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) #error Y_ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set #elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ + (defined(__clang__) && __clang_major__ >= 15) || \ (!defined(__clang__) && \ ((Y_ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \ (Y_ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) && \ defined(_LIBCPP_VERSION)))) || \ - (defined(_MSC_VER) && !defined(__NVCC__)) + (defined(_MSC_VER) && !defined(__NVCC__) && !defined(__clang__)) #define Y_ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 #define Y_ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 #endif +// Y_ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE +// +// Checks whether `std::is_trivially_copyable<T>` is supported. +// +// Notes: Clang 15+ with libc++ supports these features, GCC hasn't been tested. +#if defined(Y_ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE) +#error Y_ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE cannot be directly set +#elif defined(__clang__) && (__clang_major__ >= 15) +#define Y_ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE 1 +#endif + // Y_ABSL_HAVE_THREAD_LOCAL // // Checks whether C++11's `thread_local` storage duration specifier is @@ -741,6 +754,18 @@ static_assert(Y_ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define Y_ABSL_DLL #endif // defined(_MSC_VER) +#if defined(_MSC_VER) +#if defined(Y_ABSL_BUILD_TEST_DLL) +#define Y_ABSL_TEST_DLL __declspec(dllexport) +#elif defined(Y_ABSL_CONSUME_TEST_DLL) +#define Y_ABSL_TEST_DLL __declspec(dllimport) +#else +#define Y_ABSL_TEST_DLL +#endif +#else +#define Y_ABSL_TEST_DLL +#endif // defined(_MSC_VER) + // Y_ABSL_HAVE_MEMORY_SANITIZER // // MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of @@ -899,10 +924,26 @@ static_assert(Y_ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // Y_ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM // SIMD). +// +// If __CUDA_ARCH__ is defined, then we are compiling CUDA code in device mode. +// In device mode, NEON intrinsics are not available, regardless of host +// platform. +// https://llvm.org/docs/CompileCudaWithLLVM.html#detecting-clang-vs-nvcc-from-code #ifdef Y_ABSL_INTERNAL_HAVE_ARM_NEON #error Y_ABSL_INTERNAL_HAVE_ARM_NEON cannot be directly set -#elif defined(__ARM_NEON) +#elif defined(__ARM_NEON) && !defined(__CUDA_ARCH__) #define Y_ABSL_INTERNAL_HAVE_ARM_NEON 1 #endif +// Y_ABSL_HAVE_CONSTANT_EVALUATED is used for compile-time detection of +// constant evaluation support through `y_absl::is_constant_evaluated`. +#ifdef Y_ABSL_HAVE_CONSTANT_EVALUATED +#error Y_ABSL_HAVE_CONSTANT_EVALUATED cannot be directly set +#endif +#if defined(__cpp_lib_is_constant_evaluated) && (!defined(__CUDACC__) || CUDA_VERSION >= 11000) +#define Y_ABSL_HAVE_CONSTANT_EVALUATED 1 +#elif Y_ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated) && (!defined(__CUDACC__) || CUDA_VERSION >= 11000) +#define Y_ABSL_HAVE_CONSTANT_EVALUATED 1 +#endif + #endif // Y_ABSL_BASE_CONFIG_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/cycleclock.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/cycleclock.h index e75b28d1ad..dc8359dc97 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/cycleclock.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/cycleclock.h @@ -47,6 +47,7 @@ #include "y_absl/base/attributes.h" #include "y_absl/base/config.h" +#include "y_absl/base/internal/cycleclock_config.h" #include "y_absl/base/internal/unscaledcycleclock.h" namespace y_absl { @@ -76,25 +77,9 @@ class CycleClock { #if Y_ABSL_USE_UNSCALED_CYCLECLOCK static CycleClockSourceFunc LoadCycleClockSource(); -#ifdef NDEBUG -#ifdef Y_ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY - // Not debug mode and the UnscaledCycleClock frequency is the CPU - // frequency. Scale the CycleClock to prevent overflow if someone - // tries to represent the time as cycles since the Unix epoch. - static constexpr int32_t kShift = 1; -#else - // Not debug mode and the UnscaledCycleClock isn't operating at the - // raw CPU frequency. There is no need to do any scaling, so don't - // needlessly sacrifice precision. - static constexpr int32_t kShift = 0; -#endif -#else // NDEBUG - // In debug mode use a different shift to discourage depending on a - // particular shift value. - static constexpr int32_t kShift = 2; -#endif // NDEBUG + static constexpr int32_t kShift = kCycleClockShift; + static constexpr double kFrequencyScale = kCycleClockFrequencyScale; - static constexpr double kFrequencyScale = 1.0 / (1 << kShift); Y_ABSL_CONST_INIT static std::atomic<CycleClockSourceFunc> cycle_clock_source_; #endif // Y_ABSL_USE_UNSCALED_CYCLECLOC diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/cycleclock_config.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/cycleclock_config.h new file mode 100644 index 0000000000..390b0d2e1a --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/cycleclock_config.h @@ -0,0 +1,55 @@ +// Copyright 2022 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 Y_ABSL_BASE_INTERNAL_CYCLECLOCK_CONFIG_H_ +#define Y_ABSL_BASE_INTERNAL_CYCLECLOCK_CONFIG_H_ + +#include <cstdint> + +#include "y_absl/base/config.h" +#include "y_absl/base/internal/inline_variable.h" +#include "y_absl/base/internal/unscaledcycleclock_config.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace base_internal { + +#if Y_ABSL_USE_UNSCALED_CYCLECLOCK +#ifdef NDEBUG +#ifdef Y_ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY +// Not debug mode and the UnscaledCycleClock frequency is the CPU +// frequency. Scale the CycleClock to prevent overflow if someone +// tries to represent the time as cycles since the Unix epoch. +Y_ABSL_INTERNAL_INLINE_CONSTEXPR(int32_t, kCycleClockShift, 1); +#else +// Not debug mode and the UnscaledCycleClock isn't operating at the +// raw CPU frequency. There is no need to do any scaling, so don't +// needlessly sacrifice precision. +Y_ABSL_INTERNAL_INLINE_CONSTEXPR(int32_t, kCycleClockShift, 0); +#endif +#else // NDEBUG +// In debug mode use a different shift to discourage depending on a +// particular shift value. +Y_ABSL_INTERNAL_INLINE_CONSTEXPR(int32_t, kCycleClockShift, 2); +#endif // NDEBUG + +Y_ABSL_INTERNAL_INLINE_CONSTEXPR(double, kCycleClockFrequencyScale, + 1.0 / (1 << kCycleClockShift)); +#endif // Y_ABSL_USE_UNSCALED_CYCLECLOC + +} // namespace base_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_BASE_INTERNAL_CYCLECLOCK_CONFIG_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/direct_mmap.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/direct_mmap.h index 5b7750628a..cbee25d1ab 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/direct_mmap.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/direct_mmap.h @@ -97,7 +97,8 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, #ifdef __BIONIC__ // SYS_mmap2 has problems on Android API level <= 16. // Workaround by invoking __mmap2() instead. - return __mmap2(start, length, prot, flags, fd, offset / pagesize); + return __mmap2(start, length, prot, flags, fd, + static_cast<size_t>(offset / pagesize)); #else return reinterpret_cast<void*>( syscall(SYS_mmap2, start, length, prot, flags, fd, diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/inline_variable.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/inline_variable.h index 895727ec87..4f822973e4 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/inline_variable.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/inline_variable.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef Y_ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ -#define Y_ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ +#ifndef Y_ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ +#define Y_ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ #include <type_traits> @@ -104,4 +104,4 @@ #endif // __cpp_inline_variables -#endif // Y_ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ +#endif // Y_ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc.cc index 3d96472b27..e84a396360 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc.cc @@ -332,7 +332,7 @@ size_t GetPageSize() { #elif defined(__wasm__) || defined(__asmjs__) return getpagesize(); #else - return sysconf(_SC_PAGESIZE); + return static_cast<size_t>(sysconf(_SC_PAGESIZE)); #endif } @@ -364,7 +364,7 @@ LowLevelAlloc::Arena::Arena(uint32_t flags_value) } // L < meta_data_arena->mu -LowLevelAlloc::Arena *LowLevelAlloc::NewArena(int32_t flags) { +LowLevelAlloc::Arena *LowLevelAlloc::NewArena(uint32_t flags) { Arena *meta_data_arena = DefaultArena(); #ifndef Y_ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if ((flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc.h index c70d4fd226..82b0216036 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc.h @@ -103,7 +103,7 @@ class LowLevelAlloc { // the provided flags. For example, the call NewArena(kAsyncSignalSafe) // is itself async-signal-safe, as well as generatating an arena that provides // async-signal-safe Alloc/Free. - static Arena *NewArena(int32_t flags); + static Arena *NewArena(uint32_t flags); // Destroys an arena allocated by NewArena and returns true, // provided no allocated blocks remain in the arena. diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging.cc index 1a5a18f804..b480eca06d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging.cc @@ -72,7 +72,7 @@ namespace y_absl { Y_ABSL_NAMESPACE_BEGIN -namespace raw_logging_internal { +namespace raw_log_internal { namespace { // TODO(gfalcon): We want raw-logging to work on as many platforms as possible. @@ -89,12 +89,14 @@ constexpr char kTruncated[] = " ... (message truncated)\n"; bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) Y_ABSL_PRINTF_ATTRIBUTE(3, 0); bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) { - int n = vsnprintf(*buf, *size, format, ap); + if (*size < 0) + return false; + int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap); bool result = true; if (n < 0 || n > *size) { result = false; if (static_cast<size_t>(*size) > sizeof(kTruncated)) { - n = *size - sizeof(kTruncated); // room for truncation message + n = *size - static_cast<int>(sizeof(kTruncated)); } else { n = 0; // no room for truncation message } @@ -116,9 +118,11 @@ constexpr int kLogBufSize = 3000; bool DoRawLog(char** buf, int* size, const char* format, ...) Y_ABSL_PRINTF_ATTRIBUTE(3, 4); bool DoRawLog(char** buf, int* size, const char* format, ...) { + if (*size < 0) + return false; va_list ap; va_start(ap, format); - int n = vsnprintf(*buf, *size, format, ap); + int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap); va_end(ap); if (n < 0 || n > *size) return false; *size -= n; @@ -206,7 +210,7 @@ void AsyncSignalSafeWriteToStderr(const char* s, size_t len) { #elif defined(Y_ABSL_HAVE_POSIX_WRITE) write(STDERR_FILENO, s, len); #elif defined(Y_ABSL_HAVE_RAW_IO) - _write(/* stderr */ 2, s, len); + _write(/* stderr */ 2, s, static_cast<unsigned>(len)); #else // stderr logging unsupported on this platform (void) s; @@ -244,6 +248,6 @@ void RegisterInternalLogFunction(InternalLogFunction func) { internal_log_function.Store(func); } -} // namespace raw_logging_internal +} // namespace raw_log_internal Y_ABSL_NAMESPACE_END } // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging.h index 56cb5dc363..0a10d71630 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging.h @@ -43,12 +43,11 @@ #define Y_ABSL_RAW_LOG(severity, ...) \ do { \ - constexpr const char* absl_raw_logging_internal_basename = \ - ::y_absl::raw_logging_internal::Basename(__FILE__, \ - sizeof(__FILE__) - 1); \ - ::y_absl::raw_logging_internal::RawLog(Y_ABSL_RAW_LOGGING_INTERNAL_##severity, \ - absl_raw_logging_internal_basename, \ - __LINE__, __VA_ARGS__); \ + constexpr const char* absl_raw_log_internal_basename = \ + ::y_absl::raw_log_internal::Basename(__FILE__, sizeof(__FILE__) - 1); \ + ::y_absl::raw_log_internal::RawLog(Y_ABSL_RAW_LOG_INTERNAL_##severity, \ + absl_raw_log_internal_basename, __LINE__, \ + __VA_ARGS__); \ } while (0) // Similar to CHECK(condition) << message, but for low-level modules: @@ -72,14 +71,14 @@ // // The API is a subset of the above: each macro only takes two arguments. Use // StrCat if you need to build a richer message. -#define Y_ABSL_INTERNAL_LOG(severity, message) \ - do { \ - constexpr const char* absl_raw_logging_internal_filename = __FILE__; \ - ::y_absl::raw_logging_internal::internal_log_function( \ - Y_ABSL_RAW_LOGGING_INTERNAL_##severity, \ - absl_raw_logging_internal_filename, __LINE__, message); \ - if (Y_ABSL_RAW_LOGGING_INTERNAL_##severity == ::y_absl::LogSeverity::kFatal) \ - Y_ABSL_INTERNAL_UNREACHABLE; \ +#define Y_ABSL_INTERNAL_LOG(severity, message) \ + do { \ + constexpr const char* absl_raw_log_internal_filename = __FILE__; \ + ::y_absl::raw_log_internal::internal_log_function( \ + Y_ABSL_RAW_LOG_INTERNAL_##severity, absl_raw_log_internal_filename, \ + __LINE__, message); \ + if (Y_ABSL_RAW_LOG_INTERNAL_##severity == ::y_absl::LogSeverity::kFatal) \ + Y_ABSL_UNREACHABLE(); \ } while (0) #define Y_ABSL_INTERNAL_CHECK(condition, message) \ @@ -91,16 +90,16 @@ } \ } while (0) -#define Y_ABSL_RAW_LOGGING_INTERNAL_INFO ::y_absl::LogSeverity::kInfo -#define Y_ABSL_RAW_LOGGING_INTERNAL_WARNING ::y_absl::LogSeverity::kWarning -#define Y_ABSL_RAW_LOGGING_INTERNAL_ERROR ::y_absl::LogSeverity::kError -#define Y_ABSL_RAW_LOGGING_INTERNAL_FATAL ::y_absl::LogSeverity::kFatal -#define Y_ABSL_RAW_LOGGING_INTERNAL_LEVEL(severity) \ +#define Y_ABSL_RAW_LOG_INTERNAL_INFO ::y_absl::LogSeverity::kInfo +#define Y_ABSL_RAW_LOG_INTERNAL_WARNING ::y_absl::LogSeverity::kWarning +#define Y_ABSL_RAW_LOG_INTERNAL_ERROR ::y_absl::LogSeverity::kError +#define Y_ABSL_RAW_LOG_INTERNAL_FATAL ::y_absl::LogSeverity::kFatal +#define Y_ABSL_RAW_LOG_INTERNAL_LEVEL(severity) \ ::y_absl::NormalizeLogSeverity(severity) namespace y_absl { Y_ABSL_NAMESPACE_BEGIN -namespace raw_logging_internal { +namespace raw_log_internal { // Helper function to implement Y_ABSL_RAW_LOG // Logs format... at "severity" level, reporting it @@ -130,7 +129,7 @@ constexpr const char* Basename(const char* fname, int offset) { // TODO(gfalcon): Come up with a better name for this method. bool RawLoggingFullySupported(); -// Function type for a raw_logging customization hook for suppressing messages +// Function type for a raw_log customization hook for suppressing messages // by severity, and for writing custom prefixes on non-suppressed messages. // // The installed hook is called for every raw log invocation. The message will @@ -139,7 +138,7 @@ bool RawLoggingFullySupported(); // also provided with an output buffer, where it can write a custom log message // prefix. // -// The raw_logging system does not allocate memory or grab locks. User-provided +// The raw_log system does not allocate memory or grab locks. User-provided // hooks must avoid these operations, and must not throw exceptions. // // 'severity' is the severity level of the message being written. @@ -152,7 +151,7 @@ using LogFilterAndPrefixHook = bool (*)(y_absl::LogSeverity severity, const char* file, int line, char** buf, int* buf_size); -// Function type for a raw_logging customization hook called to abort a process +// Function type for a raw_log customization hook called to abort a process // when a FATAL message is logged. If the provided AbortHook() returns, the // logging system will call abort(). // @@ -189,7 +188,7 @@ void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func); void RegisterAbortHook(AbortHook func); void RegisterInternalLogFunction(InternalLogFunction func); -} // namespace raw_logging_internal +} // namespace raw_log_internal Y_ABSL_NAMESPACE_END } // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock.cc index aaa8b3c156..1729212fbc 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock.cc @@ -178,7 +178,7 @@ void SpinLock::SlowUnlock(uint32_t lock_value) { // reserve a unitary wait time to represent that a waiter exists without our // own acquisition having been contended. if ((lock_value & kWaitTimeMask) != kSpinLockSleeper) { - const uint64_t wait_cycles = DecodeWaitCycles(lock_value); + const int64_t wait_cycles = DecodeWaitCycles(lock_value); Y_ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); submit_profile_data(this, wait_cycles); Y_ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); @@ -220,9 +220,9 @@ uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time, return clamped; } -uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { +int64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { // Cast to uint32_t first to ensure bits [63:32] are cleared. - const uint64_t scaled_wait_time = + const int64_t scaled_wait_time = static_cast<uint32_t>(lock_value & kWaitTimeMask); return scaled_wait_time << (kProfileTimestampShift - kLockwordReservedShift); } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock.h index 67bbbabdca..f77fbfb814 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock.h @@ -29,10 +29,8 @@ #ifndef Y_ABSL_BASE_INTERNAL_SPINLOCK_H_ #define Y_ABSL_BASE_INTERNAL_SPINLOCK_H_ -#include <stdint.h> -#include <sys/types.h> - #include <atomic> +#include <cstdint> #include "y_absl/base/attributes.h" #include "y_absl/base/const_init.h" @@ -41,8 +39,6 @@ #include "y_absl/base/internal/raw_logging.h" #include "y_absl/base/internal/scheduling_mode.h" #include "y_absl/base/internal/tsan_mutex_interface.h" -#include "y_absl/base/macros.h" -#include "y_absl/base/port.h" #include "y_absl/base/thread_annotations.h" namespace y_absl { @@ -137,7 +133,7 @@ class Y_ABSL_LOCKABLE SpinLock { int64_t wait_end_time); // Extract number of wait cycles in a lock value. - static uint64_t DecodeWaitCycles(uint32_t lock_value); + static int64_t DecodeWaitCycles(uint32_t lock_value); // Provide access to protected method above. Use for testing only. friend struct SpinLockTest; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_win32.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_win32.inc index c80dfdf05c..87ce99fbf5 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_win32.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_win32.inc @@ -27,7 +27,10 @@ void Y_ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( } else if (loop == 1) { Sleep(0); } else { - Sleep(y_absl::base_internal::SpinLockSuggestedDelayNS(loop) / 1000000); + // SpinLockSuggestedDelayNS() always returns a positive integer, so this + // static_cast is safe. + Sleep(static_cast<DWORD>( + y_absl::base_internal::SpinLockSuggestedDelayNS(loop) / 1000000)); } } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/strerror.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/strerror.cc index ae1e09f791..6a4a98ba83 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/strerror.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/strerror.cc @@ -66,8 +66,8 @@ constexpr int kSysNerr = 135; std::array<TString, kSysNerr>* NewStrErrorTable() { auto* table = new std::array<TString, kSysNerr>; - for (int i = 0; i < static_cast<int>(table->size()); ++i) { - (*table)[i] = StrErrorInternal(i); + for (size_t i = 0; i < table->size(); ++i) { + (*table)[i] = StrErrorInternal(static_cast<int>(i)); } return table; } @@ -77,8 +77,8 @@ std::array<TString, kSysNerr>* NewStrErrorTable() { TString StrError(int errnum) { y_absl::base_internal::ErrnoSaver errno_saver; static const auto* table = NewStrErrorTable(); - if (errnum >= 0 && errnum < static_cast<int>(table->size())) { - return (*table)[errnum]; + if (errnum >= 0 && static_cast<size_t>(errnum) < table->size()) { + return (*table)[static_cast<size_t>(errnum)]; } return StrErrorInternal(errnum); } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/sysinfo.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/sysinfo.cc index 25b6a005bb..0172cb3311 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/sysinfo.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/sysinfo.cc @@ -117,7 +117,7 @@ int Win32NumCPUs() { } } free(info); - return logicalProcessorCount; + return static_cast<int>(logicalProcessorCount); } #endif @@ -128,7 +128,7 @@ static int GetNumCPUs() { #if defined(__myriad2__) return 1; #elif defined(_WIN32) - const unsigned hardware_concurrency = Win32NumCPUs(); + const int hardware_concurrency = Win32NumCPUs(); return hardware_concurrency ? hardware_concurrency : 1; #elif defined(_AIX) return sysconf(_SC_NPROCESSORS_ONLN); @@ -136,7 +136,7 @@ static int GetNumCPUs() { // Other possibilities: // - Read /sys/devices/system/cpu/online and use cpumask_parse() // - sysconf(_SC_NPROCESSORS_ONLN) - return std::thread::hardware_concurrency(); + return static_cast<int>(std::thread::hardware_concurrency()); #endif } @@ -189,12 +189,15 @@ static double GetNominalCPUFrequency() { // and the memory location pointed to by value is set to the value read. static bool ReadLongFromFile(const char *file, long *value) { bool ret = false; - int fd = open(file, O_RDONLY); + int fd = open(file, O_RDONLY | O_CLOEXEC); if (fd != -1) { char line[1024]; char *err; memset(line, '\0', sizeof(line)); - int len = read(fd, line, sizeof(line) - 1); + ssize_t len; + do { + len = read(fd, line, sizeof(line) - 1); + } while (len < 0 && errno == EINTR); if (len <= 0) { ret = false; } else { @@ -376,7 +379,7 @@ pid_t GetTID() { #endif pid_t GetTID() { - return syscall(SYS_gettid); + return static_cast<pid_t>(syscall(SYS_gettid)); } #elif defined(__akaros__) @@ -429,11 +432,11 @@ static constexpr int kBitsPerWord = 32; // tid_array is uint32_t. // Returns the TID to tid_array. static void FreeTID(void *v) { intptr_t tid = reinterpret_cast<intptr_t>(v); - int word = tid / kBitsPerWord; + intptr_t word = tid / kBitsPerWord; uint32_t mask = ~(1u << (tid % kBitsPerWord)); y_absl::base_internal::SpinLockHolder lock(&tid_lock); assert(0 <= word && static_cast<size_t>(word) < tid_array->size()); - (*tid_array)[word] &= mask; + (*tid_array)[static_cast<size_t>(word)] &= mask; } static void InitGetTID() { @@ -455,7 +458,7 @@ pid_t GetTID() { intptr_t tid = reinterpret_cast<intptr_t>(pthread_getspecific(tid_key)); if (tid != 0) { - return tid; + return static_cast<pid_t>(tid); } int bit; // tid_array[word] = 1u << bit; @@ -476,7 +479,8 @@ pid_t GetTID() { while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) { ++bit; } - tid = (word * kBitsPerWord) + bit; + tid = + static_cast<intptr_t>((word * kBitsPerWord) + static_cast<size_t>(bit)); (*tid_array)[word] |= 1u << bit; // Mark the TID as allocated. } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/thread_annotations.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/thread_annotations.h index 248affd621..380e2924fd 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/thread_annotations.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/thread_annotations.h @@ -38,6 +38,13 @@ #ifndef Y_ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ #define Y_ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ +// Y_ABSL_LEGACY_THREAD_ANNOTATIONS is a *temporary* compatibility macro that can +// be defined on the compile command-line to restore the legacy spellings of the +// thread annotations macros/functions. The macros in this file are available +// under Y_ABSL_ prefixed spellings in y_absl/base/thread_annotations.h. This macro +// and the legacy spellings will be removed in the future. +#ifdef Y_ABSL_LEGACY_THREAD_ANNOTATIONS + #if defined(__clang__) #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) #else @@ -268,4 +275,6 @@ inline T& y_ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS { } // namespace thread_safety_analysis +#endif // defined(Y_ABSL_LEGACY_THREAD_ANNOTATIONS) + #endif // Y_ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/thread_identity.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/thread_identity.h index 5556ba40b5..617a220631 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/thread_identity.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/thread_identity.h @@ -134,6 +134,10 @@ struct PerThreadSynch { // The instances of this class are allocated in NewThreadIdentity() with an // alignment of PerThreadSynch::kAlignment. +// +// NOTE: The layout of fields in this structure is critical, please do not +// add, remove, or modify the field placements without fully auditing the +// layout. struct ThreadIdentity { // Must be the first member. The Mutex implementation requires that // the PerThreadSynch object associated with each thread is diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/unscaledcycleclock.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/unscaledcycleclock.h index 15ee5e4434..bd64094420 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/unscaledcycleclock.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/unscaledcycleclock.h @@ -42,48 +42,11 @@ #include <TargetConditionals.h> #endif -#include "y_absl/base/port.h" - -// The following platforms have an implementation of a hardware counter. -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ - defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \ - defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) -#define Y_ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 -#else -#define Y_ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 -#endif - -// The following platforms often disable access to the hardware -// counter (through a sandbox) even if the underlying hardware has a -// usable counter. The CycleTimer interface also requires a *scaled* -// CycleClock that runs at atleast 1 MHz. We've found some Android -// ARM64 devices where this is not the case, so we disable it by -// default on Android ARM64. -#if defined(__native_client__) || (defined(__APPLE__)) || \ - (defined(__ANDROID__) && defined(__aarch64__)) -#define Y_ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 -#else -#define Y_ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 1 -#endif - -// UnscaledCycleClock is an optional internal feature. -// Use "#if Y_ABSL_USE_UNSCALED_CYCLECLOCK" to test for its presence. -// Can be overridden at compile-time via -DABSL_USE_UNSCALED_CYCLECLOCK=0|1 -#if !defined(Y_ABSL_USE_UNSCALED_CYCLECLOCK) -#define Y_ABSL_USE_UNSCALED_CYCLECLOCK \ - (Y_ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION && \ - Y_ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT) -#endif +#include "y_absl/base/config.h" +#include "y_absl/base/internal/unscaledcycleclock_config.h" #if Y_ABSL_USE_UNSCALED_CYCLECLOCK -// This macro can be used to test if UnscaledCycleClock::Frequency() -// is NominalCPUFrequency() on a particular platform. -#if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || \ - defined(_M_IX86) || defined(_M_X64)) -#define Y_ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY -#endif - namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace time_internal { @@ -119,7 +82,7 @@ class UnscaledCycleClock { inline int64_t UnscaledCycleClock::Now() { uint64_t low, high; __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); - return (high << 32) | low; + return static_cast<int64_t>((high << 32) | low); } #endif diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/unscaledcycleclock_config.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/unscaledcycleclock_config.h new file mode 100644 index 0000000000..5ea8eac335 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/unscaledcycleclock_config.h @@ -0,0 +1,62 @@ +// Copyright 2022 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 Y_ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_CONFIG_H_ +#define Y_ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_CONFIG_H_ + +#if defined(__APPLE__) +#include <TargetConditionals.h> +#endif + +// The following platforms have an implementation of a hardware counter. +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \ + defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) +#define Y_ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 +#else +#define Y_ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 +#endif + +// The following platforms often disable access to the hardware +// counter (through a sandbox) even if the underlying hardware has a +// usable counter. The CycleTimer interface also requires a *scaled* +// CycleClock that runs at atleast 1 MHz. We've found some Android +// ARM64 devices where this is not the case, so we disable it by +// default on Android ARM64. +#if defined(__native_client__) || (defined(__APPLE__)) || \ + (defined(__ANDROID__) && defined(__aarch64__)) +#define Y_ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 +#else +#define Y_ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 1 +#endif + +// UnscaledCycleClock is an optional internal feature. +// Use "#if Y_ABSL_USE_UNSCALED_CYCLECLOCK" to test for its presence. +// Can be overridden at compile-time via -DABSL_USE_UNSCALED_CYCLECLOCK=0|1 +#if !defined(Y_ABSL_USE_UNSCALED_CYCLECLOCK) +#define Y_ABSL_USE_UNSCALED_CYCLECLOCK \ + (Y_ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION && \ + Y_ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT) +#endif + +#if Y_ABSL_USE_UNSCALED_CYCLECLOCK +// This macro can be used to test if UnscaledCycleClock::Frequency() +// is NominalCPUFrequency() on a particular platform. +#if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || \ + defined(_M_IX86) || defined(_M_X64)) +#define Y_ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY +#endif +#endif + +#endif // Y_ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_CONFIG_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/macros.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/macros.h index 1caf95d46e..e343701084 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/macros.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/macros.h @@ -103,17 +103,11 @@ Y_ABSL_NAMESPACE_END // aborts the program in release mode (when NDEBUG is defined). The // implementation should abort the program as quickly as possible and ideally it // should not be possible to ignore the abort request. -#if (Y_ABSL_HAVE_BUILTIN(__builtin_trap) && \ - Y_ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \ - (defined(__GNUC__) && !defined(__clang__)) -#define Y_ABSL_INTERNAL_HARDENING_ABORT() \ - do { \ - __builtin_trap(); \ - __builtin_unreachable(); \ +#define Y_ABSL_INTERNAL_HARDENING_ABORT() \ + do { \ + Y_ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \ + Y_ABSL_INTERNAL_UNREACHABLE_IMPL(); \ } while (false) -#else -#define Y_ABSL_INTERNAL_HARDENING_ABORT() abort() -#endif // Y_ABSL_HARDENING_ASSERT() // @@ -144,15 +138,4 @@ Y_ABSL_NAMESPACE_END #define Y_ABSL_INTERNAL_RETHROW do {} while (false) #endif // Y_ABSL_HAVE_EXCEPTIONS -// `Y_ABSL_INTERNAL_UNREACHABLE` is an unreachable statement. A program which -// reaches one has undefined behavior, and the compiler may optimize -// accordingly. -#if defined(__GNUC__) || Y_ABSL_HAVE_BUILTIN(__builtin_unreachable) -#define Y_ABSL_INTERNAL_UNREACHABLE __builtin_unreachable() -#elif defined(_MSC_VER) -#define Y_ABSL_INTERNAL_UNREACHABLE __assume(0) -#else -#define Y_ABSL_INTERNAL_UNREACHABLE -#endif - #endif // Y_ABSL_BASE_MACROS_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/optimization.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/optimization.h index 024b5c163c..bffc0dcd5b 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/optimization.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/optimization.h @@ -91,6 +91,7 @@ #define Y_ABSL_CACHELINE_SIZE 64 #endif #endif +#endif #ifndef Y_ABSL_CACHELINE_SIZE // A reasonable default guess. Note that overestimates tend to waste more @@ -141,12 +142,11 @@ // the generated machine code. // 3) Prefer applying this attribute to individual variables. Avoid // applying it to types. This tends to localize the effect. +#if defined(__clang__) || defined(__GNUC__) #define Y_ABSL_CACHELINE_ALIGNED __attribute__((aligned(Y_ABSL_CACHELINE_SIZE))) #elif defined(_MSC_VER) -#define Y_ABSL_CACHELINE_SIZE 64 #define Y_ABSL_CACHELINE_ALIGNED __declspec(align(Y_ABSL_CACHELINE_SIZE)) #else -#define Y_ABSL_CACHELINE_SIZE 64 #define Y_ABSL_CACHELINE_ALIGNED #endif @@ -181,6 +181,53 @@ #define Y_ABSL_PREDICT_TRUE(x) (x) #endif +// `Y_ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL()` aborts the program in the fastest +// possible way, with no attempt at logging. One use is to implement hardening +// aborts with Y_ABSL_OPTION_HARDENED. Since this is an internal symbol, it +// should not be used directly outside of Abseil. +#if Y_ABSL_HAVE_BUILTIN(__builtin_trap) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define Y_ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL() __builtin_trap() +#else +#define Y_ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL() abort() +#endif + +// `Y_ABSL_INTERNAL_UNREACHABLE_IMPL()` is the platform specific directive to +// indicate that a statement is unreachable, and to allow the compiler to +// optimize accordingly. Clients should use `Y_ABSL_UNREACHABLE()`, which is +// defined below. +#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L +#define Y_ABSL_INTERNAL_UNREACHABLE_IMPL() std::unreachable() +#elif defined(__GNUC__) || Y_ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define Y_ABSL_INTERNAL_UNREACHABLE_IMPL() __builtin_unreachable() +#elif Y_ABSL_HAVE_BUILTIN(__builtin_assume) +#define Y_ABSL_INTERNAL_UNREACHABLE_IMPL() __builtin_assume(false) +#elif defined(_MSC_VER) +#define Y_ABSL_INTERNAL_UNREACHABLE_IMPL() __assume(false) +#else +#define Y_ABSL_INTERNAL_UNREACHABLE_IMPL() +#endif + +// `Y_ABSL_UNREACHABLE()` is an unreachable statement. A program which reaches +// one has undefined behavior, and the compiler may optimize accordingly. +#if Y_ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) +// Abort in hardened mode to avoid dangerous undefined behavior. +#define Y_ABSL_UNREACHABLE() \ + do { \ + Y_ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \ + Y_ABSL_INTERNAL_UNREACHABLE_IMPL(); \ + } while (false) +#else +// The assert only fires in debug mode to aid in debugging. +// When NDEBUG is defined, reaching Y_ABSL_UNREACHABLE() is undefined behavior. +#define Y_ABSL_UNREACHABLE() \ + do { \ + /* NOLINTNEXTLINE: misc-static-assert */ \ + assert(false && "Y_ABSL_UNREACHABLE reached"); \ + Y_ABSL_INTERNAL_UNREACHABLE_IMPL(); \ + } while (false) +#endif + // Y_ABSL_ASSUME(cond) // // Informs the compiler that a condition is always true and that it can assume @@ -209,18 +256,23 @@ #define Y_ABSL_ASSUME(cond) assert(cond) #elif Y_ABSL_HAVE_BUILTIN(__builtin_assume) #define Y_ABSL_ASSUME(cond) __builtin_assume(cond) +#elif defined(_MSC_VER) +#define Y_ABSL_ASSUME(cond) __assume(cond) +#elif defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L +#define Y_ABSL_ASSUME(cond) \ + do { \ + if (!(cond)) std::unreachable(); \ + } while (false) #elif defined(__GNUC__) || Y_ABSL_HAVE_BUILTIN(__builtin_unreachable) #define Y_ABSL_ASSUME(cond) \ do { \ if (!(cond)) __builtin_unreachable(); \ - } while (0) -#elif defined(_MSC_VER) -#define Y_ABSL_ASSUME(cond) __assume(cond) + } while (false) #else #define Y_ABSL_ASSUME(cond) \ do { \ static_cast<void>(false && (cond)); \ - } while (0) + } while (false) #endif // Y_ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/options.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/options.h index 40601c2bee..98a42d1c67 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/options.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/options.h @@ -67,12 +67,6 @@ #ifndef Y_ABSL_BASE_OPTIONS_H_ #define Y_ABSL_BASE_OPTIONS_H_ -// Include a standard library header to allow configuration based on the -// standard library in use. -#ifdef __cplusplus -#include <ciso646> -#endif - // ----------------------------------------------------------------------------- // Type Compatibility Options // ----------------------------------------------------------------------------- @@ -206,7 +200,7 @@ // allowed. #define Y_ABSL_OPTION_USE_INLINE_NAMESPACE 1 -#define Y_ABSL_OPTION_INLINE_NAMESPACE_NAME lts_y_20220623 +#define Y_ABSL_OPTION_INLINE_NAMESPACE_NAME lts_y_20230125 // Y_ABSL_OPTION_HARDENED // diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/policy_checks.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/policy_checks.h index 77b7dca964..04f00a81bf 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/base/policy_checks.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/policy_checks.h @@ -44,17 +44,17 @@ // Toolchain Check // ----------------------------------------------------------------------------- -// We support MSVC++ 14.0 update 2 and later. +// We support Visual Studio 2017 (MSVC++ 15.0) and later. // This minimum will go up. -#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__) -#error "This package requires Visual Studio 2015 Update 2 or higher." +#if defined(_MSC_VER) && _MSC_VER < 1910 && !defined(__clang__) +#error "This package requires Visual Studio 2017 (MSVC++ 15.0) or higher." #endif -// We support gcc 4.7 and later. +// We support GCC 7 and later. // This minimum will go up. #if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) -#error "This package requires gcc 4.7 or higher." +#if __GNUC__ < 7 +#error "This package requires GCC 7 or higher." #endif #endif @@ -69,13 +69,15 @@ // C++ Version Check // ----------------------------------------------------------------------------- -// Enforce C++11 as the minimum. Note that Visual Studio has not -// advanced __cplusplus despite being good enough for our purposes, so -// so we exempt it from the check. -#if defined(__cplusplus) && !defined(_MSC_VER) -#if __cplusplus < 201103L -#error "C++ versions less than C++11 are not supported." -#endif +// Enforce C++14 as the minimum. +#if defined(_MSVC_LANG) +#if _MSVC_LANG < 201402L +#error "C++ versions less than C++14 are not supported." +#endif // _MSVC_LANG < 201402L +#elif defined(__cplusplus) +#if __cplusplus < 201402L +#error "C++ versions less than C++14 are not supported." +#endif // __cplusplus < 201402L #endif // ----------------------------------------------------------------------------- diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/fixed_array.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/fixed_array.h index 94c69145fa..83c817448b 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/fixed_array.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/fixed_array.h @@ -62,11 +62,10 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1); // A `FixedArray` provides a run-time fixed-size array, allocating a small array // inline for efficiency. // -// Most users should not specify an `inline_elements` argument and let -// `FixedArray` automatically determine the number of elements -// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the -// `FixedArray` implementation will use inline storage for arrays with a -// length <= `inline_elements`. +// Most users should not specify the `N` template parameter and let `FixedArray` +// automatically determine the number of elements to store inline based on +// `sizeof(T)`. If `N` is specified, the `FixedArray` implementation will use +// inline storage for arrays with a length <= `N`. // // Note that a `FixedArray` constructed with a `size_type` argument will // default-initialize its values by leaving trivially constructible types @@ -471,6 +470,9 @@ class FixedArray { return n <= inline_elements; } +#ifdef Y_ABSL_HAVE_ADDRESS_SANITIZER + Y_ABSL_ATTRIBUTE_NOINLINE +#endif // Y_ABSL_HAVE_ADDRESS_SANITIZER StorageElement* InitializeData() { if (UsingInlinedStorage(size())) { InlinedStorage::AnnotateConstruct(size()); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/inlined_vector.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/inlined_vector.h index fd5d07019a..aaec71987e 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/inlined_vector.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/inlined_vector.h @@ -52,6 +52,7 @@ #include "y_absl/base/port.h" #include "y_absl/container/internal/inlined_vector.h" #include "y_absl/memory/memory.h" +#include "y_absl/meta/type_traits.h" namespace y_absl { Y_ABSL_NAMESPACE_BEGIN @@ -77,6 +78,8 @@ class InlinedVector { using MoveIterator = inlined_vector_internal::MoveIterator<TheA>; template <typename TheA> using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<TheA>; + template <typename TheA> + using IsMoveAssignOk = inlined_vector_internal::IsMoveAssignOk<TheA>; template <typename TheA, typename Iterator> using IteratorValueAdapter = @@ -94,6 +97,12 @@ class InlinedVector { using DisableIfAtLeastForwardIterator = y_absl::enable_if_t< !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>; + using MemcpyPolicy = typename Storage::MemcpyPolicy; + using ElementwiseAssignPolicy = typename Storage::ElementwiseAssignPolicy; + using ElementwiseConstructPolicy = + typename Storage::ElementwiseConstructPolicy; + using MoveAssignmentPolicy = typename Storage::MoveAssignmentPolicy; + public: using allocator_type = A; using value_type = inlined_vector_internal::ValueType<A>; @@ -275,8 +284,10 @@ class InlinedVector { size_type max_size() const noexcept { // One bit of the size storage is used to indicate whether the inlined // vector contains allocated memory. As a result, the maximum size that the - // inlined vector can express is half of the max for `size_type`. - return (std::numeric_limits<size_type>::max)() / 2; + // inlined vector can express is the minimum of the limit of how many + // objects we can allocate and std::numeric_limits<size_type>::max() / 2. + return (std::min)(AllocatorTraits<A>::max_size(storage_.GetAllocator()), + (std::numeric_limits<size_type>::max)() / 2); } // `InlinedVector::capacity()` @@ -484,18 +495,7 @@ class InlinedVector { // unspecified state. InlinedVector& operator=(InlinedVector&& other) { if (Y_ABSL_PREDICT_TRUE(this != std::addressof(other))) { - if (IsMemcpyOk<A>::value || other.storage_.GetIsAllocated()) { - inlined_vector_internal::DestroyAdapter<A>::DestroyElements( - storage_.GetAllocator(), data(), size()); - storage_.DeallocateIfAllocated(); - storage_.MemcpyFrom(other.storage_); - - other.storage_.SetInlinedSize(0); - } else { - storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>( - MoveIterator<A>(other.storage_.GetInlinedData())), - other.size()); - } + MoveAssignment(MoveAssignmentPolicy{}, std::move(other)); } return *this; @@ -624,9 +624,9 @@ class InlinedVector { Y_ABSL_HARDENING_ASSERT(pos <= end()); if (Y_ABSL_PREDICT_TRUE(first != last)) { - return storage_.Insert(pos, - IteratorValueAdapter<A, ForwardIterator>(first), - std::distance(first, last)); + return storage_.Insert( + pos, IteratorValueAdapter<A, ForwardIterator>(first), + static_cast<size_type>(std::distance(first, last))); } else { return const_cast<iterator>(pos); } @@ -643,7 +643,7 @@ class InlinedVector { Y_ABSL_HARDENING_ASSERT(pos >= begin()); Y_ABSL_HARDENING_ASSERT(pos <= end()); - size_type index = std::distance(cbegin(), pos); + size_type index = static_cast<size_type>(std::distance(cbegin(), pos)); for (size_type i = index; first != last; ++i, static_cast<void>(++first)) { insert(data() + i, *first); } @@ -661,10 +661,22 @@ class InlinedVector { Y_ABSL_HARDENING_ASSERT(pos <= end()); value_type dealias(std::forward<Args>(args)...); + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 + // It appears that GCC thinks that since `pos` is a const pointer and may + // point to uninitialized memory at this point, a warning should be + // issued. But `pos` is actually only used to compute an array index to + // write to. +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif return storage_.Insert(pos, IteratorValueAdapter<A, MoveIterator<A>>( MoveIterator<A>(std::addressof(dealias))), 1); +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif } // `InlinedVector::emplace_back(...)` @@ -771,6 +783,42 @@ class InlinedVector { template <typename H, typename TheT, size_t TheN, typename TheA> friend H AbslHashValue(H h, const y_absl::InlinedVector<TheT, TheN, TheA>& a); + void MoveAssignment(MemcpyPolicy, InlinedVector&& other) { + inlined_vector_internal::DestroyAdapter<A>::DestroyElements( + storage_.GetAllocator(), data(), size()); + storage_.DeallocateIfAllocated(); + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } + + void MoveAssignment(ElementwiseAssignPolicy, InlinedVector&& other) { + if (other.storage_.GetIsAllocated()) { + MoveAssignment(MemcpyPolicy{}, std::move(other)); + } else { + storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>( + MoveIterator<A>(other.storage_.GetInlinedData())), + other.size()); + } + } + + void MoveAssignment(ElementwiseConstructPolicy, InlinedVector&& other) { + if (other.storage_.GetIsAllocated()) { + MoveAssignment(MemcpyPolicy{}, std::move(other)); + } else { + inlined_vector_internal::DestroyAdapter<A>::DestroyElements( + storage_.GetAllocator(), data(), size()); + storage_.DeallocateIfAllocated(); + + IteratorValueAdapter<A, MoveIterator<A>> other_values( + MoveIterator<A>(other.storage_.GetInlinedData())); + inlined_vector_internal::ConstructElements<A>( + storage_.GetAllocator(), storage_.GetInlinedData(), other_values, + other.storage_.GetSize()); + storage_.SetInlinedSize(other.storage_.GetSize()); + } + } + Storage storage_; }; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/common.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/common.h index c32dbd8179..4fc2675f4f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/common.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/common.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef Y_ABSL_CONTAINER_INTERNAL_CONTAINER_H_ -#define Y_ABSL_CONTAINER_INTERNAL_CONTAINER_H_ +#ifndef Y_ABSL_CONTAINER_INTERNAL_COMMON_H_ +#define Y_ABSL_CONTAINER_INTERNAL_COMMON_H_ #include <cassert> #include <type_traits> @@ -204,4 +204,4 @@ struct InsertReturnType { Y_ABSL_NAMESPACE_END } // namespace y_absl -#endif // Y_ABSL_CONTAINER_INTERNAL_CONTAINER_H_ +#endif // Y_ABSL_CONTAINER_INTERNAL_COMMON_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/common_policy_traits.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/common_policy_traits.h new file mode 100644 index 0000000000..927f6dac2d --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/common_policy_traits.h @@ -0,0 +1,132 @@ +// Copyright 2022 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 Y_ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_ +#define Y_ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_ + +#include <cstddef> +#include <cstring> +#include <memory> +#include <new> +#include <type_traits> +#include <utility> + +#include "y_absl/meta/type_traits.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Defines how slots are initialized/destroyed/moved. +template <class Policy, class = void> +struct common_policy_traits { + // The actual object stored in the container. + using slot_type = typename Policy::slot_type; + using reference = decltype(Policy::element(std::declval<slot_type*>())); + using value_type = typename std::remove_reference<reference>::type; + + // PRECONDITION: `slot` is UNINITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + template <class Alloc, class... Args> + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + Policy::construct(alloc, slot, std::forward<Args>(args)...); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is UNINITIALIZED + template <class Alloc> + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::destroy(alloc, slot); + } + + // Transfers the `old_slot` to `new_slot`. Any memory allocated by the + // allocator inside `old_slot` to `new_slot` can be transferred. + // + // OPTIONAL: defaults to: + // + // clone(new_slot, std::move(*old_slot)); + // destroy(old_slot); + // + // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED + // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is + // UNINITIALIZED + template <class Alloc> + static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) { + transfer_impl(alloc, new_slot, old_slot, Rank0{}); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + // Note: we use remove_const_t so that the two overloads have different args + // in the case of sets with explicitly const value_types. + template <class P = Policy> + static auto element(y_absl::remove_const_t<slot_type>* slot) + -> decltype(P::element(slot)) { + return P::element(slot); + } + template <class P = Policy> + static auto element(const slot_type* slot) -> decltype(P::element(slot)) { + return P::element(slot); + } + + static constexpr bool transfer_uses_memcpy() { + return std::is_same<decltype(transfer_impl<std::allocator<char>>( + nullptr, nullptr, nullptr, Rank0{})), + std::true_type>::value; + } + + private: + // To rank the overloads below for overload resoltion. Rank0 is preferred. + struct Rank2 {}; + struct Rank1 : Rank2 {}; + struct Rank0 : Rank1 {}; + + // Use auto -> decltype as an enabler. + template <class Alloc, class P = Policy> + static auto transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, Rank0) + -> decltype((void)P::transfer(alloc, new_slot, old_slot)) { + P::transfer(alloc, new_slot, old_slot); + } +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + // This overload returns true_type for the trait below. + // The conditional_t is to make the enabler type dependent. + template <class Alloc, + typename = std::enable_if_t<y_absl::is_trivially_relocatable< + std::conditional_t<false, Alloc, value_type>>::value>> + static std::true_type transfer_impl(Alloc*, slot_type* new_slot, + slot_type* old_slot, Rank1) { + // TODO(b/247130232): remove casts after fixing warnings. + // TODO(b/251814870): remove casts after fixing warnings. + std::memcpy( + static_cast<void*>(std::launder( + const_cast<std::remove_const_t<value_type>*>(&element(new_slot)))), + static_cast<const void*>(&element(old_slot)), sizeof(value_type)); + return {}; + } +#endif + + template <class Alloc> + static void transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, Rank2) { + construct(alloc, new_slot, std::move(element(old_slot))); + destroy(alloc, old_slot); + } +}; + +} // namespace container_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/container_memory.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/container_memory.h index d40cf7531c..9a44a3933e 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/container_memory.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/container_memory.h @@ -17,6 +17,7 @@ #include <cassert> #include <cstddef> +#include <cstring> #include <memory> #include <new> #include <tuple> @@ -340,7 +341,8 @@ template <class K, class V> struct map_slot_policy { using slot_type = map_slot_type<K, V>; using value_type = std::pair<const K, V>; - using mutable_value_type = std::pair<K, V>; + using mutable_value_type = + std::pair<y_absl::remove_const_t<K>, y_absl::remove_const_t<V>>; private: static void emplace(slot_type* slot) { @@ -424,6 +426,16 @@ struct map_slot_policy { static void transfer(Allocator* alloc, slot_type* new_slot, slot_type* old_slot) { emplace(new_slot); +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + if (y_absl::is_trivially_relocatable<value_type>()) { + // TODO(b/247130232,b/251814870): remove casts after fixing warnings. + std::memcpy(static_cast<void*>(std::launder(&new_slot->value)), + static_cast<const void*>(&old_slot->value), + sizeof(value_type)); + return; + } +#endif + if (kMutableKeys::value) { y_absl::allocator_traits<Allocator>::construct( *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hash_policy_traits.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hash_policy_traits.h index 1ac62b0a3e..cf60b63e5f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hash_policy_traits.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hash_policy_traits.h @@ -21,6 +21,7 @@ #include <type_traits> #include <utility> +#include "y_absl/container/internal/common_policy_traits.h" #include "y_absl/meta/type_traits.h" namespace y_absl { @@ -29,7 +30,7 @@ namespace container_internal { // Defines how slots are initialized/destroyed/moved. template <class Policy, class = void> -struct hash_policy_traits { +struct hash_policy_traits : common_policy_traits<Policy> { // The type of the keys stored in the hashtable. using key_type = typename Policy::key_type; @@ -87,43 +88,6 @@ struct hash_policy_traits { // Defaults to false if not provided by the policy. using constant_iterators = ConstantIteratorsImpl<>; - // PRECONDITION: `slot` is UNINITIALIZED - // POSTCONDITION: `slot` is INITIALIZED - template <class Alloc, class... Args> - static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { - Policy::construct(alloc, slot, std::forward<Args>(args)...); - } - - // PRECONDITION: `slot` is INITIALIZED - // POSTCONDITION: `slot` is UNINITIALIZED - template <class Alloc> - static void destroy(Alloc* alloc, slot_type* slot) { - Policy::destroy(alloc, slot); - } - - // Transfers the `old_slot` to `new_slot`. Any memory allocated by the - // allocator inside `old_slot` to `new_slot` can be transferred. - // - // OPTIONAL: defaults to: - // - // clone(new_slot, std::move(*old_slot)); - // destroy(old_slot); - // - // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED - // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is - // UNINITIALIZED - template <class Alloc> - static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) { - transfer_impl(alloc, new_slot, old_slot, 0); - } - - // PRECONDITION: `slot` is INITIALIZED - // POSTCONDITION: `slot` is INITIALIZED - template <class P = Policy> - static auto element(slot_type* slot) -> decltype(P::element(slot)) { - return P::element(slot); - } - // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`. // // If `slot` is nullptr, returns the constant amount of memory owned by any @@ -174,8 +138,8 @@ struct hash_policy_traits { // Used for node handle manipulation. template <class P = Policy> static auto mutable_key(slot_type* slot) - -> decltype(P::apply(ReturnKey(), element(slot))) { - return P::apply(ReturnKey(), element(slot)); + -> decltype(P::apply(ReturnKey(), hash_policy_traits::element(slot))) { + return P::apply(ReturnKey(), hash_policy_traits::element(slot)); } // Returns the "value" (as opposed to the "key") portion of the element. Used @@ -184,21 +148,6 @@ struct hash_policy_traits { static auto value(T* elem) -> decltype(P::value(elem)) { return P::value(elem); } - - private: - // Use auto -> decltype as an enabler. - template <class Alloc, class P = Policy> - static auto transfer_impl(Alloc* alloc, slot_type* new_slot, - slot_type* old_slot, int) - -> decltype((void)P::transfer(alloc, new_slot, old_slot)) { - P::transfer(alloc, new_slot, old_slot); - } - template <class Alloc> - static void transfer_impl(Alloc* alloc, slot_type* new_slot, - slot_type* old_slot, char) { - construct(alloc, new_slot, std::move(element(old_slot))); - destroy(alloc, old_slot); - } }; } // namespace container_internal diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hashtablez_sampler.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hashtablez_sampler.cc index 36816872fd..d58a22a0b2 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hashtablez_sampler.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hashtablez_sampler.cc @@ -14,6 +14,7 @@ #include "y_absl/container/internal/hashtablez_sampler.h" +#include <algorithm> #include <atomic> #include <cassert> #include <cmath> @@ -158,6 +159,43 @@ void UnsampleSlow(HashtablezInfo* info) { GlobalHashtablezSampler().Unregister(info); } +void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { +#ifdef Y_ABSL_INTERNAL_HAVE_SSE2 + total_probe_length /= 16; +#else + total_probe_length /= 8; +#endif + info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); + // There is only one concurrent writer, so `load` then `store` is sufficient + // instead of using `fetch_add`. + info->num_rehashes.store( + 1 + info->num_rehashes.load(std::memory_order_relaxed), + std::memory_order_relaxed); +} + +void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity) { + info->max_reserve.store( + (std::max)(info->max_reserve.load(std::memory_order_relaxed), + target_capacity), + std::memory_order_relaxed); +} + +void RecordClearedReservationSlow(HashtablezInfo* info) { + info->max_reserve.store(0, std::memory_order_relaxed); +} + +void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, + size_t capacity) { + info->size.store(size, std::memory_order_relaxed); + info->capacity.store(capacity, std::memory_order_relaxed); + if (size == 0) { + // This is a clear, reset the total/num_erases too. + info->total_probe_length.store(0, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); + } +} + void RecordInsertSlow(HashtablezInfo* info, size_t hash, size_t distance_from_desired) { // SwissTables probe in groups of 16, so scale this to count items probes and @@ -180,6 +218,14 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash, info->size.fetch_add(1, std::memory_order_relaxed); } +void RecordEraseSlow(HashtablezInfo* info) { + info->size.fetch_sub(1, std::memory_order_relaxed); + // There is only one concurrent writer, so `load` then `store` is sufficient + // instead of using `fetch_add`. + info->num_erases.store(1 + info->num_erases.load(std::memory_order_relaxed), + std::memory_order_relaxed); +} + void SetHashtablezConfigListener(HashtablezConfigListener l) { g_hashtablez_config_listener.store(l, std::memory_order_release); } @@ -215,21 +261,20 @@ void SetHashtablezSampleParameterInternal(int32_t rate) { } } -int32_t GetHashtablezMaxSamples() { +size_t GetHashtablezMaxSamples() { return GlobalHashtablezSampler().GetMaxSamples(); } -void SetHashtablezMaxSamples(int32_t max) { +void SetHashtablezMaxSamples(size_t max) { SetHashtablezMaxSamplesInternal(max); TriggerHashtablezConfigListener(); } -void SetHashtablezMaxSamplesInternal(int32_t max) { +void SetHashtablezMaxSamplesInternal(size_t max) { if (max > 0) { GlobalHashtablezSampler().SetMaxSamples(max); } else { - Y_ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld", - static_cast<long long>(max)); // NOLINT(runtime/int) + Y_ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: 0"); } } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hashtablez_sampler.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hashtablez_sampler.h index fefa2bb9f0..95353663f2 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hashtablez_sampler.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/hashtablez_sampler.h @@ -95,55 +95,19 @@ struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> { size_t inline_element_size; // How big is the slot? }; -inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { -#ifdef Y_ABSL_INTERNAL_HAVE_SSE2 - total_probe_length /= 16; -#else - total_probe_length /= 8; -#endif - info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); - info->num_erases.store(0, std::memory_order_relaxed); - // There is only one concurrent writer, so `load` then `store` is sufficient - // instead of using `fetch_add`. - info->num_rehashes.store( - 1 + info->num_rehashes.load(std::memory_order_relaxed), - std::memory_order_relaxed); -} +void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length); -inline void RecordReservationSlow(HashtablezInfo* info, - size_t target_capacity) { - info->max_reserve.store( - (std::max)(info->max_reserve.load(std::memory_order_relaxed), - target_capacity), - std::memory_order_relaxed); -} +void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity); -inline void RecordClearedReservationSlow(HashtablezInfo* info) { - info->max_reserve.store(0, std::memory_order_relaxed); -} +void RecordClearedReservationSlow(HashtablezInfo* info); -inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, - size_t capacity) { - info->size.store(size, std::memory_order_relaxed); - info->capacity.store(capacity, std::memory_order_relaxed); - if (size == 0) { - // This is a clear, reset the total/num_erases too. - info->total_probe_length.store(0, std::memory_order_relaxed); - info->num_erases.store(0, std::memory_order_relaxed); - } -} +void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, + size_t capacity); void RecordInsertSlow(HashtablezInfo* info, size_t hash, size_t distance_from_desired); -inline void RecordEraseSlow(HashtablezInfo* info) { - info->size.fetch_sub(1, std::memory_order_relaxed); - // There is only one concurrent writer, so `load` then `store` is sufficient - // instead of using `fetch_add`. - info->num_erases.store( - 1 + info->num_erases.load(std::memory_order_relaxed), - std::memory_order_relaxed); -} +void RecordEraseSlow(HashtablezInfo* info); struct SamplingState { int64_t next_sample; @@ -165,7 +129,10 @@ class HashtablezInfoHandle { public: explicit HashtablezInfoHandle() : info_(nullptr) {} explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {} - ~HashtablezInfoHandle() { + + // We do not have a destructor. Caller is responsible for calling Unregister + // before destroying the handle. + void Unregister() { if (Y_ABSL_PREDICT_TRUE(info_ == nullptr)) return; UnsampleSlow(info_); } @@ -230,6 +197,7 @@ class HashtablezInfoHandle { explicit HashtablezInfoHandle() = default; explicit HashtablezInfoHandle(std::nullptr_t) {} + inline void Unregister() {} inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {} inline void RecordRehash(size_t /*total_probe_length*/) {} inline void RecordReservation(size_t /*target_capacity*/) {} @@ -281,9 +249,9 @@ void SetHashtablezSampleParameter(int32_t rate); void SetHashtablezSampleParameterInternal(int32_t rate); // Sets a soft max for the number of samples that will be kept. -int32_t GetHashtablezMaxSamples(); -void SetHashtablezMaxSamples(int32_t max); -void SetHashtablezMaxSamplesInternal(int32_t max); +size_t GetHashtablezMaxSamples(); +void SetHashtablezMaxSamples(size_t max); +void SetHashtablezMaxSamplesInternal(size_t max); // Configuration override. // This allows process-wide sampling without depending on order of diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/inlined_vector.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/inlined_vector.h index 047dd99882..b90c16528d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/inlined_vector.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/inlined_vector.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef Y_ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ -#define Y_ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ +#ifndef Y_ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_H_ +#define Y_ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_H_ #include <algorithm> #include <cstddef> @@ -83,6 +83,11 @@ using IsMemcpyOk = y_absl::is_trivially_copy_assignable<ValueType<A>>, y_absl::is_trivially_destructible<ValueType<A>>>; +template <typename A> +using IsMoveAssignOk = std::is_move_assignable<ValueType<A>>; +template <typename A> +using IsSwapOk = y_absl::type_traits_internal::IsSwappable<ValueType<A>>; + template <typename T> struct TypeIdentity { using type = T; @@ -120,8 +125,8 @@ struct DestroyAdapter<A, /* IsTriviallyDestructible */ true> { template <typename A> struct Allocation { - Pointer<A> data; - SizeType<A> capacity; + Pointer<A> data = nullptr; + SizeType<A> capacity = 0; }; template <typename A, @@ -297,6 +302,20 @@ class ConstructionTransaction { template <typename T, size_t N, typename A> class Storage { public: + struct MemcpyPolicy {}; + struct ElementwiseAssignPolicy {}; + struct ElementwiseSwapPolicy {}; + struct ElementwiseConstructPolicy {}; + + using MoveAssignmentPolicy = y_absl::conditional_t< + IsMemcpyOk<A>::value, MemcpyPolicy, + y_absl::conditional_t<IsMoveAssignOk<A>::value, ElementwiseAssignPolicy, + ElementwiseConstructPolicy>>; + using SwapPolicy = y_absl::conditional_t< + IsMemcpyOk<A>::value, MemcpyPolicy, + y_absl::conditional_t<IsSwapOk<A>::value, ElementwiseSwapPolicy, + ElementwiseConstructPolicy>>; + static SizeType<A> NextCapacity(SizeType<A> current_capacity) { return current_capacity * 2; } @@ -360,7 +379,9 @@ class Storage { return data_.allocated.allocated_capacity; } - SizeType<A> GetInlinedCapacity() const { return static_cast<SizeType<A>>(N); } + SizeType<A> GetInlinedCapacity() const { + return static_cast<SizeType<A>>(kOptimalInlinedSize); + } StorageView<A> MakeStorageView() { return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(), @@ -464,8 +485,15 @@ class Storage { SizeType<A> allocated_capacity; }; + // `kOptimalInlinedSize` is an automatically adjusted inlined capacity of the + // `InlinedVector`. Sometimes, it is possible to increase the capacity (from + // the user requested `N`) without increasing the size of the `InlinedVector`. + static constexpr size_t kOptimalInlinedSize = + (std::max)(N, sizeof(Allocated) / sizeof(ValueType<A>)); + struct Inlined { - alignas(ValueType<A>) char inlined_data[sizeof(ValueType<A>[N])]; + alignas(ValueType<A>) char inlined_data[sizeof( + ValueType<A>[kOptimalInlinedSize])]; }; union Data { @@ -473,6 +501,13 @@ class Storage { Inlined inlined; }; + void SwapN(ElementwiseSwapPolicy, Storage* other, SizeType<A> n); + void SwapN(ElementwiseConstructPolicy, Storage* other, SizeType<A> n); + + void SwapInlinedElements(MemcpyPolicy, Storage* other); + template <typename NotMemcpyPolicy> + void SwapInlinedElements(NotMemcpyPolicy, Storage* other); + template <typename... Args> Y_ABSL_ATTRIBUTE_NOINLINE Reference<A> EmplaceBackSlow(Args&&... args); @@ -641,8 +676,8 @@ auto Storage<T, N, A>::Insert(ConstIterator<A> pos, ValueAdapter values, SizeType<A> insert_count) -> Iterator<A> { StorageView<A> storage_view = MakeStorageView(); - SizeType<A> insert_index = - std::distance(ConstIterator<A>(storage_view.data), pos); + auto insert_index = static_cast<SizeType<A>>( + std::distance(ConstIterator<A>(storage_view.data), pos)); SizeType<A> insert_end_index = insert_index + insert_count; SizeType<A> new_size = storage_view.size + insert_count; @@ -784,9 +819,9 @@ auto Storage<T, N, A>::Erase(ConstIterator<A> from, ConstIterator<A> to) -> Iterator<A> { StorageView<A> storage_view = MakeStorageView(); - SizeType<A> erase_size = std::distance(from, to); - SizeType<A> erase_index = - std::distance(ConstIterator<A>(storage_view.data), from); + auto erase_size = static_cast<SizeType<A>>(std::distance(from, to)); + auto erase_index = static_cast<SizeType<A>>( + std::distance(ConstIterator<A>(storage_view.data), from)); SizeType<A> erase_end_index = erase_index + erase_size; IteratorValueAdapter<A, MoveIterator<A>> move_values( @@ -886,26 +921,7 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { swap(data_.allocated, other_storage_ptr->data_.allocated); } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { - Storage* small_ptr = this; - Storage* large_ptr = other_storage_ptr; - if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); - - for (SizeType<A> i = 0; i < small_ptr->GetSize(); ++i) { - swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); - } - - IteratorValueAdapter<A, MoveIterator<A>> move_values( - MoveIterator<A>(large_ptr->GetInlinedData() + small_ptr->GetSize())); - - ConstructElements<A>(large_ptr->GetAllocator(), - small_ptr->GetInlinedData() + small_ptr->GetSize(), - move_values, - large_ptr->GetSize() - small_ptr->GetSize()); - - DestroyAdapter<A>::DestroyElements( - large_ptr->GetAllocator(), - large_ptr->GetInlinedData() + small_ptr->GetSize(), - large_ptr->GetSize() - small_ptr->GetSize()); + SwapInlinedElements(SwapPolicy{}, other_storage_ptr); } else { Storage* allocated_ptr = this; Storage* inlined_ptr = other_storage_ptr; @@ -941,6 +957,68 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { swap(GetAllocator(), other_storage_ptr->GetAllocator()); } +template <typename T, size_t N, typename A> +void Storage<T, N, A>::SwapN(ElementwiseSwapPolicy, Storage* other, + SizeType<A> n) { + std::swap_ranges(GetInlinedData(), GetInlinedData() + n, + other->GetInlinedData()); +} + +template <typename T, size_t N, typename A> +void Storage<T, N, A>::SwapN(ElementwiseConstructPolicy, Storage* other, + SizeType<A> n) { + Pointer<A> a = GetInlinedData(); + Pointer<A> b = other->GetInlinedData(); + // see note on allocators in `SwapInlinedElements`. + A& allocator_a = GetAllocator(); + A& allocator_b = other->GetAllocator(); + for (SizeType<A> i = 0; i < n; ++i, ++a, ++b) { + ValueType<A> tmp(std::move(*a)); + + AllocatorTraits<A>::destroy(allocator_a, a); + AllocatorTraits<A>::construct(allocator_b, a, std::move(*b)); + + AllocatorTraits<A>::destroy(allocator_b, b); + AllocatorTraits<A>::construct(allocator_a, b, std::move(tmp)); + } +} + +template <typename T, size_t N, typename A> +void Storage<T, N, A>::SwapInlinedElements(MemcpyPolicy, Storage* other) { + Data tmp = data_; + data_ = other->data_; + other->data_ = tmp; +} + +template <typename T, size_t N, typename A> +template <typename NotMemcpyPolicy> +void Storage<T, N, A>::SwapInlinedElements(NotMemcpyPolicy policy, + Storage* other) { + // Note: `destroy` needs to use pre-swap allocator while `construct` - + // post-swap allocator. Allocators will be swaped later on outside of + // `SwapInlinedElements`. + Storage* small_ptr = this; + Storage* large_ptr = other; + if (small_ptr->GetSize() > large_ptr->GetSize()) { + std::swap(small_ptr, large_ptr); + } + + auto small_size = small_ptr->GetSize(); + auto diff = large_ptr->GetSize() - small_size; + SwapN(policy, other, small_size); + + IteratorValueAdapter<A, MoveIterator<A>> move_values( + MoveIterator<A>(large_ptr->GetInlinedData() + small_size)); + + ConstructElements<A>(large_ptr->GetAllocator(), + small_ptr->GetInlinedData() + small_size, move_values, + diff); + + DestroyAdapter<A>::DestroyElements(large_ptr->GetAllocator(), + large_ptr->GetInlinedData() + small_size, + diff); +} + // End ignore "array-bounds" #if !defined(__clang__) && defined(__GNUC__) #pragma GCC diagnostic pop @@ -950,4 +1028,4 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { Y_ABSL_NAMESPACE_END } // namespace y_absl -#endif // Y_ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ +#endif // Y_ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/raw_hash_set.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/raw_hash_set.cc index d18535fcbb..e41730f431 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/raw_hash_set.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/raw_hash_set.cc @@ -16,6 +16,7 @@ #include <atomic> #include <cstddef> +#include <cstring> #include "y_absl/base/config.h" @@ -25,11 +26,14 @@ namespace container_internal { // A single block of empty control bytes for tables without any slots allocated. // This enables removing a branch in the hot path of find(). -alignas(16) Y_ABSL_CONST_INIT Y_ABSL_DLL const ctrl_t kEmptyGroup[16] = { +// We have 17 bytes because there may be a generation counter. Any constant is +// fine for the generation counter. +alignas(16) Y_ABSL_CONST_INIT Y_ABSL_DLL const ctrl_t kEmptyGroup[17] = { ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, - ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + static_cast<ctrl_t>(0)}; #ifdef Y_ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr size_t Group::kWidth; @@ -63,8 +67,155 @@ void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) { std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes()); ctrl[capacity] = ctrl_t::kSentinel; } -// Extern template instantiotion for inline function. -template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); +// Extern template instantiation for inline function. +template FindInfo find_first_non_full(const CommonFields&, size_t); + +FindInfo find_first_non_full_outofline(const CommonFields& common, + size_t hash) { + return find_first_non_full(common, hash); +} + +// Return address of the ith slot in slots where each slot occupies slot_size. +static inline void* SlotAddress(void* slot_array, size_t slot, + size_t slot_size) { + return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot_array) + + (slot * slot_size)); +} + +// Return the address of the slot just after slot assuming each slot +// has the specified size. +static inline void* NextSlot(void* slot, size_t slot_size) { + return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) + slot_size); +} + +// Return the address of the slot just before slot assuming each slot +// has the specified size. +static inline void* PrevSlot(void* slot, size_t slot_size) { + return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) - slot_size); +} + +void DropDeletesWithoutResize(CommonFields& common, + const PolicyFunctions& policy, void* tmp_space) { + void* set = &common; + void* slot_array = common.slots_; + const size_t capacity = common.capacity_; + assert(IsValidCapacity(capacity)); + assert(!is_small(capacity)); + // Algorithm: + // - mark all DELETED slots as EMPTY + // - mark all FULL slots as DELETED + // - for each slot marked as DELETED + // hash = Hash(element) + // target = find_first_non_full(hash) + // if target is in the same group + // mark slot as FULL + // else if target is EMPTY + // transfer element to target + // mark slot as EMPTY + // mark target as FULL + // else if target is DELETED + // swap current element with target element + // mark target as FULL + // repeat procedure for current slot with moved from element (target) + ctrl_t* ctrl = common.control_; + ConvertDeletedToEmptyAndFullToDeleted(ctrl, capacity); + auto hasher = policy.hash_slot; + auto transfer = policy.transfer; + const size_t slot_size = policy.slot_size; + + size_t total_probe_length = 0; + void* slot_ptr = SlotAddress(slot_array, 0, slot_size); + for (size_t i = 0; i != capacity; + ++i, slot_ptr = NextSlot(slot_ptr, slot_size)) { + assert(slot_ptr == SlotAddress(slot_array, i, slot_size)); + if (!IsDeleted(ctrl[i])) continue; + const size_t hash = (*hasher)(set, slot_ptr); + const FindInfo target = find_first_non_full(common, hash); + const size_t new_i = target.offset; + total_probe_length += target.probe_length; + + // Verify if the old and new i fall within the same group wrt the hash. + // If they do, we don't need to move the object as it falls already in the + // best probe we can. + const size_t probe_offset = probe(common, hash).offset(); + const auto probe_index = [probe_offset, capacity](size_t pos) { + return ((pos - probe_offset) & capacity) / Group::kWidth; + }; + + // Element doesn't move. + if (Y_ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { + SetCtrl(common, i, H2(hash), slot_size); + continue; + } + + void* new_slot_ptr = SlotAddress(slot_array, new_i, slot_size); + if (IsEmpty(ctrl[new_i])) { + // Transfer element to the empty spot. + // SetCtrl poisons/unpoisons the slots so we have to call it at the + // right time. + SetCtrl(common, new_i, H2(hash), slot_size); + (*transfer)(set, new_slot_ptr, slot_ptr); + SetCtrl(common, i, ctrl_t::kEmpty, slot_size); + } else { + assert(IsDeleted(ctrl[new_i])); + SetCtrl(common, new_i, H2(hash), slot_size); + // Until we are done rehashing, DELETED marks previously FULL slots. + + // Swap i and new_i elements. + (*transfer)(set, tmp_space, new_slot_ptr); + (*transfer)(set, new_slot_ptr, slot_ptr); + (*transfer)(set, slot_ptr, tmp_space); + + // repeat the processing of the ith slot + --i; + slot_ptr = PrevSlot(slot_ptr, slot_size); + } + } + ResetGrowthLeft(common); + common.infoz().RecordRehash(total_probe_length); +} + +void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size) { + assert(IsFull(*it) && "erasing a dangling iterator"); + --c.size_; + const auto index = static_cast<size_t>(it - c.control_); + const size_t index_before = (index - Group::kWidth) & c.capacity_; + const auto empty_after = Group(it).MaskEmpty(); + const auto empty_before = Group(c.control_ + index_before).MaskEmpty(); + + // We count how many consecutive non empties we have to the right and to the + // left of `it`. If the sum is >= kWidth then there is at least one probe + // window that might have seen a full group. + bool was_never_full = empty_before && empty_after && + static_cast<size_t>(empty_after.TrailingZeros()) + + empty_before.LeadingZeros() < + Group::kWidth; + + SetCtrl(c, index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted, + slot_size); + c.growth_left() += (was_never_full ? 1 : 0); + c.infoz().RecordErase(); +} + +void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy, + bool reuse) { + c.size_ = 0; + if (reuse) { + ResetCtrl(c, policy.slot_size); + c.infoz().RecordStorageChanged(0, c.capacity_); + } else { + void* set = &c; + (*policy.dealloc)(set, policy, c.control_, c.slots_, c.capacity_); + c.control_ = EmptyGroup(); + c.set_generation_ptr(EmptyGeneration()); + c.slots_ = nullptr; + c.capacity_ = 0; + c.growth_left() = 0; + c.infoz().RecordClearedReservation(); + assert(c.size_ == 0); + c.infoz().RecordStorageChanged(0, 0); + } +} } // namespace container_internal Y_ABSL_NAMESPACE_END diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/raw_hash_set.h b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/raw_hash_set.h index e24a2bb813..26fda8b83c 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/raw_hash_set.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/container/internal/raw_hash_set.h @@ -186,6 +186,7 @@ #include "y_absl/base/config.h" #include "y_absl/base/internal/endian.h" #include "y_absl/base/internal/prefetch.h" +#include "y_absl/base/internal/raw_logging.h" #include "y_absl/base/optimization.h" #include "y_absl/base/port.h" #include "y_absl/container/internal/common.h" @@ -219,6 +220,29 @@ namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace container_internal { +#ifdef Y_ABSL_SWISSTABLE_ENABLE_GENERATIONS +#error Y_ABSL_SWISSTABLE_ENABLE_GENERATIONS cannot be directly set +#elif defined(Y_ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(Y_ABSL_HAVE_MEMORY_SANITIZER) +// When compiled in sanitizer mode, we add generation integers to the backing +// array and iterators. In the backing array, we store the generation between +// the control bytes and the slots. When iterators are dereferenced, we assert +// that the container has not been mutated in a way that could cause iterator +// invalidation since the iterator was initialized. +#define Y_ABSL_SWISSTABLE_ENABLE_GENERATIONS +#endif + +// We use uint8_t so we don't need to worry about padding. +using GenerationType = uint8_t; + +#ifdef Y_ABSL_SWISSTABLE_ENABLE_GENERATIONS +constexpr bool SwisstableGenerationsEnabled() { return true; } +constexpr size_t NumGenerationBytes() { return sizeof(GenerationType); } +#else +constexpr bool SwisstableGenerationsEnabled() { return false; } +constexpr size_t NumGenerationBytes() { return 0; } +#endif + template <typename AllocType> void SwapAlloc(AllocType& lhs, AllocType& rhs, std::true_type /* propagate_on_container_swap */) { @@ -451,7 +475,7 @@ static_assert(ctrl_t::kDeleted == static_cast<ctrl_t>(-2), "ctrl_t::kDeleted must be -2 to make the implementation of " "ConvertSpecialToEmptyAndFullToDeleted efficient"); -Y_ABSL_DLL extern const ctrl_t kEmptyGroup[16]; +Y_ABSL_DLL extern const ctrl_t kEmptyGroup[17]; // Returns a pointer to a control byte group that can be used by empty tables. inline ctrl_t* EmptyGroup() { @@ -460,6 +484,12 @@ inline ctrl_t* EmptyGroup() { return const_cast<ctrl_t*>(kEmptyGroup); } +// Returns a pointer to the generation byte at the end of the empty group, if it +// exists. +inline GenerationType* EmptyGeneration() { + return reinterpret_cast<GenerationType*>(EmptyGroup() + 16); +} + // Mixes a randomly generated per-process seed with `hash` and `ctrl` to // randomize insertion order within groups. bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl); @@ -545,7 +575,7 @@ struct GroupSse2Impl { // Returns a bitmask representing the positions of slots that match hash. BitMask<uint32_t, kWidth> Match(h2_t hash) const { - auto match = _mm_set1_epi8(hash); + auto match = _mm_set1_epi8(static_cast<char>(hash)); return BitMask<uint32_t, kWidth>( static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)))); } @@ -557,7 +587,7 @@ struct GroupSse2Impl { return NonIterableBitMask<uint32_t, kWidth>( static_cast<uint32_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)))); #else - auto match = _mm_set1_epi8(static_cast<h2_t>(ctrl_t::kEmpty)); + auto match = _mm_set1_epi8(static_cast<char>(ctrl_t::kEmpty)); return NonIterableBitMask<uint32_t, kWidth>( static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)))); #endif @@ -565,14 +595,14 @@ struct GroupSse2Impl { // Returns a bitmask representing the positions of empty or deleted slots. NonIterableBitMask<uint32_t, kWidth> MaskEmptyOrDeleted() const { - auto special = _mm_set1_epi8(static_cast<uint8_t>(ctrl_t::kSentinel)); + auto special = _mm_set1_epi8(static_cast<char>(ctrl_t::kSentinel)); return NonIterableBitMask<uint32_t, kWidth>(static_cast<uint32_t>( _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)))); } // Returns the number of trailing empty or deleted elements in the group. uint32_t CountLeadingEmptyOrDeleted() const { - auto special = _mm_set1_epi8(static_cast<uint8_t>(ctrl_t::kSentinel)); + auto special = _mm_set1_epi8(static_cast<char>(ctrl_t::kSentinel)); return TrailingZeros(static_cast<uint32_t>( _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1)); } @@ -612,9 +642,9 @@ struct GroupAArch64Impl { NonIterableBitMask<uint64_t, kWidth, 3> MaskEmpty() const { uint64_t mask = - vget_lane_u64(vreinterpret_u64_u8( - vceq_s8(vdup_n_s8(static_cast<h2_t>(ctrl_t::kEmpty)), - vreinterpret_s8_u8(ctrl))), + vget_lane_u64(vreinterpret_u64_u8(vceq_s8( + vdup_n_s8(static_cast<int8_t>(ctrl_t::kEmpty)), + vreinterpret_s8_u8(ctrl))), 0); return NonIterableBitMask<uint64_t, kWidth, 3>(mask); } @@ -629,13 +659,16 @@ struct GroupAArch64Impl { } uint32_t CountLeadingEmptyOrDeleted() const { - uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0); - // ctrl | ~(ctrl >> 7) will have the lowest bit set to zero for kEmpty and - // kDeleted. We lower all other bits and count number of trailing zeros. + uint64_t mask = + vget_lane_u64(vreinterpret_u64_u8(vcle_s8( + vdup_n_s8(static_cast<int8_t>(ctrl_t::kSentinel)), + vreinterpret_s8_u8(ctrl))), + 0); + // Similar to MaskEmptyorDeleted() but we invert the logic to invert the + // produced bitfield. We then count number of trailing zeros. // Clang and GCC optimize countr_zero to rbit+clz without any check for 0, // so we should be fine. - constexpr uint64_t bits = 0x0101010101010101ULL; - return countr_zero((mask | ~(mask >> 7)) & bits) >> 3; + return static_cast<uint32_t>(countr_zero(mask)) >> 3; } void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { @@ -693,7 +726,8 @@ struct GroupPortableImpl { // ctrl | ~(ctrl >> 7) will have the lowest bit set to zero for kEmpty and // kDeleted. We lower all other bits and count number of trailing zeros. constexpr uint64_t bits = 0x0101010101010101ULL; - return countr_zero((ctrl | ~(ctrl >> 7)) & bits) >> 3; + return static_cast<uint32_t>(countr_zero((ctrl | ~(ctrl >> 7)) & bits) >> + 3); } void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { @@ -715,6 +749,192 @@ using Group = GroupAArch64Impl; using Group = GroupPortableImpl; #endif +class CommonFieldsGenerationInfoEnabled { + // A sentinel value for reserved_growth_ indicating that we just ran out of + // reserved growth on the last insertion. When reserve is called and then + // insertions take place, reserved_growth_'s state machine is N, ..., 1, + // kReservedGrowthJustRanOut, 0. + static constexpr size_t kReservedGrowthJustRanOut = + (std::numeric_limits<size_t>::max)(); + + public: + CommonFieldsGenerationInfoEnabled() = default; + CommonFieldsGenerationInfoEnabled(CommonFieldsGenerationInfoEnabled&& that) + : reserved_growth_(that.reserved_growth_), generation_(that.generation_) { + that.reserved_growth_ = 0; + that.generation_ = EmptyGeneration(); + } + CommonFieldsGenerationInfoEnabled& operator=( + CommonFieldsGenerationInfoEnabled&&) = default; + + // Whether we should rehash on insert in order to detect bugs of using invalid + // references. We rehash on the first insertion after reserved_growth_ reaches + // 0 after a call to reserve. + // TODO(b/254649633): we could potentially do a rehash with low probability + // whenever reserved_growth_ is zero. + bool should_rehash_for_bug_detection_on_insert() const { + return reserved_growth_ == kReservedGrowthJustRanOut; + } + void maybe_increment_generation_on_insert() { + if (reserved_growth_ == kReservedGrowthJustRanOut) reserved_growth_ = 0; + + if (reserved_growth_ > 0) { + if (--reserved_growth_ == 0) reserved_growth_ = kReservedGrowthJustRanOut; + } else { + ++*generation_; + } + } + void reset_reserved_growth(size_t reservation, size_t size) { + reserved_growth_ = reservation - size; + } + size_t reserved_growth() const { return reserved_growth_; } + void set_reserved_growth(size_t r) { reserved_growth_ = r; } + GenerationType generation() const { return *generation_; } + void set_generation(GenerationType g) { *generation_ = g; } + GenerationType* generation_ptr() const { return generation_; } + void set_generation_ptr(GenerationType* g) { generation_ = g; } + + private: + // The number of insertions remaining that are guaranteed to not rehash due to + // a prior call to reserve. Note: we store reserved growth rather than + // reservation size because calls to erase() decrease size_ but don't decrease + // reserved growth. + size_t reserved_growth_ = 0; + // Pointer to the generation counter, which is used to validate iterators and + // is stored in the backing array between the control bytes and the slots. + // Note that we can't store the generation inside the container itself and + // keep a pointer to the container in the iterators because iterators must + // remain valid when the container is moved. + // Note: we could derive this pointer from the control pointer, but it makes + // the code more complicated, and there's a benefit in having the sizes of + // raw_hash_set in sanitizer mode and non-sanitizer mode a bit more different, + // which is that tests are less likely to rely on the size remaining the same. + GenerationType* generation_ = EmptyGeneration(); +}; + +class CommonFieldsGenerationInfoDisabled { + public: + CommonFieldsGenerationInfoDisabled() = default; + CommonFieldsGenerationInfoDisabled(CommonFieldsGenerationInfoDisabled&&) = + default; + CommonFieldsGenerationInfoDisabled& operator=( + CommonFieldsGenerationInfoDisabled&&) = default; + + bool should_rehash_for_bug_detection_on_insert() const { return false; } + void maybe_increment_generation_on_insert() {} + void reset_reserved_growth(size_t, size_t) {} + size_t reserved_growth() const { return 0; } + void set_reserved_growth(size_t) {} + GenerationType generation() const { return 0; } + void set_generation(GenerationType) {} + GenerationType* generation_ptr() const { return nullptr; } + void set_generation_ptr(GenerationType*) {} +}; + +class HashSetIteratorGenerationInfoEnabled { + public: + HashSetIteratorGenerationInfoEnabled() = default; + explicit HashSetIteratorGenerationInfoEnabled( + const GenerationType* generation_ptr) + : generation_ptr_(generation_ptr), generation_(*generation_ptr) {} + + GenerationType generation() const { return generation_; } + void reset_generation() { generation_ = *generation_ptr_; } + const GenerationType* generation_ptr() const { return generation_ptr_; } + void set_generation_ptr(const GenerationType* ptr) { generation_ptr_ = ptr; } + + private: + const GenerationType* generation_ptr_ = EmptyGeneration(); + GenerationType generation_ = *generation_ptr_; +}; + +class HashSetIteratorGenerationInfoDisabled { + public: + HashSetIteratorGenerationInfoDisabled() = default; + explicit HashSetIteratorGenerationInfoDisabled(const GenerationType*) {} + + GenerationType generation() const { return 0; } + void reset_generation() {} + const GenerationType* generation_ptr() const { return nullptr; } + void set_generation_ptr(const GenerationType*) {} +}; + +#ifdef Y_ABSL_SWISSTABLE_ENABLE_GENERATIONS +using CommonFieldsGenerationInfo = CommonFieldsGenerationInfoEnabled; +using HashSetIteratorGenerationInfo = HashSetIteratorGenerationInfoEnabled; +#else +using CommonFieldsGenerationInfo = CommonFieldsGenerationInfoDisabled; +using HashSetIteratorGenerationInfo = HashSetIteratorGenerationInfoDisabled; +#endif + +// CommonFields hold the fields in raw_hash_set that do not depend +// on template parameters. This allows us to conveniently pass all +// of this state to helper functions as a single argument. +class CommonFields : public CommonFieldsGenerationInfo { + public: + CommonFields() = default; + + // Not copyable + CommonFields(const CommonFields&) = delete; + CommonFields& operator=(const CommonFields&) = delete; + + // Movable + CommonFields(CommonFields&& that) + : CommonFieldsGenerationInfo( + std::move(static_cast<CommonFieldsGenerationInfo&&>(that))), + // Explicitly copying fields into "this" and then resetting "that" + // fields generates less code then calling y_absl::exchange per field. + control_(that.control_), + slots_(that.slots_), + size_(that.size_), + capacity_(that.capacity_), + compressed_tuple_(that.growth_left(), std::move(that.infoz())) { + that.control_ = EmptyGroup(); + that.slots_ = nullptr; + that.size_ = 0; + that.capacity_ = 0; + that.growth_left() = 0; + } + CommonFields& operator=(CommonFields&&) = default; + + // The number of slots we can still fill without needing to rehash. + size_t& growth_left() { return compressed_tuple_.template get<0>(); } + + HashtablezInfoHandle& infoz() { return compressed_tuple_.template get<1>(); } + const HashtablezInfoHandle& infoz() const { + return compressed_tuple_.template get<1>(); + } + + void reset_reserved_growth(size_t reservation) { + CommonFieldsGenerationInfo::reset_reserved_growth(reservation, size_); + } + + // TODO(b/259599413): Investigate removing some of these fields: + // - control/slots can be derived from each other + // - size can be moved into the slot array + + // The control bytes (and, also, a pointer to the base of the backing array). + // + // This contains `capacity + 1 + NumClonedBytes()` entries, even + // when the table is empty (hence EmptyGroup). + ctrl_t* control_ = EmptyGroup(); + + // The beginning of the slots, located at `SlotOffset()` bytes after + // `control`. May be null for empty tables. + void* slots_ = nullptr; + + // The number of filled slots. + size_t size_ = 0; + + // The total number of available slots. + size_t capacity_ = 0; + + // Bundle together growth_left and HashtablezInfoHandle to ensure EBO for + // HashtablezInfoHandle when sampling is turned off. + y_absl::container_internal::CompressedTuple<size_t, HashtablezInfoHandle> + compressed_tuple_{0u, HashtablezInfoHandle{}}; +}; + // Returns he number of "cloned control bytes". // // This is the number of control bytes that are present both at the beginning @@ -730,6 +950,12 @@ class raw_hash_set; // A valid capacity is a non-zero integer `2^m - 1`. inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } +// Returns the next valid capacity after `n`. +inline size_t NextCapacity(size_t n) { + assert(IsValidCapacity(n) || n == 0); + return n * 2 + 1; +} + // Applies the following mapping to every byte in the control array: // * kDeleted -> kEmpty // * kEmpty -> kEmpty @@ -795,15 +1021,69 @@ size_t SelectBucketCountForIterRange(InputIter first, InputIter last, return 0; } -#define Y_ABSL_INTERNAL_ASSERT_IS_FULL(ctrl, msg) \ - Y_ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && msg) +#define Y_ABSL_INTERNAL_ASSERT_IS_FULL(ctrl, generation, generation_ptr, \ + operation) \ + do { \ + Y_ABSL_HARDENING_ASSERT( \ + (ctrl != nullptr) && operation \ + " called on invalid iterator. The iterator might be an end() " \ + "iterator or may have been default constructed."); \ + if (SwisstableGenerationsEnabled() && generation != *generation_ptr) \ + Y_ABSL_INTERNAL_LOG(FATAL, operation \ + " called on invalidated iterator. The table could " \ + "have rehashed since this iterator was initialized."); \ + Y_ABSL_HARDENING_ASSERT( \ + (IsFull(*ctrl)) && operation \ + " called on invalid iterator. The element might have been erased or " \ + "the table might have rehashed."); \ + } while (0) + +// Note that for comparisons, null/end iterators are valid. +inline void AssertIsValidForComparison(const ctrl_t* ctrl, + GenerationType generation, + const GenerationType* generation_ptr) { + Y_ABSL_HARDENING_ASSERT((ctrl == nullptr || IsFull(*ctrl)) && + "Invalid iterator comparison. The element might have " + "been erased or the table might have rehashed."); + if (SwisstableGenerationsEnabled() && generation != *generation_ptr) { + Y_ABSL_INTERNAL_LOG(FATAL, + "Invalid iterator comparison. The table could have " + "rehashed since this iterator was initialized."); + } +} + +// If the two iterators come from the same container, then their pointers will +// interleave such that ctrl_a <= ctrl_b < slot_a <= slot_b or vice/versa. +// Note: we take slots by reference so that it's not UB if they're uninitialized +// as long as we don't read them (when ctrl is null). +inline bool AreItersFromSameContainer(const ctrl_t* ctrl_a, + const ctrl_t* ctrl_b, + const void* const& slot_a, + const void* const& slot_b) { + // If either control byte is null, then we can't tell. + if (ctrl_a == nullptr || ctrl_b == nullptr) return true; + const void* low_slot = slot_a; + const void* hi_slot = slot_b; + if (ctrl_a > ctrl_b) { + std::swap(ctrl_a, ctrl_b); + std::swap(low_slot, hi_slot); + } + return ctrl_b < low_slot && low_slot <= hi_slot; +} -inline void AssertIsValid(ctrl_t* ctrl) { +// Asserts that two iterators come from the same container. +// Note: we take slots by reference so that it's not UB if they're uninitialized +// as long as we don't read them (when ctrl is null). +// TODO(b/254649633): when generations are enabled, we can detect more cases of +// different containers by comparing the pointers to the generations - this +// can cover cases of end iterators that we would otherwise miss. +inline void AssertSameContainer(const ctrl_t* ctrl_a, const ctrl_t* ctrl_b, + const void* const& slot_a, + const void* const& slot_b) { Y_ABSL_HARDENING_ASSERT( - (ctrl == nullptr || IsFull(*ctrl)) && - "Invalid operation on iterator. The element might have " - "been erased, the table might have rehashed, or this may " - "be an end() iterator."); + AreItersFromSameContainer(ctrl_a, ctrl_b, slot_a, slot_b) && + "Invalid iterator comparison. The iterators may be from different " + "containers or the container might have rehashed."); } struct FindInfo { @@ -825,9 +1105,10 @@ struct FindInfo { // `ShouldInsertBackwards()` for small tables. inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; } -// Begins a probing operation on `ctrl`, using `hash`. -inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, size_t hash, - size_t capacity) { +// Begins a probing operation on `common.control`, using `hash`. +inline probe_seq<Group::kWidth> probe(const CommonFields& common, size_t hash) { + const ctrl_t* ctrl = common.control_; + const size_t capacity = common.capacity_; return probe_seq<Group::kWidth>(H1(hash, ctrl), capacity); } @@ -839,9 +1120,9 @@ inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, size_t hash, // NOTE: this function must work with tables having both empty and deleted // slots in the same group. Such tables appear during `erase()`. template <typename = void> -inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, - size_t capacity) { - auto seq = probe(ctrl, hash, capacity); +inline FindInfo find_first_non_full(const CommonFields& common, size_t hash) { + auto seq = probe(common, hash); + const ctrl_t* ctrl = common.control_; while (true) { Group g{ctrl + seq.offset()}; auto mask = g.MaskEmptyOrDeleted(); @@ -851,55 +1132,75 @@ inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, // In debug build we will randomly insert in either the front or back of // the group. // TODO(kfm,sbenza): revisit after we do unconditional mixing - if (!is_small(capacity) && ShouldInsertBackwards(hash, ctrl)) { + if (!is_small(common.capacity_) && ShouldInsertBackwards(hash, ctrl)) { return {seq.offset(mask.HighestBitSet()), seq.index()}; } #endif return {seq.offset(mask.LowestBitSet()), seq.index()}; } seq.next(); - assert(seq.index() <= capacity && "full table!"); + assert(seq.index() <= common.capacity_ && "full table!"); } } // Extern template for inline function keep possibility of inlining. // When compiler decided to not inline, no symbols will be added to the // corresponding translation unit. -extern template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); +extern template FindInfo find_first_non_full(const CommonFields&, size_t); + +// Non-inlined version of find_first_non_full for use in less +// performance critical routines. +FindInfo find_first_non_full_outofline(const CommonFields&, size_t); + +inline void ResetGrowthLeft(CommonFields& common) { + common.growth_left() = CapacityToGrowth(common.capacity_) - common.size_; +} // Sets `ctrl` to `{kEmpty, kSentinel, ..., kEmpty}`, marking the entire // array as marked as empty. -inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, - size_t slot_size) { +inline void ResetCtrl(CommonFields& common, size_t slot_size) { + const size_t capacity = common.capacity_; + ctrl_t* ctrl = common.control_; std::memset(ctrl, static_cast<int8_t>(ctrl_t::kEmpty), capacity + 1 + NumClonedBytes()); ctrl[capacity] = ctrl_t::kSentinel; - SanitizerPoisonMemoryRegion(slot, slot_size * capacity); + SanitizerPoisonMemoryRegion(common.slots_, slot_size * capacity); + ResetGrowthLeft(common); } // Sets `ctrl[i]` to `h`. // // Unlike setting it directly, this function will perform bounds checks and // mirror the value to the cloned tail if necessary. -inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl, - const void* slot, size_t slot_size) { +inline void SetCtrl(const CommonFields& common, size_t i, ctrl_t h, + size_t slot_size) { + const size_t capacity = common.capacity_; assert(i < capacity); - auto* slot_i = static_cast<const char*>(slot) + i * slot_size; + auto* slot_i = static_cast<const char*>(common.slots_) + i * slot_size; if (IsFull(h)) { SanitizerUnpoisonMemoryRegion(slot_i, slot_size); } else { SanitizerPoisonMemoryRegion(slot_i, slot_size); } + ctrl_t* ctrl = common.control_; ctrl[i] = h; ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h; } // Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`. -inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl, - const void* slot, size_t slot_size) { - SetCtrl(i, static_cast<ctrl_t>(h), capacity, ctrl, slot, slot_size); +inline void SetCtrl(const CommonFields& common, size_t i, h2_t h, + size_t slot_size) { + SetCtrl(common, i, static_cast<ctrl_t>(h), slot_size); +} + +// Given the capacity of a table, computes the offset (from the start of the +// backing allocation) of the generation counter (if it exists). +inline size_t GenerationOffset(size_t capacity) { + assert(IsValidCapacity(capacity)); + const size_t num_control_bytes = capacity + 1 + NumClonedBytes(); + return num_control_bytes; } // Given the capacity of a table, computes the offset (from the start of the @@ -907,7 +1208,8 @@ inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl, inline size_t SlotOffset(size_t capacity, size_t slot_align) { assert(IsValidCapacity(capacity)); const size_t num_control_bytes = capacity + 1 + NumClonedBytes(); - return (num_control_bytes + slot_align - 1) & (~slot_align + 1); + return (num_control_bytes + NumGenerationBytes() + slot_align - 1) & + (~slot_align + 1); } // Given the capacity of a table, computes the total size of the backing @@ -916,6 +1218,91 @@ inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) { return SlotOffset(capacity, slot_align) + capacity * slot_size; } +template <typename Alloc, size_t SizeOfSlot, size_t AlignOfSlot> +Y_ABSL_ATTRIBUTE_NOINLINE void InitializeSlots(CommonFields& c, Alloc alloc) { + assert(c.capacity_); + // Folks with custom allocators often make unwarranted assumptions about the + // behavior of their classes vis-a-vis trivial destructability and what + // calls they will or won't make. Avoid sampling for people with custom + // allocators to get us out of this mess. This is not a hard guarantee but + // a workaround while we plan the exact guarantee we want to provide. + const size_t sample_size = + (std::is_same<Alloc, std::allocator<char>>::value && c.slots_ == nullptr) + ? SizeOfSlot + : 0; + + const size_t cap = c.capacity_; + char* mem = static_cast<char*>( + Allocate<AlignOfSlot>(&alloc, AllocSize(cap, SizeOfSlot, AlignOfSlot))); + const GenerationType old_generation = c.generation(); + c.set_generation_ptr( + reinterpret_cast<GenerationType*>(mem + GenerationOffset(cap))); + c.set_generation(old_generation + 1); + c.control_ = reinterpret_cast<ctrl_t*>(mem); + c.slots_ = mem + SlotOffset(cap, AlignOfSlot); + ResetCtrl(c, SizeOfSlot); + if (sample_size) { + c.infoz() = Sample(sample_size); + } + c.infoz().RecordStorageChanged(c.size_, cap); +} + +// PolicyFunctions bundles together some information for a particular +// raw_hash_set<T, ...> instantiation. This information is passed to +// type-erased functions that want to do small amounts of type-specific +// work. +struct PolicyFunctions { + size_t slot_size; + + // Return the hash of the pointed-to slot. + size_t (*hash_slot)(void* set, void* slot); + + // Transfer the contents of src_slot to dst_slot. + void (*transfer)(void* set, void* dst_slot, void* src_slot); + + // Deallocate the specified backing store which is sized for n slots. + void (*dealloc)(void* set, const PolicyFunctions& policy, ctrl_t* ctrl, + void* slot_array, size_t n); +}; + +// ClearBackingArray clears the backing array, either modifying it in place, +// or creating a new one based on the value of "reuse". +// REQUIRES: c.capacity > 0 +void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy, + bool reuse); + +// Type-erased version of raw_hash_set::erase_meta_only. +void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size); + +// Function to place in PolicyFunctions::dealloc for raw_hash_sets +// that are using std::allocator. This allows us to share the same +// function body for raw_hash_set instantiations that have the +// same slot alignment. +template <size_t AlignOfSlot> +Y_ABSL_ATTRIBUTE_NOINLINE void DeallocateStandard(void*, + const PolicyFunctions& policy, + ctrl_t* ctrl, void* slot_array, + size_t n) { + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slot_array, policy.slot_size * n); + + std::allocator<char> alloc; + Deallocate<AlignOfSlot>(&alloc, ctrl, + AllocSize(n, policy.slot_size, AlignOfSlot)); +} + +// For trivially relocatable types we use memcpy directly. This allows us to +// share the same function body for raw_hash_set instantiations that have the +// same slot size as long as they are relocatable. +template <size_t SizeOfSlot> +Y_ABSL_ATTRIBUTE_NOINLINE void TransferRelocatable(void*, void* dst, void* src) { + memcpy(dst, src, SizeOfSlot); +} + +// Type-erased version of raw_hash_set::drop_deletes_without_resize. +void DropDeletesWithoutResize(CommonFields& common, + const PolicyFunctions& policy, void* tmp_space); + // A SwissTable. // // Policy: a policy defines how to perform different operations on @@ -1016,7 +1403,7 @@ class raw_hash_set { static_assert(std::is_same<const_pointer, const value_type*>::value, "Allocators with custom pointer types are not supported"); - class iterator { + class iterator : private HashSetIteratorGenerationInfo { friend class raw_hash_set; public: @@ -1032,22 +1419,22 @@ class raw_hash_set { // PRECONDITION: not an end() iterator. reference operator*() const { - Y_ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, - "operator*() called on invalid iterator."); + Y_ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, generation(), generation_ptr(), + "operator*()"); return PolicyTraits::element(slot_); } // PRECONDITION: not an end() iterator. pointer operator->() const { - Y_ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, - "operator-> called on invalid iterator."); + Y_ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, generation(), generation_ptr(), + "operator->"); return &operator*(); } // PRECONDITION: not an end() iterator. iterator& operator++() { - Y_ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, - "operator++ called on invalid iterator."); + Y_ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, generation(), generation_ptr(), + "operator++"); ++ctrl_; ++slot_; skip_empty_or_deleted(); @@ -1061,8 +1448,9 @@ class raw_hash_set { } friend bool operator==(const iterator& a, const iterator& b) { - AssertIsValid(a.ctrl_); - AssertIsValid(b.ctrl_); + AssertSameContainer(a.ctrl_, b.ctrl_, a.slot_, b.slot_); + AssertIsValidForComparison(a.ctrl_, a.generation(), a.generation_ptr()); + AssertIsValidForComparison(b.ctrl_, b.generation(), b.generation_ptr()); return a.ctrl_ == b.ctrl_; } friend bool operator!=(const iterator& a, const iterator& b) { @@ -1070,16 +1458,23 @@ class raw_hash_set { } private: - iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) { + iterator(ctrl_t* ctrl, slot_type* slot, + const GenerationType* generation_ptr) + : HashSetIteratorGenerationInfo(generation_ptr), + ctrl_(ctrl), + slot_(slot) { // This assumption helps the compiler know that any non-end iterator is // not equal to any end iterator. Y_ABSL_ASSUME(ctrl != nullptr); } + // For end() iterators. + explicit iterator(const GenerationType* generation_ptr) + : HashSetIteratorGenerationInfo(generation_ptr) {} // Fixes up `ctrl_` to point to a full by advancing it and `slot_` until // they reach one. // - // If a sentinel is reached, we null both of them out instead. + // If a sentinel is reached, we null `ctrl_` out instead. void skip_empty_or_deleted() { while (IsEmptyOrDeleted(*ctrl_)) { uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); @@ -1107,9 +1502,9 @@ class raw_hash_set { using pointer = typename raw_hash_set::const_pointer; using difference_type = typename raw_hash_set::difference_type; - const_iterator() {} + const_iterator() = default; // Implicit construction from iterator. - const_iterator(iterator i) : inner_(std::move(i)) {} + const_iterator(iterator i) : inner_(std::move(i)) {} // NOLINT reference operator*() const { return *inner_; } pointer operator->() const { return inner_.operator->(); } @@ -1128,8 +1523,10 @@ class raw_hash_set { } private: - const_iterator(const ctrl_t* ctrl, const slot_type* slot) - : inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot)) {} + const_iterator(const ctrl_t* ctrl, const slot_type* slot, + const GenerationType* gen) + : inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot), gen) { + } iterator inner_; }; @@ -1137,18 +1534,20 @@ class raw_hash_set { using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>; using insert_return_type = InsertReturnType<iterator, node_type>; + // Note: can't use `= default` due to non-default noexcept (causes + // problems for some compilers). NOLINTNEXTLINE raw_hash_set() noexcept( std::is_nothrow_default_constructible<hasher>::value&& std::is_nothrow_default_constructible<key_equal>::value&& std::is_nothrow_default_constructible<allocator_type>::value) {} - explicit raw_hash_set(size_t bucket_count, const hasher& hash = hasher(), - const key_equal& eq = key_equal(), - const allocator_type& alloc = allocator_type()) - : ctrl_(EmptyGroup()), - settings_(0, HashtablezInfoHandle(), hash, eq, alloc) { + Y_ABSL_ATTRIBUTE_NOINLINE explicit raw_hash_set( + size_t bucket_count, const hasher& hash = hasher(), + const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : settings_(CommonFields{}, hash, eq, alloc) { if (bucket_count) { - capacity_ = NormalizeCapacity(bucket_count); + common().capacity_ = NormalizeCapacity(bucket_count); initialize_slots(); } } @@ -1255,45 +1654,30 @@ class raw_hash_set { // than a full `insert`. for (const auto& v : that) { const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v); - auto target = find_first_non_full(ctrl_, hash, capacity_); - SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_, - sizeof(slot_type)); + auto target = find_first_non_full_outofline(common(), hash); + SetCtrl(common(), target.offset, H2(hash), sizeof(slot_type)); emplace_at(target.offset, v); + common().maybe_increment_generation_on_insert(); infoz().RecordInsert(hash, target.probe_length); } - size_ = that.size(); + common().size_ = that.size(); growth_left() -= that.size(); } - raw_hash_set(raw_hash_set&& that) noexcept( + Y_ABSL_ATTRIBUTE_NOINLINE raw_hash_set(raw_hash_set&& that) noexcept( std::is_nothrow_copy_constructible<hasher>::value&& std::is_nothrow_copy_constructible<key_equal>::value&& std::is_nothrow_copy_constructible<allocator_type>::value) - : ctrl_(y_absl::exchange(that.ctrl_, EmptyGroup())), - slots_(y_absl::exchange(that.slots_, nullptr)), - size_(y_absl::exchange(that.size_, 0)), - capacity_(y_absl::exchange(that.capacity_, 0)), - // Hash, equality and allocator are copied instead of moved because - // `that` must be left valid. If Hash is std::function<Key>, moving it - // would create a nullptr functor that cannot be called. - settings_(y_absl::exchange(that.growth_left(), 0), - y_absl::exchange(that.infoz(), HashtablezInfoHandle()), + : // Hash, equality and allocator are copied instead of moved because + // `that` must be left valid. If Hash is std::function<Key>, moving it + // would create a nullptr functor that cannot be called. + settings_(y_absl::exchange(that.common(), CommonFields{}), that.hash_ref(), that.eq_ref(), that.alloc_ref()) {} raw_hash_set(raw_hash_set&& that, const allocator_type& a) - : ctrl_(EmptyGroup()), - slots_(nullptr), - size_(0), - capacity_(0), - settings_(0, HashtablezInfoHandle(), that.hash_ref(), that.eq_ref(), - a) { + : settings_(CommonFields{}, that.hash_ref(), that.eq_ref(), a) { if (a == that.alloc_ref()) { - std::swap(ctrl_, that.ctrl_); - std::swap(slots_, that.slots_); - std::swap(size_, that.size_); - std::swap(capacity_, that.capacity_); - std::swap(growth_left(), that.growth_left()); - std::swap(infoz(), that.infoz()); + std::swap(common(), that.common()); } else { reserve(that.size()); // Note: this will copy elements of dense_set and unordered_set instead of @@ -1317,30 +1701,43 @@ class raw_hash_set { std::is_nothrow_move_assignable<key_equal>::value) { // TODO(sbenza): We should only use the operations from the noexcept clause // to make sure we actually adhere to that contract. + // NOLINTNEXTLINE: not returning *this for performance. return move_assign( std::move(that), typename AllocTraits::propagate_on_container_move_assignment()); } - ~raw_hash_set() { destroy_slots(); } + ~raw_hash_set() { + const size_t cap = capacity(); + if (!cap) return; + destroy_slots(); + + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * cap); + Deallocate<alignof(slot_type)>( + &alloc_ref(), control(), + AllocSize(cap, sizeof(slot_type), alignof(slot_type))); + + infoz().Unregister(); + } iterator begin() { auto it = iterator_at(0); it.skip_empty_or_deleted(); return it; } - iterator end() { return {}; } + iterator end() { return iterator(common().generation_ptr()); } const_iterator begin() const { return const_cast<raw_hash_set*>(this)->begin(); } - const_iterator end() const { return {}; } + const_iterator end() const { return iterator(common().generation_ptr()); } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } bool empty() const { return !size(); } - size_t size() const { return size_; } - size_t capacity() const { return capacity_; } + size_t size() const { return common().size_; } + size_t capacity() const { return common().capacity_; } size_t max_size() const { return (std::numeric_limits<size_t>::max)(); } Y_ABSL_ATTRIBUTE_REINITIALIZES void clear() { @@ -1351,22 +1748,26 @@ class raw_hash_set { // compared to destruction of the elements of the container. So we pick the // largest bucket_count() threshold for which iteration is still fast and // past that we simply deallocate the array. - if (capacity_ > 127) { + const size_t cap = capacity(); + if (cap == 0) { + // Already guaranteed to be empty; so nothing to do. + } else { destroy_slots(); + ClearBackingArray(common(), GetPolicyFunctions(), + /*reuse=*/cap < 128); + } + common().set_reserved_growth(0); + } - infoz().RecordClearedReservation(); - } else if (capacity_) { - for (size_t i = 0; i != capacity_; ++i) { - if (IsFull(ctrl_[i])) { - PolicyTraits::destroy(&alloc_ref(), slots_ + i); - } + inline void destroy_slots() { + const size_t cap = capacity(); + const ctrl_t* ctrl = control(); + slot_type* slot = slot_array(); + for (size_t i = 0; i != cap; ++i) { + if (IsFull(ctrl[i])) { + PolicyTraits::destroy(&alloc_ref(), slot + i); } - size_ = 0; - ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); - reset_growth_left(); } - assert(empty()); - infoz().RecordStorageChanged(0, capacity_); } // This overload kicks in when the argument is an rvalue of insertable and @@ -1554,7 +1955,7 @@ class raw_hash_set { iterator lazy_emplace(const key_arg<K>& key, F&& f) { auto res = find_or_prepare_insert(key); if (res.second) { - slot_type* slot = slots_ + res.first; + slot_type* slot = slot_array() + res.first; std::forward<F>(f)(constructor(&alloc_ref(), &slot)); assert(!slot); } @@ -1596,8 +1997,8 @@ 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) { - Y_ABSL_INTERNAL_ASSERT_IS_FULL(it.ctrl_, - "erase() called on invalid iterator."); + Y_ABSL_INTERNAL_ASSERT_IS_FULL(it.ctrl_, it.generation(), it.generation_ptr(), + "erase()"); PolicyTraits::destroy(&alloc_ref(), it.slot_); erase_meta_only(it); } @@ -1632,7 +2033,8 @@ class raw_hash_set { node_type extract(const_iterator position) { Y_ABSL_INTERNAL_ASSERT_IS_FULL(position.inner_.ctrl_, - "extract() called on invalid iterator."); + position.inner_.generation(), + position.inner_.generation_ptr(), "extract()"); auto node = CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_); erase_meta_only(position); @@ -1652,24 +2054,18 @@ class raw_hash_set { IsNoThrowSwappable<allocator_type>( typename AllocTraits::propagate_on_container_swap{})) { using std::swap; - swap(ctrl_, that.ctrl_); - swap(slots_, that.slots_); - swap(size_, that.size_); - swap(capacity_, that.capacity_); - swap(growth_left(), that.growth_left()); + swap(common(), that.common()); swap(hash_ref(), that.hash_ref()); swap(eq_ref(), that.eq_ref()); - swap(infoz(), that.infoz()); SwapAlloc(alloc_ref(), that.alloc_ref(), typename AllocTraits::propagate_on_container_swap{}); } void rehash(size_t n) { - if (n == 0 && capacity_ == 0) return; - if (n == 0 && size_ == 0) { - destroy_slots(); - infoz().RecordStorageChanged(0, 0); - infoz().RecordClearedReservation(); + if (n == 0 && capacity() == 0) return; + if (n == 0 && size() == 0) { + ClearBackingArray(common(), GetPolicyFunctions(), + /*reuse=*/false); return; } @@ -1677,7 +2073,7 @@ class raw_hash_set { // power-of-2-minus-1, so bitor is good enough. auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size())); // n == 0 unconditionally rehashes as per the standard. - if (n == 0 || m > capacity_) { + if (n == 0 || m > capacity()) { resize(m); // This is after resize, to ensure that we have completed the allocation @@ -1695,6 +2091,7 @@ class raw_hash_set { // and have potentially sampled the hashtable. infoz().RecordReservation(n); } + common().reset_reserved_growth(n); } // Extension API: support for heterogeneous keys. @@ -1722,9 +2119,9 @@ class raw_hash_set { // Avoid probing if we won't be able to prefetch the addresses received. #ifdef Y_ABSL_INTERNAL_HAVE_PREFETCH prefetch_heap_block(); - auto seq = probe(ctrl_, hash_ref()(key), capacity_); - base_internal::PrefetchT0(ctrl_ + seq.offset()); - base_internal::PrefetchT0(slots_ + seq.offset()); + auto seq = probe(common(), hash_ref()(key)); + base_internal::PrefetchT0(control() + seq.offset()); + base_internal::PrefetchT0(slot_array() + seq.offset()); #endif // Y_ABSL_INTERNAL_HAVE_PREFETCH } @@ -1737,18 +2134,20 @@ class raw_hash_set { // called heterogeneous key support. template <class K = key_type> iterator find(const key_arg<K>& key, size_t hash) { - auto seq = probe(ctrl_, hash, capacity_); + auto seq = probe(common(), hash); + slot_type* slot_ptr = slot_array(); + const ctrl_t* ctrl = control(); while (true) { - Group g{ctrl_ + seq.offset()}; + Group g{ctrl + seq.offset()}; for (uint32_t i : g.Match(H2(hash))) { if (Y_ABSL_PREDICT_TRUE(PolicyTraits::apply( EqualElement<K>{key, eq_ref()}, - PolicyTraits::element(slots_ + seq.offset(i))))) + PolicyTraits::element(slot_ptr + seq.offset(i))))) return iterator_at(seq.offset(i)); } if (Y_ABSL_PREDICT_TRUE(g.MaskEmpty())) return end(); seq.next(); - assert(seq.index() <= capacity_ && "full table!"); + assert(seq.index() <= capacity() && "full table!"); } } template <class K = key_type> @@ -1786,9 +2185,9 @@ class raw_hash_set { return {it, it}; } - size_t bucket_count() const { return capacity_; } + size_t bucket_count() const { return capacity(); } float load_factor() const { - return capacity_ ? static_cast<double>(size()) / capacity_ : 0.0; + return capacity() ? static_cast<double>(size()) / capacity() : 0.0; } float max_load_factor() const { return 1.0f; } void max_load_factor(float) { @@ -1875,7 +2274,8 @@ class raw_hash_set { std::pair<iterator, bool> operator()(const K& key, Args&&...) && { auto res = s.find_or_prepare_insert(key); if (res.second) { - PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot); + PolicyTraits::transfer(&s.alloc_ref(), s.slot_array() + res.first, + &slot); } else if (do_destroy) { PolicyTraits::destroy(&s.alloc_ref(), &slot); } @@ -1891,102 +2291,43 @@ class raw_hash_set { // This merely updates the pertinent control byte. This can be used in // conjunction with Policy::transfer to move the object to another place. void erase_meta_only(const_iterator it) { - assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator"); - --size_; - const size_t index = static_cast<size_t>(it.inner_.ctrl_ - ctrl_); - const size_t index_before = (index - Group::kWidth) & capacity_; - const auto empty_after = Group(it.inner_.ctrl_).MaskEmpty(); - const auto empty_before = Group(ctrl_ + index_before).MaskEmpty(); - - // We count how many consecutive non empties we have to the right and to the - // left of `it`. If the sum is >= kWidth then there is at least one probe - // window that might have seen a full group. - bool was_never_full = - empty_before && empty_after && - static_cast<size_t>(empty_after.TrailingZeros() + - empty_before.LeadingZeros()) < Group::kWidth; - - SetCtrl(index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted, - capacity_, ctrl_, slots_, sizeof(slot_type)); - growth_left() += was_never_full; - infoz().RecordErase(); + EraseMetaOnly(common(), it.inner_.ctrl_, sizeof(slot_type)); } // Allocates a backing array for `self` and initializes its control bytes. - // This reads `capacity_` and updates all other fields based on the result of + // This reads `capacity` and updates all other fields based on the result of // the allocation. // - // This does not free the currently held array; `capacity_` must be nonzero. - void initialize_slots() { - assert(capacity_); - // Folks with custom allocators often make unwarranted assumptions about the - // behavior of their classes vis-a-vis trivial destructability and what - // calls they will or wont make. Avoid sampling for people with custom - // allocators to get us out of this mess. This is not a hard guarantee but - // a workaround while we plan the exact guarantee we want to provide. - // + // This does not free the currently held array; `capacity` must be nonzero. + inline void initialize_slots() { // People are often sloppy with the exact type of their allocator (sometimes // it has an extra const or is missing the pair, but rebinds made it work - // anyway). To avoid the ambiguity, we work off SlotAlloc which we have - // bound more carefully. - if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value && - slots_ == nullptr) { - infoz() = Sample(sizeof(slot_type)); - } - - char* mem = static_cast<char*>(Allocate<alignof(slot_type)>( - &alloc_ref(), - AllocSize(capacity_, sizeof(slot_type), alignof(slot_type)))); - ctrl_ = reinterpret_cast<ctrl_t*>(mem); - slots_ = reinterpret_cast<slot_type*>( - mem + SlotOffset(capacity_, alignof(slot_type))); - ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); - reset_growth_left(); - infoz().RecordStorageChanged(size_, capacity_); - } - - // Destroys all slots in the backing array, frees the backing array, and - // clears all top-level book-keeping data. - // - // This essentially implements `map = raw_hash_set();`. - void destroy_slots() { - if (!capacity_) return; - for (size_t i = 0; i != capacity_; ++i) { - if (IsFull(ctrl_[i])) { - PolicyTraits::destroy(&alloc_ref(), slots_ + i); - } - } - - // Unpoison before returning the memory to the allocator. - SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); - Deallocate<alignof(slot_type)>( - &alloc_ref(), ctrl_, - AllocSize(capacity_, sizeof(slot_type), alignof(slot_type))); - ctrl_ = EmptyGroup(); - slots_ = nullptr; - size_ = 0; - capacity_ = 0; - growth_left() = 0; + // anyway). + using CharAlloc = + typename y_absl::allocator_traits<Alloc>::template rebind_alloc<char>; + InitializeSlots<CharAlloc, sizeof(slot_type), alignof(slot_type)>( + common(), CharAlloc(alloc_ref())); } - void resize(size_t new_capacity) { + Y_ABSL_ATTRIBUTE_NOINLINE void resize(size_t new_capacity) { assert(IsValidCapacity(new_capacity)); - auto* old_ctrl = ctrl_; - auto* old_slots = slots_; - const size_t old_capacity = capacity_; - capacity_ = new_capacity; + auto* old_ctrl = control(); + auto* old_slots = slot_array(); + const size_t old_capacity = common().capacity_; + common().capacity_ = new_capacity; initialize_slots(); + auto* new_slots = slot_array(); size_t total_probe_length = 0; for (size_t i = 0; i != old_capacity; ++i) { if (IsFull(old_ctrl[i])) { size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, PolicyTraits::element(old_slots + i)); - auto target = find_first_non_full(ctrl_, hash, capacity_); + auto target = find_first_non_full(common(), hash); size_t new_i = target.offset; total_probe_length += target.probe_length; - SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); - PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); + SetCtrl(common(), new_i, H2(hash), sizeof(slot_type)); + PolicyTraits::transfer(&alloc_ref(), new_slots + new_i, old_slots + i); } } if (old_capacity) { @@ -2002,70 +2343,10 @@ class raw_hash_set { // Prunes control bytes to remove as many tombstones as possible. // // See the comment on `rehash_and_grow_if_necessary()`. - void drop_deletes_without_resize() Y_ABSL_ATTRIBUTE_NOINLINE { - assert(IsValidCapacity(capacity_)); - assert(!is_small(capacity_)); - // Algorithm: - // - mark all DELETED slots as EMPTY - // - mark all FULL slots as DELETED - // - for each slot marked as DELETED - // hash = Hash(element) - // target = find_first_non_full(hash) - // if target is in the same group - // mark slot as FULL - // else if target is EMPTY - // transfer element to target - // mark slot as EMPTY - // mark target as FULL - // else if target is DELETED - // swap current element with target element - // mark target as FULL - // repeat procedure for current slot with moved from element (target) - ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_); - 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) { - if (!IsDeleted(ctrl_[i])) continue; - const size_t hash = PolicyTraits::apply( - HashElement{hash_ref()}, PolicyTraits::element(slots_ + i)); - const FindInfo target = find_first_non_full(ctrl_, hash, capacity_); - const size_t new_i = target.offset; - total_probe_length += target.probe_length; - - // Verify if the old and new i fall within the same group wrt the hash. - // If they do, we don't need to move the object as it falls already in the - // best probe we can. - const size_t probe_offset = probe(ctrl_, hash, capacity_).offset(); - const auto probe_index = [probe_offset, this](size_t pos) { - return ((pos - probe_offset) & capacity_) / Group::kWidth; - }; - - // Element doesn't move. - if (Y_ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { - SetCtrl(i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); - continue; - } - if (IsEmpty(ctrl_[new_i])) { - // Transfer element to the empty spot. - // SetCtrl poisons/unpoisons the slots so we have to call it at the - // right time. - SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); - PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); - SetCtrl(i, ctrl_t::kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type)); - } else { - assert(IsDeleted(ctrl_[new_i])); - SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); - // Until we are done rehashing, DELETED marks previously FULL slots. - // Swap i and new_i elements. - PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i); - PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i); - PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot); - --i; // repeat - } - } - reset_growth_left(); - infoz().RecordRehash(total_probe_length); + inline void drop_deletes_without_resize() { + // Stack-allocate space for swapping elements. + alignas(slot_type) unsigned char tmp[sizeof(slot_type)]; + DropDeletesWithoutResize(common(), GetPolicyFunctions(), tmp); } // Called whenever the table *might* need to conditionally grow. @@ -2074,14 +2355,13 @@ class raw_hash_set { // growth is unnecessary, because vacating tombstones is beneficial for // performance in the long-run. void rehash_and_grow_if_necessary() { - if (capacity_ == 0) { - resize(1); - } else if (capacity_ > Group::kWidth && - // Do these calcuations in 64-bit to avoid overflow. - size() * uint64_t{32} <= capacity_ * uint64_t{25}) { + const size_t cap = capacity(); + if (cap > Group::kWidth && + // Do these calcuations in 64-bit to avoid overflow. + size() * uint64_t{32} <= cap* uint64_t{25}) { // Squash DELETED without growing if there is enough capacity. // - // Rehash in place if the current size is <= 25/32 of capacity_. + // Rehash in place if the current size is <= 25/32 of capacity. // Rationale for such a high factor: 1) drop_deletes_without_resize() is // faster than resize, and 2) it takes quite a bit of work to add // tombstones. In the worst case, seems to take approximately 4 @@ -2099,8 +2379,8 @@ class raw_hash_set { // // Here is output of an experiment using the BM_CacheInSteadyState // benchmark running the old case (where we rehash-in-place only if we can - // reclaim at least 7/16*capacity_) vs. this code (which rehashes in place - // if we can recover 3/32*capacity_). + // reclaim at least 7/16*capacity) vs. this code (which rehashes in place + // if we can recover 3/32*capacity). // // Note that although in the worst-case number of rehashes jumped up from // 15 to 190, but the number of operations per second is almost the same. @@ -2123,23 +2403,24 @@ class raw_hash_set { drop_deletes_without_resize(); } else { // Otherwise grow the container. - resize(capacity_ * 2 + 1); + resize(NextCapacity(cap)); } } bool has_element(const value_type& elem) const { size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem); - auto seq = probe(ctrl_, hash, capacity_); + auto seq = probe(common(), hash); + const ctrl_t* ctrl = control(); while (true) { - Group g{ctrl_ + seq.offset()}; + Group g{ctrl + seq.offset()}; for (uint32_t i : g.Match(H2(hash))) { - if (Y_ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) == - elem)) + if (Y_ABSL_PREDICT_TRUE( + PolicyTraits::element(slot_array() + seq.offset(i)) == elem)) return true; } if (Y_ABSL_PREDICT_TRUE(g.MaskEmpty())) return false; seq.next(); - assert(seq.index() <= capacity_ && "full table!"); + assert(seq.index() <= capacity() && "full table!"); } return false; } @@ -2164,18 +2445,19 @@ class raw_hash_set { std::pair<size_t, bool> find_or_prepare_insert(const K& key) { prefetch_heap_block(); auto hash = hash_ref()(key); - auto seq = probe(ctrl_, hash, capacity_); + auto seq = probe(common(), hash); + const ctrl_t* ctrl = control(); while (true) { - Group g{ctrl_ + seq.offset()}; + Group g{ctrl + seq.offset()}; for (uint32_t i : g.Match(H2(hash))) { if (Y_ABSL_PREDICT_TRUE(PolicyTraits::apply( EqualElement<K>{key, eq_ref()}, - PolicyTraits::element(slots_ + seq.offset(i))))) + PolicyTraits::element(slot_array() + seq.offset(i))))) return {seq.offset(i), false}; } if (Y_ABSL_PREDICT_TRUE(g.MaskEmpty())) break; seq.next(); - assert(seq.index() <= capacity_ && "full table!"); + assert(seq.index() <= capacity() && "full table!"); } return {prepare_insert(hash), true}; } @@ -2185,16 +2467,24 @@ class raw_hash_set { // // REQUIRES: At least one non-full slot available. size_t prepare_insert(size_t hash) Y_ABSL_ATTRIBUTE_NOINLINE { - auto target = find_first_non_full(ctrl_, hash, capacity_); - if (Y_ABSL_PREDICT_FALSE(growth_left() == 0 && - !IsDeleted(ctrl_[target.offset]))) { + const bool rehash_for_bug_detection = + common().should_rehash_for_bug_detection_on_insert(); + if (rehash_for_bug_detection) { + // Move to a different heap allocation in order to detect bugs. + const size_t cap = capacity(); + resize(growth_left() > 0 ? cap : NextCapacity(cap)); + } + auto target = find_first_non_full(common(), hash); + if (!rehash_for_bug_detection && + Y_ABSL_PREDICT_FALSE(growth_left() == 0 && + !IsDeleted(control()[target.offset]))) { rehash_and_grow_if_necessary(); - target = find_first_non_full(ctrl_, hash, capacity_); + target = find_first_non_full(common(), hash); } - ++size_; - growth_left() -= IsEmpty(ctrl_[target.offset]); - SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_, - sizeof(slot_type)); + ++common().size_; + growth_left() -= IsEmpty(control()[target.offset]); + SetCtrl(common(), target.offset, H2(hash), sizeof(slot_type)); + common().maybe_increment_generation_on_insert(); infoz().RecordInsert(hash, target.probe_length); return target.offset; } @@ -2209,7 +2499,7 @@ class raw_hash_set { // POSTCONDITION: *m.iterator_at(i) == value_type(forward<Args>(args)...). template <class... Args> void emplace_at(size_t i, Args&&... args) { - PolicyTraits::construct(&alloc_ref(), slots_ + i, + PolicyTraits::construct(&alloc_ref(), slot_array() + i, std::forward<Args>(args)...); assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) == @@ -2217,16 +2507,16 @@ class raw_hash_set { "constructed value does not match the lookup key"); } - iterator iterator_at(size_t i) { return {ctrl_ + i, slots_ + i}; } - const_iterator iterator_at(size_t i) const { return {ctrl_ + i, slots_ + i}; } + iterator iterator_at(size_t i) { + return {control() + i, slot_array() + i, common().generation_ptr()}; + } + const_iterator iterator_at(size_t i) const { + return {control() + i, slot_array() + i, common().generation_ptr()}; + } private: friend struct RawHashSetTestOnlyAccess; - void reset_growth_left() { - growth_left() = CapacityToGrowth(capacity()) - size_; - } - // The number of slots we can still fill without needing to rehash. // // This is stored separately due to tombstones: we do not include tombstones @@ -2237,49 +2527,76 @@ class raw_hash_set { // side-effect. // // See `CapacityToGrowth()`. - size_t& growth_left() { return settings_.template get<0>(); } + size_t& growth_left() { return common().growth_left(); } // Prefetch the heap-allocated memory region to resolve potential TLB misses. // This is intended to overlap with execution of calculating the hash for a // key. - void prefetch_heap_block() const { - base_internal::PrefetchT2(ctrl_); - } + void prefetch_heap_block() const { base_internal::PrefetchT2(control()); } + + CommonFields& common() { return settings_.template get<0>(); } + const CommonFields& common() const { return settings_.template get<0>(); } - HashtablezInfoHandle& infoz() { return settings_.template get<1>(); } + ctrl_t* control() const { return common().control_; } + slot_type* slot_array() const { + return static_cast<slot_type*>(common().slots_); + } + HashtablezInfoHandle& infoz() { return common().infoz(); } - hasher& hash_ref() { return settings_.template get<2>(); } - const hasher& hash_ref() const { return settings_.template get<2>(); } - key_equal& eq_ref() { return settings_.template get<3>(); } - const key_equal& eq_ref() const { return settings_.template get<3>(); } - allocator_type& alloc_ref() { return settings_.template get<4>(); } + hasher& hash_ref() { return settings_.template get<1>(); } + const hasher& hash_ref() const { return settings_.template get<1>(); } + key_equal& eq_ref() { return settings_.template get<2>(); } + const key_equal& eq_ref() const { return settings_.template get<2>(); } + allocator_type& alloc_ref() { return settings_.template get<3>(); } const allocator_type& alloc_ref() const { - return settings_.template get<4>(); + return settings_.template get<3>(); } - // TODO(alkis): Investigate removing some of these fields: - // - ctrl/slots can be derived from each other - // - size can be moved into the slot array + // Make type-specific functions for this type's PolicyFunctions struct. + static size_t hash_slot_fn(void* set, void* slot) { + auto* h = static_cast<raw_hash_set*>(set); + return PolicyTraits::apply( + HashElement{h->hash_ref()}, + PolicyTraits::element(static_cast<slot_type*>(slot))); + } + static void transfer_slot_fn(void* set, void* dst, void* src) { + auto* h = static_cast<raw_hash_set*>(set); + PolicyTraits::transfer(&h->alloc_ref(), static_cast<slot_type*>(dst), + static_cast<slot_type*>(src)); + } + // Note: dealloc_fn will only be used if we have a non-standard allocator. + static void dealloc_fn(void* set, const PolicyFunctions&, ctrl_t* ctrl, + void* slot_mem, size_t n) { + auto* h = static_cast<raw_hash_set*>(set); - // The control bytes (and, also, a pointer to the base of the backing array). - // - // This contains `capacity_ + 1 + NumClonedBytes()` entries, even - // when the table is empty (hence EmptyGroup). - ctrl_t* ctrl_ = EmptyGroup(); - // The beginning of the slots, located at `SlotOffset()` bytes after - // `ctrl_`. May be null for empty tables. - slot_type* slots_ = nullptr; + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slot_mem, sizeof(slot_type) * n); - // The number of filled slots. - size_t size_ = 0; + Deallocate<alignof(slot_type)>( + &h->alloc_ref(), ctrl, + AllocSize(n, sizeof(slot_type), alignof(slot_type))); + } + + static const PolicyFunctions& GetPolicyFunctions() { + static constexpr PolicyFunctions value = { + sizeof(slot_type), + &raw_hash_set::hash_slot_fn, + PolicyTraits::transfer_uses_memcpy() + ? TransferRelocatable<sizeof(slot_type)> + : &raw_hash_set::transfer_slot_fn, + (std::is_same<SlotAlloc, std::allocator<slot_type>>::value + ? &DeallocateStandard<alignof(slot_type)> + : &raw_hash_set::dealloc_fn), + }; + return value; + } - // The total number of available slots. - size_t capacity_ = 0; - y_absl::container_internal::CompressedTuple<size_t /* growth_left */, - HashtablezInfoHandle, hasher, - key_equal, allocator_type> - settings_{0u, HashtablezInfoHandle{}, hasher{}, key_equal{}, - allocator_type{}}; + // Bundle together CommonFields plus other objects which might be empty. + // CompressedTuple will ensure that sizeof is not affected by any of the empty + // fields that occur after CommonFields. + y_absl::container_internal::CompressedTuple<CommonFields, hasher, key_equal, + allocator_type> + settings_{CommonFields{}, hasher{}, key_equal{}, allocator_type{}}; }; // Erases all elements that satisfy the predicate `pred` from the container `c`. @@ -2307,14 +2624,15 @@ struct HashtableDebugAccess<Set, y_absl::void_t<typename Set::raw_hash_set>> { const typename Set::key_type& key) { size_t num_probes = 0; size_t hash = set.hash_ref()(key); - auto seq = probe(set.ctrl_, hash, set.capacity_); + auto seq = probe(set.common(), hash); + const ctrl_t* ctrl = set.control(); while (true) { - container_internal::Group g{set.ctrl_ + seq.offset()}; + container_internal::Group g{ctrl + seq.offset()}; for (uint32_t i : g.Match(container_internal::H2(hash))) { if (Traits::apply( typename Set::template EqualElement<typename Set::key_type>{ key, set.eq_ref()}, - Traits::element(set.slots_ + seq.offset(i)))) + Traits::element(set.slot_array() + seq.offset(i)))) return num_probes; ++num_probes; } @@ -2325,7 +2643,7 @@ struct HashtableDebugAccess<Set, y_absl::void_t<typename Set::raw_hash_set>> { } static size_t AllocatedByteSize(const Set& c) { - size_t capacity = c.capacity_; + size_t capacity = c.capacity(); if (capacity == 0) return 0; size_t m = AllocSize(capacity, sizeof(Slot), alignof(Slot)); @@ -2333,9 +2651,10 @@ struct HashtableDebugAccess<Set, y_absl::void_t<typename Set::raw_hash_set>> { if (per_slot != ~size_t{}) { m += per_slot * c.size(); } else { + const ctrl_t* ctrl = c.control(); for (size_t i = 0; i != capacity; ++i) { - if (container_internal::IsFull(c.ctrl_[i])) { - m += Traits::space_used(c.slots_ + i); + if (container_internal::IsFull(ctrl[i])) { + m += Traits::space_used(c.slot_array() + i); } } } @@ -2360,6 +2679,7 @@ struct HashtableDebugAccess<Set, y_absl::void_t<typename Set::raw_hash_set>> { Y_ABSL_NAMESPACE_END } // namespace y_absl +#undef Y_ABSL_SWISSTABLE_ENABLE_GENERATIONS #undef Y_ABSL_INTERNAL_ASSERT_IS_FULL #endif // Y_ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.cc new file mode 100644 index 0000000000..62cf321cc9 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.cc @@ -0,0 +1,99 @@ +// Copyright 2022 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 "y_absl/crc/crc32c.h" + +#include <cstdint> + +#include "y_absl/crc/internal/crc.h" +#include "y_absl/crc/internal/crc32c.h" +#include "y_absl/crc/internal/crc_memcpy.h" +#include "y_absl/strings/string_view.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN + +namespace { + +const crc_internal::CRC* CrcEngine() { + static const crc_internal::CRC* engine = crc_internal::CRC::Crc32c(); + return engine; +} + +constexpr uint32_t kCRC32Xor = 0xffffffffU; + +} // namespace + +namespace crc_internal { + +crc32c_t UnextendCrc32cByZeroes(crc32c_t initial_crc, size_t length) { + uint32_t crc = static_cast<uint32_t>(initial_crc) ^ kCRC32Xor; + CrcEngine()->UnextendByZeroes(&crc, length); + return static_cast<crc32c_t>(crc ^ kCRC32Xor); +} + +// Called by `y_absl::ExtendCrc32c()` on strings with size > 64 or when hardware +// CRC32C support is missing. +crc32c_t ExtendCrc32cInternal(crc32c_t initial_crc, + y_absl::string_view buf_to_add) { + uint32_t crc = static_cast<uint32_t>(initial_crc) ^ kCRC32Xor; + CrcEngine()->Extend(&crc, buf_to_add.data(), buf_to_add.size()); + return static_cast<crc32c_t>(crc ^ kCRC32Xor); +} + +} // namespace crc_internal + +crc32c_t ComputeCrc32c(y_absl::string_view buf) { + return ExtendCrc32c(crc32c_t{0}, buf); +} + +crc32c_t ExtendCrc32cByZeroes(crc32c_t initial_crc, size_t length) { + uint32_t crc = static_cast<uint32_t>(initial_crc) ^ kCRC32Xor; + CrcEngine()->ExtendByZeroes(&crc, length); + return static_cast<crc32c_t>(crc ^ kCRC32Xor); +} + +crc32c_t ConcatCrc32c(crc32c_t lhs_crc, crc32c_t rhs_crc, size_t rhs_len) { + uint32_t result = static_cast<uint32_t>(lhs_crc); + CrcEngine()->ExtendByZeroes(&result, rhs_len); + return crc32c_t{result ^ static_cast<uint32_t>(rhs_crc)}; +} + +crc32c_t RemoveCrc32cPrefix(crc32c_t crc_a, crc32c_t crc_ab, size_t length_b) { + return ConcatCrc32c(crc_a, crc_ab, length_b); +} + +crc32c_t MemcpyCrc32c(void* dest, const void* src, size_t count, + crc32c_t initial_crc) { + return static_cast<crc32c_t>( + crc_internal::Crc32CAndCopy(dest, src, count, initial_crc, false)); +} + +// Remove a Suffix of given size from a buffer +// +// Given a CRC32C of an existing buffer, `full_string_crc`; the CRC32C of a +// suffix of that buffer to remove, `suffix_crc`; and suffix buffer's length, +// `suffix_len` return the CRC32C of the buffer with suffix removed +// +// This operation has a runtime cost of O(log(`suffix_len`)) +crc32c_t RemoveCrc32cSuffix(crc32c_t full_string_crc, crc32c_t suffix_crc, + size_t suffix_len) { + uint32_t result = static_cast<uint32_t>(full_string_crc) ^ + static_cast<uint32_t>(suffix_crc); + CrcEngine()->UnextendByZeroes(&result, suffix_len); + return crc32c_t{result}; +} + +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.h new file mode 100644 index 0000000000..fb25687e28 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.h @@ -0,0 +1,183 @@ +// Copyright 2022 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: crc32c.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for computing CRC32C values as checksums +// for arbitrary sequences of bytes provided as a string buffer. +// +// The API includes the basic functions for computing such CRC32C values and +// some utility functions for performing more efficient mathematical +// computations using an existing checksum. +#ifndef Y_ABSL_CRC_CRC32C_H_ +#define Y_ABSL_CRC_CRC32C_H_ + +#include <cstdint> +#include <ostream> + +#include "y_absl/crc/internal/crc32c_inline.h" +#include "y_absl/strings/string_view.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN + +//----------------------------------------------------------------------------- +// crc32c_t +//----------------------------------------------------------------------------- + +// `crc32c_t` defines a strongly-typed integer for holding a CRC32C value. +// +// Some operators are intentionally omitted. Only equality operators are defined +// so that `crc32c_t` can be directly compared. Methods for putting `crc32c_t` +// directly into a set are omitted because this is bug-prone due to checksum +// collisions. Use an explicit conversion to the `uint32_t` space for operations +// that treat `crc32c_t` as an integer. +class crc32c_t final { + public: + crc32c_t() = default; + constexpr explicit crc32c_t(uint32_t crc) : crc_(crc) {} + + crc32c_t(const crc32c_t&) = default; + crc32c_t& operator=(const crc32c_t&) = default; + + explicit operator uint32_t() const { return crc_; } + + friend bool operator==(crc32c_t lhs, crc32c_t rhs) { + return static_cast<uint32_t>(lhs) == static_cast<uint32_t>(rhs); + } + + friend bool operator!=(crc32c_t lhs, crc32c_t rhs) { return !(lhs == rhs); } + + private: + uint32_t crc_; +}; + +namespace crc_internal { +// Non-inline code path for `y_absl::ExtendCrc32c()`. Do not call directly. +// Call `y_absl::ExtendCrc32c()` (defined below) instead. +crc32c_t ExtendCrc32cInternal(crc32c_t initial_crc, + y_absl::string_view buf_to_add); +} // namespace crc_internal + +// ----------------------------------------------------------------------------- +// CRC32C Computation Functions +// ----------------------------------------------------------------------------- + +// ComputeCrc32c() +// +// Returns the CRC32C value of the provided string. +crc32c_t ComputeCrc32c(y_absl::string_view buf); + +// ExtendCrc32c() +// +// Computes a CRC32C value from an `initial_crc` CRC32C value including the +// `buf_to_add` bytes of an additional buffer. Using this function is more +// efficient than computing a CRC32C value for the combined buffer from +// scratch. +// +// Note: `ExtendCrc32c` with an initial_crc of 0 is equivalent to +// `ComputeCrc32c`. +// +// This operation has a runtime cost of O(`buf_to_add.size()`) +inline crc32c_t ExtendCrc32c(crc32c_t initial_crc, + y_absl::string_view buf_to_add) { + // Approximately 75% of calls have size <= 64. + if (buf_to_add.size() <= 64) { + uint32_t crc = static_cast<uint32_t>(initial_crc); + if (crc_internal::ExtendCrc32cInline(&crc, buf_to_add.data(), + buf_to_add.size())) { + return crc32c_t{crc}; + } + } + return crc_internal::ExtendCrc32cInternal(initial_crc, buf_to_add); +} + +// ExtendCrc32cByZeroes() +// +// Computes a CRC32C value for a buffer with an `initial_crc` CRC32C value, +// where `length` bytes with a value of 0 are appended to the buffer. Using this +// function is more efficient than computing a CRC32C value for the combined +// buffer from scratch. +// +// This operation has a runtime cost of O(log(`length`)) +crc32c_t ExtendCrc32cByZeroes(crc32c_t initial_crc, size_t length); + +// MemcpyCrc32c() +// +// Copies `src` to `dest` using `memcpy()` semantics, returning the CRC32C +// value of the copied buffer. +// +// Using `MemcpyCrc32c()` is potentially faster than performing the `memcpy()` +// and `ComputeCrc32c()` operations separately. +crc32c_t MemcpyCrc32c(void* dest, const void* src, size_t count, + crc32c_t initial_crc = crc32c_t{0}); + +// ----------------------------------------------------------------------------- +// CRC32C Arithmetic Functions +// ----------------------------------------------------------------------------- + +// The following functions perform arithmetic on CRC32C values, which are +// generally more efficient than recalculating any given result's CRC32C value. + +// ConcatCrc32c() +// +// Calculates the CRC32C value of two buffers with known CRC32C values +// concatenated together. +// +// Given a buffer with CRC32C value `crc1` and a buffer with +// CRC32C value `crc2` and length, `crc2_length`, returns the CRC32C value of +// the concatenation of these two buffers. +// +// This operation has a runtime cost of O(log(`crc2_length`)). +crc32c_t ConcatCrc32c(crc32c_t crc1, crc32c_t crc2, size_t crc2_length); + +// RemoveCrc32cPrefix() +// +// Calculates the CRC32C value of an existing buffer with a series of bytes +// (the prefix) removed from the beginning of that buffer. +// +// Given the CRC32C value of an existing buffer, `full_string_crc`; The CRC32C +// value of a prefix of that buffer, `prefix_crc`; and the length of the buffer +// with the prefix removed, `remaining_string_length` , return the CRC32C +// value of the buffer with the prefix removed. +// +// This operation has a runtime cost of O(log(`remaining_string_length`)). +crc32c_t RemoveCrc32cPrefix(crc32c_t prefix_crc, crc32c_t full_string_crc, + size_t remaining_string_length); +// RemoveCrc32cSuffix() +// +// Calculates the CRC32C value of an existing buffer with a series of bytes +// (the suffix) removed from the end of that buffer. +// +// Given a CRC32C value of an existing buffer `full_string_crc`, the CRC32C +// value of the suffix to remove `suffix_crc`, and the length of that suffix +// `suffix_len`, returns the CRC32C value of the buffer with suffix removed. +// +// This operation has a runtime cost of O(log(`suffix_len`)) +crc32c_t RemoveCrc32cSuffix(crc32c_t full_string_crc, crc32c_t suffix_crc, + size_t suffix_length); + +// operator<< +// +// Streams the CRC32C value `crc` to the stream `os`. +inline std::ostream& operator<<(std::ostream& os, crc32c_t crc) { + return os << static_cast<uint32_t>(crc); +} + +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_CRC32C_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.cc new file mode 100644 index 0000000000..8c8f8d5580 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.cc @@ -0,0 +1,256 @@ +// Copyright 2022 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 "y_absl/crc/internal/cpu_detect.h" + +#include <cstdint> +#include <util/generic/string.h> + +#include "y_absl/base/config.h" + +#if defined(__aarch64__) && defined(__linux__) +#include <asm/hwcap.h> +#include <sys/auxv.h> +#endif + +#if defined(_WIN32) || defined(_WIN64) +#include <intrin.h> +#endif + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +#if defined(__x86_64__) || defined(_M_X64) + +namespace { + +#if !defined(_WIN32) && !defined(_WIN64) +// MSVC defines this function for us. +// https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex +static void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile("cpuid \n\t" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type), "c"(0)); +} +#endif // !defined(_WIN32) && !defined(_WIN64) + +enum class Vendor { + kUnknown, + kIntel, + kAmd, +}; + +Vendor GetVendor() { + // Get the vendor string (issue CPUID with eax = 0). + int cpu_info[4]; + __cpuid(cpu_info, 0); + + TString vendor; + vendor.append(reinterpret_cast<char*>(&cpu_info[1]), 4); + vendor.append(reinterpret_cast<char*>(&cpu_info[3]), 4); + vendor.append(reinterpret_cast<char*>(&cpu_info[2]), 4); + if (vendor == "GenuineIntel") { + return Vendor::kIntel; + } else if (vendor == "AuthenticAMD") { + return Vendor::kAmd; + } else { + return Vendor::kUnknown; + } +} + +CpuType GetIntelCpuType() { + // To get general information and extended features we send eax = 1 and + // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx. + // (See Intel 64 and IA-32 Architectures Software Developer's Manual + // Volume 2A: Instruction Set Reference, A-M CPUID). + // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html + // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex + int cpu_info[4]; + __cpuid(cpu_info, 1); + + // Response in eax bits as follows: + // 0-3 (stepping id) + // 4-7 (model number), + // 8-11 (family code), + // 12-13 (processor type), + // 16-19 (extended model) + // 20-27 (extended family) + + int family = (cpu_info[0] >> 8) & 0x0f; + int model_num = (cpu_info[0] >> 4) & 0x0f; + int ext_family = (cpu_info[0] >> 20) & 0xff; + int ext_model_num = (cpu_info[0] >> 16) & 0x0f; + + int brand_id = cpu_info[1] & 0xff; + + // Process the extended family and model info if necessary + if (family == 0x0f) { + family += ext_family; + } + + if (family == 0x0f || family == 0x6) { + model_num += (ext_model_num << 4); + } + + switch (brand_id) { + case 0: // no brand ID, so parse CPU family/model + switch (family) { + case 6: // Most PentiumIII processors are in this category + switch (model_num) { + case 0x2c: // Westmere: Gulftown + return CpuType::kIntelWestmere; + case 0x2d: // Sandybridge + return CpuType::kIntelSandybridge; + case 0x3e: // Ivybridge + return CpuType::kIntelIvybridge; + case 0x3c: // Haswell (client) + case 0x3f: // Haswell + return CpuType::kIntelHaswell; + case 0x4f: // Broadwell + case 0x56: // BroadwellDE + return CpuType::kIntelBroadwell; + case 0x55: // Skylake Xeon + if ((cpu_info[0] & 0x0f) < 5) { // stepping < 5 is skylake + return CpuType::kIntelSkylakeXeon; + } else { // stepping >= 5 is cascadelake + return CpuType::kIntelCascadelakeXeon; + } + case 0x5e: // Skylake (client) + return CpuType::kIntelSkylake; + default: + return CpuType::kUnknown; + } + default: + return CpuType::kUnknown; + } + default: + return CpuType::kUnknown; + } +} + +CpuType GetAmdCpuType() { + // To get general information and extended features we send eax = 1 and + // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx. + // (See Intel 64 and IA-32 Architectures Software Developer's Manual + // Volume 2A: Instruction Set Reference, A-M CPUID). + // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex + int cpu_info[4]; + __cpuid(cpu_info, 1); + + // Response in eax bits as follows: + // 0-3 (stepping id) + // 4-7 (model number), + // 8-11 (family code), + // 12-13 (processor type), + // 16-19 (extended model) + // 20-27 (extended family) + + int family = (cpu_info[0] >> 8) & 0x0f; + int model_num = (cpu_info[0] >> 4) & 0x0f; + int ext_family = (cpu_info[0] >> 20) & 0xff; + int ext_model_num = (cpu_info[0] >> 16) & 0x0f; + + if (family == 0x0f) { + family += ext_family; + model_num += (ext_model_num << 4); + } + + switch (family) { + case 0x17: + switch (model_num) { + case 0x0: // Stepping Ax + case 0x1: // Stepping Bx + return CpuType::kAmdNaples; + case 0x30: // Stepping Ax + case 0x31: // Stepping Bx + return CpuType::kAmdRome; + default: + return CpuType::kUnknown; + } + break; + case 0x19: + switch (model_num) { + case 0x1: // Stepping B0 + return CpuType::kAmdMilan; + default: + return CpuType::kUnknown; + } + break; + default: + return CpuType::kUnknown; + } +} + +} // namespace + +CpuType GetCpuType() { + switch (GetVendor()) { + case Vendor::kIntel: + return GetIntelCpuType(); + case Vendor::kAmd: + return GetAmdCpuType(); + default: + return CpuType::kUnknown; + } +} + +bool SupportsArmCRC32PMULL() { return false; } + +#elif defined(__aarch64__) && defined(__linux__) + +#ifndef HWCAP_CPUID +#define HWCAP_CPUID (1 << 11) +#endif + +#define Y_ABSL_INTERNAL_AARCH64_ID_REG_READ(id, val) \ + asm("mrs %0, " #id : "=r"(val)) + +CpuType GetCpuType() { + // MIDR_EL1 is not visible to EL0, however the access will be emulated by + // linux if AT_HWCAP has HWCAP_CPUID set. + // + // This method will be unreliable on heterogeneous computing systems (ex: + // big.LITTLE) since the value of MIDR_EL1 will change based on the calling + // thread. + uint64_t hwcaps = getauxval(AT_HWCAP); + if (hwcaps & HWCAP_CPUID) { + uint64_t midr = 0; + Y_ABSL_INTERNAL_AARCH64_ID_REG_READ(MIDR_EL1, midr); + uint32_t implementer = (midr >> 24) & 0xff; + uint32_t part_number = (midr >> 4) & 0xfff; + if (implementer == 0x41 && part_number == 0xd0c) { + return CpuType::kArmNeoverseN1; + } + } + return CpuType::kUnknown; +} + +bool SupportsArmCRC32PMULL() { + uint64_t hwcaps = getauxval(AT_HWCAP); + return (hwcaps & HWCAP_CRC32) && (hwcaps & HWCAP_PMULL); +} + +#else + +CpuType GetCpuType() { return CpuType::kUnknown; } + +bool SupportsArmCRC32PMULL() { return false; } + +#endif + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.h new file mode 100644 index 0000000000..55539d9a52 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.h @@ -0,0 +1,57 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_CPU_DETECT_H_ +#define Y_ABSL_CRC_INTERNAL_CPU_DETECT_H_ + +#include "y_absl/base/config.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +// Enumeration of architectures that we have special-case tuning parameters for. +// This set may change over time. +enum class CpuType { + kUnknown, + kIntelHaswell, + kAmdRome, + kAmdNaples, + kAmdMilan, + kIntelCascadelakeXeon, + kIntelSkylakeXeon, + kIntelBroadwell, + kIntelSkylake, + kIntelIvybridge, + kIntelSandybridge, + kIntelWestmere, + kArmNeoverseN1, +}; + +// Returns the type of host CPU this code is running on. Returns kUnknown if +// the host CPU is of unknown type, or if detection otherwise fails. +CpuType GetCpuType(); + +// Returns whether the host CPU supports the CPU features needed for our +// accelerated implementations. The CpuTypes enumerated above apart from +// kUnknown support the required features. On unknown CPUs, we can use +// this to see if it's safe to use hardware acceleration, though without any +// tuning. +bool SupportsArmCRC32PMULL(); + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_CPU_DETECT_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.cc new file mode 100644 index 0000000000..a5b04de0bb --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.cc @@ -0,0 +1,468 @@ +// Copyright 2022 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 of CRCs (aka Rabin Fingerprints). +// Treats the input as a polynomial with coefficients in Z(2), +// and finds the remainder when divided by an irreducible polynomial +// of the appropriate length. +// It handles all CRC sizes from 8 to 128 bits. +// It's somewhat complicated by having separate implementations optimized for +// CRC's <=32 bits, <= 64 bits, and <= 128 bits. +// The input string is prefixed with a "1" bit, and has "degree" "0" bits +// appended to it before the remainder is found. This ensures that +// short strings are scrambled somewhat and that strings consisting +// of all nulls have a non-zero CRC. +// +// Uses the "interleaved word-by-word" method from +// "Everything we know about CRC but afraid to forget" by Andrew Kadatch +// and Bob Jenkins, +// http://crcutil.googlecode.com/files/crc-doc.1.0.pdf +// +// The idea is to compute kStride CRCs simultaneously, allowing the +// processor to more effectively use multiple execution units. Each of +// the CRCs is calculated on one word of data followed by kStride - 1 +// words of zeroes; the CRC starting points are staggered by one word. +// Assuming a stride of 4 with data words "ABCDABCDABCD", the first +// CRC is over A000A000A, the second over 0B000B000B, and so on. +// The CRC of the whole data is then calculated by properly aligning the +// CRCs by appending zeroes until the data lengths agree then XORing +// the CRCs. + +#include "y_absl/crc/internal/crc.h" + +#include <cstdint> + +#include "y_absl/base/internal/endian.h" +#include "y_absl/base/internal/prefetch.h" +#include "y_absl/base/internal/raw_logging.h" +#include "y_absl/crc/internal/crc_internal.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +namespace { + +// Constants +#if defined(__i386__) || defined(__x86_64__) +constexpr bool kNeedAlignedLoads = false; +#else +constexpr bool kNeedAlignedLoads = true; +#endif + +// We express the number of zeroes as a number in base ZEROES_BASE. By +// pre-computing the zero extensions for all possible components of such an +// expression (numbers in a form a*ZEROES_BASE**b), we can calculate the +// resulting extension by multiplying the extensions for individual components +// using log_{ZEROES_BASE}(num_zeroes) polynomial multiplications. The tables of +// zero extensions contain (ZEROES_BASE - 1) * (log_{ZEROES_BASE}(64)) entries. +constexpr int ZEROES_BASE_LG = 4; // log_2(ZEROES_BASE) +constexpr int ZEROES_BASE = (1 << ZEROES_BASE_LG); // must be a power of 2 + +constexpr uint32_t kCrc32cPoly = 0x82f63b78; + +uint32_t ReverseBits(uint32_t bits) { + bits = (bits & 0xaaaaaaaau) >> 1 | (bits & 0x55555555u) << 1; + bits = (bits & 0xccccccccu) >> 2 | (bits & 0x33333333u) << 2; + bits = (bits & 0xf0f0f0f0u) >> 4 | (bits & 0x0f0f0f0fu) << 4; + return y_absl::gbswap_32(bits); +} + +// Polynomial long multiplication mod the polynomial of degree 32. +void PolyMultiply(uint32_t* val, uint32_t m, uint32_t poly) { + uint32_t l = *val; + uint32_t result = 0; + auto onebit = uint32_t{0x80000000u}; + for (uint32_t one = onebit; one != 0; one >>= 1) { + if ((l & one) != 0) { + result ^= m; + } + if (m & 1) { + m = (m >> 1) ^ poly; + } else { + m >>= 1; + } + } + *val = result; +} +} // namespace + +void CRCImpl::FillWordTable(uint32_t poly, uint32_t last, int word_size, + Uint32By256* t) { + for (int j = 0; j != word_size; j++) { // for each byte of extension.... + t[j][0] = 0; // a zero has no effect + for (int i = 128; i != 0; i >>= 1) { // fill in entries for powers of 2 + if (j == 0 && i == 128) { + t[j][i] = last; // top bit in last byte is given + } else { + // each successive power of two is derived from the previous + // one, either in this table, or the last table + uint32_t pred; + if (i == 128) { + pred = t[j - 1][1]; + } else { + pred = t[j][i << 1]; + } + // Advance the CRC by one bit (multiply by X, and take remainder + // through one step of polynomial long division) + if (pred & 1) { + t[j][i] = (pred >> 1) ^ poly; + } else { + t[j][i] = pred >> 1; + } + } + } + // CRCs have the property that CRC(a xor b) == CRC(a) xor CRC(b) + // so we can make all the tables for non-powers of two by + // xoring previously created entries. + for (int i = 2; i != 256; i <<= 1) { + for (int k = i + 1; k != (i << 1); k++) { + t[j][k] = t[j][i] ^ t[j][k - i]; + } + } + } +} + +int CRCImpl::FillZeroesTable(uint32_t poly, Uint32By256* t) { + uint32_t inc = 1; + inc <<= 31; + + // Extend by one zero bit. We know degree > 1 so (inc & 1) == 0. + inc >>= 1; + + // Now extend by 2, 4, and 8 bits, so now `inc` is extended by one zero byte. + for (int i = 0; i < 3; ++i) { + PolyMultiply(&inc, inc, poly); + } + + int j = 0; + for (uint64_t inc_len = 1; inc_len != 0; inc_len <<= ZEROES_BASE_LG) { + // Every entry in the table adds an additional inc_len zeroes. + uint32_t v = inc; + for (int a = 1; a != ZEROES_BASE; a++) { + t[0][j] = v; + PolyMultiply(&v, inc, poly); + j++; + } + inc = v; + } + Y_ABSL_RAW_CHECK(j <= 256, ""); + return j; +} + +// Internal version of the "constructor". +CRCImpl* CRCImpl::NewInternal() { + // Find an accelearated implementation first. + CRCImpl* result = TryNewCRC32AcceleratedX86ARMCombined(); + + // Fall back to generic implementions if no acceleration is available. + if (result == nullptr) { + result = new CRC32(); + } + + result->InitTables(); + + return result; +} + +// The CRC of the empty string is always the CRC polynomial itself. +void CRCImpl::Empty(uint32_t* crc) const { *crc = kCrc32cPoly; } + +// The 32-bit implementation + +void CRC32::InitTables() { + // Compute the table for extending a CRC by one byte. + Uint32By256* t = new Uint32By256[4]; + FillWordTable(kCrc32cPoly, kCrc32cPoly, 1, t); + for (int i = 0; i != 256; i++) { + this->table0_[i] = t[0][i]; + } + + // Construct a table for updating the CRC by 4 bytes data followed by + // 12 bytes of zeroes. + // + // Note: the data word size could be larger than the CRC size; it might + // be slightly faster to use a 64-bit data word, but doing so doubles the + // table size. + uint32_t last = kCrc32cPoly; + const size_t size = 12; + for (size_t i = 0; i < size; ++i) { + last = (last >> 8) ^ this->table0_[last & 0xff]; + } + FillWordTable(kCrc32cPoly, last, 4, t); + for (size_t b = 0; b < 4; ++b) { + for (int i = 0; i < 256; ++i) { + this->table_[b][i] = t[b][i]; + } + } + + int j = FillZeroesTable(kCrc32cPoly, t); + Y_ABSL_RAW_CHECK(j <= static_cast<int>(Y_ABSL_ARRAYSIZE(this->zeroes_)), ""); + for (int i = 0; i < j; i++) { + this->zeroes_[i] = t[0][i]; + } + + delete[] t; + + // Build up tables for _reversing_ the operation of doing CRC operations on + // zero bytes. + + // In C++, extending `crc` by a single zero bit is done by the following: + // (A) bool low_bit_set = (crc & 1); + // crc >>= 1; + // if (low_bit_set) crc ^= kCrc32cPoly; + // + // In particular note that the high bit of `crc` after this operation will be + // set if and only if the low bit of `crc` was set before it. This means that + // no information is lost, and the operation can be reversed, as follows: + // (B) bool high_bit_set = (crc & 0x80000000u); + // if (high_bit_set) crc ^= kCrc32cPoly; + // crc <<= 1; + // if (high_bit_set) crc ^= 1; + // + // Or, equivalently: + // (C) bool high_bit_set = (crc & 0x80000000u); + // crc <<= 1; + // if (high_bit_set) crc ^= ((kCrc32cPoly << 1) ^ 1); + // + // The last observation is, if we store our checksums in variable `rcrc`, + // with order of the bits reversed, the inverse operation becomes: + // (D) bool low_bit_set = (rcrc & 1); + // rcrc >>= 1; + // if (low_bit_set) rcrc ^= ReverseBits((kCrc32cPoly << 1) ^ 1) + // + // This is the same algorithm (A) that we started with, only with a different + // polynomial bit pattern. This means that by building up our tables with + // this alternate polynomial, we can apply the CRC algorithms to a + // bit-reversed CRC checksum to perform inverse zero-extension. + + const uint32_t kCrc32cUnextendPoly = + ReverseBits(static_cast<uint32_t>((kCrc32cPoly << 1) ^ 1)); + FillWordTable(kCrc32cUnextendPoly, kCrc32cUnextendPoly, 1, &reverse_table0_); + + j = FillZeroesTable(kCrc32cUnextendPoly, &reverse_zeroes_); + Y_ABSL_RAW_CHECK(j <= static_cast<int>(Y_ABSL_ARRAYSIZE(this->reverse_zeroes_)), + ""); +} + +void CRC32::Extend(uint32_t* crc, const void* bytes, size_t length) const { + const uint8_t* p = static_cast<const uint8_t*>(bytes); + const uint8_t* e = p + length; + uint32_t l = *crc; + + auto step_one_byte = [this, &p, &l] () { + int c = (l & 0xff) ^ *p++; + l = this->table0_[c] ^ (l >> 8); + }; + + if (kNeedAlignedLoads) { + // point x at first 4-byte aligned byte in string. this might be past the + // end of the string. + const uint8_t* x = RoundUp<4>(p); + if (x <= e) { + // Process bytes until finished or p is 4-byte aligned + while (p != x) { + step_one_byte(); + } + } + } + + const size_t kSwathSize = 16; + if (static_cast<size_t>(e - p) >= kSwathSize) { + // Load one swath of data into the operating buffers. + uint32_t buf0 = y_absl::little_endian::Load32(p) ^ l; + uint32_t buf1 = y_absl::little_endian::Load32(p + 4); + uint32_t buf2 = y_absl::little_endian::Load32(p + 8); + uint32_t buf3 = y_absl::little_endian::Load32(p + 12); + p += kSwathSize; + + // Increment a CRC value by a "swath"; this combines the four bytes + // starting at `ptr` and twelve zero bytes, so that four CRCs can be + // built incrementally and combined at the end. + const auto step_swath = [this](uint32_t crc_in, const std::uint8_t* ptr) { + return y_absl::little_endian::Load32(ptr) ^ + this->table_[3][crc_in & 0xff] ^ + this->table_[2][(crc_in >> 8) & 0xff] ^ + this->table_[1][(crc_in >> 16) & 0xff] ^ + this->table_[0][crc_in >> 24]; + }; + + // Run one CRC calculation step over all swaths in one 16-byte stride + const auto step_stride = [&]() { + buf0 = step_swath(buf0, p); + buf1 = step_swath(buf1, p + 4); + buf2 = step_swath(buf2, p + 8); + buf3 = step_swath(buf3, p + 12); + p += 16; + }; + + // Process kStride interleaved swaths through the data in parallel. + while ((e - p) > kPrefetchHorizon) { + base_internal::PrefetchNta( + reinterpret_cast<const void*>(p + kPrefetchHorizon)); + // Process 64 bytes at a time + step_stride(); + step_stride(); + step_stride(); + step_stride(); + } + while (static_cast<size_t>(e - p) >= kSwathSize) { + step_stride(); + } + + // Now advance one word at a time as far as possible. This isn't worth + // doing if we have word-advance tables. + while (static_cast<size_t>(e - p) >= 4) { + buf0 = step_swath(buf0, p); + uint32_t tmp = buf0; + buf0 = buf1; + buf1 = buf2; + buf2 = buf3; + buf3 = tmp; + p += 4; + } + + // Combine the results from the different swaths. This is just a CRC + // on the data values in the bufX words. + auto combine_one_word = [this](uint32_t crc_in, uint32_t w) { + w ^= crc_in; + for (size_t i = 0; i < 4; ++i) { + w = (w >> 8) ^ this->table0_[w & 0xff]; + } + return w; + }; + + l = combine_one_word(0, buf0); + l = combine_one_word(l, buf1); + l = combine_one_word(l, buf2); + l = combine_one_word(l, buf3); + } + + // Process the last few bytes + while (p != e) { + step_one_byte(); + } + + *crc = l; +} + +void CRC32::ExtendByZeroesImpl(uint32_t* crc, size_t length, + const uint32_t zeroes_table[256], + const uint32_t poly_table[256]) const { + if (length != 0) { + uint32_t l = *crc; + // For each ZEROES_BASE_LG bits in length + // (after the low-order bits have been removed) + // we lookup the appropriate polynomial in the zeroes_ array + // and do a polynomial long multiplication (mod the CRC polynomial) + // to extend the CRC by the appropriate number of bits. + for (int i = 0; length != 0; + i += ZEROES_BASE - 1, length >>= ZEROES_BASE_LG) { + int c = length & (ZEROES_BASE - 1); // pick next ZEROES_BASE_LG bits + if (c != 0) { // if they are not zero, + // multiply by entry in table + // Build a table to aid in multiplying 2 bits at a time. + // It takes too long to build tables for more bits. + uint64_t m = zeroes_table[c + i - 1]; + m <<= 1; + uint64_t m2 = m << 1; + uint64_t mtab[4] = {0, m, m2, m2 ^ m}; + + // Do the multiply one byte at a time. + uint64_t result = 0; + for (int x = 0; x < 32; x += 8) { + // The carry-less multiply. + result ^= mtab[l & 3] ^ (mtab[(l >> 2) & 3] << 2) ^ + (mtab[(l >> 4) & 3] << 4) ^ (mtab[(l >> 6) & 3] << 6); + l >>= 8; + + // Reduce modulo the polynomial + result = (result >> 8) ^ poly_table[result & 0xff]; + } + l = static_cast<uint32_t>(result); + } + } + *crc = l; + } +} + +void CRC32::ExtendByZeroes(uint32_t* crc, size_t length) const { + return CRC32::ExtendByZeroesImpl(crc, length, zeroes_, table0_); +} + +void CRC32::UnextendByZeroes(uint32_t* crc, size_t length) const { + // See the comment in CRC32::InitTables() for an explanation of the algorithm + // below. + *crc = ReverseBits(*crc); + ExtendByZeroesImpl(crc, length, reverse_zeroes_, reverse_table0_); + *crc = ReverseBits(*crc); +} + +void CRC32::Scramble(uint32_t* crc) const { + // Rotate by near half the word size plus 1. See the scramble comment in + // crc_internal.h for an explanation. + constexpr int scramble_rotate = (32 / 2) + 1; + *crc = RotateRight<uint32_t>(static_cast<unsigned int>(*crc + kScrambleLo), + 32, scramble_rotate) & + MaskOfLength<uint32_t>(32); +} + +void CRC32::Unscramble(uint32_t* crc) const { + constexpr int scramble_rotate = (32 / 2) + 1; + uint64_t rotated = RotateRight<uint32_t>(static_cast<unsigned int>(*crc), 32, + 32 - scramble_rotate); + *crc = (rotated - kScrambleLo) & MaskOfLength<uint32_t>(32); +} + +// Constructor and destructor for base class CRC. +CRC::~CRC() {} +CRC::CRC() {} + +// The "constructor" for a CRC32C with a standard polynomial. +CRC* CRC::Crc32c() { + static CRC* singleton = CRCImpl::NewInternal(); + return singleton; +} + +// This Concat implementation works for arbitrary polynomials. +void CRC::Concat(uint32_t* px, uint32_t y, size_t ylen) { + // https://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks + // The CRC of a message M is the remainder of polynomial divison modulo G, + // where the coefficient arithmetic is performed modulo 2 (so +/- are XOR): + // R(x) = M(x) x**n (mod G) + // (n is the degree of G) + // In practice, we use an initial value A and a bitmask B to get + // R = (A ^ B)x**|M| ^ Mx**n ^ B (mod G) + // If M is the concatenation of two strings S and T, and Z is the string of + // len(T) 0s, then the remainder CRC(ST) can be expressed as: + // R = (A ^ B)x**|ST| ^ STx**n ^ B + // = (A ^ B)x**|SZ| ^ SZx**n ^ B ^ Tx**n + // = CRC(SZ) ^ Tx**n + // CRC(Z) = (A ^ B)x**|T| ^ B + // CRC(T) = (A ^ B)x**|T| ^ Tx**n ^ B + // So R = CRC(SZ) ^ CRC(Z) ^ CRC(T) + // + // And further, since CRC(SZ) = Extend(CRC(S), Z), + // CRC(SZ) ^ CRC(Z) = Extend(CRC(S) ^ CRC(''), Z). + uint32_t z; + uint32_t t; + Empty(&z); + t = *px ^ z; + ExtendByZeroes(&t, ylen); + *px = t ^ y; +} + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.h new file mode 100644 index 0000000000..051015a5f3 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.h @@ -0,0 +1,91 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_CRC_H_ +#define Y_ABSL_CRC_INTERNAL_CRC_H_ + +#include <cstdint> + +#include "y_absl/base/config.h" + +// This class implements CRCs (aka Rabin Fingerprints). +// Treats the input as a polynomial with coefficients in Z(2), +// and finds the remainder when divided by an primitive polynomial +// of the appropriate length. + +// A polynomial is represented by the bit pattern formed by its coefficients, +// but with the highest order bit not stored. +// The highest degree coefficient is stored in the lowest numbered bit +// in the lowest addressed byte. Thus, in what follows, the highest degree +// coefficient that is stored is in the low order bit of "lo" or "*lo". + +// Hardware acceleration is used when available. + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +class CRC { + public: + virtual ~CRC(); + + // Place the CRC of the empty string in "*crc" + virtual void Empty(uint32_t* crc) const = 0; + + // If "*crc" is the CRC of bytestring A, place the CRC of + // the bytestring formed from the concatenation of A and the "length" + // bytes at "bytes" into "*crc". + virtual void Extend(uint32_t* crc, const void* bytes, + size_t length) const = 0; + + // Equivalent to Extend(crc, bytes, length) where "bytes" + // points to an array of "length" zero bytes. + virtual void ExtendByZeroes(uint32_t* crc, size_t length) const = 0; + + // Inverse opration of ExtendByZeroes. If `crc` is the CRC value of a string + // ending in `length` zero bytes, this returns a CRC value of that string + // with those zero bytes removed. + virtual void UnextendByZeroes(uint32_t* crc, size_t length) const = 0; + + // If *px is the CRC (as defined by *crc) of some string X, + // and y is the CRC of some string Y that is ylen bytes long, set + // *px to the CRC of the concatenation of X followed by Y. + virtual void Concat(uint32_t* px, uint32_t y, size_t ylen); + + // Apply a non-linear transformation to "*crc" so that + // it is safe to CRC the result with the same polynomial without + // any reduction of error-detection ability in the outer CRC. + // Unscramble() performs the inverse transformation. + // It is strongly recommended that CRCs be scrambled before storage or + // transmission, and unscrambled at the other end before futher manipulation. + virtual void Scramble(uint32_t* crc) const = 0; + virtual void Unscramble(uint32_t* crc) const = 0; + + // Crc32c() returns the singleton implementation of CRC for the CRC32C + // polynomial. Returns a handle that MUST NOT be destroyed with delete. + static CRC* Crc32c(); + + protected: + CRC(); // Clients may not call constructor; use Crc32c() instead. + + private: + CRC(const CRC&) = delete; + CRC& operator=(const CRC&) = delete; +}; + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_CRC_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32_x86_arm_combined_simd.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32_x86_arm_combined_simd.h new file mode 100644 index 0000000000..3e49100c8d --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32_x86_arm_combined_simd.h @@ -0,0 +1,269 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_CRC32_X86_ARM_COMBINED_SIMD_H_ +#define Y_ABSL_CRC_INTERNAL_CRC32_X86_ARM_COMBINED_SIMD_H_ + +#include <cstdint> + +#include "y_absl/base/config.h" + +// ------------------------------------------------------------------------- +// Many x86 and ARM machines have CRC acceleration hardware. +// We can do a faster version of Extend() on such machines. +// We define a translation layer for both x86 and ARM for the ease of use and +// most performance gains. + +// This implementation requires 64-bit CRC instructions (part of SSE 4.2) and +// PCLMULQDQ instructions. 32-bit builds with SSE 4.2 do exist, so the +// __x86_64__ condition is necessary. +#if defined(__x86_64__) && defined(__SSE4_2__) && defined(__PCLMUL__) + +#include <x86intrin.h> +#define Y_ABSL_CRC_INTERNAL_HAVE_X86_SIMD + +#elif defined(_MSC_VER) && defined(__AVX__) + +// MSVC AVX (/arch:AVX) implies SSE 4.2 and PCLMULQDQ. +#include <intrin.h> +#define Y_ABSL_CRC_INTERNAL_HAVE_X86_SIMD + +#elif defined(__aarch64__) && defined(__LITTLE_ENDIAN__) && \ + defined(__ARM_FEATURE_CRC32) && defined(Y_ABSL_INTERNAL_HAVE_ARM_NEON) && \ + defined(__ARM_FEATURE_CRYPTO) + +#include <arm_acle.h> +#include <arm_neon.h> +#define Y_ABSL_CRC_INTERNAL_HAVE_ARM_SIMD + +#endif + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +#if defined(Y_ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) || \ + defined(Y_ABSL_CRC_INTERNAL_HAVE_X86_SIMD) + +#if defined(Y_ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) +using V128 = uint64x2_t; +#else +using V128 = __m128i; +#endif + +// Starting with the initial value in |crc|, accumulates a CRC32 value for +// unsigned integers of different sizes. +uint32_t CRC32_u8(uint32_t crc, uint8_t v); + +uint32_t CRC32_u16(uint32_t crc, uint16_t v); + +uint32_t CRC32_u32(uint32_t crc, uint32_t v); + +uint32_t CRC32_u64(uint32_t crc, uint64_t v); + +// Loads 128 bits of integer data. |src| must be 16-byte aligned. +V128 V128_Load(const V128* src); + +// Load 128 bits of integer data. |src| does not need to be aligned. +V128 V128_LoadU(const V128* src); + +// Polynomially multiplies the high 64 bits of |l| and |r|. +V128 V128_PMulHi(const V128 l, const V128 r); + +// Polynomially multiplies the low 64 bits of |l| and |r|. +V128 V128_PMulLow(const V128 l, const V128 r); + +// Polynomially multiplies the low 64 bits of |r| and high 64 bits of |l|. +V128 V128_PMul01(const V128 l, const V128 r); + +// Polynomially multiplies the low 64 bits of |l| and high 64 bits of |r|. +V128 V128_PMul10(const V128 l, const V128 r); + +// Produces a XOR operation of |l| and |r|. +V128 V128_Xor(const V128 l, const V128 r); + +// Produces an AND operation of |l| and |r|. +V128 V128_And(const V128 l, const V128 r); + +// Sets two 64 bit integers to one 128 bit vector. The order is reverse. +// dst[63:0] := |r| +// dst[127:64] := |l| +V128 V128_From2x64(const uint64_t l, const uint64_t r); + +// Shift |l| right by |imm| bytes while shifting in zeros. +template <int imm> +V128 V128_ShiftRight(const V128 l); + +// Extracts a 32-bit integer from |l|, selected with |imm|. +template <int imm> +int V128_Extract32(const V128 l); + +// Extracts the low 64 bits from V128. +int64_t V128_Low64(const V128 l); + +// Left-shifts packed 64-bit integers in l by r. +V128 V128_ShiftLeft64(const V128 l, const V128 r); + +#endif + +#if defined(Y_ABSL_CRC_INTERNAL_HAVE_X86_SIMD) + +inline uint32_t CRC32_u8(uint32_t crc, uint8_t v) { + return _mm_crc32_u8(crc, v); +} + +inline uint32_t CRC32_u16(uint32_t crc, uint16_t v) { + return _mm_crc32_u16(crc, v); +} + +inline uint32_t CRC32_u32(uint32_t crc, uint32_t v) { + return _mm_crc32_u32(crc, v); +} + +inline uint32_t CRC32_u64(uint32_t crc, uint64_t v) { + return static_cast<uint32_t>(_mm_crc32_u64(crc, v)); +} + +inline V128 V128_Load(const V128* src) { return _mm_load_si128(src); } + +inline V128 V128_LoadU(const V128* src) { return _mm_loadu_si128(src); } + +inline V128 V128_PMulHi(const V128 l, const V128 r) { + return _mm_clmulepi64_si128(l, r, 0x11); +} + +inline V128 V128_PMulLow(const V128 l, const V128 r) { + return _mm_clmulepi64_si128(l, r, 0x00); +} + +inline V128 V128_PMul01(const V128 l, const V128 r) { + return _mm_clmulepi64_si128(l, r, 0x01); +} + +inline V128 V128_PMul10(const V128 l, const V128 r) { + return _mm_clmulepi64_si128(l, r, 0x10); +} + +inline V128 V128_Xor(const V128 l, const V128 r) { return _mm_xor_si128(l, r); } + +inline V128 V128_And(const V128 l, const V128 r) { return _mm_and_si128(l, r); } + +inline V128 V128_From2x64(const uint64_t l, const uint64_t r) { + return _mm_set_epi64x(static_cast<int64_t>(l), static_cast<int64_t>(r)); +} + +template <int imm> +inline V128 V128_ShiftRight(const V128 l) { + return _mm_srli_si128(l, imm); +} + +template <int imm> +inline int V128_Extract32(const V128 l) { + return _mm_extract_epi32(l, imm); +} + +inline int64_t V128_Low64(const V128 l) { return _mm_cvtsi128_si64(l); } + +inline V128 V128_ShiftLeft64(const V128 l, const V128 r) { + return _mm_sll_epi64(l, r); +} + +#elif defined(Y_ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) + +inline uint32_t CRC32_u8(uint32_t crc, uint8_t v) { return __crc32cb(crc, v); } + +inline uint32_t CRC32_u16(uint32_t crc, uint16_t v) { + return __crc32ch(crc, v); +} + +inline uint32_t CRC32_u32(uint32_t crc, uint32_t v) { + return __crc32cw(crc, v); +} + +inline uint32_t CRC32_u64(uint32_t crc, uint64_t v) { + return __crc32cd(crc, v); +} + +inline V128 V128_Load(const V128* src) { + return vld1q_u64(reinterpret_cast<const uint64_t*>(src)); +} + +inline V128 V128_LoadU(const V128* src) { + return vld1q_u64(reinterpret_cast<const uint64_t*>(src)); +} + +// Using inline assembly as clang does not generate the pmull2 instruction and +// performance drops by 15-20%. +// TODO(b/193678732): Investigate why the compiler decides not to generate +// such instructions and why it becomes so much worse. +inline V128 V128_PMulHi(const V128 l, const V128 r) { + uint64x2_t res; + __asm__ __volatile__("pmull2 %0.1q, %1.2d, %2.2d \n\t" + : "=w"(res) + : "w"(l), "w"(r)); + return res; +} + +inline V128 V128_PMulLow(const V128 l, const V128 r) { + return reinterpret_cast<V128>(vmull_p64( + reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(l))), + reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(r))))); +} + +inline V128 V128_PMul01(const V128 l, const V128 r) { + return reinterpret_cast<V128>(vmull_p64( + reinterpret_cast<poly64_t>(vget_high_p64(vreinterpretq_p64_u64(l))), + reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(r))))); +} + +inline V128 V128_PMul10(const V128 l, const V128 r) { + return reinterpret_cast<V128>(vmull_p64( + reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(l))), + reinterpret_cast<poly64_t>(vget_high_p64(vreinterpretq_p64_u64(r))))); +} + +inline V128 V128_Xor(const V128 l, const V128 r) { return veorq_u64(l, r); } + +inline V128 V128_And(const V128 l, const V128 r) { return vandq_u64(l, r); } + +inline V128 V128_From2x64(const uint64_t l, const uint64_t r) { + return vcombine_u64(vcreate_u64(r), vcreate_u64(l)); +} + +template <int imm> +inline V128 V128_ShiftRight(const V128 l) { + return vreinterpretq_u64_s8( + vextq_s8(vreinterpretq_s8_u64(l), vdupq_n_s8(0), imm)); +} + +template <int imm> +inline int V128_Extract32(const V128 l) { + return vgetq_lane_s32(vreinterpretq_s32_u64(l), imm); +} + +inline int64_t V128_Low64(const V128 l) { + return vgetq_lane_s64(vreinterpretq_s64_u64(l), 0); +} + +inline V128 V128_ShiftLeft64(const V128 l, const V128 r) { + return vshlq_u64(l, vreinterpretq_s64_u64(r)); +} + +#endif + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_CRC32_X86_ARM_COMBINED_SIMD_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32c.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32c.h new file mode 100644 index 0000000000..9778c85fb9 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32c.h @@ -0,0 +1,39 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_CRC32C_H_ +#define Y_ABSL_CRC_INTERNAL_CRC32C_H_ + +#include "y_absl/base/config.h" +#include "y_absl/crc/crc32c.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +// Modifies a CRC32 value by removing `length` bytes with a value of 0 from +// the end of the string. +// +// This is the inverse operation of ExtendCrc32cByZeroes(). +// +// This operation has a runtime cost of O(log(`length`)) +// +// Internal implementation detail, exposed for testing only. +crc32c_t UnextendCrc32cByZeroes(crc32c_t initial_crc, size_t length); + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_CRC32C_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32c_inline.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32c_inline.h new file mode 100644 index 0000000000..96f7f7892e --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc32c_inline.h @@ -0,0 +1,72 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_CRC32C_INLINE_H_ +#define Y_ABSL_CRC_INTERNAL_CRC32C_INLINE_H_ + +#include <cstdint> + +#include "y_absl/base/config.h" +#include "y_absl/base/internal/endian.h" +#include "y_absl/crc/internal/crc32_x86_arm_combined_simd.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +// CRC32C implementation optimized for small inputs. +// Either computes crc and return true, or if there is +// no hardware support does nothing and returns false. +inline bool ExtendCrc32cInline(uint32_t* crc, const char* p, size_t n) { +#if defined(Y_ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) || \ + defined(Y_ABSL_CRC_INTERNAL_HAVE_X86_SIMD) + constexpr uint32_t kCrc32Xor = 0xffffffffU; + *crc ^= kCrc32Xor; + if (n & 1) { + *crc = CRC32_u8(*crc, static_cast<uint8_t>(*p)); + n--; + p++; + } + if (n & 2) { + *crc = CRC32_u16(*crc, y_absl::little_endian::Load16(p)); + n -= 2; + p += 2; + } + if (n & 4) { + *crc = CRC32_u32(*crc, y_absl::little_endian::Load32(p)); + n -= 4; + p += 4; + } + while (n) { + *crc = CRC32_u64(*crc, y_absl::little_endian::Load64(p)); + n -= 8; + p += 8; + } + *crc ^= kCrc32Xor; + return true; +#else + // No hardware support, signal the need to fallback. + static_cast<void>(crc); + static_cast<void>(p); + static_cast<void>(n); + return false; +#endif // defined(Y_ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) || + // defined(Y_ABSL_CRC_INTERNAL_HAVE_X86_SIMD) +} + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_CRC32C_INLINE_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.cc new file mode 100644 index 0000000000..4e8e88bce7 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.cc @@ -0,0 +1,130 @@ +// Copyright 2022 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 "y_absl/crc/internal/crc_cord_state.h" + +#include <cassert> + +#include "y_absl/base/config.h" +#include "y_absl/numeric/bits.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +CrcCordState::RefcountedRep* CrcCordState::RefSharedEmptyRep() { + static CrcCordState::RefcountedRep* empty = new CrcCordState::RefcountedRep; + + assert(empty->count.load(std::memory_order_relaxed) >= 1); + assert(empty->rep.removed_prefix.length == 0); + assert(empty->rep.prefix_crc.empty()); + + Ref(empty); + return empty; +} + +CrcCordState::CrcCordState() : refcounted_rep_(new RefcountedRep) {} + +CrcCordState::CrcCordState(const CrcCordState& other) + : refcounted_rep_(other.refcounted_rep_) { + Ref(refcounted_rep_); +} + +CrcCordState::CrcCordState(CrcCordState&& other) + : refcounted_rep_(other.refcounted_rep_) { + // Make `other` valid for use after move. + other.refcounted_rep_ = RefSharedEmptyRep(); +} + +CrcCordState& CrcCordState::operator=(const CrcCordState& other) { + if (this != &other) { + Unref(refcounted_rep_); + refcounted_rep_ = other.refcounted_rep_; + Ref(refcounted_rep_); + } + return *this; +} + +CrcCordState& CrcCordState::operator=(CrcCordState&& other) { + if (this != &other) { + Unref(refcounted_rep_); + refcounted_rep_ = other.refcounted_rep_; + // Make `other` valid for use after move. + other.refcounted_rep_ = RefSharedEmptyRep(); + } + return *this; +} + +CrcCordState::~CrcCordState() { + Unref(refcounted_rep_); +} + +crc32c_t CrcCordState::Checksum() const { + if (rep().prefix_crc.empty()) { + return y_absl::crc32c_t{0}; + } + if (IsNormalized()) { + return rep().prefix_crc.back().crc; + } + return y_absl::RemoveCrc32cPrefix( + rep().removed_prefix.crc, rep().prefix_crc.back().crc, + rep().prefix_crc.back().length - rep().removed_prefix.length); +} + +CrcCordState::PrefixCrc CrcCordState::NormalizedPrefixCrcAtNthChunk( + size_t n) const { + assert(n < NumChunks()); + if (IsNormalized()) { + return rep().prefix_crc[n]; + } + size_t length = rep().prefix_crc[n].length - rep().removed_prefix.length; + return PrefixCrc(length, + y_absl::RemoveCrc32cPrefix(rep().removed_prefix.crc, + rep().prefix_crc[n].crc, length)); +} + +void CrcCordState::Normalize() { + if (IsNormalized() || rep().prefix_crc.empty()) { + return; + } + + Rep* r = mutable_rep(); + for (auto& prefix_crc : r->prefix_crc) { + size_t remaining = prefix_crc.length - r->removed_prefix.length; + prefix_crc.crc = y_absl::RemoveCrc32cPrefix(r->removed_prefix.crc, + prefix_crc.crc, remaining); + prefix_crc.length = remaining; + } + r->removed_prefix = PrefixCrc(); +} + +void CrcCordState::Poison() { + Rep* rep = mutable_rep(); + if (NumChunks() > 0) { + for (auto& prefix_crc : rep->prefix_crc) { + // This is basically CRC32::Scramble(). + uint32_t crc = static_cast<uint32_t>(prefix_crc.crc); + crc += 0x2e76e41b; + crc = y_absl::rotr(crc, 17); + prefix_crc.crc = crc32c_t{crc}; + } + } else { + // Add a fake corrupt chunk. + rep->prefix_crc.push_back(PrefixCrc(0, crc32c_t{1})); + } +} + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.h new file mode 100644 index 0000000000..610b0fbbc6 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.h @@ -0,0 +1,159 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_CRC_CORD_STATE_H_ +#define Y_ABSL_CRC_INTERNAL_CRC_CORD_STATE_H_ + +#include <atomic> +#include <cstddef> +#include <deque> + +#include "y_absl/base/config.h" +#include "y_absl/crc/crc32c.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +// CrcCordState is a copy-on-write class that holds the chunked CRC32C data +// that allows CrcCord to perform efficient substring operations. CrcCordState +// is used as a member variable in CrcCord. When a CrcCord is converted to a +// Cord, the CrcCordState is shallow-copied into the root node of the Cord. If +// the converted Cord is modified outside of CrcCord, the CrcCordState is +// discarded from the Cord. If the Cord is converted back to a CrcCord, and the +// Cord is still carrying the CrcCordState in its root node, the CrcCord can +// re-use the CrcCordState, making the construction of the CrcCord cheap. +// +// CrcCordState does not try to encapsulate the CRC32C state (CrcCord requires +// knowledge of how CrcCordState represents the CRC32C state). It does +// encapsulate the copy-on-write nature of the state. +class CrcCordState { + public: + // Constructors. + CrcCordState(); + CrcCordState(const CrcCordState&); + CrcCordState(CrcCordState&&); + + // Destructor. Atomically unreferences the data. + ~CrcCordState(); + + // Copy and move operators. + CrcCordState& operator=(const CrcCordState&); + CrcCordState& operator=(CrcCordState&&); + + // A (length, crc) pair. + struct PrefixCrc { + PrefixCrc() = default; + PrefixCrc(size_t length_arg, y_absl::crc32c_t crc_arg) + : length(length_arg), crc(crc_arg) {} + + size_t length = 0; + + // TODO(y_absl-team): Memory stomping often zeros out memory. If this struct + // gets overwritten, we could end up with {0, 0}, which is the correct CRC + // for a string of length 0. Consider storing a scrambled value and + // unscrambling it before verifying it. + y_absl::crc32c_t crc = y_absl::crc32c_t{0}; + }; + + // The representation of the chunked CRC32C data. + struct Rep { + // `removed_prefix` is the crc and length of any prefix that has been + // removed from the Cord (for example, by calling + // `CrcCord::RemovePrefix()`). To get the checkum of any prefix of the cord, + // this value must be subtracted from `prefix_crc`. See `Checksum()` for an + // example. + // + // CrcCordState is said to be "normalized" if removed_prefix.length == 0. + PrefixCrc removed_prefix; + + // A deque of (length, crc) pairs, representing length and crc of a prefix + // of the Cord, before removed_prefix has been subtracted. The lengths of + // the prefixes are stored in increasing order. If the Cord is not empty, + // the last value in deque is the contains the CRC32C of the entire Cord + // when removed_prefix is subtracted from it. + std::deque<PrefixCrc> prefix_crc; + }; + + // Returns a reference to the representation of the chunked CRC32C data. + const Rep& rep() const { return refcounted_rep_->rep; } + + // Returns a mutable reference to the representation of the chunked CRC32C + // data. Calling this function will copy the data if another instance also + // holds a reference to the data, so it is important to call rep() instead if + // the data may not be mutated. + Rep* mutable_rep() { + if (refcounted_rep_->count.load(std::memory_order_acquire) != 1) { + RefcountedRep* copy = new RefcountedRep; + copy->rep = refcounted_rep_->rep; + Unref(refcounted_rep_); + refcounted_rep_ = copy; + } + return &refcounted_rep_->rep; + } + + // Returns the CRC32C of the entire Cord. + y_absl::crc32c_t Checksum() const; + + // Returns true if the chunked CRC32C cached is normalized. + bool IsNormalized() const { return rep().removed_prefix.length == 0; } + + // Normalizes the chunked CRC32C checksum cache by substracting any removed + // prefix from the chunks. + void Normalize(); + + // Returns the number of cached chunks. + size_t NumChunks() const { return rep().prefix_crc.size(); } + + // Helper that returns the (length, crc) of the `n`-th cached chunked. + PrefixCrc NormalizedPrefixCrcAtNthChunk(size_t n) const; + + // Poisons all chunks to so that Checksum() will likely be incorrect with high + // probability. + void Poison(); + + private: + struct RefcountedRep { + std::atomic<int32_t> count{1}; + Rep rep; + }; + + // Adds a reference to the shared global empty `RefcountedRep`, and returns a + // pointer to the `RefcountedRep`. This is an optimization to avoid unneeded + // allocations when the allocation is unlikely to ever be used. The returned + // pointer can be `Unref()`ed when it is no longer needed. Since the returned + // instance will always have a reference counter greater than 1, attempts to + // modify it (by calling `mutable_rep()`) will create a new unshared copy. + static RefcountedRep* RefSharedEmptyRep(); + + static void Ref(RefcountedRep* r) { + assert(r != nullptr); + r->count.fetch_add(1, std::memory_order_relaxed); + } + + static void Unref(RefcountedRep* r) { + assert(r != nullptr); + if (r->count.fetch_sub(1, std::memory_order_acq_rel) == 1) { + delete r; + } + } + + RefcountedRep* refcounted_rep_; +}; + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_CRC_CORD_STATE_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_internal.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_internal.h new file mode 100644 index 0000000000..591b5200ee --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_internal.h @@ -0,0 +1,179 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_CRC_INTERNAL_H_ +#define Y_ABSL_CRC_INTERNAL_CRC_INTERNAL_H_ + +#include <cstdint> +#include <memory> +#include <vector> + +#include "y_absl/base/internal/raw_logging.h" +#include "y_absl/crc/internal/crc.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN + +namespace crc_internal { + +// Prefetch constants used in some Extend() implementations +constexpr int kPrefetchHorizon = Y_ABSL_CACHELINE_SIZE * 4; // Prefetch this far +// Shorter prefetch distance for smaller buffers +constexpr int kPrefetchHorizonMedium = Y_ABSL_CACHELINE_SIZE * 1; +static_assert(kPrefetchHorizon >= 64, "CRCPrefetchHorizon less than loop len"); + +// We require the Scramble() function: +// - to be reversible (Unscramble() must exist) +// - to be non-linear in the polynomial's Galois field (so the CRC of a +// scrambled CRC is not linearly affected by the scrambled CRC, even if +// using the same polynomial) +// - not to be its own inverse. Preferably, if X=Scramble^N(X) and N!=0, then +// N is large. +// - to be fast. +// - not to change once defined. +// We introduce non-linearity in two ways: +// Addition of a constant. +// - The carries introduce non-linearity; we use bits of an irrational +// (phi) to make it unlikely that we introduce no carries. +// Rotate by a constant number of bits. +// - We use floor(degree/2)+1, which does not divide the degree, and +// splits the bits nearly evenly, which makes it less likely the +// halves will be the same or one will be all zeroes. +// We do both things to improve the chances of non-linearity in the face of +// bit patterns with low numbers of bits set, while still being fast. +// Below is the constant that we add. The bits are the first 128 bits of the +// fractional part of phi, with a 1 ored into the bottom bit to maximize the +// cycle length of repeated adds. +constexpr uint64_t kScrambleHi = (static_cast<uint64_t>(0x4f1bbcdcU) << 32) | + static_cast<uint64_t>(0xbfa53e0aU); +constexpr uint64_t kScrambleLo = (static_cast<uint64_t>(0xf9ce6030U) << 32) | + static_cast<uint64_t>(0x2e76e41bU); + +class CRCImpl : public CRC { // Implemention of the abstract class CRC + public: + using Uint32By256 = uint32_t[256]; + + CRCImpl() {} + ~CRCImpl() override = default; + + // The internal version of CRC::New(). + static CRCImpl* NewInternal(); + + void Empty(uint32_t* crc) const override; + + // Fill in a table for updating a CRC by one word of 'word_size' bytes + // [last_lo, last_hi] contains the answer if the last bit in the word + // is set. + static void FillWordTable(uint32_t poly, uint32_t last, int word_size, + Uint32By256* t); + + // Build the table for extending by zeroes, returning the number of entries. + // For a in {1, 2, ..., ZEROES_BASE-1}, b in {0, 1, 2, 3, ...}, + // entry j=a-1+(ZEROES_BASE-1)*b + // contains a polynomial Pi such that multiplying + // a CRC by Pi mod P, where P is the CRC polynomial, is equivalent to + // appending a*2**(ZEROES_BASE_LG*b) zero bytes to the original string. + static int FillZeroesTable(uint32_t poly, Uint32By256* t); + + virtual void InitTables() = 0; + + private: + CRCImpl(const CRCImpl&) = delete; + CRCImpl& operator=(const CRCImpl&) = delete; +}; + +// This is the 32-bit implementation. It handles all sizes from 8 to 32. +class CRC32 : public CRCImpl { + public: + CRC32() {} + ~CRC32() override {} + + void Extend(uint32_t* crc, const void* bytes, size_t length) const override; + void ExtendByZeroes(uint32_t* crc, size_t length) const override; + void Scramble(uint32_t* crc) const override; + void Unscramble(uint32_t* crc) const override; + void UnextendByZeroes(uint32_t* crc, size_t length) const override; + + void InitTables() override; + + private: + // Common implementation guts for ExtendByZeroes and UnextendByZeroes(). + // + // zeroes_table is a table as returned by FillZeroesTable(), containing + // polynomials representing CRCs of strings-of-zeros of various lenghts, + // and which can be combined by polynomial multiplication. poly_table is + // a table of CRC byte extension values. These tables are determined by + // the generator polynomial. + // + // These will be set to reverse_zeroes_ and reverse_table0_ for Unextend, and + // CRC32::zeroes_ and CRC32::table0_ for Extend. + void ExtendByZeroesImpl(uint32_t* crc, size_t length, + const uint32_t zeroes_table[256], + const uint32_t poly_table[256]) const; + + uint32_t table0_[256]; // table of byte extensions + uint32_t zeroes_[256]; // table of zero extensions + + // table of 4-byte extensions shifted by 12 bytes of zeroes + uint32_t table_[4][256]; + + // Reverse lookup tables, using the alternate polynomial used by + // UnextendByZeroes(). + uint32_t reverse_table0_[256]; // table of reverse byte extensions + uint32_t reverse_zeroes_[256]; // table of reverse zero extensions + + CRC32(const CRC32&) = delete; + CRC32& operator=(const CRC32&) = delete; +}; + +// Helpers + +// Return a bit mask containing len 1-bits. +// Requires 0 < len <= sizeof(T) +template <typename T> +T MaskOfLength(int len) { + // shift 2 by len-1 rather than 1 by len because shifts of wordsize + // are undefined. + return (T(2) << (len - 1)) - 1; +} + +// Rotate low-order "width" bits of "in" right by "r" bits, +// setting other bits in word to arbitrary values. +template <typename T> +T RotateRight(T in, int width, int r) { + return (in << (width - r)) | ((in >> r) & MaskOfLength<T>(width - r)); +} + +// RoundUp<N>(p) returns the lowest address >= p aligned to an N-byte +// boundary. Requires that N is a power of 2. +template <int alignment> +const uint8_t* RoundUp(const uint8_t* p) { + static_assert((alignment & (alignment - 1)) == 0, "alignment is not 2^n"); + constexpr uintptr_t mask = alignment - 1; + const uintptr_t as_uintptr = reinterpret_cast<uintptr_t>(p); + return reinterpret_cast<const uint8_t*>((as_uintptr + mask) & ~mask); +} + +// Return a newly created CRC32AcceleratedX86ARMCombined if we can use Intel's +// or ARM's CRC acceleration for a given polynomial. Return nullptr otherwise. +CRCImpl* TryNewCRC32AcceleratedX86ARMCombined(); + +// Return all possible hardware accelerated implementations. For testing only. +std::vector<std::unique_ptr<CRCImpl>> NewCRC32AcceleratedX86ARMCombinedAll(); + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_CRC_INTERNAL_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy.h new file mode 100644 index 0000000000..520a49c879 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy.h @@ -0,0 +1,119 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_CRC_MEMCPY_H_ +#define Y_ABSL_CRC_INTERNAL_CRC_MEMCPY_H_ + +#include <cstddef> +#include <memory> + +#include "y_absl/base/config.h" +#include "y_absl/crc/crc32c.h" + +// Defined if the class AcceleratedCrcMemcpyEngine exists. +#if defined(__x86_64__) && defined(__SSE4_2__) +#define Y_ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1 +#elif defined(_MSC_VER) && defined(__AVX__) +#define Y_ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1 +#endif + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +class CrcMemcpyEngine { + public: + virtual ~CrcMemcpyEngine() = default; + + virtual crc32c_t Compute(void* __restrict dst, const void* __restrict src, + std::size_t length, crc32c_t initial_crc) const = 0; + + protected: + CrcMemcpyEngine() = default; +}; + +class CrcMemcpy { + public: + static crc32c_t CrcAndCopy(void* __restrict dst, const void* __restrict src, + std::size_t length, + crc32c_t initial_crc = crc32c_t{0}, + bool non_temporal = false) { + static const ArchSpecificEngines engines = GetArchSpecificEngines(); + auto* engine = non_temporal ? engines.non_temporal : engines.temporal; + return engine->Compute(dst, src, length, initial_crc); + } + + // For testing only: get an architecture-specific engine for tests. + static std::unique_ptr<CrcMemcpyEngine> GetTestEngine(int vector, + int integer); + + private: + struct ArchSpecificEngines { + CrcMemcpyEngine* temporal; + CrcMemcpyEngine* non_temporal; + }; + + static ArchSpecificEngines GetArchSpecificEngines(); +}; + +// Fallback CRC-memcpy engine. +class FallbackCrcMemcpyEngine : public CrcMemcpyEngine { + public: + FallbackCrcMemcpyEngine() = default; + FallbackCrcMemcpyEngine(const FallbackCrcMemcpyEngine&) = delete; + FallbackCrcMemcpyEngine operator=(const FallbackCrcMemcpyEngine&) = delete; + + crc32c_t Compute(void* __restrict dst, const void* __restrict src, + std::size_t length, crc32c_t initial_crc) const override; +}; + +// CRC Non-Temporal-Memcpy engine. +class CrcNonTemporalMemcpyEngine : public CrcMemcpyEngine { + public: + CrcNonTemporalMemcpyEngine() = default; + CrcNonTemporalMemcpyEngine(const CrcNonTemporalMemcpyEngine&) = delete; + CrcNonTemporalMemcpyEngine operator=(const CrcNonTemporalMemcpyEngine&) = + delete; + + crc32c_t Compute(void* __restrict dst, const void* __restrict src, + std::size_t length, crc32c_t initial_crc) const override; +}; + +// CRC Non-Temporal-Memcpy AVX engine. +class CrcNonTemporalMemcpyAVXEngine : public CrcMemcpyEngine { + public: + CrcNonTemporalMemcpyAVXEngine() = default; + CrcNonTemporalMemcpyAVXEngine(const CrcNonTemporalMemcpyAVXEngine&) = delete; + CrcNonTemporalMemcpyAVXEngine operator=( + const CrcNonTemporalMemcpyAVXEngine&) = delete; + + crc32c_t Compute(void* __restrict dst, const void* __restrict src, + std::size_t length, crc32c_t initial_crc) const override; +}; + +// Copy source to destination and return the CRC32C of the data copied. If an +// accelerated version is available, use the accelerated version, otherwise use +// the generic fallback version. +inline crc32c_t Crc32CAndCopy(void* __restrict dst, const void* __restrict src, + std::size_t length, + crc32c_t initial_crc = crc32c_t{0}, + bool non_temporal = false) { + return CrcMemcpy::CrcAndCopy(dst, src, length, initial_crc, non_temporal); +} + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_CRC_MEMCPY_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_fallback.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_fallback.cc new file mode 100644 index 0000000000..58d921d316 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_fallback.cc @@ -0,0 +1,75 @@ +// Copyright 2022 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 <cstdint> +#include <memory> + +#include "y_absl/base/config.h" +#include "y_absl/crc/crc32c.h" +#include "y_absl/crc/internal/crc_memcpy.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +y_absl::crc32c_t FallbackCrcMemcpyEngine::Compute(void* __restrict dst, + const void* __restrict src, + std::size_t length, + crc32c_t initial_crc) const { + constexpr size_t kBlockSize = 8192; + y_absl::crc32c_t crc = initial_crc; + + const char* src_bytes = reinterpret_cast<const char*>(src); + char* dst_bytes = reinterpret_cast<char*>(dst); + + // Copy + CRC loop - run 8k chunks until we are out of full chunks. CRC + // then copy was found to be slightly more efficient in our test cases. + std::size_t offset = 0; + for (; offset + kBlockSize < length; offset += kBlockSize) { + crc = y_absl::ExtendCrc32c(crc, + y_absl::string_view(src_bytes + offset, kBlockSize)); + memcpy(dst_bytes + offset, src_bytes + offset, kBlockSize); + } + + // Save some work if length is 0. + if (offset < length) { + std::size_t final_copy_size = length - offset; + crc = y_absl::ExtendCrc32c( + crc, y_absl::string_view(src_bytes + offset, final_copy_size)); + memcpy(dst_bytes + offset, src_bytes + offset, final_copy_size); + } + + return crc; +} + +// Compile the following only if we don't have +#ifndef Y_ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE + +CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() { + CrcMemcpy::ArchSpecificEngines engines; + engines.temporal = new FallbackCrcMemcpyEngine(); + engines.non_temporal = new FallbackCrcMemcpyEngine(); + return engines; +} + +std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int /*vector*/, + int /*integer*/) { + return std::make_unique<FallbackCrcMemcpyEngine>(); +} + +#endif // Y_ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_x86_64.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_x86_64.cc new file mode 100644 index 0000000000..9cef4b5454 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_x86_64.cc @@ -0,0 +1,434 @@ +// Copyright 2022 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. + +// Simultaneous memcopy and CRC-32C for x86-64. Uses integer registers because +// XMM registers do not support the CRC instruction (yet). While copying, +// compute the running CRC of the data being copied. +// +// It is assumed that any CPU running this code has SSE4.2 instructions +// available (for CRC32C). This file will do nothing if that is not true. +// +// The CRC instruction has a 3-byte latency, and we are stressing the ALU ports +// here (unlike a traditional memcopy, which has almost no ALU use), so we will +// need to copy in such a way that the CRC unit is used efficiently. We have two +// regimes in this code: +// 1. For operations of size < kCrcSmallSize, do the CRC then the memcpy +// 2. For operations of size > kCrcSmallSize: +// a) compute an initial CRC + copy on a small amount of data to align the +// destination pointer on a 16-byte boundary. +// b) Split the data into 3 main regions and a tail (smaller than 48 bytes) +// c) Do the copy and CRC of the 3 main regions, interleaving (start with +// full cache line copies for each region, then move to single 16 byte +// pieces per region). +// d) Combine the CRCs with CRC32C::Concat. +// e) Copy the tail and extend the CRC with the CRC of the tail. +// This method is not ideal for op sizes between ~1k and ~8k because CRC::Concat +// takes a significant amount of time. A medium-sized approach could be added +// using 3 CRCs over fixed-size blocks where the zero-extensions required for +// CRC32C::Concat can be precomputed. + +#ifdef __SSE4_2__ +#include <immintrin.h> +#endif + +#ifdef _MSC_VER +#include <intrin.h> +#endif + +#include <array> +#include <cstddef> +#include <cstdint> +#include <type_traits> + +#include "y_absl/base/dynamic_annotations.h" +#include "y_absl/base/internal/prefetch.h" +#include "y_absl/base/optimization.h" +#include "y_absl/crc/crc32c.h" +#include "y_absl/crc/internal/cpu_detect.h" +#include "y_absl/crc/internal/crc_memcpy.h" +#include "y_absl/strings/string_view.h" + +#ifdef Y_ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +namespace { + +inline crc32c_t ShortCrcCopy(char* dst, const char* src, std::size_t length, + crc32c_t crc) { + // Small copy: just go 1 byte at a time: being nice to the branch predictor + // is more important here than anything else + uint32_t crc_uint32 = static_cast<uint32_t>(crc); + for (std::size_t i = 0; i < length; i++) { + uint8_t data = *reinterpret_cast<const uint8_t*>(src); + crc_uint32 = _mm_crc32_u8(crc_uint32, data); + *reinterpret_cast<uint8_t*>(dst) = data; + ++src; + ++dst; + } + return crc32c_t{crc_uint32}; +} + +constexpr size_t kIntLoadsPerVec = sizeof(__m128i) / sizeof(uint64_t); + +// Common function for copying the tails of multiple large regions. +template <size_t vec_regions, size_t int_regions> +inline void LargeTailCopy(crc32c_t* crcs, char** dst, const char** src, + size_t region_size, size_t copy_rounds) { + std::array<__m128i, vec_regions> data; + std::array<uint64_t, kIntLoadsPerVec * int_regions> int_data; + + while (copy_rounds > 0) { + for (size_t i = 0; i < vec_regions; i++) { + size_t region = i; + + auto* vsrc = + reinterpret_cast<const __m128i*>(*src + region_size * region); + auto* vdst = reinterpret_cast<__m128i*>(*dst + region_size * region); + + // Load the blocks, unaligned + data[i] = _mm_loadu_si128(vsrc); + + // Store the blocks, aligned + _mm_store_si128(vdst, data[i]); + + // Compute the running CRC + crcs[region] = crc32c_t{static_cast<uint32_t>( + _mm_crc32_u64(static_cast<uint32_t>(crcs[region]), + static_cast<uint64_t>(_mm_extract_epi64(data[i], 0))))}; + crcs[region] = crc32c_t{static_cast<uint32_t>( + _mm_crc32_u64(static_cast<uint32_t>(crcs[region]), + static_cast<uint64_t>(_mm_extract_epi64(data[i], 1))))}; + } + + for (size_t i = 0; i < int_regions; i++) { + size_t region = vec_regions + i; + + auto* usrc = + reinterpret_cast<const uint64_t*>(*src + region_size * region); + auto* udst = reinterpret_cast<uint64_t*>(*dst + region_size * region); + + for (size_t j = 0; j < kIntLoadsPerVec; j++) { + size_t data_index = i * kIntLoadsPerVec + j; + + int_data[data_index] = *(usrc + j); + crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64( + static_cast<uint32_t>(crcs[region]), int_data[data_index]))}; + + *(udst + j) = int_data[data_index]; + } + } + + // Increment pointers + *src += sizeof(__m128i); + *dst += sizeof(__m128i); + --copy_rounds; + } +} + +} // namespace + +template <size_t vec_regions, size_t int_regions> +class AcceleratedCrcMemcpyEngine : public CrcMemcpyEngine { + public: + AcceleratedCrcMemcpyEngine() = default; + AcceleratedCrcMemcpyEngine(const AcceleratedCrcMemcpyEngine&) = delete; + AcceleratedCrcMemcpyEngine operator=(const AcceleratedCrcMemcpyEngine&) = + delete; + + crc32c_t Compute(void* __restrict dst, const void* __restrict src, + std::size_t length, crc32c_t initial_crc) const override; +}; + +template <size_t vec_regions, size_t int_regions> +crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute( + void* __restrict dst, const void* __restrict src, std::size_t length, + crc32c_t initial_crc) const { + constexpr std::size_t kRegions = vec_regions + int_regions; + constexpr uint32_t kCrcDataXor = uint32_t{0xffffffff}; + constexpr std::size_t kBlockSize = sizeof(__m128i); + constexpr std::size_t kCopyRoundSize = kRegions * kBlockSize; + + // Number of blocks per cacheline. + constexpr std::size_t kBlocksPerCacheLine = Y_ABSL_CACHELINE_SIZE / kBlockSize; + + char* dst_bytes = static_cast<char*>(dst); + const char* src_bytes = static_cast<const char*>(src); + + // Make sure that one prefetch per big block is enough to cover the whole + // dataset, and we don't prefetch too much. + static_assert(Y_ABSL_CACHELINE_SIZE % kBlockSize == 0, + "Cache lines are not divided evenly into blocks, may have " + "unintended behavior!"); + + // Experimentally-determined boundary between a small and large copy. + // Below this number, spin-up and concatenation of CRCs takes enough time that + // it kills the throughput gains of using 3 regions and wide vectors. + constexpr size_t kCrcSmallSize = 256; + + // Experimentally-determined prefetch distance. Main loop copies will + // prefeth data 2 cache lines ahead. + constexpr std::size_t kPrefetchAhead = 2 * Y_ABSL_CACHELINE_SIZE; + + // Small-size CRC-memcpy : just do CRC + memcpy + if (length < kCrcSmallSize) { + crc32c_t crc = + ExtendCrc32c(initial_crc, y_absl::string_view(src_bytes, length)); + memcpy(dst, src, length); + return crc; + } + + // Start work on the CRC: undo the XOR from the previous calculation or set up + // the initial value of the CRC. + // initial_crc ^= kCrcDataXor; + initial_crc = crc32c_t{static_cast<uint32_t>(initial_crc) ^ kCrcDataXor}; + + // Do an initial alignment copy, so we can use aligned store instructions to + // the destination pointer. We align the destination pointer because the + // penalty for an unaligned load is small compared to the penalty of an + // unaligned store on modern CPUs. + std::size_t bytes_from_last_aligned = + reinterpret_cast<uintptr_t>(dst) & (kBlockSize - 1); + if (bytes_from_last_aligned != 0) { + std::size_t bytes_for_alignment = kBlockSize - bytes_from_last_aligned; + + // Do the short-sized copy and CRC. + initial_crc = + ShortCrcCopy(dst_bytes, src_bytes, bytes_for_alignment, initial_crc); + src_bytes += bytes_for_alignment; + dst_bytes += bytes_for_alignment; + length -= bytes_for_alignment; + } + + // We are going to do the copy and CRC in kRegions regions to make sure that + // we can saturate the CRC unit. The CRCs will be combined at the end of the + // run. Copying will use the SSE registers, and we will extract words from + // the SSE registers to add to the CRC. Initially, we run the loop one full + // cache line per region at a time, in order to insert prefetches. + + // Initialize CRCs for kRegions regions. + crc32c_t crcs[kRegions]; + crcs[0] = initial_crc; + for (size_t i = 1; i < kRegions; i++) { + crcs[i] = crc32c_t{kCrcDataXor}; + } + + // Find the number of rounds to copy and the region size. Also compute the + // tail size here. + size_t copy_rounds = length / kCopyRoundSize; + + // Find the size of each region and the size of the tail. + const std::size_t region_size = copy_rounds * kBlockSize; + const std::size_t tail_size = length - (kRegions * region_size); + + // Holding registers for data in each region. + std::array<__m128i, vec_regions> vec_data; + std::array<uint64_t, int_regions * kIntLoadsPerVec> int_data; + + // Main loop. + while (copy_rounds > kBlocksPerCacheLine) { + // Prefetch kPrefetchAhead bytes ahead of each pointer. + for (size_t i = 0; i < kRegions; i++) { + y_absl::base_internal::PrefetchT0(src_bytes + kPrefetchAhead + + region_size * i); + y_absl::base_internal::PrefetchT0(dst_bytes + kPrefetchAhead + + region_size * i); + } + + // Load and store data, computing CRC on the way. + for (size_t i = 0; i < kBlocksPerCacheLine; i++) { + // Copy and CRC the data for the CRC regions. + for (size_t j = 0; j < vec_regions; j++) { + // Cycle which regions get vector load/store and integer load/store, to + // engage prefetching logic around vector load/stores and save issue + // slots by using the integer registers. + size_t region = (j + i) % kRegions; + + auto* vsrc = + reinterpret_cast<const __m128i*>(src_bytes + region_size * region); + auto* vdst = + reinterpret_cast<__m128i*>(dst_bytes + region_size * region); + + // Load and CRC data. + vec_data[j] = _mm_loadu_si128(vsrc + i); + crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64( + static_cast<uint32_t>(crcs[region]), + static_cast<uint64_t>(_mm_extract_epi64(vec_data[j], 0))))}; + crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64( + static_cast<uint32_t>(crcs[region]), + static_cast<uint64_t>(_mm_extract_epi64(vec_data[j], 1))))}; + + // Store the data. + _mm_store_si128(vdst + i, vec_data[j]); + } + + // Preload the partial CRCs for the CLMUL subregions. + for (size_t j = 0; j < int_regions; j++) { + // Cycle which regions get vector load/store and integer load/store, to + // engage prefetching logic around vector load/stores and save issue + // slots by using the integer registers. + size_t region = (j + vec_regions + i) % kRegions; + + auto* usrc = + reinterpret_cast<const uint64_t*>(src_bytes + region_size * region); + auto* udst = + reinterpret_cast<uint64_t*>(dst_bytes + region_size * region); + + for (size_t k = 0; k < kIntLoadsPerVec; k++) { + size_t data_index = j * kIntLoadsPerVec + k; + + // Load and CRC the data. + int_data[data_index] = *(usrc + i * kIntLoadsPerVec + k); + crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64( + static_cast<uint32_t>(crcs[region]), int_data[data_index]))}; + + // Store the data. + *(udst + i * kIntLoadsPerVec + k) = int_data[data_index]; + } + } + } + + // Increment pointers + src_bytes += kBlockSize * kBlocksPerCacheLine; + dst_bytes += kBlockSize * kBlocksPerCacheLine; + copy_rounds -= kBlocksPerCacheLine; + } + + // Copy and CRC the tails of each region. + LargeTailCopy<vec_regions, int_regions>(crcs, &dst_bytes, &src_bytes, + region_size, copy_rounds); + + // Move the source and destination pointers to the end of the region + src_bytes += region_size * (kRegions - 1); + dst_bytes += region_size * (kRegions - 1); + + // Finalize the first CRCs: XOR the internal CRCs by the XOR mask to undo the + // XOR done before doing block copy + CRCs. + for (size_t i = 0; i + 1 < kRegions; i++) { + crcs[i] = crc32c_t{static_cast<uint32_t>(crcs[i]) ^ kCrcDataXor}; + } + + // Build a CRC of the first kRegions - 1 regions. + crc32c_t full_crc = crcs[0]; + for (size_t i = 1; i + 1 < kRegions; i++) { + full_crc = ConcatCrc32c(full_crc, crcs[i], region_size); + } + + // Copy and CRC the tail through the XMM registers. + std::size_t tail_blocks = tail_size / kBlockSize; + LargeTailCopy<0, 1>(&crcs[kRegions - 1], &dst_bytes, &src_bytes, 0, + tail_blocks); + + // Final tail copy for under 16 bytes. + crcs[kRegions - 1] = + ShortCrcCopy(dst_bytes, src_bytes, tail_size - tail_blocks * kBlockSize, + crcs[kRegions - 1]); + + // Finalize and concatenate the final CRC, then return. + crcs[kRegions - 1] = + crc32c_t{static_cast<uint32_t>(crcs[kRegions - 1]) ^ kCrcDataXor}; + return ConcatCrc32c(full_crc, crcs[kRegions - 1], region_size + tail_size); +} + +CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() { +#ifdef UNDEFINED_BEHAVIOR_SANITIZER + // UBSAN does not play nicely with unaligned loads (which we use a lot). + // Get the underlying architecture. + CpuType cpu_type = GetCpuType(); + switch (cpu_type) { + case CpuType::kUnknown: + case CpuType::kAmdRome: + case CpuType::kAmdNaples: + case CpuType::kIntelCascadelakeXeon: + case CpuType::kIntelSkylakeXeon: + case CpuType::kIntelSkylake: + case CpuType::kIntelBroadwell: + case CpuType::kIntelHaswell: + case CpuType::kIntelIvybridge: + return { + .temporal = new FallbackCrcMemcpyEngine(), + .non_temporal = new CrcNonTemporalMemcpyAVXEngine(), + }; + // INTEL_SANDYBRIDGE performs better with SSE than AVX. + case CpuType::kIntelSandybridge: + return { + .temporal = new FallbackCrcMemcpyEngine(), + .non_temporal = new CrcNonTemporalMemcpyEngine(), + }; + default: + return {.temporal = new FallbackCrcMemcpyEngine(), + .non_temporal = new FallbackCrcMemcpyEngine()}; + } +#else + // Get the underlying architecture. + CpuType cpu_type = GetCpuType(); + switch (cpu_type) { + // On Zen 2, PEXTRQ uses 2 micro-ops, including one on the vector store port + // which data movement from the vector registers to the integer registers + // (where CRC32C happens) to crowd the same units as vector stores. As a + // result, using that path exclusively causes bottlenecking on this port. + // We can avoid this bottleneck by using the integer side of the CPU for + // most operations rather than the vector side. We keep a vector region to + // engage some of the prefetching logic in the cache hierarchy which seems + // to give vector instructions special treatment. These prefetch units see + // strided access to each region, and do the right thing. + case CpuType::kAmdRome: + case CpuType::kAmdNaples: + return { + .temporal = new AcceleratedCrcMemcpyEngine<1, 2>(), + .non_temporal = new CrcNonTemporalMemcpyAVXEngine(), + }; + // PCLMULQDQ is slow and we don't have wide enough issue width to take + // advantage of it. For an unknown architecture, don't risk using CLMULs. + case CpuType::kIntelCascadelakeXeon: + case CpuType::kIntelSkylakeXeon: + case CpuType::kIntelSkylake: + case CpuType::kIntelBroadwell: + case CpuType::kIntelHaswell: + case CpuType::kIntelIvybridge: + return { + .temporal = new AcceleratedCrcMemcpyEngine<3, 0>(), + .non_temporal = new CrcNonTemporalMemcpyAVXEngine(), + }; + // INTEL_SANDYBRIDGE performs better with SSE than AVX. + case CpuType::kIntelSandybridge: + return { + .temporal = new AcceleratedCrcMemcpyEngine<3, 0>(), + .non_temporal = new CrcNonTemporalMemcpyEngine(), + }; + default: + return {.temporal = new FallbackCrcMemcpyEngine(), + .non_temporal = new FallbackCrcMemcpyEngine()}; + } +#endif // UNDEFINED_BEHAVIOR_SANITIZER +} + +// For testing, allow the user to specify which engine they want. +std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int vector, + int integer) { + if (vector == 3 && integer == 0) { + return std::make_unique<AcceleratedCrcMemcpyEngine<3, 0>>(); + } else if (vector == 1 && integer == 2) { + return std::make_unique<AcceleratedCrcMemcpyEngine<1, 2>>(); + } + return nullptr; +} + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_non_temporal_memcpy.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_non_temporal_memcpy.cc new file mode 100644 index 0000000000..e73e6487cf --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_non_temporal_memcpy.cc @@ -0,0 +1,93 @@ +// Copyright 2022 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 <cstdint> + +#include "y_absl/base/config.h" +#include "y_absl/crc/crc32c.h" +#include "y_absl/crc/internal/crc_memcpy.h" +#include "y_absl/crc/internal/non_temporal_memcpy.h" +#include "y_absl/strings/string_view.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +crc32c_t CrcNonTemporalMemcpyEngine::Compute(void* __restrict dst, + const void* __restrict src, + std::size_t length, + crc32c_t initial_crc) const { + constexpr size_t kBlockSize = 8192; + crc32c_t crc = initial_crc; + + const char* src_bytes = reinterpret_cast<const char*>(src); + char* dst_bytes = reinterpret_cast<char*>(dst); + + // Copy + CRC loop - run 8k chunks until we are out of full chunks. + std::size_t offset = 0; + for (; offset + kBlockSize < length; offset += kBlockSize) { + crc = y_absl::ExtendCrc32c(crc, + y_absl::string_view(src_bytes + offset, kBlockSize)); + non_temporal_store_memcpy(dst_bytes + offset, src_bytes + offset, + kBlockSize); + } + + // Save some work if length is 0. + if (offset < length) { + std::size_t final_copy_size = length - offset; + crc = ExtendCrc32c(crc, + y_absl::string_view(src_bytes + offset, final_copy_size)); + + non_temporal_store_memcpy(dst_bytes + offset, src_bytes + offset, + final_copy_size); + } + + return crc; +} + +crc32c_t CrcNonTemporalMemcpyAVXEngine::Compute(void* __restrict dst, + const void* __restrict src, + std::size_t length, + crc32c_t initial_crc) const { + constexpr size_t kBlockSize = 8192; + crc32c_t crc = initial_crc; + + const char* src_bytes = reinterpret_cast<const char*>(src); + char* dst_bytes = reinterpret_cast<char*>(dst); + + // Copy + CRC loop - run 8k chunks until we are out of full chunks. + std::size_t offset = 0; + for (; offset + kBlockSize < length; offset += kBlockSize) { + crc = ExtendCrc32c(crc, y_absl::string_view(src_bytes + offset, kBlockSize)); + + non_temporal_store_memcpy_avx(dst_bytes + offset, src_bytes + offset, + kBlockSize); + } + + // Save some work if length is 0. + if (offset < length) { + std::size_t final_copy_size = length - offset; + crc = ExtendCrc32c(crc, + y_absl::string_view(src_bytes + offset, final_copy_size)); + + non_temporal_store_memcpy_avx(dst_bytes + offset, src_bytes + offset, + final_copy_size); + } + + return crc; +} + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_x86_arm_combined.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_x86_arm_combined.cc new file mode 100644 index 0000000000..96e33d8f56 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_x86_arm_combined.cc @@ -0,0 +1,725 @@ +// Copyright 2022 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. + +// Hardware accelerated CRC32 computation on Intel and ARM architecture. + +#include <cstddef> +#include <cstdint> + +#include "y_absl/base/attributes.h" +#include "y_absl/base/config.h" +#include "y_absl/base/dynamic_annotations.h" +#include "y_absl/base/internal/endian.h" +#include "y_absl/base/internal/prefetch.h" +#include "y_absl/crc/internal/cpu_detect.h" +#include "y_absl/crc/internal/crc.h" +#include "y_absl/crc/internal/crc32_x86_arm_combined_simd.h" +#include "y_absl/crc/internal/crc_internal.h" +#include "y_absl/memory/memory.h" +#include "y_absl/numeric/bits.h" + +#if defined(Y_ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) || \ + defined(Y_ABSL_CRC_INTERNAL_HAVE_X86_SIMD) +#define Y_ABSL_INTERNAL_CAN_USE_SIMD_CRC32C +#endif + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +#if defined(Y_ABSL_INTERNAL_CAN_USE_SIMD_CRC32C) + +// Implementation details not exported outside of file +namespace { + +// Some machines have CRC acceleration hardware. +// We can do a faster version of Extend() on such machines. +class CRC32AcceleratedX86ARMCombined : public CRC32 { + public: + CRC32AcceleratedX86ARMCombined() {} + ~CRC32AcceleratedX86ARMCombined() override {} + void ExtendByZeroes(uint32_t* crc, size_t length) const override; + uint32_t ComputeZeroConstant(size_t length) const; + + private: + CRC32AcceleratedX86ARMCombined(const CRC32AcceleratedX86ARMCombined&) = + delete; + CRC32AcceleratedX86ARMCombined& operator=( + const CRC32AcceleratedX86ARMCombined&) = delete; +}; + +// Constants for switching between algorithms. +// Chosen by comparing speed at different powers of 2. +constexpr size_t kSmallCutoff = 256; +constexpr size_t kMediumCutoff = 2048; + +#define Y_ABSL_INTERNAL_STEP1(crc) \ + do { \ + crc = CRC32_u8(static_cast<uint32_t>(crc), *p++); \ + } while (0) +#define Y_ABSL_INTERNAL_STEP2(crc) \ + do { \ + crc = \ + CRC32_u16(static_cast<uint32_t>(crc), y_absl::little_endian::Load16(p)); \ + p += 2; \ + } while (0) +#define Y_ABSL_INTERNAL_STEP4(crc) \ + do { \ + crc = \ + CRC32_u32(static_cast<uint32_t>(crc), y_absl::little_endian::Load32(p)); \ + p += 4; \ + } while (0) +#define Y_ABSL_INTERNAL_STEP8(crc, data) \ + do { \ + crc = CRC32_u64(static_cast<uint32_t>(crc), \ + y_absl::little_endian::Load64(data)); \ + data += 8; \ + } while (0) +#define Y_ABSL_INTERNAL_STEP8BY2(crc0, crc1, p0, p1) \ + do { \ + Y_ABSL_INTERNAL_STEP8(crc0, p0); \ + Y_ABSL_INTERNAL_STEP8(crc1, p1); \ + } while (0) +#define Y_ABSL_INTERNAL_STEP8BY3(crc0, crc1, crc2, p0, p1, p2) \ + do { \ + Y_ABSL_INTERNAL_STEP8(crc0, p0); \ + Y_ABSL_INTERNAL_STEP8(crc1, p1); \ + Y_ABSL_INTERNAL_STEP8(crc2, p2); \ + } while (0) + +namespace { + +uint32_t multiply(uint32_t a, uint32_t b) { + V128 shifts = V128_From2x64(0, 1); + V128 power = V128_From2x64(0, a); + V128 crc = V128_From2x64(0, b); + V128 res = V128_PMulLow(power, crc); + + // Combine crc values + res = V128_ShiftLeft64(res, shifts); + return static_cast<uint32_t>(V128_Extract32<1>(res)) ^ + CRC32_u32(0, static_cast<uint32_t>(V128_Low64(res))); +} + +// Powers of crc32c polynomial, for faster ExtendByZeros. +// Verified against folly: +// folly/hash/detail/Crc32CombineDetail.cpp +constexpr uint32_t kCRC32CPowers[] = { + 0x82f63b78, 0x6ea2d55c, 0x18b8ea18, 0x510ac59a, 0xb82be955, 0xb8fdb1e7, + 0x88e56f72, 0x74c360a4, 0xe4172b16, 0x0d65762a, 0x35d73a62, 0x28461564, + 0xbf455269, 0xe2ea32dc, 0xfe7740e6, 0xf946610b, 0x3c204f8f, 0x538586e3, + 0x59726915, 0x734d5309, 0xbc1ac763, 0x7d0722cc, 0xd289cabe, 0xe94ca9bc, + 0x05b74f3f, 0xa51e1f42, 0x40000000, 0x20000000, 0x08000000, 0x00800000, + 0x00008000, 0x82f63b78, 0x6ea2d55c, 0x18b8ea18, 0x510ac59a, 0xb82be955, + 0xb8fdb1e7, 0x88e56f72, 0x74c360a4, 0xe4172b16, 0x0d65762a, 0x35d73a62, + 0x28461564, 0xbf455269, 0xe2ea32dc, 0xfe7740e6, 0xf946610b, 0x3c204f8f, + 0x538586e3, 0x59726915, 0x734d5309, 0xbc1ac763, 0x7d0722cc, 0xd289cabe, + 0xe94ca9bc, 0x05b74f3f, 0xa51e1f42, 0x40000000, 0x20000000, 0x08000000, + 0x00800000, 0x00008000, +}; + +} // namespace + +// Compute a magic constant, so that multiplying by it is the same as +// extending crc by length zeros. +uint32_t CRC32AcceleratedX86ARMCombined::ComputeZeroConstant( + size_t length) const { + // Lowest 2 bits are handled separately in ExtendByZeroes + length >>= 2; + + int index = y_absl::countr_zero(length); + uint32_t prev = kCRC32CPowers[index]; + length &= length - 1; + + while (length) { + // For each bit of length, extend by 2**n zeros. + index = y_absl::countr_zero(length); + prev = multiply(prev, kCRC32CPowers[index]); + length &= length - 1; + } + return prev; +} + +void CRC32AcceleratedX86ARMCombined::ExtendByZeroes(uint32_t* crc, + size_t length) const { + uint32_t val = *crc; + // Don't bother with multiplication for small length. + switch (length & 3) { + case 0: + break; + case 1: + val = CRC32_u8(val, 0); + break; + case 2: + val = CRC32_u16(val, 0); + break; + case 3: + val = CRC32_u8(val, 0); + val = CRC32_u16(val, 0); + break; + } + if (length > 3) { + val = multiply(val, ComputeZeroConstant(length)); + } + *crc = val; +} + +// Taken from Intel paper "Fast CRC Computation for iSCSI Polynomial Using CRC32 +// Instruction" +// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/crc-iscsi-polynomial-crc32-instruction-paper.pdf +// We only need every 4th value, because we unroll loop by 4. +constexpr uint64_t kClmulConstants[] = { + 0x09e4addf8, 0x0ba4fc28e, 0x00d3b6092, 0x09e4addf8, 0x0ab7aff2a, + 0x102f9b8a2, 0x0b9e02b86, 0x00d3b6092, 0x1bf2e8b8a, 0x18266e456, + 0x0d270f1a2, 0x0ab7aff2a, 0x11eef4f8e, 0x083348832, 0x0dd7e3b0c, + 0x0b9e02b86, 0x0271d9844, 0x1b331e26a, 0x06b749fb2, 0x1bf2e8b8a, + 0x0e6fc4e6a, 0x0ce7f39f4, 0x0d7a4825c, 0x0d270f1a2, 0x026f6a60a, + 0x12ed0daac, 0x068bce87a, 0x11eef4f8e, 0x1329d9f7e, 0x0b3e32c28, + 0x0170076fa, 0x0dd7e3b0c, 0x1fae1cc66, 0x010746f3c, 0x086d8e4d2, + 0x0271d9844, 0x0b3af077a, 0x093a5f730, 0x1d88abd4a, 0x06b749fb2, + 0x0c9c8b782, 0x0cec3662e, 0x1ddffc5d4, 0x0e6fc4e6a, 0x168763fa6, + 0x0b0cd4768, 0x19b1afbc4, 0x0d7a4825c, 0x123888b7a, 0x00167d312, + 0x133d7a042, 0x026f6a60a, 0x000bcf5f6, 0x19d34af3a, 0x1af900c24, + 0x068bce87a, 0x06d390dec, 0x16cba8aca, 0x1f16a3418, 0x1329d9f7e, + 0x19fb2a8b0, 0x02178513a, 0x1a0f717c4, 0x0170076fa, +}; + +enum class CutoffStrategy { + // Use 3 CRC streams to fold into 1. + Fold3, + // Unroll CRC instructions for 64 bytes. + Unroll64CRC, +}; + +// Base class for CRC32AcceleratedX86ARMCombinedMultipleStreams containing the +// methods and data that don't need the template arguments. +class CRC32AcceleratedX86ARMCombinedMultipleStreamsBase + : public CRC32AcceleratedX86ARMCombined { + protected: + // Update partialCRC with crc of 64 byte block. Calling FinalizePclmulStream + // would produce a single crc checksum, but it is expensive. PCLMULQDQ has a + // high latency, so we run 4 128-bit partial checksums that can be reduced to + // a single value by FinalizePclmulStream later. Computing crc for arbitrary + // polynomialas with PCLMULQDQ is described in Intel paper "Fast CRC + // Computation for Generic Polynomials Using PCLMULQDQ Instruction" + // https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf + // We are applying it to CRC32C polynomial. + Y_ABSL_ATTRIBUTE_ALWAYS_INLINE void Process64BytesPclmul( + const uint8_t* p, V128* partialCRC) const { + V128 loopMultiplicands = V128_Load(reinterpret_cast<const V128*>(k1k2)); + + V128 partialCRC1 = partialCRC[0]; + V128 partialCRC2 = partialCRC[1]; + V128 partialCRC3 = partialCRC[2]; + V128 partialCRC4 = partialCRC[3]; + + V128 tmp1 = V128_PMulHi(partialCRC1, loopMultiplicands); + V128 tmp2 = V128_PMulHi(partialCRC2, loopMultiplicands); + V128 tmp3 = V128_PMulHi(partialCRC3, loopMultiplicands); + V128 tmp4 = V128_PMulHi(partialCRC4, loopMultiplicands); + V128 data1 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 0)); + V128 data2 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 1)); + V128 data3 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 2)); + V128 data4 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 3)); + partialCRC1 = V128_PMulLow(partialCRC1, loopMultiplicands); + partialCRC2 = V128_PMulLow(partialCRC2, loopMultiplicands); + partialCRC3 = V128_PMulLow(partialCRC3, loopMultiplicands); + partialCRC4 = V128_PMulLow(partialCRC4, loopMultiplicands); + partialCRC1 = V128_Xor(tmp1, partialCRC1); + partialCRC2 = V128_Xor(tmp2, partialCRC2); + partialCRC3 = V128_Xor(tmp3, partialCRC3); + partialCRC4 = V128_Xor(tmp4, partialCRC4); + partialCRC1 = V128_Xor(partialCRC1, data1); + partialCRC2 = V128_Xor(partialCRC2, data2); + partialCRC3 = V128_Xor(partialCRC3, data3); + partialCRC4 = V128_Xor(partialCRC4, data4); + partialCRC[0] = partialCRC1; + partialCRC[1] = partialCRC2; + partialCRC[2] = partialCRC3; + partialCRC[3] = partialCRC4; + } + + // Reduce partialCRC produced by Process64BytesPclmul into a single value, + // that represents crc checksum of all the processed bytes. + Y_ABSL_ATTRIBUTE_ALWAYS_INLINE uint64_t + FinalizePclmulStream(V128* partialCRC) const { + V128 partialCRC1 = partialCRC[0]; + V128 partialCRC2 = partialCRC[1]; + V128 partialCRC3 = partialCRC[2]; + V128 partialCRC4 = partialCRC[3]; + + // Combine 4 vectors of partial crc into a single vector. + V128 reductionMultiplicands = + V128_Load(reinterpret_cast<const V128*>(k5k6)); + + V128 low = V128_PMulLow(reductionMultiplicands, partialCRC1); + V128 high = V128_PMulHi(reductionMultiplicands, partialCRC1); + + partialCRC1 = V128_Xor(low, high); + partialCRC1 = V128_Xor(partialCRC1, partialCRC2); + + low = V128_PMulLow(reductionMultiplicands, partialCRC3); + high = V128_PMulHi(reductionMultiplicands, partialCRC3); + + partialCRC3 = V128_Xor(low, high); + partialCRC3 = V128_Xor(partialCRC3, partialCRC4); + + reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k3k4)); + + low = V128_PMulLow(reductionMultiplicands, partialCRC1); + high = V128_PMulHi(reductionMultiplicands, partialCRC1); + V128 fullCRC = V128_Xor(low, high); + fullCRC = V128_Xor(fullCRC, partialCRC3); + + // Reduce fullCRC into scalar value. + reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k5k6)); + + V128 mask = V128_Load(reinterpret_cast<const V128*>(kMask)); + + V128 tmp = V128_PMul01(reductionMultiplicands, fullCRC); + fullCRC = V128_ShiftRight<8>(fullCRC); + fullCRC = V128_Xor(fullCRC, tmp); + + reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k7k0)); + + tmp = V128_ShiftRight<4>(fullCRC); + fullCRC = V128_And(fullCRC, mask); + fullCRC = V128_PMulLow(reductionMultiplicands, fullCRC); + fullCRC = V128_Xor(tmp, fullCRC); + + reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(kPoly)); + + tmp = V128_And(fullCRC, mask); + tmp = V128_PMul01(reductionMultiplicands, tmp); + tmp = V128_And(tmp, mask); + tmp = V128_PMulLow(reductionMultiplicands, tmp); + + fullCRC = V128_Xor(tmp, fullCRC); + + return static_cast<uint64_t>(V128_Extract32<1>(fullCRC)); + } + + // Update crc with 64 bytes of data from p. + Y_ABSL_ATTRIBUTE_ALWAYS_INLINE uint64_t Process64BytesCRC(const uint8_t* p, + uint64_t crc) const { + for (int i = 0; i < 8; i++) { + crc = + CRC32_u64(static_cast<uint32_t>(crc), y_absl::little_endian::Load64(p)); + p += 8; + } + return crc; + } + + // Generated by crc32c_x86_test --crc32c_generate_constants=true + // and verified against constants in linux kernel for S390: + // https://github.com/torvalds/linux/blob/master/arch/s390/crypto/crc32le-vx.S + alignas(16) static constexpr uint64_t k1k2[2] = {0x0740eef02, 0x09e4addf8}; + alignas(16) static constexpr uint64_t k3k4[2] = {0x1384aa63a, 0x0ba4fc28e}; + alignas(16) static constexpr uint64_t k5k6[2] = {0x0f20c0dfe, 0x14cd00bd6}; + alignas(16) static constexpr uint64_t k7k0[2] = {0x0dd45aab8, 0x000000000}; + alignas(16) static constexpr uint64_t kPoly[2] = {0x105ec76f0, 0x0dea713f1}; + alignas(16) static constexpr uint32_t kMask[4] = {~0u, 0u, ~0u, 0u}; + + // Medium runs of bytes are broken into groups of kGroupsSmall blocks of same + // size. Each group is CRCed in parallel then combined at the end of the + // block. + static constexpr size_t kGroupsSmall = 3; + // For large runs we use up to kMaxStreams blocks computed with CRC + // instruction, and up to kMaxStreams blocks computed with PCLMULQDQ, which + // are combined in the end. + static constexpr size_t kMaxStreams = 3; +}; + +#ifdef Y_ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +alignas(16) constexpr uint64_t + CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k1k2[2]; +alignas(16) constexpr uint64_t + CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k3k4[2]; +alignas(16) constexpr uint64_t + CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k5k6[2]; +alignas(16) constexpr uint64_t + CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k7k0[2]; +alignas(16) constexpr uint64_t + CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kPoly[2]; +alignas(16) constexpr uint32_t + CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kMask[4]; +constexpr size_t + CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kGroupsSmall; +constexpr size_t CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kMaxStreams; +#endif // Y_ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL + +template <size_t num_crc_streams, size_t num_pclmul_streams, + CutoffStrategy strategy> +class CRC32AcceleratedX86ARMCombinedMultipleStreams + : public CRC32AcceleratedX86ARMCombinedMultipleStreamsBase { + Y_ABSL_ATTRIBUTE_HOT + void Extend(uint32_t* crc, const void* bytes, size_t length) const override { + static_assert(num_crc_streams >= 1 && num_crc_streams <= kMaxStreams, + "Invalid number of crc streams"); + static_assert(num_pclmul_streams >= 0 && num_pclmul_streams <= kMaxStreams, + "Invalid number of pclmul streams"); + const uint8_t* p = static_cast<const uint8_t*>(bytes); + const uint8_t* e = p + length; + uint32_t l = *crc; + uint64_t l64; + + // We have dedicated instruction for 1,2,4 and 8 bytes. + if (length & 8) { + Y_ABSL_INTERNAL_STEP8(l, p); + length &= ~size_t{8}; + } + if (length & 4) { + Y_ABSL_INTERNAL_STEP4(l); + length &= ~size_t{4}; + } + if (length & 2) { + Y_ABSL_INTERNAL_STEP2(l); + length &= ~size_t{2}; + } + if (length & 1) { + Y_ABSL_INTERNAL_STEP1(l); + length &= ~size_t{1}; + } + if (length == 0) { + *crc = l; + return; + } + // length is now multiple of 16. + + // For small blocks just run simple loop, because cost of combining multiple + // streams is significant. + if (strategy != CutoffStrategy::Unroll64CRC) { + if (length < kSmallCutoff) { + while (length >= 16) { + Y_ABSL_INTERNAL_STEP8(l, p); + Y_ABSL_INTERNAL_STEP8(l, p); + length -= 16; + } + *crc = l; + return; + } + } + + // For medium blocks we run 3 crc streams and combine them as described in + // Intel paper above. Running 4th stream doesn't help, because crc + // instruction has latency 3 and throughput 1. + if (length < kMediumCutoff) { + l64 = l; + if (strategy == CutoffStrategy::Fold3) { + uint64_t l641 = 0; + uint64_t l642 = 0; + const size_t blockSize = 32; + size_t bs = static_cast<size_t>(e - p) / kGroupsSmall / blockSize; + const uint8_t* p1 = p + bs * blockSize; + const uint8_t* p2 = p1 + bs * blockSize; + + for (size_t i = 0; i + 1 < bs; ++i) { + Y_ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2); + Y_ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2); + Y_ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2); + Y_ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2); + base_internal::PrefetchT0( + reinterpret_cast<const char*>(p + kPrefetchHorizonMedium)); + base_internal::PrefetchT0( + reinterpret_cast<const char*>(p1 + kPrefetchHorizonMedium)); + base_internal::PrefetchT0( + reinterpret_cast<const char*>(p2 + kPrefetchHorizonMedium)); + } + // Don't run crc on last 8 bytes. + Y_ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2); + Y_ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2); + Y_ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2); + Y_ABSL_INTERNAL_STEP8BY2(l64, l641, p, p1); + + V128 magic = *(reinterpret_cast<const V128*>(kClmulConstants) + bs - 1); + + V128 tmp = V128_From2x64(0, l64); + + V128 res1 = V128_PMulLow(tmp, magic); + + tmp = V128_From2x64(0, l641); + + V128 res2 = V128_PMul10(tmp, magic); + V128 x = V128_Xor(res1, res2); + l64 = static_cast<uint64_t>(V128_Low64(x)) ^ + y_absl::little_endian::Load64(p2); + l64 = CRC32_u64(static_cast<uint32_t>(l642), l64); + + p = p2 + 8; + } else if (strategy == CutoffStrategy::Unroll64CRC) { + while ((e - p) >= 64) { + l64 = Process64BytesCRC(p, l64); + p += 64; + } + } + } else { + // There is a lot of data, we can ignore combine costs and run all + // requested streams (num_crc_streams + num_pclmul_streams), + // using prefetch. CRC and PCLMULQDQ use different cpu execution units, + // so on some cpus it makes sense to execute both of them for different + // streams. + + // Point x at first 8-byte aligned byte in string. + const uint8_t* x = RoundUp<8>(p); + // Process bytes until p is 8-byte aligned, if that isn't past the end. + while (p != x) { + Y_ABSL_INTERNAL_STEP1(l); + } + + size_t bs = static_cast<size_t>(e - p) / + (num_crc_streams + num_pclmul_streams) / 64; + const uint8_t* crc_streams[kMaxStreams]; + const uint8_t* pclmul_streams[kMaxStreams]; + // We are guaranteed to have at least one crc stream. + crc_streams[0] = p; + for (size_t i = 1; i < num_crc_streams; i++) { + crc_streams[i] = crc_streams[i - 1] + bs * 64; + } + pclmul_streams[0] = crc_streams[num_crc_streams - 1] + bs * 64; + for (size_t i = 1; i < num_pclmul_streams; i++) { + pclmul_streams[i] = pclmul_streams[i - 1] + bs * 64; + } + + // Per stream crc sums. + uint64_t l64_crc[kMaxStreams] = {l}; + uint64_t l64_pclmul[kMaxStreams] = {0}; + + // Peel first iteration, because PCLMULQDQ stream, needs setup. + for (size_t i = 0; i < num_crc_streams; i++) { + l64_crc[i] = Process64BytesCRC(crc_streams[i], l64_crc[i]); + crc_streams[i] += 16 * 4; + } + + V128 partialCRC[kMaxStreams][4]; + for (size_t i = 0; i < num_pclmul_streams; i++) { + partialCRC[i][0] = V128_LoadU( + reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 0)); + partialCRC[i][1] = V128_LoadU( + reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 1)); + partialCRC[i][2] = V128_LoadU( + reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 2)); + partialCRC[i][3] = V128_LoadU( + reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 3)); + pclmul_streams[i] += 16 * 4; + } + + for (size_t i = 1; i < bs; i++) { + // Prefetch data for next itterations. + for (size_t j = 0; j < num_crc_streams; j++) { + base_internal::PrefetchT0( + reinterpret_cast<const char*>(crc_streams[j] + kPrefetchHorizon)); + } + for (size_t j = 0; j < num_pclmul_streams; j++) { + base_internal::PrefetchT0(reinterpret_cast<const char*>( + pclmul_streams[j] + kPrefetchHorizon)); + } + + // We process each stream in 64 byte blocks. This can be written as + // for (int i = 0; i < num_pclmul_streams; i++) { + // Process64BytesPclmul(pclmul_streams[i], partialCRC[i]); + // pclmul_streams[i] += 16 * 4; + // } + // for (int i = 0; i < num_crc_streams; i++) { + // l64_crc[i] = Process64BytesCRC(crc_streams[i], l64_crc[i]); + // crc_streams[i] += 16*4; + // } + // But unrolling and interleaving PCLMULQDQ and CRC blocks manually + // gives ~2% performance boost. + l64_crc[0] = Process64BytesCRC(crc_streams[0], l64_crc[0]); + crc_streams[0] += 16 * 4; + if (num_pclmul_streams > 0) { + Process64BytesPclmul(pclmul_streams[0], partialCRC[0]); + pclmul_streams[0] += 16 * 4; + } + if (num_crc_streams > 1) { + l64_crc[1] = Process64BytesCRC(crc_streams[1], l64_crc[1]); + crc_streams[1] += 16 * 4; + } + if (num_pclmul_streams > 1) { + Process64BytesPclmul(pclmul_streams[1], partialCRC[1]); + pclmul_streams[1] += 16 * 4; + } + if (num_crc_streams > 2) { + l64_crc[2] = Process64BytesCRC(crc_streams[2], l64_crc[2]); + crc_streams[2] += 16 * 4; + } + if (num_pclmul_streams > 2) { + Process64BytesPclmul(pclmul_streams[2], partialCRC[2]); + pclmul_streams[2] += 16 * 4; + } + } + + // PCLMULQDQ based streams require special final step; + // CRC based don't. + for (size_t i = 0; i < num_pclmul_streams; i++) { + l64_pclmul[i] = FinalizePclmulStream(partialCRC[i]); + } + + // Combine all streams into single result. + uint32_t magic = ComputeZeroConstant(bs * 64); + l64 = l64_crc[0]; + for (size_t i = 1; i < num_crc_streams; i++) { + l64 = multiply(static_cast<uint32_t>(l64), magic); + l64 ^= l64_crc[i]; + } + for (size_t i = 0; i < num_pclmul_streams; i++) { + l64 = multiply(static_cast<uint32_t>(l64), magic); + l64 ^= l64_pclmul[i]; + } + + // Update p. + if (num_pclmul_streams > 0) { + p = pclmul_streams[num_pclmul_streams - 1]; + } else { + p = crc_streams[num_crc_streams - 1]; + } + } + l = static_cast<uint32_t>(l64); + + while ((e - p) >= 16) { + Y_ABSL_INTERNAL_STEP8(l, p); + Y_ABSL_INTERNAL_STEP8(l, p); + } + // Process the last few bytes + while (p != e) { + Y_ABSL_INTERNAL_STEP1(l); + } + +#undef Y_ABSL_INTERNAL_STEP8BY3 +#undef Y_ABSL_INTERNAL_STEP8BY2 +#undef Y_ABSL_INTERNAL_STEP8 +#undef Y_ABSL_INTERNAL_STEP4 +#undef Y_ABSL_INTERNAL_STEP2 +#undef Y_ABSL_INTERNAL_STEP1 + + *crc = l; + } +}; + +} // namespace + +// Intel processors with SSE4.2 have an instruction for one particular +// 32-bit CRC polynomial: crc32c +CRCImpl* TryNewCRC32AcceleratedX86ARMCombined() { + CpuType type = GetCpuType(); + switch (type) { + case CpuType::kIntelHaswell: + case CpuType::kAmdRome: + case CpuType::kAmdNaples: + case CpuType::kAmdMilan: + return new CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 1, CutoffStrategy::Fold3>(); + // PCLMULQDQ is fast, use combined PCLMULQDQ + CRC implementation. + case CpuType::kIntelCascadelakeXeon: + case CpuType::kIntelSkylakeXeon: + case CpuType::kIntelBroadwell: + case CpuType::kIntelSkylake: + return new CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 2, CutoffStrategy::Fold3>(); + // PCLMULQDQ is slow, don't use it. + case CpuType::kIntelIvybridge: + case CpuType::kIntelSandybridge: + case CpuType::kIntelWestmere: + return new CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 0, CutoffStrategy::Fold3>(); + case CpuType::kArmNeoverseN1: + return new CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 1, CutoffStrategy::Unroll64CRC>(); +#if defined(__aarch64__) + default: + // Not all ARM processors support the needed instructions, so check here + // before trying to use an accelerated implementation. + if (SupportsArmCRC32PMULL()) { + return new CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 1, CutoffStrategy::Unroll64CRC>(); + } else { + return nullptr; + } +#else + default: + // Something else, play it safe and assume slow PCLMULQDQ. + return new CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 0, CutoffStrategy::Fold3>(); +#endif + } +} + +std::vector<std::unique_ptr<CRCImpl>> NewCRC32AcceleratedX86ARMCombinedAll() { + auto ret = std::vector<std::unique_ptr<CRCImpl>>(); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 0, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 1, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 2, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 3, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 2, 0, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 2, 1, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 2, 2, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 2, 3, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 0, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 1, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 2, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 3, CutoffStrategy::Fold3>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 0, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 1, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 2, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 1, 3, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 2, 0, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 2, 1, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 2, 2, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 2, 3, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 0, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 1, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 2, CutoffStrategy::Unroll64CRC>>()); + ret.push_back(y_absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams< + 3, 3, CutoffStrategy::Unroll64CRC>>()); + + return ret; +} + +#else // !Y_ABSL_INTERNAL_CAN_USE_SIMD_CRC32C + +std::vector<std::unique_ptr<CRCImpl>> NewCRC32AcceleratedX86ARMCombinedAll() { + return std::vector<std::unique_ptr<CRCImpl>>(); +} + +// no hardware acceleration available +CRCImpl* TryNewCRC32AcceleratedX86ARMCombined() { return nullptr; } + +#endif + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/non_temporal_arm_intrinsics.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/non_temporal_arm_intrinsics.h new file mode 100644 index 0000000000..7a1dbf9a83 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/non_temporal_arm_intrinsics.h @@ -0,0 +1,79 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_ +#define Y_ABSL_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_ + +#include "y_absl/base/config.h" + +#ifdef __aarch64__ +#include <arm_neon.h> + +typedef int64x2_t __m128i; /* 128-bit vector containing integers */ +#define vreinterpretq_m128i_s32(x) vreinterpretq_s64_s32(x) +#define vreinterpretq_s64_m128i(x) (x) + +// Guarantees that every preceding store is globally visible before any +// subsequent store. +// https://msdn.microsoft.com/en-us/library/5h2w73d1%28v=vs.90%29.aspx +static inline __attribute__((always_inline)) void _mm_sfence(void) { + __sync_synchronize(); +} + +// Load 128-bits of integer data from unaligned memory into dst. This intrinsic +// may perform better than _mm_loadu_si128 when the data crosses a cache line +// boundary. +// +// dst[127:0] := MEM[mem_addr+127:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_lddqu_si128 +#define _mm_lddqu_si128 _mm_loadu_si128 + +// Loads 128-bit value. : +// https://msdn.microsoft.com/zh-cn/library/f4k12ae8(v=vs.90).aspx +static inline __attribute__((always_inline)) __m128i _mm_loadu_si128( + const __m128i *p) { + return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *)p)); +} + +// Stores the data in a to the address p without polluting the caches. If the +// cache line containing address p is already in the cache, the cache will be +// updated. +// https://msdn.microsoft.com/en-us/library/ba08y07y%28v=vs.90%29.aspx +static inline __attribute__((always_inline)) void _mm_stream_si128(__m128i *p, + __m128i a) { +#if Y_ABSL_HAVE_BUILTIN(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, p); +#else + vst1q_s64((int64_t *)p, vreinterpretq_s64_m128i(a)); +#endif +} + +// Sets the 16 signed 8-bit integer values. +// https://msdn.microsoft.com/en-us/library/x0cx8zd3(v=vs.90).aspx +static inline __attribute__((always_inline)) __m128i _mm_set_epi8( + signed char b15, signed char b14, signed char b13, signed char b12, + signed char b11, signed char b10, signed char b9, signed char b8, + signed char b7, signed char b6, signed char b5, signed char b4, + signed char b3, signed char b2, signed char b1, signed char b0) { + int8_t __attribute__((aligned(16))) + data[16] = {(int8_t)b0, (int8_t)b1, (int8_t)b2, (int8_t)b3, + (int8_t)b4, (int8_t)b5, (int8_t)b6, (int8_t)b7, + (int8_t)b8, (int8_t)b9, (int8_t)b10, (int8_t)b11, + (int8_t)b12, (int8_t)b13, (int8_t)b14, (int8_t)b15}; + return (__m128i)vld1q_s8(data); +} +#endif // __aarch64__ + +#endif // Y_ABSL_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/non_temporal_memcpy.h b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/non_temporal_memcpy.h new file mode 100644 index 0000000000..fce0007046 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/non_temporal_memcpy.h @@ -0,0 +1,180 @@ +// Copyright 2022 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 Y_ABSL_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_ +#define Y_ABSL_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_ + +#ifdef _MSC_VER +#include <intrin.h> +#endif + +#ifdef __SSE__ +#include <xmmintrin.h> +#endif + +#ifdef __SSE2__ +#include <emmintrin.h> +#endif + +#ifdef __SSE3__ +#include <pmmintrin.h> +#endif + +#ifdef __AVX__ +#include <immintrin.h> +#endif + +#ifdef __aarch64__ +#include "y_absl/crc/internal/non_temporal_arm_intrinsics.h" +#endif + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> + +#include "y_absl/base/config.h" +#include "y_absl/base/optimization.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace crc_internal { + +// This non-temporal memcpy does regular load and non-temporal store memory +// copy. It is compatible to both 16-byte aligned and unaligned addresses. If +// data at the destination is not immediately accessed, using non-temporal +// memcpy can save 1 DRAM load of the destination cacheline. +constexpr size_t kCacheLineSize = Y_ABSL_CACHELINE_SIZE; + +// If the objects overlap, the behavior is undefined. +inline void *non_temporal_store_memcpy(void *__restrict dst, + const void *__restrict src, size_t len) { +#if defined(__SSE3__) || defined(__aarch64__) || \ + (defined(_MSC_VER) && defined(__AVX__)) + // This implementation requires SSE3. + // MSVC cannot target SSE3 directly, but when MSVC targets AVX, + // SSE3 support is implied. + uint8_t *d = reinterpret_cast<uint8_t *>(dst); + const uint8_t *s = reinterpret_cast<const uint8_t *>(src); + + // memcpy() the misaligned header. At the end of this if block, <d> is + // aligned to a 64-byte cacheline boundary or <len> == 0. + if (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1)) { + uintptr_t bytes_before_alignment_boundary = + kCacheLineSize - + (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1)); + size_t header_len = (std::min)(bytes_before_alignment_boundary, len); + assert(bytes_before_alignment_boundary < kCacheLineSize); + memcpy(d, s, header_len); + d += header_len; + s += header_len; + len -= header_len; + } + + if (len >= kCacheLineSize) { + _mm_sfence(); + __m128i *dst_cacheline = reinterpret_cast<__m128i *>(d); + const __m128i *src_cacheline = reinterpret_cast<const __m128i *>(s); + constexpr int kOpsPerCacheLine = kCacheLineSize / sizeof(__m128i); + size_t loops = len / kCacheLineSize; + + while (len >= kCacheLineSize) { + __m128i temp1, temp2, temp3, temp4; + temp1 = _mm_lddqu_si128(src_cacheline + 0); + temp2 = _mm_lddqu_si128(src_cacheline + 1); + temp3 = _mm_lddqu_si128(src_cacheline + 2); + temp4 = _mm_lddqu_si128(src_cacheline + 3); + _mm_stream_si128(dst_cacheline + 0, temp1); + _mm_stream_si128(dst_cacheline + 1, temp2); + _mm_stream_si128(dst_cacheline + 2, temp3); + _mm_stream_si128(dst_cacheline + 3, temp4); + src_cacheline += kOpsPerCacheLine; + dst_cacheline += kOpsPerCacheLine; + len -= kCacheLineSize; + } + d += loops * kCacheLineSize; + s += loops * kCacheLineSize; + _mm_sfence(); + } + + // memcpy the tail. + if (len) { + memcpy(d, s, len); + } + return dst; +#else + // Fallback to regular memcpy. + return memcpy(dst, src, len); +#endif // __SSE3__ || __aarch64__ || (_MSC_VER && __AVX__) +} + +inline void *non_temporal_store_memcpy_avx(void *__restrict dst, + const void *__restrict src, + size_t len) { +#ifdef __AVX__ + uint8_t *d = reinterpret_cast<uint8_t *>(dst); + const uint8_t *s = reinterpret_cast<const uint8_t *>(src); + + // memcpy() the misaligned header. At the end of this if block, <d> is + // aligned to a 64-byte cacheline boundary or <len> == 0. + if (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1)) { + uintptr_t bytes_before_alignment_boundary = + kCacheLineSize - + (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1)); + size_t header_len = (std::min)(bytes_before_alignment_boundary, len); + assert(bytes_before_alignment_boundary < kCacheLineSize); + memcpy(d, s, header_len); + d += header_len; + s += header_len; + len -= header_len; + } + + if (len >= kCacheLineSize) { + _mm_sfence(); + __m256i *dst_cacheline = reinterpret_cast<__m256i *>(d); + const __m256i *src_cacheline = reinterpret_cast<const __m256i *>(s); + constexpr int kOpsPerCacheLine = kCacheLineSize / sizeof(__m256i); + size_t loops = len / kCacheLineSize; + + while (len >= kCacheLineSize) { + __m256i temp1, temp2; + temp1 = _mm256_lddqu_si256(src_cacheline + 0); + temp2 = _mm256_lddqu_si256(src_cacheline + 1); + _mm256_stream_si256(dst_cacheline + 0, temp1); + _mm256_stream_si256(dst_cacheline + 1, temp2); + src_cacheline += kOpsPerCacheLine; + dst_cacheline += kOpsPerCacheLine; + len -= kCacheLineSize; + } + d += loops * kCacheLineSize; + s += loops * kCacheLineSize; + _mm_sfence(); + } + + // memcpy the tail. + if (len) { + memcpy(d, s, len); + } + return dst; +#else + // Fallback to regular memcpy when AVX is not available. + return memcpy(dst, src, len); +#endif // __AVX__ +} + +} // namespace crc_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/failure_signal_handler.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/failure_signal_handler.cc index 9e35a345d9..25ed376e0f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/failure_signal_handler.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/failure_signal_handler.cc @@ -50,7 +50,9 @@ #ifndef _WIN32 #define Y_ABSL_HAVE_SIGACTION // Apple WatchOS and TVOS don't allow sigaltstack -#if !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \ +// Apple macOS has sigaltstack, but using it makes backtrace() unusable. +#if !(defined(TARGET_OS_OSX) && TARGET_OS_OSX) && \ + !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \ !(defined(TARGET_OS_TV) && TARGET_OS_TV) && !defined(__QNX__) #define Y_ABSL_HAVE_SIGALTSTACK #endif @@ -133,10 +135,11 @@ static bool SetupAlternateStackOnce() { #if defined(__wasm__) || defined (__asjms__) const size_t page_mask = getpagesize() - 1; #else - const size_t page_mask = sysconf(_SC_PAGESIZE) - 1; + const size_t page_mask = static_cast<size_t>(sysconf(_SC_PAGESIZE)) - 1; #endif size_t stack_size = - (std::max<size_t>(SIGSTKSZ, 65536) + page_mask) & ~page_mask; + (std::max(static_cast<size_t>(SIGSTKSZ), size_t{65536}) + page_mask) & + ~page_mask; #if defined(Y_ABSL_HAVE_ADDRESS_SANITIZER) || \ defined(Y_ABSL_HAVE_MEMORY_SANITIZER) || defined(Y_ABSL_HAVE_THREAD_SANITIZER) // Account for sanitizer instrumentation requiring additional stack space. @@ -216,7 +219,7 @@ static void InstallOneFailureHandler(FailureSignalData* data, #endif static void WriteToStderr(const char* data) { - y_absl::raw_logging_internal::AsyncSignalSafeWriteToStderr(data, strlen(data)); + y_absl::raw_log_internal::AsyncSignalSafeWriteToStderr(data, strlen(data)); } static void WriteSignalMessage(int signo, int cpu, @@ -289,7 +292,7 @@ static void WriteFailureInfo(int signo, void* ucontext, int cpu, // some platforms. static void PortableSleepForSeconds(int seconds) { #ifdef _WIN32 - Sleep(seconds * 1000); + Sleep(static_cast<DWORD>(seconds * 1000)); #else struct timespec sleep_time; sleep_time.tv_sec = seconds; @@ -323,9 +326,9 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) { const GetTidType this_tid = y_absl::base_internal::GetTID(); GetTidType previous_failed_tid = 0; - if (!failed_tid.compare_exchange_strong( - previous_failed_tid, static_cast<intptr_t>(this_tid), - std::memory_order_acq_rel, std::memory_order_relaxed)) { + if (!failed_tid.compare_exchange_strong(previous_failed_tid, this_tid, + std::memory_order_acq_rel, + std::memory_order_relaxed)) { Y_ABSL_RAW_LOG( ERROR, "Signal %d raised at PC=%p while already in AbslFailureSignalHandler()", @@ -354,7 +357,7 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) { if (fsh_options.alarm_on_failure_secs > 0) { alarm(0); // Cancel any existing alarms. signal(SIGALRM, ImmediateAbortSignalHandler); - alarm(fsh_options.alarm_on_failure_secs); + alarm(static_cast<unsigned int>(fsh_options.alarm_on_failure_secs)); } #endif diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/address_is_readable.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/address_is_readable.cc index ccfed8bc42..3e0ce9fc6c 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/address_is_readable.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/address_is_readable.cc @@ -52,7 +52,7 @@ namespace debugging_internal { bool AddressIsReadable(const void *addr) { // Align address on 8-byte boundary. On aarch64, checking last // byte before inaccessible page returned unexpected EFAULT. - const uintptr_t u_addr = reinterpret_cast<uintptr_t>(addr) & ~7; + const uintptr_t u_addr = reinterpret_cast<uintptr_t>(addr) & ~uintptr_t{7}; addr = reinterpret_cast<const void *>(u_addr); // rt_sigprocmask below will succeed for this input. diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/demangle.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/demangle.cc index e9319575cb..d9a1e33ed8 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/demangle.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/demangle.cc @@ -151,12 +151,12 @@ static const AbbrevPair kSubstitutionList[] = { // State needed for demangling. This struct is copied in almost every stack // frame, so every byte counts. typedef struct { - int mangled_idx; // Cursor of mangled name. - int out_cur_idx; // Cursor of output string. - int prev_name_idx; // For constructors/destructors. - signed int prev_name_length : 16; // For constructors/destructors. - signed int nest_level : 15; // For nested names. - unsigned int append : 1; // Append flag. + int mangled_idx; // Cursor of mangled name. + int out_cur_idx; // Cursor of output string. + int prev_name_idx; // For constructors/destructors. + unsigned int prev_name_length : 16; // For constructors/destructors. + signed int nest_level : 15; // For nested names. + unsigned int append : 1; // Append flag. // Note: for some reason MSVC can't pack "bool append : 1" into the same int // with the above two fields, so we use an int instead. Amusingly it can pack // "signed bool" as expected, but relying on that to continue to be a legal @@ -235,8 +235,8 @@ static size_t StrLen(const char *str) { } // Returns true if "str" has at least "n" characters remaining. -static bool AtLeastNumCharsRemaining(const char *str, int n) { - for (int i = 0; i < n; ++i) { +static bool AtLeastNumCharsRemaining(const char *str, size_t n) { + for (size_t i = 0; i < n; ++i) { if (str[i] == '\0') { return false; } @@ -253,18 +253,20 @@ static bool StrPrefix(const char *str, const char *prefix) { return prefix[i] == '\0'; // Consumed everything in "prefix". } -static void InitState(State *state, const char *mangled, char *out, - int out_size) { +static void InitState(State* state, + const char* mangled, + char* out, + size_t out_size) { state->mangled_begin = mangled; state->out = out; - state->out_end_idx = out_size; + state->out_end_idx = static_cast<int>(out_size); state->recursion_depth = 0; state->steps = 0; state->parse_state.mangled_idx = 0; state->parse_state.out_cur_idx = 0; state->parse_state.prev_name_idx = 0; - state->parse_state.prev_name_length = -1; + state->parse_state.prev_name_length = 0; state->parse_state.nest_level = -1; state->parse_state.append = true; } @@ -356,8 +358,8 @@ static bool ZeroOrMore(ParseFunc parse_func, State *state) { // Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is // set to out_end_idx+1. The output string is ensured to // always terminate with '\0' as long as there is no overflow. -static void Append(State *state, const char *const str, const int length) { - for (int i = 0; i < length; ++i) { +static void Append(State *state, const char *const str, const size_t length) { + for (size_t i = 0; i < length; ++i) { if (state->parse_state.out_cur_idx + 1 < state->out_end_idx) { // +1 for '\0' state->out[state->parse_state.out_cur_idx++] = str[i]; @@ -420,7 +422,7 @@ static bool EndsWith(State *state, const char chr) { // Append "str" with some tweaks, iff "append" state is true. static void MaybeAppendWithLength(State *state, const char *const str, - const int length) { + const size_t length) { if (state->parse_state.append && length > 0) { // Append a space if the output buffer ends with '<' and "str" // starts with '<' to avoid <<<. @@ -432,14 +434,14 @@ static void MaybeAppendWithLength(State *state, const char *const str, if (state->parse_state.out_cur_idx < state->out_end_idx && (IsAlpha(str[0]) || str[0] == '_')) { state->parse_state.prev_name_idx = state->parse_state.out_cur_idx; - state->parse_state.prev_name_length = length; + state->parse_state.prev_name_length = static_cast<unsigned int>(length); } Append(state, str, length); } } // Appends a positive decimal number to the output if appending is enabled. -static bool MaybeAppendDecimal(State *state, unsigned int val) { +static bool MaybeAppendDecimal(State *state, int val) { // Max {32-64}-bit unsigned int is 20 digits. constexpr size_t kMaxLength = 20; char buf[kMaxLength]; @@ -451,12 +453,12 @@ static bool MaybeAppendDecimal(State *state, unsigned int val) { // one-past-the-end and manipulate one character before the pointer. char *p = &buf[kMaxLength]; do { // val=0 is the only input that should write a leading zero digit. - *--p = (val % 10) + '0'; + *--p = static_cast<char>((val % 10) + '0'); val /= 10; } while (p > buf && val != 0); // 'p' landed on the last character we set. How convenient. - Append(state, p, kMaxLength - (p - buf)); + Append(state, p, kMaxLength - static_cast<size_t>(p - buf)); } return true; @@ -466,7 +468,7 @@ static bool MaybeAppendDecimal(State *state, unsigned int val) { // Returns true so that it can be placed in "if" conditions. static bool MaybeAppend(State *state, const char *const str) { if (state->parse_state.append) { - int length = StrLen(str); + size_t length = StrLen(str); MaybeAppendWithLength(state, str, length); } return true; @@ -521,10 +523,10 @@ static void MaybeCancelLastSeparator(State *state) { // Returns true if the identifier of the given length pointed to by // "mangled_cur" is anonymous namespace. -static bool IdentifierIsAnonymousNamespace(State *state, int length) { +static bool IdentifierIsAnonymousNamespace(State *state, size_t length) { // Returns true if "anon_prefix" is a proper prefix of "mangled_cur". static const char anon_prefix[] = "_GLOBAL__N_"; - return (length > static_cast<int>(sizeof(anon_prefix) - 1) && + return (length > (sizeof(anon_prefix) - 1) && StrPrefix(RemainingInput(state), anon_prefix)); } @@ -542,12 +544,13 @@ static bool ParseUnnamedTypeName(State *state); static bool ParseNumber(State *state, int *number_out); static bool ParseFloatNumber(State *state); static bool ParseSeqId(State *state); -static bool ParseIdentifier(State *state, int length); +static bool ParseIdentifier(State *state, size_t length); static bool ParseOperatorName(State *state, int *arity); static bool ParseSpecialName(State *state); static bool ParseCallOffset(State *state); static bool ParseNVOffset(State *state); static bool ParseVOffset(State *state); +static bool ParseAbiTags(State *state); static bool ParseCtorDtorName(State *state); static bool ParseDecltype(State *state); static bool ParseType(State *state); @@ -601,7 +604,7 @@ static bool ParseSubstitution(State *state, bool accept_std); // // Reference: // - Itanium C++ ABI -// <https://mentorembedded.github.io/cxx-abi/abi.html#mangling> +// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling> // <mangled-name> ::= _Z <encoding> static bool ParseMangledName(State *state) { @@ -741,17 +744,42 @@ static bool ParsePrefix(State *state) { return true; } -// <unqualified-name> ::= <operator-name> -// ::= <ctor-dtor-name> -// ::= <source-name> -// ::= <local-source-name> // GCC extension; see below. -// ::= <unnamed-type-name> +// <unqualified-name> ::= <operator-name> [<abi-tags>] +// ::= <ctor-dtor-name> [<abi-tags>] +// ::= <source-name> [<abi-tags>] +// ::= <local-source-name> [<abi-tags>] +// ::= <unnamed-type-name> [<abi-tags>] +// +// <local-source-name> is a GCC extension; see below. static bool ParseUnqualifiedName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; - return (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || - ParseSourceName(state) || ParseLocalSourceName(state) || - ParseUnnamedTypeName(state)); + if (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || + ParseSourceName(state) || ParseLocalSourceName(state) || + ParseUnnamedTypeName(state)) { + return ParseAbiTags(state); + } + return false; +} + +// <abi-tags> ::= <abi-tag> [<abi-tags>] +// <abi-tag> ::= B <source-name> +static bool ParseAbiTags(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + while (ParseOneCharToken(state, 'B')) { + ParseState copy = state->parse_state; + MaybeAppend(state, "[abi:"); + + if (!ParseSourceName(state)) { + state->parse_state = copy; + return false; + } + MaybeAppend(state, "]"); + } + + return true; } // <source-name> ::= <positive length number> <identifier> @@ -760,7 +788,8 @@ static bool ParseSourceName(State *state) { if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; int length = -1; - if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { + if (ParseNumber(state, &length) && + ParseIdentifier(state, static_cast<size_t>(length))) { return true; } state->parse_state = copy; @@ -838,7 +867,7 @@ static bool ParseNumber(State *state, int *number_out) { uint64_t number = 0; for (; *p != '\0'; ++p) { if (IsDigit(*p)) { - number = number * 10 + (*p - '0'); + number = number * 10 + static_cast<uint64_t>(*p - '0'); } else { break; } @@ -853,7 +882,7 @@ static bool ParseNumber(State *state, int *number_out) { state->parse_state.mangled_idx += p - RemainingInput(state); if (number_out != nullptr) { // Note: possibly truncate "number". - *number_out = number; + *number_out = static_cast<int>(number); } return true; } @@ -897,10 +926,10 @@ static bool ParseSeqId(State *state) { } // <identifier> ::= <unqualified source code identifier> (of given length) -static bool ParseIdentifier(State *state, int length) { +static bool ParseIdentifier(State *state, size_t length) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; - if (length < 0 || !AtLeastNumCharsRemaining(RemainingInput(state), length)) { + if (!AtLeastNumCharsRemaining(RemainingInput(state), length)) { return false; } if (IdentifierIsAnonymousNamespace(state, length)) { @@ -1947,7 +1976,7 @@ static bool Overflowed(const State *state) { } // The demangler entry point. -bool Demangle(const char *mangled, char *out, int out_size) { +bool Demangle(const char* mangled, char* out, size_t out_size) { State state; InitState(&state, mangled, out, out_size); return ParseTopLevelMangledName(&state) && !Overflowed(&state) && diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/demangle.h b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/demangle.h index 28d4dfb0e9..5ed51a1547 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/demangle.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/demangle.h @@ -62,7 +62,7 @@ namespace debugging_internal { // Demangle `mangled`. On success, return true and write the // demangled symbol name to `out`. Otherwise, return false. // `out` is modified even if demangling is unsuccessful. -bool Demangle(const char *mangled, char *out, int out_size); +bool Demangle(const char* mangled, char* out, size_t out_size); } // namespace debugging_internal Y_ABSL_NAMESPACE_END diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/elf_mem_image.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/elf_mem_image.cc index 0a82359e27..cead525c93 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/elf_mem_image.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/elf_mem_image.cc @@ -91,7 +91,7 @@ int ElfMemImage::GetNumSymbols() const { return 0; } // See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash - return hash_[1]; + return static_cast<int>(hash_[1]); } const ElfW(Sym) *ElfMemImage::GetDynsym(int index) const { @@ -105,11 +105,9 @@ const ElfW(Versym) *ElfMemImage::GetVersym(int index) const { } const ElfW(Phdr) *ElfMemImage::GetPhdr(int index) const { - Y_ABSL_RAW_CHECK(index < ehdr_->e_phnum, "index out of range"); - return GetTableElement<ElfW(Phdr)>(ehdr_, - ehdr_->e_phoff, - ehdr_->e_phentsize, - index); + Y_ABSL_RAW_CHECK(index >= 0 && index < ehdr_->e_phnum, "index out of range"); + return GetTableElement<ElfW(Phdr)>(ehdr_, ehdr_->e_phoff, ehdr_->e_phentsize, + static_cast<size_t>(index)); } const char *ElfMemImage::GetDynstr(ElfW(Word) offset) const { @@ -159,7 +157,8 @@ void ElfMemImage::Init(const void *base) { hash_ = nullptr; strsize_ = 0; verdefnum_ = 0; - link_base_ = ~0L; // Sentinel: PT_LOAD .p_vaddr can't possibly be this. + // Sentinel: PT_LOAD .p_vaddr can't possibly be this. + link_base_ = ~ElfW(Addr){0}; // NOLINT(readability/braces) if (!base) { return; } @@ -218,11 +217,11 @@ void ElfMemImage::Init(const void *base) { } ptrdiff_t relocation = base_as_char - reinterpret_cast<const char *>(link_base_); - ElfW(Dyn) *dynamic_entry = - reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr + - relocation); + ElfW(Dyn)* dynamic_entry = reinterpret_cast<ElfW(Dyn)*>( + static_cast<intptr_t>(dynamic_program_header->p_vaddr) + relocation); for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) { - const auto value = dynamic_entry->d_un.d_val + relocation; + const auto value = + static_cast<intptr_t>(dynamic_entry->d_un.d_val) + relocation; switch (dynamic_entry->d_tag) { case DT_HASH: hash_ = reinterpret_cast<ElfW(Word) *>(value); @@ -240,10 +239,10 @@ void ElfMemImage::Init(const void *base) { verdef_ = reinterpret_cast<ElfW(Verdef) *>(value); break; case DT_VERDEFNUM: - verdefnum_ = dynamic_entry->d_un.d_val; + verdefnum_ = static_cast<size_t>(dynamic_entry->d_un.d_val); break; case DT_STRSZ: - strsize_ = dynamic_entry->d_un.d_val; + strsize_ = static_cast<size_t>(dynamic_entry->d_un.d_val); break; default: // Unrecognized entries explicitly ignored. diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/examine_stack.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/examine_stack.cc index 3f1c0b8b79..62d600f5a8 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/examine_stack.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/examine_stack.cc @@ -278,13 +278,14 @@ void DumpStackTrace(int min_dropped_frames, int max_num_frames, void* stack_buf[kDefaultDumpStackFramesLimit]; void** stack = stack_buf; int num_stack = kDefaultDumpStackFramesLimit; - int allocated_bytes = 0; + size_t allocated_bytes = 0; if (num_stack >= max_num_frames) { // User requested fewer frames than we already have space for. num_stack = max_num_frames; } else { - const size_t needed_bytes = max_num_frames * sizeof(stack[0]); + const size_t needed_bytes = + static_cast<size_t>(max_num_frames) * sizeof(stack[0]); void* p = Allocate(needed_bytes); if (p != nullptr) { // We got the space. num_stack = max_num_frames; @@ -293,12 +294,13 @@ void DumpStackTrace(int min_dropped_frames, int max_num_frames, } } - size_t depth = y_absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1); - for (size_t i = 0; i < depth; i++) { + int depth = y_absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1); + for (int i = 0; i < depth; i++) { if (symbolize_stacktrace) { - DumpPCAndSymbol(writer, writer_arg, stack[i], " "); + DumpPCAndSymbol(writer, writer_arg, stack[static_cast<size_t>(i)], + " "); } else { - DumpPC(writer, writer_arg, stack[i], " "); + DumpPC(writer, writer_arg, stack[static_cast<size_t>(i)], " "); } } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_aarch64-inl.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_aarch64-inl.inc index d33f47e1cd..b9a7919222 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_aarch64-inl.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -19,7 +19,7 @@ #include "y_absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems #include "y_absl/debugging/stacktrace.h" -static const uintptr_t kUnknownFrameSize = 0; +static const size_t kUnknownFrameSize = 0; #if defined(__linux__) // Returns the address of the VDSO __kernel_rt_sigreturn function, if present. @@ -65,11 +65,12 @@ static const unsigned char* GetKernelRtSigreturnAddress() { // Compute the size of a stack frame in [low..high). We assume that // low < high. Return size of kUnknownFrameSize. template<typename T> -static inline uintptr_t ComputeStackFrameSize(const T* low, - const T* high) { +static inline size_t ComputeStackFrameSize(const T* low, + const T* high) { const char* low_char_ptr = reinterpret_cast<const char *>(low); const char* high_char_ptr = reinterpret_cast<const char *>(high); - return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize; + return low < high ? static_cast<size_t>(high_char_ptr - low_char_ptr) + : kUnknownFrameSize; } // Given a pointer to a stack frame, locate and return the calling @@ -110,15 +111,15 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc) { } #endif - // aarch64 ABI requires stack pointer to be 16-byte-aligned. - if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0) + // The frame pointer should be 8-byte aligned. + if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0) return nullptr; // Check frame size. In strict mode, we assume frames to be under // 100,000 bytes. In non-strict mode, we relax the limit to 1MB. if (check_frame_size) { - const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; - const uintptr_t frame_size = + const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const size_t frame_size = ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); if (frame_size == kUnknownFrameSize || frame_size > max_size) return nullptr; @@ -165,7 +166,8 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } else { result[n] = prev_return_address; if (IS_STACK_FRAMES) { - sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); + sizes[n] = static_cast<int>( + ComputeStackFrameSize(frame_pointer, next_frame_pointer)); } n++; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_generic-inl.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_generic-inl.inc index fd0408fc24..cf41e48a10 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_generic-inl.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_generic-inl.inc @@ -80,7 +80,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, if (IS_STACK_FRAMES) { // No implementation for finding out the stack frame sizes yet. - memset(sizes, 0, sizeof(*sizes) * result_count); + memset(sizes, 0, sizeof(*sizes) * static_cast<size_t>(result_count)); } if (min_dropped_frames != nullptr) { if (size - skip_count - max_depth > 0) { diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_riscv-inl.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_riscv-inl.inc index 39a1fb2408..5cae7c3a9b 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_riscv-inl.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_riscv-inl.inc @@ -30,56 +30,14 @@ #include <cassert> #include <cstdint> #include <iostream> +#include <limits> +#include <utility> #include "y_absl/base/attributes.h" -#include "y_absl/debugging/internal/address_is_readable.h" -#include "y_absl/debugging/internal/vdso_support.h" #include "y_absl/debugging/stacktrace.h" static const uintptr_t kUnknownFrameSize = 0; -#if defined(__linux__) -// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. -static const unsigned char *GetKernelRtSigreturnAddress() { - constexpr uintptr_t kImpossibleAddress = 0; - Y_ABSL_CONST_INIT static std::atomic<uintptr_t> memoized(kImpossibleAddress); - uintptr_t address = memoized.load(std::memory_order_relaxed); - if (address != kImpossibleAddress) { - return reinterpret_cast<const unsigned char *>(address); - } - - address = reinterpret_cast<uintptr_t>(nullptr); - -#if Y_ABSL_HAVE_VDSO_SUPPORT - y_absl::debugging_internal::VDSOSupport vdso; - if (vdso.IsPresent()) { - y_absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; - // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10. - auto lookup = [&](int type) { - return vdso.LookupSymbol("__vdso_rt_sigreturn", "LINUX_4.15", type, - &symbol_info); - }; - if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || - symbol_info.address == nullptr) { - // Unexpected: VDSO is present, yet the expected symbol is missing or - // null. - assert(false && "VDSO is present, but doesn't have expected symbol"); - } else { - if (reinterpret_cast<uintptr_t>(symbol_info.address) != - kImpossibleAddress) { - address = reinterpret_cast<uintptr_t>(symbol_info.address); - } else { - assert(false && "VDSO returned invalid address"); - } - } - } -#endif - - memoized.store(address, std::memory_order_relaxed); - return reinterpret_cast<const unsigned char *>(address); -} -#endif // __linux__ - // Compute the size of a stack frame in [low..high). We assume that low < high. // Return size of kUnknownFrameSize. template <typename T> @@ -96,7 +54,8 @@ static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) { template <bool STRICT_UNWINDING, bool WITH_CONTEXT> Y_ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. Y_ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. -static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { +static void ** NextStackFrame(void **old_frame_pointer, const void *uc, + const std::pair<size_t, size_t> range) { // . // . // . @@ -114,55 +73,43 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { // $sp ->| ... | // +----------------+ void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]); - bool check_frame_size = true; - -#if defined(__linux__) - if (WITH_CONTEXT && uc != nullptr) { - // Check to see if next frame's return address is __kernel_rt_sigreturn. - if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) { - const ucontext_t *ucv = static_cast<const ucontext_t *>(uc); - // old_frame_pointer is not suitable for unwinding, look at ucontext to - // discover frame pointer before signal. - // - // RISCV ELF psABI has the frame pointer at x8/fp/s0. - // -- RISCV psABI Table 18.2 - void **const pre_signal_frame_pointer = - reinterpret_cast<void **>(ucv->uc_mcontext.__gregs[8]); - - // Check the alleged frame pointer is actually readable. This is to - // prevent "double fault" in case we hit the first fault due to stack - // corruption. - if (!y_absl::debugging_internal::AddressIsReadable( - pre_signal_frame_pointer)) - return nullptr; - - // Alleged frame pointer is readable, use it for further unwinding. - new_frame_pointer = pre_signal_frame_pointer; - - // Skip frame size check if we return from a signal. We may be using an - // alterate stack for signals. - check_frame_size = false; - } - } -#endif + uintptr_t frame_pointer = reinterpret_cast<uintptr_t>(new_frame_pointer); // The RISCV ELF psABI mandates that the stack pointer is always 16-byte // aligned. - // FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte + // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte // alignment. - if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0) + if (frame_pointer & 15) return nullptr; + // If the new frame pointer matches the signal context, avoid terminating + // early to deal with alternate signal stacks. + if (WITH_CONTEXT) + if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc)) + // RISCV ELF psABI has the frame pointer at x8/fp/s0. + // -- RISCV psABI Table 18.2 + if (ucv->uc_mcontext.__gregs[8] == frame_pointer) + return new_frame_pointer; + // Check frame size. In strict mode, we assume frames to be under 100,000 // bytes. In non-strict mode, we relax the limit to 1MB. - if (check_frame_size) { - const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; - const uintptr_t frame_size = - ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); - if (frame_size == kUnknownFrameSize || frame_size > max_size) + const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const uintptr_t frame_size = + ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); + if (frame_size == kUnknownFrameSize) { + if (STRICT_UNWINDING) + return nullptr; + + // In non-strict mode permit non-contiguous stacks (e.g. alternate signal + // frame handling). + if (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first || + reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second) return nullptr; } + if (frame_size > max_size) + return nullptr; + return new_frame_pointer; } @@ -180,6 +127,12 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, #error reading stack pointer not yet supported on this platform #endif + std::pair<size_t, size_t> stack = { + // assume that the first page is not the stack. + static_cast<size_t>(sysconf(_SC_PAGESIZE)), + std::numeric_limits<size_t>::max() - sizeof(void *) + }; + int n = 0; void *return_address = nullptr; while (frame_pointer && n < max_depth) { @@ -190,7 +143,8 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, // non-strict unwinding rules to produce a stack trace that is as complete // as possible (even if it contains a few bogus entries in some rare cases). void **next_frame_pointer = - NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp); + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, + stack); if (skip_count > 0) { skip_count--; @@ -217,7 +171,8 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, num_dropped_frames++; } frame_pointer = - NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp); + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, + stack); } *min_dropped_frames = num_dropped_frames; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_win32-inl.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_win32-inl.inc index 654990f206..46f3441b62 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_win32-inl.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_win32-inl.inc @@ -63,11 +63,12 @@ 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*, int* min_dropped_frames) { - int n = 0; - if (!RtlCaptureStackBackTrace_fn) { - // can't find a stacktrace with no function to call + USHORT n = 0; + if (!RtlCaptureStackBackTrace_fn || skip_count < 0 || max_depth < 0) { + // can't get a stacktrace with no function/invalid args } else { - n = (int)RtlCaptureStackBackTrace_fn(skip_count + 2, max_depth, result, 0); + n = RtlCaptureStackBackTrace_fn(static_cast<ULONG>(skip_count) + 2, + static_cast<ULONG>(max_depth), result, 0); } if (IS_STACK_FRAMES) { // No implementation for finding out the stack frame sizes yet. diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_x86-inl.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_x86-inl.inc index bd5dc13d99..278a0b6da4 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_x86-inl.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/stacktrace_x86-inl.inc @@ -29,14 +29,13 @@ #include <cstdint> #include <limits> +#include "y_absl/base/attributes.h" #include "y_absl/base/macros.h" #include "y_absl/base/port.h" #include "y_absl/debugging/internal/address_is_readable.h" #include "y_absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems #include "y_absl/debugging/stacktrace.h" -#include "y_absl/base/internal/raw_logging.h" - using y_absl::debugging_internal::AddressIsReadable; #if defined(__linux__) && defined(__i386__) @@ -113,6 +112,10 @@ static int CountPushInstructions(const unsigned char *const addr) { // Assume stack frames larger than 100,000 bytes are bogus. static const int kMaxFrameBytes = 100000; +// Stack end to use when we don't know the actual stack end +// (effectively just the end of address space). +constexpr uintptr_t kUnknownStackEnd = + std::numeric_limits<size_t>::max() - sizeof(void *); // Returns the stack frame pointer from signal context, 0 if unknown. // vuc is a ucontext_t *. We use void* to avoid the use @@ -140,13 +143,14 @@ static uintptr_t GetFP(const void *vuc) { // TODO(bcmills): -momit-leaf-frame-pointer is currently the default // behavior when building with clang. Talk to the C++ toolchain team about // fixing that. - if (bp >= sp && bp - sp <= kMaxFrameBytes) return bp; + if (bp >= sp && bp - sp <= kMaxFrameBytes) + return static_cast<uintptr_t>(bp); // If bp isn't a plausible frame pointer, return the stack pointer instead. // If we're lucky, it points to the start of a stack frame; otherwise, we'll // get one frame of garbage in the stack trace and fail the sanity check on // the next iteration. - return sp; + return static_cast<uintptr_t>(sp); } #endif return 0; @@ -258,8 +262,26 @@ static void **NextStackFrame(void **old_fp, const void *uc, // With the stack growing downwards, older stack frame must be // at a greater address that the current one. if (new_fp_u <= old_fp_u) return nullptr; - if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr; + // If we get a very large frame size, it may be an indication that we + // guessed frame pointers incorrectly and now risk a paging fault + // dereferencing a wrong frame pointer. Or maybe not because large frames + // are possible as well. The main stack is assumed to be readable, + // so we assume the large frame is legit if we know the real stack bounds + // and are within the stack. + if (new_fp_u - old_fp_u > kMaxFrameBytes) { + if (stack_high < kUnknownStackEnd && + static_cast<size_t>(getpagesize()) < stack_low) { + // Stack bounds are known. + if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) { + // new_fp_u is not within the known stack. + return nullptr; + } + } else { + // Stack bounds are unknown, prefer truncated stack to possible crash. + return nullptr; + } + } if (stack_low < old_fp_u && old_fp_u <= stack_high) { // Old BP was in the expected stack region... if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) { @@ -310,8 +332,9 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, int n = 0; void **fp = reinterpret_cast<void **>(__builtin_frame_address(0)); - size_t stack_low = getpagesize(); // Assume that the first page is not stack. - size_t stack_high = std::numeric_limits<size_t>::max() - sizeof(void *); + // Assume that the first page is not stack. + size_t stack_low = static_cast<size_t>(getpagesize()); + size_t stack_high = kUnknownStackEnd; while (fp && n < max_depth) { if (*(fp + 1) == reinterpret_cast<void *>(0)) { @@ -327,7 +350,9 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, result[n] = *(fp + 1); if (IS_STACK_FRAMES) { if (next_fp > fp) { - sizes[n] = (uintptr_t)next_fp - (uintptr_t)fp; + sizes[n] = static_cast<int>( + reinterpret_cast<uintptr_t>(next_fp) - + reinterpret_cast<uintptr_t>(fp)); } else { // A frame-size of 0 is used to indicate unknown frame size. sizes[n] = 0; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/vdso_support.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/vdso_support.cc index 282b406bb7..3fd0ea2349 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/vdso_support.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/internal/vdso_support.cc @@ -193,8 +193,9 @@ long VDSOSupport::InitAndGetCPU(unsigned *cpu, // NOLINT(runtime/int) Y_ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY int GetCPU() { unsigned cpu; - int ret_code = (*VDSOSupport::getcpu_fn_)(&cpu, nullptr, nullptr); - return ret_code == 0 ? cpu : ret_code; + long ret_code = // NOLINT(runtime/int) + (*VDSOSupport::getcpu_fn_)(&cpu, nullptr, nullptr); + return ret_code == 0 ? static_cast<int>(cpu) : static_cast<int>(ret_code); } } // namespace debugging_internal diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/leak_check.h b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/leak_check.h index 17256e0c03..ba5cc70ce5 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/leak_check.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/leak_check.h @@ -37,7 +37,7 @@ // not also use AddressSanitizer). To use the mode, simply pass // `-fsanitize=leak` to both the compiler and linker. Since GCC does not // currently provide a way of detecting this mode at compile-time, GCC users -// must also pass -DLEAK_SANIITIZER to the compiler. An example Bazel command +// must also pass -DLEAK_SANITIZER to the compiler. An example Bazel command // could be // // $ bazel test --copt=-DLEAK_SANITIZER --copt=-fsanitize=leak diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_darwin.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_darwin.inc index f36444f9b7..e986402a9f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_darwin.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_darwin.inc @@ -83,13 +83,14 @@ bool Symbolize(const void* pc, char* out, int out_size) { memmove(out, tmp_buf, len + 1); } } else { - strncpy(out, symbol.c_str(), out_size); + strncpy(out, symbol.c_str(), static_cast<size_t>(out_size)); } if (out[out_size - 1] != '\0') { // strncpy() does not '\0' terminate when it truncates. static constexpr char kEllipsis[] = "..."; - int ellipsis_size = std::min<int>(sizeof(kEllipsis) - 1, out_size - 1); + size_t ellipsis_size = + std::min(sizeof(kEllipsis) - 1, static_cast<size_t>(out_size) - 1); memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); out[out_size - 1] = '\0'; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_elf.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_elf.inc index 0640bfc76f..33ad4a9b0c 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_elf.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_elf.inc @@ -205,7 +205,8 @@ struct ObjFile { // PT_LOAD program header describing executable code. // Normally we expect just one, but SWIFT binaries have two. - std::array<ElfW(Phdr), 2> phdr; + // CUDA binaries have 3 (see cr/473913254 description). + std::array<ElfW(Phdr), 4> phdr; }; // Build 4-way associative cache for symbols. Within each cache line, symbols @@ -252,21 +253,21 @@ class AddrMap { public: AddrMap() : size_(0), allocated_(0), obj_(nullptr) {} ~AddrMap() { base_internal::LowLevelAlloc::Free(obj_); } - int Size() const { return size_; } - ObjFile *At(int i) { return &obj_[i]; } + size_t Size() const { return size_; } + ObjFile *At(size_t i) { return &obj_[i]; } ObjFile *Add(); void Clear(); private: - int size_; // count of valid elements (<= allocated_) - int allocated_; // count of allocated elements - ObjFile *obj_; // array of allocated_ elements + size_t size_; // count of valid elements (<= allocated_) + size_t allocated_; // count of allocated elements + ObjFile *obj_; // array of allocated_ elements AddrMap(const AddrMap &) = delete; AddrMap &operator=(const AddrMap &) = delete; }; void AddrMap::Clear() { - for (int i = 0; i != size_; i++) { + for (size_t i = 0; i != size_; i++) { At(i)->~ObjFile(); } size_ = 0; @@ -274,7 +275,7 @@ void AddrMap::Clear() { ObjFile *AddrMap::Add() { if (size_ == allocated_) { - int new_allocated = allocated_ * 2 + 50; + size_t new_allocated = allocated_ * 2 + 50; ObjFile *new_obj_ = static_cast<ObjFile *>(base_internal::LowLevelAlloc::AllocWithArena( new_allocated * sizeof(*new_obj_), SigSafeArena())); @@ -300,7 +301,7 @@ class Symbolizer { private: char *CopyString(const char *s) { - int len = strlen(s); + size_t len = strlen(s); char *dst = static_cast<char *>( base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena())); Y_ABSL_RAW_CHECK(dst != nullptr, "out of memory"); @@ -321,8 +322,8 @@ class Symbolizer { FindSymbolResult GetSymbolFromObjectFile(const ObjFile &obj, const void *const pc, const ptrdiff_t relocation, - char *out, int out_size, - char *tmp_buf, int tmp_buf_size); + char *out, size_t out_size, + char *tmp_buf, size_t tmp_buf_size); const char *GetUncachedSymbol(const void *pc); enum { @@ -353,11 +354,11 @@ static std::atomic<Symbolizer *> g_cached_symbolizer; } // namespace -static int SymbolizerSize() { +static size_t SymbolizerSize() { #if defined(__wasm__) || defined(__asmjs__) - int pagesize = getpagesize(); + auto pagesize = static_cast<size_t>(getpagesize()); #else - int pagesize = sysconf(_SC_PAGESIZE); + auto pagesize = static_cast<size_t>(sysconf(_SC_PAGESIZE)); #endif return ((sizeof(Symbolizer) - 1) / pagesize + 1) * pagesize; } @@ -429,7 +430,7 @@ static ssize_t ReadPersistent(int fd, void *buf, size_t count) { if (len == 0) { // Reached EOF. break; } - num_bytes += len; + num_bytes += static_cast<size_t>(len); } SAFE_ASSERT(num_bytes <= count); return static_cast<ssize_t>(num_bytes); @@ -442,8 +443,8 @@ static ssize_t ReadFromOffset(const int fd, void *buf, const size_t count, const off_t offset) { off_t off = lseek(fd, offset, SEEK_SET); if (off == (off_t)-1) { - Y_ABSL_RAW_LOG(WARNING, "lseek(%d, %ju, SEEK_SET) failed: errno=%d", fd, - static_cast<uintmax_t>(offset), errno); + Y_ABSL_RAW_LOG(WARNING, "lseek(%d, %jd, SEEK_SET) failed: errno=%d", fd, + static_cast<intmax_t>(offset), errno); return -1; } return ReadPersistent(fd, buf, count); @@ -478,29 +479,37 @@ static int FileGetElfType(const int fd) { // inlined. static Y_ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType( const int fd, ElfW(Half) sh_num, const off_t sh_offset, ElfW(Word) type, - ElfW(Shdr) * out, char *tmp_buf, int tmp_buf_size) { + ElfW(Shdr) * out, char *tmp_buf, size_t tmp_buf_size) { ElfW(Shdr) *buf = reinterpret_cast<ElfW(Shdr) *>(tmp_buf); - const int buf_entries = tmp_buf_size / sizeof(buf[0]); - const int buf_bytes = buf_entries * sizeof(buf[0]); + const size_t buf_entries = tmp_buf_size / sizeof(buf[0]); + const size_t buf_bytes = buf_entries * sizeof(buf[0]); - for (int i = 0; i < sh_num;) { - const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]); - const ssize_t num_bytes_to_read = + for (size_t i = 0; static_cast<int>(i) < sh_num;) { + const size_t num_bytes_left = + (static_cast<size_t>(sh_num) - i) * sizeof(buf[0]); + const size_t num_bytes_to_read = (buf_bytes > num_bytes_left) ? num_bytes_left : buf_bytes; - const off_t offset = sh_offset + i * sizeof(buf[0]); + const off_t offset = sh_offset + static_cast<off_t>(i * sizeof(buf[0])); const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, offset); - if (len % sizeof(buf[0]) != 0) { + if (len < 0) { Y_ABSL_RAW_LOG( WARNING, - "Reading %zd bytes from offset %ju returned %zd which is not a " + "Reading %zu bytes from offset %ju returned %zd which is negative.", + num_bytes_to_read, static_cast<intmax_t>(offset), len); + return false; + } + if (static_cast<size_t>(len) % sizeof(buf[0]) != 0) { + Y_ABSL_RAW_LOG( + WARNING, + "Reading %zu bytes from offset %jd returned %zd which is not a " "multiple of %zu.", - num_bytes_to_read, static_cast<uintmax_t>(offset), len, + num_bytes_to_read, static_cast<intmax_t>(offset), len, sizeof(buf[0])); return false; } - const ssize_t num_headers_in_buf = len / sizeof(buf[0]); + const size_t num_headers_in_buf = static_cast<size_t>(len) / sizeof(buf[0]); SAFE_ASSERT(num_headers_in_buf <= buf_entries); - for (int j = 0; j < num_headers_in_buf; ++j) { + for (size_t j = 0; j < num_headers_in_buf; ++j) { if (buf[j].sh_type == type) { *out = buf[j]; return true; @@ -524,8 +533,8 @@ bool ForEachSection(int fd, } ElfW(Shdr) shstrtab; - off_t shstrtab_offset = - (elf_header.e_shoff + elf_header.e_shentsize * elf_header.e_shstrndx); + off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) + + elf_header.e_shentsize * elf_header.e_shstrndx; if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { return false; } @@ -533,22 +542,23 @@ bool ForEachSection(int fd, for (int i = 0; i < elf_header.e_shnum; ++i) { ElfW(Shdr) out; off_t section_header_offset = - (elf_header.e_shoff + elf_header.e_shentsize * i); + static_cast<off_t>(elf_header.e_shoff) + elf_header.e_shentsize * i; if (!ReadFromOffsetExact(fd, &out, sizeof(out), section_header_offset)) { return false; } - off_t name_offset = shstrtab.sh_offset + out.sh_name; + off_t name_offset = static_cast<off_t>(shstrtab.sh_offset) + out.sh_name; char header_name[kMaxSectionNameLen]; ssize_t n_read = ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset); - if (n_read == -1) { + if (n_read < 0) { return false; } else if (n_read > kMaxSectionNameLen) { // Long read? return false; } - y_absl::string_view name(header_name, strnlen(header_name, n_read)); + y_absl::string_view name(header_name, + strnlen(header_name, static_cast<size_t>(n_read))); if (!callback(name, out)) { break; } @@ -575,19 +585,19 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, } ElfW(Shdr) shstrtab; - off_t shstrtab_offset = - (elf_header.e_shoff + elf_header.e_shentsize * elf_header.e_shstrndx); + off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) + + elf_header.e_shentsize * elf_header.e_shstrndx; if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { return false; } for (int i = 0; i < elf_header.e_shnum; ++i) { off_t section_header_offset = - (elf_header.e_shoff + elf_header.e_shentsize * i); + static_cast<off_t>(elf_header.e_shoff) + elf_header.e_shentsize * i; if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { return false; } - off_t name_offset = shstrtab.sh_offset + out->sh_name; + off_t name_offset = static_cast<off_t>(shstrtab.sh_offset) + out->sh_name; ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset); if (n_read < 0) { return false; @@ -645,10 +655,10 @@ static bool InSection(const void *address, const ElfW(Shdr) * section) { } static const char *ComputeOffset(const char *base, ptrdiff_t offset) { - // Note: cast to uintptr_t to avoid undefined behavior when base evaluates to + // Note: cast to intptr_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); + return reinterpret_cast<const char *>(reinterpret_cast<intptr_t>(base) + + offset); } // Read a symbol table and look for the symbol containing the @@ -661,18 +671,18 @@ static const char *ComputeOffset(const char *base, ptrdiff_t offset) { // To keep stack consumption low, we would like this function to not get // inlined. static Y_ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( - const void *const pc, const int fd, char *out, int out_size, + const void *const pc, const int fd, char *out, size_t out_size, ptrdiff_t relocation, const ElfW(Shdr) * strtab, const ElfW(Shdr) * symtab, - const ElfW(Shdr) * opd, char *tmp_buf, int tmp_buf_size) { + const ElfW(Shdr) * opd, char *tmp_buf, size_t tmp_buf_size) { if (symtab == nullptr) { return SYMBOL_NOT_FOUND; } // Read multiple symbols at once to save read() calls. ElfW(Sym) *buf = reinterpret_cast<ElfW(Sym) *>(tmp_buf); - const int buf_entries = tmp_buf_size / sizeof(buf[0]); + const size_t buf_entries = tmp_buf_size / sizeof(buf[0]); - const int num_symbols = symtab->sh_size / symtab->sh_entsize; + const size_t num_symbols = symtab->sh_size / symtab->sh_entsize; // On platforms using an .opd section (PowerPC & IA64), a function symbol // has the address of a function descriptor, which contains the real @@ -687,16 +697,19 @@ static Y_ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( ElfW(Sym) best_match; SafeMemZero(&best_match, sizeof(best_match)); bool found_match = false; - for (int i = 0; i < num_symbols;) { - off_t offset = symtab->sh_offset + i * symtab->sh_entsize; - const int num_remaining_symbols = num_symbols - i; - const int entries_in_chunk = std::min(num_remaining_symbols, buf_entries); - const int bytes_in_chunk = entries_in_chunk * sizeof(buf[0]); + for (size_t i = 0; i < num_symbols;) { + off_t offset = + static_cast<off_t>(symtab->sh_offset + i * symtab->sh_entsize); + const size_t num_remaining_symbols = num_symbols - i; + const size_t entries_in_chunk = + std::min(num_remaining_symbols, buf_entries); + const size_t bytes_in_chunk = entries_in_chunk * sizeof(buf[0]); const ssize_t len = ReadFromOffset(fd, buf, bytes_in_chunk, offset); - SAFE_ASSERT(len % sizeof(buf[0]) == 0); - const ssize_t num_symbols_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(len >= 0); + SAFE_ASSERT(static_cast<size_t>(len) % sizeof(buf[0]) == 0); + const size_t num_symbols_in_buf = static_cast<size_t>(len) / sizeof(buf[0]); SAFE_ASSERT(num_symbols_in_buf <= entries_in_chunk); - for (int j = 0; j < num_symbols_in_buf; ++j) { + for (size_t j = 0; j < num_symbols_in_buf; ++j) { const ElfW(Sym) &symbol = buf[j]; // For a DSO, a symbol address is relocated by the loading address. @@ -713,7 +726,7 @@ static Y_ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( // about what encoding is being used; we just want the real start address // of the function. start_address = reinterpret_cast<const char *>( - reinterpret_cast<uintptr_t>(start_address) & ~1); + reinterpret_cast<uintptr_t>(start_address) & ~1u); #endif if (deref_function_descriptor_pointer && @@ -726,7 +739,8 @@ static Y_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 = ComputeOffset(start_address, size); + const void *const end_address = + ComputeOffset(start_address, static_cast<ptrdiff_t>(size)); if (symbol.st_value != 0 && // Skip null value symbols. symbol.st_shndx != 0 && // Skip undefined symbols. #ifdef STT_TLS @@ -744,16 +758,18 @@ static Y_ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( } if (found_match) { - const size_t off = strtab->sh_offset + best_match.st_name; + const off_t off = + static_cast<off_t>(strtab->sh_offset) + best_match.st_name; const ssize_t n_read = ReadFromOffset(fd, out, out_size, off); if (n_read <= 0) { // This should never happen. Y_ABSL_RAW_LOG(WARNING, - "Unable to read from fd %d at offset %zu: n_read = %zd", fd, - off, n_read); + "Unable to read from fd %d at offset %lld: n_read = %zd", fd, + static_cast<long long>(off), n_read); return SYMBOL_NOT_FOUND; } - Y_ABSL_RAW_CHECK(n_read <= out_size, "ReadFromOffset read too much data."); + Y_ABSL_RAW_CHECK(static_cast<size_t>(n_read) <= out_size, + "ReadFromOffset read too much data."); // strtab->sh_offset points into .strtab-like section that contains // NUL-terminated strings: '\0foo\0barbaz\0...". @@ -761,7 +777,7 @@ static Y_ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( // sh_offset+st_name points to the start of symbol name, but we don't know // how long the symbol is, so we try to read as much as we have space for, // and usually over-read (i.e. there is a NUL somewhere before n_read). - if (memchr(out, '\0', n_read) == nullptr) { + if (memchr(out, '\0', static_cast<size_t>(n_read)) == nullptr) { // Either out_size was too small (n_read == out_size and no NUL), or // we tried to read past the EOF (n_read < out_size) and .strtab is // corrupt (missing terminating NUL; should never happen for valid ELF). @@ -779,7 +795,7 @@ static Y_ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( // See FindSymbol() comment for description of return value. FindSymbolResult Symbolizer::GetSymbolFromObjectFile( const ObjFile &obj, const void *const pc, const ptrdiff_t relocation, - char *out, int out_size, char *tmp_buf, int tmp_buf_size) { + char *out, size_t out_size, char *tmp_buf, size_t tmp_buf_size) { ElfW(Shdr) symtab; ElfW(Shdr) strtab; ElfW(Shdr) opd; @@ -802,13 +818,15 @@ FindSymbolResult Symbolizer::GetSymbolFromObjectFile( // Consult a regular symbol table, then fall back to the dynamic symbol table. for (const auto symbol_table_type : {SHT_SYMTAB, SHT_DYNSYM}) { if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, - obj.elf_header.e_shoff, symbol_table_type, + static_cast<off_t>(obj.elf_header.e_shoff), + static_cast<ElfW(Word)>(symbol_table_type), &symtab, tmp_buf, tmp_buf_size)) { continue; } if (!ReadFromOffsetExact( obj.fd, &strtab, sizeof(strtab), - obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { + static_cast<off_t>(obj.elf_header.e_shoff + + symtab.sh_link * sizeof(symtab)))) { continue; } const FindSymbolResult rc = @@ -833,7 +851,7 @@ class FileDescriptor { ~FileDescriptor() { if (fd_ >= 0) { - NO_INTR(close(fd_)); + close(fd_); } } @@ -850,7 +868,7 @@ class FileDescriptor { // and snprintf(). class LineReader { public: - explicit LineReader(int fd, char *buf, int buf_len) + explicit LineReader(int fd, char *buf, size_t buf_len) : fd_(fd), buf_len_(buf_len), buf_(buf), @@ -878,12 +896,12 @@ class LineReader { bol_ = eol_ + 1; // Advance to the next line in the buffer. SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_". if (!HasCompleteLine()) { - const int incomplete_line_length = eod_ - bol_; + const auto incomplete_line_length = static_cast<size_t>(eod_ - bol_); // Move the trailing incomplete line to the beginning. memmove(buf_, bol_, incomplete_line_length); // Read text from file and append it. char *const append_pos = buf_ + incomplete_line_length; - const int capacity_left = buf_len_ - incomplete_line_length; + const size_t capacity_left = buf_len_ - incomplete_line_length; const ssize_t num_bytes = ReadPersistent(fd_, append_pos, capacity_left); if (num_bytes <= 0) { // EOF or error. @@ -906,7 +924,8 @@ class LineReader { private: char *FindLineFeed() const { - return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_)); + return reinterpret_cast<char *>( + memchr(bol_, '\n', static_cast<size_t>(eod_ - bol_))); } bool BufferIsEmpty() const { return buf_ == eod_; } @@ -916,7 +935,7 @@ class LineReader { } const int fd_; - const int buf_len_; + const size_t buf_len_; char *const buf_; char *bol_; char *eol_; @@ -934,7 +953,8 @@ static const char *GetHex(const char *start, const char *end, int ch = *p; if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) { - hex = (hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9); + hex = (hex << 4) | + static_cast<uint64_t>(ch < 'A' ? ch - '0' : (ch & 0xF) + 9); } else { // Encountered the first non-hex character. break; } @@ -966,7 +986,7 @@ static bool ShouldUseMapping(const char *const flags) { static Y_ABSL_ATTRIBUTE_NOINLINE bool ReadAddrMap( bool (*callback)(const char *filename, const void *const start_addr, const void *const end_addr, uint64_t offset, void *arg), - void *arg, void *tmp_buf, int tmp_buf_size) { + void *arg, void *tmp_buf, size_t tmp_buf_size) { // Use /proc/self/task/<pid>/maps instead of /proc/self/maps. The latter // requires kernel to stop all threads, and is significantly slower when there // are 1000s of threads. @@ -1081,10 +1101,10 @@ ObjFile *Symbolizer::FindObjFile(const void *const addr, size_t len) { } } - int lo = 0; - int hi = addr_map_.Size(); + size_t lo = 0; + size_t hi = addr_map_.Size(); while (lo < hi) { - int mid = (lo + hi) / 2; + size_t mid = (lo + hi) / 2; if (addr < addr_map_.At(mid)->end_addr) { hi = mid; } else { @@ -1106,11 +1126,11 @@ ObjFile *Symbolizer::FindObjFile(const void *const addr, size_t len) { } void Symbolizer::ClearAddrMap() { - for (int i = 0; i != addr_map_.Size(); i++) { + for (size_t i = 0; i != addr_map_.Size(); i++) { ObjFile *o = addr_map_.At(i); base_internal::LowLevelAlloc::Free(o->filename); if (o->fd >= 0) { - NO_INTR(close(o->fd)); + close(o->fd); } } addr_map_.Clear(); @@ -1126,7 +1146,7 @@ bool Symbolizer::RegisterObjFile(const char *filename, // Files are supposed to be added in the increasing address order. Make // sure that's the case. - int addr_map_size = impl->addr_map_.Size(); + size_t addr_map_size = impl->addr_map_.Size(); if (addr_map_size != 0) { ObjFile *old = impl->addr_map_.At(addr_map_size - 1); if (old->end_addr > end_addr) { @@ -1170,12 +1190,12 @@ bool Symbolizer::RegisterObjFile(const char *filename, // where the input symbol is demangled in-place. // To keep stack consumption low, we would like this function to not // get inlined. -static Y_ABSL_ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size, +static Y_ABSL_ATTRIBUTE_NOINLINE void DemangleInplace(char *out, size_t out_size, char *tmp_buf, - int tmp_buf_size) { + size_t tmp_buf_size) { if (Demangle(out, tmp_buf, tmp_buf_size)) { // Demangling succeeded. Copy to out if the space allows. - int len = strlen(tmp_buf); + size_t len = strlen(tmp_buf); if (len + 1 <= out_size) { // +1 for '\0'. SAFE_ASSERT(len < tmp_buf_size); memmove(out, tmp_buf, len + 1); @@ -1218,7 +1238,8 @@ const char *Symbolizer::InsertSymbolInCache(const void *const pc, SymbolCacheLine *line = GetCacheLine(pc); uint32_t max_age = 0; - int oldest_index = -1; + size_t oldest_index = 0; + bool found_oldest_index = false; for (size_t i = 0; i < Y_ABSL_ARRAYSIZE(line->pc); ++i) { if (line->pc[i] == nullptr) { AgeSymbols(line); @@ -1230,11 +1251,12 @@ const char *Symbolizer::InsertSymbolInCache(const void *const pc, if (line->age[i] >= max_age) { max_age = line->age[i]; oldest_index = i; + found_oldest_index = true; } } AgeSymbols(line); - Y_ABSL_RAW_CHECK(oldest_index >= 0, "Corrupt cache"); + Y_ABSL_RAW_CHECK(found_oldest_index, "Corrupt cache"); base_internal::LowLevelAlloc::Free(line->name[oldest_index]); line->pc[oldest_index] = pc; line->name[oldest_index] = CopyString(name); @@ -1303,7 +1325,7 @@ static bool MaybeInitializeObjFile(ObjFile *obj) { } const int phnum = obj->elf_header.e_phnum; const int phentsize = obj->elf_header.e_phentsize; - size_t phoff = obj->elf_header.e_phoff; + auto phoff = static_cast<off_t>(obj->elf_header.e_phoff); size_t num_executable_load_segments = 0; for (int j = 0; j < phnum; j++) { ElfW(Phdr) phdr; @@ -1321,8 +1343,9 @@ static bool MaybeInitializeObjFile(ObjFile *obj) { if (num_executable_load_segments < obj->phdr.size()) { memcpy(&obj->phdr[num_executable_load_segments++], &phdr, sizeof(phdr)); } else { - Y_ABSL_RAW_LOG(WARNING, "%s: too many executable LOAD segments", - obj->filename); + Y_ABSL_RAW_LOG( + WARNING, "%s: too many executable LOAD segments: %zu >= %zu", + obj->filename, num_executable_load_segments, obj->phdr.size()); break; } } @@ -1354,7 +1377,7 @@ const char *Symbolizer::GetUncachedSymbol(const void *pc) { // // For obj->offset > 0, adjust the relocation since a mapping at offset // X in the file will have a start address of [true relocation]+X. - relocation = start_addr - obj->offset; + relocation = static_cast<ptrdiff_t>(start_addr - obj->offset); // Note: some binaries have multiple "rx" LOAD segments. We must // find the right one. @@ -1529,7 +1552,7 @@ bool RegisterFileMappingHint(const void *start, const void *end, uint64_t offset ret = false; } else { // TODO(ckennelly): Move this into a string copy routine. - int len = strlen(filename); + size_t len = strlen(filename); char *dst = static_cast<char *>( base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena())); Y_ABSL_RAW_CHECK(dst != nullptr, "out of memory"); @@ -1585,16 +1608,17 @@ bool Symbolize(const void *pc, char *out, int out_size) { const char *name = s->GetSymbol(pc); bool ok = false; if (name != nullptr && out_size > 0) { - strncpy(out, name, out_size); + strncpy(out, name, static_cast<size_t>(out_size)); ok = true; - if (out[out_size - 1] != '\0') { + if (out[static_cast<size_t>(out_size) - 1] != '\0') { // strncpy() does not '\0' terminate when it truncates. Do so, with // trailing ellipsis. static constexpr char kEllipsis[] = "..."; - int ellipsis_size = - std::min(implicit_cast<int>(strlen(kEllipsis)), out_size - 1); - memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); - out[out_size - 1] = '\0'; + size_t ellipsis_size = + std::min(strlen(kEllipsis), static_cast<size_t>(out_size) - 1); + memcpy(out + static_cast<size_t>(out_size) - ellipsis_size - 1, kEllipsis, + ellipsis_size); + out[static_cast<size_t>(out_size) - 1] = '\0'; } } debugging_internal::FreeSymbolizer(s); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_win32.inc b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_win32.inc index ac6748092c..cc1bcc7fd2 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_win32.inc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize_win32.inc @@ -65,14 +65,15 @@ bool Symbolize(const void* pc, char* out, int out_size) { if (!SymFromAddr(process, reinterpret_cast<DWORD64>(pc), nullptr, symbol)) { return false; } - strncpy(out, symbol->Name, out_size); - if (out[out_size - 1] != '\0') { + const size_t out_size_t = static_cast<size_t>(out_size); + strncpy(out, symbol->Name, out_size_t); + if (out[out_size_t - 1] != '\0') { // strncpy() does not '\0' terminate when it truncates. static constexpr char kEllipsis[] = "..."; - int ellipsis_size = - std::min<int>(sizeof(kEllipsis) - 1, out_size - 1); - memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); - out[out_size - 1] = '\0'; + size_t ellipsis_size = + std::min(sizeof(kEllipsis) - 1, out_size_t - 1); + memcpy(out + out_size_t - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size_t - 1] = '\0'; } return true; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/functional/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/functional/ya.make index d53dc4c83a..41ce8373c6 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/functional/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/functional/ya.make @@ -6,9 +6,9 @@ LICENSE(Apache-2.0) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -VERSION(20220623.1) +VERSION(20230125.1) -ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20220623.1.tar.gz) +ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20230125.1.tar.gz) NO_RUNTIME() diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/city.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/city.cc index 9b6021ab85..13b20454b0 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/city.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/city.cc @@ -97,7 +97,7 @@ static uint32_t Hash32Len13to24(const char *s, size_t len) { uint32_t d = Fetch32(s + (len >> 1)); uint32_t e = Fetch32(s); uint32_t f = Fetch32(s + len - 4); - uint32_t h = len; + uint32_t h = static_cast<uint32_t>(len); return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); } @@ -106,15 +106,15 @@ static uint32_t Hash32Len0to4(const char *s, size_t len) { uint32_t b = 0; uint32_t c = 9; for (size_t i = 0; i < len; i++) { - signed char v = s[i]; - b = b * c1 + v; + signed char v = static_cast<signed char>(s[i]); + b = b * c1 + static_cast<uint32_t>(v); c ^= b; } - return fmix(Mur(b, Mur(len, c))); + return fmix(Mur(b, Mur(static_cast<uint32_t>(len), c))); } static uint32_t Hash32Len5to12(const char *s, size_t len) { - uint32_t a = len, b = len * 5, c = 9, d = b; + uint32_t a = static_cast<uint32_t>(len), b = a * 5, c = 9, d = b; a += Fetch32(s); b += Fetch32(s + len - 4); c += Fetch32(s + ((len >> 1) & 4)); @@ -129,7 +129,7 @@ uint32_t CityHash32(const char *s, size_t len) { } // len > 24 - uint32_t h = len, g = c1 * len, f = g; + uint32_t h = static_cast<uint32_t>(len), g = c1 * h, f = g; uint32_t a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; uint32_t a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; @@ -230,11 +230,11 @@ static uint64_t HashLen0to16(const char *s, size_t len) { return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); } if (len > 0) { - uint8_t a = s[0]; - uint8_t b = s[len >> 1]; - uint8_t c = s[len - 1]; + uint8_t a = static_cast<uint8_t>(s[0]); + uint8_t b = static_cast<uint8_t>(s[len >> 1]); + uint8_t c = static_cast<uint8_t>(s[len - 1]); uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8); - uint32_t z = len + (static_cast<uint32_t>(c) << 2); + uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2); return ShiftMix(y * k2 ^ z * k0) * k2; } return k2; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/hash.h b/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/hash.h index f295ea6018..5b559ffd0d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/hash.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/hash.h @@ -49,6 +49,7 @@ #include "y_absl/hash/internal/city.h" #include "y_absl/hash/internal/low_level_hash.h" #include "y_absl/meta/type_traits.h" +#include "y_absl/numeric/bits.h" #include "y_absl/numeric/int128.h" #include "y_absl/strings/string_view.h" #include "y_absl/types/optional.h" @@ -444,7 +445,7 @@ H AbslHashValue(H hash_state, T C::* ptr) { // On other platforms, we assume that pointers-to-members do not have // padding. #ifdef __cpp_lib_has_unique_object_representations - static_assert(std::has_unique_object_representations_v<T C::*>); + static_assert(std::has_unique_object_representations<T C::*>::value); #endif // __cpp_lib_has_unique_object_representations return n; #endif @@ -1052,7 +1053,7 @@ class Y_ABSL_DLL MixingHashState : public HashStateBase<MixingHashState> { uint64_t most_significant = low_mem; uint64_t least_significant = high_mem; #endif - return {least_significant, most_significant >> (128 - len * 8)}; + return {least_significant, most_significant}; } // Reads 4 to 8 bytes from p. Zero pads to fill uint64_t. @@ -1183,9 +1184,22 @@ inline uint64_t MixingHashState::CombineContiguousImpl( } v = Hash64(first, len); } else if (len > 8) { + // This hash function was constructed by the ML-driven algorithm discovery + // using reinforcement learning. We fed the agent lots of inputs from + // microbenchmarks, SMHasher, low hamming distance from generated inputs and + // picked up the one that was good on micro and macrobenchmarks. auto p = Read9To16(first, len); - state = Mix(state, p.first); - v = p.second; + uint64_t lo = p.first; + uint64_t hi = p.second; + // Rotation by 53 was found to be most often useful when discovering these + // hashing algorithms with ML techniques. + lo = y_absl::rotr(lo, 53); + state += kMul; + lo += state; + state ^= hi; + uint128 m = state; + m *= lo; + return static_cast<uint64_t>(m ^ (m >> 64)); } else if (len >= 4) { v = Read4To8(first, len); } else if (len > 0) { diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/low_level_hash.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/low_level_hash.cc index e4d982e2ad..272e007eb9 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/low_level_hash.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/hash/internal/low_level_hash.cc @@ -15,7 +15,6 @@ #include "y_absl/hash/internal/low_level_hash.h" #include "y_absl/base/internal/unaligned_access.h" -#include "y_absl/numeric/bits.h" #include "y_absl/numeric/int128.h" namespace y_absl { @@ -23,24 +22,13 @@ Y_ABSL_NAMESPACE_BEGIN namespace hash_internal { static uint64_t Mix(uint64_t v0, uint64_t v1) { -#if !defined(__aarch64__) - // The default bit-mixer uses 64x64->128-bit multiplication. y_absl::uint128 p = v0; p *= v1; return y_absl::Uint128Low64(p) ^ y_absl::Uint128High64(p); -#else - // The default bit-mixer above would perform poorly on some ARM microarchs, - // where calculating a 128-bit product requires a sequence of two - // instructions with a high combined latency and poor throughput. - // Instead, we mix bits using only 64-bit arithmetic, which is faster. - uint64_t p = v0 ^ y_absl::rotl(v1, 40); - p *= v1 ^ y_absl::rotl(v0, 39); - return p ^ (p >> 11); -#endif } uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, - const uint64_t salt[]) { + const uint64_t salt[5]) { const uint8_t* ptr = static_cast<const uint8_t*>(data); uint64_t starting_length = static_cast<uint64_t>(len); uint64_t current_state = seed ^ salt[0]; @@ -106,7 +94,8 @@ uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, } else if (len > 0) { // If we have at least 1 and at most 3 bytes, read all of the provided // bits into A, with some adjustments. - a = ((ptr[0] << 16) | (ptr[len >> 1] << 8) | ptr[len - 1]); + a = static_cast<uint64_t>((ptr[0] << 16) | (ptr[len >> 1] << 8) | + ptr[len - 1]); b = 0; } else { a = 0; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/memory/memory.h b/contrib/restricted/abseil-cpp-tstring/y_absl/memory/memory.h index 4addc6fb91..e791095cec 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/memory/memory.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/memory/memory.h @@ -75,31 +75,6 @@ std::unique_ptr<T> WrapUnique(T* ptr) { return std::unique_ptr<T>(ptr); } -namespace memory_internal { - -// Traits to select proper overload and return type for `y_absl::make_unique<>`. -template <typename T> -struct MakeUniqueResult { - using scalar = std::unique_ptr<T>; -}; -template <typename T> -struct MakeUniqueResult<T[]> { - using array = std::unique_ptr<T[]>; -}; -template <typename T, size_t N> -struct MakeUniqueResult<T[N]> { - using invalid = void; -}; - -} // namespace memory_internal - -// 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 defined(__cpp_lib_make_unique) -using std::make_unique; -#else // ----------------------------------------------------------------------------- // Function Template: make_unique<T>() // ----------------------------------------------------------------------------- @@ -108,82 +83,18 @@ using std::make_unique; // during the construction process. `y_absl::make_unique<>` also avoids redundant // type declarations, by avoiding the need to explicitly use the `new` operator. // -// This implementation of `y_absl::make_unique<>` is designed for C++11 code and -// will be replaced in C++14 by the equivalent `std::make_unique<>` abstraction. -// `y_absl::make_unique<>` is designed to be 100% compatible with -// `std::make_unique<>` so that the eventual migration will involve a simple -// rename operation. +// https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique // // For more background on why `std::unique_ptr<T>(new T(a,b))` is problematic, // see Herb Sutter's explanation on // (Exception-Safe Function Calls)[https://herbsutter.com/gotw/_102/]. // (In general, reviewers should treat `new T(a,b)` with scrutiny.) // -// Example usage: -// -// auto p = make_unique<X>(args...); // 'p' is a std::unique_ptr<X> -// auto pa = make_unique<X[]>(5); // 'pa' is a std::unique_ptr<X[]> -// -// Three overloads of `y_absl::make_unique` are required: -// -// - For non-array T: -// -// Allocates a T with `new T(std::forward<Args> args...)`, -// forwarding all `args` to T's constructor. -// Returns a `std::unique_ptr<T>` owning that object. -// -// - For an array of unknown bounds T[]: -// -// `y_absl::make_unique<>` will allocate an array T of type U[] with -// `new U[n]()` and return a `std::unique_ptr<U[]>` owning that array. -// -// Note that 'U[n]()' is different from 'U[n]', and elements will be -// value-initialized. Note as well that `std::unique_ptr` will perform its -// own destruction of the array elements upon leaving scope, even though -// the array [] does not have a default destructor. -// -// NOTE: an array of unknown bounds T[] may still be (and often will be) -// initialized to have a size, and will still use this overload. E.g: -// -// auto my_array = y_absl::make_unique<int[]>(10); -// -// - For an array of known bounds T[N]: -// -// `y_absl::make_unique<>` is deleted (like with `std::make_unique<>`) as -// this overload is not useful. -// -// NOTE: an array of known bounds T[N] is not considered a useful -// construction, and may cause undefined behavior in templates. E.g: -// -// auto my_array = y_absl::make_unique<int[10]>(); -// -// In those cases, of course, you can still use the overload above and -// simply initialize it to its desired size: -// -// auto my_array = y_absl::make_unique<int[]>(10); - -// `y_absl::make_unique` overload for non-array types. -template <typename T, typename... Args> -typename memory_internal::MakeUniqueResult<T>::scalar make_unique( - Args&&... args) { - return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); -} - -// `y_absl::make_unique` overload for an array T[] of unknown bounds. -// The array allocation needs to use the `new T[size]` form and cannot take -// element constructor arguments. The `std::unique_ptr` will manage destructing -// these array elements. -template <typename T> -typename memory_internal::MakeUniqueResult<T>::array make_unique(size_t n) { - return std::unique_ptr<T>(new typename y_absl::remove_extent_t<T>[n]()); -} - -// `y_absl::make_unique` overload for an array T[N] of known bounds. -// This construction will be rejected. -template <typename T, typename... Args> -typename memory_internal::MakeUniqueResult<T>::invalid make_unique( - Args&&... /* args */) = delete; -#endif +// Historical note: Abseil once provided a C++11 compatible implementation of +// the C++14's `std::make_unique`. Now that C++11 support has been sunsetted, +// `y_absl::make_unique` simply uses the STL-provided implementation. New code +// should use `std::make_unique`. +using std::make_unique; // ----------------------------------------------------------------------------- // Function Template: RawPtr() @@ -247,6 +158,26 @@ std::weak_ptr<T> WeakenPtr(const std::shared_ptr<T>& ptr) { return std::weak_ptr<T>(ptr); } +// ----------------------------------------------------------------------------- +// Class Template: pointer_traits +// ----------------------------------------------------------------------------- +// +// Historical note: Abseil once provided an implementation of +// `std::pointer_traits` for platforms that had not yet provided it. Those +// platforms are no longer supported. New code should simply use +// `std::pointer_traits`. +using std::pointer_traits; + +// ----------------------------------------------------------------------------- +// Class Template: allocator_traits +// ----------------------------------------------------------------------------- +// +// Historical note: Abseil once provided an implementation of +// `std::allocator_traits` for platforms that had not yet provided it. Those +// platforms are no longer supported. New code should simply use +// `std::allocator_traits`. +using std::allocator_traits; + namespace memory_internal { // ExtractOr<E, O, D>::type evaluates to E<O> if possible. Otherwise, D. @@ -264,357 +195,6 @@ struct ExtractOr<Extract, Obj, Default, void_t<Extract<Obj>>> { template <template <typename> class Extract, typename Obj, typename Default> using ExtractOrT = typename ExtractOr<Extract, Obj, Default, void>::type; -// Extractors for the features of allocators. -template <typename T> -using GetPointer = typename T::pointer; - -template <typename T> -using GetConstPointer = typename T::const_pointer; - -template <typename T> -using GetVoidPointer = typename T::void_pointer; - -template <typename T> -using GetConstVoidPointer = typename T::const_void_pointer; - -template <typename T> -using GetDifferenceType = typename T::difference_type; - -template <typename T> -using GetSizeType = typename T::size_type; - -template <typename T> -using GetPropagateOnContainerCopyAssignment = - typename T::propagate_on_container_copy_assignment; - -template <typename T> -using GetPropagateOnContainerMoveAssignment = - typename T::propagate_on_container_move_assignment; - -template <typename T> -using GetPropagateOnContainerSwap = typename T::propagate_on_container_swap; - -template <typename T> -using GetIsAlwaysEqual = typename T::is_always_equal; - -template <typename T> -struct GetFirstArg; - -template <template <typename...> class Class, typename T, typename... Args> -struct GetFirstArg<Class<T, Args...>> { - using type = T; -}; - -template <typename Ptr, typename = void> -struct ElementType { - using type = typename GetFirstArg<Ptr>::type; -}; - -template <typename T> -struct ElementType<T, void_t<typename T::element_type>> { - using type = typename T::element_type; -}; - -template <typename T, typename U> -struct RebindFirstArg; - -template <template <typename...> class Class, typename T, typename... Args, - typename U> -struct RebindFirstArg<Class<T, Args...>, U> { - using type = Class<U, Args...>; -}; - -template <typename T, typename U, typename = void> -struct RebindPtr { - using type = typename RebindFirstArg<T, U>::type; -}; - -template <typename T, typename U> -struct RebindPtr<T, U, void_t<typename T::template rebind<U>>> { - using type = typename T::template rebind<U>; -}; - -template <typename T, typename U> -constexpr bool HasRebindAlloc(...) { - return false; -} - -template <typename T, typename U> -constexpr bool HasRebindAlloc(typename T::template rebind<U>::other*) { - return true; -} - -template <typename T, typename U, bool = HasRebindAlloc<T, U>(nullptr)> -struct RebindAlloc { - using type = typename RebindFirstArg<T, U>::type; -}; - -template <typename T, typename U> -struct RebindAlloc<T, U, true> { - using type = typename T::template rebind<U>::other; -}; - -} // namespace memory_internal - -// ----------------------------------------------------------------------------- -// Class Template: pointer_traits -// ----------------------------------------------------------------------------- -// -// An implementation of C++11's std::pointer_traits. -// -// Provided for portability on toolchains that have a working C++11 compiler, -// but the standard library is lacking in C++11 support. For example, some -// version of the Android NDK. -// - -template <typename Ptr> -struct pointer_traits { - using pointer = Ptr; - - // element_type: - // Ptr::element_type if present. Otherwise T if Ptr is a template - // instantiation Template<T, Args...> - using element_type = typename memory_internal::ElementType<Ptr>::type; - - // difference_type: - // Ptr::difference_type if present, otherwise std::ptrdiff_t - using difference_type = - memory_internal::ExtractOrT<memory_internal::GetDifferenceType, Ptr, - std::ptrdiff_t>; - - // rebind: - // Ptr::rebind<U> if exists, otherwise Template<U, Args...> if Ptr is a - // template instantiation Template<T, Args...> - template <typename U> - using rebind = typename memory_internal::RebindPtr<Ptr, U>::type; - - // pointer_to: - // Calls Ptr::pointer_to(r) - static pointer pointer_to(element_type& r) { // NOLINT(runtime/references) - return Ptr::pointer_to(r); - } -}; - -// Specialization for T*. -template <typename T> -struct pointer_traits<T*> { - using pointer = T*; - using element_type = T; - using difference_type = std::ptrdiff_t; - - template <typename U> - using rebind = U*; - - // pointer_to: - // Calls std::addressof(r) - static pointer pointer_to( - element_type& r) noexcept { // NOLINT(runtime/references) - return std::addressof(r); - } -}; - -// ----------------------------------------------------------------------------- -// Class Template: allocator_traits -// ----------------------------------------------------------------------------- -// -// A C++11 compatible implementation of C++17's std::allocator_traits. -// -#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -using std::allocator_traits; -#else // __cplusplus >= 201703L -template <typename Alloc> -struct allocator_traits { - using allocator_type = Alloc; - - // value_type: - // Alloc::value_type - using value_type = typename Alloc::value_type; - - // pointer: - // Alloc::pointer if present, otherwise value_type* - using pointer = memory_internal::ExtractOrT<memory_internal::GetPointer, - Alloc, value_type*>; - - // const_pointer: - // Alloc::const_pointer if present, otherwise - // y_absl::pointer_traits<pointer>::rebind<const value_type> - using const_pointer = - memory_internal::ExtractOrT<memory_internal::GetConstPointer, Alloc, - typename y_absl::pointer_traits<pointer>:: - template rebind<const value_type>>; - - // void_pointer: - // Alloc::void_pointer if present, otherwise - // y_absl::pointer_traits<pointer>::rebind<void> - using void_pointer = memory_internal::ExtractOrT< - memory_internal::GetVoidPointer, Alloc, - typename y_absl::pointer_traits<pointer>::template rebind<void>>; - - // const_void_pointer: - // Alloc::const_void_pointer if present, otherwise - // y_absl::pointer_traits<pointer>::rebind<const void> - using const_void_pointer = memory_internal::ExtractOrT< - memory_internal::GetConstVoidPointer, Alloc, - typename y_absl::pointer_traits<pointer>::template rebind<const void>>; - - // difference_type: - // Alloc::difference_type if present, otherwise - // y_absl::pointer_traits<pointer>::difference_type - using difference_type = memory_internal::ExtractOrT< - memory_internal::GetDifferenceType, Alloc, - typename y_absl::pointer_traits<pointer>::difference_type>; - - // size_type: - // Alloc::size_type if present, otherwise - // std::make_unsigned<difference_type>::type - using size_type = memory_internal::ExtractOrT< - memory_internal::GetSizeType, Alloc, - typename std::make_unsigned<difference_type>::type>; - - // propagate_on_container_copy_assignment: - // Alloc::propagate_on_container_copy_assignment if present, otherwise - // std::false_type - using propagate_on_container_copy_assignment = memory_internal::ExtractOrT< - memory_internal::GetPropagateOnContainerCopyAssignment, Alloc, - std::false_type>; - - // propagate_on_container_move_assignment: - // Alloc::propagate_on_container_move_assignment if present, otherwise - // std::false_type - using propagate_on_container_move_assignment = memory_internal::ExtractOrT< - memory_internal::GetPropagateOnContainerMoveAssignment, Alloc, - std::false_type>; - - // propagate_on_container_swap: - // Alloc::propagate_on_container_swap if present, otherwise std::false_type - using propagate_on_container_swap = - memory_internal::ExtractOrT<memory_internal::GetPropagateOnContainerSwap, - Alloc, std::false_type>; - - // is_always_equal: - // Alloc::is_always_equal if present, otherwise std::is_empty<Alloc>::type - using is_always_equal = - memory_internal::ExtractOrT<memory_internal::GetIsAlwaysEqual, Alloc, - typename std::is_empty<Alloc>::type>; - - // rebind_alloc: - // Alloc::rebind<T>::other if present, otherwise Alloc<T, Args> if this Alloc - // is Alloc<U, Args> - template <typename T> - using rebind_alloc = typename memory_internal::RebindAlloc<Alloc, T>::type; - - // rebind_traits: - // y_absl::allocator_traits<rebind_alloc<T>> - template <typename T> - using rebind_traits = y_absl::allocator_traits<rebind_alloc<T>>; - - // allocate(Alloc& a, size_type n): - // Calls a.allocate(n) - static pointer allocate(Alloc& a, // NOLINT(runtime/references) - size_type n) { - return a.allocate(n); - } - - // allocate(Alloc& a, size_type n, const_void_pointer hint): - // Calls a.allocate(n, hint) if possible. - // If not possible, calls a.allocate(n) - static pointer allocate(Alloc& a, size_type n, // NOLINT(runtime/references) - const_void_pointer hint) { - return allocate_impl(0, a, n, hint); - } - - // deallocate(Alloc& a, pointer p, size_type n): - // Calls a.deallocate(p, n) - static void deallocate(Alloc& a, pointer p, // NOLINT(runtime/references) - size_type n) { - a.deallocate(p, n); - } - - // construct(Alloc& a, T* p, Args&&... args): - // Calls a.construct(p, std::forward<Args>(args)...) if possible. - // If not possible, calls - // ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...) - template <typename T, typename... Args> - static void construct(Alloc& a, T* p, // NOLINT(runtime/references) - Args&&... args) { - construct_impl(0, a, p, std::forward<Args>(args)...); - } - - // destroy(Alloc& a, T* p): - // Calls a.destroy(p) if possible. If not possible, calls p->~T(). - template <typename T> - static void destroy(Alloc& a, T* p) { // NOLINT(runtime/references) - destroy_impl(0, a, p); - } - - // max_size(const Alloc& a): - // Returns a.max_size() if possible. If not possible, returns - // std::numeric_limits<size_type>::max() / sizeof(value_type) - static size_type max_size(const Alloc& a) { return max_size_impl(0, a); } - - // select_on_container_copy_construction(const Alloc& a): - // Returns a.select_on_container_copy_construction() if possible. - // If not possible, returns a. - static Alloc select_on_container_copy_construction(const Alloc& a) { - return select_on_container_copy_construction_impl(0, a); - } - - private: - template <typename A> - static auto allocate_impl(int, A& a, // NOLINT(runtime/references) - size_type n, const_void_pointer hint) - -> decltype(a.allocate(n, hint)) { - return a.allocate(n, hint); - } - static pointer allocate_impl(char, Alloc& a, // NOLINT(runtime/references) - size_type n, const_void_pointer) { - return a.allocate(n); - } - - template <typename A, typename... Args> - static auto construct_impl(int, A& a, // NOLINT(runtime/references) - Args&&... args) - -> decltype(a.construct(std::forward<Args>(args)...)) { - a.construct(std::forward<Args>(args)...); - } - - template <typename T, typename... Args> - static void construct_impl(char, Alloc&, T* p, Args&&... args) { - ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...); - } - - template <typename A, typename T> - static auto destroy_impl(int, A& a, // NOLINT(runtime/references) - T* p) -> decltype(a.destroy(p)) { - a.destroy(p); - } - template <typename T> - static void destroy_impl(char, Alloc&, T* p) { - p->~T(); - } - - template <typename A> - static auto max_size_impl(int, const A& a) -> decltype(a.max_size()) { - return a.max_size(); - } - static size_type max_size_impl(char, const Alloc&) { - return (std::numeric_limits<size_type>::max)() / sizeof(value_type); - } - - template <typename A> - static auto select_on_container_copy_construction_impl(int, const A& a) - -> decltype(a.select_on_container_copy_construction()) { - return a.select_on_container_copy_construction(); - } - static Alloc select_on_container_copy_construction_impl(char, - const Alloc& a) { - return a; - } -}; -#endif // __cplusplus >= 201703L - -namespace memory_internal { - // This template alias transforms Alloc::is_nothrow into a metafunction with // Alloc as a parameter so it can be used with ExtractOrT<>. template <typename Alloc> diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/memory/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/memory/ya.make index e8aff34ba5..d18aab37c5 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/memory/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/memory/ya.make @@ -6,9 +6,9 @@ LICENSE(Apache-2.0) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -VERSION(20220623.1) +VERSION(20230125.1) -ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20220623.1.tar.gz) +ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20230125.1.tar.gz) PEERDIR( contrib/restricted/abseil-cpp-tstring/y_absl/meta diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/meta/type_traits.h b/contrib/restricted/abseil-cpp-tstring/y_absl/meta/type_traits.h index 3ef4411009..0ffbcbee27 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/meta/type_traits.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/meta/type_traits.h @@ -114,18 +114,6 @@ struct VoidTImpl { using type = void; }; -// This trick to retrieve a default alignment is necessary for our -// implementation of aligned_storage_t to be consistent with any implementation -// of std::aligned_storage. -template <size_t Len, typename T = std::aligned_storage<Len>> -struct default_alignment_of_aligned_storage; - -template <size_t Len, size_t Align> -struct default_alignment_of_aligned_storage<Len, - std::aligned_storage<Len, Align>> { - static constexpr size_t value = Align; -}; - //////////////////////////////// // Library Fundamentals V2 TS // //////////////////////////////// @@ -298,8 +286,12 @@ struct is_function // https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html#Type-Traits. template <typename T> struct is_trivially_destructible +#ifdef Y_ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE + : std::is_trivially_destructible<T> { +#else : std::integral_constant<bool, __has_trivial_destructor(T) && std::is_destructible<T>::value> { +#endif #ifdef Y_ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE private: static constexpr bool compliant = std::is_trivially_destructible<T>::value == @@ -347,9 +339,13 @@ struct is_trivially_destructible // Nontrivially destructible types will cause the expression to be nontrivial. template <typename T> struct is_trivially_default_constructible +#if defined(Y_ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) + : std::is_trivially_default_constructible<T> { +#else : std::integral_constant<bool, __has_trivial_constructor(T) && std::is_default_constructible<T>::value && is_trivially_destructible<T>::value> { +#endif #if defined(Y_ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ !defined( \ Y_ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) @@ -381,10 +377,14 @@ struct is_trivially_default_constructible // expression to be nontrivial. template <typename T> struct is_trivially_move_constructible +#if defined(Y_ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) + : std::is_trivially_move_constructible<T> { +#else : std::conditional< std::is_object<T>::value && !std::is_array<T>::value, type_traits_internal::IsTriviallyMoveConstructibleObject<T>, std::is_reference<T>>::type::type { +#endif #if defined(Y_ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ !defined( \ Y_ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) @@ -490,9 +490,13 @@ struct is_trivially_move_assignable // `is_trivially_assignable<T&, const T&>`. template <typename T> struct is_trivially_copy_assignable +#ifdef Y_ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE + : std::is_trivially_copy_assignable<T> { +#else : std::integral_constant< bool, __has_trivial_assign(typename std::remove_reference<T>::type) && y_absl::is_copy_assignable<T>::value> { +#endif #ifdef Y_ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE private: static constexpr bool compliant = @@ -544,6 +548,11 @@ namespace type_traits_internal { // destructible. Arrays of trivially copyable types are trivially copyable. // // We expose this metafunction only for internal use within y_absl. + +#if defined(Y_ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE) +template <typename T> +struct is_trivially_copyable : std::is_trivially_copyable<T> {}; +#else template <typename T> class is_trivially_copyable_impl { using ExtentsRemoved = typename std::remove_all_extents<T>::type; @@ -569,6 +578,7 @@ template <typename T> struct is_trivially_copyable : std::integral_constant< bool, type_traits_internal::is_trivially_copyable_impl<T>::kValue> {}; +#endif } // namespace type_traits_internal // ----------------------------------------------------------------------------- @@ -620,6 +630,21 @@ using remove_extent_t = typename std::remove_extent<T>::type; template <typename T> using remove_all_extents_t = typename std::remove_all_extents<T>::type; +namespace type_traits_internal { +// This trick to retrieve a default alignment is necessary for our +// implementation of aligned_storage_t to be consistent with any +// implementation of std::aligned_storage. +template <size_t Len, typename T = std::aligned_storage<Len>> +struct default_alignment_of_aligned_storage; + +template <size_t Len, size_t Align> +struct default_alignment_of_aligned_storage< + Len, std::aligned_storage<Len, Align>> { + static constexpr size_t value = Align; +}; +} // namespace type_traits_internal + +// TODO(b/260219225): std::aligned_storage(_t) is deprecated in C++23. template <size_t Len, size_t Align = type_traits_internal:: default_alignment_of_aligned_storage<Len>::value> using aligned_storage_t = typename std::aligned_storage<Len, Align>::type; @@ -791,6 +816,73 @@ using swap_internal::Swap; using swap_internal::StdSwapIsUnconstrained; } // namespace type_traits_internal + +// y_absl::is_trivially_relocatable<T> +// Detects whether a type is "trivially relocatable" -- meaning it can be +// relocated without invoking the constructor/destructor, using a form of move +// elision. +// +// Example: +// +// if constexpr (y_absl::is_trivially_relocatable<T>::value) { +// memcpy(new_location, old_location, sizeof(T)); +// } else { +// new(new_location) T(std::move(*old_location)); +// old_location->~T(); +// } +// +// Upstream documentation: +// +// https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable +// +#if Y_ABSL_HAVE_BUILTIN(__is_trivially_relocatable) +template <class T> +struct is_trivially_relocatable + : std::integral_constant<bool, __is_trivially_relocatable(T)> {}; +#else +template <class T> +struct is_trivially_relocatable : std::integral_constant<bool, false> {}; +#endif + +// y_absl::is_constant_evaluated() +// +// Detects whether the function call occurs within a constant-evaluated context. +// Returns true if the evaluation of the call occurs within the evaluation of an +// expression or conversion that is manifestly constant-evaluated; otherwise +// returns false. +// +// This function is implemented in terms of `std::is_constant_evaluated` for +// c++20 and up. For older c++ versions, the function is implemented in terms +// of `__builtin_is_constant_evaluated` if available, otherwise the function +// will fail to compile. +// +// Applications can inspect `Y_ABSL_HAVE_CONSTANT_EVALUATED` at compile time +// to check if this function is supported. +// +// Example: +// +// constexpr MyClass::MyClass(int param) { +// #ifdef Y_ABSL_HAVE_CONSTANT_EVALUATED +// if (!y_absl::is_constant_evaluated()) { +// Y_ABSL_LOG(INFO) << "MyClass(" << param << ")"; +// } +// #endif // Y_ABSL_HAVE_CONSTANT_EVALUATED +// } +// +// Upstream documentation: +// +// http://en.cppreference.com/w/cpp/types/is_constant_evaluated +// http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#:~:text=__builtin_is_constant_evaluated +// +#if defined(Y_ABSL_HAVE_CONSTANT_EVALUATED) +constexpr bool is_constant_evaluated() noexcept { +#ifdef __cpp_lib_is_constant_evaluated + return std::is_constant_evaluated(); +#elif Y_ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated) + return __builtin_is_constant_evaluated(); +#endif +} +#endif // Y_ABSL_HAVE_CONSTANT_EVALUATED Y_ABSL_NAMESPACE_END } // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/meta/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/meta/ya.make index 040277a55b..2a87dbcbcc 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/meta/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/meta/ya.make @@ -6,9 +6,9 @@ LICENSE(Apache-2.0) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -VERSION(20220623.1) +VERSION(20230125.1) -ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20220623.1.tar.gz) +ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20230125.1.tar.gz) PEERDIR( contrib/restricted/abseil-cpp-tstring/y_absl/base diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/numeric/bits.h b/contrib/restricted/abseil-cpp-tstring/y_absl/numeric/bits.h index 75cd272d77..501c756da5 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/numeric/bits.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/numeric/bits.h @@ -131,10 +131,9 @@ has_single_bit(T x) noexcept { // fractional part discarded. template <class T> Y_ABSL_INTERNAL_CONSTEXPR_CLZ inline - typename std::enable_if<std::is_unsigned<T>::value, T>::type + typename std::enable_if<std::is_unsigned<T>::value, int>::type bit_width(T x) noexcept { - return std::numeric_limits<T>::digits - - static_cast<unsigned int>(countl_zero(x)); + return std::numeric_limits<T>::digits - countl_zero(x); } // Returns: If x == 0, 0; otherwise the maximal value y such that diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/numeric/int128.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/numeric/int128.cc index fe9d65cc63..a054aad2c1 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/numeric/int128.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/numeric/int128.cc @@ -209,15 +209,16 @@ std::ostream& operator<<(std::ostream& os, uint128 v) { // Add the requisite padding. std::streamsize width = os.width(0); if (static_cast<size_t>(width) > rep.size()) { + const size_t count = static_cast<size_t>(width) - rep.size(); std::ios::fmtflags adjustfield = flags & std::ios::adjustfield; if (adjustfield == std::ios::left) { - rep.append(width - rep.size(), os.fill()); + rep.append(count, os.fill()); } else if (adjustfield == std::ios::internal && (flags & std::ios::showbase) && (flags & std::ios::basefield) == std::ios::hex && v != 0) { - rep.insert((size_t)2, width - rep.size(), os.fill()); + rep.insert((size_t)2, count, os.fill()); } else { - rep.insert((size_t)0, width - rep.size(), os.fill()); + rep.insert((size_t)0, count, os.fill()); } } @@ -306,22 +307,23 @@ std::ostream& operator<<(std::ostream& os, int128 v) { // Add the requisite padding. std::streamsize width = os.width(0); if (static_cast<size_t>(width) > rep.size()) { + const size_t count = static_cast<size_t>(width) - rep.size(); switch (flags & std::ios::adjustfield) { case std::ios::left: - rep.append(width - rep.size(), os.fill()); + rep.append(count, os.fill()); break; case std::ios::internal: if (print_as_decimal && (rep[0] == '+' || rep[0] == '-')) { - rep.insert(1, width - rep.size(), os.fill()); + rep.insert(1u, count, os.fill()); } else if ((flags & std::ios::basefield) == std::ios::hex && (flags & std::ios::showbase) && v != 0) { - rep.insert((size_t)2, width - rep.size(), os.fill()); + rep.insert((size_t)2, count, os.fill()); } else { - rep.insert((size_t)0, width - rep.size(), os.fill()); + rep.insert((size_t)0, count, os.fill()); } break; default: // std::ios::right - rep.insert((size_t)0, width - rep.size(), os.fill()); + rep.insert((size_t)0, count, os.fill()); break; } } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/profiling/internal/sample_recorder.h b/contrib/restricted/abseil-cpp-tstring/y_absl/profiling/internal/sample_recorder.h index f820d567c9..3f7118b3d3 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/profiling/internal/sample_recorder.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/profiling/internal/sample_recorder.h @@ -77,8 +77,8 @@ class SampleRecorder { // samples that have been dropped. int64_t Iterate(const std::function<void(const T& stack)>& f); - int32_t GetMaxSamples() const; - void SetMaxSamples(int32_t max); + size_t GetMaxSamples() const; + void SetMaxSamples(size_t max); private: void PushNew(T* sample); @@ -88,7 +88,7 @@ class SampleRecorder { std::atomic<size_t> dropped_samples_; std::atomic<size_t> size_estimate_; - std::atomic<int32_t> max_samples_{1 << 20}; + std::atomic<size_t> max_samples_{1 << 20}; // Intrusive lock free linked lists for tracking samples. // @@ -186,7 +186,7 @@ T* SampleRecorder<T>::PopDead(Targs... args) { template <typename T> template <typename... Targs> T* SampleRecorder<T>::Register(Targs&&... args) { - int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); + size_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); if (size > max_samples_.load(std::memory_order_relaxed)) { size_estimate_.fetch_sub(1, std::memory_order_relaxed); dropped_samples_.fetch_add(1, std::memory_order_relaxed); @@ -199,6 +199,14 @@ T* SampleRecorder<T>::Register(Targs&&... args) { sample = new T(); { y_absl::MutexLock sample_lock(&sample->init_mu); + // If flag initialization happens to occur (perhaps in another thread) + // while in this block, it will lock `graveyard_` which is usually always + // locked before any sample. This will appear as a lock inversion. + // However, this code is run exactly once per sample, and this sample + // cannot be accessed until after it is returned from this method. This + // means that this lock state can never be recreated, so we can safely + // inform the deadlock detector to ignore it. + sample->init_mu.ForgetDeadlockInfo(); sample->PrepareForSampling(std::forward<Targs>(args)...); } PushNew(sample); @@ -229,12 +237,12 @@ int64_t SampleRecorder<T>::Iterate( } template <typename T> -void SampleRecorder<T>::SetMaxSamples(int32_t max) { +void SampleRecorder<T>::SetMaxSamples(size_t max) { max_samples_.store(max, std::memory_order_release); } template <typename T> -int32_t SampleRecorder<T>::GetMaxSamples() const { +size_t SampleRecorder<T>::GetMaxSamples() const { return max_samples_.load(std::memory_order_acquire); } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/fast_uniform_bits.h b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/fast_uniform_bits.h index f4ae282010..9827d1899e 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/fast_uniform_bits.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/fast_uniform_bits.h @@ -151,7 +151,8 @@ FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references) result_type r = static_cast<result_type>(g() - kMin); for (size_t n = 1; n < kIters; ++n) { - r = (r << kShift) + static_cast<result_type>(g() - kMin); + r = static_cast<result_type>(r << kShift) + + static_cast<result_type>(g() - kMin); } return r; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/nonsecure_base.h b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/nonsecure_base.h index 593cd880c7..920dfe73b8 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/nonsecure_base.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/nonsecure_base.h @@ -44,7 +44,7 @@ class RandenPoolSeedSeq { // Generate random unsigned values directly into the buffer. template <typename Contiguous> void generate_impl(ContiguousTag, Contiguous begin, Contiguous end) { - const size_t n = std::distance(begin, end); + const size_t n = static_cast<size_t>(std::distance(begin, end)); auto* a = &(*begin); RandenPool<uint8_t>::Fill( y_absl::MakeSpan(reinterpret_cast<uint8_t*>(a), sizeof(*a) * n)); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/pcg_engine.h b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/pcg_engine.h index 678af064de..a6d61bc21b 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/pcg_engine.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/pcg_engine.h @@ -221,47 +221,26 @@ class pcg_engine { template <uint64_t kMultA, uint64_t kMultB, uint64_t kIncA, uint64_t kIncB> class pcg128_params { public: -#if Y_ABSL_HAVE_INTRINSIC_INT128 - using state_type = __uint128_t; - static inline constexpr state_type make_u128(uint64_t a, uint64_t b) { - return (static_cast<__uint128_t>(a) << 64) | b; - } -#else using state_type = y_absl::uint128; - static inline constexpr state_type make_u128(uint64_t a, uint64_t b) { - return y_absl::MakeUint128(a, b); - } -#endif - static inline constexpr state_type multiplier() { - return make_u128(kMultA, kMultB); + return y_absl::MakeUint128(kMultA, kMultB); } static inline constexpr state_type increment() { - return make_u128(kIncA, kIncB); + return y_absl::MakeUint128(kIncA, kIncB); } }; // Implementation of the PCG xsl_rr_128_64 128-bit mixing function, which // accepts an input of state_type and mixes it into an output of result_type. struct pcg_xsl_rr_128_64 { -#if Y_ABSL_HAVE_INTRINSIC_INT128 - using state_type = __uint128_t; -#else using state_type = y_absl::uint128; -#endif using result_type = uint64_t; inline uint64_t operator()(state_type state) { // This is equivalent to the xsl_rr_128_64 mixing function. -#if Y_ABSL_HAVE_INTRINSIC_INT128 uint64_t rotate = static_cast<uint64_t>(state >> 122u); state ^= state >> 64; uint64_t s = static_cast<uint64_t>(state); -#else - uint64_t h = Uint128High64(state); - uint64_t rotate = h >> 58u; - uint64_t s = Uint128Low64(state) ^ h; -#endif return rotr(s, static_cast<int>(rotate)); } }; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/pool_urbg.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/pool_urbg.cc index 4468bc479b..7c4c18744f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/pool_urbg.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/pool_urbg.cc @@ -131,7 +131,7 @@ void RandenPoolEntry::Fill(uint8_t* out, size_t bytes) { } // Number of pooled urbg entries. -static constexpr int kPoolSize = 8; +static constexpr size_t kPoolSize = 8; // Shared pool entries. static y_absl::once_flag pool_once; @@ -147,15 +147,15 @@ Y_ABSL_CACHELINE_ALIGNED static RandenPoolEntry* shared_pools[kPoolSize]; // on subsequent runs the order within the same program may be significantly // different. However, as other thread IDs are not assigned sequentially, // this is not expected to matter. -int GetPoolID() { +size_t GetPoolID() { static_assert(kPoolSize >= 1, "At least one urbg instance is required for PoolURBG"); - Y_ABSL_CONST_INIT static std::atomic<int64_t> sequence{0}; + Y_ABSL_CONST_INIT static std::atomic<uint64_t> sequence{0}; #ifdef Y_ABSL_HAVE_THREAD_LOCAL - static thread_local int my_pool_id = -1; - if (Y_ABSL_PREDICT_FALSE(my_pool_id < 0)) { + static thread_local size_t my_pool_id = kPoolSize; + if (Y_ABSL_PREDICT_FALSE(my_pool_id == kPoolSize)) { my_pool_id = (sequence++ % kPoolSize); } return my_pool_id; @@ -171,8 +171,8 @@ int GetPoolID() { // Store the value in the pthread_{get/set}specific. However an uninitialized // value is 0, so add +1 to distinguish from the null value. - intptr_t my_pool_id = - reinterpret_cast<intptr_t>(pthread_getspecific(tid_key)); + uintptr_t my_pool_id = + reinterpret_cast<uintptr_t>(pthread_getspecific(tid_key)); if (Y_ABSL_PREDICT_FALSE(my_pool_id == 0)) { // No allocated ID, allocate the next value, cache it, and return. my_pool_id = (sequence++ % kPoolSize) + 1; @@ -194,7 +194,7 @@ RandenPoolEntry* PoolAlignedAlloc() { // Not all the platforms that we build for have std::aligned_alloc, however // since we never free these objects, we can over allocate and munge the // pointers to the correct alignment. - intptr_t x = reinterpret_cast<intptr_t>( + uintptr_t x = reinterpret_cast<uintptr_t>( new char[sizeof(RandenPoolEntry) + kAlignment]); auto y = x % kAlignment; void* aligned = reinterpret_cast<void*>(y == 0 ? x : (x + kAlignment - y)); @@ -215,7 +215,7 @@ void InitPoolURBG() { y_absl::MakeSpan(seed_material))) { random_internal::ThrowSeedGenException(); } - for (int i = 0; i < kPoolSize; i++) { + for (size_t i = 0; i < kPoolSize; i++) { shared_pools[i] = PoolAlignedAlloc(); shared_pools[i]->Init( y_absl::MakeSpan(&seed_material[i * kSeedSize], kSeedSize)); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/seed_material.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/seed_material.cc index 9268912827..19cea29649 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/seed_material.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/random/internal/seed_material.cc @@ -173,12 +173,12 @@ bool ReadSeedMaterialFromDevURandom(y_absl::Span<uint32_t> values) { } while (success && buffer_size > 0) { - int bytes_read = read(dev_urandom, buffer, buffer_size); + ssize_t bytes_read = read(dev_urandom, buffer, buffer_size); int read_error = errno; success = (bytes_read > 0); if (success) { buffer += bytes_read; - buffer_size -= bytes_read; + buffer_size -= static_cast<size_t>(bytes_read); } else if (bytes_read == -1 && read_error == EINTR) { success = true; // Need to try again. } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/random/random.h b/contrib/restricted/abseil-cpp-tstring/y_absl/random/random.h index 5c5ffb3f95..b98938ba7c 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/random/random.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/random/random.h @@ -68,7 +68,7 @@ Y_ABSL_NAMESPACE_BEGIN // // `y_absl::BitGen` may be constructed with an optional seed sequence type, // conforming to [rand.req.seed_seq], which will be mixed with additional -// non-deterministic data. +// non-deterministic data as detailed below. // // Example: // @@ -79,16 +79,16 @@ Y_ABSL_NAMESPACE_BEGIN // // Generate an integer value in the closed interval [1,6] // int die_roll2 = y_absl::uniform_int_distribution<int>(1, 6)(gen_with_seed); // +// Constructing two `y_absl::BitGen`s with the same seed sequence in the same +// process will produce the same sequence of variates, but need not do so across +// multiple processes even if they're executing the same binary. +// // `y_absl::BitGen` meets the requirements of the Uniform Random Bit Generator // (URBG) concept as per the C++17 standard [rand.req.urng] though differs // slightly with [rand.req.eng]. Like its standard library equivalents (e.g. // `std::mersenne_twister_engine`) `y_absl::BitGen` is not cryptographically // secure. // -// Constructing two `y_absl::BitGen`s with the same seed sequence in the same -// binary will produce the same sequence of variates within the same binary, but -// need not do so across multiple binary invocations. -// // This type has been optimized to perform better than Mersenne Twister // (https://en.wikipedia.org/wiki/Mersenne_Twister) and many other complex URBG // types on modern x86, ARM, and PPC architectures. @@ -147,7 +147,7 @@ using BitGen = random_internal::NonsecureURBGBase< // // `y_absl::InsecureBitGen` may be constructed with an optional seed sequence // type, conforming to [rand.req.seed_seq], which will be mixed with additional -// non-deterministic data. (See std_seed_seq.h for more information.) +// non-deterministic data, as detailed in the `y_absl::BitGen` comment. // // `y_absl::InsecureBitGen` meets the requirements of the Uniform Random Bit // Generator (URBG) concept as per the C++17 standard [rand.req.urng] though diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/status/internal/status_internal.h b/contrib/restricted/abseil-cpp-tstring/y_absl/status/internal/status_internal.h index 87259b1fc4..91ab0ebded 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/status/internal/status_internal.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/status/internal/status_internal.h @@ -14,6 +14,7 @@ #ifndef Y_ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ #define Y_ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ +#include <memory> #include <util/generic/string.h> #include <utility> diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/status/status.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/status/status.cc index 3bcf02dbbd..e0a660b98d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/status/status.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/status/status.cc @@ -16,9 +16,11 @@ #include <errno.h> #include <cassert> +#include <utility> #include "y_absl/base/internal/raw_logging.h" #include "y_absl/base/internal/strerror.h" +#include "y_absl/base/macros.h" #include "y_absl/debugging/stacktrace.h" #include "y_absl/debugging/symbolize.h" #include "y_absl/status/status_payload_printer.h" @@ -77,15 +79,17 @@ std::ostream& operator<<(std::ostream& os, StatusCode code) { namespace status_internal { -static int FindPayloadIndexByUrl(const Payloads* payloads, - y_absl::string_view type_url) { - if (payloads == nullptr) return -1; +static y_absl::optional<size_t> FindPayloadIndexByUrl( + const Payloads* payloads, + y_absl::string_view type_url) { + if (payloads == nullptr) + return y_absl::nullopt; for (size_t i = 0; i < payloads->size(); ++i) { if ((*payloads)[i].type_url == type_url) return i; } - return -1; + return y_absl::nullopt; } // Convert canonical code to a value known to this binary. @@ -119,8 +123,10 @@ y_absl::StatusCode MapToLocalCode(int value) { y_absl::optional<y_absl::Cord> Status::GetPayload( y_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; + y_absl::optional<size_t> index = + status_internal::FindPayloadIndexByUrl(payloads, type_url); + if (index.has_value()) + return (*payloads)[index.value()].payload; return y_absl::nullopt; } @@ -135,10 +141,10 @@ void Status::SetPayload(y_absl::string_view type_url, y_absl::Cord payload) { rep->payloads = y_absl::make_unique<status_internal::Payloads>(); } - int index = + y_absl::optional<size_t> index = status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url); - if (index != -1) { - (*rep->payloads)[index].payload = std::move(payload); + if (index.has_value()) { + (*rep->payloads)[index.value()].payload = std::move(payload); return; } @@ -146,10 +152,11 @@ void Status::SetPayload(y_absl::string_view type_url, y_absl::Cord payload) { } bool Status::ErasePayload(y_absl::string_view type_url) { - int index = status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url); - if (index != -1) { + y_absl::optional<size_t> index = + status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url); + if (index.has_value()) { PrepareToModify(); - GetPayloads()->erase(GetPayloads()->begin() + index); + GetPayloads()->erase(GetPayloads()->begin() + index.value()); if (GetPayloads()->empty() && message().empty()) { // Special case: If this can be represented inlined, it MUST be // inlined (EqualsSlow depends on this behavior). diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/status/status.h b/contrib/restricted/abseil-cpp-tstring/y_absl/status/status.h index a5705cec46..6ed9d5d32a 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/status/status.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/status/status.h @@ -51,10 +51,10 @@ #ifndef Y_ABSL_STATUS_STATUS_H_ #define Y_ABSL_STATUS_STATUS_H_ -#include <iostream> +#include <ostream> #include <util/generic/string.h> +#include <utility> -#include "y_absl/container/inlined_vector.h" #include "y_absl/functional/function_ref.h" #include "y_absl/status/internal/status_internal.h" #include "y_absl/strings/cord.h" diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.darwin-x86_64.txt b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.darwin-x86_64.txt index 18d72c6685..6b5c6935a6 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.darwin-x86_64.txt +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.darwin-x86_64.txt @@ -21,6 +21,14 @@ target_link_libraries(abseil-cpp-tstring-y_absl-strings PUBLIC abseil-cpp-tstring-y_absl-numeric ) target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_fallback.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_x86_64.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_non_temporal_memcpy.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_x86_arm_combined.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/status/statusor.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ascii.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/charconv.cc @@ -41,6 +49,7 @@ target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_sample_token.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/memutil.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.cc @@ -50,6 +59,7 @@ target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/output.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/utf8.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/match.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/numbers.cc diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.linux-aarch64.txt b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.linux-aarch64.txt index 43faced540..36dbc8869d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.linux-aarch64.txt +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.linux-aarch64.txt @@ -22,6 +22,14 @@ target_link_libraries(abseil-cpp-tstring-y_absl-strings PUBLIC abseil-cpp-tstring-y_absl-numeric ) target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_fallback.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_x86_64.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_non_temporal_memcpy.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_x86_arm_combined.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/status/statusor.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ascii.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/charconv.cc @@ -42,6 +50,7 @@ target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_sample_token.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/memutil.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.cc @@ -51,6 +60,7 @@ target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/output.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/utf8.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/match.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/numbers.cc diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.linux-x86_64.txt b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.linux-x86_64.txt index 43faced540..36dbc8869d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.linux-x86_64.txt +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.linux-x86_64.txt @@ -22,6 +22,14 @@ target_link_libraries(abseil-cpp-tstring-y_absl-strings PUBLIC abseil-cpp-tstring-y_absl-numeric ) target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_fallback.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_x86_64.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_non_temporal_memcpy.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_x86_arm_combined.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/status/statusor.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ascii.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/charconv.cc @@ -42,6 +50,7 @@ target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_sample_token.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/memutil.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.cc @@ -51,6 +60,7 @@ target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/output.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/utf8.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/match.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/numbers.cc diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.windows-x86_64.txt b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.windows-x86_64.txt index 18d72c6685..6b5c6935a6 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.windows-x86_64.txt +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/CMakeLists.windows-x86_64.txt @@ -21,6 +21,14 @@ target_link_libraries(abseil-cpp-tstring-y_absl-strings PUBLIC abseil-cpp-tstring-y_absl-numeric ) target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/crc32c.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/cpu_detect.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_cord_state.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_fallback.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_memcpy_x86_64.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_non_temporal_memcpy.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/crc/internal/crc_x86_arm_combined.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/status/statusor.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ascii.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/charconv.cc @@ -41,6 +49,7 @@ target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_sample_token.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/memutil.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.cc @@ -50,6 +59,7 @@ target_sources(abseil-cpp-tstring-y_absl-strings PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/output.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.cc + ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/utf8.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/match.cc ${CMAKE_SOURCE_DIR}/contrib/restricted/abseil-cpp-tstring/y_absl/strings/numbers.cc diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ascii.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ascii.cc index a628cb0f94..e8e7332a2a 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ascii.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ascii.cc @@ -157,13 +157,13 @@ Y_ABSL_DLL const char kToUpper[256] = { void AsciiStrToLower(TString* s) { for (auto& ch : *s) { - ch = y_absl::ascii_tolower(ch); + ch = y_absl::ascii_tolower(static_cast<unsigned char>(ch)); } } void AsciiStrToUpper(TString* s) { for (auto& ch : *s) { - ch = y_absl::ascii_toupper(ch); + ch = y_absl::ascii_toupper(static_cast<unsigned char>(ch)); } } @@ -183,17 +183,17 @@ void RemoveExtraAsciiWhitespace(TString* str) { for (; input_it < input_end; ++input_it) { if (is_ws) { // Consecutive whitespace? Keep only the last. - is_ws = y_absl::ascii_isspace(*input_it); + is_ws = y_absl::ascii_isspace(static_cast<unsigned char>(*input_it)); if (is_ws) --output_it; } else { - is_ws = y_absl::ascii_isspace(*input_it); + is_ws = y_absl::ascii_isspace(static_cast<unsigned char>(*input_it)); } *output_it = *input_it; ++output_it; } - str->erase(output_it - &(*str)[0]); + str->erase(static_cast<size_t>(output_it - &(*str)[0])); } Y_ABSL_NAMESPACE_END diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/charconv.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/charconv.cc index 01ac7c6260..67134e6181 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/charconv.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/charconv.cc @@ -18,6 +18,7 @@ #include <cassert> #include <cmath> #include <cstring> +#include <limits> #include "y_absl/base/casts.h" #include "y_absl/numeric/bits.h" @@ -65,6 +66,14 @@ struct FloatTraits; template <> struct FloatTraits<double> { + using mantissa_t = uint64_t; + + // The number of bits in the given float type. + static constexpr int kTargetBits = 64; + + // The number of exponent bits in the given float type. + static constexpr int kTargetExponentBits = 11; + // The number of mantissa bits in the given float type. This includes the // implied high bit. static constexpr int kTargetMantissaBits = 53; @@ -83,6 +92,31 @@ struct FloatTraits<double> { // m * 2**kMinNormalExponent is exactly equal to DBL_MIN. static constexpr int kMinNormalExponent = -1074; + // The IEEE exponent bias. It equals ((1 << (kTargetExponentBits - 1)) - 1). + static constexpr int kExponentBias = 1023; + + // The Eisel-Lemire "Shifting to 54/25 Bits" adjustment. It equals (63 - 1 - + // kTargetMantissaBits). + static constexpr int kEiselLemireShift = 9; + + // The Eisel-Lemire high64_mask. It equals ((1 << kEiselLemireShift) - 1). + static constexpr uint64_t kEiselLemireMask = uint64_t{0x1FF}; + + // The smallest negative integer N (smallest negative means furthest from + // zero) such that parsing 9999999999999999999eN, with 19 nines, is still + // positive. Parsing a smaller (more negative) N will produce zero. + // + // Adjusting the decimal point and exponent, without adjusting the value, + // 9999999999999999999eN equals 9.999999999999999999eM where M = N + 18. + // + // 9999999999999999999, with 19 nines but no decimal point, is the largest + // "repeated nines" integer that fits in a uint64_t. + static constexpr int kEiselLemireMinInclusiveExp10 = -324 - 18; + + // The smallest positive integer N such that parsing 1eN produces infinity. + // Parsing a smaller N will produce something finite. + static constexpr int kEiselLemireMaxExclusiveExp10 = 309; + static double MakeNan(const char* tagp) { // Support nan no matter which namespace it's in. Some platforms // incorrectly don't put it in namespace std. @@ -103,7 +137,7 @@ struct FloatTraits<double> { // a normal value is made, or it must be less narrow than that, in which case // `exponent` must be exactly kMinNormalExponent, and a subnormal value is // made. - static double Make(uint64_t mantissa, int exponent, bool sign) { + static double Make(mantissa_t mantissa, int exponent, bool sign) { #ifndef Y_ABSL_BIT_PACK_FLOATS // Support ldexp no matter which namespace it's in. Some platforms // incorrectly don't put it in namespace std. @@ -116,8 +150,10 @@ struct FloatTraits<double> { if (mantissa > kMantissaMask) { // Normal value. // Adjust by 1023 for the exponent representation bias, and an additional - // 52 due to the implied decimal point in the IEEE mantissa represenation. - dbl += uint64_t{exponent + 1023u + kTargetMantissaBits - 1} << 52; + // 52 due to the implied decimal point in the IEEE mantissa + // representation. + dbl += static_cast<uint64_t>(exponent + 1023 + kTargetMantissaBits - 1) + << 52; mantissa &= kMantissaMask; } else { // subnormal value @@ -134,16 +170,27 @@ struct FloatTraits<double> { // members and methods. template <> struct FloatTraits<float> { + using mantissa_t = uint32_t; + + static constexpr int kTargetBits = 32; + static constexpr int kTargetExponentBits = 8; static constexpr int kTargetMantissaBits = 24; static constexpr int kMaxExponent = 104; static constexpr int kMinNormalExponent = -149; + static constexpr int kExponentBias = 127; + static constexpr int kEiselLemireShift = 38; + static constexpr uint64_t kEiselLemireMask = uint64_t{0x3FFFFFFFFF}; + static constexpr int kEiselLemireMinInclusiveExp10 = -46 - 18; + static constexpr int kEiselLemireMaxExclusiveExp10 = 39; + static float MakeNan(const char* tagp) { // Support nanf no matter which namespace it's in. Some platforms // incorrectly don't put it in namespace std. using namespace std; // NOLINT return nanf(tagp); } - static float Make(uint32_t mantissa, int exponent, bool sign) { + + static float Make(mantissa_t mantissa, int exponent, bool sign) { #ifndef Y_ABSL_BIT_PACK_FLOATS // Support ldexpf no matter which namespace it's in. Some platforms // incorrectly don't put it in namespace std. @@ -157,7 +204,8 @@ struct FloatTraits<float> { // Normal value. // Adjust by 127 for the exponent representation bias, and an additional // 23 due to the implied decimal point in the IEEE mantissa represenation. - flt += uint32_t{exponent + 127u + kTargetMantissaBits - 1} << 23; + flt += static_cast<uint32_t>(exponent + 127 + kTargetMantissaBits - 1) + << 23; mantissa &= kMantissaMask; } else { // subnormal value @@ -181,39 +229,45 @@ struct FloatTraits<float> { // // 2**63 <= Power10Mantissa(n) < 2**64. // +// See the "Table of powers of 10" comment below for a "1e60" example. +// // Lookups into the power-of-10 table must first check the Power10Overflow() and // Power10Underflow() functions, to avoid out-of-bounds table access. // -// Indexes into these tables are biased by -kPower10TableMin, and the table has -// values in the range [kPower10TableMin, kPower10TableMax]. -extern const uint64_t kPower10MantissaTable[]; -extern const int16_t kPower10ExponentTable[]; +// Indexes into these tables are biased by -kPower10TableMinInclusive. Valid +// indexes range from kPower10TableMinInclusive to kPower10TableMaxExclusive. +extern const uint64_t kPower10MantissaHighTable[]; // High 64 of 128 bits. +extern const uint64_t kPower10MantissaLowTable[]; // Low 64 of 128 bits. -// The smallest allowed value for use with the Power10Mantissa() and -// Power10Exponent() functions below. (If a smaller exponent is needed in +// The smallest (inclusive) allowed value for use with the Power10Mantissa() +// and Power10Exponent() functions below. (If a smaller exponent is needed in // calculations, the end result is guaranteed to underflow.) -constexpr int kPower10TableMin = -342; +constexpr int kPower10TableMinInclusive = -342; -// The largest allowed value for use with the Power10Mantissa() and -// Power10Exponent() functions below. (If a smaller exponent is needed in -// calculations, the end result is guaranteed to overflow.) -constexpr int kPower10TableMax = 308; +// The largest (exclusive) allowed value for use with the Power10Mantissa() and +// Power10Exponent() functions below. (If a larger-or-equal exponent is needed +// in calculations, the end result is guaranteed to overflow.) +constexpr int kPower10TableMaxExclusive = 309; uint64_t Power10Mantissa(int n) { - return kPower10MantissaTable[n - kPower10TableMin]; + return kPower10MantissaHighTable[n - kPower10TableMinInclusive]; } int Power10Exponent(int n) { - return kPower10ExponentTable[n - kPower10TableMin]; + // The 217706 etc magic numbers encode the results as a formula instead of a + // table. Their equivalence (over the kPower10TableMinInclusive .. + // kPower10TableMaxExclusive range) is confirmed by + // https://github.com/google/wuffs/blob/315b2e52625ebd7b02d8fac13e3cd85ea374fb80/script/print-mpb-powers-of-10.go + return (217706 * n >> 16) - 63; } // Returns true if n is large enough that 10**n always results in an IEEE // overflow. -bool Power10Overflow(int n) { return n > kPower10TableMax; } +bool Power10Overflow(int n) { return n >= kPower10TableMaxExclusive; } // Returns true if n is small enough that 10**n times a ParsedFloat mantissa // always results in an IEEE underflow. -bool Power10Underflow(int n) { return n < kPower10TableMin; } +bool Power10Underflow(int n) { return n < kPower10TableMinInclusive; } // Returns true if Power10Mantissa(n) * 2**Power10Exponent(n) is exactly equal // to 10**n numerically. Put another way, this returns true if there is no @@ -242,9 +296,11 @@ struct CalculatedFloat { // Returns the bit width of the given uint128. (Equivalently, returns 128 // minus the number of leading zero bits.) -unsigned BitWidth(uint128 value) { +int BitWidth(uint128 value) { if (Uint128High64(value) == 0) { - return static_cast<unsigned>(bit_width(Uint128Low64(value))); + // This static_cast is only needed when using a std::bit_width() + // implementation that does not have the fix for LWG 3656 applied. + return static_cast<int>(bit_width(Uint128Low64(value))); } return 128 - countl_zero(Uint128High64(value)); } @@ -285,14 +341,19 @@ template <typename FloatType> bool HandleEdgeCase(const strings_internal::ParsedFloat& input, bool negative, FloatType* value) { if (input.type == strings_internal::FloatType::kNan) { - // A bug in both clang and gcc would cause the compiler to optimize away the - // buffer we are building below. Declaring the buffer volatile avoids the - // issue, and has no measurable performance impact in microbenchmarks. + // A bug in both clang < 7 and gcc would cause the compiler to optimize + // away the buffer we are building below. Declaring the buffer volatile + // avoids the issue, and has no measurable performance impact in + // microbenchmarks. // // https://bugs.llvm.org/show_bug.cgi?id=37778 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86113 constexpr ptrdiff_t kNanBufferSize = 128; +#if defined(__GNUC__) || (defined(__clang__) && __clang_major__ < 7) volatile char n_char_sequence[kNanBufferSize]; +#else + char n_char_sequence[kNanBufferSize]; +#endif if (input.subrange_begin == nullptr) { n_char_sequence[0] = '\0'; } else { @@ -337,8 +398,10 @@ void EncodeResult(const CalculatedFloat& calculated, bool negative, *value = negative ? -0.0 : 0.0; return; } - *value = FloatTraits<FloatType>::Make(calculated.mantissa, - calculated.exponent, negative); + *value = FloatTraits<FloatType>::Make( + static_cast<typename FloatTraits<FloatType>::mantissa_t>( + calculated.mantissa), + calculated.exponent, negative); } // Returns the given uint128 shifted to the right by `shift` bits, and rounds @@ -519,7 +582,9 @@ CalculatedFloat CalculateFromParsedHexadecimal( const strings_internal::ParsedFloat& parsed_hex) { uint64_t mantissa = parsed_hex.mantissa; int exponent = parsed_hex.exponent; - auto mantissa_width = static_cast<unsigned>(bit_width(mantissa)); + // This static_cast is only needed when using a std::bit_width() + // implementation that does not have the fix for LWG 3656 applied. + int mantissa_width = static_cast<int>(bit_width(mantissa)); const int shift = NormalizedShiftSize<FloatType>(mantissa_width, exponent); bool result_exact; exponent += shift; @@ -595,6 +660,185 @@ CalculatedFloat CalculateFromParsedDecimal( binary_exponent); } +// As discussed in https://nigeltao.github.io/blog/2020/eisel-lemire.html the +// primary goal of the Eisel-Lemire algorithm is speed, for 99+% of the cases, +// not 100% coverage. As long as Eisel-Lemire doesn’t claim false positives, +// the combined approach (falling back to an alternative implementation when +// this function returns false) is both fast and correct. +template <typename FloatType> +bool EiselLemire(const strings_internal::ParsedFloat& input, bool negative, + FloatType* value, std::errc* ec) { + uint64_t man = input.mantissa; + int exp10 = input.exponent; + if (exp10 < FloatTraits<FloatType>::kEiselLemireMinInclusiveExp10) { + *value = negative ? -0.0 : 0.0; + *ec = std::errc::result_out_of_range; + return true; + } else if (exp10 >= FloatTraits<FloatType>::kEiselLemireMaxExclusiveExp10) { + // Return max (a finite value) consistent with from_chars and DR 3081. For + // SimpleAtod and SimpleAtof, post-processing will return infinity. + *value = negative ? -std::numeric_limits<FloatType>::max() + : std::numeric_limits<FloatType>::max(); + *ec = std::errc::result_out_of_range; + return true; + } + + // Assert kPower10TableMinInclusive <= exp10 < kPower10TableMaxExclusive. + // Equivalently, !Power10Underflow(exp10) and !Power10Overflow(exp10). + static_assert( + FloatTraits<FloatType>::kEiselLemireMinInclusiveExp10 >= + kPower10TableMinInclusive, + "(exp10-kPower10TableMinInclusive) in kPower10MantissaHighTable bounds"); + static_assert( + FloatTraits<FloatType>::kEiselLemireMaxExclusiveExp10 <= + kPower10TableMaxExclusive, + "(exp10-kPower10TableMinInclusive) in kPower10MantissaHighTable bounds"); + + // The terse (+) comments in this function body refer to sections of the + // https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post. + // + // That blog post discusses double precision (11 exponent bits with a -1023 + // bias, 52 mantissa bits), but the same approach applies to single precision + // (8 exponent bits with a -127 bias, 23 mantissa bits). Either way, the + // computation here happens with 64-bit values (e.g. man) or 128-bit values + // (e.g. x) before finally converting to 64- or 32-bit floating point. + // + // See also "Number Parsing at a Gigabyte per Second, Software: Practice and + // Experience 51 (8), 2021" (https://arxiv.org/abs/2101.11408) for detail. + + // (+) Normalization. + int clz = countl_zero(man); + man <<= static_cast<unsigned int>(clz); + // The 217706 etc magic numbers are from the Power10Exponent function. + uint64_t ret_exp2 = + static_cast<uint64_t>((217706 * exp10 >> 16) + 64 + + FloatTraits<FloatType>::kExponentBias - clz); + + // (+) Multiplication. + uint128 x = static_cast<uint128>(man) * + static_cast<uint128>( + kPower10MantissaHighTable[exp10 - kPower10TableMinInclusive]); + + // (+) Wider Approximation. + static constexpr uint64_t high64_mask = + FloatTraits<FloatType>::kEiselLemireMask; + if (((Uint128High64(x) & high64_mask) == high64_mask) && + (man > (std::numeric_limits<uint64_t>::max() - Uint128Low64(x)))) { + uint128 y = + static_cast<uint128>(man) * + static_cast<uint128>( + kPower10MantissaLowTable[exp10 - kPower10TableMinInclusive]); + x += Uint128High64(y); + // For example, parsing "4503599627370497.5" will take the if-true + // branch here (for double precision), since: + // - x = 0x8000000000000BFF_FFFFFFFFFFFFFFFF + // - y = 0x8000000000000BFF_7FFFFFFFFFFFF400 + // - man = 0xA000000000000F00 + // Likewise, when parsing "0.0625" for single precision: + // - x = 0x7FFFFFFFFFFFFFFF_FFFFFFFFFFFFFFFF + // - y = 0x813FFFFFFFFFFFFF_8A00000000000000 + // - man = 0x9C40000000000000 + if (((Uint128High64(x) & high64_mask) == high64_mask) && + ((Uint128Low64(x) + 1) == 0) && + (man > (std::numeric_limits<uint64_t>::max() - Uint128Low64(y)))) { + return false; + } + } + + // (+) Shifting to 54 Bits (or for single precision, to 25 bits). + uint64_t msb = Uint128High64(x) >> 63; + uint64_t ret_man = + Uint128High64(x) >> (msb + FloatTraits<FloatType>::kEiselLemireShift); + ret_exp2 -= 1 ^ msb; + + // (+) Half-way Ambiguity. + // + // For example, parsing "1e+23" will take the if-true branch here (for double + // precision), since: + // - x = 0x54B40B1F852BDA00_0000000000000000 + // - ret_man = 0x002A5A058FC295ED + // Likewise, when parsing "20040229.0" for single precision: + // - x = 0x4C72894000000000_0000000000000000 + // - ret_man = 0x000000000131CA25 + if ((Uint128Low64(x) == 0) && ((Uint128High64(x) & high64_mask) == 0) && + ((ret_man & 3) == 1)) { + return false; + } + + // (+) From 54 to 53 Bits (or for single precision, from 25 to 24 bits). + ret_man += ret_man & 1; // Line From54a. + ret_man >>= 1; // Line From54b. + // Incrementing ret_man (at line From54a) may have overflowed 54 bits (53 + // bits after the right shift by 1 at line From54b), so adjust for that. + // + // For example, parsing "9223372036854775807" will take the if-true branch + // here (for double precision), since: + // - ret_man = 0x0020000000000000 = (1 << 53) + // Likewise, when parsing "2147483647.0" for single precision: + // - ret_man = 0x0000000001000000 = (1 << 24) + if ((ret_man >> FloatTraits<FloatType>::kTargetMantissaBits) > 0) { + ret_exp2 += 1; + // Conceptually, we need a "ret_man >>= 1" in this if-block to balance + // incrementing ret_exp2 in the line immediately above. However, we only + // get here when line From54a overflowed (after adding a 1), so ret_man + // here is (1 << 53). Its low 53 bits are therefore all zeroes. The only + // remaining use of ret_man is to mask it with ((1 << 52) - 1), so only its + // low 52 bits matter. A "ret_man >>= 1" would have no effect in practice. + // + // We omit the "ret_man >>= 1", even if it is cheap (and this if-branch is + // rarely taken) and technically 'more correct', so that mutation tests + // that would otherwise modify or omit that "ret_man >>= 1" don't complain + // that such code mutations have no observable effect. + } + + // ret_exp2 is a uint64_t. Zero or underflow means that we're in subnormal + // space. max_exp2 (0x7FF for double precision, 0xFF for single precision) or + // above means that we're in Inf/NaN space. + // + // The if block is equivalent to (but has fewer branches than): + // if ((ret_exp2 <= 0) || (ret_exp2 >= max_exp2)) { etc } + // + // For example, parsing "4.9406564584124654e-324" will take the if-true + // branch here, since ret_exp2 = -51. + static constexpr uint64_t max_exp2 = + (1 << FloatTraits<FloatType>::kTargetExponentBits) - 1; + if ((ret_exp2 - 1) >= (max_exp2 - 1)) { + return false; + } + +#ifndef Y_ABSL_BIT_PACK_FLOATS + if (FloatTraits<FloatType>::kTargetBits == 64) { + *value = FloatTraits<FloatType>::Make( + (ret_man & 0x000FFFFFFFFFFFFFu) | 0x0010000000000000u, + static_cast<int>(ret_exp2) - 1023 - 52, negative); + return true; + } else if (FloatTraits<FloatType>::kTargetBits == 32) { + *value = FloatTraits<FloatType>::Make( + (static_cast<uint32_t>(ret_man) & 0x007FFFFFu) | 0x00800000u, + static_cast<int>(ret_exp2) - 127 - 23, negative); + return true; + } +#else + if (FloatTraits<FloatType>::kTargetBits == 64) { + uint64_t ret_bits = (ret_exp2 << 52) | (ret_man & 0x000FFFFFFFFFFFFFu); + if (negative) { + ret_bits |= 0x8000000000000000u; + } + *value = y_absl::bit_cast<double>(ret_bits); + return true; + } else if (FloatTraits<FloatType>::kTargetBits == 32) { + uint32_t ret_bits = (static_cast<uint32_t>(ret_exp2) << 23) | + (static_cast<uint32_t>(ret_man) & 0x007FFFFFu); + if (negative) { + ret_bits |= 0x80000000u; + } + *value = y_absl::bit_cast<float>(ret_bits); + return true; + } +#endif // Y_ABSL_BIT_PACK_FLOATS + return false; +} + template <typename FloatType> from_chars_result FromCharsImpl(const char* first, const char* last, FloatType& value, chars_format fmt_flags) { @@ -668,6 +912,12 @@ from_chars_result FromCharsImpl(const char* first, const char* last, if (HandleEdgeCase(decimal_parse, negative, &value)) { return result; } + // A nullptr subrange_begin means that the decimal_parse.mantissa is exact + // (not truncated), a precondition of the Eisel-Lemire algorithm. + if ((decimal_parse.subrange_begin == nullptr) && + EiselLemire<FloatType>(decimal_parse, negative, &value, &result.ec)) { + return result; + } CalculatedFloat calculated = CalculateFromParsedDecimal<FloatType>(decimal_parse); EncodeResult(calculated, negative, &result, &value); @@ -688,15 +938,46 @@ from_chars_result from_chars(const char* first, const char* last, float& value, namespace { -// Table of powers of 10, from kPower10TableMin to kPower10TableMax. +// Table of powers of 10, from kPower10TableMinInclusive to +// kPower10TableMaxExclusive. +// +// kPower10MantissaHighTable[i - kPower10TableMinInclusive] stores the 64-bit +// mantissa. The high bit is always on. +// +// kPower10MantissaLowTable extends that 64-bit mantissa to 128 bits. +// +// Power10Exponent(i) calculates the power-of-two exponent. +// +// For a number i, this gives the unique mantissaHigh and exponent such that +// (mantissaHigh * 2**exponent) <= 10**i < ((mantissaHigh + 1) * 2**exponent). +// +// For example, Python can confirm that the exact hexadecimal value of 1e60 is: +// >>> a = 1000000000000000000000000000000000000000000000000000000000000 +// >>> hex(a) +// '0x9f4f2726179a224501d762422c946590d91000000000000000' +// Adding underscores at every 8th hex digit shows 50 hex digits: +// '0x9f4f2726_179a2245_01d76242_2c946590_d9100000_00000000_00'. +// In this case, the high bit of the first hex digit, 9, is coincidentally set, +// so we do not have to do further shifting to deduce the 128-bit mantissa: +// - kPower10MantissaHighTable[60 - kP10TMI] = 0x9f4f2726179a2245U +// - kPower10MantissaLowTable[ 60 - kP10TMI] = 0x01d762422c946590U +// where kP10TMI is kPower10TableMinInclusive. The low 18 of those 50 hex +// digits are truncated. +// +// 50 hex digits (with the high bit set) is 200 bits and mantissaHigh holds 64 +// bits, so Power10Exponent(60) = 200 - 64 = 136. Again, Python can confirm: +// >>> b = 0x9f4f2726179a2245 +// >>> ((b+0)<<136) <= a +// True +// >>> ((b+1)<<136) <= a +// False // -// kPower10MantissaTable[i - kPower10TableMin] stores the 64-bit mantissa (high -// bit always on), and kPower10ExponentTable[i - kPower10TableMin] stores the -// power-of-two exponent. For a given number i, this gives the unique mantissa -// and exponent such that mantissa * 2**exponent <= 10**i < (mantissa + 1) * -// 2**exponent. +// The tables were generated by +// https://github.com/google/wuffs/blob/315b2e52625ebd7b02d8fac13e3cd85ea374fb80/script/print-mpb-powers-of-10.go +// after re-formatting its output into two arrays of N uint64_t values (instead +// of an N element array of uint64_t pairs). -const uint64_t kPower10MantissaTable[] = { +const uint64_t kPower10MantissaHighTable[] = { 0xeef453d6923bd65aU, 0x9558b4661b6565f8U, 0xbaaee17fa23ebf76U, 0xe95a99df8ace6f53U, 0x91d8a02bb6c10594U, 0xb64ec836a47146f9U, 0xe3e27a444d8d98b7U, 0x8e6d8c6ab0787f72U, 0xb208ef855c969f4fU, @@ -916,67 +1197,224 @@ const uint64_t kPower10MantissaTable[] = { 0xb6472e511c81471dU, 0xe3d8f9e563a198e5U, 0x8e679c2f5e44ff8fU, }; -const int16_t kPower10ExponentTable[] = { - -1200, -1196, -1193, -1190, -1186, -1183, -1180, -1176, -1173, -1170, -1166, - -1163, -1160, -1156, -1153, -1150, -1146, -1143, -1140, -1136, -1133, -1130, - -1127, -1123, -1120, -1117, -1113, -1110, -1107, -1103, -1100, -1097, -1093, - -1090, -1087, -1083, -1080, -1077, -1073, -1070, -1067, -1063, -1060, -1057, - -1053, -1050, -1047, -1043, -1040, -1037, -1034, -1030, -1027, -1024, -1020, - -1017, -1014, -1010, -1007, -1004, -1000, -997, -994, -990, -987, -984, - -980, -977, -974, -970, -967, -964, -960, -957, -954, -950, -947, - -944, -940, -937, -934, -931, -927, -924, -921, -917, -914, -911, - -907, -904, -901, -897, -894, -891, -887, -884, -881, -877, -874, - -871, -867, -864, -861, -857, -854, -851, -847, -844, -841, -838, - -834, -831, -828, -824, -821, -818, -814, -811, -808, -804, -801, - -798, -794, -791, -788, -784, -781, -778, -774, -771, -768, -764, - -761, -758, -754, -751, -748, -744, -741, -738, -735, -731, -728, - -725, -721, -718, -715, -711, -708, -705, -701, -698, -695, -691, - -688, -685, -681, -678, -675, -671, -668, -665, -661, -658, -655, - -651, -648, -645, -642, -638, -635, -632, -628, -625, -622, -618, - -615, -612, -608, -605, -602, -598, -595, -592, -588, -585, -582, - -578, -575, -572, -568, -565, -562, -558, -555, -552, -549, -545, - -542, -539, -535, -532, -529, -525, -522, -519, -515, -512, -509, - -505, -502, -499, -495, -492, -489, -485, -482, -479, -475, -472, - -469, -465, -462, -459, -455, -452, -449, -446, -442, -439, -436, - -432, -429, -426, -422, -419, -416, -412, -409, -406, -402, -399, - -396, -392, -389, -386, -382, -379, -376, -372, -369, -366, -362, - -359, -356, -353, -349, -346, -343, -339, -336, -333, -329, -326, - -323, -319, -316, -313, -309, -306, -303, -299, -296, -293, -289, - -286, -283, -279, -276, -273, -269, -266, -263, -259, -256, -253, - -250, -246, -243, -240, -236, -233, -230, -226, -223, -220, -216, - -213, -210, -206, -203, -200, -196, -193, -190, -186, -183, -180, - -176, -173, -170, -166, -163, -160, -157, -153, -150, -147, -143, - -140, -137, -133, -130, -127, -123, -120, -117, -113, -110, -107, - -103, -100, -97, -93, -90, -87, -83, -80, -77, -73, -70, - -67, -63, -60, -57, -54, -50, -47, -44, -40, -37, -34, - -30, -27, -24, -20, -17, -14, -10, -7, -4, 0, 3, - 6, 10, 13, 16, 20, 23, 26, 30, 33, 36, 39, - 43, 46, 49, 53, 56, 59, 63, 66, 69, 73, 76, - 79, 83, 86, 89, 93, 96, 99, 103, 106, 109, 113, - 116, 119, 123, 126, 129, 132, 136, 139, 142, 146, 149, - 152, 156, 159, 162, 166, 169, 172, 176, 179, 182, 186, - 189, 192, 196, 199, 202, 206, 209, 212, 216, 219, 222, - 226, 229, 232, 235, 239, 242, 245, 249, 252, 255, 259, - 262, 265, 269, 272, 275, 279, 282, 285, 289, 292, 295, - 299, 302, 305, 309, 312, 315, 319, 322, 325, 328, 332, - 335, 338, 342, 345, 348, 352, 355, 358, 362, 365, 368, - 372, 375, 378, 382, 385, 388, 392, 395, 398, 402, 405, - 408, 412, 415, 418, 422, 425, 428, 431, 435, 438, 441, - 445, 448, 451, 455, 458, 461, 465, 468, 471, 475, 478, - 481, 485, 488, 491, 495, 498, 501, 505, 508, 511, 515, - 518, 521, 524, 528, 531, 534, 538, 541, 544, 548, 551, - 554, 558, 561, 564, 568, 571, 574, 578, 581, 584, 588, - 591, 594, 598, 601, 604, 608, 611, 614, 617, 621, 624, - 627, 631, 634, 637, 641, 644, 647, 651, 654, 657, 661, - 664, 667, 671, 674, 677, 681, 684, 687, 691, 694, 697, - 701, 704, 707, 711, 714, 717, 720, 724, 727, 730, 734, - 737, 740, 744, 747, 750, 754, 757, 760, 764, 767, 770, - 774, 777, 780, 784, 787, 790, 794, 797, 800, 804, 807, - 810, 813, 817, 820, 823, 827, 830, 833, 837, 840, 843, - 847, 850, 853, 857, 860, 863, 867, 870, 873, 877, 880, - 883, 887, 890, 893, 897, 900, 903, 907, 910, 913, 916, - 920, 923, 926, 930, 933, 936, 940, 943, 946, 950, 953, - 956, 960, +const uint64_t kPower10MantissaLowTable[] = { + 0x113faa2906a13b3fU, 0x4ac7ca59a424c507U, 0x5d79bcf00d2df649U, + 0xf4d82c2c107973dcU, 0x79071b9b8a4be869U, 0x9748e2826cdee284U, + 0xfd1b1b2308169b25U, 0xfe30f0f5e50e20f7U, 0xbdbd2d335e51a935U, + 0xad2c788035e61382U, 0x4c3bcb5021afcc31U, 0xdf4abe242a1bbf3dU, + 0xd71d6dad34a2af0dU, 0x8672648c40e5ad68U, 0x680efdaf511f18c2U, + 0x0212bd1b2566def2U, 0x014bb630f7604b57U, 0x419ea3bd35385e2dU, + 0x52064cac828675b9U, 0x7343efebd1940993U, 0x1014ebe6c5f90bf8U, + 0xd41a26e077774ef6U, 0x8920b098955522b4U, 0x55b46e5f5d5535b0U, + 0xeb2189f734aa831dU, 0xa5e9ec7501d523e4U, 0x47b233c92125366eU, + 0x999ec0bb696e840aU, 0xc00670ea43ca250dU, 0x380406926a5e5728U, + 0xc605083704f5ecf2U, 0xf7864a44c633682eU, 0x7ab3ee6afbe0211dU, + 0x5960ea05bad82964U, 0x6fb92487298e33bdU, 0xa5d3b6d479f8e056U, + 0x8f48a4899877186cU, 0x331acdabfe94de87U, 0x9ff0c08b7f1d0b14U, + 0x07ecf0ae5ee44dd9U, 0xc9e82cd9f69d6150U, 0xbe311c083a225cd2U, + 0x6dbd630a48aaf406U, 0x092cbbccdad5b108U, 0x25bbf56008c58ea5U, + 0xaf2af2b80af6f24eU, 0x1af5af660db4aee1U, 0x50d98d9fc890ed4dU, + 0xe50ff107bab528a0U, 0x1e53ed49a96272c8U, 0x25e8e89c13bb0f7aU, + 0x77b191618c54e9acU, 0xd59df5b9ef6a2417U, 0x4b0573286b44ad1dU, + 0x4ee367f9430aec32U, 0x229c41f793cda73fU, 0x6b43527578c1110fU, + 0x830a13896b78aaa9U, 0x23cc986bc656d553U, 0x2cbfbe86b7ec8aa8U, + 0x7bf7d71432f3d6a9U, 0xdaf5ccd93fb0cc53U, 0xd1b3400f8f9cff68U, + 0x23100809b9c21fa1U, 0xabd40a0c2832a78aU, 0x16c90c8f323f516cU, + 0xae3da7d97f6792e3U, 0x99cd11cfdf41779cU, 0x40405643d711d583U, + 0x482835ea666b2572U, 0xda3243650005eecfU, 0x90bed43e40076a82U, + 0x5a7744a6e804a291U, 0x711515d0a205cb36U, 0x0d5a5b44ca873e03U, + 0xe858790afe9486c2U, 0x626e974dbe39a872U, 0xfb0a3d212dc8128fU, + 0x7ce66634bc9d0b99U, 0x1c1fffc1ebc44e80U, 0xa327ffb266b56220U, + 0x4bf1ff9f0062baa8U, 0x6f773fc3603db4a9U, 0xcb550fb4384d21d3U, + 0x7e2a53a146606a48U, 0x2eda7444cbfc426dU, 0xfa911155fefb5308U, + 0x793555ab7eba27caU, 0x4bc1558b2f3458deU, 0x9eb1aaedfb016f16U, + 0x465e15a979c1cadcU, 0x0bfacd89ec191ec9U, 0xcef980ec671f667bU, + 0x82b7e12780e7401aU, 0xd1b2ecb8b0908810U, 0x861fa7e6dcb4aa15U, + 0x67a791e093e1d49aU, 0xe0c8bb2c5c6d24e0U, 0x58fae9f773886e18U, + 0xaf39a475506a899eU, 0x6d8406c952429603U, 0xc8e5087ba6d33b83U, + 0xfb1e4a9a90880a64U, 0x5cf2eea09a55067fU, 0xf42faa48c0ea481eU, + 0xf13b94daf124da26U, 0x76c53d08d6b70858U, 0x54768c4b0c64ca6eU, + 0xa9942f5dcf7dfd09U, 0xd3f93b35435d7c4cU, 0xc47bc5014a1a6dafU, + 0x359ab6419ca1091bU, 0xc30163d203c94b62U, 0x79e0de63425dcf1dU, + 0x985915fc12f542e4U, 0x3e6f5b7b17b2939dU, 0xa705992ceecf9c42U, + 0x50c6ff782a838353U, 0xa4f8bf5635246428U, 0x871b7795e136be99U, + 0x28e2557b59846e3fU, 0x331aeada2fe589cfU, 0x3ff0d2c85def7621U, + 0x0fed077a756b53a9U, 0xd3e8495912c62894U, 0x64712dd7abbbd95cU, + 0xbd8d794d96aacfb3U, 0xecf0d7a0fc5583a0U, 0xf41686c49db57244U, + 0x311c2875c522ced5U, 0x7d633293366b828bU, 0xae5dff9c02033197U, + 0xd9f57f830283fdfcU, 0xd072df63c324fd7bU, 0x4247cb9e59f71e6dU, + 0x52d9be85f074e608U, 0x67902e276c921f8bU, 0x00ba1cd8a3db53b6U, + 0x80e8a40eccd228a4U, 0x6122cd128006b2cdU, 0x796b805720085f81U, + 0xcbe3303674053bb0U, 0xbedbfc4411068a9cU, 0xee92fb5515482d44U, + 0x751bdd152d4d1c4aU, 0xd262d45a78a0635dU, 0x86fb897116c87c34U, + 0xd45d35e6ae3d4da0U, 0x8974836059cca109U, 0x2bd1a438703fc94bU, + 0x7b6306a34627ddcfU, 0x1a3bc84c17b1d542U, 0x20caba5f1d9e4a93U, + 0x547eb47b7282ee9cU, 0xe99e619a4f23aa43U, 0x6405fa00e2ec94d4U, + 0xde83bc408dd3dd04U, 0x9624ab50b148d445U, 0x3badd624dd9b0957U, + 0xe54ca5d70a80e5d6U, 0x5e9fcf4ccd211f4cU, 0x7647c3200069671fU, + 0x29ecd9f40041e073U, 0xf468107100525890U, 0x7182148d4066eeb4U, + 0xc6f14cd848405530U, 0xb8ada00e5a506a7cU, 0xa6d90811f0e4851cU, + 0x908f4a166d1da663U, 0x9a598e4e043287feU, 0x40eff1e1853f29fdU, + 0xd12bee59e68ef47cU, 0x82bb74f8301958ceU, 0xe36a52363c1faf01U, + 0xdc44e6c3cb279ac1U, 0x29ab103a5ef8c0b9U, 0x7415d448f6b6f0e7U, + 0x111b495b3464ad21U, 0xcab10dd900beec34U, 0x3d5d514f40eea742U, + 0x0cb4a5a3112a5112U, 0x47f0e785eaba72abU, 0x59ed216765690f56U, + 0x306869c13ec3532cU, 0x1e414218c73a13fbU, 0xe5d1929ef90898faU, + 0xdf45f746b74abf39U, 0x6b8bba8c328eb783U, 0x066ea92f3f326564U, + 0xc80a537b0efefebdU, 0xbd06742ce95f5f36U, 0x2c48113823b73704U, + 0xf75a15862ca504c5U, 0x9a984d73dbe722fbU, 0xc13e60d0d2e0ebbaU, + 0x318df905079926a8U, 0xfdf17746497f7052U, 0xfeb6ea8bedefa633U, + 0xfe64a52ee96b8fc0U, 0x3dfdce7aa3c673b0U, 0x06bea10ca65c084eU, + 0x486e494fcff30a62U, 0x5a89dba3c3efccfaU, 0xf89629465a75e01cU, + 0xf6bbb397f1135823U, 0x746aa07ded582e2cU, 0xa8c2a44eb4571cdcU, + 0x92f34d62616ce413U, 0x77b020baf9c81d17U, 0x0ace1474dc1d122eU, + 0x0d819992132456baU, 0x10e1fff697ed6c69U, 0xca8d3ffa1ef463c1U, + 0xbd308ff8a6b17cb2U, 0xac7cb3f6d05ddbdeU, 0x6bcdf07a423aa96bU, + 0x86c16c98d2c953c6U, 0xe871c7bf077ba8b7U, 0x11471cd764ad4972U, + 0xd598e40d3dd89bcfU, 0x4aff1d108d4ec2c3U, 0xcedf722a585139baU, + 0xc2974eb4ee658828U, 0x733d226229feea32U, 0x0806357d5a3f525fU, + 0xca07c2dcb0cf26f7U, 0xfc89b393dd02f0b5U, 0xbbac2078d443ace2U, + 0xd54b944b84aa4c0dU, 0x0a9e795e65d4df11U, 0x4d4617b5ff4a16d5U, + 0x504bced1bf8e4e45U, 0xe45ec2862f71e1d6U, 0x5d767327bb4e5a4cU, + 0x3a6a07f8d510f86fU, 0x890489f70a55368bU, 0x2b45ac74ccea842eU, + 0x3b0b8bc90012929dU, 0x09ce6ebb40173744U, 0xcc420a6a101d0515U, + 0x9fa946824a12232dU, 0x47939822dc96abf9U, 0x59787e2b93bc56f7U, + 0x57eb4edb3c55b65aU, 0xede622920b6b23f1U, 0xe95fab368e45ecedU, + 0x11dbcb0218ebb414U, 0xd652bdc29f26a119U, 0x4be76d3346f0495fU, + 0x6f70a4400c562ddbU, 0xcb4ccd500f6bb952U, 0x7e2000a41346a7a7U, + 0x8ed400668c0c28c8U, 0x728900802f0f32faU, 0x4f2b40a03ad2ffb9U, + 0xe2f610c84987bfa8U, 0x0dd9ca7d2df4d7c9U, 0x91503d1c79720dbbU, + 0x75a44c6397ce912aU, 0xc986afbe3ee11abaU, 0xfbe85badce996168U, + 0xfae27299423fb9c3U, 0xdccd879fc967d41aU, 0x5400e987bbc1c920U, + 0x290123e9aab23b68U, 0xf9a0b6720aaf6521U, 0xf808e40e8d5b3e69U, + 0xb60b1d1230b20e04U, 0xb1c6f22b5e6f48c2U, 0x1e38aeb6360b1af3U, + 0x25c6da63c38de1b0U, 0x579c487e5a38ad0eU, 0x2d835a9df0c6d851U, + 0xf8e431456cf88e65U, 0x1b8e9ecb641b58ffU, 0xe272467e3d222f3fU, + 0x5b0ed81dcc6abb0fU, 0x98e947129fc2b4e9U, 0x3f2398d747b36224U, + 0x8eec7f0d19a03aadU, 0x1953cf68300424acU, 0x5fa8c3423c052dd7U, + 0x3792f412cb06794dU, 0xe2bbd88bbee40bd0U, 0x5b6aceaeae9d0ec4U, + 0xf245825a5a445275U, 0xeed6e2f0f0d56712U, 0x55464dd69685606bU, + 0xaa97e14c3c26b886U, 0xd53dd99f4b3066a8U, 0xe546a8038efe4029U, + 0xde98520472bdd033U, 0x963e66858f6d4440U, 0xdde7001379a44aa8U, + 0x5560c018580d5d52U, 0xaab8f01e6e10b4a6U, 0xcab3961304ca70e8U, + 0x3d607b97c5fd0d22U, 0x8cb89a7db77c506aU, 0x77f3608e92adb242U, + 0x55f038b237591ed3U, 0x6b6c46dec52f6688U, 0x2323ac4b3b3da015U, + 0xabec975e0a0d081aU, 0x96e7bd358c904a21U, 0x7e50d64177da2e54U, + 0xdde50bd1d5d0b9e9U, 0x955e4ec64b44e864U, 0xbd5af13bef0b113eU, + 0xecb1ad8aeacdd58eU, 0x67de18eda5814af2U, 0x80eacf948770ced7U, + 0xa1258379a94d028dU, 0x096ee45813a04330U, 0x8bca9d6e188853fcU, + 0x775ea264cf55347dU, 0x95364afe032a819dU, 0x3a83ddbd83f52204U, + 0xc4926a9672793542U, 0x75b7053c0f178293U, 0x5324c68b12dd6338U, + 0xd3f6fc16ebca5e03U, 0x88f4bb1ca6bcf584U, 0x2b31e9e3d06c32e5U, + 0x3aff322e62439fcfU, 0x09befeb9fad487c2U, 0x4c2ebe687989a9b3U, + 0x0f9d37014bf60a10U, 0x538484c19ef38c94U, 0x2865a5f206b06fb9U, + 0xf93f87b7442e45d3U, 0xf78f69a51539d748U, 0xb573440e5a884d1bU, + 0x31680a88f8953030U, 0xfdc20d2b36ba7c3dU, 0x3d32907604691b4cU, + 0xa63f9a49c2c1b10fU, 0x0fcf80dc33721d53U, 0xd3c36113404ea4a8U, + 0x645a1cac083126e9U, 0x3d70a3d70a3d70a3U, 0xccccccccccccccccU, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x0000000000000000U, 0x0000000000000000U, + 0x0000000000000000U, 0x4000000000000000U, 0x5000000000000000U, + 0xa400000000000000U, 0x4d00000000000000U, 0xf020000000000000U, + 0x6c28000000000000U, 0xc732000000000000U, 0x3c7f400000000000U, + 0x4b9f100000000000U, 0x1e86d40000000000U, 0x1314448000000000U, + 0x17d955a000000000U, 0x5dcfab0800000000U, 0x5aa1cae500000000U, + 0xf14a3d9e40000000U, 0x6d9ccd05d0000000U, 0xe4820023a2000000U, + 0xdda2802c8a800000U, 0xd50b2037ad200000U, 0x4526f422cc340000U, + 0x9670b12b7f410000U, 0x3c0cdd765f114000U, 0xa5880a69fb6ac800U, + 0x8eea0d047a457a00U, 0x72a4904598d6d880U, 0x47a6da2b7f864750U, + 0x999090b65f67d924U, 0xfff4b4e3f741cf6dU, 0xbff8f10e7a8921a4U, + 0xaff72d52192b6a0dU, 0x9bf4f8a69f764490U, 0x02f236d04753d5b4U, + 0x01d762422c946590U, 0x424d3ad2b7b97ef5U, 0xd2e0898765a7deb2U, + 0x63cc55f49f88eb2fU, 0x3cbf6b71c76b25fbU, 0x8bef464e3945ef7aU, + 0x97758bf0e3cbb5acU, 0x3d52eeed1cbea317U, 0x4ca7aaa863ee4bddU, + 0x8fe8caa93e74ef6aU, 0xb3e2fd538e122b44U, 0x60dbbca87196b616U, + 0xbc8955e946fe31cdU, 0x6babab6398bdbe41U, 0xc696963c7eed2dd1U, + 0xfc1e1de5cf543ca2U, 0x3b25a55f43294bcbU, 0x49ef0eb713f39ebeU, + 0x6e3569326c784337U, 0x49c2c37f07965404U, 0xdc33745ec97be906U, + 0x69a028bb3ded71a3U, 0xc40832ea0d68ce0cU, 0xf50a3fa490c30190U, + 0x792667c6da79e0faU, 0x577001b891185938U, 0xed4c0226b55e6f86U, + 0x544f8158315b05b4U, 0x696361ae3db1c721U, 0x03bc3a19cd1e38e9U, + 0x04ab48a04065c723U, 0x62eb0d64283f9c76U, 0x3ba5d0bd324f8394U, + 0xca8f44ec7ee36479U, 0x7e998b13cf4e1ecbU, 0x9e3fedd8c321a67eU, + 0xc5cfe94ef3ea101eU, 0xbba1f1d158724a12U, 0x2a8a6e45ae8edc97U, + 0xf52d09d71a3293bdU, 0x593c2626705f9c56U, 0x6f8b2fb00c77836cU, + 0x0b6dfb9c0f956447U, 0x4724bd4189bd5eacU, 0x58edec91ec2cb657U, + 0x2f2967b66737e3edU, 0xbd79e0d20082ee74U, 0xecd8590680a3aa11U, + 0xe80e6f4820cc9495U, 0x3109058d147fdcddU, 0xbd4b46f0599fd415U, + 0x6c9e18ac7007c91aU, 0x03e2cf6bc604ddb0U, 0x84db8346b786151cU, + 0xe612641865679a63U, 0x4fcb7e8f3f60c07eU, 0xe3be5e330f38f09dU, + 0x5cadf5bfd3072cc5U, 0x73d9732fc7c8f7f6U, 0x2867e7fddcdd9afaU, + 0xb281e1fd541501b8U, 0x1f225a7ca91a4226U, 0x3375788de9b06958U, + 0x0052d6b1641c83aeU, 0xc0678c5dbd23a49aU, 0xf840b7ba963646e0U, + 0xb650e5a93bc3d898U, 0xa3e51f138ab4cebeU, 0xc66f336c36b10137U, + 0xb80b0047445d4184U, 0xa60dc059157491e5U, 0x87c89837ad68db2fU, + 0x29babe4598c311fbU, 0xf4296dd6fef3d67aU, 0x1899e4a65f58660cU, + 0x5ec05dcff72e7f8fU, 0x76707543f4fa1f73U, 0x6a06494a791c53a8U, + 0x0487db9d17636892U, 0x45a9d2845d3c42b6U, 0x0b8a2392ba45a9b2U, + 0x8e6cac7768d7141eU, 0x3207d795430cd926U, 0x7f44e6bd49e807b8U, + 0x5f16206c9c6209a6U, 0x36dba887c37a8c0fU, 0xc2494954da2c9789U, + 0xf2db9baa10b7bd6cU, 0x6f92829494e5acc7U, 0xcb772339ba1f17f9U, + 0xff2a760414536efbU, 0xfef5138519684abaU, 0x7eb258665fc25d69U, + 0xef2f773ffbd97a61U, 0xaafb550ffacfd8faU, 0x95ba2a53f983cf38U, + 0xdd945a747bf26183U, 0x94f971119aeef9e4U, 0x7a37cd5601aab85dU, + 0xac62e055c10ab33aU, 0x577b986b314d6009U, 0xed5a7e85fda0b80bU, + 0x14588f13be847307U, 0x596eb2d8ae258fc8U, 0x6fca5f8ed9aef3bbU, + 0x25de7bb9480d5854U, 0xaf561aa79a10ae6aU, 0x1b2ba1518094da04U, + 0x90fb44d2f05d0842U, 0x353a1607ac744a53U, 0x42889b8997915ce8U, + 0x69956135febada11U, 0x43fab9837e699095U, 0x94f967e45e03f4bbU, + 0x1d1be0eebac278f5U, 0x6462d92a69731732U, 0x7d7b8f7503cfdcfeU, + 0x5cda735244c3d43eU, 0x3a0888136afa64a7U, 0x088aaa1845b8fdd0U, + 0x8aad549e57273d45U, 0x36ac54e2f678864bU, 0x84576a1bb416a7ddU, + 0x656d44a2a11c51d5U, 0x9f644ae5a4b1b325U, 0x873d5d9f0dde1feeU, + 0xa90cb506d155a7eaU, 0x09a7f12442d588f2U, 0x0c11ed6d538aeb2fU, + 0x8f1668c8a86da5faU, 0xf96e017d694487bcU, 0x37c981dcc395a9acU, + 0x85bbe253f47b1417U, 0x93956d7478ccec8eU, 0x387ac8d1970027b2U, + 0x06997b05fcc0319eU, 0x441fece3bdf81f03U, 0xd527e81cad7626c3U, + 0x8a71e223d8d3b074U, 0xf6872d5667844e49U, 0xb428f8ac016561dbU, + 0xe13336d701beba52U, 0xecc0024661173473U, 0x27f002d7f95d0190U, + 0x31ec038df7b441f4U, 0x7e67047175a15271U, 0x0f0062c6e984d386U, + 0x52c07b78a3e60868U, 0xa7709a56ccdf8a82U, 0x88a66076400bb691U, + 0x6acff893d00ea435U, 0x0583f6b8c4124d43U, 0xc3727a337a8b704aU, + 0x744f18c0592e4c5cU, 0x1162def06f79df73U, 0x8addcb5645ac2ba8U, + 0x6d953e2bd7173692U, 0xc8fa8db6ccdd0437U, 0x1d9c9892400a22a2U, + 0x2503beb6d00cab4bU, 0x2e44ae64840fd61dU, 0x5ceaecfed289e5d2U, + 0x7425a83e872c5f47U, 0xd12f124e28f77719U, 0x82bd6b70d99aaa6fU, + 0x636cc64d1001550bU, 0x3c47f7e05401aa4eU, 0x65acfaec34810a71U, + 0x7f1839a741a14d0dU, 0x1ede48111209a050U, 0x934aed0aab460432U, + 0xf81da84d5617853fU, 0x36251260ab9d668eU, 0xc1d72b7c6b426019U, + 0xb24cf65b8612f81fU, 0xdee033f26797b627U, 0x169840ef017da3b1U, + 0x8e1f289560ee864eU, 0xf1a6f2bab92a27e2U, 0xae10af696774b1dbU, + 0xacca6da1e0a8ef29U, 0x17fd090a58d32af3U, 0xddfc4b4cef07f5b0U, + 0x4abdaf101564f98eU, 0x9d6d1ad41abe37f1U, 0x84c86189216dc5edU, + 0x32fd3cf5b4e49bb4U, 0x3fbc8c33221dc2a1U, 0x0fabaf3feaa5334aU, + 0x29cb4d87f2a7400eU, 0x743e20e9ef511012U, 0x914da9246b255416U, + 0x1ad089b6c2f7548eU, 0xa184ac2473b529b1U, 0xc9e5d72d90a2741eU, + 0x7e2fa67c7a658892U, 0xddbb901b98feeab7U, 0x552a74227f3ea565U, + 0xd53a88958f87275fU, 0x8a892abaf368f137U, 0x2d2b7569b0432d85U, + 0x9c3b29620e29fc73U, 0x8349f3ba91b47b8fU, 0x241c70a936219a73U, + 0xed238cd383aa0110U, 0xf4363804324a40aaU, 0xb143c6053edcd0d5U, + 0xdd94b7868e94050aU, 0xca7cf2b4191c8326U, 0xfd1c2f611f63a3f0U, + 0xbc633b39673c8cecU, 0xd5be0503e085d813U, 0x4b2d8644d8a74e18U, + 0xddf8e7d60ed1219eU, 0xcabb90e5c942b503U, 0x3d6a751f3b936243U, + 0x0cc512670a783ad4U, 0x27fb2b80668b24c5U, 0xb1f9f660802dedf6U, + 0x5e7873f8a0396973U, 0xdb0b487b6423e1e8U, 0x91ce1a9a3d2cda62U, + 0x7641a140cc7810fbU, 0xa9e904c87fcb0a9dU, 0x546345fa9fbdcd44U, + 0xa97c177947ad4095U, 0x49ed8eabcccc485dU, 0x5c68f256bfff5a74U, + 0x73832eec6fff3111U, 0xc831fd53c5ff7eabU, 0xba3e7ca8b77f5e55U, + 0x28ce1bd2e55f35ebU, 0x7980d163cf5b81b3U, 0xd7e105bcc332621fU, + 0x8dd9472bf3fefaa7U, 0xb14f98f6f0feb951U, 0x6ed1bf9a569f33d3U, + 0x0a862f80ec4700c8U, 0xcd27bb612758c0faU, 0x8038d51cb897789cU, + 0xe0470a63e6bd56c3U, 0x1858ccfce06cac74U, 0x0f37801e0c43ebc8U, + 0xd30560258f54e6baU, 0x47c6b82ef32a2069U, 0x4cdc331d57fa5441U, + 0xe0133fe4adf8e952U, 0x58180fddd97723a6U, 0x570f09eaa7ea7648U, }; } // namespace diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord.cc index 8559893616..78aebe711f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord.cc @@ -20,6 +20,7 @@ #include <cstdio> #include <cstdlib> #include <iomanip> +#include <ios> #include <iostream> #include <limits> #include <ostream> @@ -34,6 +35,7 @@ #include "y_absl/base/port.h" #include "y_absl/container/fixed_array.h" #include "y_absl/container/inlined_vector.h" +#include "y_absl/crc/internal/crc_cord_state.h" #include "y_absl/strings/cord_buffer.h" #include "y_absl/strings/escaping.h" #include "y_absl/strings/internal/cord_data_edge.h" @@ -166,9 +168,7 @@ constexpr unsigned char Cord::InlineRep::kMaxInline; inline void Cord::InlineRep::set_data(const char* data, size_t n) { static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15"); - - cord_internal::SmallMemmove<true>(data_.as_chars(), data, n); - set_inline_size(n); + data_.set_inline_data(data, n); } inline char* Cord::InlineRep::set_data(size_t n) { @@ -184,7 +184,7 @@ inline void Cord::InlineRep::reduce_size(size_t n) { assert(tag >= n); tag -= n; memset(data_.as_chars() + tag, 0, n); - set_inline_size(static_cast<char>(tag)); + set_inline_size(tag); } inline void Cord::InlineRep::remove_prefix(size_t n) { @@ -419,6 +419,7 @@ Cord& Cord::operator=(y_absl::string_view src) { // we keep it here to make diffs easier. void Cord::InlineRep::AppendArray(y_absl::string_view src, MethodIdentifier method) { + MaybeRemoveEmptyCrcNode(); if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined. size_t appended = 0; @@ -436,8 +437,8 @@ void Cord::InlineRep::AppendArray(y_absl::string_view src, size_t inline_length = inline_size(); if (src.size() <= kMaxInline - inline_length) { // Append new data to embedded array - memcpy(data_.as_chars() + inline_length, src.data(), src.size()); set_inline_size(inline_length + src.size()); + memcpy(data_.as_chars() + inline_length, src.data(), src.size()); return; } @@ -478,6 +479,10 @@ inline CordRep* Cord::TakeRep() && { template <typename C> inline void Cord::AppendImpl(C&& src) { auto constexpr method = CordzUpdateTracker::kAppendCord; + + contents_.MaybeRemoveEmptyCrcNode(); + if (src.empty()) return; + if (empty()) { // Since destination is empty, we can avoid allocating a node, if (src.contents_.is_tree()) { @@ -537,18 +542,23 @@ static CordRep::ExtractResult ExtractAppendBuffer(CordRep* rep, } } -static CordBuffer CreateAppendBuffer(InlineData& data, size_t capacity) { +static CordBuffer CreateAppendBuffer(InlineData& data, size_t block_size, + size_t capacity) { // Watch out for overflow, people can ask for size_t::max(). const size_t size = data.inline_size(); - capacity = (std::min)(std::numeric_limits<size_t>::max() - size, capacity); - CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(size + capacity); + const size_t max_capacity = std::numeric_limits<size_t>::max() - size; + capacity = (std::min)(max_capacity, capacity) + size; + CordBuffer buffer = + block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity) + : CordBuffer::CreateWithDefaultLimit(capacity); cord_internal::SmallMemmove(buffer.data(), data.as_chars(), size); buffer.SetLength(size); data = {}; return buffer; } -CordBuffer Cord::GetAppendBufferSlowPath(size_t capacity, size_t min_capacity) { +CordBuffer Cord::GetAppendBufferSlowPath(size_t block_size, size_t capacity, + size_t min_capacity) { auto constexpr method = CordzUpdateTracker::kGetAppendBuffer; CordRep* tree = contents_.tree(); if (tree != nullptr) { @@ -558,9 +568,10 @@ CordBuffer Cord::GetAppendBufferSlowPath(size_t capacity, size_t min_capacity) { contents_.SetTreeOrEmpty(result.tree, scope); return CordBuffer(result.extracted->flat()); } - return CordBuffer::CreateWithDefaultLimit(capacity); + return block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity) + : CordBuffer::CreateWithDefaultLimit(capacity); } - return CreateAppendBuffer(contents_.data_, capacity); + return CreateAppendBuffer(contents_.data_, block_size, capacity); } void Cord::Append(const Cord& src) { @@ -584,6 +595,9 @@ void Cord::Append(T&& src) { template void Cord::Append(TString&& src); void Cord::Prepend(const Cord& src) { + contents_.MaybeRemoveEmptyCrcNode(); + if (src.empty()) return; + CordRep* src_tree = src.contents_.tree(); if (src_tree != nullptr) { CordRep::Ref(src_tree); @@ -598,16 +612,18 @@ void Cord::Prepend(const Cord& src) { } void Cord::PrependArray(y_absl::string_view src, MethodIdentifier method) { + contents_.MaybeRemoveEmptyCrcNode(); if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined. + if (!contents_.is_tree()) { size_t cur_size = contents_.inline_size(); if (cur_size + src.size() <= InlineRep::kMaxInline) { // Use embedded storage. - char data[InlineRep::kMaxInline + 1] = {0}; - memcpy(data, src.data(), src.size()); - memcpy(data + src.size(), contents_.data(), cur_size); - memcpy(contents_.data_.as_chars(), data, InlineRep::kMaxInline + 1); - contents_.set_inline_size(cur_size + src.size()); + InlineData data; + data.set_inline_size(cur_size + src.size()); + memcpy(data.as_chars(), src.data(), src.size()); + memcpy(data.as_chars() + src.size(), contents_.data(), cur_size); + contents_.data_ = data; return; } } @@ -620,8 +636,8 @@ void Cord::AppendPrecise(y_absl::string_view src, MethodIdentifier method) { assert(src.size() <= cord_internal::kMaxFlatLength); if (contents_.remaining_inline_capacity() >= src.size()) { const size_t inline_length = contents_.inline_size(); - memcpy(contents_.data_.as_chars() + inline_length, src.data(), src.size()); contents_.set_inline_size(inline_length + src.size()); + memcpy(contents_.data_.as_chars() + inline_length, src.data(), src.size()); } else { contents_.AppendTree(CordRepFlat::Create(src), method); } @@ -631,12 +647,12 @@ void Cord::PrependPrecise(y_absl::string_view src, MethodIdentifier method) { assert(!src.empty()); assert(src.size() <= cord_internal::kMaxFlatLength); if (contents_.remaining_inline_capacity() >= src.size()) { - const size_t inline_length = contents_.inline_size(); - char data[InlineRep::kMaxInline + 1] = {0}; - memcpy(data, src.data(), src.size()); - memcpy(data + src.size(), contents_.data(), inline_length); - memcpy(contents_.data_.as_chars(), data, InlineRep::kMaxInline + 1); - contents_.set_inline_size(inline_length + src.size()); + const size_t cur_size = contents_.inline_size(); + InlineData data; + data.set_inline_size(cur_size + src.size()); + memcpy(data.as_chars(), src.data(), src.size()); + memcpy(data.as_chars() + src.size(), contents_.data(), cur_size); + contents_.data_ = data; } else { contents_.PrependTree(CordRepFlat::Create(src), method); } @@ -658,6 +674,7 @@ void Cord::RemovePrefix(size_t n) { Y_ABSL_INTERNAL_CHECK(n <= size(), y_absl::StrCat("Requested prefix size ", n, " exceeds Cord's size ", size())); + contents_.MaybeRemoveEmptyCrcNode(); CordRep* tree = contents_.tree(); if (tree == nullptr) { contents_.remove_prefix(n); @@ -688,6 +705,7 @@ void Cord::RemoveSuffix(size_t n) { Y_ABSL_INTERNAL_CHECK(n <= size(), y_absl::StrCat("Requested suffix size ", n, " exceeds Cord's size ", size())); + contents_.MaybeRemoveEmptyCrcNode(); CordRep* tree = contents_.tree(); if (tree == nullptr) { contents_.reduce_size(n); @@ -726,6 +744,7 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { } if (new_size <= InlineRep::kMaxInline) { + sub_cord.contents_.set_inline_size(new_size); char* dest = sub_cord.contents_.data_.as_chars(); Cord::ChunkIterator it = chunk_begin(); it.AdvanceBytes(pos); @@ -737,7 +756,6 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { ++it; } cord_internal::SmallMemmove(dest, it->data(), remaining_size); - sub_cord.contents_.set_inline_size(new_size); return sub_cord; } @@ -835,26 +853,44 @@ inline y_absl::string_view Cord::InlineRep::FindFlatStartPiece() const { return y_absl::string_view(node->external()->base + offset, length); } -void Cord::SetExpectedChecksum(uint32_t crc) { +void Cord::SetCrcCordState(crc_internal::CrcCordState state) { auto constexpr method = CordzUpdateTracker::kSetExpectedChecksum; - if (empty()) return; - - if (!contents_.is_tree()) { + if (empty()) { + contents_.MaybeRemoveEmptyCrcNode(); + CordRep* rep = CordRepCrc::New(nullptr, std::move(state)); + contents_.EmplaceTree(rep, method); + } else if (!contents_.is_tree()) { CordRep* rep = contents_.MakeFlatWithExtraCapacity(0); - rep = CordRepCrc::New(rep, crc); + rep = CordRepCrc::New(rep, std::move(state)); contents_.EmplaceTree(rep, method); } else { const CordzUpdateScope scope(contents_.data_.cordz_info(), method); - CordRep* rep = CordRepCrc::New(contents_.data_.as_tree(), crc); + CordRep* rep = CordRepCrc::New(contents_.data_.as_tree(), std::move(state)); contents_.SetTree(rep, scope); } } +void Cord::SetExpectedChecksum(uint32_t crc) { + // Construct a CrcCordState with a single chunk. + crc_internal::CrcCordState state; + state.mutable_rep()->prefix_crc.push_back( + crc_internal::CrcCordState::PrefixCrc(size(), y_absl::crc32c_t{crc})); + SetCrcCordState(std::move(state)); +} + +const crc_internal::CrcCordState* Cord::MaybeGetCrcCordState() const { + if (!contents_.is_tree() || !contents_.tree()->IsCrc()) { + return nullptr; + } + return &contents_.tree()->crc()->crc_cord_state; +} + y_absl::optional<uint32_t> Cord::ExpectedChecksum() const { if (!contents_.is_tree() || !contents_.tree()->IsCrc()) { return y_absl::nullopt; } - return contents_.tree()->crc()->crc; + return static_cast<uint32_t>( + contents_.tree()->crc()->crc_cord_state.Checksum()); } inline int Cord::CompareSlowPath(y_absl::string_view rhs, size_t compared_size, @@ -922,6 +958,7 @@ inline int Cord::CompareSlowPath(const Cord& rhs, size_t compared_size, } inline y_absl::string_view Cord::GetFirstChunk(const Cord& c) { + if (c.empty()) return {}; return c.contents_.FindFlatStartPiece(); } inline y_absl::string_view Cord::GetFirstChunk(y_absl::string_view sv) { @@ -1092,7 +1129,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { : current_leaf_; const char* data = payload->IsExternal() ? payload->external()->base : payload->flat()->Data(); - const size_t offset = current_chunk_.data() - data; + const size_t offset = static_cast<size_t>(current_chunk_.data() - data); auto* tree = CordRepSubstring::Substring(payload, offset, n); subcord.contents_.EmplaceTree(VerifyTree(tree), method); @@ -1159,6 +1196,10 @@ y_absl::string_view Cord::FlattenSlowPath() { /* static */ bool Cord::GetFlatAux(CordRep* rep, y_absl::string_view* fragment) { assert(rep != nullptr); + if (rep->length == 0) { + *fragment = y_absl::string_view(); + return true; + } rep = cord_internal::SkipCrcNode(rep); if (rep->IsFlat()) { *fragment = y_absl::string_view(rep->flat()->Data(), rep->length); @@ -1190,6 +1231,7 @@ y_absl::string_view Cord::FlattenSlowPath() { y_absl::cord_internal::CordRep* rep, y_absl::FunctionRef<void(y_absl::string_view)> callback) { assert(rep != nullptr); + if (rep->length == 0) return; rep = cord_internal::SkipCrcNode(rep); if (rep->IsBtree()) { @@ -1223,8 +1265,12 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, if (include_data) *os << static_cast<void*>(rep); *os << "]"; *os << " " << std::setw(indent) << ""; - if (rep->IsCrc()) { - *os << "CRC crc=" << rep->crc()->crc << "\n"; + bool leaf = false; + if (rep == nullptr) { + *os << "NULL\n"; + leaf = true; + } else if (rep->IsCrc()) { + *os << "CRC crc=" << rep->crc()->crc_cord_state.Checksum() << "\n"; indent += kIndentStep; rep = rep->crc()->child; } else if (rep->IsSubstring()) { @@ -1232,6 +1278,7 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, indent += kIndentStep; rep = rep->substring()->child; } else { // Leaf or ring + leaf = true; if (rep->IsExternal()) { *os << "EXTERNAL ["; if (include_data) @@ -1245,6 +1292,8 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, } else { CordRepBtree::Dump(rep, /*label=*/ "", include_data, *os); } + } + if (leaf) { if (stack.empty()) break; rep = stack.back(); stack.pop_back(); @@ -1290,11 +1339,14 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, node->substring()->child->length, ReportError(root, node)); } else if (node->IsCrc()) { - Y_ABSL_INTERNAL_CHECK(node->crc()->child != nullptr, - ReportError(root, node)); - Y_ABSL_INTERNAL_CHECK(node->crc()->length == node->crc()->child->length, - ReportError(root, node)); - worklist.push_back(node->crc()->child); + Y_ABSL_INTERNAL_CHECK( + node->crc()->child != nullptr || node->crc()->length == 0, + ReportError(root, node)); + if (node->crc()->child != nullptr) { + Y_ABSL_INTERNAL_CHECK(node->crc()->length == node->crc()->child->length, + ReportError(root, node)); + worklist.push_back(node->crc()->child); + } } } while (!worklist.empty()); return true; @@ -1302,7 +1354,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, std::ostream& operator<<(std::ostream& out, const Cord& cord) { for (y_absl::string_view chunk : cord.Chunks()) { - out.write(chunk.data(), chunk.size()); + out.write(chunk.data(), static_cast<std::streamsize>(chunk.size())); } return out; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord.h index a30c7b2a19..872188a89a 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord.h @@ -20,8 +20,7 @@ // structure. A Cord is a string-like sequence of characters optimized for // specific use cases. Unlike a `TString`, which stores an array of // contiguous characters, Cord data is stored in a structure consisting of -// separate, reference-counted "chunks." (Currently, this implementation is a -// tree structure, though that implementation may change.) +// separate, reference-counted "chunks." // // Because a Cord consists of these chunks, data can be added to or removed from // a Cord during its lifetime. Chunks may also be shared between Cords. Unlike a @@ -77,6 +76,7 @@ #include "y_absl/base/macros.h" #include "y_absl/base/port.h" #include "y_absl/container/inlined_vector.h" +#include "y_absl/crc/internal/crc_cord_state.h" #include "y_absl/functional/function_ref.h" #include "y_absl/meta/type_traits.h" #include "y_absl/strings/cord_analysis.h" @@ -284,6 +284,19 @@ class Cord { // } CordBuffer GetAppendBuffer(size_t capacity, size_t min_capacity = 16); + // Returns a CordBuffer, re-using potential existing capacity in this cord. + // + // This function is identical to `GetAppendBuffer`, except that in the case + // where a new `CordBuffer` is allocated, it is allocated using the provided + // custom limit instead of the default limit. `GetAppendBuffer` will default + // to `CordBuffer::CreateWithDefaultLimit(capacity)` whereas this method + // will default to `CordBuffer::CreateWithCustomLimit(block_size, capacity)`. + // This method is equivalent to `GetAppendBuffer` if `block_size` is zero. + // See the documentation for `CreateWithCustomLimit` for more details on the + // restrictions and legal values for `block_size`. + CordBuffer GetCustomAppendBuffer(size_t block_size, size_t capacity, + size_t min_capacity = 16); + // Cord::Prepend() // // Prepends data to the Cord, which may come from another Cord or other string @@ -802,7 +815,7 @@ class Cord { InlineRep& operator=(const InlineRep& src); InlineRep& operator=(InlineRep&& src) noexcept; - explicit constexpr InlineRep(cord_internal::InlineData data); + explicit constexpr InlineRep(y_absl::string_view sv, CordRep* rep); void Swap(InlineRep* rhs); bool empty() const; @@ -861,33 +874,14 @@ class Cord { void PrependTreeToTree(CordRep* tree, MethodIdentifier method); void PrependTree(CordRep* tree, MethodIdentifier method); - template <bool has_length> - void GetAppendRegion(char** region, size_t* size, size_t length); + bool IsSame(const InlineRep& other) const { return data_ == other.data_; } - 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 aliasing issues. - memcpy(&x, &data_, sizeof(x)); - memcpy(&y, &other.data_, sizeof(y)); - if (x == y) { - memcpy(&x, reinterpret_cast<const char*>(&data_) + 8, sizeof(x)); - memcpy(&y, reinterpret_cast<const char*>(&other.data_) + 8, sizeof(y)); - if (x == y) return 0; - } - return y_absl::big_endian::FromHost64(x) < y_absl::big_endian::FromHost64(y) - ? -1 - : 1; - } void CopyTo(TString* dst) const { // memcpy is much faster when operating on a known size. On most supported // platforms, the small string optimization is large enough that resizing // to 15 bytes does not cause a memory allocation. - y_absl::strings_internal::STLStringResizeUninitialized(dst, - sizeof(data_) - 1); - memcpy(&(*dst)[0], &data_, sizeof(data_) - 1); + y_absl::strings_internal::STLStringResizeUninitialized(dst, kMaxInline); + data_.copy_max_inline_to(&(*dst)[0]); // erase is faster than resize because the logic for memory allocation is // not needed. dst->erase(inline_size()); @@ -932,6 +926,13 @@ class Cord { void set_inline_size(size_t size) { data_.set_inline_size(size); } size_t inline_size() const { return data_.inline_size(); } + // Empty cords that carry a checksum have a CordRepCrc node with a null + // child node. The code can avoid lots of special cases where it would + // otherwise transition from tree to inline storage if we just remove the + // CordRepCrc node before mutations. Must never be called inside a + // CordzUpdateScope since it untracks the cordz info. + void MaybeRemoveEmptyCrcNode(); + cord_internal::InlineData data_; }; InlineRep contents_; @@ -980,7 +981,8 @@ class Cord { void AppendPrecise(y_absl::string_view src, MethodIdentifier method); void PrependPrecise(y_absl::string_view src, MethodIdentifier method); - CordBuffer GetAppendBufferSlowPath(size_t capacity, size_t min_capacity); + CordBuffer GetAppendBufferSlowPath(size_t block_size, size_t capacity, + size_t min_capacity); // Prepends the provided data to this instance. `method` contains the public // API method for this action which is tracked for Cordz sampling purposes. @@ -1000,6 +1002,10 @@ class Cord { }); return H::combine(combiner.finalize(std::move(hash_state)), size()); } + + friend class CrcCord; + void SetCrcCordState(crc_internal::CrcCordState state); + const crc_internal::CrcCordState* MaybeGetCrcCordState() const; }; Y_ABSL_NAMESPACE_END @@ -1016,46 +1022,6 @@ extern std::ostream& operator<<(std::ostream& out, const Cord& cord); 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. -template <bool nullify_tail = false> -inline void SmallMemmove(char* dst, const char* src, size_t n) { - 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); - } - } -} - // Does non-template-specific `CordRepExternal` initialization. // Requires `data` to be non-empty. void InitializeCordRepExternal(y_absl::string_view data, CordRepExternal* rep); @@ -1099,8 +1065,8 @@ Cord MakeCordFromExternal(y_absl::string_view data, Releaser&& releaser) { return cord; } -constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data) - : data_(data) {} +constexpr Cord::InlineRep::InlineRep(y_absl::string_view sv, CordRep* rep) + : data_(sv, rep) {} inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) : data_(InlineData::kDefaultInit) { @@ -1179,7 +1145,7 @@ inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity( size_t len = data_.inline_size(); auto* result = CordRepFlat::New(len + extra); result->length = len; - memcpy(result->Data(), data_.as_chars(), sizeof(data_)); + data_.copy_max_inline_to(result->Data()); return result; } @@ -1241,6 +1207,18 @@ inline void Cord::InlineRep::CopyToArray(char* dst) const { cord_internal::SmallMemmove(dst, data_.as_chars(), n); } +inline void Cord::InlineRep::MaybeRemoveEmptyCrcNode() { + CordRep* rep = tree(); + if (rep == nullptr || Y_ABSL_PREDICT_TRUE(rep->length > 0)) { + return; + } + assert(rep->IsCrc()); + assert(rep->crc()->child == nullptr); + CordzInfo::MaybeUntrackCord(cordz_info()); + CordRep::Unref(rep); + ResetToEmpty(); +} + constexpr inline Cord::Cord() noexcept {} inline Cord::Cord(y_absl::string_view src) @@ -1248,13 +1226,12 @@ inline Cord::Cord(y_absl::string_view src) template <typename T> constexpr Cord::Cord(strings_internal::StringConstant<T>) - : contents_(strings_internal::StringConstant<T>::value.size() <= + : contents_(strings_internal::StringConstant<T>::value, + strings_internal::StringConstant<T>::value.size() <= cord_internal::kMaxInline - ? cord_internal::InlineData( - strings_internal::StringConstant<T>::value) - : cord_internal::InlineData( - &cord_internal::ConstInitExternalStorage< - strings_internal::StringConstant<T>>::value)) {} + ? nullptr + : &cord_internal::ConstInitExternalStorage< + strings_internal::StringConstant<T>>::value) {} inline Cord& Cord::operator=(const Cord& x) { contents_ = x.contents_; @@ -1290,7 +1267,7 @@ inline size_t Cord::size() const { return contents_.size(); } -inline bool Cord::empty() const { return contents_.empty(); } +inline bool Cord::empty() const { return size() == 0; } inline size_t Cord::EstimatedMemoryUsage( CordMemoryAccounting accounting_method) const { @@ -1360,7 +1337,17 @@ inline void Cord::Prepend(CordBuffer buffer) { inline CordBuffer Cord::GetAppendBuffer(size_t capacity, size_t min_capacity) { if (empty()) return CordBuffer::CreateWithDefaultLimit(capacity); - return GetAppendBufferSlowPath(capacity, min_capacity); + return GetAppendBufferSlowPath(0, capacity, min_capacity); +} + +inline CordBuffer Cord::GetCustomAppendBuffer(size_t block_size, + size_t capacity, + size_t min_capacity) { + if (empty()) { + return block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity) + : CordBuffer::CreateWithDefaultLimit(capacity); + } + return GetAppendBufferSlowPath(block_size, capacity, min_capacity); } extern template void Cord::Append(TString&& src); @@ -1368,7 +1355,7 @@ extern template void Cord::Prepend(TString&& src); inline int Cord::Compare(const Cord& rhs) const { if (!contents_.is_tree() && !rhs.contents_.is_tree()) { - return contents_.BitwiseCompare(rhs.contents_); + return contents_.data_.Compare(rhs.contents_.data_); } return CompareImpl(rhs); @@ -1406,7 +1393,11 @@ inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree) { inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) { if (CordRep* tree = cord->contents_.tree()) { bytes_remaining_ = tree->length; - InitTree(tree); + if (Y_ABSL_PREDICT_TRUE(bytes_remaining_ != 0)) { + InitTree(tree); + } else { + current_chunk_ = {}; + } } else { bytes_remaining_ = cord->contents_.inline_size(); current_chunk_ = {cord->contents_.data(), bytes_remaining_}; @@ -1575,7 +1566,7 @@ inline void Cord::ForEachChunk( if (rep == nullptr) { callback(y_absl::string_view(contents_.data(), contents_.size())); } else { - return ForEachChunkAux(rep, callback); + ForEachChunkAux(rep, callback); } } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord_buffer.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord_buffer.h index cbe4287b07..1199ed0f3d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord_buffer.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/cord_buffer.h @@ -330,8 +330,7 @@ class CordBuffer { // Returns the available area of the internal SSO data y_absl::Span<char> short_available() { - assert(is_short()); - const size_t length = (short_rep.raw_size >> 1); + const size_t length = short_length(); return y_absl::Span<char>(short_rep.data + length, kInlineCapacity - length); } @@ -347,7 +346,7 @@ class CordBuffer { // Returns the length of the internal SSO data. size_t short_length() const { assert(is_short()); - return short_rep.raw_size >> 1; + return static_cast<size_t>(short_rep.raw_size >> 1); } // Sets the length of the internal SSO data. @@ -412,8 +411,12 @@ class CordBuffer { // Power2 functions static bool IsPow2(size_t size) { return y_absl::has_single_bit(size); } - static size_t Log2Floor(size_t size) { return y_absl::bit_width(size) - 1; } - static size_t Log2Ceil(size_t size) { return y_absl::bit_width(size - 1); } + static size_t Log2Floor(size_t size) { + return static_cast<size_t>(y_absl::bit_width(size) - 1); + } + static size_t Log2Ceil(size_t size) { + return static_cast<size_t>(y_absl::bit_width(size - 1)); + } // Implementation of `CreateWithCustomLimit()`. // This implementation allows for future memory allocation hints to diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/escaping.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/escaping.cc index cd05355fa7..ce90d54fad 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/escaping.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/escaping.cc @@ -42,11 +42,11 @@ constexpr bool kUnescapeNulls = false; inline bool is_octal_digit(char c) { return ('0' <= c) && (c <= '7'); } -inline int hex_digit_to_int(char c) { +inline unsigned int hex_digit_to_int(char c) { static_assert('0' == 0x30 && 'A' == 0x41 && 'a' == 0x61, "Character set must be ASCII."); - assert(y_absl::ascii_isxdigit(c)); - int x = static_cast<unsigned char>(c); + assert(y_absl::ascii_isxdigit(static_cast<unsigned char>(c))); + unsigned int x = static_cast<unsigned char>(c); if (x > '9') { x += 9; } @@ -121,27 +121,29 @@ bool CUnescapeInternal(y_absl::string_view source, bool leave_nulls_escaped, case '7': { // octal digit: 1 to 3 digits const char* octal_start = p; - unsigned int ch = *p - '0'; - if (p < last_byte && is_octal_digit(p[1])) ch = ch * 8 + *++p - '0'; + unsigned int ch = static_cast<unsigned int>(*p - '0'); // digit 1 if (p < last_byte && is_octal_digit(p[1])) - ch = ch * 8 + *++p - '0'; // now points at last digit + ch = ch * 8 + static_cast<unsigned int>(*++p - '0'); // digit 2 + if (p < last_byte && is_octal_digit(p[1])) + ch = ch * 8 + static_cast<unsigned int>(*++p - '0'); // digit 3 if (ch > 0xff) { if (error) { *error = "Value of \\" + - TString(octal_start, p + 1 - octal_start) + + TString(octal_start, + static_cast<size_t>(p + 1 - octal_start)) + " exceeds 0xff"; } return false; } if ((ch == 0) && leave_nulls_escaped) { // Copy the escape sequence for the null character - const ptrdiff_t octal_size = p + 1 - octal_start; + const size_t octal_size = static_cast<size_t>(p + 1 - octal_start); *d++ = '\\'; memmove(d, octal_start, octal_size); d += octal_size; break; } - *d++ = ch; + *d++ = static_cast<char>(ch); break; } case 'x': @@ -149,32 +151,34 @@ bool CUnescapeInternal(y_absl::string_view source, bool leave_nulls_escaped, if (p >= last_byte) { if (error) *error = "String cannot end with \\x"; return false; - } else if (!y_absl::ascii_isxdigit(p[1])) { + } else if (!y_absl::ascii_isxdigit(static_cast<unsigned char>(p[1]))) { if (error) *error = "\\x cannot be followed by a non-hex digit"; return false; } unsigned int ch = 0; const char* hex_start = p; - while (p < last_byte && y_absl::ascii_isxdigit(p[1])) + while (p < last_byte && + y_absl::ascii_isxdigit(static_cast<unsigned char>(p[1]))) // Arbitrarily many hex digits ch = (ch << 4) + hex_digit_to_int(*++p); if (ch > 0xFF) { if (error) { *error = "Value of \\" + - TString(hex_start, p + 1 - hex_start) + + TString(hex_start, + static_cast<size_t>(p + 1 - hex_start)) + " exceeds 0xff"; } return false; } if ((ch == 0) && leave_nulls_escaped) { // Copy the escape sequence for the null character - const ptrdiff_t hex_size = p + 1 - hex_start; + const size_t hex_size = static_cast<size_t>(p + 1 - hex_start); *d++ = '\\'; memmove(d, hex_start, hex_size); d += hex_size; break; } - *d++ = ch; + *d++ = static_cast<char>(ch); break; } case 'u': { @@ -184,18 +188,20 @@ bool CUnescapeInternal(y_absl::string_view source, bool leave_nulls_escaped, if (p + 4 >= end) { if (error) { *error = "\\u must be followed by 4 hex digits: \\" + - TString(hex_start, p + 1 - hex_start); + TString(hex_start, + static_cast<size_t>(p + 1 - hex_start)); } return false; } for (int i = 0; i < 4; ++i) { // Look one char ahead. - if (y_absl::ascii_isxdigit(p[1])) { + if (y_absl::ascii_isxdigit(static_cast<unsigned char>(p[1]))) { rune = (rune << 4) + hex_digit_to_int(*++p); // Advance p. } else { if (error) { *error = "\\u must be followed by 4 hex digits: \\" + - TString(hex_start, p + 1 - hex_start); + TString(hex_start, + static_cast<size_t>(p + 1 - hex_start)); } return false; } @@ -220,20 +226,22 @@ bool CUnescapeInternal(y_absl::string_view source, bool leave_nulls_escaped, if (p + 8 >= end) { if (error) { *error = "\\U must be followed by 8 hex digits: \\" + - TString(hex_start, p + 1 - hex_start); + TString(hex_start, + static_cast<size_t>(p + 1 - hex_start)); } return false; } for (int i = 0; i < 8; ++i) { // Look one char ahead. - if (y_absl::ascii_isxdigit(p[1])) { + if (y_absl::ascii_isxdigit(static_cast<unsigned char>(p[1]))) { // Don't change rune until we're sure this // is within the Unicode limit, but do advance p. uint32_t newrune = (rune << 4) + hex_digit_to_int(*++p); if (newrune > 0x10FFFF) { if (error) { *error = "Value of \\" + - TString(hex_start, p + 1 - hex_start) + + TString(hex_start, + static_cast<size_t>(p + 1 - hex_start)) + " exceeds Unicode limit (0x10FFFF)"; } return false; @@ -243,7 +251,8 @@ bool CUnescapeInternal(y_absl::string_view source, bool leave_nulls_escaped, } else { if (error) { *error = "\\U must be followed by 8 hex digits: \\" + - TString(hex_start, p + 1 - hex_start); + TString(hex_start, + static_cast<size_t>(p + 1 - hex_start)); } return false; } @@ -291,7 +300,7 @@ bool CUnescapeInternal(y_absl::string_view source, bool leave_nulls_escaped, error)) { return false; } - dest->erase(dest_size); + dest->erase(static_cast<size_t>(dest_size)); return true; } @@ -311,7 +320,7 @@ TString CEscapeInternal(y_absl::string_view src, bool use_hex, TString dest; bool last_hex_escape = false; // true if last output char was \xNN. - for (unsigned char c : src) { + for (char c : src) { bool is_hex_escape = false; switch (c) { case '\n': dest.append("\\" "n"); break; @@ -320,28 +329,30 @@ TString CEscapeInternal(y_absl::string_view src, bool use_hex, case '\"': dest.append("\\" "\""); break; case '\'': dest.append("\\" "'"); break; case '\\': dest.append("\\" "\\"); break; - default: + default: { // Note that if we emit \xNN and the src character after that is a hex // digit then that digit must be escaped too to prevent it being // interpreted as part of the character code by C. - if ((!utf8_safe || c < 0x80) && - (!y_absl::ascii_isprint(c) || - (last_hex_escape && y_absl::ascii_isxdigit(c)))) { + const unsigned char uc = static_cast<unsigned char>(c); + if ((!utf8_safe || uc < 0x80) && + (!y_absl::ascii_isprint(uc) || + (last_hex_escape && y_absl::ascii_isxdigit(uc)))) { if (use_hex) { dest.append("\\" "x"); - dest.push_back(numbers_internal::kHexChar[c / 16]); - dest.push_back(numbers_internal::kHexChar[c % 16]); + dest.push_back(numbers_internal::kHexChar[uc / 16]); + dest.push_back(numbers_internal::kHexChar[uc % 16]); is_hex_escape = true; } else { dest.append("\\"); - 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]); + dest.push_back(numbers_internal::kHexChar[uc / 64]); + dest.push_back(numbers_internal::kHexChar[(uc % 64) / 8]); + dest.push_back(numbers_internal::kHexChar[uc % 8]); } } else { dest.push_back(c); break; } + } } last_hex_escape = is_hex_escape; } @@ -350,7 +361,7 @@ TString CEscapeInternal(y_absl::string_view src, bool use_hex, } /* clang-format off */ -constexpr char c_escaped_len[256] = { +constexpr unsigned char c_escaped_len[256] = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4, // \t, \n, \r 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // ", ' @@ -375,7 +386,8 @@ constexpr char c_escaped_len[256] = { // that UTF-8 bytes are not handled specially. inline size_t CEscapedLength(y_absl::string_view src) { size_t escaped_len = 0; - for (unsigned char c : src) escaped_len += c_escaped_len[c]; + for (char c : src) + escaped_len += c_escaped_len[static_cast<unsigned char>(c)]; return escaped_len; } @@ -391,8 +403,8 @@ void CEscapeAndAppendInternal(y_absl::string_view src, TString* dest) { cur_dest_len + escaped_len); char* append_ptr = &(*dest)[cur_dest_len]; - for (unsigned char c : src) { - int char_len = c_escaped_len[c]; + for (char c : src) { + size_t char_len = c_escaped_len[static_cast<unsigned char>(c)]; if (char_len == 1) { *append_ptr++ = c; } else if (char_len == 2) { @@ -424,9 +436,9 @@ void CEscapeAndAppendInternal(y_absl::string_view src, TString* dest) { } } else { *append_ptr++ = '\\'; - *append_ptr++ = '0' + c / 64; - *append_ptr++ = '0' + (c % 64) / 8; - *append_ptr++ = '0' + c % 8; + *append_ptr++ = '0' + static_cast<unsigned char>(c) / 64; + *append_ptr++ = '0' + (static_cast<unsigned char>(c) % 64) / 8; + *append_ptr++ = '0' + static_cast<unsigned char>(c) % 8; } } } @@ -440,7 +452,7 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, size_t destidx = 0; int decode = 0; int state = 0; - unsigned int ch = 0; + unsigned char ch = 0; unsigned int temp = 0; // If "char" is signed by default, using *src as an array index results in @@ -500,13 +512,13 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, // how to handle those cases. GET_INPUT(first, 4); - temp = decode; + temp = static_cast<unsigned char>(decode); GET_INPUT(second, 3); - temp = (temp << 6) | decode; + temp = (temp << 6) | static_cast<unsigned char>(decode); GET_INPUT(third, 2); - temp = (temp << 6) | decode; + temp = (temp << 6) | static_cast<unsigned char>(decode); GET_INPUT(fourth, 1); - temp = (temp << 6) | decode; + temp = (temp << 6) | static_cast<unsigned char>(decode); } else { // We really did have four good data bytes, so advance four // characters in the string. @@ -518,11 +530,11 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, // temp has 24 bits of input, so write that out as three bytes. if (destidx + 3 > szdest) return false; - dest[destidx + 2] = temp; + dest[destidx + 2] = static_cast<char>(temp); temp >>= 8; - dest[destidx + 1] = temp; + dest[destidx + 1] = static_cast<char>(temp); temp >>= 8; - dest[destidx] = temp; + dest[destidx] = static_cast<char>(temp); destidx += 3; } } else { @@ -583,18 +595,18 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, } // Each input character gives us six bits of output. - temp = (temp << 6) | decode; + temp = (temp << 6) | static_cast<unsigned char>(decode); ++state; if (state == 4) { // If we've accumulated 24 bits of output, write that out as // three bytes. if (dest) { if (destidx + 3 > szdest) return false; - dest[destidx + 2] = temp; + dest[destidx + 2] = static_cast<char>(temp); temp >>= 8; - dest[destidx + 1] = temp; + dest[destidx + 1] = static_cast<char>(temp); temp >>= 8; - dest[destidx] = temp; + dest[destidx] = static_cast<char>(temp); } destidx += 3; state = 0; @@ -619,7 +631,7 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, if (dest) { if (destidx + 1 > szdest) return false; temp >>= 4; - dest[destidx] = temp; + dest[destidx] = static_cast<char>(temp); } ++destidx; expected_equals = 2; @@ -630,9 +642,9 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, if (dest) { if (destidx + 2 > szdest) return false; temp >>= 2; - dest[destidx + 1] = temp; + dest[destidx + 1] = static_cast<char>(temp); temp >>= 8; - dest[destidx] = temp; + dest[destidx] = static_cast<char>(temp); } destidx += 2; expected_equals = 1; @@ -772,8 +784,7 @@ template <typename String> bool Base64UnescapeInternal(const char* src, size_t slen, String* dest, const signed char* unbase64) { // Determine the size of the output string. Base64 encodes every 3 bytes into - // 4 characters. any leftover chars are added directly for good measure. - // This is documented in the base64 RFC: http://tools.ietf.org/html/rfc3548 + // 4 characters. Any leftover chars are added directly for good measure. const size_t dest_len = 3 * (slen / 4) + (slen % 4); strings_internal::STLStringResizeUninitialized(dest, dest_len); @@ -821,9 +832,9 @@ constexpr char kHexValueLenient[256] = { // or a string. This works because we use the [] operator to access // individual characters at a time. template <typename T> -void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) { - for (int i = 0; i < num; i++) { - to[i] = (kHexValueLenient[from[i * 2] & 0xFF] << 4) + +void HexStringToBytesInternal(const char* from, T to, size_t num) { + for (size_t i = 0; i < num; i++) { + to[i] = static_cast<char>(kHexValueLenient[from[i * 2] & 0xFF] << 4) + (kHexValueLenient[from[i * 2 + 1] & 0xFF]); } } @@ -831,7 +842,7 @@ void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) { // This is a templated function so that T can be either a char* or a // TString. template <typename T> -void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) { +void BytesToHexStringInternal(const unsigned char* src, T dest, size_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 = &numbers_internal::kHexTable[*src_ptr * 2]; @@ -876,8 +887,8 @@ TString Utf8SafeCHexEscape(y_absl::string_view src) { // WebSafeBase64Escape() - Google's variation of base64 encoder // // Check out -// http://tools.ietf.org/html/rfc2045 for formal description, but what we -// care about is that... +// https://datatracker.ietf.org/doc/html/rfc2045 for formal description, but +// what we care about is that... // Take the encoded stuff in groups of 4 characters and turn each // character into a code 0 to 63 thus: // A-Z map to 0 to 25 diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/escaping.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/escaping.h index dde4f4f3ce..ab83ba5f97 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/escaping.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/escaping.h @@ -117,35 +117,40 @@ TString Utf8SafeCEscape(y_absl::string_view src); // conversion. TString Utf8SafeCHexEscape(y_absl::string_view src); -// Base64Unescape() -// -// Converts a `src` string encoded in Base64 to its binary equivalent, writing -// it to a `dest` buffer, returning `true` on success. If `src` contains invalid -// characters, `dest` is cleared and returns `false`. -bool Base64Unescape(y_absl::string_view src, TString* dest); - -// WebSafeBase64Unescape() -// -// Converts a `src` string encoded in Base64 to its binary equivalent, writing -// it to a `dest` buffer, but using '-' instead of '+', and '_' instead of '/'. -// If `src` contains invalid characters, `dest` is cleared and returns `false`. -bool WebSafeBase64Unescape(y_absl::string_view src, TString* dest); - // Base64Escape() // -// Encodes a `src` string into a base64-encoded string, with padding characters. -// This function conforms with RFC 4648 section 4 (base64). +// Encodes a `src` string into a base64-encoded 'dest' string with padding +// characters. This function conforms with RFC 4648 section 4 (base64) and RFC +// 2045. See also CalculateBase64EscapedLen(). void Base64Escape(y_absl::string_view src, TString* dest); TString Base64Escape(y_absl::string_view src); // WebSafeBase64Escape() // -// Encodes a `src` string into a base64-like string, using '-' instead of '+' -// and '_' instead of '/', and without padding. This function conforms with RFC -// 4648 section 5 (base64url). +// Encodes a `src` string into a base64 string, like Base64Escape() does, but +// outputs '-' instead of '+' and '_' instead of '/', and does not pad 'dest'. +// This function conforms with RFC 4648 section 5 (base64url). void WebSafeBase64Escape(y_absl::string_view src, TString* dest); TString WebSafeBase64Escape(y_absl::string_view src); +// Base64Unescape() +// +// Converts a `src` string encoded in Base64 (RFC 4648 section 4) to its binary +// equivalent, writing it to a `dest` buffer, returning `true` on success. If +// `src` contains invalid characters, `dest` is cleared and returns `false`. +// If padding is included (note that `Base64Escape()` does produce it), it must +// be correct. In the padding, '=' and '.' are treated identically. +bool Base64Unescape(y_absl::string_view src, TString* dest); + +// WebSafeBase64Unescape() +// +// Converts a `src` string encoded in "web safe" Base64 (RFC 4648 section 5) to +// its binary equivalent, writing it to a `dest` buffer. If `src` contains +// invalid characters, `dest` is cleared and returns `false`. If padding is +// included (note that `WebSafeBase64Escape()` does not produce it), it must be +// correct. In the padding, '=' and '.' are treated identically. +bool WebSafeBase64Unescape(y_absl::string_view src, TString* dest); + // HexStringToBytes() // // Converts an ASCII hex string into bytes, returning binary data of length diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/char_map.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/char_map.h index cee2e75f0d..ef761ab03d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/char_map.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/char_map.h @@ -73,10 +73,10 @@ class Charmap { } // Containing all the chars in the C-string 's'. - // Note that this is expensively recursive because of the C++11 constexpr - // formulation. Use only in constexpr initializers. static constexpr Charmap FromString(const char* s) { - return *s == 0 ? Charmap() : (Char(*s) | FromString(s + 1)); + Charmap ret; + while (*s) ret = ret | Char(*s++); + return ret; } // Containing all the chars in the closed interval [lo,hi]. @@ -103,10 +103,9 @@ class Charmap { constexpr Charmap(uint64_t b0, uint64_t b1, uint64_t b2, uint64_t b3) : m_{b0, b1, b2, b3} {} - static constexpr uint64_t RangeForWord(unsigned char lo, unsigned char hi, - uint64_t word) { - return OpenRangeFromZeroForWord(hi + 1, word) & - ~OpenRangeFromZeroForWord(lo, word); + static constexpr uint64_t RangeForWord(char lo, char hi, uint64_t word) { + return OpenRangeFromZeroForWord(static_cast<unsigned char>(hi) + 1, word) & + ~OpenRangeFromZeroForWord(static_cast<unsigned char>(lo), word); } // All the chars in the specified word of the range [0, upper). @@ -119,13 +118,16 @@ class Charmap { : (~static_cast<uint64_t>(0) >> (64 - upper % 64)); } - static constexpr uint64_t CharMaskForWord(unsigned char x, uint64_t word) { - return (x / 64 == word) ? (static_cast<uint64_t>(1) << (x % 64)) : 0; + static constexpr uint64_t CharMaskForWord(char x, uint64_t word) { + const auto unsigned_x = static_cast<unsigned char>(x); + return (unsigned_x / 64 == word) + ? (static_cast<uint64_t>(1) << (unsigned_x % 64)) + : 0; } - private: - void SetChar(unsigned char c) { - m_[c / 64] |= static_cast<uint64_t>(1) << (c % 64); + void SetChar(char c) { + const auto unsigned_c = static_cast<unsigned char>(c); + m_[unsigned_c / 64] |= static_cast<uint64_t>(1) << (unsigned_c % 64); } uint64_t m_[4]; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_bigint.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_bigint.cc index 8bb5dc0d5c..924bea66e3 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_bigint.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_bigint.cc @@ -242,7 +242,7 @@ int BigUnsigned<max_words>::ReadDigits(const char* begin, const char* end, // decimal exponent to compensate. --exponent_adjust; } - int digit = (*begin - '0'); + char digit = (*begin - '0'); --significant_digits; if (significant_digits == 0 && std::next(begin) != end && (digit == 0 || digit == 5)) { @@ -255,7 +255,7 @@ int BigUnsigned<max_words>::ReadDigits(const char* begin, const char* end, // 500000...000000000001 to correctly round up, rather than to nearest. ++digit; } - queued = 10 * queued + digit; + queued = 10 * queued + static_cast<uint32_t>(digit); ++digits_queued; if (digits_queued == kMaxSmallPowerOfTen) { MultiplyBy(kTenToNth[kMaxSmallPowerOfTen]); @@ -341,8 +341,8 @@ TString BigUnsigned<max_words>::ToString() const { TString result; // Build result in reverse order while (copy.size() > 0) { - int next_digit = copy.DivMod<10>(); - result.push_back('0' + next_digit); + uint32_t next_digit = copy.DivMod<10>(); + result.push_back('0' + static_cast<char>(next_digit)); } if (result.empty()) { result.push_back('0'); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_parse.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_parse.cc index 6bfe3c7e5c..d6a9881abd 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_parse.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_parse.cc @@ -190,11 +190,11 @@ bool IsDigit<16>(char ch) { template <> unsigned ToDigit<10>(char ch) { - return ch - '0'; + return static_cast<unsigned>(ch - '0'); } template <> unsigned ToDigit<16>(char ch) { - return kAsciiToInt[static_cast<unsigned char>(ch)]; + return static_cast<unsigned>(kAsciiToInt[static_cast<unsigned char>(ch)]); } template <> diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_internal.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_internal.h index 87201b7fb0..c1f12f1428 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_internal.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_internal.h @@ -27,9 +27,20 @@ #include "y_absl/base/internal/invoke.h" #include "y_absl/base/optimization.h" #include "y_absl/container/internal/compressed_tuple.h" +#include "y_absl/container/internal/container_memory.h" #include "y_absl/meta/type_traits.h" #include "y_absl/strings/string_view.h" +// We can only add poisoning if we can detect consteval executions. +#if defined(Y_ABSL_HAVE_CONSTANT_EVALUATED) && \ + (defined(Y_ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(Y_ABSL_HAVE_MEMORY_SANITIZER)) +#define Y_ABSL_INTERNAL_CORD_HAVE_SANITIZER 1 +#endif + +#define Y_ABSL_CORD_INTERNAL_NO_SANITIZE \ + Y_ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS Y_ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY + namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace cord_internal { @@ -91,6 +102,46 @@ enum Constants { // Emits a fatal error "Unexpected node type: xyz" and aborts the program. Y_ABSL_ATTRIBUTE_NORETURN void LogFatalNodeType(CordRep* rep); +// 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 15 bytes. +template <bool nullify_tail = false> +inline void SmallMemmove(char* dst, const char* src, size_t n) { + if (n >= 8) { + assert(n <= 15); + uint64_t buf1; + uint64_t buf2; + memcpy(&buf1, src, 8); + memcpy(&buf2, src + n - 8, 8); + if (nullify_tail) { + memset(dst + 7, 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 + 7, 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 + 7, 0, 8); + memset(dst + n, 0, 8); + } + } +} + // Compact class for tracking the reference count and state flags for CordRep // instances. Data is stored in an atomic int32_t for compactness and speed. class RefcountAndFlags { @@ -129,8 +180,9 @@ class RefcountAndFlags { } // Returns the current reference count using acquire semantics. - inline int32_t Get() const { - return count_.load(std::memory_order_acquire) >> kNumFlags; + inline size_t Get() const { + return static_cast<size_t>(count_.load(std::memory_order_acquire) >> + kNumFlags); } // Returns whether the atomic integer is 1. @@ -224,7 +276,11 @@ struct CordRep { : length(l), refcount(immortal), tag(EXTERNAL), storage{} {} // The following three fields have to be less than 32 bytes since - // that is the smallest supported flat node size. + // that is the smallest supported flat node size. Some code optimizations rely + // on the specific layout of these fields. Notably: the non-trivial field + // `refcount` being preceded by `length`, and being tailed by POD data + // members only. + // # LINT.IfChange size_t length; RefcountAndFlags refcount; // If tag < FLAT, it represents CordRepKind and indicates the type of node. @@ -240,6 +296,7 @@ struct CordRep { // allocate room for these in the derived class, as not all compilers reuse // padding space from the base class (clang and gcc do, MSVC does not, etc) uint8_t storage[3]; + // # LINT.ThenChange(cord_rep_btree.h:copy_raw) // Returns true if this instance's tag matches the requested type. constexpr bool IsRing() const { return tag == RING; } @@ -422,25 +479,25 @@ constexpr char GetOrNull(y_absl::string_view data, size_t pos) { return pos < data.size() ? data[pos] : '\0'; } -// We store cordz_info as 64 bit pointer value in big endian format. This -// guarantees that the least significant byte of cordz_info matches the last -// byte of the inline data representation in as_chars_, which holds the inlined +// We store cordz_info as 64 bit pointer value in little endian format. This +// guarantees that the least significant byte of cordz_info matches the first +// byte of the inline data representation in `data`, which holds the inlined // size or the 'is_tree' bit. using cordz_info_t = int64_t; // Assert that the `cordz_info` pointer value perfectly overlaps the last half -// of `as_chars_` and can hold a pointer value. +// of `data` and can hold a pointer value. static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, ""); static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), ""); -// BigEndianByte() creates a big endian representation of 'value', i.e.: a big -// endian value where the last byte in the host's representation holds 'value`, -// with all other bytes being 0. -static constexpr cordz_info_t BigEndianByte(unsigned char value) { +// LittleEndianByte() creates a little endian representation of 'value', i.e.: +// a little endian value where the first byte in the host's representation +// holds 'value`, with all other bytes being 0. +static constexpr cordz_info_t LittleEndianByte(unsigned char value) { #if defined(Y_ABSL_IS_BIG_ENDIAN) - return value; -#else return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8); +#else + return value; #endif } @@ -449,38 +506,80 @@ class InlineData { // DefaultInitType forces the use of the default initialization constructor. enum DefaultInitType { kDefaultInit }; - // kNullCordzInfo holds the big endian representation of intptr_t(1) + // kNullCordzInfo holds the little endian representation of intptr_t(1) // This is the 'null' / initial value of 'cordz_info'. The null value // is specifically big endian 1 as with 64-bit pointers, the last // byte of cordz_info overlaps with the last byte holding the tag. - static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1); - - constexpr InlineData() : as_chars_{0} {} - explicit InlineData(DefaultInitType) {} - explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {} - explicit constexpr InlineData(y_absl::string_view chars) - : as_chars_{ - GetOrNull(chars, 0), GetOrNull(chars, 1), - GetOrNull(chars, 2), GetOrNull(chars, 3), - GetOrNull(chars, 4), GetOrNull(chars, 5), - GetOrNull(chars, 6), GetOrNull(chars, 7), - GetOrNull(chars, 8), GetOrNull(chars, 9), - GetOrNull(chars, 10), GetOrNull(chars, 11), - GetOrNull(chars, 12), GetOrNull(chars, 13), - GetOrNull(chars, 14), static_cast<char>((chars.size() << 1))} {} + static constexpr cordz_info_t kNullCordzInfo = LittleEndianByte(1); + + // kTagOffset contains the offset of the control byte / tag. This constant is + // intended mostly for debugging purposes: do not remove this constant as it + // is actively inspected and used by gdb pretty printing code. + static constexpr size_t kTagOffset = 0; + + // Implement `~InlineData()` conditionally: we only need this destructor to + // unpoison poisoned instances under *SAN, and it will only compile correctly + // if the current compiler supports `y_absl::is_constant_evaluated()`. +#ifdef Y_ABSL_INTERNAL_CORD_HAVE_SANITIZER + ~InlineData() noexcept { unpoison(); } +#endif + + constexpr InlineData() noexcept { poison_this(); } + + explicit InlineData(DefaultInitType) noexcept : rep_(kDefaultInit) { + poison_this(); + } + + explicit InlineData(CordRep* rep) noexcept : rep_(rep) { + Y_ABSL_ASSERT(rep != nullptr); + } + + // Explicit constexpr constructor to create a constexpr InlineData + // value. Creates an inlined SSO value if `rep` is null, otherwise + // creates a tree instance value. + constexpr InlineData(y_absl::string_view sv, CordRep* rep) noexcept + : rep_(rep ? Rep(rep) : Rep(sv)) { + poison(); + } + + constexpr InlineData(const InlineData& rhs) noexcept; + InlineData& operator=(const InlineData& rhs) noexcept; + + friend bool operator==(const InlineData& lhs, const InlineData& rhs) { +#ifdef Y_ABSL_INTERNAL_CORD_HAVE_SANITIZER + const Rep l = lhs.rep_.SanitizerSafeCopy(); + const Rep r = rhs.rep_.SanitizerSafeCopy(); + return memcmp(&l, &r, sizeof(l)) == 0; +#else + return memcmp(&lhs, &rhs, sizeof(lhs)) == 0; +#endif + } + friend bool operator!=(const InlineData& lhs, const InlineData& rhs) { + return !operator==(lhs, rhs); + } + + // Poisons the unused inlined SSO data if the current instance + // is inlined, else un-poisons the entire instance. + constexpr void poison(); + + // Un-poisons this instance. + constexpr void unpoison(); + + // Poisons the current instance. This is used on default initialization. + constexpr void poison_this(); // Returns true if the current instance is empty. // The 'empty value' is an inlined data value of zero length. - bool is_empty() const { return tag() == 0; } + bool is_empty() const { return rep_.tag() == 0; } // Returns true if the current instance holds a tree value. - bool is_tree() const { return (tag() & 1) != 0; } + bool is_tree() const { return (rep_.tag() & 1) != 0; } // Returns true if the current instance holds a cordz_info value. // Requires the current instance to hold a tree value. bool is_profiled() const { assert(is_tree()); - return as_tree_.cordz_info != kNullCordzInfo; + return rep_.cordz_info() != kNullCordzInfo; } // Returns true if either of the provided instances hold a cordz_info value. @@ -489,7 +588,7 @@ class InlineData { static bool is_either_profiled(const InlineData& data1, const InlineData& data2) { assert(data1.is_tree() && data2.is_tree()); - return (data1.as_tree_.cordz_info | data2.as_tree_.cordz_info) != + return (data1.rep_.cordz_info() | data2.rep_.cordz_info()) != kNullCordzInfo; } @@ -498,8 +597,8 @@ class InlineData { // Requires the current instance to hold a tree value. CordzInfo* cordz_info() const { assert(is_tree()); - intptr_t info = static_cast<intptr_t>( - y_absl::big_endian::ToHost64(static_cast<uint64_t>(as_tree_.cordz_info))); + intptr_t info = static_cast<intptr_t>(y_absl::little_endian::ToHost64( + static_cast<uint64_t>(rep_.cordz_info()))); assert(info & 1); return reinterpret_cast<CordzInfo*>(info - 1); } @@ -510,21 +609,21 @@ class InlineData { void set_cordz_info(CordzInfo* cordz_info) { assert(is_tree()); uintptr_t info = reinterpret_cast<uintptr_t>(cordz_info) | 1; - as_tree_.cordz_info = - static_cast<cordz_info_t>(y_absl::big_endian::FromHost64(info)); + rep_.set_cordz_info( + static_cast<cordz_info_t>(y_absl::little_endian::FromHost64(info))); } // Resets the current cordz_info to null / empty. void clear_cordz_info() { assert(is_tree()); - as_tree_.cordz_info = kNullCordzInfo; + rep_.set_cordz_info(kNullCordzInfo); } // Returns a read only pointer to the character data inside this instance. // Requires the current instance to hold inline data. const char* as_chars() const { assert(!is_tree()); - return as_chars_; + return rep_.as_chars(); } // Returns a mutable pointer to the character data inside this instance. @@ -542,20 +641,33 @@ class InlineData { // // It's an error to read from the returned pointer without a preceding write // if the current instance does not hold inline data, i.e.: is_tree() == true. - char* as_chars() { return as_chars_; } + char* as_chars() { return rep_.as_chars(); } // Returns the tree value of this value. // Requires the current instance to hold a tree value. CordRep* as_tree() const { assert(is_tree()); - return as_tree_.rep; + return rep_.tree(); + } + + void set_inline_data(const char* data, size_t n) { + Y_ABSL_ASSERT(n <= kMaxInline); + unpoison(); + rep_.set_tag(static_cast<int8_t>(n << 1)); + SmallMemmove<true>(rep_.as_chars(), data, n); + poison(); + } + + void copy_max_inline_to(char* dst) const { + assert(!is_tree()); + memcpy(dst, rep_.SanitizerSafeCopy().as_chars(), kMaxInline); } // Initialize this instance to holding the tree value `rep`, // initializing the cordz_info to null, i.e.: 'not profiled'. void make_tree(CordRep* rep) { - as_tree_.rep = rep; - as_tree_.cordz_info = kNullCordzInfo; + unpoison(); + rep_.make_tree(rep); } // Set the tree value of this instance to 'rep`. @@ -563,54 +675,202 @@ class InlineData { // Does not affect the value of cordz_info. void set_tree(CordRep* rep) { assert(is_tree()); - as_tree_.rep = rep; + rep_.set_tree(rep); } // Returns the size of the inlined character data inside this instance. // Requires the current instance to hold inline data. - size_t inline_size() const { - assert(!is_tree()); - return tag() >> 1; - } + size_t inline_size() const { return rep_.inline_size(); } // Sets the size of the inlined character data inside this instance. // Requires `size` to be <= kMaxInline. // See the documentation on 'as_chars()' for more information and examples. void set_inline_size(size_t size) { - Y_ABSL_ASSERT(size <= kMaxInline); - tag() = static_cast<char>(size << 1); + unpoison(); + rep_.set_inline_size(size); + poison(); + } + + // Compares 'this' inlined data with rhs. The comparison is a straightforward + // lexicographic comparison. `Compare()` returns values as follows: + // + // -1 'this' InlineData instance is smaller + // 0 the InlineData instances are equal + // 1 'this' InlineData instance larger + int Compare(const InlineData& rhs) const { + return Compare(rep_.SanitizerSafeCopy(), rhs.rep_.SanitizerSafeCopy()); } private: - // See cordz_info_t for forced alignment and size of `cordz_info` details. - struct AsTree { - explicit constexpr AsTree(y_absl::cord_internal::CordRep* tree) - : rep(tree), cordz_info(kNullCordzInfo) {} - // This union uses up extra space so that whether rep is 32 or 64 bits, - // cordz_info will still start at the eighth byte, and the last - // byte of cordz_info will still be the last byte of InlineData. - union { + struct Rep { + // See cordz_info_t for forced alignment and size of `cordz_info` details. + struct AsTree { + explicit constexpr AsTree(y_absl::cord_internal::CordRep* tree) + : rep(tree) {} + cordz_info_t cordz_info = kNullCordzInfo; y_absl::cord_internal::CordRep* rep; - cordz_info_t unused_aligner; }; - cordz_info_t cordz_info; - }; - char& tag() { return reinterpret_cast<char*>(this)[kMaxInline]; } - char tag() const { return reinterpret_cast<const char*>(this)[kMaxInline]; } + explicit Rep(DefaultInitType) {} + constexpr Rep() : data{0} {} + constexpr Rep(const Rep&) = default; + constexpr Rep& operator=(const Rep&) = default; + + explicit constexpr Rep(CordRep* rep) : as_tree(rep) {} + + explicit constexpr Rep(y_absl::string_view chars) + : data{static_cast<char>((chars.size() << 1)), + GetOrNull(chars, 0), + GetOrNull(chars, 1), + GetOrNull(chars, 2), + GetOrNull(chars, 3), + GetOrNull(chars, 4), + GetOrNull(chars, 5), + GetOrNull(chars, 6), + GetOrNull(chars, 7), + GetOrNull(chars, 8), + GetOrNull(chars, 9), + GetOrNull(chars, 10), + GetOrNull(chars, 11), + GetOrNull(chars, 12), + GetOrNull(chars, 13), + GetOrNull(chars, 14)} {} + + // Disable sanitizer as we must always be able to read `tag`. + Y_ABSL_CORD_INTERNAL_NO_SANITIZE + int8_t tag() const { return reinterpret_cast<const int8_t*>(this)[0]; } + void set_tag(int8_t rhs) { reinterpret_cast<int8_t*>(this)[0] = rhs; } + + char* as_chars() { return data + 1; } + const char* as_chars() const { return data + 1; } + + bool is_tree() const { return (tag() & 1) != 0; } + + size_t inline_size() const { + Y_ABSL_ASSERT(!is_tree()); + return static_cast<size_t>(tag()) >> 1; + } + + void set_inline_size(size_t size) { + Y_ABSL_ASSERT(size <= kMaxInline); + set_tag(static_cast<int8_t>(size << 1)); + } + + CordRep* tree() const { return as_tree.rep; } + void set_tree(CordRep* rhs) { as_tree.rep = rhs; } + + cordz_info_t cordz_info() const { return as_tree.cordz_info; } + void set_cordz_info(cordz_info_t rhs) { as_tree.cordz_info = rhs; } + + void make_tree(CordRep* tree) { + as_tree.rep = tree; + as_tree.cordz_info = kNullCordzInfo; + } + +#ifdef Y_ABSL_INTERNAL_CORD_HAVE_SANITIZER + constexpr Rep SanitizerSafeCopy() const { + if (!y_absl::is_constant_evaluated()) { + Rep res; + if (is_tree()) { + res = *this; + } else { + res.set_tag(tag()); + memcpy(res.as_chars(), as_chars(), inline_size()); + } + return res; + } else { + return *this; + } + } +#else + constexpr const Rep& SanitizerSafeCopy() const { return *this; } +#endif - // If the data has length <= kMaxInline, we store it in `as_chars_`, and - // store the size in the last char of `as_chars_` shifted left + 1. - // Else we store it in a tree and store a pointer to that tree in - // `as_tree_.rep` and store a tag in `tagged_size`. - union { - char as_chars_[kMaxInline + 1]; - AsTree as_tree_; + // If the data has length <= kMaxInline, we store it in `data`, and + // store the size in the first char of `data` shifted left + 1. + // Else we store it in a tree and store a pointer to that tree in + // `as_tree.rep` with a tagged pointer to make `tag() & 1` non zero. + union { + char data[kMaxInline + 1]; + AsTree as_tree; + }; }; + + // Private implementation of `Compare()` + static inline int Compare(const Rep& lhs, const Rep& rhs) { + uint64_t x, y; + memcpy(&x, lhs.as_chars(), sizeof(x)); + memcpy(&y, rhs.as_chars(), sizeof(y)); + if (x == y) { + memcpy(&x, lhs.as_chars() + 7, sizeof(x)); + memcpy(&y, rhs.as_chars() + 7, sizeof(y)); + if (x == y) { + if (lhs.inline_size() == rhs.inline_size()) return 0; + return lhs.inline_size() < rhs.inline_size() ? -1 : 1; + } + } + x = y_absl::big_endian::FromHost64(x); + y = y_absl::big_endian::FromHost64(y); + return x < y ? -1 : 1; + } + + Rep rep_; }; static_assert(sizeof(InlineData) == kMaxInline + 1, ""); +#ifdef Y_ABSL_INTERNAL_CORD_HAVE_SANITIZER + +constexpr InlineData::InlineData(const InlineData& rhs) noexcept + : rep_(rhs.rep_.SanitizerSafeCopy()) { + poison(); +} + +inline InlineData& InlineData::operator=(const InlineData& rhs) noexcept { + unpoison(); + rep_ = rhs.rep_.SanitizerSafeCopy(); + poison(); + return *this; +} + +constexpr void InlineData::poison_this() { + if (!y_absl::is_constant_evaluated()) { + container_internal::SanitizerPoisonObject(this); + } +} + +constexpr void InlineData::unpoison() { + if (!y_absl::is_constant_evaluated()) { + container_internal::SanitizerUnpoisonObject(this); + } +} + +constexpr void InlineData::poison() { + if (!y_absl::is_constant_evaluated()) { + if (is_tree()) { + container_internal::SanitizerUnpoisonObject(this); + } else if (const size_t size = inline_size()) { + if (size < kMaxInline) { + const char* end = rep_.as_chars() + size; + container_internal::SanitizerPoisonMemoryRegion(end, kMaxInline - size); + } + } else { + container_internal::SanitizerPoisonObject(this); + } + } +} + +#else // Y_ABSL_INTERNAL_CORD_HAVE_SANITIZER + +constexpr InlineData::InlineData(const InlineData&) noexcept = default; +inline InlineData& InlineData::operator=(const InlineData&) noexcept = default; + +constexpr void InlineData::poison_this() {} +constexpr void InlineData::unpoison() {} +constexpr void InlineData::poison() {} + +#endif // Y_ABSL_INTERNAL_CORD_HAVE_SANITIZER + inline CordRepSubstring* CordRep::substring() { assert(IsSubstring()); return static_cast<CordRepSubstring*>(this); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree.cc index e073876f8c..3f283ed4f1 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree.cc @@ -17,11 +17,13 @@ #include <cassert> #include <cstdint> #include <iostream> +#include <ostream> #include <util/generic/string.h> #include "y_absl/base/attributes.h" #include "y_absl/base/config.h" #include "y_absl/base/internal/raw_logging.h" +#include "y_absl/base/optimization.h" #include "y_absl/strings/internal/cord_data_edge.h" #include "y_absl/strings/internal/cord_internal.h" #include "y_absl/strings/internal/cord_rep_consume.h" @@ -55,8 +57,10 @@ inline bool exhaustive_validation() { // Prints the entire tree structure or 'rep'. External callers should // not specify 'depth' and leave it to its default (0) value. // Rep may be a CordRepBtree tree, or a SUBSTRING / EXTERNAL / FLAT node. -void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream, - int depth = 0) { +void DumpAll(const CordRep* rep, + bool include_contents, + std::ostream& stream, + size_t depth = 0) { // Allow for full height trees + substring -> flat / external nodes. assert(depth <= CordRepBtree::kMaxDepth + 2); TString sharing = const_cast<CordRep*>(rep)->refcount.IsOne() @@ -283,7 +287,7 @@ struct StackOperations { case CordRepBtree::kSelf: return result.tree; } - Y_ABSL_INTERNAL_UNREACHABLE; + Y_ABSL_UNREACHABLE(); return result.tree; } @@ -499,7 +503,7 @@ OpResult CordRepBtree::SetEdge(bool owned, CordRep* edge, size_t delta) { // open interval [begin, back) or [begin + 1, end) depending on `edge_type`. // We conveniently cover both case using a constexpr `shift` being 0 or 1 // as `end :== back + 1`. - result = {CopyRaw(), kCopied}; + result = {CopyRaw(length), kCopied}; constexpr int shift = edge_type == kFront ? 1 : 0; for (CordRep* r : Edges(begin() + shift, back() + shift)) { CordRep::Ref(r); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree.h index c6d01fdda3..c013bbbdc7 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree.h @@ -95,8 +95,9 @@ class CordRepBtree : public CordRep { // local stack variable compared to Cord's current near 400 bytes stack use. // The maximum `height` value of a node is then `kMaxDepth - 1` as node height // values start with a value of 0 for leaf nodes. - static constexpr int kMaxDepth = 12; - static constexpr int kMaxHeight = kMaxDepth - 1; + static constexpr size_t kMaxDepth = 12; + // See comments on height() for why this is an int and not a size_t. + static constexpr int kMaxHeight = static_cast<int>(kMaxDepth - 1); // `Action` defines the action for unwinding changes done at the btree's leaf // level that need to be propagated up to the parent node(s). Each operation @@ -445,9 +446,9 @@ class CordRepBtree : public CordRep { template <EdgeType edge_type> static CordRepBtree* NewLeaf(y_absl::string_view data, size_t extra); - // Creates a raw copy of this Btree node, copying all properties, but - // without adding any references to existing edges. - CordRepBtree* CopyRaw() const; + // Creates a raw copy of this Btree node with the specified length, copying + // all properties, but without adding any references to existing edges. + CordRepBtree* CopyRaw(size_t new_length) const; // Creates a full copy of this Btree node, adding a reference on all edges. CordRepBtree* Copy() const; @@ -665,15 +666,28 @@ inline void CordRepBtree::Unref(y_absl::Span<CordRep* const> edges) { } } -inline CordRepBtree* CordRepBtree::CopyRaw() const { - auto* tree = static_cast<CordRepBtree*>(::operator new(sizeof(CordRepBtree))); - memcpy(static_cast<void*>(tree), this, sizeof(CordRepBtree)); - new (&tree->refcount) RefcountAndFlags; +inline CordRepBtree* CordRepBtree::CopyRaw(size_t new_length) const { + CordRepBtree* tree = new CordRepBtree; + + // `length` and `refcount` are the first members of `CordRepBtree`. + // We initialize `length` using the given length, have `refcount` be set to + // ref = 1 through its default constructor, and copy all data beyond + // 'refcount' which starts with `tag` using a single memcpy: all contents + // except `refcount` is trivially copyable, and the compiler does not + // efficiently coalesce member-wise copy of these members. + // See https://gcc.godbolt.org/z/qY8zsca6z + // # LINT.IfChange(copy_raw) + tree->length = new_length; + uint8_t* dst = &tree->tag; + const uint8_t* src = &tag; + const ptrdiff_t offset = src - reinterpret_cast<const uint8_t*>(this); + memcpy(dst, src, sizeof(CordRepBtree) - static_cast<size_t>(offset)); return tree; + // # LINT.ThenChange() } inline CordRepBtree* CordRepBtree::Copy() const { - CordRepBtree* tree = CopyRaw(); + CordRepBtree* tree = CopyRaw(length); for (CordRep* rep : Edges()) CordRep::Ref(rep); return tree; } @@ -682,8 +696,7 @@ inline CordRepBtree* CordRepBtree::CopyToEndFrom(size_t begin, size_t new_length) const { assert(begin >= this->begin()); assert(begin <= this->end()); - CordRepBtree* tree = CopyRaw(); - tree->length = new_length; + CordRepBtree* tree = CopyRaw(new_length); tree->set_begin(begin); for (CordRep* edge : tree->Edges()) CordRep::Ref(edge); return tree; @@ -693,8 +706,7 @@ inline CordRepBtree* CordRepBtree::CopyBeginTo(size_t end, size_t new_length) const { assert(end <= capacity()); assert(end >= this->begin()); - CordRepBtree* tree = CopyRaw(); - tree->length = new_length; + CordRepBtree* tree = CopyRaw(new_length); tree->set_end(end); for (CordRep* edge : tree->Edges()) CordRep::Ref(edge); return tree; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree_navigator.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree_navigator.cc index 861a3c034b..c72ef4e6a1 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree_navigator.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_btree_navigator.cc @@ -90,7 +90,7 @@ CordRepBtreeNavigator::Position CordRepBtreeNavigator::Skip(size_t n) { // edges that must be skipped. while (height > 0) { node = edge->btree(); - index_[height] = index; + index_[height] = static_cast<uint8_t>(index); node_[--height] = node; index = node->begin(); edge = node->Edge(index); @@ -101,7 +101,7 @@ CordRepBtreeNavigator::Position CordRepBtreeNavigator::Skip(size_t n) { edge = node->Edge(index); } } - index_[0] = index; + index_[0] = static_cast<uint8_t>(index); return {edge, n}; } @@ -126,7 +126,7 @@ ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) { do { length -= edge->length; while (++index == node->end()) { - index_[height] = index; + index_[height] = static_cast<uint8_t>(index); if (++height > height_) { subtree->set_end(subtree_end); if (length == 0) return {subtree, 0}; @@ -154,7 +154,7 @@ ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) { // edges that must be read, adding 'down' nodes to `subtree`. while (height > 0) { node = edge->btree(); - index_[height] = index; + index_[height] = static_cast<uint8_t>(index); node_[--height] = node; index = node->begin(); edge = node->Edge(index); @@ -178,7 +178,7 @@ ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) { subtree->edges_[subtree_end++] = Substring(edge, 0, length); } subtree->set_end(subtree_end); - index_[0] = index; + index_[0] = static_cast<uint8_t>(index); return {tree, length}; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_crc.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_crc.cc index a2f6382f72..a48d2dcb1f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_crc.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_crc.cc @@ -16,6 +16,7 @@ #include <cassert> #include <cstdint> +#include <utility> #include "y_absl/base/config.h" #include "y_absl/strings/internal/cord_internal.h" @@ -24,11 +25,10 @@ namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace cord_internal { -CordRepCrc* CordRepCrc::New(CordRep* child, uint32_t crc) { - assert(child != nullptr); - if (child->IsCrc()) { +CordRepCrc* CordRepCrc::New(CordRep* child, crc_internal::CrcCordState state) { + if (child != nullptr && child->IsCrc()) { if (child->refcount.IsOne()) { - child->crc()->crc = crc; + child->crc()->crc_cord_state = std::move(state); return child->crc(); } CordRep* old = child; @@ -37,15 +37,17 @@ CordRepCrc* CordRepCrc::New(CordRep* child, uint32_t crc) { CordRep::Unref(old); } auto* new_cordrep = new CordRepCrc; - new_cordrep->length = child->length; + new_cordrep->length = child != nullptr ? child->length : 0; new_cordrep->tag = cord_internal::CRC; new_cordrep->child = child; - new_cordrep->crc = crc; + new_cordrep->crc_cord_state = std::move(state); return new_cordrep; } void CordRepCrc::Destroy(CordRepCrc* node) { - CordRep::Unref(node->child); + if (node->child != nullptr) { + CordRep::Unref(node->child); + } delete node; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_crc.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_crc.h index 15d22fa248..68aef03e7c 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_crc.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_crc.h @@ -20,6 +20,7 @@ #include "y_absl/base/config.h" #include "y_absl/base/optimization.h" +#include "y_absl/crc/internal/crc_cord_state.h" #include "y_absl/strings/internal/cord_internal.h" namespace y_absl { @@ -34,14 +35,14 @@ namespace cord_internal { // the contained checksum is the user's responsibility. struct CordRepCrc : public CordRep { CordRep* child; - uint32_t crc; + y_absl::crc_internal::CrcCordState crc_cord_state; // Consumes `child` and returns a CordRepCrc prefixed tree containing `child`. // If the specified `child` is itself a CordRepCrc node, then this method - // either replaces the existing node, or directly updates the crc value in it + // either replaces the existing node, or directly updates the crc state in it // depending on the node being shared or not, i.e.: refcount.IsOne(). - // `child` must not be null. Never returns null. - static CordRepCrc* New(CordRep* child, uint32_t crc); + // `child` must only be null if the Cord is empty. Never returns null. + static CordRepCrc* New(CordRep* child, crc_internal::CrcCordState state); // Destroys (deletes) the provided node. `node` must not be null. static void Destroy(CordRepCrc* node); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions.h index a43850aba9..0404447777 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef Y_ABSL_STRINGS_CORDZ_FUNCTIONS_H_ -#define Y_ABSL_STRINGS_CORDZ_FUNCTIONS_H_ +#ifndef Y_ABSL_STRINGS_INTERNAL_CORDZ_FUNCTIONS_H_ +#define Y_ABSL_STRINGS_INTERNAL_CORDZ_FUNCTIONS_H_ #include <stdint.h> @@ -32,18 +32,10 @@ int32_t get_cordz_mean_interval(); // Sets the sample rate with the average interval between samples. void set_cordz_mean_interval(int32_t mean_interval); -// Enable cordz unless any of the following applies: -// - no thread local support -// - MSVC build -// - Android build -// - Apple build -// - DLL build -// Hashtablez is turned off completely in opensource builds. -// MSVC's static atomics are dynamically initialized in debug mode, which breaks -// sampling. -#if defined(Y_ABSL_HAVE_THREAD_LOCAL) && !defined(_MSC_VER) && \ - !defined(Y_ABSL_BUILD_DLL) && !defined(Y_ABSL_CONSUME_DLL) && \ - !defined(__ANDROID__) && !defined(__APPLE__) +// Cordz is only enabled on Linux with thread_local support. +#if defined(Y_ABSL_INTERNAL_CORDZ_ENABLED) +#error Y_ABSL_INTERNAL_CORDZ_ENABLED cannot be set directly +#elif defined(__linux__) && defined(Y_ABSL_HAVE_THREAD_LOCAL) #define Y_ABSL_INTERNAL_CORDZ_ENABLED 1 #endif @@ -82,4 +74,4 @@ inline void cordz_set_next_sample_for_testing(int64_t) {} Y_ABSL_NAMESPACE_END } // namespace y_absl -#endif // Y_ABSL_STRINGS_CORDZ_FUNCTIONS_H_ +#endif // Y_ABSL_STRINGS_INTERNAL_CORDZ_FUNCTIONS_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle.h index 5ab0cee569..ab5d895e06 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef Y_ABSL_STRINGS_CORDZ_HANDLE_H_ -#define Y_ABSL_STRINGS_CORDZ_HANDLE_H_ +#ifndef Y_ABSL_STRINGS_INTERNAL_CORDZ_HANDLE_H_ +#define Y_ABSL_STRINGS_INTERNAL_CORDZ_HANDLE_H_ #include <atomic> #include <vector> @@ -128,4 +128,4 @@ class CordzSnapshot : public CordzHandle { Y_ABSL_NAMESPACE_END } // namespace y_absl -#endif // Y_ABSL_STRINGS_CORDZ_HANDLE_H_ +#endif // Y_ABSL_STRINGS_INTERNAL_CORDZ_HANDLE_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.cc index bc257c9be2..beabc48dd9 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.cc @@ -35,7 +35,7 @@ namespace cord_internal { using ::y_absl::base_internal::SpinLockHolder; #ifdef Y_ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL -constexpr int CordzInfo::kMaxStackDepth; +constexpr size_t CordzInfo::kMaxStackDepth; #endif Y_ABSL_CONST_INIT CordzInfo::List CordzInfo::global_list_{y_absl::kConstInit}; @@ -291,7 +291,7 @@ CordzInfo::MethodIdentifier CordzInfo::GetParentMethod(const CordzInfo* src) { : src->method_; } -int CordzInfo::FillParentStack(const CordzInfo* src, void** stack) { +size_t CordzInfo::FillParentStack(const CordzInfo* src, void** stack) { assert(stack); if (src == nullptr) return 0; if (src->parent_stack_depth_) { @@ -302,11 +302,14 @@ int CordzInfo::FillParentStack(const CordzInfo* src, void** stack) { return src->stack_depth_; } -CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src, +CordzInfo::CordzInfo(CordRep* rep, + const CordzInfo* src, MethodIdentifier method) : rep_(rep), - stack_depth_(y_absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth, - /*skip_count=*/1)), + stack_depth_( + static_cast<size_t>(y_absl::GetStackTrace(stack_, + /*max_depth=*/kMaxStackDepth, + /*skip_count=*/1))), parent_stack_depth_(FillParentStack(src, parent_stack_)), method_(method), parent_method_(GetParentMethod(src)), diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.h index fd89875933..ca7ecab469 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef Y_ABSL_STRINGS_CORDZ_INFO_H_ -#define Y_ABSL_STRINGS_CORDZ_INFO_H_ +#ifndef Y_ABSL_STRINGS_INTERNAL_CORDZ_INFO_H_ +#define Y_ABSL_STRINGS_INTERNAL_CORDZ_INFO_H_ #include <atomic> #include <cstdint> @@ -196,7 +196,7 @@ class Y_ABSL_LOCKABLE CordzInfo : public CordzHandle { std::atomic<CordzInfo*> head Y_ABSL_GUARDED_BY(mutex){nullptr}; }; - static constexpr int kMaxStackDepth = 64; + static constexpr size_t kMaxStackDepth = 64; explicit CordzInfo(CordRep* rep, const CordzInfo* src, MethodIdentifier method); @@ -216,7 +216,7 @@ class Y_ABSL_LOCKABLE CordzInfo : public CordzHandle { // `stack_` depending on `parent_stack_` being empty, returning the size of // the parent stack. // Returns 0 if `src` is null. - static int FillParentStack(const CordzInfo* src, void** stack); + static size_t FillParentStack(const CordzInfo* src, void** stack); void ODRCheck() const { #ifndef NDEBUG @@ -244,8 +244,8 @@ class Y_ABSL_LOCKABLE CordzInfo : public CordzHandle { void* stack_[kMaxStackDepth]; void* parent_stack_[kMaxStackDepth]; - const int stack_depth_; - const int parent_stack_depth_; + const size_t stack_depth_; + const size_t parent_stack_depth_; const MethodIdentifier method_; const MethodIdentifier parent_method_; CordzUpdateTracker update_tracker_; @@ -295,4 +295,4 @@ inline CordRep* CordzInfo::RefCordRep() const Y_ABSL_LOCKS_EXCLUDED(mutex_) { Y_ABSL_NAMESPACE_END } // namespace y_absl -#endif // Y_ABSL_STRINGS_CORDZ_INFO_H_ +#endif // Y_ABSL_STRINGS_INTERNAL_CORDZ_INFO_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_sample_token.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_sample_token.h index 8ef2f64dc6..821c1566b6 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_sample_token.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_sample_token.h @@ -16,8 +16,8 @@ #include "y_absl/strings/internal/cordz_handle.h" #include "y_absl/strings/internal/cordz_info.h" -#ifndef Y_ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ -#define Y_ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ +#ifndef Y_ABSL_STRINGS_INTERNAL_CORDZ_SAMPLE_TOKEN_H_ +#define Y_ABSL_STRINGS_INTERNAL_CORDZ_SAMPLE_TOKEN_H_ namespace y_absl { Y_ABSL_NAMESPACE_BEGIN @@ -94,4 +94,4 @@ class CordzSampleToken : public CordzSnapshot { Y_ABSL_NAMESPACE_END } // namespace y_absl -#endif // Y_ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ +#endif // Y_ABSL_STRINGS_INTERNAL_CORDZ_SAMPLE_TOKEN_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_statistics.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_statistics.h index 01194adeed..f7624abe1d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_statistics.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_statistics.h @@ -45,12 +45,12 @@ struct CordzStatistics { }; // The size of the cord in bytes. This matches the result of Cord::size(). - int64_t size = 0; + size_t size = 0; // The estimated memory used by the sampled cord. This value matches the // value as reported by Cord::EstimatedMemoryUsage(). // A value of 0 implies the property has not been recorded. - int64_t estimated_memory_usage = 0; + size_t estimated_memory_usage = 0; // The effective memory used by the sampled cord, inversely weighted by the // effective indegree of each allocated node. This is a representation of the @@ -59,14 +59,14 @@ struct CordzStatistics { // by multiple Cord instances, and for cases where a Cord includes the same // node multiple times (either directly or indirectly). // A value of 0 implies the property has not been recorded. - int64_t estimated_fair_share_memory_usage = 0; + size_t estimated_fair_share_memory_usage = 0; // The total number of nodes referenced by this cord. // For ring buffer Cords, this includes the 'ring buffer' node. // For btree Cords, this includes all 'CordRepBtree' tree nodes as well as all // the substring, flat and external nodes referenced by the tree. // A value of 0 implies the property has not been recorded. - int64_t node_count = 0; + size_t node_count = 0; // Detailed node counts per type NodeCounts node_counts; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.cc new file mode 100644 index 0000000000..87b211c001 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.cc @@ -0,0 +1,93 @@ +// Copyright 2022 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 "y_absl/strings/internal/damerau_levenshtein_distance.h" + +#include <algorithm> +#include <array> +#include <numeric> + +#include "y_absl/strings/string_view.h" +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace strings_internal { +// Calculate DamerauLevenshtein (adjacent transpositions) distance +// between two strings, +// https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance. The +// algorithm follows the condition that no substring is edited more than once. +// While this can reduce is larger distance, it's a) a much simpler algorithm +// and b) more realistic for the case that typographic mistakes should be +// detected. +// When the distance is larger than cutoff, or one of the strings has more +// than MAX_SIZE=100 characters, the code returns min(MAX_SIZE, cutoff) + 1. +uint8_t CappedDamerauLevenshteinDistance(y_absl::string_view s1, + y_absl::string_view s2, uint8_t cutoff) { + const uint8_t MAX_SIZE = 100; + const uint8_t _cutoff = std::min(MAX_SIZE, cutoff); + const uint8_t cutoff_plus_1 = static_cast<uint8_t>(_cutoff + 1); + + if (s1.size() > s2.size()) std::swap(s1, s2); + if (s1.size() + _cutoff < s2.size() || s2.size() > MAX_SIZE) + return cutoff_plus_1; + + if (s1.empty()) + return static_cast<uint8_t>(s2.size()); + + // Lower diagonal bound: y = x - lower_diag + const uint8_t lower_diag = + _cutoff - static_cast<uint8_t>(s2.size() - s1.size()); + // Upper diagonal bound: y = x + upper_diag + const uint8_t upper_diag = _cutoff; + + // d[i][j] is the number of edits required to convert s1[0, i] to s2[0, j] + std::array<std::array<uint8_t, MAX_SIZE + 2>, MAX_SIZE + 2> d; + std::iota(d[0].begin(), d[0].begin() + upper_diag + 1, 0); + d[0][cutoff_plus_1] = cutoff_plus_1; + for (size_t i = 1; i <= s1.size(); ++i) { + // Deduce begin of relevant window. + size_t j_begin = 1; + if (i > lower_diag) { + j_begin = i - lower_diag; + d[i][j_begin - 1] = cutoff_plus_1; + } else { + d[i][0] = static_cast<uint8_t>(i); + } + + // Deduce end of relevant window. + size_t j_end = i + upper_diag; + if (j_end > s2.size()) { + j_end = s2.size(); + } else { + d[i][j_end + 1] = cutoff_plus_1; + } + + for (size_t j = j_begin; j <= j_end; ++j) { + const uint8_t deletion_distance = d[i - 1][j] + 1; + const uint8_t insertion_distance = d[i][j - 1] + 1; + const uint8_t mismatched_tail_cost = s1[i - 1] == s2[j - 1] ? 0 : 1; + const uint8_t mismatch_distance = d[i - 1][j - 1] + mismatched_tail_cost; + uint8_t transposition_distance = _cutoff + 1; + if (i > 1 && j > 1 && s1[i - 1] == s2[j - 2] && s1[i - 2] == s2[j - 1]) + transposition_distance = d[i - 2][j - 2] + 1; + d[i][j] = std::min({cutoff_plus_1, deletion_distance, insertion_distance, + mismatch_distance, transposition_distance}); + } + } + return d[s1.size()][s2.size()]; +} + +} // namespace strings_internal + +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.h new file mode 100644 index 0000000000..4620e31685 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/damerau_levenshtein_distance.h @@ -0,0 +1,34 @@ +// Copyright 2022 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 Y_ABSL_STRINGS_INTERNAL_DAMERAU_LEVENSHTEIN_DISTANCE_H_ +#define Y_ABSL_STRINGS_INTERNAL_DAMERAU_LEVENSHTEIN_DISTANCE_H_ + +#include <cstdint> + +#include "y_absl/strings/string_view.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace strings_internal { +// Calculate DamerauLevenshtein distance between two strings. +// When the distance is larger than cutoff, the code just returns cutoff + 1. +uint8_t CappedDamerauLevenshteinDistance(y_absl::string_view s1, + y_absl::string_view s2, uint8_t cutoff); + +} // namespace strings_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_STRINGS_INTERNAL_DAMERAU_LEVENSHTEIN_DISTANCE_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.cc index 9e7c766565..421838cc85 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.cc @@ -28,19 +28,11 @@ 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; + // Since all base 64 input is an integral number of octets, only the following + // cases can arise: 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 @@ -83,6 +75,16 @@ size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, char* const limit_dest = dest + szdest; const unsigned char* const limit_src = src + szsrc; + // (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. + // + // If do_padding is true, padding at the end of the data is performed. This + // output padding uses the '=' character. + // 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. diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.h index 743ac2519d..1b49ef4bc0 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/escaping.h @@ -25,19 +25,17 @@ namespace strings_internal { Y_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. +// Calculates the length of a Base64 encoding (RFC 4648) of a string of length +// `input_len`, with or without padding per `do_padding`. Note that 'web-safe' +// encoding (section 5 of the RFC) does not change this length. 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`. +// Base64-encodes `src` using the alphabet provided in `base64` (which +// determines whether to do web-safe encoding or not) 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) { diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/has_absl_stringify.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/has_absl_stringify.h new file mode 100644 index 0000000000..bf0b91d8a9 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/has_absl_stringify.h @@ -0,0 +1,55 @@ +// Copyright 2022 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 Y_ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ +#define Y_ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ +#include <util/generic/string.h> +#include <type_traits> +#include <utility> + +#include "y_absl/strings/string_view.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN + +namespace strings_internal { + +// This is an empty class not intended to be used. It exists so that +// `HasAbslStringify` can reference a universal class rather than needing to be +// copied for each new sink. +class UnimplementedSink { + public: + void Append(size_t count, char ch); + + void Append(string_view v); + + // Support `y_absl::Format(&sink, format, args...)`. + friend void AbslFormatFlush(UnimplementedSink* sink, y_absl::string_view v); +}; + +template <typename T, typename = void> +struct HasAbslStringify : std::false_type {}; + +template <typename T> +struct HasAbslStringify< + T, std::enable_if_t<std::is_void<decltype(AbslStringify( + std::declval<strings_internal::UnimplementedSink&>(), + std::declval<const T&>()))>::value>> : std::true_type {}; + +} // namespace strings_internal + +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/memutil.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/memutil.cc index 3ef534118e..7d1ed56a3a 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/memutil.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/memutil.cc @@ -54,10 +54,11 @@ size_t memspn(const char* s, size_t slen, const char* accept) { cont: c = *p++; - if (slen-- == 0) return p - 1 - s; + if (slen-- == 0) + return static_cast<size_t>(p - 1 - s); for (spanp = accept; (sc = *spanp++) != '\0';) if (sc == c) goto cont; - return p - 1 - s; + return static_cast<size_t>(p - 1 - s); } size_t memcspn(const char* s, size_t slen, const char* reject) { @@ -68,9 +69,10 @@ size_t memcspn(const char* s, size_t slen, const char* reject) { while (slen-- != 0) { c = *p++; for (spanp = reject; (sc = *spanp++) != '\0';) - if (sc == c) return p - 1 - s; + if (sc == c) + return static_cast<size_t>(p - 1 - s); } - return p - s; + return static_cast<size_t>(p - s); } char* mempbrk(const char* s, size_t slen, const char* accept) { @@ -97,8 +99,9 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, const char* hayend = phaystack + haylen - neelen + 1; // A static cast is used here to work around the fact that memchr returns // a void* on Posix-compliant systems and const void* on Windows. - while ((match = static_cast<const char*>( - memchr(phaystack, pneedle[0], hayend - phaystack)))) { + while ( + (match = static_cast<const char*>(memchr( + phaystack, pneedle[0], static_cast<size_t>(hayend - phaystack))))) { if (memcmp(match, pneedle, neelen) == 0) return match; else diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.cc index 2c23a18e12..b250977bc2 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.cc @@ -14,20 +14,27 @@ #include "y_absl/strings/internal/ostringstream.h" +#include <cassert> +#include <cstddef> +#include <ios> +#include <streambuf> + namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace strings_internal { -OStringStream::Buf::int_type OStringStream::overflow(int c) { - assert(s_); - if (!Buf::traits_type::eq_int_type(c, Buf::traits_type::eof())) - s_->push_back(static_cast<char>(c)); +OStringStream::Streambuf::int_type OStringStream::Streambuf::overflow(int c) { + assert(str_); + if (!std::streambuf::traits_type::eq_int_type( + c, std::streambuf::traits_type::eof())) + str_->push_back(static_cast<char>(c)); return 1; } -std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) { - assert(s_); - s_->append(s, static_cast<size_t>(n)); +std::streamsize OStringStream::Streambuf::xsputn(const char* s, + std::streamsize n) { + assert(str_); + str_->append(s, static_cast<size_t>(n)); return n; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.h index f9e47f0071..3e45d7fa37 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/ostringstream.h @@ -16,11 +16,13 @@ #define Y_ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_ #include <cassert> +#include <ios> #include <ostream> #include <streambuf> #include <util/generic/string.h> +#include <utility> -#include "y_absl/base/port.h" +#include "y_absl/base/config.h" namespace y_absl { Y_ABSL_NAMESPACE_BEGIN @@ -60,26 +62,49 @@ namespace strings_internal { // strm << 3.14; // // Note: flush() has no effect. No reason to call it. -class OStringStream : private std::basic_streambuf<char>, public std::ostream { +class OStringStream final : public std::ostream { public: // The argument can be null, in which case you'll need to call str(p) with a // non-null argument before you can write to the stream. // // The destructor of OStringStream doesn't use the TString. It's OK to // destroy the TString before the stream. - explicit OStringStream(TString* s) : std::ostream(this), s_(s) {} + explicit OStringStream(TString* str) + : std::ostream(&buf_), buf_(str) {} + OStringStream(OStringStream&& that) + : std::ostream(std::move(static_cast<std::ostream&>(that))), + buf_(that.buf_) { + rdbuf(&buf_); + } + OStringStream& operator=(OStringStream&& that) { + std::ostream::operator=(std::move(static_cast<std::ostream&>(that))); + buf_ = that.buf_; + rdbuf(&buf_); + return *this; + } - TString* str() { return s_; } - const TString* str() const { return s_; } - void str(TString* s) { s_ = s; } + TString* str() { return buf_.str(); } + const TString* str() const { return buf_.str(); } + void str(TString* str) { buf_.str(str); } private: - using Buf = std::basic_streambuf<char>; + class Streambuf final : public std::streambuf { + public: + explicit Streambuf(TString* str) : str_(str) {} + Streambuf(const Streambuf&) = default; + Streambuf& operator=(const Streambuf&) = default; - Buf::int_type overflow(int c) override; - std::streamsize xsputn(const char* s, std::streamsize n) override; + TString* str() { return str_; } + const TString* str() const { return str_; } + void str(TString* str) { str_ = str; } - TString* s_; + protected: + int_type overflow(int c) override; + std::streamsize xsputn(const char* s, std::streamsize n) override; + + private: + TString* str_; + } buf_; }; } // namespace strings_internal diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/arg.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/arg.cc index 0d503208f0..67ecc3af7c 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/arg.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/arg.cc @@ -77,7 +77,7 @@ class IntDigits { v >>= 3; } while (v); start_ = p; - size_ = storage_ + sizeof(storage_) - p; + size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p); } // Print the signed or unsigned integer as decimal. @@ -86,7 +86,8 @@ class IntDigits { void PrintAsDec(T v) { static_assert(std::is_integral<T>::value, ""); start_ = storage_; - size_ = numbers_internal::FastIntToBuffer(v, storage_) - storage_; + size_ = static_cast<size_t>(numbers_internal::FastIntToBuffer(v, storage_) - + storage_); } void PrintAsDec(int128 v) { @@ -115,7 +116,7 @@ class IntDigits { if (add_neg) { *--p = '-'; } - size_ = storage_ + sizeof(storage_) - p; + size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p); start_ = p; } @@ -138,7 +139,7 @@ class IntDigits { ++p; } start_ = p; - size_ = storage_ + sizeof(storage_) - p; + size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p); } // Print the unsigned integer as hex using uppercase. @@ -154,7 +155,7 @@ class IntDigits { v >>= 4; } while (v); start_ = p; - size_ = storage_ + sizeof(storage_) - p; + size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p); } // The printed value including the '-' sign if available. @@ -208,10 +209,12 @@ string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) { return {}; } -bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +bool ConvertCharImpl(char v, + const FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { size_t fill = 0; - if (conv.width() >= 0) fill = conv.width(); + if (conv.width() >= 0) + fill = static_cast<size_t>(conv.width()); ReducePadding(1, &fill); if (!conv.has_left_flag()) sink->Append(fill, ' '); sink->Append(1, v); @@ -225,7 +228,8 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits, // Print as a sequence of Substrings: // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces] size_t fill = 0; - if (conv.width() >= 0) fill = conv.width(); + if (conv.width() >= 0) + fill = static_cast<size_t>(conv.width()); string_view formatted = as_digits.without_neg_or_zero(); ReducePadding(formatted, &fill); @@ -236,10 +240,9 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits, string_view base_indicator = BaseIndicator(as_digits, conv); ReducePadding(base_indicator, &fill); - int precision = conv.precision(); - bool precision_specified = precision >= 0; - if (!precision_specified) - precision = 1; + bool precision_specified = conv.precision() >= 0; + size_t precision = + precision_specified ? static_cast<size_t>(conv.precision()) : size_t{1}; if (conv.has_alt_flag() && conv.conversion_char() == FormatConversionCharInternal::o) { @@ -247,7 +250,7 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits, // "For o conversion, it increases the precision (if necessary) to // force the first digit of the result to be zero." if (formatted.empty() || *formatted.begin() != '0') { - int needed = static_cast<int>(formatted.size()) + 1; + size_t needed = formatted.size() + 1; precision = std::max(precision, needed); } } @@ -275,19 +278,71 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits, return true; } +template <typename T, + typename std::enable_if<(std::is_integral<T>::value && + std::is_signed<T>::value) || + std::is_same<T, int128>::value, + int>::type = 0> +constexpr auto ConvertV(T) { + return FormatConversionCharInternal::d; +} + +template <typename T, + typename std::enable_if<(std::is_integral<T>::value && + std::is_unsigned<T>::value) || + std::is_same<T, uint128>::value, + int>::type = 0> +constexpr auto ConvertV(T) { + return FormatConversionCharInternal::u; +} + +template <typename T> +bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) { + if (conv.conversion_char() == FormatConversionCharInternal::v) { + conv.set_conversion_char(FormatConversionCharInternal::g); + } + + return FormatConversionCharIsFloat(conv.conversion_char()) && + ConvertFloatImpl(v, conv, sink); +} + +inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + if (conv.is_basic()) { + sink->Append(v); + return true; + } + return sink->PutPaddedString(v, conv.width(), conv.precision(), + conv.has_left_flag()); +} + +} // namespace + +bool ConvertBoolArg(bool v, FormatSinkImpl *sink) { + if (v) { + sink->Append("true"); + } else { + sink->Append("false"); + } + return true; +} + template <typename T> -bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) { using U = typename MakeUnsigned<T>::type; IntDigits as_digits; + if (conv.conversion_char() == FormatConversionCharInternal::v) { + conv.set_conversion_char(ConvertV(T{})); + } + // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes // it to complain about a switch/case type mismatch, even though both are // FormatConverionChar. Likely this is because at this point // FormatConversionChar is declared, but not defined. switch (static_cast<uint8_t>(conv.conversion_char())) { case static_cast<uint8_t>(FormatConversionCharInternal::c): - return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); + return ConvertCharImpl(static_cast<char>(v), conv, sink); case static_cast<uint8_t>(FormatConversionCharInternal::o): as_digits.PrintAsOct(static_cast<U>(v)); @@ -320,7 +375,7 @@ bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, return ConvertFloatImpl(static_cast<double>(v), conv, sink); default: - Y_ABSL_ASSUME(false); + Y_ABSL_ASSUME(false); } if (conv.is_basic()) { @@ -330,24 +385,37 @@ bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, return ConvertIntImplInnerSlow(as_digits, conv, sink); } -template <typename T> -bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return FormatConversionCharIsFloat(conv.conversion_char()) && - ConvertFloatImpl(v, conv, sink); -} - -inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - if (conv.is_basic()) { - sink->Append(v); - return true; - } - return sink->PutPaddedString(v, conv.width(), conv.precision(), - conv.has_left_flag()); -} - -} // namespace +template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<signed char>(signed char v, + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<unsigned char>(unsigned char v, + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<short>(short v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<unsigned short>(unsigned short v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<unsigned int>(unsigned int v, + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<long>(long v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<long long>(long long v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); +template bool ConvertIntArg<unsigned long long>(unsigned long long v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl *sink); // ==================== Strings ==================== StringConvertResult FormatConvertImpl(const TString &v, @@ -375,7 +443,7 @@ FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, len = std::strlen(v); } else { // If precision is set, we look for the NUL-terminator on the valid range. - len = std::find(v, v + conv.precision(), '\0') - v; + len = static_cast<size_t>(std::find(v, v + conv.precision(), '\0') - v); } return {ConvertStringArg(string_view(v, len), conv, sink)}; } @@ -410,19 +478,18 @@ FloatingConvertResult FormatConvertImpl(long double v, } // ==================== Chars ==================== -IntegralConvertResult FormatConvertImpl(char v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } -IntegralConvertResult FormatConvertImpl(signed char v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +CharConvertResult FormatConvertImpl(signed char v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } -IntegralConvertResult FormatConvertImpl(unsigned char v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +CharConvertResult FormatConvertImpl(unsigned char v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/arg.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/arg.h index f6621166e5..8ed5827835 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/arg.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/arg.h @@ -18,6 +18,7 @@ #include <string.h> #include <wchar.h> +#include <algorithm> #include <cstdio> #include <iomanip> #include <limits> @@ -26,10 +27,12 @@ #include <util/generic/string.h> #include <util/stream/str.h> #include <type_traits> +#include <utility> #include "y_absl/base/port.h" #include "y_absl/meta/type_traits.h" #include "y_absl/numeric/int128.h" +#include "y_absl/strings/internal/has_absl_stringify.h" #include "y_absl/strings/internal/str_format/extension.h" #include "y_absl/strings/string_view.h" @@ -46,6 +49,24 @@ class FormatConversionSpec; namespace str_format_internal { +template <FormatConversionCharSet C> +struct ArgConvertResult { + bool value; +}; + +using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::c, + FormatConversionCharSetInternal::kNumeric, + FormatConversionCharSetInternal::kStar, + FormatConversionCharSetInternal::v)>; +using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::kFloating, + FormatConversionCharSetInternal::v)>; +using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::c, + FormatConversionCharSetInternal::kNumeric, + FormatConversionCharSetInternal::kStar)>; + template <typename T, typename = void> struct HasUserDefinedConvert : std::false_type {}; @@ -56,7 +77,50 @@ struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert( std::declval<FormatSink*>()))>> : std::true_type {}; -void AbslFormatConvert(); // Stops the lexical name lookup +// These declarations prevent ADL lookup from continuing in y_absl namespaces, +// we are deliberately using these as ADL hooks and want them to consider +// non-y_absl namespaces only. +void AbslFormatConvert(); +void AbslStringify(); + +template <typename T> +bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); + +// Forward declarations of internal `ConvertIntArg` function template +// instantiations are here to avoid including the template body in the headers +// and instantiating it in large numbers of translation units. Explicit +// instantiations can be found in "y_absl/strings/internal/str_format/arg.cc" +extern template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg<signed char>(signed char v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg<unsigned char>(unsigned char v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg<short>(short v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg<unsigned short>( // NOLINT + unsigned short v, FormatConversionSpecImpl conv, // NOLINT + FormatSinkImpl* sink); +extern template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg<unsigned int>(unsigned int v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg<long>( // NOLINT + long v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // NOLINT +extern template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg<long long>(long long v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg<unsigned long long>( // NOLINT + unsigned long long v, FormatConversionSpecImpl conv, // NOLINT + FormatSinkImpl* sink); + template <typename T> auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) @@ -73,6 +137,39 @@ auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, } template <typename T> +auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) + -> std::enable_if_t<std::is_enum<T>::value && + std::is_void<decltype(AbslStringify( + std::declval<FormatSink&>(), v))>::value, + IntegralConvertResult> { + if (conv.conversion_char() == FormatConversionCharInternal::v) { + using FormatSinkT = + y_absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + auto fs = sink->Wrap<FormatSinkT>(); + AbslStringify(fs, v); + return {true}; + } else { + return {ConvertIntArg( + static_cast<typename std::underlying_type<T>::type>(v), conv, sink)}; + } +} + +template <typename T> +auto FormatConvertImpl(const T& v, FormatConversionSpecImpl, + FormatSinkImpl* sink) + -> std::enable_if_t<!std::is_enum<T>::value && + std::is_void<decltype(AbslStringify( + std::declval<FormatSink&>(), v))>::value, + ArgConvertResult<FormatConversionCharSetInternal::v>> { + using FormatSinkT = + y_absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + auto fs = sink->Wrap<FormatSinkT>(); + AbslStringify(fs, v); + return {true}; +} + +template <typename T> class StreamedWrapper; // If 'v' can be converted (in the printf sense) according to 'conv', @@ -97,11 +194,6 @@ struct VoidPtr { }; template <FormatConversionCharSet C> -struct ArgConvertResult { - bool value; -}; - -template <FormatConversionCharSet C> constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) { return C; } @@ -111,8 +203,8 @@ constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) { return C; } -using StringConvertResult = - ArgConvertResult<FormatConversionCharSetInternal::s>; +using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::v)>; ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); @@ -174,12 +266,7 @@ StringConvertResult FormatConvertImpl(const AbslCord& value, return {true}; } -using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::c, - FormatConversionCharSetInternal::kNumeric, - FormatConversionCharSetInternal::kStar)>; -using FloatingConvertResult = - ArgConvertResult<FormatConversionCharSetInternal::kFloating>; +bool ConvertBoolArg(bool v, FormatSinkImpl* sink); // Floats. FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, @@ -191,14 +278,14 @@ FloatingConvertResult FormatConvertImpl(long double v, FormatSinkImpl* sink); // Chars. -IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(signed char v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned char v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); +CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +CharConvertResult FormatConvertImpl(signed char v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +CharConvertResult FormatConvertImpl(unsigned char v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); // Ints. IntegralConvertResult FormatConvertImpl(short v, // NOLINT @@ -229,9 +316,16 @@ IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, IntegralConvertResult FormatConvertImpl(uint128 v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); + +// This function needs to be a template due to ambiguity regarding type +// conversions. template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0> IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { + if (conv.conversion_char() == FormatConversionCharInternal::v) { + return {ConvertBoolArg(v, sink)}; + } + return FormatConvertImpl(static_cast<int>(v), conv, sink); } @@ -239,7 +333,8 @@ IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, // FormatArgImpl will use the underlying Convert functions instead. template <typename T> typename std::enable_if<std::is_enum<T>::value && - !HasUserDefinedConvert<T>::value, + !HasUserDefinedConvert<T>::value && + !strings_internal::HasAbslStringify<T>::value, IntegralConvertResult>::type FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); @@ -303,11 +398,11 @@ struct FormatArgImplFriend { template <typename Arg> constexpr FormatConversionCharSet ArgumentToConv() { - return y_absl::str_format_internal::ExtractCharSet( - decltype(str_format_internal::FormatConvertImpl( - std::declval<const Arg&>(), - std::declval<const FormatConversionSpecImpl&>(), - std::declval<FormatSinkImpl*>())){}); + using ConvResult = decltype(str_format_internal::FormatConvertImpl( + std::declval<const Arg&>(), + std::declval<const FormatConversionSpecImpl&>(), + std::declval<FormatSinkImpl*>())); + return y_absl::str_format_internal::ExtractCharSet(ConvResult{}); } // A type-erased handle to a format argument. @@ -353,7 +448,8 @@ class FormatArgImpl { template <typename T, typename = void> struct DecayType { static constexpr bool kHasUserDefined = - str_format_internal::HasUserDefinedConvert<T>::value; + str_format_internal::HasUserDefinedConvert<T>::value || + strings_internal::HasAbslStringify<T>::value; using type = typename std::conditional< !kHasUserDefined && std::is_convertible<T, const char*>::value, const char*, @@ -365,6 +461,7 @@ class FormatArgImpl { struct DecayType<T, typename std::enable_if< !str_format_internal::HasUserDefinedConvert<T>::value && + !strings_internal::HasAbslStringify<T>::value && std::is_enum<T>::value>::type> { using type = typename std::underlying_type<T>::type; }; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/bind.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/bind.cc index d9e7ccaac9..745bd12ab2 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/bind.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/bind.cc @@ -32,7 +32,8 @@ inline bool BindFromPosition(int position, int* value, return false; } // -1 because positions are 1-based - return FormatArgImplFriend::ToInt(pack[position - 1], value); + return FormatArgImplFriend::ToInt(pack[static_cast<size_t>(position) - 1], + value); } class ArgContext { @@ -56,7 +57,7 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound, const FormatArgImpl* arg = nullptr; int arg_position = unbound->arg_position; if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false; - arg = &pack_[arg_position - 1]; // 1-based + arg = &pack_[static_cast<size_t>(arg_position - 1)]; // 1-based if (unbound->flags != Flags::kBasic) { int width = unbound->width.value(); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/bind.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/bind.h index 3688b503af..daa756c5f4 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/bind.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/bind.h @@ -235,9 +235,10 @@ class StreamedWrapper { private: template <typename S> - friend ArgConvertResult<FormatConversionCharSetInternal::s> FormatConvertImpl( - const StreamedWrapper<S>& v, FormatConversionSpecImpl conv, - FormatSinkImpl* out); + friend ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::v)> + FormatConvertImpl(const StreamedWrapper<S>& v, FormatConversionSpecImpl conv, + FormatSinkImpl* out); const T& v_; }; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/checker.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/checker.h index e262c6ba87..2aafe0016a 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/checker.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/checker.h @@ -15,8 +15,11 @@ #ifndef Y_ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ #define Y_ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ +#include <algorithm> + #include "y_absl/base/attributes.h" #include "y_absl/strings/internal/str_format/arg.h" +#include "y_absl/strings/internal/str_format/constexpr_parser.h" #include "y_absl/strings/internal/str_format/extension.h" // Compile time check support for entry points. @@ -36,297 +39,56 @@ namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace str_format_internal { -constexpr bool AllOf() { return true; } - -template <typename... T> -constexpr bool AllOf(bool b, T... t) { - return b && AllOf(t...); -} - #ifdef Y_ABSL_INTERNAL_ENABLE_FORMAT_CHECKER -constexpr bool ContainsChar(const char* chars, char c) { - return *chars == c || (*chars && ContainsChar(chars + 1, c)); -} - -// A constexpr compatible list of Convs. -struct ConvList { - const FormatConversionCharSet* array; - int count; - - // We do the bound check here to avoid having to do it on the callers. - // Returning an empty FormatConversionCharSet has the same effect as - // short circuiting because it will never match any conversion. - constexpr FormatConversionCharSet operator[](int i) const { - return i < count ? array[i] : FormatConversionCharSet{}; - } - - constexpr ConvList without_front() const { - return count != 0 ? ConvList{array + 1, count - 1} : *this; - } -}; - -template <size_t count> -struct ConvListT { - // Make sure the array has size > 0. - FormatConversionCharSet list[count ? count : 1]; -}; - -constexpr char GetChar(string_view str, size_t index) { - return index < str.size() ? str[index] : char{}; -} - -constexpr string_view ConsumeFront(string_view str, size_t len = 1) { - return len <= str.size() ? string_view(str.data() + len, str.size() - len) - : string_view(); -} - -constexpr string_view ConsumeAnyOf(string_view format, const char* chars) { - return ContainsChar(chars, GetChar(format, 0)) - ? ConsumeAnyOf(ConsumeFront(format), chars) - : format; -} - -constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; } - -// Helper class for the ParseDigits function. -// It encapsulates the two return values we need there. -struct Integer { - string_view format; - int value; - - // If the next character is a '$', consume it. - // Otherwise, make `this` an invalid positional argument. - constexpr Integer ConsumePositionalDollar() const { - return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value} - : Integer{format, 0}; - } -}; - -constexpr Integer ParseDigits(string_view format, int value = 0) { - return IsDigit(GetChar(format, 0)) - ? ParseDigits(ConsumeFront(format), - 10 * value + GetChar(format, 0) - '0') - : Integer{format, value}; -} - -// Parse digits for a positional argument. -// The parsing also consumes the '$'. -constexpr Integer ParsePositional(string_view format) { - return ParseDigits(format).ConsumePositionalDollar(); -} - -// Parses a single conversion specifier. -// See ConvParser::Run() for post conditions. -class ConvParser { - constexpr ConvParser SetFormat(string_view format) const { - return ConvParser(format, args_, error_, arg_position_, is_positional_); - } - - constexpr ConvParser SetArgs(ConvList args) const { - return ConvParser(format_, args, error_, arg_position_, is_positional_); - } - - constexpr ConvParser SetError(bool error) const { - return ConvParser(format_, args_, error_ || error, arg_position_, - is_positional_); - } - - constexpr ConvParser SetArgPosition(int arg_position) const { - return ConvParser(format_, args_, error_, arg_position, is_positional_); - } - - // Consumes the next arg and verifies that it matches `conv`. - // `error_` is set if there is no next arg or if it doesn't match `conv`. - constexpr ConvParser ConsumeNextArg(char conv) const { - return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv)); - } - - // Verify that positional argument `i.value` matches `conv`. - // `error_` is set if `i.value` is not a valid argument or if it doesn't - // match. - constexpr ConvParser VerifyPositional(Integer i, char conv) const { - return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv)); - } - - // Parse the position of the arg and store it in `arg_position_`. - constexpr ConvParser ParseArgPosition(Integer arg) const { - return SetFormat(arg.format).SetArgPosition(arg.value); - } - - // Consume the flags. - constexpr ConvParser ParseFlags() const { - return SetFormat(ConsumeAnyOf(format_, "-+ #0")); - } - - // Consume the width. - // If it is '*', we verify that it matches `args_`. `error_` is set if it - // doesn't match. - constexpr ConvParser ParseWidth() const { - return IsDigit(GetChar(format_, 0)) - ? SetFormat(ParseDigits(format_).format) - : GetChar(format_, 0) == '*' - ? is_positional_ - ? VerifyPositional( - ParsePositional(ConsumeFront(format_)), '*') - : SetFormat(ConsumeFront(format_)) - .ConsumeNextArg('*') - : *this; - } - - // Consume the precision. - // If it is '*', we verify that it matches `args_`. `error_` is set if it - // doesn't match. - constexpr ConvParser ParsePrecision() const { - return GetChar(format_, 0) != '.' - ? *this - : GetChar(format_, 1) == '*' - ? is_positional_ - ? VerifyPositional( - ParsePositional(ConsumeFront(format_, 2)), '*') - : SetFormat(ConsumeFront(format_, 2)) - .ConsumeNextArg('*') - : SetFormat(ParseDigits(ConsumeFront(format_)).format); - } - - // Consume the length characters. - constexpr ConvParser ParseLength() const { - return SetFormat(ConsumeAnyOf(format_, "lLhjztq")); - } - - // Consume the conversion character and verify that it matches `args_`. - // `error_` is set if it doesn't match. - constexpr ConvParser ParseConversion() const { - return is_positional_ - ? VerifyPositional({ConsumeFront(format_), arg_position_}, - GetChar(format_, 0)) - : ConsumeNextArg(GetChar(format_, 0)) - .SetFormat(ConsumeFront(format_)); - } - - constexpr ConvParser(string_view format, ConvList args, bool error, - int arg_position, bool is_positional) - : format_(format), - args_(args), - error_(error), - arg_position_(arg_position), - is_positional_(is_positional) {} - - public: - constexpr ConvParser(string_view format, ConvList args, bool is_positional) - : format_(format), - args_(args), - error_(false), - arg_position_(0), - is_positional_(is_positional) {} - - // Consume the whole conversion specifier. - // `format()` will be set to the character after the conversion character. - // `error()` will be set if any of the arguments do not match. - constexpr ConvParser Run() const { - return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this) - .ParseFlags() - .ParseWidth() - .ParsePrecision() - .ParseLength() - .ParseConversion(); - } - - constexpr string_view format() const { return format_; } - constexpr ConvList args() const { return args_; } - constexpr bool error() const { return error_; } - constexpr bool is_positional() const { return is_positional_; } - - private: - string_view format_; - // Current list of arguments. If we are not in positional mode we will consume - // from the front. - ConvList args_; - bool error_; - // Holds the argument position of the conversion character, if we are in - // positional mode. Otherwise, it is unspecified. - int arg_position_; - // Whether we are in positional mode. - // It changes the behavior of '*' and where to find the converted argument. - bool is_positional_; -}; - -// Parses a whole format expression. -// See FormatParser::Run(). -class FormatParser { - static constexpr bool FoundPercent(string_view format) { - return format.empty() || - (GetChar(format, 0) == '%' && GetChar(format, 1) != '%'); - } - - // We use an inner function to increase the recursion limit. - // The inner function consumes up to `limit` characters on every run. - // This increases the limit from 512 to ~512*limit. - static constexpr string_view ConsumeNonPercentInner(string_view format, - int limit = 20) { - return FoundPercent(format) || !limit - ? format - : ConsumeNonPercentInner( - ConsumeFront(format, GetChar(format, 0) == '%' && - GetChar(format, 1) == '%' - ? 2 - : 1), - limit - 1); - } - - // Consume characters until the next conversion spec %. - // It skips %%. - static constexpr string_view ConsumeNonPercent(string_view format) { - return FoundPercent(format) - ? format - : ConsumeNonPercent(ConsumeNonPercentInner(format)); - } - - static constexpr bool IsPositional(string_view format) { - return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format)) - : GetChar(format, 0) == '$'; - } - - constexpr bool RunImpl(bool is_positional) const { - // In non-positional mode we require all arguments to be consumed. - // In positional mode just reaching the end of the format without errors is - // enough. - return (format_.empty() && (is_positional || args_.count == 0)) || - (!format_.empty() && - ValidateArg( - ConvParser(ConsumeFront(format_), args_, is_positional).Run())); - } - - constexpr bool ValidateArg(ConvParser conv) const { - return !conv.error() && FormatParser(conv.format(), conv.args()) - .RunImpl(conv.is_positional()); - } - - public: - constexpr FormatParser(string_view format, ConvList args) - : format_(ConsumeNonPercent(format)), args_(args) {} - - // Runs the parser for `format` and `args`. - // It verifies that the format is valid and that all conversion specifiers - // match the arguments passed. - // In non-positional mode it also verfies that all arguments are consumed. - constexpr bool Run() const { - return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_))); - } - - private: - string_view format_; - // Current list of arguments. - // If we are not in positional mode we will consume from the front and will - // have to be empty in the end. - ConvList args_; -}; - template <FormatConversionCharSet... C> constexpr bool ValidFormatImpl(string_view format) { - return FormatParser(format, - {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)}) - .Run(); + int next_arg = 0; + const char* p = format.data(); + const char* const end = p + format.size(); + constexpr FormatConversionCharSet + kAllowedConvs[(std::max)(sizeof...(C), size_t{1})] = {C...}; + bool used[(std::max)(sizeof...(C), size_t{1})]{}; + constexpr int kNumArgs = sizeof...(C); + while (p != end) { + while (p != end && *p != '%') ++p; + if (p == end) { + break; + } + if (p + 1 >= end) return false; + if (p[1] == '%') { + // %% + p += 2; + continue; + } + + UnboundConversion conv(y_absl::kConstInit); + p = ConsumeUnboundConversion(p + 1, end, &conv, &next_arg); + if (p == nullptr) return false; + if (conv.arg_position <= 0 || conv.arg_position > kNumArgs) { + return false; + } + if (!Contains(kAllowedConvs[conv.arg_position - 1], conv.conv)) { + return false; + } + used[conv.arg_position - 1] = true; + for (auto extra : {conv.width, conv.precision}) { + if (extra.is_from_arg()) { + int pos = extra.get_from_arg(); + if (pos <= 0 || pos > kNumArgs) return false; + used[pos - 1] = true; + if (!Contains(kAllowedConvs[pos - 1], '*')) { + return false; + } + } + } + } + if (sizeof...(C) != 0) { + for (bool b : used) { + if (!b) return false; + } + } + return true; } #endif // Y_ABSL_INTERNAL_ENABLE_FORMAT_CHECKER diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/constexpr_parser.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/constexpr_parser.h new file mode 100644 index 0000000000..d3cc23a00c --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/constexpr_parser.h @@ -0,0 +1,351 @@ +// Copyright 2022 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 Y_ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_ +#define Y_ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_ + +#include <cassert> +#include <cstdint> +#include <limits> + +#include "y_absl/base/const_init.h" +#include "y_absl/strings/internal/str_format/extension.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace str_format_internal { + +enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none }; + +// The analyzed properties of a single specified conversion. +struct UnboundConversion { + // This is a user defined default constructor on purpose to skip the + // initialization of parts of the object that are not necessary. + UnboundConversion() {} // NOLINT + + // This constructor is provided for the static checker. We don't want to do + // the unnecessary initialization in the normal case. + explicit constexpr UnboundConversion(y_absl::ConstInitType) + : arg_position{}, width{}, precision{} {} + + class InputValue { + public: + constexpr void set_value(int value) { + assert(value >= 0); + value_ = value; + } + constexpr int value() const { return value_; } + + // Marks the value as "from arg". aka the '*' format. + // Requires `value >= 1`. + // When set, is_from_arg() return true and get_from_arg() returns the + // original value. + // `value()`'s return value is unspecified in this state. + constexpr void set_from_arg(int value) { + assert(value > 0); + value_ = -value - 1; + } + constexpr bool is_from_arg() const { return value_ < -1; } + constexpr int get_from_arg() const { + assert(is_from_arg()); + return -value_ - 1; + } + + private: + int value_ = -1; + }; + + // No need to initialize. It will always be set in the parser. + int arg_position; + + InputValue width; + InputValue precision; + + Flags flags = Flags::kBasic; + LengthMod length_mod = LengthMod::none; + FormatConversionChar conv = FormatConversionCharInternal::kNone; +}; + +// Helper tag class for the table below. +// It allows fast `char -> ConversionChar/LengthMod/Flags` checking and +// conversions. +class ConvTag { + public: + constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT + : tag_(static_cast<uint8_t>(conversion_char)) {} + constexpr ConvTag(LengthMod length_mod) // NOLINT + : tag_(0x80 | static_cast<uint8_t>(length_mod)) {} + constexpr ConvTag(Flags flags) // NOLINT + : tag_(0xc0 | static_cast<uint8_t>(flags)) {} + constexpr ConvTag() : tag_(0xFF) {} + + constexpr bool is_conv() const { return (tag_ & 0x80) == 0; } + constexpr bool is_length() const { return (tag_ & 0xC0) == 0x80; } + constexpr bool is_flags() const { return (tag_ & 0xE0) == 0xC0; } + + constexpr FormatConversionChar as_conv() const { + assert(is_conv()); + assert(!is_length()); + assert(!is_flags()); + return static_cast<FormatConversionChar>(tag_); + } + constexpr LengthMod as_length() const { + assert(!is_conv()); + assert(is_length()); + assert(!is_flags()); + return static_cast<LengthMod>(tag_ & 0x3F); + } + constexpr Flags as_flags() const { + assert(!is_conv()); + assert(!is_length()); + assert(is_flags()); + return static_cast<Flags>(tag_ & 0x1F); + } + + private: + uint8_t tag_; +}; + +struct ConvTagHolder { + using CC = FormatConversionCharInternal; + using LM = LengthMod; + + // Abbreviations to fit in the table below. + static constexpr auto kFSign = Flags::kSignCol; + static constexpr auto kFAlt = Flags::kAlt; + static constexpr auto kFPos = Flags::kShowPos; + static constexpr auto kFLeft = Flags::kLeft; + static constexpr auto kFZero = Flags::kZero; + + static constexpr ConvTag value[256] = { + {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 + {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f + {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 + {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f + kFSign, {}, {}, kFAlt, {}, {}, {}, {}, // !"#$%&' + {}, {}, {}, kFPos, {}, kFLeft, {}, {}, // ()*+,-./ + kFZero, {}, {}, {}, {}, {}, {}, {}, // 01234567 + {}, {}, {}, {}, {}, {}, {}, {}, // 89:;<=>? + {}, CC::A, {}, {}, {}, CC::E, CC::F, CC::G, // @ABCDEFG + {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO + {}, {}, {}, {}, {}, {}, {}, {}, // PQRSTUVW + CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ + {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg + LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno + CC::p, LM::q, {}, CC::s, LM::t, CC::u, CC::v, {}, // pqrstuvw + CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! + {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 + {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f + {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 + {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f + {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 + {}, {}, {}, {}, {}, {}, {}, {}, // a8-af + {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 + {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf + {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 + {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf + {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 + {}, {}, {}, {}, {}, {}, {}, {}, // d8-df + {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 + {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef + {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 + {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff + }; +}; + +// Keep a single table for all the conversion chars and length modifiers. +constexpr ConvTag GetTagForChar(char c) { + return ConvTagHolder::value[static_cast<unsigned char>(c)]; +} + +constexpr bool CheckFastPathSetting(const UnboundConversion& conv) { + bool width_precision_needed = + conv.width.value() >= 0 || conv.precision.value() >= 0; + if (width_precision_needed && conv.flags == Flags::kBasic) { +#if defined(__clang__) + // Some compilers complain about this in constexpr even when not executed, + // so only enable the error dump in clang. + fprintf(stderr, + "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d " + "width=%d precision=%d\n", + conv.flags == Flags::kBasic ? 1 : 0, + FlagsContains(conv.flags, Flags::kLeft) ? 1 : 0, + FlagsContains(conv.flags, Flags::kShowPos) ? 1 : 0, + FlagsContains(conv.flags, Flags::kSignCol) ? 1 : 0, + FlagsContains(conv.flags, Flags::kAlt) ? 1 : 0, + FlagsContains(conv.flags, Flags::kZero) ? 1 : 0, conv.width.value(), + conv.precision.value()); +#endif // defined(__clang__) + return false; + } + return true; +} + +constexpr int ParseDigits(char& c, const char*& pos, const char* const end) { + int digits = c - '0'; + // We do not want to overflow `digits` so we consume at most digits10 + // digits. If there are more digits the parsing will fail later on when the + // digit doesn't match the expected characters. + int num_digits = std::numeric_limits<int>::digits10; + for (;;) { + if (Y_ABSL_PREDICT_FALSE(pos == end)) break; + c = *pos++; + if ('0' > c || c > '9') break; + --num_digits; + if (Y_ABSL_PREDICT_FALSE(!num_digits)) break; + digits = 10 * digits + c - '0'; + } + return digits; +} + +template <bool is_positional> +constexpr const char* ConsumeConversion(const char* pos, const char* const end, + UnboundConversion* conv, + int* next_arg) { + const char* const original_pos = pos; + char c = 0; + // Read the next char into `c` and update `pos`. Returns false if there are + // no more chars to read. +#define Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR() \ + do { \ + if (Y_ABSL_PREDICT_FALSE(pos == end)) return nullptr; \ + c = *pos++; \ + } while (0) + + if (is_positional) { + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if (Y_ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; + conv->arg_position = ParseDigits(c, pos, end); + assert(conv->arg_position > 0); + if (Y_ABSL_PREDICT_FALSE(c != '$')) return nullptr; + } + + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + + // We should start with the basic flag on. + assert(conv->flags == Flags::kBasic); + + // Any non alpha character makes this conversion not basic. + // This includes flags (-+ #0), width (1-9, *) or precision (.). + // All conversion characters and length modifiers are alpha characters. + if (c < 'A') { + while (c <= '0') { + auto tag = GetTagForChar(c); + if (tag.is_flags()) { + conv->flags = conv->flags | tag.as_flags(); + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + break; + } + } + + if (c <= '9') { + if (c >= '0') { + int maybe_width = ParseDigits(c, pos, end); + if (!is_positional && c == '$') { + if (Y_ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr; + // Positional conversion. + *next_arg = -1; + return ConsumeConversion<true>(original_pos, end, conv, next_arg); + } + conv->flags = conv->flags | Flags::kNonBasic; + conv->width.set_value(maybe_width); + } else if (c == '*') { + conv->flags = conv->flags | Flags::kNonBasic; + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if (is_positional) { + if (Y_ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; + conv->width.set_from_arg(ParseDigits(c, pos, end)); + if (Y_ABSL_PREDICT_FALSE(c != '$')) return nullptr; + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + conv->width.set_from_arg(++*next_arg); + } + } + } + + if (c == '.') { + conv->flags = conv->flags | Flags::kNonBasic; + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if ('0' <= c && c <= '9') { + conv->precision.set_value(ParseDigits(c, pos, end)); + } else if (c == '*') { + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if (is_positional) { + if (Y_ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; + conv->precision.set_from_arg(ParseDigits(c, pos, end)); + if (c != '$') return nullptr; + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + conv->precision.set_from_arg(++*next_arg); + } + } else { + conv->precision.set_value(0); + } + } + } + + auto tag = GetTagForChar(c); + + if (Y_ABSL_PREDICT_FALSE(c == 'v' && conv->flags != Flags::kBasic)) { + return nullptr; + } + + if (Y_ABSL_PREDICT_FALSE(!tag.is_conv())) { + if (Y_ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr; + + // It is a length modifier. + using str_format_internal::LengthMod; + LengthMod length_mod = tag.as_length(); + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if (c == 'h' && length_mod == LengthMod::h) { + conv->length_mod = LengthMod::hh; + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else if (c == 'l' && length_mod == LengthMod::l) { + conv->length_mod = LengthMod::ll; + Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + conv->length_mod = length_mod; + } + tag = GetTagForChar(c); + + if (Y_ABSL_PREDICT_FALSE(c == 'v')) return nullptr; + if (Y_ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr; + } + + assert(CheckFastPathSetting(*conv)); + (void)(&CheckFastPathSetting); + + conv->conv = tag.as_conv(); + if (!is_positional) conv->arg_position = ++*next_arg; + return pos; +} + +// Consume conversion spec prefix (not including '%') of [p, end) if valid. +// Examples of valid specs would be e.g.: "s", "d", "-12.6f". +// If valid, it returns the first character following the conversion spec, +// and the spec part is broken down and returned in 'conv'. +// If invalid, returns nullptr. +constexpr 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); + return ConsumeConversion<false>(p, end, conv, next_arg); +} + +} // namespace str_format_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/extension.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/extension.cc index db6182f35d..01585b581f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/extension.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/extension.cc @@ -58,7 +58,8 @@ constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer; bool FormatSinkImpl::PutPaddedString(string_view value, int width, int precision, bool left) { size_t space_remaining = 0; - if (width >= 0) space_remaining = width; + if (width >= 0) + space_remaining = static_cast<size_t>(width); size_t n = value.size(); if (precision >= 0) n = std::min(n, static_cast<size_t>(precision)); string_view shown(value.data(), n); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/extension.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/extension.h index cc1a5d6bad..01a2063b17 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/extension.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/extension.h @@ -169,7 +169,7 @@ inline std::ostream& operator<<(std::ostream& os, Flags v) { X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \ 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) + X_VAL(n) X_SEP X_VAL(p) X_SEP X_VAL(v) // clang-format on // This type should not be referenced, it exists only to provide labels @@ -191,7 +191,7 @@ struct FormatConversionCharInternal { c, s, // text d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float - n, p, // misc + n, p, v, // misc kNone }; // clang-format on @@ -292,6 +292,8 @@ class FormatConversionSpecImpl { return conv_; } + void set_conversion_char(FormatConversionChar c) { conv_ = c; } + // Returns the specified width. If width is unspecfied, it returns a negative // value. int width() const { return width_; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.cc index 4b3ff2f652..b109e93103 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.cc @@ -92,27 +92,30 @@ class StackArray { // Calculates `10 * (*v) + carry` and stores the result in `*v` and returns // the carry. +// Requires: `0 <= carry <= 9` template <typename Int> -inline Int MultiplyBy10WithCarry(Int *v, Int carry) { +inline char MultiplyBy10WithCarry(Int* v, char carry) { using BiggerInt = y_absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; - BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + carry; + BiggerInt tmp = + 10 * static_cast<BiggerInt>(*v) + static_cast<BiggerInt>(carry); *v = static_cast<Int>(tmp); - return static_cast<Int>(tmp >> (sizeof(Int) * 8)); + return static_cast<char>(tmp >> (sizeof(Int) * 8)); } // Calculates `(2^64 * carry + *v) / 10`. // Stores the quotient in `*v` and returns the remainder. // Requires: `0 <= carry <= 9` -inline uint64_t DivideBy10WithCarry(uint64_t *v, uint64_t carry) { +inline char DivideBy10WithCarry(uint64_t* v, char carry) { constexpr uint64_t divisor = 10; // 2^64 / divisor = chunk_quotient + chunk_remainder / divisor constexpr uint64_t chunk_quotient = (uint64_t{1} << 63) / (divisor / 2); constexpr uint64_t chunk_remainder = uint64_t{} - chunk_quotient * divisor; + const uint64_t carry_u64 = static_cast<uint64_t>(carry); const uint64_t mod = *v % divisor; - const uint64_t next_carry = chunk_remainder * carry + mod; - *v = *v / divisor + carry * chunk_quotient + next_carry / divisor; - return next_carry % divisor; + const uint64_t next_carry = chunk_remainder * carry_u64 + mod; + *v = *v / divisor + carry_u64 * chunk_quotient + next_carry / divisor; + return static_cast<char>(next_carry % divisor); } using MaxFloatType = @@ -125,11 +128,11 @@ using MaxFloatType = // // Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`. class BinaryToDecimal { - static constexpr int ChunksNeeded(int exp) { + static constexpr size_t ChunksNeeded(int exp) { // We will left shift a uint128 by `exp` bits, so we need `128+exp` total // bits. Round up to 32. // See constructor for details about adding `10%` to the value. - return (128 + exp + 31) / 32 * 11 / 10; + return static_cast<size_t>((128 + exp + 31) / 32 * 11 / 10); } public: @@ -140,7 +143,7 @@ class BinaryToDecimal { assert(exp > 0); assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent); static_assert( - static_cast<int>(StackArray::kMaxCapacity) >= + StackArray::kMaxCapacity >= ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent), ""); @@ -149,9 +152,9 @@ class BinaryToDecimal { [=](y_absl::Span<uint32_t> input) { f(BinaryToDecimal(input, v, exp)); }); } - int TotalDigits() const { - return static_cast<int>((decimal_end_ - decimal_start_) * kDigitsPerChunk + - CurrentDigits().size()); + size_t TotalDigits() const { + return (decimal_end_ - decimal_start_) * kDigitsPerChunk + + CurrentDigits().size(); } // See the current block of digits. @@ -190,30 +193,31 @@ class BinaryToDecimal { // the decimal representation is around 7% less efficient in space than the // binary one. We allocate an extra 10% memory to account for this. See // ChunksNeeded for this calculation. - int chunk_index = exp / 32; + size_t after_chunk_index = static_cast<size_t>(exp / 32 + 1); decimal_start_ = decimal_end_ = ChunksNeeded(exp); const int offset = exp % 32; // Left shift v by exp bits. - data_[chunk_index] = static_cast<uint32_t>(v << offset); + data_[after_chunk_index - 1] = static_cast<uint32_t>(v << offset); for (v >>= (32 - offset); v; v >>= 32) - data_[++chunk_index] = static_cast<uint32_t>(v); + data_[++after_chunk_index - 1] = static_cast<uint32_t>(v); - while (chunk_index >= 0) { + while (after_chunk_index > 0) { // While we have more than one chunk available, go in steps of 1e9. - // `data_[chunk_index]` holds the highest non-zero binary chunk, so keep - // the variable updated. + // `data_[after_chunk_index - 1]` holds the highest non-zero binary chunk, + // so keep the variable updated. uint32_t carry = 0; - for (int i = chunk_index; i >= 0; --i) { - uint64_t tmp = uint64_t{data_[i]} + (uint64_t{carry} << 32); - data_[i] = static_cast<uint32_t>(tmp / uint64_t{1000000000}); + for (size_t i = after_chunk_index; i > 0; --i) { + uint64_t tmp = uint64_t{data_[i - 1]} + (uint64_t{carry} << 32); + data_[i - 1] = static_cast<uint32_t>(tmp / uint64_t{1000000000}); carry = static_cast<uint32_t>(tmp % uint64_t{1000000000}); } // If the highest chunk is now empty, remove it from view. - if (data_[chunk_index] == 0) --chunk_index; + if (data_[after_chunk_index - 1] == 0) + --after_chunk_index; --decimal_start_; - assert(decimal_start_ != chunk_index); + assert(decimal_start_ != after_chunk_index - 1); data_[decimal_start_] = carry; } @@ -225,13 +229,13 @@ class BinaryToDecimal { } private: - static constexpr int kDigitsPerChunk = 9; + static constexpr size_t kDigitsPerChunk = 9; - int decimal_start_; - int decimal_end_; + size_t decimal_start_; + size_t decimal_end_; char digits_[kDigitsPerChunk]; - int size_ = 0; + size_t size_ = 0; y_absl::Span<uint32_t> data_; }; @@ -251,25 +255,26 @@ class FractionalDigitGenerator { static_assert(StackArray::kMaxCapacity >= (Limits::digits + 128 - Limits::min_exponent + 31) / 32, ""); - StackArray::RunWithCapacity((Limits::digits + exp + 31) / 32, - [=](y_absl::Span<uint32_t> input) { - f(FractionalDigitGenerator(input, v, exp)); - }); + StackArray::RunWithCapacity( + static_cast<size_t>((Limits::digits + exp + 31) / 32), + [=](y_absl::Span<uint32_t> input) { + f(FractionalDigitGenerator(input, v, exp)); + }); } // Returns true if there are any more non-zero digits left. - bool HasMoreDigits() const { return next_digit_ != 0 || chunk_index_ >= 0; } + bool HasMoreDigits() const { return next_digit_ != 0 || after_chunk_index_; } // Returns true if the remainder digits are greater than 5000... bool IsGreaterThanHalf() const { - return next_digit_ > 5 || (next_digit_ == 5 && chunk_index_ >= 0); + return next_digit_ > 5 || (next_digit_ == 5 && after_chunk_index_); } // Returns true if the remainder digits are exactly 5000... - bool IsExactlyHalf() const { return next_digit_ == 5 && chunk_index_ < 0; } + bool IsExactlyHalf() const { return next_digit_ == 5 && !after_chunk_index_; } struct Digits { - int digit_before_nine; - int num_nines; + char digit_before_nine; + size_t num_nines; }; // Get the next set of digits. @@ -288,35 +293,37 @@ class FractionalDigitGenerator { private: // Return the next digit. - int GetOneDigit() { - if (chunk_index_ < 0) return 0; + char GetOneDigit() { + if (!after_chunk_index_) + return 0; - uint32_t carry = 0; - for (int i = chunk_index_; i >= 0; --i) { - carry = MultiplyBy10WithCarry(&data_[i], carry); + char carry = 0; + for (size_t i = after_chunk_index_; i > 0; --i) { + carry = MultiplyBy10WithCarry(&data_[i - 1], carry); } // If the lowest chunk is now empty, remove it from view. - if (data_[chunk_index_] == 0) --chunk_index_; + if (data_[after_chunk_index_ - 1] == 0) + --after_chunk_index_; return carry; } FractionalDigitGenerator(y_absl::Span<uint32_t> data, uint128 v, int exp) - : chunk_index_(exp / 32), data_(data) { + : after_chunk_index_(static_cast<size_t>(exp / 32 + 1)), data_(data) { const int offset = exp % 32; // Right shift `v` by `exp` bits. - data_[chunk_index_] = static_cast<uint32_t>(v << (32 - offset)); + data_[after_chunk_index_ - 1] = static_cast<uint32_t>(v << (32 - offset)); v >>= offset; // Make sure we don't overflow the data. We already calculated that // non-zero bits fit, so we might not have space for leading zero bits. - for (int pos = chunk_index_; v; v >>= 32) + for (size_t pos = after_chunk_index_ - 1; v; v >>= 32) data_[--pos] = static_cast<uint32_t>(v); // Fill next_digit_, as GetDigits expects it to be populated always. next_digit_ = GetOneDigit(); } - int next_digit_; - int chunk_index_; + char next_digit_; + size_t after_chunk_index_; y_absl::Span<uint32_t> data_; }; @@ -362,7 +369,7 @@ char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) { auto low = static_cast<uint64_t>(v); while (high != 0) { - uint64_t carry = DivideBy10WithCarry(&high, 0); + char carry = DivideBy10WithCarry(&high, 0); carry = DivideBy10WithCarry(&low, carry); *--p = carry + '0'; } @@ -373,13 +380,15 @@ char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) { // shifting. // Performs rounding if necessary to fit within `precision`. // Returns the pointer to one after the last character written. -char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp, - int precision) { +char* PrintFractionalDigitsFast(uint64_t v, + char* start, + int exp, + size_t precision) { char *p = start; v <<= (64 - exp); while (precision > 0) { if (!v) return p; - *p++ = MultiplyBy10WithCarry(&v, uint64_t{0}) + '0'; + *p++ = MultiplyBy10WithCarry(&v, 0) + '0'; --precision; } @@ -393,8 +402,6 @@ char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp, RoundToEven(p - 1); } - assert(precision == 0); - // Precision can only be zero here. return p; } @@ -402,8 +409,10 @@ char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp, // after shifting. // Performs rounding if necessary to fit within `precision`. // Returns the pointer to one after the last character written. -char *PrintFractionalDigitsFast(uint128 v, char *start, int exp, - int precision) { +char* PrintFractionalDigitsFast(uint128 v, + char* start, + int exp, + size_t precision) { char *p = start; v <<= (128 - exp); auto high = static_cast<uint64_t>(v >> 64); @@ -412,7 +421,7 @@ char *PrintFractionalDigitsFast(uint128 v, char *start, int exp, // While we have digits to print and `low` is not empty, do the long // multiplication. while (precision > 0 && low != 0) { - uint64_t carry = MultiplyBy10WithCarry(&low, uint64_t{0}); + char carry = MultiplyBy10WithCarry(&low, 0); carry = MultiplyBy10WithCarry(&high, carry); *p++ = carry + '0'; @@ -424,7 +433,7 @@ char *PrintFractionalDigitsFast(uint128 v, char *start, int exp, // above. while (precision > 0) { if (!high) return p; - *p++ = MultiplyBy10WithCarry(&high, uint64_t{0}) + '0'; + *p++ = MultiplyBy10WithCarry(&high, 0) + '0'; --precision; } @@ -438,14 +447,12 @@ char *PrintFractionalDigitsFast(uint128 v, char *start, int exp, RoundToEven(p - 1); } - assert(precision == 0); - // Precision can only be zero here. return p; } struct FormatState { char sign_char; - int precision; + size_t precision; const FormatConversionSpecImpl &conv; FormatSinkImpl *sink; @@ -455,9 +462,9 @@ struct FormatState { }; struct Padding { - int left_spaces; - int zeros; - int right_spaces; + size_t left_spaces; + size_t zeros; + size_t right_spaces; }; Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { @@ -465,7 +472,7 @@ Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { static_cast<size_t>(state.conv.width()) <= total_size) { return {0, 0, 0}; } - int missing_chars = state.conv.width() - total_size; + size_t missing_chars = static_cast<size_t>(state.conv.width()) - total_size; if (state.conv.has_left_flag()) { return {0, 0, missing_chars}; } else if (state.conv.has_zero_flag()) { @@ -475,8 +482,10 @@ Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { } } -void FinalPrint(const FormatState &state, y_absl::string_view data, - int padding_offset, int trailing_zeros, +void FinalPrint(const FormatState& state, + y_absl::string_view data, + size_t padding_offset, + size_t trailing_zeros, y_absl::string_view data_postfix) { if (state.conv.width() < 0) { // No width specified. Fast-path. @@ -487,10 +496,10 @@ void FinalPrint(const FormatState &state, y_absl::string_view data, return; } - auto padding = ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + - data.size() + data_postfix.size() + - static_cast<size_t>(trailing_zeros), - state); + auto padding = + ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + data.size() + + data_postfix.size() + trailing_zeros, + state); state.sink->Append(padding.left_spaces, ' '); if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); @@ -547,15 +556,16 @@ void FormatFFast(Int v, int exp, const FormatState &state) { if (integral_digits_start[-1] != '0') --integral_digits_start; } - size_t size = fractional_digits_end - integral_digits_start; + size_t size = + static_cast<size_t>(fractional_digits_end - integral_digits_start); // In `alt` mode (flag #) we keep the `.` even if there are no fractional // digits. In non-alt mode, we strip it. if (!state.ShouldPrintDot()) --size; FinalPrint(state, y_absl::string_view(integral_digits_start, size), /*padding_offset=*/0, - static_cast<int>(state.precision - (fractional_digits_end - - fractional_digits_start)), + state.precision - static_cast<size_t>(fractional_digits_end - + fractional_digits_start), /*data_postfix=*/""); } @@ -567,21 +577,22 @@ void FormatFFast(Int v, int exp, const FormatState &state) { void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) { BinaryToDecimal::RunConversion(v, exp, [&](BinaryToDecimal btd) { const size_t total_digits = - btd.TotalDigits() + - (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0); + btd.TotalDigits() + (state.ShouldPrintDot() ? state.precision + 1 : 0); const auto padding = ExtraWidthToPadding( total_digits + (state.sign_char != '\0' ? 1 : 0), state); state.sink->Append(padding.left_spaces, ' '); - if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + if (state.sign_char != '\0') + state.sink->Append(1, state.sign_char); state.sink->Append(padding.zeros, '0'); do { state.sink->Append(btd.CurrentDigits()); } while (btd.AdvanceDigits()); - if (state.ShouldPrintDot()) state.sink->Append(1, '.'); + if (state.ShouldPrintDot()) + state.sink->Append(1, '.'); state.sink->Append(state.precision, '0'); state.sink->Append(padding.right_spaces, ' '); }); @@ -594,8 +605,7 @@ void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) { // digits. void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) { const size_t total_digits = - /* 0 */ 1 + - (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0); + /* 0 */ 1 + (state.ShouldPrintDot() ? state.precision + 1 : 0); auto padding = ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state); padding.zeros += 1; @@ -606,7 +616,7 @@ void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) { if (state.ShouldPrintDot()) state.sink->Append(1, '.'); // Print digits - int digits_to_go = state.precision; + size_t digits_to_go = state.precision; FractionalDigitGenerator::RunConversion( v, exp, [&](FractionalDigitGenerator digit_gen) { @@ -666,7 +676,8 @@ void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) { template <typename Int> void FormatF(Int mantissa, int exp, const FormatState &state) { if (exp >= 0) { - const int total_bits = sizeof(Int) * 8 - LeadingZeros(mantissa) + exp; + const int total_bits = + static_cast<int>(sizeof(Int) * 8) - LeadingZeros(mantissa) + exp; // Fallback to the slow stack-based approach if we can't do it in a 64 or // 128 bit state. @@ -686,9 +697,9 @@ void FormatF(Int mantissa, int exp, const FormatState &state) { // Grab the group of four bits (nibble) from `n`. E.g., nibble 1 corresponds to // bits 4-7. template <typename Int> -uint8_t GetNibble(Int n, int nibble_index) { +uint8_t GetNibble(Int n, size_t nibble_index) { constexpr Int mask_low_nibble = Int{0xf}; - int shift = nibble_index * 4; + int shift = static_cast<int>(nibble_index * 4); n &= mask_low_nibble << shift; return static_cast<uint8_t>((n >> shift) & 0xf); } @@ -696,9 +707,9 @@ uint8_t GetNibble(Int n, int nibble_index) { // Add one to the given nibble, applying carry to higher nibbles. Returns true // if overflow, false otherwise. template <typename Int> -bool IncrementNibble(int nibble_index, Int *n) { - constexpr int kShift = sizeof(Int) * 8 - 1; - constexpr int kNumNibbles = sizeof(Int) * 8 / 4; +bool IncrementNibble(size_t nibble_index, Int* n) { + constexpr size_t kShift = sizeof(Int) * 8 - 1; + constexpr size_t kNumNibbles = sizeof(Int) * 8 / 4; Int before = *n >> kShift; // Here we essentially want to take the number 1 and move it into the requsted // nibble, then add it to *n to effectively increment the nibble. However, @@ -706,28 +717,32 @@ bool IncrementNibble(int nibble_index, Int *n) { // i.e., if the nibble_index is out of range. So therefore we check for this // and if we are out of range we just add 0 which leaves *n unchanged, which // seems like the reasonable thing to do in that case. - *n += ((nibble_index >= kNumNibbles) ? 0 : (Int{1} << (nibble_index * 4))); + *n += ((nibble_index >= kNumNibbles) + ? 0 + : (Int{1} << static_cast<int>(nibble_index * 4))); Int after = *n >> kShift; return (before && !after) || (nibble_index >= kNumNibbles); } // Return a mask with 1's in the given nibble and all lower nibbles. template <typename Int> -Int MaskUpToNibbleInclusive(int nibble_index) { - constexpr int kNumNibbles = sizeof(Int) * 8 / 4; +Int MaskUpToNibbleInclusive(size_t nibble_index) { + constexpr size_t kNumNibbles = sizeof(Int) * 8 / 4; static const Int ones = ~Int{0}; - return ones >> std::max(0, 4 * (kNumNibbles - nibble_index - 1)); + ++nibble_index; + return ones >> static_cast<int>( + 4 * (std::max(kNumNibbles, nibble_index) - nibble_index)); } // Return a mask with 1's below the given nibble. template <typename Int> -Int MaskUpToNibbleExclusive(int nibble_index) { - return nibble_index <= 0 ? 0 : MaskUpToNibbleInclusive<Int>(nibble_index - 1); +Int MaskUpToNibbleExclusive(size_t nibble_index) { + return nibble_index == 0 ? 0 : MaskUpToNibbleInclusive<Int>(nibble_index - 1); } template <typename Int> -Int MoveToNibble(uint8_t nibble, int nibble_index) { - return Int{nibble} << (4 * nibble_index); +Int MoveToNibble(uint8_t nibble, size_t nibble_index) { + return Int{nibble} << static_cast<int>(4 * nibble_index); } // Given mantissa size, find optimal # of mantissa bits to put in initial digit. @@ -744,10 +759,10 @@ Int MoveToNibble(uint8_t nibble, int nibble_index) { // a multiple of four. Once again, the goal is to have all fractional digits // represent real precision. template <typename Float> -constexpr int HexFloatLeadingDigitSizeInBits() { +constexpr size_t HexFloatLeadingDigitSizeInBits() { return std::numeric_limits<Float>::digits % 4 > 0 - ? std::numeric_limits<Float>::digits % 4 - : 4; + ? static_cast<size_t>(std::numeric_limits<Float>::digits % 4) + : size_t{4}; } // This function captures the rounding behavior of glibc for hex float @@ -757,16 +772,17 @@ constexpr int HexFloatLeadingDigitSizeInBits() { // point that is not followed by 800000..., it disregards the parity and rounds // up if > 8 and rounds down if < 8. template <typename Int> -bool HexFloatNeedsRoundUp(Int mantissa, int final_nibble_displayed, +bool HexFloatNeedsRoundUp(Int mantissa, + size_t final_nibble_displayed, uint8_t leading) { // If the last nibble (hex digit) to be displayed is the lowest on in the // mantissa then that means that we don't have any further nibbles to inform // rounding, so don't round. - if (final_nibble_displayed <= 0) { + if (final_nibble_displayed == 0) { return false; } - int rounding_nibble_idx = final_nibble_displayed - 1; - constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; + size_t rounding_nibble_idx = final_nibble_displayed - 1; + constexpr size_t kTotalNibbles = sizeof(Int) * 8 / 4; assert(final_nibble_displayed <= kTotalNibbles); Int mantissa_up_to_rounding_nibble_inclusive = mantissa & MaskUpToNibbleInclusive<Int>(rounding_nibble_idx); @@ -793,7 +809,7 @@ struct HexFloatTypeParams { } int min_exponent; - int leading_digit_size_bits; + size_t leading_digit_size_bits; }; // Hex Float Rounding. First check if we need to round; if so, then we do that @@ -803,10 +819,12 @@ struct HexFloatTypeParams { template <typename Int> void FormatARound(bool precision_specified, const FormatState &state, uint8_t *leading, Int *mantissa, int *exp) { - constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; + constexpr size_t kTotalNibbles = sizeof(Int) * 8 / 4; // Index of the last nibble that we could display given precision. - int final_nibble_displayed = - precision_specified ? std::max(0, (kTotalNibbles - state.precision)) : 0; + size_t final_nibble_displayed = + precision_specified + ? (std::max(kTotalNibbles, state.precision) - state.precision) + : 0; if (HexFloatNeedsRoundUp(*mantissa, final_nibble_displayed, *leading)) { // Need to round up. bool overflow = IncrementNibble(final_nibble_displayed, mantissa); @@ -830,9 +848,9 @@ void FormatARound(bool precision_specified, const FormatState &state, template <typename Int> void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading, Int *mantissa, int *exp) { - constexpr int kIntBits = sizeof(Int) * 8; + constexpr size_t kIntBits = sizeof(Int) * 8; static const Int kHighIntBit = Int{1} << (kIntBits - 1); - const int kLeadDigitBitsCount = float_traits.leading_digit_size_bits; + const size_t kLeadDigitBitsCount = float_traits.leading_digit_size_bits; // Normalize mantissa so that highest bit set is in MSB position, unless we // get interrupted by the exponent threshold. while (*mantissa && !(*mantissa & kHighIntBit)) { @@ -846,18 +864,18 @@ void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading, } // Extract bits for leading digit then shift them away leaving the // fractional part. - *leading = - static_cast<uint8_t>(*mantissa >> (kIntBits - kLeadDigitBitsCount)); - *exp -= (*mantissa != 0) ? kLeadDigitBitsCount : *exp; - *mantissa <<= kLeadDigitBitsCount; + *leading = static_cast<uint8_t>( + *mantissa >> static_cast<int>(kIntBits - kLeadDigitBitsCount)); + *exp -= (*mantissa != 0) ? static_cast<int>(kLeadDigitBitsCount) : *exp; + *mantissa <<= static_cast<int>(kLeadDigitBitsCount); } template <typename Int> void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp, bool uppercase, const FormatState &state) { // Int properties. - constexpr int kIntBits = sizeof(Int) * 8; - constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; + constexpr size_t kIntBits = sizeof(Int) * 8; + constexpr size_t kTotalNibbles = sizeof(Int) * 8 / 4; // Did the user specify a precision explicitly? const bool precision_specified = state.conv.precision() >= 0; @@ -903,16 +921,19 @@ void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp, } // ============ Fractional Digits ============ - int digits_emitted = 0; + size_t digits_emitted = 0; while (mantissa > 0) { *digits_iter++ = digits[GetNibble(mantissa, kTotalNibbles - 1)]; mantissa <<= 4; ++digits_emitted; } - int trailing_zeros = - precision_specified ? state.precision - digits_emitted : 0; - assert(trailing_zeros >= 0); - auto digits_result = string_view(digits_buffer, digits_iter - digits_buffer); + size_t trailing_zeros = 0; + if (precision_specified) { + assert(state.precision >= digits_emitted); + trailing_zeros = state.precision - digits_emitted; + } + auto digits_result = string_view( + digits_buffer, static_cast<size_t>(digits_iter - digits_buffer)); // =============== Exponent ================== constexpr size_t kBufSizeForExpDecRepr = @@ -925,11 +946,11 @@ void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp, numbers_internal::FastIntToBuffer(exp < 0 ? -exp : exp, exp_buffer + 2); // ============ Assemble Result ============== - FinalPrint(state, // - digits_result, // 0xN.NNN... - 2, // offset in `data` to start padding if needed. - trailing_zeros, // num remaining mantissa padding zeros - exp_buffer); // exponent + FinalPrint(state, + digits_result, // 0xN.NNN... + 2, // offset of any padding + static_cast<size_t>(trailing_zeros), // remaining mantissa padding + exp_buffer); // exponent } char *CopyStringTo(y_absl::string_view v, char *out) { @@ -961,10 +982,10 @@ bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv, int n = snprintf(&space[0], space.size(), fmt, w, p, v); if (n < 0) return false; if (static_cast<size_t>(n) < space.size()) { - result = y_absl::string_view(space.data(), n); + result = y_absl::string_view(space.data(), static_cast<size_t>(n)); break; } - space.resize(n + 1); + space.resize(static_cast<size_t>(n) + 1); } sink->Append(result); return true; @@ -972,13 +993,13 @@ bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv, // 128-bits in decimal: ceil(128*log(2)/log(10)) // or std::numeric_limits<__uint128_t>::digits10 -constexpr int kMaxFixedPrecision = 39; +constexpr size_t kMaxFixedPrecision = 39; -constexpr int kBufferLength = /*sign*/ 1 + - /*integer*/ kMaxFixedPrecision + - /*point*/ 1 + - /*fraction*/ kMaxFixedPrecision + - /*exponent e+123*/ 5; +constexpr size_t kBufferLength = /*sign*/ 1 + + /*integer*/ kMaxFixedPrecision + + /*point*/ 1 + + /*fraction*/ kMaxFixedPrecision + + /*exponent e+123*/ 5; struct Buffer { void push_front(char c) { @@ -1001,7 +1022,7 @@ struct Buffer { char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; } - int size() const { return static_cast<int>(end - begin); } + size_t size() const { return static_cast<size_t>(end - begin); } char data[kBufferLength]; char *begin; @@ -1030,8 +1051,9 @@ bool ConvertNonNumericFloats(char sign_char, Float v, return false; } - return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, - conv.has_left_flag()); + return sink->PutPaddedString( + string_view(text, static_cast<size_t>(ptr - text)), conv.width(), -1, + conv.has_left_flag()); } // Round up the last digit of the value. @@ -1068,12 +1090,12 @@ void PrintExponent(int exp, char e, Buffer *out) { } // Exponent digits. if (exp > 99) { - out->push_back(exp / 100 + '0'); - out->push_back(exp / 10 % 10 + '0'); - out->push_back(exp % 10 + '0'); + out->push_back(static_cast<char>(exp / 100 + '0')); + out->push_back(static_cast<char>(exp / 10 % 10 + '0')); + out->push_back(static_cast<char>(exp % 10 + '0')); } else { - out->push_back(exp / 10 + '0'); - out->push_back(exp % 10 + '0'); + out->push_back(static_cast<char>(exp / 10 + '0')); + out->push_back(static_cast<char>(exp % 10 + '0')); } } @@ -1115,8 +1137,8 @@ Decomposed<Float> Decompose(Float v) { // In Fixed mode, we add a '.' at the end. // In Precision mode, we add a '.' after the first digit. template <FormatStyle mode, typename Int> -int PrintIntegralDigits(Int digits, Buffer *out) { - int printed = 0; +size_t PrintIntegralDigits(Int digits, Buffer* out) { + size_t printed = 0; if (digits) { for (; digits; digits /= 10) out->push_front(digits % 10 + '0'); printed = out->size(); @@ -1135,10 +1157,10 @@ int PrintIntegralDigits(Int digits, Buffer *out) { } // Back out 'extra_digits' digits and round up if necessary. -bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value, - Buffer *out, int *exp_out) { - if (extra_digits <= 0) return false; - +void RemoveExtraPrecision(size_t extra_digits, + bool has_leftover_value, + Buffer* out, + int* exp_out) { // Back out the extra digits out->end -= extra_digits; @@ -1158,15 +1180,17 @@ bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value, if (needs_to_round_up) { RoundUp<FormatStyle::Precision>(out, exp_out); } - return true; } // Print the value into the buffer. // This will not include the exponent, which will be returned in 'exp_out' for // Precision mode. template <typename Int, typename Float, FormatStyle mode> -bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, - int *exp_out) { +bool FloatToBufferImpl(Int int_mantissa, + int exp, + size_t precision, + Buffer* out, + int* exp_out) { assert((CanFitMantissa<Float, Int>())); const int int_bits = std::numeric_limits<Int>::digits; @@ -1182,14 +1206,16 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, // The value will overflow the Int return false; } - int digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out); - int digits_to_zero_pad = precision; + size_t digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out); + size_t digits_to_zero_pad = precision; if (mode == FormatStyle::Precision) { - *exp_out = digits_printed - 1; - digits_to_zero_pad -= digits_printed - 1; - if (RemoveExtraPrecision(-digits_to_zero_pad, false, out, exp_out)) { + *exp_out = static_cast<int>(digits_printed - 1); + if (digits_to_zero_pad < digits_printed - 1) { + RemoveExtraPrecision(digits_printed - 1 - digits_to_zero_pad, false, + out, exp_out); return true; } + digits_to_zero_pad -= digits_printed - 1; } for (; digits_to_zero_pad-- > 0;) out->push_back('0'); return true; @@ -1203,10 +1229,10 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, const Int mask = (Int{1} << exp) - 1; // Print the integral part first. - int digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out); + size_t digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out); int_mantissa &= mask; - int fractional_count = precision; + size_t fractional_count = precision; if (mode == FormatStyle::Precision) { if (digits_printed == 0) { // Find the first non-zero digit, when in Precision mode. @@ -1222,20 +1248,21 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, int_mantissa &= mask; } else { // We already have a digit, and a '.' - *exp_out = digits_printed - 1; - fractional_count -= *exp_out; - if (RemoveExtraPrecision(-fractional_count, int_mantissa != 0, out, - exp_out)) { + *exp_out = static_cast<int>(digits_printed - 1); + if (fractional_count < digits_printed - 1) { // If we had enough digits, return right away. // The code below will try to round again otherwise. + RemoveExtraPrecision(digits_printed - 1 - fractional_count, + int_mantissa != 0, out, exp_out); return true; } + fractional_count -= digits_printed - 1; } } auto get_next_digit = [&] { int_mantissa *= 10; - int digit = static_cast<int>(int_mantissa >> exp); + char digit = static_cast<char>(int_mantissa >> exp); int_mantissa &= mask; return digit; }; @@ -1245,7 +1272,7 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, out->push_back(get_next_digit() + '0'); } - int next_digit = get_next_digit(); + char next_digit = get_next_digit(); if (next_digit > 5 || (next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) { RoundUp<mode>(out, exp_out); @@ -1255,24 +1282,25 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, } template <FormatStyle mode, typename Float> -bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out, - int *exp) { +bool FloatToBuffer(Decomposed<Float> decomposed, + size_t precision, + Buffer* out, + int* exp) { if (precision > kMaxFixedPrecision) return false; // Try with uint64_t. if (CanFitMantissa<Float, std::uint64_t>() && FloatToBufferImpl<std::uint64_t, Float, mode>( - static_cast<std::uint64_t>(decomposed.mantissa), - static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp)) + static_cast<std::uint64_t>(decomposed.mantissa), decomposed.exponent, + precision, out, exp)) return true; #if defined(Y_ABSL_HAVE_INTRINSIC_INT128) // If that is not enough, try with __uint128_t. return CanFitMantissa<Float, __uint128_t>() && FloatToBufferImpl<__uint128_t, Float, mode>( - static_cast<__uint128_t>(decomposed.mantissa), - static_cast<__uint128_t>(decomposed.exponent), precision, out, - exp); + static_cast<__uint128_t>(decomposed.mantissa), decomposed.exponent, + precision, out, exp); #endif return false; } @@ -1280,12 +1308,15 @@ bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out, void WriteBufferToSink(char sign_char, y_absl::string_view str, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { - int left_spaces = 0, zeros = 0, right_spaces = 0; - int missing_chars = - conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) - - static_cast<int>(sign_char != 0), - 0) - : 0; + size_t left_spaces = 0, zeros = 0, right_spaces = 0; + size_t missing_chars = 0; + if (conv.width() >= 0) { + const size_t conv_width_size_t = static_cast<size_t>(conv.width()); + const size_t existing_chars = + str.size() + static_cast<size_t>(sign_char != 0); + if (conv_width_size_t > existing_chars) + missing_chars = conv_width_size_t - existing_chars; + } if (conv.has_left_flag()) { right_spaces = missing_chars; } else if (conv.has_zero_flag()) { @@ -1321,7 +1352,8 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, return true; } - int precision = conv.precision() < 0 ? 6 : conv.precision(); + size_t precision = + conv.precision() < 0 ? 6 : static_cast<size_t>(conv.precision()); int exp = 0; @@ -1348,12 +1380,12 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, &buffer); } else if (c == FormatConversionCharInternal::g || c == FormatConversionCharInternal::G) { - precision = std::max(0, precision - 1); + precision = std::max(precision, size_t{1}) - 1; if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, &exp)) { return FallbackToSnprintf(v, conv, sink); } - if (precision + 1 > exp && exp >= -4) { + if ((exp < 0 || precision + 1 > static_cast<size_t>(exp)) && exp >= -4) { if (exp < 0) { // Have 1.23456, needs 0.00123456 // Move the first digit @@ -1388,9 +1420,11 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, return false; } - WriteBufferToSink(sign_char, - y_absl::string_view(buffer.begin, buffer.end - buffer.begin), - conv, sink); + WriteBufferToSink( + sign_char, + y_absl::string_view(buffer.begin, + static_cast<size_t>(buffer.end - buffer.begin)), + conv, sink); return true; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.cc index 76f68c9cb3..f10bae8483 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.cc @@ -31,207 +31,14 @@ namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace str_format_internal { -using CC = FormatConversionCharInternal; -using LM = LengthMod; +// Define the array for non-constexpr uses. +constexpr ConvTag ConvTagHolder::value[256]; -// Abbreviations to fit in the table below. -constexpr auto f_sign = Flags::kSignCol; -constexpr auto f_alt = Flags::kAlt; -constexpr auto f_pos = Flags::kShowPos; -constexpr auto f_left = Flags::kLeft; -constexpr auto f_zero = Flags::kZero; - -Y_ABSL_CONST_INIT const ConvTag kTags[256] = { - {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 - {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f - {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 - {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f - f_sign, {}, {}, f_alt, {}, {}, {}, {}, // !"#$%&' - {}, {}, {}, f_pos, {}, f_left, {}, {}, // ()*+,-./ - f_zero, {}, {}, {}, {}, {}, {}, {}, // 01234567 - {}, {}, {}, {}, {}, {}, {}, {}, // 89:;<=>? - {}, CC::A, {}, {}, {}, CC::E, CC::F, CC::G, // @ABCDEFG - {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO - {}, {}, {}, {}, {}, {}, {}, {}, // PQRSTUVW - CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ - {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg - LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno - CC::p, LM::q, {}, CC::s, LM::t, CC::u, {}, {}, // pqrstuvw - CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! - {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 - {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f - {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 - {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f - {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 - {}, {}, {}, {}, {}, {}, {}, {}, // a8-af - {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 - {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf - {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 - {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf - {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 - {}, {}, {}, {}, {}, {}, {}, {}, // d8-df - {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 - {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef - {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 - {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff -}; - -namespace { - -bool CheckFastPathSetting(const UnboundConversion& conv) { - bool width_precision_needed = - conv.width.value() >= 0 || conv.precision.value() >= 0; - if (width_precision_needed && conv.flags == Flags::kBasic) { - fprintf(stderr, - "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d " - "width=%d precision=%d\n", - conv.flags == Flags::kBasic ? 1 : 0, - FlagsContains(conv.flags, Flags::kLeft) ? 1 : 0, - FlagsContains(conv.flags, Flags::kShowPos) ? 1 : 0, - FlagsContains(conv.flags, Flags::kSignCol) ? 1 : 0, - FlagsContains(conv.flags, Flags::kAlt) ? 1 : 0, - FlagsContains(conv.flags, Flags::kZero) ? 1 : 0, conv.width.value(), - conv.precision.value()); - return false; - } - return true; -} - -template <bool is_positional> -const char *ConsumeConversion(const char *pos, const char *const end, - UnboundConversion *conv, int *next_arg) { - const char* const original_pos = pos; - char c; - // Read the next char into `c` and update `pos`. Returns false if there are - // no more chars to read. -#define Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR() \ - do { \ - if (Y_ABSL_PREDICT_FALSE(pos == end)) return nullptr; \ - c = *pos++; \ - } while (0) - - const auto parse_digits = [&] { - int digits = c - '0'; - // We do not want to overflow `digits` so we consume at most digits10 - // digits. If there are more digits the parsing will fail later on when the - // digit doesn't match the expected characters. - int num_digits = std::numeric_limits<int>::digits10; - for (;;) { - if (Y_ABSL_PREDICT_FALSE(pos == end)) break; - c = *pos++; - if (!std::isdigit(c)) break; - --num_digits; - if (Y_ABSL_PREDICT_FALSE(!num_digits)) break; - digits = 10 * digits + c - '0'; - } - return digits; - }; - - if (is_positional) { - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (Y_ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; - conv->arg_position = parse_digits(); - assert(conv->arg_position > 0); - if (Y_ABSL_PREDICT_FALSE(c != '$')) return nullptr; - } - - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - - // We should start with the basic flag on. - assert(conv->flags == Flags::kBasic); - - // Any non alpha character makes this conversion not basic. - // This includes flags (-+ #0), width (1-9, *) or precision (.). - // All conversion characters and length modifiers are alpha characters. - if (c < 'A') { - while (c <= '0') { - auto tag = GetTagForChar(c); - if (tag.is_flags()) { - conv->flags = conv->flags | tag.as_flags(); - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else { - break; - } - } - - if (c <= '9') { - if (c >= '0') { - int maybe_width = parse_digits(); - if (!is_positional && c == '$') { - if (Y_ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr; - // Positional conversion. - *next_arg = -1; - return ConsumeConversion<true>(original_pos, end, conv, next_arg); - } - conv->flags = conv->flags | Flags::kNonBasic; - conv->width.set_value(maybe_width); - } else if (c == '*') { - conv->flags = conv->flags | Flags::kNonBasic; - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (is_positional) { - if (Y_ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; - conv->width.set_from_arg(parse_digits()); - if (Y_ABSL_PREDICT_FALSE(c != '$')) return nullptr; - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else { - conv->width.set_from_arg(++*next_arg); - } - } - } - - if (c == '.') { - conv->flags = conv->flags | Flags::kNonBasic; - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (std::isdigit(c)) { - conv->precision.set_value(parse_digits()); - } else if (c == '*') { - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (is_positional) { - if (Y_ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; - conv->precision.set_from_arg(parse_digits()); - if (c != '$') return nullptr; - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else { - conv->precision.set_from_arg(++*next_arg); - } - } else { - conv->precision.set_value(0); - } - } - } - - auto tag = GetTagForChar(c); - - if (Y_ABSL_PREDICT_FALSE(!tag.is_conv())) { - if (Y_ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr; - - // It is a length modifier. - using str_format_internal::LengthMod; - LengthMod length_mod = tag.as_length(); - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (c == 'h' && length_mod == LengthMod::h) { - conv->length_mod = LengthMod::hh; - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else if (c == 'l' && length_mod == LengthMod::l) { - conv->length_mod = LengthMod::ll; - Y_ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else { - conv->length_mod = length_mod; - } - tag = GetTagForChar(c); - if (Y_ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr; - } - - assert(CheckFastPathSetting(*conv)); - (void)(&CheckFastPathSetting); - - conv->conv = tag.as_conv(); - if (!is_positional) conv->arg_position = ++*next_arg; - return pos; +Y_ABSL_ATTRIBUTE_NOINLINE const char* ConsumeUnboundConversionNoInline( + const char* p, const char* end, UnboundConversion* conv, int* next_arg) { + return ConsumeUnboundConversion(p, end, conv, next_arg); } -} // namespace - TString LengthModToString(LengthMod v) { switch (v) { case LengthMod::h: @@ -258,12 +65,6 @@ TString LengthModToString(LengthMod v) { 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); - return ConsumeConversion<false>(p, end, conv, next_arg); -} - struct ParsedFormatBase::ParsedFormatConsumer { explicit ParsedFormatConsumer(ParsedFormatBase *parsedformat) : parsed(parsedformat), data_pos(parsedformat->data_.get()) {} @@ -312,11 +113,11 @@ bool ParsedFormatBase::MatchesConversions( std::initializer_list<FormatConversionCharSet> convs) const { std::unordered_set<int> used; auto add_if_valid_conv = [&](int pos, char c) { - if (static_cast<size_t>(pos) > convs.size() || - !Contains(convs.begin()[pos - 1], c)) - return false; - used.insert(pos); - return true; + if (static_cast<size_t>(pos) > convs.size() || + !Contains(convs.begin()[pos - 1], c)) + return false; + used.insert(pos); + return true; }; for (const ConversionItem &item : items_) { if (!item.is_conversion) continue; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.h index 16bc0b12be..d3bf515b1f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/parser.h @@ -29,111 +29,18 @@ #include <vector> #include "y_absl/strings/internal/str_format/checker.h" +#include "y_absl/strings/internal/str_format/constexpr_parser.h" #include "y_absl/strings/internal/str_format/extension.h" namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace str_format_internal { -enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none }; - TString LengthModToString(LengthMod v); -// The analyzed properties of a single specified conversion. -struct UnboundConversion { - UnboundConversion() {} - - class InputValue { - public: - void set_value(int value) { - assert(value >= 0); - value_ = value; - } - int value() const { return value_; } - - // Marks the value as "from arg". aka the '*' format. - // Requires `value >= 1`. - // When set, is_from_arg() return true and get_from_arg() returns the - // original value. - // `value()`'s return value is unspecfied in this state. - void set_from_arg(int value) { - assert(value > 0); - value_ = -value - 1; - } - bool is_from_arg() const { return value_ < -1; } - int get_from_arg() const { - assert(is_from_arg()); - return -value_ - 1; - } - - private: - int value_ = -1; - }; - - // No need to initialize. It will always be set in the parser. - int arg_position; - - InputValue width; - InputValue precision; - - Flags flags = Flags::kBasic; - LengthMod length_mod = LengthMod::none; - FormatConversionChar conv = FormatConversionCharInternal::kNone; -}; - -// Consume conversion spec prefix (not including '%') of [p, end) if valid. -// Examples of valid specs would be e.g.: "s", "d", "-12.6f". -// If valid, it returns the first character following the conversion spec, -// and the spec part is broken down and returned in 'conv'. -// If invalid, returns nullptr. -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/Flags` checking and -// conversions. -class ConvTag { - public: - constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT - : tag_(static_cast<uint8_t>(conversion_char)) {} - constexpr ConvTag(LengthMod length_mod) // NOLINT - : tag_(0x80 | static_cast<uint8_t>(length_mod)) {} - constexpr ConvTag(Flags flags) // NOLINT - : tag_(0xc0 | static_cast<uint8_t>(flags)) {} - constexpr ConvTag() : tag_(0xFF) {} - - bool is_conv() const { return (tag_ & 0x80) == 0; } - bool is_length() const { return (tag_ & 0xC0) == 0x80; } - bool is_flags() const { return (tag_ & 0xE0) == 0xC0; } - - FormatConversionChar as_conv() const { - assert(is_conv()); - assert(!is_length()); - assert(!is_flags()); - return static_cast<FormatConversionChar>(tag_); - } - LengthMod as_length() const { - assert(!is_conv()); - assert(is_length()); - assert(!is_flags()); - return static_cast<LengthMod>(tag_ & 0x3F); - } - Flags as_flags() const { - assert(!is_conv()); - assert(!is_length()); - assert(is_flags()); - return static_cast<Flags>(tag_ & 0x1F); - } - - private: - uint8_t tag_; -}; - -extern const ConvTag kTags[256]; -// Keep a single table for all the conversion chars and length modifiers. -inline ConvTag GetTagForChar(char c) { - return kTags[static_cast<unsigned char>(c)]; -} +const char* ConsumeUnboundConversionNoInline(const char* p, const char* end, + UnboundConversion* conv, + int* next_arg); // Parse the format string provided in 'src' and pass the identified items into // 'consumer'. @@ -155,10 +62,11 @@ bool ParseFormatString(string_view src, Consumer consumer) { static_cast<const char*>(memchr(p, '%', static_cast<size_t>(end - p))); if (!percent) { // We found the last substring. - return consumer.Append(string_view(p, end - p)); + return consumer.Append(string_view(p, static_cast<size_t>(end - p))); } // We found a percent, so push the text run then process the percent. - if (Y_ABSL_PREDICT_FALSE(!consumer.Append(string_view(p, percent - p)))) { + if (Y_ABSL_PREDICT_FALSE(!consumer.Append( + string_view(p, static_cast<size_t>(percent - p))))) { return false; } if (Y_ABSL_PREDICT_FALSE(percent + 1 >= end)) return false; @@ -186,10 +94,11 @@ bool ParseFormatString(string_view src, Consumer consumer) { } } else if (percent[1] != '%') { UnboundConversion conv; - p = ConsumeUnboundConversion(percent + 1, end, &conv, &next_arg); + p = ConsumeUnboundConversionNoInline(percent + 1, end, &conv, &next_arg); if (Y_ABSL_PREDICT_FALSE(p == nullptr)) return false; if (Y_ABSL_PREDICT_FALSE(!consumer.ConvertOne( - conv, string_view(percent + 1, p - (percent + 1))))) { + conv, string_view(percent + 1, + static_cast<size_t>(p - (percent + 1)))))) { return false; } } else { diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_split_internal.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_split_internal.h index ed68b2e195..1f1d7342ce 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_split_internal.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_split_internal.h @@ -132,7 +132,8 @@ class SplitIterator { const y_absl::string_view text = splitter_->text(); const y_absl::string_view d = delimiter_.Find(text, pos_); if (d.data() == text.data() + text.size()) state_ = kLastState; - curr_ = text.substr(pos_, d.data() - (text.data() + pos_)); + curr_ = text.substr(pos_, + static_cast<size_t>(d.data() - (text.data() + pos_))); pos_ += curr_.size() + d.size(); } while (!predicate_(curr_)); return *this; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.cc new file mode 100644 index 0000000000..177e8b34a2 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.cc @@ -0,0 +1,28 @@ +// Copyright 2022 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 "y_absl/strings/internal/stringify_sink.h" +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN +namespace strings_internal { + +void StringifySink::Append(size_t count, char ch) { buffer_.append(count, ch); } + +void StringifySink::Append(string_view v) { + buffer_.append(v.data(), v.size()); +} + +} // namespace strings_internal +Y_ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.h new file mode 100644 index 0000000000..f0522772ad --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/stringify_sink.h @@ -0,0 +1,57 @@ +// Copyright 2022 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 Y_ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ +#define Y_ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ + +#include <util/generic/string.h> +#include <type_traits> +#include <utility> + +#include "y_absl/strings/string_view.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN + +namespace strings_internal { +class StringifySink { + public: + void Append(size_t count, char ch); + + void Append(string_view v); + + // Support `y_absl::Format(&sink, format, args...)`. + friend void AbslFormatFlush(StringifySink* sink, y_absl::string_view v) { + sink->Append(v); + } + + private: + template <typename T> + friend string_view ExtractStringification(StringifySink& sink, const T& v); + + TString buffer_; +}; + +template <typename T> +string_view ExtractStringification(StringifySink& sink, const T& v) { + AbslStringify(sink, v); + return sink.buffer_; +} + +} // namespace strings_internal + +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/numbers.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/numbers.cc index e2c8ddf329..af1ecfd6d4 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/numbers.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/numbers.cc @@ -190,32 +190,32 @@ char* numbers_internal::FastIntToBuffer(uint32_t i, char* buffer) { if (i >= 1000) goto lt10_000; digits = i / 100; i -= digits * 100; - *buffer++ = '0' + digits; + *buffer++ = '0' + static_cast<char>(digits); goto lt100; } if (i < 1000000) { // 1,000,000 if (i >= 100000) goto lt1_000_000; digits = i / 10000; // 10,000 i -= digits * 10000; - *buffer++ = '0' + digits; + *buffer++ = '0' + static_cast<char>(digits); goto lt10_000; } if (i < 100000000) { // 100,000,000 if (i >= 10000000) goto lt100_000_000; digits = i / 1000000; // 1,000,000 i -= digits * 1000000; - *buffer++ = '0' + digits; + *buffer++ = '0' + static_cast<char>(digits); goto lt1_000_000; } // we already know that i < 1,000,000,000 digits = i / 100000000; // 100,000,000 i -= digits * 100000000; - *buffer++ = '0' + digits; + *buffer++ = '0' + static_cast<char>(digits); goto lt100_000_000; } char* numbers_internal::FastIntToBuffer(int32_t i, char* buffer) { - uint32_t u = i; + uint32_t u = static_cast<uint32_t>(i); if (i < 0) { *buffer++ = '-'; // We need to do the negation in modular (i.e., "unsigned") @@ -268,7 +268,7 @@ char* numbers_internal::FastIntToBuffer(uint64_t i, char* buffer) { } char* numbers_internal::FastIntToBuffer(int64_t i, char* buffer) { - uint64_t u = i; + uint64_t u = static_cast<uint64_t>(i); if (i < 0) { *buffer++ = '-'; u = 0 - u; @@ -329,7 +329,7 @@ static std::pair<uint64_t, uint64_t> PowFive(uint64_t num, int expfive) { result = Mul32(result, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5); expfive -= 13; } - constexpr int powers_of_five[13] = { + constexpr uint32_t powers_of_five[13] = { 1, 5, 5 * 5, @@ -404,14 +404,14 @@ static ExpDigits SplitToSix(const double value) { // we multiply it by 65536 and see if the fractional part is close to 32768. // (The number doesn't have to be a power of two,but powers of two are faster) uint64_t d64k = d * 65536; - int dddddd; // A 6-digit decimal integer. + uint32_t dddddd; // A 6-digit decimal integer. if ((d64k % 65536) == 32767 || (d64k % 65536) == 32768) { // OK, it's fairly likely that precision was lost above, which is // not a surprise given only 52 mantissa bits are available. Therefore // redo the calculation using 128-bit numbers. (64 bits are not enough). // Start out with digits rounded down; maybe add one below. - dddddd = static_cast<int>(d64k / 65536); + dddddd = static_cast<uint32_t>(d64k / 65536); // mantissa is a 64-bit integer representing M.mmm... * 2^63. The actual // value we're representing, of course, is M.mmm... * 2^exp2. @@ -461,7 +461,7 @@ static ExpDigits SplitToSix(const double value) { } } else { // Here, we are not close to the edge. - dddddd = static_cast<int>((d64k + 32768) / 65536); + dddddd = static_cast<uint32_t>((d64k + 32768) / 65536); } if (dddddd == 1000000) { dddddd = 100000; @@ -469,7 +469,7 @@ static ExpDigits SplitToSix(const double value) { } exp_dig.exponent = exp; - int two_digits = dddddd / 10000; + uint32_t two_digits = dddddd / 10000; dddddd -= two_digits * 10000; numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[0]); @@ -499,7 +499,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { if (std::signbit(d)) *out++ = '-'; *out++ = '0'; *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); } if (d < 0) { *out++ = '-'; @@ -507,7 +507,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { } if (d > std::numeric_limits<double>::max()) { strcpy(out, "inf"); // NOLINT(runtime/printf) - return out + 3 - buffer; + return static_cast<size_t>(out + 3 - buffer); } auto exp_dig = SplitToSix(d); @@ -519,7 +519,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { case 5: memcpy(out, &digits[0], 6), out += 6; *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); case 4: memcpy(out, &digits[0], 5), out += 5; if (digits[5] != '0') { @@ -527,7 +527,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { *out++ = digits[5]; } *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); case 3: memcpy(out, &digits[0], 4), out += 4; if ((digits[5] | digits[4]) != '0') { @@ -536,7 +536,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { if (digits[5] != '0') *out++ = digits[5]; } *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); case 2: memcpy(out, &digits[0], 3), out += 3; *out++ = '.'; @@ -545,7 +545,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { while (out[-1] == '0') --out; if (out[-1] == '.') --out; *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); case 1: memcpy(out, &digits[0], 2), out += 2; *out++ = '.'; @@ -554,7 +554,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { while (out[-1] == '0') --out; if (out[-1] == '.') --out; *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); case 0: memcpy(out, &digits[0], 1), out += 1; *out++ = '.'; @@ -563,7 +563,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { while (out[-1] == '0') --out; if (out[-1] == '.') --out; *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); case -4: out[2] = '0'; ++out; @@ -582,7 +582,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { out += 6; while (out[-1] == '0') --out; *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); } assert(exp < -4 || exp >= 6); out[0] = digits[0]; @@ -601,12 +601,12 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { if (exp > 99) { int dig1 = exp / 100; exp -= dig1 * 100; - *out++ = '0' + dig1; + *out++ = '0' + static_cast<char>(dig1); } - PutTwoDigits(exp, out); + PutTwoDigits(static_cast<uint32_t>(exp), out); out += 2; *out = 0; - return out - buffer; + return static_cast<size_t>(out - buffer); } namespace { @@ -642,10 +642,12 @@ inline bool safe_parse_sign_and_base(y_absl::string_view* text /*inout*/, int base = *base_ptr; // Consume whitespace. - while (start < end && y_absl::ascii_isspace(start[0])) { + while (start < end && + y_absl::ascii_isspace(static_cast<unsigned char>(start[0]))) { ++start; } - while (start < end && y_absl::ascii_isspace(end[-1])) { + while (start < end && + y_absl::ascii_isspace(static_cast<unsigned char>(end[-1]))) { --end; } if (start >= end) { @@ -694,7 +696,7 @@ inline bool safe_parse_sign_and_base(y_absl::string_view* text /*inout*/, } else { return false; } - *text = y_absl::string_view(start, end - start); + *text = y_absl::string_view(start, static_cast<size_t>(end - start)); *base_ptr = base; return true; } @@ -920,17 +922,18 @@ inline bool safe_parse_positive_int(y_absl::string_view text, int base, const IntType vmax = std::numeric_limits<IntType>::max(); assert(vmax > 0); assert(base >= 0); - assert(vmax >= static_cast<IntType>(base)); + const IntType base_inttype = static_cast<IntType>(base); + assert(vmax >= base_inttype); const IntType vmax_over_base = LookupTables<IntType>::kVmaxOverBase[base]; assert(base < 2 || - std::numeric_limits<IntType>::max() / base == vmax_over_base); + std::numeric_limits<IntType>::max() / base_inttype == vmax_over_base); const char* start = text.data(); const char* end = start + text.size(); // loop over digits for (; start < end; ++start) { unsigned char c = static_cast<unsigned char>(start[0]); - int digit = kAsciiToInt[c]; - if (digit >= base) { + IntType digit = static_cast<IntType>(kAsciiToInt[c]); + if (digit >= base_inttype) { *value_p = value; return false; } @@ -938,7 +941,7 @@ inline bool safe_parse_positive_int(y_absl::string_view text, int base, *value_p = vmax; return false; } - value *= base; + value *= base_inttype; if (value > vmax - digit) { *value_p = vmax; return false; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_cat.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_cat.cc index b598201d45..29c3361c30 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_cat.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_cat.cc @@ -17,12 +17,15 @@ #include <assert.h> #include <algorithm> +#include <cstddef> #include <cstdint> #include <cstring> +#include <util/generic/string.h> #include "y_absl/strings/ascii.h" #include "y_absl/strings/internal/resize_uninitialized.h" #include "y_absl/strings/numbers.h" +#include "y_absl/strings/string_view.h" namespace y_absl { Y_ABSL_NAMESPACE_BEGIN @@ -56,7 +59,7 @@ AlphaNum::AlphaNum(Dec dec) { *--writer = '0' + (value % 10); value /= 10; } - *--writer = '0' + value; + *--writer = '0' + static_cast<char>(value); if (neg) *--writer = '-'; ptrdiff_t fillers = writer - minfill; @@ -73,7 +76,7 @@ AlphaNum::AlphaNum(Dec dec) { if (add_sign_again) *--writer = '-'; } - piece_ = y_absl::string_view(writer, end - writer); + piece_ = y_absl::string_view(writer, static_cast<size_t>(end - writer)); } // ---------------------------------------------------------------------- @@ -141,12 +144,12 @@ namespace strings_internal { TString CatPieces(std::initializer_list<y_absl::string_view> pieces) { TString result; size_t total_size = 0; - for (const y_absl::string_view& piece : pieces) total_size += piece.size(); + for (y_absl::string_view piece : pieces) total_size += piece.size(); strings_internal::STLStringResizeUninitialized(&result, total_size); char* const begin = &result[0]; char* out = begin; - for (const y_absl::string_view& piece : pieces) { + for (y_absl::string_view piece : pieces) { const size_t this_size = piece.size(); if (this_size != 0) { memcpy(out, piece.data(), this_size); @@ -170,7 +173,7 @@ void AppendPieces(TString* dest, std::initializer_list<y_absl::string_view> pieces) { size_t old_size = dest->size(); size_t total_size = old_size; - for (const y_absl::string_view& piece : pieces) { + for (y_absl::string_view piece : pieces) { ASSERT_NO_OVERLAP(*dest, piece); total_size += piece.size(); } @@ -178,7 +181,7 @@ void AppendPieces(TString* dest, char* const begin = &(*dest)[0]; char* out = begin + old_size; - for (const y_absl::string_view& piece : pieces) { + for (y_absl::string_view piece : pieces) { const size_t this_size = piece.size(); if (this_size != 0) { memcpy(out, piece.data(), this_size); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_cat.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_cat.h index 59ef206816..f64d522149 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_cat.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_cat.h @@ -48,6 +48,40 @@ // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using // a `PadSpec` enum. // +// User-defined types can be formatted with the `AbslStringify()` customization +// point. The API relies on detecting an overload in the user-defined type's +// namespace of a free (non-member) `AbslStringify()` function as a definition +// (typically declared as a friend and implemented in-line. +// with the following signature: +// +// class MyClass { ... }; +// +// template <typename Sink> +// void AbslStringify(Sink& sink, const MyClass& value); +// +// An `AbslStringify()` overload for a type should only be declared in the same +// file and namespace as said type. +// +// Note that `AbslStringify()` also supports use with `y_absl::StrFormat()` and +// `y_absl::Substitute()`. +// +// Example: +// +// struct Point { +// // To add formatting support to `Point`, we simply need to add a free +// // (non-member) function `AbslStringify()`. This method specifies how +// // Point should be printed when y_absl::StrCat() is called on it. You can add +// // such a free function using a friend declaration within the body of the +// // class. The sink parameter is a templated type to avoid requiring +// // dependencies. +// template <typename Sink> friend void AbslStringify(Sink& +// sink, const Point& p) { +// y_absl::Format(&sink, "(%v, %v)", p.x, p.y); +// } +// +// int x; +// int y; +// }; // ----------------------------------------------------------------------------- #ifndef Y_ABSL_STRINGS_STR_CAT_H_ @@ -57,9 +91,12 @@ #include <cstdint> #include <util/generic/string.h> #include <type_traits> +#include <utility> #include <vector> #include "y_absl/base/port.h" +#include "y_absl/strings/internal/has_absl_stringify.h" +#include "y_absl/strings/internal/stringify_sink.h" #include "y_absl/strings/numbers.h" #include "y_absl/strings/string_view.h" @@ -205,8 +242,10 @@ struct Dec { // ----------------------------------------------------------------------------- // // The `AlphaNum` class acts as the main parameter type for `StrCat()` and -// `StrAppend()`, providing efficient conversion of numeric, boolean, and -// hexadecimal values (through the `Hex` type) into strings. +// `StrAppend()`, providing efficient conversion of numeric, boolean, decimal, +// and hexadecimal values (through the `Dec` and `Hex` types) into strings. +// `AlphaNum` should only be used as a function parameter. Do not instantiate +// `AlphaNum` directly as a stack variable. class AlphaNum { public: @@ -255,6 +294,13 @@ class AlphaNum { : piece_(NullSafeStringView(c_str)) {} // NOLINT(runtime/explicit) AlphaNum(y_absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit) + template <typename T, typename = typename std::enable_if< + strings_internal::HasAbslStringify<T>::value>::type> + AlphaNum( // NOLINT(runtime/explicit) + const T& v, // NOLINT(runtime/explicit) + strings_internal::StringifySink&& sink = {}) // NOLINT(runtime/explicit) + : piece_(strings_internal::ExtractStringification(sink, v)) {} + template <typename Allocator> AlphaNum( // NOLINT(runtime/explicit) const std::basic_string<char, std::char_traits<char>, Allocator>& str) @@ -277,7 +323,8 @@ class AlphaNum { // This overload matches only scoped enums. template <typename T, typename = typename std::enable_if< - std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type> + std::is_enum<T>{} && !std::is_convertible<T, int>{} && + !strings_internal::HasAbslStringify<T>::value>::type> AlphaNum(T e) // NOLINT(runtime/explicit) : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {} diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_format.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_format.h index 099cd1b26a..521581978d 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_format.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/str_format.h @@ -191,9 +191,9 @@ class FormatCountCapture { // y_absl::StrFormat(formatString, "TheVillage", 6); // // A format string generally follows the POSIX syntax as used within the POSIX -// `printf` specification. +// `printf` specification. (Exceptions are noted below.) // -// (See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html.) +// (See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html) // // In specific, the `FormatSpec` supports the following type specifiers: // * `c` for characters @@ -211,6 +211,10 @@ class FormatCountCapture { // * `n` for the special case of writing out the number of characters // written to this point. The resulting value must be captured within an // `y_absl::FormatCountCapture` type. +// * `v` for values using the default format for a deduced type. These deduced +// types include many of the primitive types denoted here as well as +// user-defined types containing the proper extensions. (See below for more +// information.) // // Implementation-defined behavior: // * A null pointer provided to "%s" or "%p" is output as "(nil)". @@ -239,6 +243,15 @@ class FormatCountCapture { // "%s%d%n", "hello", 123, y_absl::FormatCountCapture(&n)); // EXPECT_EQ(8, n); // +// NOTE: the `v` specifier (for "value") is a type specifier not present in the +// POSIX specification. %v will format values according to their deduced type. +// `v` uses `d` for signed integer values, `u` for unsigned integer values, `g` +// for floating point values, and formats boolean values as "true"/"false" +// (instead of 1 or 0 for booleans formatted using d). `const char*` is not +// supported; please use `std:string` and `string_view`. `char` is also not +// supported due to ambiguity of the type. This specifier does not support +// modifiers. +// // The `FormatSpec` intrinsically supports all of these fundamental C++ types: // // * Characters: `char`, `signed char`, `unsigned char` @@ -570,6 +583,41 @@ Y_ABSL_MUST_USE_RESULT inline bool FormatUntyped( // StrFormat Extensions //------------------------------------------------------------------------------ // +// AbslStringify() +// +// A simpler customization API for formatting user-defined types using +// y_absl::StrFormat(). The API relies on detecting an overload in the +// user-defined type's namespace of a free (non-member) `AbslStringify()` +// function as a friend definition with the following signature: +// +// template <typename Sink> +// void AbslStringify(Sink& sink, const X& value); +// +// An `AbslStringify()` overload for a type should only be declared in the same +// file and namespace as said type. +// +// Note that unlike with AbslFormatConvert(), AbslStringify() does not allow +// customization of allowed conversion characters. AbslStringify() uses `%v` as +// the underlying conversion specififer. Additionally, AbslStringify() supports +// use with y_absl::StrCat while AbslFormatConvert() does not. +// +// Example: +// +// struct Point { +// // To add formatting support to `Point`, we simply need to add a free +// // (non-member) function `AbslStringify()`. This method prints in the +// // request format using the underlying `%v` specifier. You can add such a +// // free function using a friend declaration within the body of the class. +// // The sink parameter is a templated type to avoid requiring dependencies. +// template <typename Sink> +// friend void AbslStringify(Sink& sink, const Point& p) { +// y_absl::Format(&sink, "(%v, %v)", p.x, p.y); +// } +// +// int x; +// int y; +// }; +// // AbslFormatConvert() // // The StrFormat library provides a customization API for formatting @@ -616,9 +664,9 @@ Y_ABSL_MUST_USE_RESULT inline bool FormatUntyped( // AbslFormatConvert(const Point& p, const y_absl::FormatConversionSpec& spec, // y_absl::FormatSink* s) { // if (spec.conversion_char() == y_absl::FormatConversionChar::s) { -// s->Append(y_absl::StrCat("x=", p.x, " y=", p.y)); +// y_absl::Format(s, "x=%vy=%v", p.x, p.y); // } else { -// s->Append(y_absl::StrCat(p.x, ",", p.y)); +// y_absl::Format(s, "%v,%v", p.x, p.y); // } // return {true}; // } @@ -637,7 +685,7 @@ enum class FormatConversionChar : uint8_t { c, s, // text d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float - n, p // misc + n, p, v // misc }; // clang-format on @@ -757,6 +805,7 @@ enum class FormatConversionCharSet : uint64_t { // misc n = str_format_internal::FormatConversionCharToConvInt('n'), p = str_format_internal::FormatConversionCharToConvInt('p'), + v = str_format_internal::FormatConversionCharToConvInt('v'), // Used for width/precision '*' specification. kStar = static_cast<uint64_t>( @@ -771,23 +820,36 @@ enum class FormatConversionCharSet : uint64_t { // FormatSink // -// An abstraction to which conversions write their string data. +// A format sink is a generic abstraction to which conversions may write their +// formatted string data. `y_absl::FormatConvert()` uses this sink to write its +// formatted string. // class FormatSink { public: - // Appends `count` copies of `ch`. + // FormatSink::Append() + // + // Appends `count` copies of `ch` to the format sink. void Append(size_t count, char ch) { sink_->Append(count, ch); } + // Overload of FormatSink::Append() for appending the characters of a string + // view to a format sink. void Append(string_view v) { sink_->Append(v); } - // Appends the first `precision` bytes of `v`. If this is less than - // `width`, spaces will be appended first (if `left` is false), or + // FormatSink::PutPaddedString() + // + // Appends `precision` number of bytes of `v` to the format sink. If this is + // less than `width`, spaces will be appended first (if `left` is false), or // after (if `left` is true) to ensure the total amount appended is // at least `width`. bool PutPaddedString(string_view v, int width, int precision, bool left) { return sink_->PutPaddedString(v, width, precision, left); } + // Support `y_absl::Format(&sink, format, args...)`. + friend void AbslFormatFlush(FormatSink* sink, y_absl::string_view v) { + sink->Append(v); + } + private: friend str_format_internal::FormatSinkImpl; explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {} diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/string_view.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/string_view.cc index cfc9f7cdb2..fc31726ed4 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/string_view.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/string_view.cc @@ -32,7 +32,7 @@ void WritePadding(std::ostream& o, size_t pad) { memset(fill_buf, o.fill(), sizeof(fill_buf)); while (pad) { size_t n = std::min(pad, sizeof(fill_buf)); - o.write(fill_buf, n); + o.write(fill_buf, static_cast<std::streamsize>(n)); pad -= n; } } @@ -63,7 +63,7 @@ std::ostream& operator<<(std::ostream& o, string_view piece) { size_t lpad = 0; size_t rpad = 0; if (static_cast<size_t>(o.width()) > piece.size()) { - size_t pad = o.width() - piece.size(); + size_t pad = static_cast<size_t>(o.width()) - piece.size(); if ((o.flags() & o.adjustfield) == o.left) { rpad = pad; } else { @@ -71,7 +71,7 @@ std::ostream& operator<<(std::ostream& o, string_view piece) { } } if (lpad) WritePadding(o, lpad); - o.write(piece.data(), piece.size()); + o.write(piece.data(), static_cast<std::streamsize>(piece.size())); if (rpad) WritePadding(o, rpad); o.width(0); } @@ -86,7 +86,7 @@ string_view::size_type string_view::find(string_view s, } const char* result = strings_internal::memmatch(ptr_ + pos, length_ - pos, s.ptr_, s.length_); - return result ? result - ptr_ : npos; + return result ? static_cast<size_type>(result - ptr_) : npos; } string_view::size_type string_view::find(char c, size_type pos) const noexcept { @@ -95,7 +95,7 @@ string_view::size_type string_view::find(char c, size_type pos) const noexcept { } const char* result = static_cast<const char*>(memchr(ptr_ + pos, c, length_ - pos)); - return result != nullptr ? result - ptr_ : npos; + return result != nullptr ? static_cast<size_type>(result - ptr_) : npos; } string_view::size_type string_view::rfind(string_view s, @@ -104,7 +104,7 @@ string_view::size_type string_view::rfind(string_view s, if (s.empty()) return std::min(length_, pos); const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_; const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_); - return result != last ? result - ptr_ : npos; + return result != last ? static_cast<size_type>(result - ptr_) : npos; } // Search range is [0..pos] inclusive. If pos == npos, search everything. diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/string_view.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/string_view.h index 3c725fbf5d..4d7b8b14bf 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/string_view.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/string_view.h @@ -65,12 +65,6 @@ Y_ABSL_NAMESPACE_END #define Y_ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp #endif // Y_ABSL_HAVE_BUILTIN(__builtin_memcmp) -#if defined(__cplusplus) && __cplusplus >= 201402L -#define Y_ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR constexpr -#else -#define Y_ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR -#endif - namespace y_absl { Y_ABSL_NAMESPACE_BEGIN @@ -343,7 +337,7 @@ class string_view { // // Removes the first `n` characters from the `string_view`. Note that the // underlying string is not changed, only the view. - Y_ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void remove_prefix(size_type n) { + constexpr void remove_prefix(size_type n) { Y_ABSL_HARDENING_ASSERT(n <= length_); ptr_ += n; length_ -= n; @@ -353,7 +347,7 @@ class string_view { // // Removes the last `n` characters from the `string_view`. Note that the // underlying string is not changed, only the view. - Y_ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void remove_suffix(size_type n) { + constexpr void remove_suffix(size_type n) { Y_ABSL_HARDENING_ASSERT(n <= length_); length_ -= n; } @@ -361,7 +355,7 @@ class string_view { // string_view::swap() // // Swaps this `string_view` with another `string_view`. - Y_ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void swap(string_view& s) noexcept { + constexpr void swap(string_view& s) noexcept { auto t = *this; *this = s; s = t; @@ -680,7 +674,6 @@ std::ostream& operator<<(std::ostream& o, string_view piece); Y_ABSL_NAMESPACE_END } // namespace y_absl -#undef Y_ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR #undef Y_ABSL_INTERNAL_STRING_VIEW_MEMCMP #endif // Y_ABSL_USES_STD_STRING_VIEW diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/substitute.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/substitute.cc index 5cee39e4a0..093ae16562 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/substitute.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/substitute.cc @@ -40,7 +40,8 @@ void SubstituteAndAppendArray(TString* output, y_absl::string_view format, y_absl::CEscape(format).c_str()); #endif return; - } else if (y_absl::ascii_isdigit(format[i + 1])) { + } else if (y_absl::ascii_isdigit( + static_cast<unsigned char>(format[i + 1]))) { int index = format[i + 1] - '0'; if (static_cast<size_t>(index) >= num_args) { #ifndef NDEBUG @@ -80,7 +81,7 @@ void SubstituteAndAppendArray(TString* output, y_absl::string_view format, char* target = &(*output)[original_size]; for (size_t i = 0; i < format.size(); i++) { if (format[i] == '$') { - if (y_absl::ascii_isdigit(format[i + 1])) { + if (y_absl::ascii_isdigit(static_cast<unsigned char>(format[i + 1]))) { const y_absl::string_view src = args_array[format[i + 1] - '0']; target = std::copy(src.begin(), src.end(), target); ++i; // Skip next char. @@ -110,7 +111,8 @@ Arg::Arg(const void* value) { } while (num != 0); *--ptr = 'x'; *--ptr = '0'; - piece_ = y_absl::string_view(ptr, scratch_ + sizeof(scratch_) - ptr); + piece_ = y_absl::string_view( + ptr, static_cast<size_t>(scratch_ + sizeof(scratch_) - ptr)); } } @@ -132,7 +134,7 @@ Arg::Arg(Hex hex) { beg = writer; } - piece_ = y_absl::string_view(beg, end - beg); + piece_ = y_absl::string_view(beg, static_cast<size_t>(end - beg)); } // TODO(jorg): Don't duplicate so much code between here and str_cat.cc @@ -147,7 +149,7 @@ Arg::Arg(Dec dec) { *--writer = '0' + (value % 10); value /= 10; } - *--writer = '0' + value; + *--writer = '0' + static_cast<char>(value); if (neg) *--writer = '-'; ptrdiff_t fillers = writer - minfill; @@ -164,7 +166,7 @@ Arg::Arg(Dec dec) { if (add_sign_again) *--writer = '-'; } - piece_ = y_absl::string_view(writer, end - writer); + piece_ = y_absl::string_view(writer, static_cast<size_t>(end - writer)); } } // namespace substitute_internal diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/substitute.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/substitute.h index cb49392ddb..45a5e1c01e 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/substitute.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/substitute.h @@ -55,6 +55,8 @@ // * bool (Printed as "true" or "false") // * pointer types other than char* (Printed as "0x<lower case hex string>", // except that null is printed as "NULL") +// * user-defined types via the `AbslStringify()` customization point. See the +// documentation for `y_absl::StrCat` for an explanation on how to use this. // // If an invalid format string is provided, Substitute returns an empty string // and SubstituteAndAppend does not change the provided output string. @@ -79,6 +81,7 @@ #include "y_absl/base/port.h" #include "y_absl/strings/ascii.h" #include "y_absl/strings/escaping.h" +#include "y_absl/strings/internal/stringify_sink.h" #include "y_absl/strings/numbers.h" #include "y_absl/strings/str_cat.h" #include "y_absl/strings/str_split.h" @@ -102,14 +105,14 @@ class Arg { // Overloads for string-y things // // Explicitly overload `const char*` so the compiler doesn't cast to `bool`. - Arg(const char* value) // NOLINT(runtime/explicit) + Arg(const char* value) // NOLINT(google-explicit-constructor) : piece_(y_absl::NullSafeStringView(value)) {} template <typename Allocator> Arg( // NOLINT const std::basic_string<char, std::char_traits<char>, Allocator>& value) noexcept : piece_(value) {} - Arg(y_absl::string_view value) // NOLINT(runtime/explicit) + Arg(y_absl::string_view value) // NOLINT(google-explicit-constructor) : piece_(value) {} Arg(const TString& s) : piece_(s.data(), s.size()) {} @@ -121,45 +124,67 @@ class Arg { // probably using them as 8-bit integers and would probably prefer an integer // representation. However, we can't really know, so we make the caller decide // what to do. - Arg(char value) // NOLINT(runtime/explicit) + Arg(char value) // NOLINT(google-explicit-constructor) : piece_(scratch_, 1) { scratch_[0] = value; } Arg(short value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast<size_t>( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(unsigned short value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(int value) // NOLINT(runtime/explicit) + static_cast<size_t>( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} + Arg(int value) // NOLINT(google-explicit-constructor) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(unsigned int value) // NOLINT(runtime/explicit) + static_cast<size_t>( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} + Arg(unsigned int value) // NOLINT(google-explicit-constructor) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast<size_t>( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(long value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast<size_t>( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(unsigned long value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast<size_t>( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(long long value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast<size_t>( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(unsigned long long value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(float value) // NOLINT(runtime/explicit) + static_cast<size_t>( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} + Arg(float value) // NOLINT(google-explicit-constructor) : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) { } - Arg(double value) // NOLINT(runtime/explicit) + Arg(double value) // NOLINT(google-explicit-constructor) : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) { } - Arg(bool value) // NOLINT(runtime/explicit) + Arg(bool value) // NOLINT(google-explicit-constructor) : piece_(value ? "true" : "false") {} - Arg(Hex hex); // NOLINT(runtime/explicit) - Arg(Dec dec); // NOLINT(runtime/explicit) + template <typename T, typename = typename std::enable_if< + strings_internal::HasAbslStringify<T>::value>::type> + Arg( // NOLINT(google-explicit-constructor) + const T& v, strings_internal::StringifySink&& sink = {}) + : piece_(strings_internal::ExtractStringification(sink, v)) {} + + Arg(Hex hex); // NOLINT(google-explicit-constructor) + Arg(Dec dec); // NOLINT(google-explicit-constructor) // vector<bool>::reference and const_reference require special help to convert // to `Arg` because it requires two user defined conversions. @@ -174,13 +199,14 @@ class Arg { // `void*` values, with the exception of `char*`, are printed as // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed. - Arg(const void* value); // NOLINT(runtime/explicit) + Arg(const void* value); // NOLINT(google-explicit-constructor) // Normal enums are already handled by the integer formatters. // This overload matches only scoped enums. template <typename T, typename = typename std::enable_if< - std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type> + std::is_enum<T>{} && !std::is_convertible<T, int>{} && + !strings_internal::HasAbslStringify<T>::value>::type> Arg(T value) // NOLINT(google-explicit-constructor) : Arg(static_cast<typename std::underlying_type<T>::type>(value)) {} diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ya.make index 5d62e67529..fde0b02323 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/ya.make @@ -20,6 +20,14 @@ NO_COMPILER_WARNINGS() SRCDIR(contrib/restricted/abseil-cpp-tstring/y_absl) SRCS( + crc/crc32c.cc + crc/internal/cpu_detect.cc + crc/internal/crc.cc + crc/internal/crc_cord_state.cc + crc/internal/crc_memcpy_fallback.cc + crc/internal/crc_memcpy_x86_64.cc + crc/internal/crc_non_temporal_memcpy.cc + crc/internal/crc_x86_arm_combined.cc status/statusor.cc strings/ascii.cc strings/charconv.cc @@ -40,6 +48,7 @@ SRCS( strings/internal/cordz_handle.cc strings/internal/cordz_info.cc strings/internal/cordz_sample_token.cc + strings/internal/damerau_levenshtein_distance.cc strings/internal/escaping.cc strings/internal/memutil.cc strings/internal/ostringstream.cc @@ -49,6 +58,7 @@ SRCS( strings/internal/str_format/float_conversion.cc strings/internal/str_format/output.cc strings/internal/str_format/parser.cc + strings/internal/stringify_sink.cc strings/internal/utf8.cc strings/match.cc strings/numbers.cc diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/futex.h b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/futex.h index c59a7cc6ab..890bc10015 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/futex.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/futex.h @@ -87,7 +87,7 @@ class FutexImpl { public: static int WaitUntil(std::atomic<int32_t> *v, int32_t val, KernelTimeout t) { - int err = 0; + long err = 0; // NOLINT(runtime/int) if (t.has_timeout()) { // https://locklessinc.com/articles/futex_cheat_sheet/ // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time. @@ -105,41 +105,44 @@ class FutexImpl { FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, nullptr); } if (Y_ABSL_PREDICT_FALSE(err != 0)) { - err = -errno; + return -errno; } - return err; + return 0; } static int WaitBitsetAbsoluteTimeout(std::atomic<int32_t> *v, int32_t val, int32_t bits, const struct timespec *abstime) { - int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), - FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, val, abstime, - nullptr, bits); + // NOLINTNEXTLINE(runtime/int) + long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), + FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, val, abstime, + nullptr, bits); if (Y_ABSL_PREDICT_FALSE(err != 0)) { - err = -errno; + return -errno; } - return err; + return 0; } static int Wake(std::atomic<int32_t> *v, int32_t count) { - int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), - FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count); + // NOLINTNEXTLINE(runtime/int) + long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), + FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count); if (Y_ABSL_PREDICT_FALSE(err < 0)) { - err = -errno; + return -errno; } - return err; + return 0; } // FUTEX_WAKE_BITSET static int WakeBitset(std::atomic<int32_t> *v, int32_t count, int32_t bits) { - int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), - FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, count, nullptr, - nullptr, bits); + // NOLINTNEXTLINE(runtime/int) + long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), + FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, count, nullptr, + nullptr, bits); if (Y_ABSL_PREDICT_FALSE(err < 0)) { - err = -errno; + return -errno; } - return err; + return 0; } }; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/graphcycles.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/graphcycles.cc index 89a9d77bf0..9d61ca0f2b 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/graphcycles.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/graphcycles.cc @@ -181,9 +181,9 @@ class NodeSet { return true; } - void erase(uint32_t v) { + void erase(int32_t v) { uint32_t i = FindIndex(v); - if (static_cast<uint32_t>(table_[i]) == v) { + if (table_[i] == v) { table_[i] = kDel; } } @@ -195,7 +195,7 @@ class NodeSet { for (int32_t elem, _cursor = 0; (eset).Next(&_cursor, &elem); ) bool Next(int32_t* cursor, int32_t* elem) { while (static_cast<uint32_t>(*cursor) < table_.size()) { - int32_t v = table_[*cursor]; + int32_t v = table_[static_cast<uint32_t>(*cursor)]; (*cursor)++; if (v >= 0) { *elem = v; @@ -210,24 +210,26 @@ class NodeSet { Vec<int32_t> table_; uint32_t occupied_; // Count of non-empty slots (includes deleted slots) - static uint32_t Hash(uint32_t a) { return a * 41; } + static uint32_t Hash(int32_t a) { return static_cast<uint32_t>(a * 41); } // Return index for storing v. May return an empty index or deleted index - int FindIndex(int32_t v) const { + uint32_t FindIndex(int32_t v) const { // Search starting at hash index. const uint32_t mask = table_.size() - 1; uint32_t i = Hash(v) & mask; - int deleted_index = -1; // If >= 0, index of first deleted element we see + uint32_t deleted_index = 0; // index of first deleted element we see + bool seen_deleted_element = false; while (true) { int32_t e = table_[i]; if (v == e) { return i; } else if (e == kEmpty) { // Return any previously encountered deleted slot. - return (deleted_index >= 0) ? deleted_index : i; - } else if (e == kDel && deleted_index < 0) { + return seen_deleted_element ? deleted_index : i; + } else if (e == kDel && !seen_deleted_element) { // Keep searching since v might be present later. deleted_index = i; + seen_deleted_element = true; } i = (i + 1) & mask; // Linear probing; quadratic is slightly slower. } @@ -268,7 +270,7 @@ inline GraphId MakeId(int32_t index, uint32_t version) { } inline int32_t NodeIndex(GraphId id) { - return static_cast<uint32_t>(id.handle & 0xfffffffful); + return static_cast<int32_t>(id.handle); } inline uint32_t NodeVersion(GraphId id) { @@ -298,7 +300,7 @@ class PointerMap { int32_t Find(void* ptr) { auto masked = base_internal::HidePtr(ptr); for (int32_t i = table_[Hash(ptr)]; i != -1;) { - Node* n = (*nodes_)[i]; + Node* n = (*nodes_)[static_cast<uint32_t>(i)]; if (n->masked_ptr == masked) return i; i = n->next_hash; } @@ -307,7 +309,7 @@ class PointerMap { void Add(void* ptr, int32_t i) { int32_t* head = &table_[Hash(ptr)]; - (*nodes_)[i]->next_hash = *head; + (*nodes_)[static_cast<uint32_t>(i)]->next_hash = *head; *head = i; } @@ -317,7 +319,7 @@ class PointerMap { auto masked = base_internal::HidePtr(ptr); for (int32_t* slot = &table_[Hash(ptr)]; *slot != -1; ) { int32_t index = *slot; - Node* n = (*nodes_)[index]; + Node* n = (*nodes_)[static_cast<uint32_t>(index)]; if (n->masked_ptr == masked) { *slot = n->next_hash; // Remove n from linked list n->next_hash = -1; @@ -358,7 +360,7 @@ struct GraphCycles::Rep { }; static Node* FindNode(GraphCycles::Rep* rep, GraphId id) { - Node* n = rep->nodes_[NodeIndex(id)]; + Node* n = rep->nodes_[static_cast<uint32_t>(NodeIndex(id))]; return (n->version == NodeVersion(id)) ? n : nullptr; } @@ -393,7 +395,7 @@ bool GraphCycles::CheckInvariants() const { Y_ABSL_RAW_LOG(FATAL, "Duplicate occurrence of rank %d", nx->rank); } HASH_FOR_EACH(y, nx->out) { - Node* ny = r->nodes_[y]; + Node* ny = r->nodes_[static_cast<uint32_t>(y)]; if (nx->rank >= ny->rank) { Y_ABSL_RAW_LOG(FATAL, "Edge %u->%d has bad rank assignment %d->%d", x, y, nx->rank, ny->rank); @@ -406,14 +408,14 @@ bool GraphCycles::CheckInvariants() const { GraphId GraphCycles::GetId(void* ptr) { int32_t i = rep_->ptrmap_.Find(ptr); if (i != -1) { - return MakeId(i, rep_->nodes_[i]->version); + return MakeId(i, rep_->nodes_[static_cast<uint32_t>(i)]->version); } else if (rep_->free_nodes_.empty()) { Node* n = new (base_internal::LowLevelAlloc::AllocWithArena(sizeof(Node), arena)) Node; n->version = 1; // Avoid 0 since it is used by InvalidGraphId() n->visited = false; - n->rank = rep_->nodes_.size(); + n->rank = static_cast<int32_t>(rep_->nodes_.size()); n->masked_ptr = base_internal::HidePtr(ptr); n->nstack = 0; n->priority = 0; @@ -425,7 +427,7 @@ GraphId GraphCycles::GetId(void* ptr) { // a permutation of [0,rep_->nodes_.size()-1]. int32_t r = rep_->free_nodes_.back(); rep_->free_nodes_.pop_back(); - Node* n = rep_->nodes_[r]; + Node* n = rep_->nodes_[static_cast<uint32_t>(r)]; n->masked_ptr = base_internal::HidePtr(ptr); n->nstack = 0; n->priority = 0; @@ -439,12 +441,12 @@ void GraphCycles::RemoveNode(void* ptr) { if (i == -1) { return; } - Node* x = rep_->nodes_[i]; + Node* x = rep_->nodes_[static_cast<uint32_t>(i)]; HASH_FOR_EACH(y, x->out) { - rep_->nodes_[y]->in.erase(i); + rep_->nodes_[static_cast<uint32_t>(y)]->in.erase(i); } HASH_FOR_EACH(y, x->in) { - rep_->nodes_[y]->out.erase(i); + rep_->nodes_[static_cast<uint32_t>(y)]->out.erase(i); } x->in.clear(); x->out.clear(); @@ -520,7 +522,7 @@ bool GraphCycles::InsertEdge(GraphId idx, GraphId idy) { // Since we do not call Reorder() on this path, clear any visited // markers left by ForwardDFS. for (const auto& d : r->deltaf_) { - r->nodes_[d]->visited = false; + r->nodes_[static_cast<uint32_t>(d)]->visited = false; } return false; } @@ -538,14 +540,14 @@ static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int32_t upper_bound) { while (!r->stack_.empty()) { n = r->stack_.back(); r->stack_.pop_back(); - Node* nn = r->nodes_[n]; + Node* nn = r->nodes_[static_cast<uint32_t>(n)]; if (nn->visited) continue; nn->visited = true; r->deltaf_.push_back(n); HASH_FOR_EACH(w, nn->out) { - Node* nw = r->nodes_[w]; + Node* nw = r->nodes_[static_cast<uint32_t>(w)]; if (nw->rank == upper_bound) { return false; // Cycle } @@ -564,14 +566,14 @@ static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int32_t lower_bound) { while (!r->stack_.empty()) { n = r->stack_.back(); r->stack_.pop_back(); - Node* nn = r->nodes_[n]; + Node* nn = r->nodes_[static_cast<uint32_t>(n)]; if (nn->visited) continue; nn->visited = true; r->deltab_.push_back(n); HASH_FOR_EACH(w, nn->in) { - Node* nw = r->nodes_[w]; + Node* nw = r->nodes_[static_cast<uint32_t>(w)]; if (!nw->visited && lower_bound < nw->rank) { r->stack_.push_back(w); } @@ -596,7 +598,7 @@ static void Reorder(GraphCycles::Rep* r) { // Assign the ranks in order to the collected list. for (uint32_t i = 0; i < r->list_.size(); i++) { - r->nodes_[r->list_[i]]->rank = r->merged_[i]; + r->nodes_[static_cast<uint32_t>(r->list_[i])]->rank = r->merged_[i]; } } @@ -604,7 +606,8 @@ static void Sort(const Vec<Node*>& nodes, Vec<int32_t>* delta) { struct ByRank { const Vec<Node*>* nodes; bool operator()(int32_t a, int32_t b) const { - return (*nodes)[a]->rank < (*nodes)[b]->rank; + return (*nodes)[static_cast<uint32_t>(a)]->rank < + (*nodes)[static_cast<uint32_t>(b)]->rank; } }; ByRank cmp; @@ -616,8 +619,10 @@ static void MoveToList( GraphCycles::Rep* r, Vec<int32_t>* src, Vec<int32_t>* dst) { for (auto& v : *src) { int32_t w = v; - v = r->nodes_[w]->rank; // Replace v entry with its rank - r->nodes_[w]->visited = false; // Prepare for future DFS calls + // Replace v entry with its rank + v = r->nodes_[static_cast<uint32_t>(w)]->rank; + // Prepare for future DFS calls + r->nodes_[static_cast<uint32_t>(w)]->visited = false; dst->push_back(w); } } @@ -647,7 +652,8 @@ int GraphCycles::FindPath(GraphId idx, GraphId idy, int max_path_len, } if (path_len < max_path_len) { - path[path_len] = MakeId(n, rep_->nodes_[n]->version); + path[path_len] = + MakeId(n, rep_->nodes_[static_cast<uint32_t>(n)]->version); } path_len++; r->stack_.push_back(-1); // Will remove tentative path entry @@ -656,7 +662,7 @@ int GraphCycles::FindPath(GraphId idx, GraphId idy, int max_path_len, return path_len; } - HASH_FOR_EACH(w, r->nodes_[n]->out) { + HASH_FOR_EACH(w, r->nodes_[static_cast<uint32_t>(n)]->out) { if (seen.insert(w)) { r->stack_.push_back(w); } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/kernel_timeout.h b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/kernel_timeout.h index b4c8b55f13..a39d09ca86 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/kernel_timeout.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal/kernel_timeout.h @@ -19,8 +19,8 @@ // Constructible from a y_absl::Time (for a timeout to be respected) or {} // (for "no timeout".) // This is a private low-level API for use by a handful of low-level -// components that are friends of this class. Higher-level components -// should build APIs based on y_absl::Time and y_absl::Duration. +// components. Higher-level components should build APIs based on +// y_absl::Time and y_absl::Duration. #ifndef Y_ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ #define Y_ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ @@ -28,6 +28,7 @@ #include <time.h> #include <algorithm> +#include <cstdint> #include <limits> #include "y_absl/base/internal/raw_logging.h" @@ -38,7 +39,6 @@ namespace y_absl { Y_ABSL_NAMESPACE_BEGIN namespace synchronization_internal { -class Futex; class Waiter; class KernelTimeout { @@ -60,7 +60,10 @@ class KernelTimeout { // Convert to parameter for sem_timedwait/futex/similar. Only for approved // users. Do not call if !has_timeout. - struct timespec MakeAbsTimespec(); + struct timespec MakeAbsTimespec() const; + + // Convert to unix epoch nanos. Do not call if !has_timeout. + int64_t MakeAbsNanos() const; private: // internal rep, not user visible: ns after unix epoch. @@ -111,7 +114,8 @@ class KernelTimeout { constexpr uint64_t max_nanos = (std::numeric_limits<int64_t>::max)() - 999999u; uint64_t ms_from_now = - (std::min<uint64_t>(max_nanos, ns_ - now) + 999999u) / 1000000u; + ((std::min)(max_nanos, static_cast<uint64_t>(ns_ - now)) + 999999u) / + 1000000u; if (ms_from_now > kInfinite) { return kInfinite; } @@ -119,13 +123,12 @@ class KernelTimeout { } return 0; } -#endif - friend class Futex; friend class Waiter; +#endif }; -inline struct timespec KernelTimeout::MakeAbsTimespec() { +inline struct timespec KernelTimeout::MakeAbsTimespec() const { int64_t n = ns_; static const int64_t kNanosPerSecond = 1000 * 1000 * 1000; if (n == 0) { @@ -149,6 +152,17 @@ inline struct timespec KernelTimeout::MakeAbsTimespec() { return abstime; } +inline int64_t KernelTimeout::MakeAbsNanos() const { + if (ns_ == 0) { + Y_ABSL_RAW_LOG( + ERROR, "Tried to create a timeout from a non-timeout; never do this."); + // But we'll try to continue sanely. no-timeout ~= saturated timeout. + return (std::numeric_limits<int64_t>::max)(); + } + + return ns_; +} + } // namespace synchronization_internal Y_ABSL_NAMESPACE_END } // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/mutex.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/mutex.cc index 27e6a84f7a..a8a6ca8d67 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/mutex.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/mutex.cc @@ -36,6 +36,9 @@ #include <algorithm> #include <atomic> #include <cinttypes> +#include <cstddef> +#include <cstring> +#include <iterator> #include <thread> // NOLINT(build/c++11) #include "y_absl/base/attributes.h" @@ -51,6 +54,7 @@ #include "y_absl/base/internal/sysinfo.h" #include "y_absl/base/internal/thread_identity.h" #include "y_absl/base/internal/tsan_mutex_interface.h" +#include "y_absl/base/optimization.h" #include "y_absl/base/port.h" #include "y_absl/debugging/stacktrace.h" #include "y_absl/debugging/symbolize.h" @@ -134,25 +138,42 @@ enum DelayMode { AGGRESSIVE, GENTLE }; struct Y_ABSL_CACHELINE_ALIGNED MutexGlobals { y_absl::once_flag once; int spinloop_iterations = 0; - int32_t mutex_sleep_limit[2] = {}; + int32_t mutex_sleep_spins[2] = {}; + y_absl::Duration mutex_sleep_time; }; +y_absl::Duration MeasureTimeToYield() { + y_absl::Time before = y_absl::Now(); + Y_ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)(); + return y_absl::Now() - before; +} + const MutexGlobals &GetMutexGlobals() { Y_ABSL_CONST_INIT static MutexGlobals data; y_absl::base_internal::LowLevelCallOnce(&data.once, [&]() { const int num_cpus = y_absl::base_internal::NumCPUs(); data.spinloop_iterations = num_cpus > 1 ? 1500 : 0; - // If this a uniprocessor, only yield/sleep. Otherwise, if the mode is + // If this a uniprocessor, only yield/sleep. + // Real-time threads are often unable to yield, so the sleep time needs + // to be long enough to keep the calling thread asleep until scheduling + // happens. + // If this is multiprocessor, allow spinning. If the mode is // aggressive then spin many times before yielding. If the mode is // gentle then spin only a few times before yielding. Aggressive spinning // is used to ensure that an Unlock() call, which must get the spin lock // for any thread to make progress gets it without undue delay. if (num_cpus > 1) { - data.mutex_sleep_limit[AGGRESSIVE] = 5000; - data.mutex_sleep_limit[GENTLE] = 250; + data.mutex_sleep_spins[AGGRESSIVE] = 5000; + data.mutex_sleep_spins[GENTLE] = 250; + data.mutex_sleep_time = y_absl::Microseconds(10); } else { - data.mutex_sleep_limit[AGGRESSIVE] = 0; - data.mutex_sleep_limit[GENTLE] = 0; + data.mutex_sleep_spins[AGGRESSIVE] = 0; + data.mutex_sleep_spins[GENTLE] = 0; + data.mutex_sleep_time = MeasureTimeToYield() * 5; + data.mutex_sleep_time = + std::min(data.mutex_sleep_time, y_absl::Milliseconds(1)); + data.mutex_sleep_time = + std::max(data.mutex_sleep_time, y_absl::Microseconds(10)); } }); return data; @@ -163,7 +184,8 @@ namespace synchronization_internal { // Returns the Mutex delay on iteration `c` depending on the given `mode`. // The returned value should be used as `c` for the next call to `MutexDelay`. int MutexDelay(int32_t c, int mode) { - const int32_t limit = GetMutexGlobals().mutex_sleep_limit[mode]; + const int32_t limit = GetMutexGlobals().mutex_sleep_spins[mode]; + const y_absl::Duration sleep_time = GetMutexGlobals().mutex_sleep_time; if (c < limit) { // Spin. c++; @@ -176,7 +198,7 @@ int MutexDelay(int32_t c, int mode) { c++; } else { // Then wait. - y_absl::SleepFor(y_absl::Microseconds(10)); + y_absl::SleepFor(sleep_time); c = 0; } Y_ABSL_TSAN_MUTEX_POST_DIVERT(nullptr, 0); @@ -325,7 +347,7 @@ static struct SynchEvent { // this is a trivial hash table for the events static SynchEvent *EnsureSynchEvent(std::atomic<intptr_t> *addr, const char *name, intptr_t bits, intptr_t lockbit) { - uint32_t h = reinterpret_cast<intptr_t>(addr) % kNSynchEvent; + uint32_t h = reinterpret_cast<uintptr_t>(addr) % kNSynchEvent; SynchEvent *e; // first look for existing SynchEvent struct.. synch_event_mu.Lock(); @@ -378,7 +400,7 @@ static void UnrefSynchEvent(SynchEvent *e) { // is clear before doing so). static void ForgetSynchEvent(std::atomic<intptr_t> *addr, intptr_t bits, intptr_t lockbit) { - uint32_t h = reinterpret_cast<intptr_t>(addr) % kNSynchEvent; + uint32_t h = reinterpret_cast<uintptr_t>(addr) % kNSynchEvent; SynchEvent **pe; SynchEvent *e; synch_event_mu.Lock(); @@ -402,7 +424,7 @@ static void ForgetSynchEvent(std::atomic<intptr_t> *addr, intptr_t bits, // "addr", if any. The pointer returned is valid until the UnrefSynchEvent() is // called. static SynchEvent *GetSynchEvent(const void *addr) { - uint32_t h = reinterpret_cast<intptr_t>(addr) % kNSynchEvent; + uint32_t h = reinterpret_cast<uintptr_t>(addr) % kNSynchEvent; SynchEvent *e; synch_event_mu.Lock(); for (e = synch_event[h]; @@ -430,7 +452,13 @@ static void PostSynchEvent(void *obj, int ev) { char buffer[Y_ABSL_ARRAYSIZE(pcs) * 24]; int pos = snprintf(buffer, sizeof (buffer), " @"); for (int i = 0; i != n; i++) { - pos += snprintf(&buffer[pos], sizeof (buffer) - pos, " %p", pcs[i]); + int b = snprintf(&buffer[pos], sizeof(buffer) - static_cast<size_t>(pos), + " %p", pcs[i]); + if (b < 0 || + static_cast<size_t>(b) >= sizeof(buffer) - static_cast<size_t>(pos)) { + break; + } + pos += b; } Y_ABSL_RAW_LOG(INFO, "%s%p %s %s", event_properties[ev].msg, obj, (e == nullptr ? "" : e->name), buffer); @@ -486,7 +514,8 @@ struct SynchWaitParams { cvmu(cvmu_arg), thread(thread_arg), cv_word(cv_word_arg), - contention_start_cycles(base_internal::CycleClock::Now()) {} + contention_start_cycles(base_internal::CycleClock::Now()), + should_submit_contention_data(false) {} const Mutex::MuHow how; // How this thread needs to wait. const Condition *cond; // The condition that this thread is waiting for. @@ -504,6 +533,7 @@ struct SynchWaitParams { int64_t contention_start_cycles; // Time (in cycles) when this thread started // to contend for the mutex. + bool should_submit_contention_data; }; struct SynchLocksHeld { @@ -562,10 +592,15 @@ static SynchLocksHeld *Synch_GetAllLocks() { void Mutex::IncrementSynchSem(Mutex *mu, PerThreadSynch *w) { if (mu) { Y_ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0); - } - PerThreadSem::Post(w->thread_identity()); - if (mu) { + // We miss synchronization around passing PerThreadSynch between threads + // since it happens inside of the Mutex code, so we need to ignore all + // accesses to the object. + Y_ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN(); + PerThreadSem::Post(w->thread_identity()); + Y_ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END(); Y_ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0); + } else { + PerThreadSem::Post(w->thread_identity()); } } @@ -1120,7 +1155,7 @@ void Mutex::TryRemove(PerThreadSynch *s) { // if the wait extends past the absolute time specified, even if "s" is still // on the mutex queue. In this case, remove "s" from the queue and return // true, otherwise return false. -Y_ABSL_XRAY_LOG_ARGS(1) void Mutex::Block(PerThreadSynch *s) { +void Mutex::Block(PerThreadSynch *s) { while (s->state.load(std::memory_order_acquire) == PerThreadSynch::kQueued) { if (!DecrementSynchSem(this, s, s->waitp->timeout)) { // After a timeout, we go into a spin loop until we remove ourselves @@ -1273,15 +1308,17 @@ static char *StackString(void **pcs, int n, char *buf, int maxlen, char sym[kSymLen]; int len = 0; for (int i = 0; i != n; i++) { + if (len >= maxlen) + return buf; + size_t count = static_cast<size_t>(maxlen - len); if (symbolize) { if (!symbolizer(pcs[i], sym, kSymLen)) { sym[0] = '\0'; } - snprintf(buf + len, maxlen - len, "%s\t@ %p %s\n", - (i == 0 ? "\n" : ""), - pcs[i], sym); + snprintf(buf + len, count, "%s\t@ %p %s\n", (i == 0 ? "\n" : ""), pcs[i], + sym); } else { - snprintf(buf + len, maxlen - len, " %p", pcs[i]); + snprintf(buf + len, count, " %p", pcs[i]); } len += strlen(&buf[len]); } @@ -1366,12 +1403,12 @@ static GraphId DeadlockCheck(Mutex *mu) { bool symbolize = number_of_reported_deadlocks <= 2; Y_ABSL_RAW_LOG(ERROR, "Potential Mutex deadlock: %s", CurrentStackString(b->buf, sizeof (b->buf), symbolize)); - int len = 0; + size_t len = 0; for (int j = 0; j != all_locks->n; j++) { void* pr = deadlock_graph->Ptr(all_locks->locks[j].id); if (pr != nullptr) { snprintf(b->buf + len, sizeof (b->buf) - len, " %p", pr); - len += static_cast<int>(strlen(&b->buf[len])); + len += strlen(&b->buf[len]); } } Y_ABSL_RAW_LOG(ERROR, @@ -1467,7 +1504,7 @@ static bool TryAcquireWithSpinning(std::atomic<intptr_t>* mu) { return false; } -Y_ABSL_XRAY_LOG_ARGS(1) void Mutex::Lock() { +void Mutex::Lock() { Y_ABSL_TSAN_MUTEX_PRE_LOCK(this, 0); GraphId id = DebugOnlyDeadlockCheck(this); intptr_t v = mu_.load(std::memory_order_relaxed); @@ -1485,7 +1522,7 @@ Y_ABSL_XRAY_LOG_ARGS(1) void Mutex::Lock() { Y_ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0); } -Y_ABSL_XRAY_LOG_ARGS(1) void Mutex::ReaderLock() { +void Mutex::ReaderLock() { Y_ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock); GraphId id = DebugOnlyDeadlockCheck(this); intptr_t v = mu_.load(std::memory_order_relaxed); @@ -1598,7 +1635,7 @@ bool Mutex::AwaitCommon(const Condition &cond, KernelTimeout t) { return res; } -Y_ABSL_XRAY_LOG_ARGS(1) bool Mutex::TryLock() { +bool Mutex::TryLock() { Y_ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock); intptr_t v = mu_.load(std::memory_order_relaxed); if ((v & (kMuWriter | kMuReader | kMuEvent)) == 0 && // try fast acquire @@ -1627,7 +1664,7 @@ Y_ABSL_XRAY_LOG_ARGS(1) bool Mutex::TryLock() { return false; } -Y_ABSL_XRAY_LOG_ARGS(1) bool Mutex::ReaderTryLock() { +bool Mutex::ReaderTryLock() { Y_ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock | __tsan_mutex_try_lock); intptr_t v = mu_.load(std::memory_order_relaxed); @@ -1673,7 +1710,7 @@ Y_ABSL_XRAY_LOG_ARGS(1) bool Mutex::ReaderTryLock() { return false; } -Y_ABSL_XRAY_LOG_ARGS(1) void Mutex::Unlock() { +void Mutex::Unlock() { Y_ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0); DebugOnlyLockLeave(this); intptr_t v = mu_.load(std::memory_order_relaxed); @@ -1725,7 +1762,7 @@ static bool ExactlyOneReader(intptr_t v) { return (v & kMuMultipleWaitersMask) == 0; } -Y_ABSL_XRAY_LOG_ARGS(1) void Mutex::ReaderUnlock() { +void Mutex::ReaderUnlock() { Y_ABSL_TSAN_MUTEX_PRE_UNLOCK(this, __tsan_mutex_read_lock); DebugOnlyLockLeave(this); intptr_t v = mu_.load(std::memory_order_relaxed); @@ -1755,7 +1792,7 @@ static intptr_t ClearDesignatedWakerMask(int flag) { case 1: // blocked; turn off the designated waker bit return ~static_cast<intptr_t>(kMuDesig); } - Y_ABSL_INTERNAL_UNREACHABLE; + Y_ABSL_UNREACHABLE(); } // Conditionally ignores the existence of waiting writers if a reader that has @@ -1769,7 +1806,7 @@ static intptr_t IgnoreWaitingWritersMask(int flag) { case 1: // blocked; pretend there are no waiting writers return ~static_cast<intptr_t>(kMuWrWait); } - Y_ABSL_INTERNAL_UNREACHABLE; + Y_ABSL_UNREACHABLE(); } // Internal version of LockWhen(). See LockSlowWithDeadline() @@ -1790,8 +1827,8 @@ static inline bool EvalConditionAnnotated(const Condition *cond, Mutex *mu, // operation tsan considers that we've already released the mutex. bool res = false; #ifdef Y_ABSL_INTERNAL_HAVE_TSAN_INTERFACE - const int flags = read_lock ? __tsan_mutex_read_lock : 0; - const int tryflags = flags | (trylock ? __tsan_mutex_try_lock : 0); + const uint32_t flags = read_lock ? __tsan_mutex_read_lock : 0; + const uint32_t tryflags = flags | (trylock ? __tsan_mutex_try_lock : 0); #endif if (locking) { // For lock we pretend that we have finished the operation, @@ -1904,7 +1941,7 @@ static void CheckForMutexCorruption(intptr_t v, const char* label) { // Test for either of two situations that should not occur in v: // kMuWriter and kMuReader // kMuWrWait and !kMuWait - const uintptr_t w = v ^ kMuWait; + const uintptr_t w = static_cast<uintptr_t>(v ^ kMuWait); // By flipping that bit, we can now test for: // kMuWriter and kMuReader in w // kMuWrWait and kMuWait in w @@ -2331,21 +2368,26 @@ Y_ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams *waitp) { } // end of for(;;)-loop if (wake_list != kPerThreadSynchNull) { - int64_t wait_cycles = 0; + int64_t total_wait_cycles = 0; + int64_t max_wait_cycles = 0; int64_t now = base_internal::CycleClock::Now(); do { - // Sample lock contention events only if the waiter was trying to acquire + // Profile lock contention events only if the waiter was trying to acquire // the lock, not waiting on a condition variable or Condition. if (!wake_list->cond_waiter) { - wait_cycles += (now - wake_list->waitp->contention_start_cycles); + int64_t cycles_waited = + (now - wake_list->waitp->contention_start_cycles); + total_wait_cycles += cycles_waited; + if (max_wait_cycles == 0) max_wait_cycles = cycles_waited; wake_list->waitp->contention_start_cycles = now; + wake_list->waitp->should_submit_contention_data = true; } wake_list = Wakeup(wake_list); // wake waiters } while (wake_list != kPerThreadSynchNull); - if (wait_cycles > 0) { - mutex_tracer("slow release", this, wait_cycles); + if (total_wait_cycles > 0) { + mutex_tracer("slow release", this, total_wait_cycles); Y_ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); - submit_profile_data(wait_cycles); + submit_profile_data(total_wait_cycles); Y_ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); } } @@ -2746,25 +2788,31 @@ static bool Dereference(void *arg) { return *(static_cast<bool *>(arg)); } -Condition::Condition() {} // null constructor, used for kTrue only -const Condition Condition::kTrue; +Y_ABSL_CONST_INIT const Condition Condition::kTrue; Condition::Condition(bool (*func)(void *), void *arg) : eval_(&CallVoidPtrFunction), - function_(func), - method_(nullptr), - arg_(arg) {} + arg_(arg) { + static_assert(sizeof(&func) <= sizeof(callback_), + "An overlarge function pointer passed to Condition."); + StoreCallback(func); +} bool Condition::CallVoidPtrFunction(const Condition *c) { - return (*c->function_)(c->arg_); + using FunctionPointer = bool (*)(void *); + FunctionPointer function_pointer; + std::memcpy(&function_pointer, c->callback_, sizeof(function_pointer)); + return (*function_pointer)(c->arg_); } Condition::Condition(const bool *cond) : eval_(CallVoidPtrFunction), - function_(Dereference), - method_(nullptr), // const_cast is safe since Dereference does not modify arg - arg_(const_cast<bool *>(cond)) {} + arg_(const_cast<bool *>(cond)) { + using FunctionPointer = bool (*)(void *); + const FunctionPointer dereference = Dereference; + StoreCallback(dereference); +} bool Condition::Eval() const { // eval_ == null for kTrue @@ -2772,14 +2820,15 @@ bool Condition::Eval() const { } bool Condition::GuaranteedEqual(const Condition *a, const Condition *b) { - if (a == nullptr) { + // kTrue logic. + if (a == nullptr || a->eval_ == nullptr) { return b == nullptr || b->eval_ == nullptr; + } else if (b == nullptr || b->eval_ == nullptr) { + return false; } - if (b == nullptr || b->eval_ == nullptr) { - return a->eval_ == nullptr; - } - return a->eval_ == b->eval_ && a->function_ == b->function_ && - a->arg_ == b->arg_ && a->method_ == b->method_; + // Check equality of the representative fields. + return a->eval_ == b->eval_ && a->arg_ == b->arg_ && + !memcmp(a->callback_, b->callback_, sizeof(a->callback_)); } Y_ABSL_NAMESPACE_END diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/mutex.h b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/mutex.h index 1cadd01bd8..4b0b54b1fb 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/mutex.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/mutex.h @@ -60,6 +60,8 @@ #include <atomic> #include <cstdint> +#include <cstring> +#include <iterator> #include <util/generic/string.h> #include "y_absl/base/const_init.h" @@ -612,12 +614,12 @@ class Y_ABSL_SCOPED_LOCKABLE WriterMutexLock { // Condition // ----------------------------------------------------------------------------- // -// As noted above, `Mutex` contains a number of member functions which take a -// `Condition` as an argument; clients can wait for conditions to become `true` -// before attempting to acquire the mutex. These sections are known as -// "condition critical" sections. To use a `Condition`, you simply need to -// construct it, and use within an appropriate `Mutex` member function; -// everything else in the `Condition` class is an implementation detail. +// `Mutex` contains a number of member functions which take a `Condition` as an +// argument; clients can wait for conditions to become `true` before attempting +// to acquire the mutex. These sections are known as "condition critical" +// sections. To use a `Condition`, you simply need to construct it, and use +// within an appropriate `Mutex` member function; everything else in the +// `Condition` class is an implementation detail. // // A `Condition` is specified as a function pointer which returns a boolean. // `Condition` functions should be pure functions -- their results should depend @@ -727,7 +729,7 @@ class Condition { : Condition(obj, static_cast<bool (T::*)() const>(&T::operator())) {} // A Condition that always returns `true`. - static const Condition kTrue; + Y_ABSL_CONST_INIT static const Condition kTrue; // Evaluates the condition. bool Eval() const; @@ -742,22 +744,54 @@ class Condition { static bool GuaranteedEqual(const Condition *a, const Condition *b); private: - typedef bool (*InternalFunctionType)(void * arg); - typedef bool (Condition::*InternalMethodType)(); - typedef bool (*InternalMethodCallerType)(void * arg, - InternalMethodType internal_method); - - bool (*eval_)(const Condition*); // Actual evaluator - InternalFunctionType function_; // function taking pointer returning bool - InternalMethodType method_; // method returning bool - void *arg_; // arg of function_ or object of method_ - - Condition(); // null constructor used only to create kTrue + // Sizing an allocation for a method pointer can be subtle. In the Itanium + // specifications, a method pointer has a predictable, uniform size. On the + // other hand, MSVC ABI, method pointer sizes vary based on the + // inheritance of the class. Specifically, method pointers from classes with + // multiple inheritance are bigger than those of classes with single + // inheritance. Other variations also exist. + +#ifndef _MSC_VER + // Allocation for a function pointer or method pointer. + // The {0} initializer ensures that all unused bytes of this buffer are + // always zeroed out. This is necessary, because GuaranteedEqual() compares + // all of the bytes, unaware of which bytes are relevant to a given `eval_`. + using MethodPtr = bool (Condition::*)(); + char callback_[sizeof(MethodPtr)] = {0}; +#else + // It is well known that the larget MSVC pointer-to-member is 24 bytes. This + // may be the largest known pointer-to-member of any platform. For this + // reason we will allocate 24 bytes for MSVC platform toolchains. + char callback_[24] = {0}; +#endif + + // Function with which to evaluate callbacks and/or arguments. + bool (*eval_)(const Condition*) = nullptr; + + // Either an argument for a function call or an object for a method call. + void *arg_ = nullptr; // Various functions eval_ can point to: static bool CallVoidPtrFunction(const Condition*); template <typename T> static bool CastAndCallFunction(const Condition* c); template <typename T> static bool CastAndCallMethod(const Condition* c); + + // Helper methods for storing, validating, and reading callback arguments. + template <typename T> + inline void StoreCallback(T callback) { + static_assert( + sizeof(callback) <= sizeof(callback_), + "An overlarge pointer was passed as a callback to Condition."); + std::memcpy(callback_, &callback, sizeof(callback)); + } + + template <typename T> + inline void ReadCallback(T *callback) const { + std::memcpy(callback, callback_, sizeof(*callback)); + } + + // Used only to create kTrue. + constexpr Condition() = default; }; // ----------------------------------------------------------------------------- @@ -949,56 +983,61 @@ inline CondVar::CondVar() : cv_(0) {} // static template <typename T> bool Condition::CastAndCallMethod(const Condition *c) { - typedef bool (T::*MemberType)(); - MemberType rm = reinterpret_cast<MemberType>(c->method_); - T *x = static_cast<T *>(c->arg_); - return (x->*rm)(); + T *object = static_cast<T *>(c->arg_); + bool (T::*method_pointer)(); + c->ReadCallback(&method_pointer); + return (object->*method_pointer)(); } // static template <typename T> bool Condition::CastAndCallFunction(const Condition *c) { - typedef bool (*FuncType)(T *); - FuncType fn = reinterpret_cast<FuncType>(c->function_); - T *x = static_cast<T *>(c->arg_); - return (*fn)(x); + bool (*function)(T *); + c->ReadCallback(&function); + T *argument = static_cast<T *>(c->arg_); + return (*function)(argument); } template <typename T> inline Condition::Condition(bool (*func)(T *), T *arg) : eval_(&CastAndCallFunction<T>), - function_(reinterpret_cast<InternalFunctionType>(func)), - method_(nullptr), - arg_(const_cast<void *>(static_cast<const void *>(arg))) {} + arg_(const_cast<void *>(static_cast<const void *>(arg))) { + static_assert(sizeof(&func) <= sizeof(callback_), + "An overlarge function pointer was passed to Condition."); + StoreCallback(func); +} template <typename T> inline Condition::Condition(T *object, bool (y_absl::internal::identity<T>::type::*method)()) : eval_(&CastAndCallMethod<T>), - function_(nullptr), - method_(reinterpret_cast<InternalMethodType>(method)), - arg_(object) {} + arg_(object) { + static_assert(sizeof(&method) <= sizeof(callback_), + "An overlarge method pointer was passed to Condition."); + StoreCallback(method); +} template <typename T> inline Condition::Condition(const T *object, bool (y_absl::internal::identity<T>::type::*method)() const) : eval_(&CastAndCallMethod<T>), - function_(nullptr), - method_(reinterpret_cast<InternalMethodType>(method)), - arg_(reinterpret_cast<void *>(const_cast<T *>(object))) {} + arg_(reinterpret_cast<void *>(const_cast<T *>(object))) { + StoreCallback(method); +} -// Register a hook for profiling support. +// Register hooks for profiling support. // // The function pointer registered here will be called whenever a mutex is // contended. The callback is given the cycles for which waiting happened (as // measured by //y_absl/base/internal/cycleclock.h, and which may not // be real "cycle" counts.) // -// Calls to this function do not race or block, but there is no ordering -// guaranteed between calls to this function and call to the provided hook. -// In particular, the previously registered hook may still be called for some -// time after this function returns. +// There is no ordering guarantee between when the hook is registered and when +// callbacks will begin. Only a single profiler can be installed in a running +// binary; if this function is called a second time with a different function +// pointer, the value is ignored (and will cause an assertion failure in debug +// mode.) void RegisterMutexProfiler(void (*fn)(int64_t wait_cycles)); // Register a hook for Mutex tracing. @@ -1011,13 +1050,11 @@ void RegisterMutexProfiler(void (*fn)(int64_t wait_cycles)); // // The only event name currently sent is "slow release". // -// This has the same memory ordering concerns as RegisterMutexProfiler() above. +// This has the same ordering and single-use limitations as +// RegisterMutexProfiler() above. void RegisterMutexTracer(void (*fn)(const char *msg, const void *obj, int64_t wait_cycles)); -// TODO(gfalcon): Combine RegisterMutexProfiler() and RegisterMutexTracer() -// into a single interface, since they are only ever called in pairs. - // Register a hook for CondVar tracing. // // The function pointer registered here will be called here on various CondVar @@ -1028,7 +1065,8 @@ void RegisterMutexTracer(void (*fn)(const char *msg, const void *obj, // Events that can be sent are "Wait", "Unwait", "Signal wakeup", and // "SignalAll wakeup". // -// This has the same memory ordering concerns as RegisterMutexProfiler() above. +// This has the same ordering and single-use limitations as +// RegisterMutexProfiler() above. void RegisterCondVarTracer(void (*fn)(const char *msg, const void *cv)); // Register a hook for symbolizing stack traces in deadlock detector reports. @@ -1038,7 +1076,8 @@ void RegisterCondVarTracer(void (*fn)(const char *msg, const void *cv)); // 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. +// This has the same ordering and single-use limitations as +// RegisterMutexProfiler() above. // // DEPRECATED: The default symbolizer function is y_absl::Symbolize() and the // ability to register a different hook for symbolizing stack traces will be diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/notification.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/notification.cc index f6e779f248..c56b2ed4b9 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/notification.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/notification.cc @@ -16,7 +16,6 @@ #include <atomic> -#include "y_absl/base/attributes.h" #include "y_absl/base/internal/raw_logging.h" #include "y_absl/synchronization/mutex.h" #include "y_absl/time/time.h" diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/notification.h b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/notification.h index e4a77271e9..9a82f3bbe8 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/notification.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/notification.h @@ -53,7 +53,6 @@ #include <atomic> #include "y_absl/base/attributes.h" -#include "y_absl/base/macros.h" #include "y_absl/synchronization/mutex.h" #include "y_absl/time/time.h" diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time.cc index b97ec570bf..32d6e7130a 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time.cc @@ -15,6 +15,7 @@ #include "y_absl/time/civil_time.h" #include <cstdlib> +#include <ostream> #include <util/generic/string.h> #include "y_absl/strings/str_cat.h" @@ -167,6 +168,31 @@ std::ostream& operator<<(std::ostream& os, CivilSecond s) { return os << FormatCivilTime(s); } +bool AbslParseFlag(string_view s, CivilSecond* c, TString*) { + return ParseLenientCivilTime(s, c); +} +bool AbslParseFlag(string_view s, CivilMinute* c, TString*) { + return ParseLenientCivilTime(s, c); +} +bool AbslParseFlag(string_view s, CivilHour* c, TString*) { + return ParseLenientCivilTime(s, c); +} +bool AbslParseFlag(string_view s, CivilDay* c, TString*) { + return ParseLenientCivilTime(s, c); +} +bool AbslParseFlag(string_view s, CivilMonth* c, TString*) { + return ParseLenientCivilTime(s, c); +} +bool AbslParseFlag(string_view s, CivilYear* c, TString*) { + return ParseLenientCivilTime(s, c); +} +TString AbslUnparseFlag(CivilSecond c) { return FormatCivilTime(c); } +TString AbslUnparseFlag(CivilMinute c) { return FormatCivilTime(c); } +TString AbslUnparseFlag(CivilHour c) { return FormatCivilTime(c); } +TString AbslUnparseFlag(CivilDay c) { return FormatCivilTime(c); } +TString AbslUnparseFlag(CivilMonth c) { return FormatCivilTime(c); } +TString AbslUnparseFlag(CivilYear c) { return FormatCivilTime(c); } + } // namespace time_internal Y_ABSL_NAMESPACE_END diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time.h b/contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time.h index 32ca3cfda4..2654bc56fa 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time.h @@ -70,8 +70,10 @@ #ifndef Y_ABSL_TIME_CIVIL_TIME_H_ #define Y_ABSL_TIME_CIVIL_TIME_H_ +#include <iosfwd> #include <util/generic/string.h> +#include "y_absl/base/config.h" #include "y_absl/strings/string_view.h" #include "y_absl/time/internal/cctz/include/cctz/civil_time.h" @@ -530,6 +532,29 @@ std::ostream& operator<<(std::ostream& os, CivilHour h); std::ostream& operator<<(std::ostream& os, CivilMinute m); std::ostream& operator<<(std::ostream& os, CivilSecond s); +// AbslParseFlag() +// +// Parses the command-line flag string representation `s` into a civil-time +// value. Flags must be specified in a format that is valid for +// `y_absl::ParseLenientCivilTime()`. +bool AbslParseFlag(y_absl::string_view s, CivilSecond* c, TString* error); +bool AbslParseFlag(y_absl::string_view s, CivilMinute* c, TString* error); +bool AbslParseFlag(y_absl::string_view s, CivilHour* c, TString* error); +bool AbslParseFlag(y_absl::string_view s, CivilDay* c, TString* error); +bool AbslParseFlag(y_absl::string_view s, CivilMonth* c, TString* error); +bool AbslParseFlag(y_absl::string_view s, CivilYear* c, TString* error); + +// AbslUnparseFlag() +// +// Unparses a civil-time value into a command-line string representation using +// the format specified by `y_absl::ParseCivilTime()`. +TString AbslUnparseFlag(CivilSecond c); +TString AbslUnparseFlag(CivilMinute c); +TString AbslUnparseFlag(CivilHour c); +TString AbslUnparseFlag(CivilDay c); +TString AbslUnparseFlag(CivilMonth c); +TString AbslUnparseFlag(CivilYear c); + } // namespace time_internal Y_ABSL_NAMESPACE_END diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/clock.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/clock.cc index d941b7a23c..7503e97a68 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/clock.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/clock.cc @@ -217,9 +217,11 @@ static int64_t GetCurrentTimeNanosFromKernel(uint64_t last_cycleclock, uint64_t elapsed_cycles; int loops = 0; do { - before_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW(); + before_cycles = + static_cast<uint64_t>(GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW()); current_time_nanos_from_system = GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); - after_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW(); + after_cycles = + static_cast<uint64_t>(GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW()); // elapsed_cycles is unsigned, so is large on overflow elapsed_cycles = after_cycles - before_cycles; if (elapsed_cycles >= local_approx_syscall_time_in_cycles && @@ -316,7 +318,8 @@ int64_t GetCurrentTimeNanos() { // contribute to register pressure - reading it early before initializing // the other pieces of the calculation minimizes spill/restore instructions, // minimizing icache cost. - uint64_t now_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW(); + uint64_t now_cycles = + static_cast<uint64_t>(GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW()); // Acquire pairs with the barrier in SeqRelease - if this load sees that // store, the shared-data reads necessarily see that SeqRelease's updates @@ -356,7 +359,8 @@ int64_t GetCurrentTimeNanos() { uint64_t delta_cycles; if (seq_read0 == seq_read1 && (seq_read0 & 1) == 0 && (delta_cycles = now_cycles - base_cycles) < min_cycles_per_sample) { - return base_ns + ((delta_cycles * nsscaled_per_cycle) >> kScale); + return static_cast<int64_t>( + base_ns + ((delta_cycles * nsscaled_per_cycle) >> kScale)); } return GetCurrentTimeNanosSlowPath(); } @@ -404,8 +408,8 @@ static int64_t GetCurrentTimeNanosSlowPath() // Sample the kernel time base. This is the definition of // "now" if we take the slow path. uint64_t now_cycles; - uint64_t now_ns = - GetCurrentTimeNanosFromKernel(time_state.last_now_cycles, &now_cycles); + uint64_t now_ns = static_cast<uint64_t>( + GetCurrentTimeNanosFromKernel(time_state.last_now_cycles, &now_cycles)); time_state.last_now_cycles = now_cycles; uint64_t estimated_base_ns; @@ -432,7 +436,7 @@ static int64_t GetCurrentTimeNanosSlowPath() time_state.lock.Unlock(); - return estimated_base_ns; + return static_cast<int64_t>(estimated_base_ns); } // Main part of the algorithm. Locks out readers, updates the approximation @@ -489,7 +493,8 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, uint64_t assumed_next_sample_delta_cycles = SafeDivideAndScale(kMinNSBetweenSamples, measured_nsscaled_per_cycle); - int64_t diff_ns = now_ns - estimated_base_ns; // estimate low by this much + // Estimate low by this much. + int64_t diff_ns = static_cast<int64_t>(now_ns - estimated_base_ns); // We want to set nsscaled_per_cycle so that our estimate of the ns time // at the assumed cycle time is the assumed ns time. @@ -500,7 +505,8 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, // of our current error, by solving: // kMinNSBetweenSamples + diff_ns - (diff_ns / 16) == // (assumed_next_sample_delta_cycles * nsscaled_per_cycle) >> kScale - ns = kMinNSBetweenSamples + diff_ns - (diff_ns / 16); + ns = static_cast<uint64_t>(static_cast<int64_t>(kMinNSBetweenSamples) + + diff_ns - (diff_ns / 16)); uint64_t new_nsscaled_per_cycle = SafeDivideAndScale(ns, assumed_next_sample_delta_cycles); if (new_nsscaled_per_cycle != 0 && @@ -558,7 +564,7 @@ constexpr y_absl::Duration MaxSleep() { // REQUIRES: to_sleep <= MaxSleep(). void SleepOnce(y_absl::Duration to_sleep) { #ifdef _WIN32 - Sleep(to_sleep / y_absl::Milliseconds(1)); + Sleep(static_cast<DWORD>(to_sleep / y_absl::Milliseconds(1))); #else struct timespec sleep_time = y_absl::ToTimespec(to_sleep); while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) { diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/duration.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/duration.cc index c9a3b73b73..55b8a04117 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/duration.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/duration.cc @@ -617,7 +617,7 @@ timespec ToTimespec(Duration d) { rep_lo -= kTicksPerSecond; } } - ts.tv_sec = rep_hi; + ts.tv_sec = static_cast<decltype(ts.tv_sec)>(rep_hi); if (ts.tv_sec == rep_hi) { // no time_t narrowing ts.tv_nsec = rep_lo / kTicksPerNanosecond; return ts; @@ -645,7 +645,7 @@ timeval ToTimeval(Duration d) { ts.tv_nsec -= 1000 * 1000 * 1000; } } - tv.tv_sec = ts.tv_sec; + tv.tv_sec = static_cast<decltype(tv.tv_sec)>(ts.tv_sec); if (tv.tv_sec != ts.tv_sec) { // narrowing if (ts.tv_sec < 0) { tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::min(); @@ -691,7 +691,7 @@ namespace { char* Format64(char* ep, int width, int64_t v) { do { --width; - *--ep = '0' + (v % 10); // contiguous digits + *--ep = static_cast<char>('0' + (v % 10)); // contiguous digits } while (v /= 10); while (--width >= 0) *--ep = '0'; // zero pad return ep; @@ -728,7 +728,7 @@ void AppendNumberUnit(TString* out, int64_t n, DisplayUnit unit) { char* const ep = buf + sizeof(buf); char* bp = Format64(ep, 0, n); if (*bp != '0' || bp + 1 != ep) { - out->append(bp, ep - bp); + out->append(bp, static_cast<size_t>(ep - bp)); out->append(unit.abbr.data(), unit.abbr.size()); } } @@ -745,12 +745,12 @@ void AppendNumberUnit(TString* out, double n, DisplayUnit unit) { int64_t int_part = d; if (int_part != 0 || frac_part != 0) { char* bp = Format64(ep, 0, int_part); // always < 1000 - out->append(bp, ep - bp); + out->append(bp, static_cast<size_t>(ep - bp)); if (frac_part != 0) { out->push_back('.'); bp = Format64(ep, prec, frac_part); while (ep[-1] == '0') --ep; - out->append(bp, ep - bp); + out->append(bp, static_cast<size_t>(ep - bp)); } out->append(unit.abbr.data(), unit.abbr.size()); } @@ -841,7 +841,7 @@ bool ConsumeDurationNumber(const char** dpp, const char* ep, int64_t* int_part, // in "*unit". The given string pointer is modified to point to the first // unconsumed char. bool ConsumeDurationUnit(const char** start, const char* end, Duration* unit) { - size_t size = end - *start; + size_t size = static_cast<size_t>(end - *start); switch (size) { case 0: return false; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/format.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/format.cc index 5e8bd8f7ba..fca2ff6043 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/format.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/format.cc @@ -64,7 +64,8 @@ cctz_parts Split(y_absl::Time t) { // details about rep_hi and rep_lo. y_absl::Time Join(const cctz_parts& parts) { const int64_t rep_hi = (parts.sec - unix_epoch()).count(); - const uint32_t rep_lo = parts.fem.count() / (1000 * 1000 / 4); + const uint32_t rep_lo = + static_cast<uint32_t>(parts.fem.count() / (1000 * 1000 / 4)); const auto d = time_internal::MakeDuration(rep_hi, rep_lo); return time_internal::FromUnixDuration(d); } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_format.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_format.cc index 1f6a76ad49..5e8f790ec0 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_format.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_format.cc @@ -19,7 +19,7 @@ #endif #if defined(HAS_STRPTIME) && HAS_STRPTIME -#if !defined(_XOPEN_SOURCE) +#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) #define _XOPEN_SOURCE // Definedness suffices for strptime. #endif #endif diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_info.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_info.cc index cf61e49c80..3444f6ced9 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_info.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_info.cc @@ -134,6 +134,21 @@ std::int_fast64_t Decode64(const char* cp) { return static_cast<std::int_fast64_t>(v - s64maxU - 1) - s64max - 1; } +// Does the rule for future transitions call for year-round daylight time? +// See tz/zic.c:stringzone() for the details on how such rules are encoded. +bool AllYearDST(const PosixTimeZone& posix) { + if (posix.dst_start.date.fmt != PosixTransition::N) return false; + if (posix.dst_start.date.n.day != 0) return false; + if (posix.dst_start.time.offset != 0) return false; + + if (posix.dst_end.date.fmt != PosixTransition::J) return false; + if (posix.dst_end.date.j.day != kDaysPerYear[0]) return false; + const auto offset = posix.std_offset - posix.dst_offset; + if (posix.dst_end.time.offset + offset != kSecsPerDay) return false; + + return true; +} + // Generate a year-relative offset for a PosixTransition. std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday, const PosixTransition& pt) { @@ -351,6 +366,12 @@ bool TimeZoneInfo::ExtendTransitions() { if (!GetTransitionType(posix.dst_offset, true, posix.dst_abbr, &dst_ti)) return false; + if (AllYearDST(posix)) { // dst only + // The future specification should match the last transition, and + // that means that handling the future will fall out naturally. + return EquivTransitions(transitions_.back().type_index, dst_ti); + } + // Extend the transitions for an additional 400 years using the // future specification. Years beyond those can be handled by // mapping back to a cycle-equivalent year within that range. @@ -481,9 +502,9 @@ bool TimeZoneInfo::Load(ZoneInfoSource* zip) { // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when // interpreting a POSIX spec that does not include start/end rules, and // that isn't the case here (see "zic -p"). - bp += (8 + 4) * hdr.leapcnt; // leap-time + TAI-UTC - bp += 1 * hdr.ttisstdcnt; // UTC/local indicators - bp += 1 * hdr.ttisutcnt; // standard/wall indicators + bp += (time_len + 4) * hdr.leapcnt; // leap-time + TAI-UTC + bp += 1 * hdr.ttisstdcnt; // UTC/local indicators + bp += 1 * hdr.ttisutcnt; // standard/wall indicators assert(bp == tbuf.data() + tbuf.size()); future_spec_.clear(); @@ -512,8 +533,8 @@ bool TimeZoneInfo::Load(ZoneInfoSource* zip) { // Trim redundant transitions. zic may have added these to work around // differences between the glibc and reference implementations (see - // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071). - // For us, they just get in the way when we do future_spec_ extension. + // zic.c:dontmerge) or to avoid bugs in old readers. For us, they just + // get in the way when we do future_spec_ extension. while (hdr.timecnt > 1) { if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index, transitions_[hdr.timecnt - 2].type_index)) { diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_lookup.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_lookup.cc index 4f26734918..cd2490c91c 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/time_zone_lookup.cc @@ -31,7 +31,7 @@ #if defined(__Fuchsia__) #error #include <fuchsia/intl/cpp/fidl.h> #error #include <lib/async-loop/cpp/loop.h> -#error #include <lib/sys/cpp/component_context.h> +#error #include <lib/fdio/directory.h> #error #include <zircon/types.h> #endif @@ -140,8 +140,9 @@ time_zone local_time_zone() { if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) { CFStringEncoding encoding = kCFStringEncodingUTF8; CFIndex length = CFStringGetLength(tz_name); - buffer.resize(CFStringGetMaximumSizeForEncoding(length, encoding) + 1); - if (CFStringGetCString(tz_name, &buffer[0], buffer.size(), encoding)) { + CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, encoding) + 1; + buffer.resize(static_cast<size_t>(max_size)); + if (CFStringGetCString(tz_name, &buffer[0], max_size, encoding)) { zone = &buffer[0]; } } @@ -160,11 +161,11 @@ time_zone local_time_zone() { // would be set to null when the loop is destroyed, causing any other FIDL // code running on the same thread to crash. async::Loop loop(&kAsyncLoopConfigNeverAttachToThread); - std::unique_ptr<sys::ComponentContext> context = - sys::ComponentContext::Create(); fuchsia::intl::PropertyProviderHandle handle; - zx_status_t status = context->svc()->Connect(handle.NewRequest()); + zx_status_t status = fdio_service_connect_by_name( + fuchsia::intl::PropertyProvider::Name_, + handle.NewRequest().TakeChannel().release()); if (status != ZX_OK) { return; } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/zone_info_source.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/zone_info_source.cc index 8342abe891..098283a681 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/zone_info_source.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/internal/cctz/src/zone_info_source.cc @@ -58,7 +58,8 @@ std::unique_ptr<y_absl::time_internal::cctz::ZoneInfoSource> DefaultFactory( // 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__) +#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__) && \ + !defined(__CYGWIN__) ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) = DefaultFactory; #elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION) @@ -66,41 +67,41 @@ extern ZoneInfoSourceFactory zone_info_source_factory; extern ZoneInfoSourceFactory default_factory; ZoneInfoSourceFactory default_factory = DefaultFactory; #if defined(_M_IX86) || defined(_M_ARM) -#pragma comment( \ - linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@@ZA=?default_factory@cctz_extension@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@@ZA") +#pragma comment( \ + linker, \ + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZA=?default_factory@cctz_extension@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" Y_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@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@@ZEA=?default_factory@cctz_extension@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@@ZEA") +#pragma comment( \ + linker, \ + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZEA=?default_factory@cctz_extension@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" Y_ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" Y_ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZEA") #else #error Unsupported MSVC platform #endif // _M_<PLATFORM> diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/time.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/time/time.cc index 68da5f1588..5664a4b4c1 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/time.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/time.cc @@ -297,7 +297,7 @@ timespec ToTimespec(Time t) { timespec ts; y_absl::Duration d = time_internal::ToUnixDuration(t); if (!time_internal::IsInfiniteDuration(d)) { - ts.tv_sec = time_internal::GetRepHi(d); + ts.tv_sec = static_cast<decltype(ts.tv_sec)>(time_internal::GetRepHi(d)); if (ts.tv_sec == time_internal::GetRepHi(d)) { // no time_t narrowing ts.tv_nsec = time_internal::GetRepLo(d) / 4; // floor return ts; @@ -316,7 +316,7 @@ timespec ToTimespec(Time t) { timeval ToTimeval(Time t) { timeval tv; timespec ts = y_absl::ToTimespec(t); - tv.tv_sec = ts.tv_sec; + tv.tv_sec = static_cast<decltype(tv.tv_sec)>(ts.tv_sec); if (tv.tv_sec != ts.tv_sec) { // narrowing if (ts.tv_sec < 0) { tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::min(); diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/time/time.h b/contrib/restricted/abseil-cpp-tstring/y_absl/time/time.h index 99009dfc30..affcd18c88 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/time/time.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/time/time.h @@ -78,6 +78,7 @@ struct timeval; #include <cmath> #include <cstdint> #include <ctime> +#include <limits> #include <ostream> #include <util/generic/string.h> #include <type_traits> @@ -97,19 +98,24 @@ class TimeZone; // Defined below namespace time_internal { int64_t IDivDuration(bool satq, Duration num, Duration den, Duration* rem); -constexpr Time FromUnixDuration(Duration d); -constexpr Duration ToUnixDuration(Time t); -constexpr int64_t GetRepHi(Duration d); -constexpr uint32_t GetRepLo(Duration d); -constexpr Duration MakeDuration(int64_t hi, uint32_t lo); -constexpr Duration MakeDuration(int64_t hi, int64_t lo); -inline Duration MakePosDoubleDuration(double n); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixDuration(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration ToUnixDuration(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr int64_t GetRepHi(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr uint32_t GetRepLo(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeDuration(int64_t hi, + uint32_t lo); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeDuration(int64_t hi, + int64_t lo); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration MakePosDoubleDuration(double n); constexpr int64_t kTicksPerNanosecond = 4; constexpr int64_t kTicksPerSecond = 1000 * 1000 * 1000 * kTicksPerNanosecond; template <std::intmax_t N> -constexpr Duration FromInt64(int64_t v, std::ratio<1, N>); -constexpr Duration FromInt64(int64_t v, std::ratio<60>); -constexpr Duration FromInt64(int64_t v, std::ratio<3600>); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v, + std::ratio<1, N>); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v, + std::ratio<60>); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v, + std::ratio<3600>); template <typename T> using EnableIfIntegral = typename std::enable_if< std::is_integral<T>::value || std::is_enum<T>::value, int>::type; @@ -222,37 +228,61 @@ class Duration { }; // Relational Operators -constexpr bool operator<(Duration lhs, Duration rhs); -constexpr bool operator>(Duration lhs, Duration rhs) { return rhs < lhs; } -constexpr bool operator>=(Duration lhs, Duration rhs) { return !(lhs < rhs); } -constexpr bool operator<=(Duration lhs, Duration rhs) { return !(rhs < lhs); } -constexpr bool operator==(Duration lhs, Duration rhs); -constexpr bool operator!=(Duration lhs, Duration rhs) { return !(lhs == rhs); } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<(Duration lhs, + Duration rhs); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator>(Duration lhs, + Duration rhs) { + return rhs < lhs; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator>=(Duration lhs, + Duration rhs) { + return !(lhs < rhs); +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<=(Duration lhs, + Duration rhs) { + return !(rhs < lhs); +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator==(Duration lhs, + Duration rhs); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator!=(Duration lhs, + Duration rhs) { + return !(lhs == rhs); +} // Additive Operators -constexpr Duration operator-(Duration d); -inline Duration operator+(Duration lhs, Duration rhs) { return lhs += rhs; } -inline Duration operator-(Duration lhs, Duration rhs) { return lhs -= rhs; } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration operator-(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration operator+(Duration lhs, + Duration rhs) { + return lhs += rhs; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration operator-(Duration lhs, + Duration rhs) { + return lhs -= rhs; +} // Multiplicative Operators // Integer operands must be representable as int64_t. template <typename T> -Duration operator*(Duration lhs, T rhs) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration operator*(Duration lhs, T rhs) { return lhs *= rhs; } template <typename T> -Duration operator*(T lhs, Duration rhs) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration operator*(T lhs, Duration rhs) { return rhs *= lhs; } template <typename T> -Duration operator/(Duration lhs, T rhs) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration operator/(Duration lhs, T rhs) { return lhs /= rhs; } -inline int64_t operator/(Duration lhs, Duration rhs) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t operator/(Duration lhs, + Duration rhs) { return time_internal::IDivDuration(true, lhs, rhs, &lhs); // trunc towards zero } -inline Duration operator%(Duration lhs, Duration rhs) { return lhs %= rhs; } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration operator%(Duration lhs, + Duration rhs) { + return lhs %= rhs; +} // IDivDuration() // @@ -299,18 +329,20 @@ inline int64_t IDivDuration(Duration num, Duration den, Duration* rem) { // // double d = y_absl::FDivDuration(y_absl::Milliseconds(1500), y_absl::Seconds(1)); // // d == 1.5 -double FDivDuration(Duration num, Duration den); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION double FDivDuration(Duration num, Duration den); // ZeroDuration() // // Returns a zero-length duration. This function behaves just like the default // constructor, but the name helps make the semantics clear at call sites. -constexpr Duration ZeroDuration() { return Duration(); } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration ZeroDuration() { + return Duration(); +} // AbsDuration() // // Returns the absolute value of a duration. -inline Duration AbsDuration(Duration d) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration AbsDuration(Duration d) { return (d < ZeroDuration()) ? -d : d; } @@ -322,7 +354,7 @@ inline Duration AbsDuration(Duration d) { // // y_absl::Duration d = y_absl::Nanoseconds(123456789); // y_absl::Duration a = y_absl::Trunc(d, y_absl::Microseconds(1)); // 123456us -Duration Trunc(Duration d, Duration unit); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Trunc(Duration d, Duration unit); // Floor() // @@ -333,7 +365,7 @@ Duration Trunc(Duration d, Duration unit); // // y_absl::Duration d = y_absl::Nanoseconds(123456789); // y_absl::Duration b = y_absl::Floor(d, y_absl::Microseconds(1)); // 123456us -Duration Floor(Duration d, Duration unit); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Floor(Duration d, Duration unit); // Ceil() // @@ -344,7 +376,7 @@ Duration Floor(Duration d, Duration unit); // // y_absl::Duration d = y_absl::Nanoseconds(123456789); // y_absl::Duration c = y_absl::Ceil(d, y_absl::Microseconds(1)); // 123457us -Duration Ceil(Duration d, Duration unit); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Ceil(Duration d, Duration unit); // InfiniteDuration() // @@ -380,7 +412,7 @@ Duration Ceil(Duration d, Duration unit); // // The examples involving the `/` operator above also apply to `IDivDuration()` // and `FDivDuration()`. -constexpr Duration InfiniteDuration(); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration InfiniteDuration(); // Nanoseconds() // Microseconds() @@ -404,27 +436,27 @@ constexpr Duration InfiniteDuration(); // y_absl::Duration a = y_absl::Seconds(60); // y_absl::Duration b = y_absl::Minutes(1); // b == a template <typename T, time_internal::EnableIfIntegral<T> = 0> -constexpr Duration Nanoseconds(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration Nanoseconds(T n) { return time_internal::FromInt64(n, std::nano{}); } template <typename T, time_internal::EnableIfIntegral<T> = 0> -constexpr Duration Microseconds(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration Microseconds(T n) { return time_internal::FromInt64(n, std::micro{}); } template <typename T, time_internal::EnableIfIntegral<T> = 0> -constexpr Duration Milliseconds(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration Milliseconds(T n) { return time_internal::FromInt64(n, std::milli{}); } template <typename T, time_internal::EnableIfIntegral<T> = 0> -constexpr Duration Seconds(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration Seconds(T n) { return time_internal::FromInt64(n, std::ratio<1>{}); } template <typename T, time_internal::EnableIfIntegral<T> = 0> -constexpr Duration Minutes(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration Minutes(T n) { return time_internal::FromInt64(n, std::ratio<60>{}); } template <typename T, time_internal::EnableIfIntegral<T> = 0> -constexpr Duration Hours(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration Hours(T n) { return time_internal::FromInt64(n, std::ratio<3600>{}); } @@ -438,19 +470,19 @@ constexpr Duration Hours(T n) { // auto a = y_absl::Seconds(1.5); // OK // auto b = y_absl::Milliseconds(1500); // BETTER template <typename T, time_internal::EnableIfFloat<T> = 0> -Duration Nanoseconds(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Nanoseconds(T n) { return n * Nanoseconds(1); } template <typename T, time_internal::EnableIfFloat<T> = 0> -Duration Microseconds(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Microseconds(T n) { return n * Microseconds(1); } template <typename T, time_internal::EnableIfFloat<T> = 0> -Duration Milliseconds(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Milliseconds(T n) { return n * Milliseconds(1); } template <typename T, time_internal::EnableIfFloat<T> = 0> -Duration Seconds(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Seconds(T n) { if (n >= 0) { // Note: `NaN >= 0` is false. if (n >= static_cast<T>((std::numeric_limits<int64_t>::max)())) { return InfiniteDuration(); @@ -464,11 +496,11 @@ Duration Seconds(T n) { } } template <typename T, time_internal::EnableIfFloat<T> = 0> -Duration Minutes(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Minutes(T n) { return n * Minutes(1); } template <typename T, time_internal::EnableIfFloat<T> = 0> -Duration Hours(T n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration Hours(T n) { return n * Hours(1); } @@ -488,14 +520,14 @@ Duration Hours(T n) { // // y_absl::Duration d = y_absl::Milliseconds(1500); // int64_t isec = y_absl::ToInt64Seconds(d); // isec == 1 -Y_ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Nanoseconds(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Microseconds(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Seconds(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Minutes(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Hours(Duration d); - -// ToDoubleNanoSeconds() +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Nanoseconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Microseconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Milliseconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Seconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Minutes(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Hours(Duration d); + +// ToDoubleNanoseconds() // ToDoubleMicroseconds() // ToDoubleMilliseconds() // ToDoubleSeconds() @@ -510,12 +542,12 @@ Y_ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Hours(Duration d); // // y_absl::Duration d = y_absl::Milliseconds(1500); // double dsec = y_absl::ToDoubleSeconds(d); // dsec == 1.5 -Y_ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleNanoseconds(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMicroseconds(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMilliseconds(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleSeconds(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMinutes(Duration d); -Y_ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleHours(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleNanoseconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleMicroseconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleMilliseconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleSeconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleMinutes(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleHours(Duration d); // FromChrono() // @@ -525,12 +557,18 @@ Y_ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleHours(Duration d); // // std::chrono::milliseconds ms(123); // y_absl::Duration d = y_absl::FromChrono(ms); -constexpr Duration FromChrono(const std::chrono::nanoseconds& d); -constexpr Duration FromChrono(const std::chrono::microseconds& d); -constexpr Duration FromChrono(const std::chrono::milliseconds& d); -constexpr Duration FromChrono(const std::chrono::seconds& d); -constexpr Duration FromChrono(const std::chrono::minutes& d); -constexpr Duration FromChrono(const std::chrono::hours& d); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::nanoseconds& d); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::microseconds& d); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::milliseconds& d); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::seconds& d); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::minutes& d); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::hours& d); // ToChronoNanoseconds() // ToChronoMicroseconds() @@ -550,18 +588,21 @@ constexpr Duration FromChrono(const std::chrono::hours& d); // auto y = y_absl::ToChronoNanoseconds(d); // x == y // auto z = y_absl::ToChronoSeconds(y_absl::InfiniteDuration()); // // z == std::chrono::seconds::max() -std::chrono::nanoseconds ToChronoNanoseconds(Duration d); -std::chrono::microseconds ToChronoMicroseconds(Duration d); -std::chrono::milliseconds ToChronoMilliseconds(Duration d); -std::chrono::seconds ToChronoSeconds(Duration d); -std::chrono::minutes ToChronoMinutes(Duration d); -std::chrono::hours ToChronoHours(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::nanoseconds ToChronoNanoseconds( + Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::microseconds ToChronoMicroseconds( + Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::milliseconds ToChronoMilliseconds( + Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::seconds ToChronoSeconds(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::minutes ToChronoMinutes(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::hours ToChronoHours(Duration d); // FormatDuration() // // Returns a string representing the duration in the form "72h3m0.5s". // Returns "inf" or "-inf" for +/- `InfiniteDuration()`. -TString FormatDuration(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION TString FormatDuration(Duration d); // Output stream operator. inline std::ostream& operator<<(std::ostream& os, Duration d) { @@ -725,29 +766,49 @@ class Time { }; // Relational Operators -constexpr bool operator<(Time lhs, Time rhs) { return lhs.rep_ < rhs.rep_; } -constexpr bool operator>(Time lhs, Time rhs) { return rhs < lhs; } -constexpr bool operator>=(Time lhs, Time rhs) { return !(lhs < rhs); } -constexpr bool operator<=(Time lhs, Time rhs) { return !(rhs < lhs); } -constexpr bool operator==(Time lhs, Time rhs) { return lhs.rep_ == rhs.rep_; } -constexpr bool operator!=(Time lhs, Time rhs) { return !(lhs == rhs); } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<(Time lhs, Time rhs) { + return lhs.rep_ < rhs.rep_; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator>(Time lhs, Time rhs) { + return rhs < lhs; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator>=(Time lhs, Time rhs) { + return !(lhs < rhs); +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<=(Time lhs, Time rhs) { + return !(rhs < lhs); +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator==(Time lhs, Time rhs) { + return lhs.rep_ == rhs.rep_; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator!=(Time lhs, Time rhs) { + return !(lhs == rhs); +} // Additive Operators -inline Time operator+(Time lhs, Duration rhs) { return lhs += rhs; } -inline Time operator+(Duration lhs, Time rhs) { return rhs += lhs; } -inline Time operator-(Time lhs, Duration rhs) { return lhs -= rhs; } -inline Duration operator-(Time lhs, Time rhs) { return lhs.rep_ - rhs.rep_; } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Time operator+(Time lhs, Duration rhs) { + return lhs += rhs; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Time operator+(Duration lhs, Time rhs) { + return rhs += lhs; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Time operator-(Time lhs, Duration rhs) { + return lhs -= rhs; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration operator-(Time lhs, Time rhs) { + return lhs.rep_ - rhs.rep_; +} // UnixEpoch() // // Returns the `y_absl::Time` representing "1970-01-01 00:00:00.0 +0000". -constexpr Time UnixEpoch() { return Time(); } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time UnixEpoch() { return Time(); } // UniversalEpoch() // // Returns the `y_absl::Time` representing "0001-01-01 00:00:00.0 +0000", the // epoch of the ICU Universal Time Scale. -constexpr Time UniversalEpoch() { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time UniversalEpoch() { // 719162 is the number of days from 0001-01-01 to 1970-01-01, // assuming the Gregorian calendar. return Time( @@ -757,7 +818,7 @@ constexpr Time UniversalEpoch() { // InfiniteFuture() // // Returns an `y_absl::Time` that is infinitely far in the future. -constexpr Time InfiniteFuture() { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time InfiniteFuture() { return Time(time_internal::MakeDuration((std::numeric_limits<int64_t>::max)(), ~uint32_t{0})); } @@ -765,7 +826,7 @@ constexpr Time InfiniteFuture() { // InfinitePast() // // Returns an `y_absl::Time` that is infinitely far in the past. -constexpr Time InfinitePast() { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time InfinitePast() { return Time(time_internal::MakeDuration((std::numeric_limits<int64_t>::min)(), ~uint32_t{0})); } @@ -779,13 +840,13 @@ constexpr Time InfinitePast() { // FromUniversal() // // Creates an `y_absl::Time` from a variety of other representations. -constexpr Time FromUnixNanos(int64_t ns); -constexpr Time FromUnixMicros(int64_t us); -constexpr Time FromUnixMillis(int64_t ms); -constexpr Time FromUnixSeconds(int64_t s); -constexpr Time FromTimeT(time_t t); -Time FromUDate(double udate); -Time FromUniversal(int64_t universal); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixNanos(int64_t ns); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixMicros(int64_t us); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixMillis(int64_t ms); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixSeconds(int64_t s); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromTimeT(time_t t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Time FromUDate(double udate); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Time FromUniversal(int64_t universal); // ToUnixNanos() // ToUnixMicros() @@ -799,13 +860,13 @@ Time FromUniversal(int64_t universal); // these operations round down toward negative infinity where necessary to // adjust to the resolution of the result type. Beware of possible time_t // over/underflow in ToTime{T,val,spec}() on 32-bit platforms. -int64_t ToUnixNanos(Time t); -int64_t ToUnixMicros(Time t); -int64_t ToUnixMillis(Time t); -int64_t ToUnixSeconds(Time t); -time_t ToTimeT(Time t); -double ToUDate(Time t); -int64_t ToUniversal(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUnixNanos(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUnixMicros(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUnixMillis(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUnixSeconds(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION time_t ToTimeT(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION double ToUDate(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUniversal(Time t); // DurationFromTimespec() // DurationFromTimeval() @@ -821,14 +882,14 @@ int64_t ToUniversal(Time t); // and gettimeofday(2)), so conversion functions are provided for both cases. // The "to timespec/val" direction is easily handled via overloading, but // for "from timespec/val" the desired type is part of the function name. -Duration DurationFromTimespec(timespec ts); -Duration DurationFromTimeval(timeval tv); -timespec ToTimespec(Duration d); -timeval ToTimeval(Duration d); -Time TimeFromTimespec(timespec ts); -Time TimeFromTimeval(timeval tv); -timespec ToTimespec(Time t); -timeval ToTimeval(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration DurationFromTimespec(timespec ts); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Duration DurationFromTimeval(timeval tv); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION timespec ToTimespec(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION timeval ToTimeval(Duration d); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Time TimeFromTimespec(timespec ts); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION Time TimeFromTimeval(timeval tv); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION timespec ToTimespec(Time t); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION timeval ToTimeval(Time t); // FromChrono() // @@ -839,7 +900,8 @@ timeval ToTimeval(Time t); // auto tp = std::chrono::system_clock::from_time_t(123); // y_absl::Time t = y_absl::FromChrono(tp); // // t == y_absl::FromTimeT(123) -Time FromChrono(const std::chrono::system_clock::time_point& tp); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION Time +FromChrono(const std::chrono::system_clock::time_point& tp); // ToChronoTime() // @@ -852,7 +914,8 @@ Time FromChrono(const std::chrono::system_clock::time_point& tp); // y_absl::Time t = y_absl::FromTimeT(123); // auto tp = y_absl::ToChronoTime(t); // // tp == std::chrono::system_clock::from_time_t(123); -std::chrono::system_clock::time_point ToChronoTime(Time); +Y_ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::system_clock::time_point + ToChronoTime(Time); // AbslParseFlag() // @@ -1124,22 +1187,25 @@ inline TimeZone LocalTimeZone() { // y_absl::Time t = ...; // y_absl::TimeZone tz = ...; // const auto cd = y_absl::ToCivilDay(t, tz); -inline CivilSecond ToCivilSecond(Time t, TimeZone tz) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilSecond ToCivilSecond(Time t, + TimeZone tz) { return tz.At(t).cs; // already a CivilSecond } -inline CivilMinute ToCivilMinute(Time t, TimeZone tz) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilMinute ToCivilMinute(Time t, + TimeZone tz) { return CivilMinute(tz.At(t).cs); } -inline CivilHour ToCivilHour(Time t, TimeZone tz) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilHour ToCivilHour(Time t, TimeZone tz) { return CivilHour(tz.At(t).cs); } -inline CivilDay ToCivilDay(Time t, TimeZone tz) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilDay ToCivilDay(Time t, TimeZone tz) { return CivilDay(tz.At(t).cs); } -inline CivilMonth ToCivilMonth(Time t, TimeZone tz) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilMonth ToCivilMonth(Time t, + TimeZone tz) { return CivilMonth(tz.At(t).cs); } -inline CivilYear ToCivilYear(Time t, TimeZone tz) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilYear ToCivilYear(Time t, TimeZone tz) { return CivilYear(tz.At(t).cs); } @@ -1155,7 +1221,8 @@ inline CivilYear ToCivilYear(Time t, TimeZone tz) { // being when two non-existent civil times map to the same transition time. // // Note: Accepts civil times of any alignment. -inline Time FromCivil(CivilSecond ct, TimeZone tz) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION inline Time FromCivil(CivilSecond ct, + TimeZone tz) { const auto ti = tz.At(ct); if (ti.kind == TimeZone::TimeInfo::SKIPPED) return ti.trans; return ti.pre; @@ -1240,13 +1307,13 @@ inline Time FromDateTime(int64_t year, int mon, int day, int hour, // instant, so `tm_isdst != 0` returns the DST instant, and `tm_isdst == 0` // returns the non-DST instant, that would have matched if the transition never // happened. -Time FromTM(const struct tm& tm, TimeZone tz); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION Time FromTM(const struct tm& tm, TimeZone tz); // ToTM() // // Converts the given `y_absl::Time` to a struct tm using the given time zone. // See ctime(3) for a description of the values of the tm fields. -struct tm ToTM(Time t, TimeZone tz); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION struct tm ToTM(Time t, TimeZone tz); // RFC3339_full // RFC3339_sec @@ -1305,13 +1372,14 @@ Y_ABSL_DLL extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z // `y_absl::InfinitePast()`, the returned string will be exactly "infinite-past". // In both cases the given format string and `y_absl::TimeZone` are ignored. // -TString FormatTime(y_absl::string_view format, Time t, TimeZone tz); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION TString FormatTime(y_absl::string_view format, + Time t, TimeZone tz); // Convenience functions that format the given time using the RFC3339_full // format. The first overload uses the provided TimeZone, while the second // uses LocalTimeZone(). -TString FormatTime(Time t, TimeZone tz); -TString FormatTime(Time t); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION TString FormatTime(Time t, TimeZone tz); +Y_ABSL_ATTRIBUTE_PURE_FUNCTION TString FormatTime(Time t); // Output stream operator. inline std::ostream& operator<<(std::ostream& os, Time t) { @@ -1389,18 +1457,20 @@ namespace time_internal { // Creates a Duration with a given representation. // REQUIRES: hi,lo is a valid representation of a Duration as specified // in time/duration.cc. -constexpr Duration MakeDuration(int64_t hi, uint32_t lo = 0) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeDuration(int64_t hi, + uint32_t lo = 0) { return Duration(hi, lo); } -constexpr Duration MakeDuration(int64_t hi, int64_t lo) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeDuration(int64_t hi, + int64_t lo) { return MakeDuration(hi, static_cast<uint32_t>(lo)); } // Make a Duration value from a floating-point number, as long as that number // is in the range [ 0 .. numeric_limits<int64_t>::max ), that is, as long as // it's positive and can be converted to int64_t without risk of UB. -inline Duration MakePosDoubleDuration(double n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration MakePosDoubleDuration(double n) { const int64_t int_secs = static_cast<int64_t>(n); const uint32_t ticks = static_cast<uint32_t>( std::round((n - static_cast<double>(int_secs)) * kTicksPerSecond)); @@ -1413,23 +1483,28 @@ inline Duration MakePosDoubleDuration(double n) { // pair. sec may be positive or negative. ticks must be in the range // -kTicksPerSecond < *ticks < kTicksPerSecond. If ticks is negative it // will be normalized to a positive value in the resulting Duration. -constexpr Duration MakeNormalizedDuration(int64_t sec, int64_t ticks) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeNormalizedDuration( + int64_t sec, int64_t ticks) { return (ticks < 0) ? MakeDuration(sec - 1, ticks + kTicksPerSecond) : MakeDuration(sec, ticks); } // Provide access to the Duration representation. -constexpr int64_t GetRepHi(Duration d) { return d.rep_hi_; } -constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr int64_t GetRepHi(Duration d) { + return d.rep_hi_; +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr uint32_t GetRepLo(Duration d) { + return d.rep_lo_; +} // Returns true iff d is positive or negative infinity. -constexpr bool IsInfiniteDuration(Duration d) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool IsInfiniteDuration(Duration d) { return GetRepLo(d) == ~uint32_t{0}; } // Returns an infinite Duration with the opposite sign. // REQUIRES: IsInfiniteDuration(d) -constexpr Duration OppositeInfinity(Duration d) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration OppositeInfinity(Duration d) { return GetRepHi(d) < 0 ? MakeDuration((std::numeric_limits<int64_t>::max)(), ~uint32_t{0}) : MakeDuration((std::numeric_limits<int64_t>::min)(), @@ -1437,7 +1512,8 @@ constexpr Duration OppositeInfinity(Duration d) { } // Returns (-n)-1 (equivalently -(n+1)) without avoidable overflow. -constexpr int64_t NegateAndSubtractOne(int64_t n) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr int64_t NegateAndSubtractOne( + int64_t n) { // Note: Good compilers will optimize this expression to ~n when using // a two's-complement representation (which is required for int64_t). return (n < 0) ? -(n + 1) : (-n) - 1; @@ -1447,23 +1523,30 @@ constexpr int64_t NegateAndSubtractOne(int64_t n) { // functions depend on the above mentioned choice of the Unix epoch for the // Time representation (and both need to be Time friends). Without this // knowledge, we would need to add-in/subtract-out UnixEpoch() respectively. -constexpr Time FromUnixDuration(Duration d) { return Time(d); } -constexpr Duration ToUnixDuration(Time t) { return t.rep_; } +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixDuration(Duration d) { + return Time(d); +} +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration ToUnixDuration(Time t) { + return t.rep_; +} template <std::intmax_t N> -constexpr Duration FromInt64(int64_t v, std::ratio<1, N>) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v, + std::ratio<1, N>) { static_assert(0 < N && N <= 1000 * 1000 * 1000, "Unsupported ratio"); // Subsecond ratios cannot overflow. return MakeNormalizedDuration( v / N, v % N * kTicksPerNanosecond * 1000 * 1000 * 1000 / N); } -constexpr Duration FromInt64(int64_t v, std::ratio<60>) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v, + std::ratio<60>) { return (v <= (std::numeric_limits<int64_t>::max)() / 60 && v >= (std::numeric_limits<int64_t>::min)() / 60) ? MakeDuration(v * 60) : v > 0 ? InfiniteDuration() : -InfiniteDuration(); } -constexpr Duration FromInt64(int64_t v, std::ratio<3600>) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v, + std::ratio<3600>) { return (v <= (std::numeric_limits<int64_t>::max)() / 3600 && v >= (std::numeric_limits<int64_t>::min)() / 3600) ? MakeDuration(v * 3600) @@ -1483,40 +1566,44 @@ constexpr auto IsValidRep64(char) -> bool { // Converts a std::chrono::duration to an y_absl::Duration. template <typename Rep, typename Period> -constexpr Duration FromChrono(const std::chrono::duration<Rep, Period>& d) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::duration<Rep, Period>& d) { static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid"); return FromInt64(int64_t{d.count()}, Period{}); } template <typename Ratio> -int64_t ToInt64(Duration d, Ratio) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64(Duration d, Ratio) { // Note: This may be used on MSVC, which may have a system_clock period of // std::ratio<1, 10 * 1000 * 1000> return ToInt64Seconds(d * Ratio::den / Ratio::num); } // Fastpath implementations for the 6 common duration units. -inline int64_t ToInt64(Duration d, std::nano) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, std::nano) { return ToInt64Nanoseconds(d); } -inline int64_t ToInt64(Duration d, std::micro) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, std::micro) { return ToInt64Microseconds(d); } -inline int64_t ToInt64(Duration d, std::milli) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, std::milli) { return ToInt64Milliseconds(d); } -inline int64_t ToInt64(Duration d, std::ratio<1>) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, + std::ratio<1>) { return ToInt64Seconds(d); } -inline int64_t ToInt64(Duration d, std::ratio<60>) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, + std::ratio<60>) { return ToInt64Minutes(d); } -inline int64_t ToInt64(Duration d, std::ratio<3600>) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, + std::ratio<3600>) { return ToInt64Hours(d); } // Converts an y_absl::Duration to a chrono duration of type T. template <typename T> -T ToChronoDuration(Duration d) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION T ToChronoDuration(Duration d) { using Rep = typename T::rep; using Period = typename T::period; static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid"); @@ -1530,7 +1617,8 @@ T ToChronoDuration(Duration d) { } // namespace time_internal -constexpr bool operator<(Duration lhs, Duration rhs) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<(Duration lhs, + Duration rhs) { return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs) ? time_internal::GetRepHi(lhs) < time_internal::GetRepHi(rhs) : time_internal::GetRepHi(lhs) == (std::numeric_limits<int64_t>::min)() @@ -1539,12 +1627,13 @@ constexpr bool operator<(Duration lhs, Duration rhs) { : time_internal::GetRepLo(lhs) < time_internal::GetRepLo(rhs); } -constexpr bool operator==(Duration lhs, Duration rhs) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator==(Duration lhs, + Duration rhs) { return time_internal::GetRepHi(lhs) == time_internal::GetRepHi(rhs) && time_internal::GetRepLo(lhs) == time_internal::GetRepLo(rhs); } -constexpr Duration operator-(Duration d) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration operator-(Duration d) { // This is a little interesting because of the special cases. // // If rep_lo_ is zero, we have it easy; it's safe to negate rep_hi_, we're @@ -1570,47 +1659,53 @@ constexpr Duration operator-(Duration d) { time_internal::GetRepLo(d)); } -constexpr Duration InfiniteDuration() { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration InfiniteDuration() { return time_internal::MakeDuration((std::numeric_limits<int64_t>::max)(), ~uint32_t{0}); } -constexpr Duration FromChrono(const std::chrono::nanoseconds& d) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::nanoseconds& d) { return time_internal::FromChrono(d); } -constexpr Duration FromChrono(const std::chrono::microseconds& d) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::microseconds& d) { return time_internal::FromChrono(d); } -constexpr Duration FromChrono(const std::chrono::milliseconds& d) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::milliseconds& d) { return time_internal::FromChrono(d); } -constexpr Duration FromChrono(const std::chrono::seconds& d) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::seconds& d) { return time_internal::FromChrono(d); } -constexpr Duration FromChrono(const std::chrono::minutes& d) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::minutes& d) { return time_internal::FromChrono(d); } -constexpr Duration FromChrono(const std::chrono::hours& d) { +Y_ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono( + const std::chrono::hours& d) { return time_internal::FromChrono(d); } -constexpr Time FromUnixNanos(int64_t ns) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixNanos(int64_t ns) { return time_internal::FromUnixDuration(Nanoseconds(ns)); } -constexpr Time FromUnixMicros(int64_t us) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixMicros(int64_t us) { return time_internal::FromUnixDuration(Microseconds(us)); } -constexpr Time FromUnixMillis(int64_t ms) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixMillis(int64_t ms) { return time_internal::FromUnixDuration(Milliseconds(ms)); } -constexpr Time FromUnixSeconds(int64_t s) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixSeconds(int64_t s) { return time_internal::FromUnixDuration(Seconds(s)); } -constexpr Time FromTimeT(time_t t) { +Y_ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromTimeT(time_t t) { return time_internal::FromUnixDuration(Seconds(t)); } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/types/internal/span.h b/contrib/restricted/abseil-cpp-tstring/y_absl/types/internal/span.h index 0ff6119f9e..3b30eabf1f 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/types/internal/span.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/types/internal/span.h @@ -28,10 +28,10 @@ namespace y_absl { Y_ABSL_NAMESPACE_BEGIN -namespace span_internal { -// A constexpr min function -constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; } +template <typename T> +class Span; +namespace span_internal { // Wrappers for access to container data pointers. template <typename C> constexpr auto GetDataImpl(C& c, char) noexcept // NOLINT(runtime/references) @@ -99,28 +99,39 @@ bool LessThanImpl(SpanT<T> a, SpanT<T> b) { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } -// The `IsConvertible` classes here are needed because of the -// `std::is_convertible` bug in libcxx when compiled with GCC. This build -// configuration is used by Android NDK toolchain. Reference link: -// https://bugs.llvm.org/show_bug.cgi?id=27538. template <typename From, typename To> -struct IsConvertibleHelper { - private: - static std::true_type testval(To); - static std::false_type testval(...); +using EnableIfConvertibleTo = + typename std::enable_if<std::is_convertible<From, To>::value>::type; + +// IsView is true for types where the return type of .data() is the same for +// mutable and const instances. This isn't foolproof, but it's only used to +// enable a compiler warning. +template <typename T, typename = void, typename = void> +struct IsView { + static constexpr bool value = false; +}; +template <typename T> +struct IsView< + T, y_absl::void_t<decltype(span_internal::GetData(std::declval<const T&>()))>, + y_absl::void_t<decltype(span_internal::GetData(std::declval<T&>()))>> { + private: + using Container = std::remove_const_t<T>; + using ConstData = + decltype(span_internal::GetData(std::declval<const Container&>())); + using MutData = decltype(span_internal::GetData(std::declval<Container&>())); public: - using type = decltype(testval(std::declval<From>())); + static constexpr bool value = std::is_same<ConstData, MutData>::value; }; -template <typename From, typename To> -struct IsConvertible : IsConvertibleHelper<From, To>::type {}; +// These enablers result in 'int' so they can be used as typenames or defaults +// in template paramters lists. +template <typename T> +using EnableIfIsView = std::enable_if_t<IsView<T>::value, int>; + +template <typename T> +using EnableIfNotIsView = std::enable_if_t<!IsView<T>::value, int>; -// TODO(zhangxy): replace `IsConvertible` with `std::is_convertible` once the -// older version of libcxx is not supported. -template <typename From, typename To> -using EnableIfConvertibleTo = - typename std::enable_if<IsConvertible<From, To>::value>::type; } // namespace span_internal Y_ABSL_NAMESPACE_END } // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/types/internal/variant.h b/contrib/restricted/abseil-cpp-tstring/y_absl/types/internal/variant.h index 03fb92016a..3e25015a99 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/types/internal/variant.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/types/internal/variant.h @@ -16,8 +16,8 @@ // separate file to avoid cluttering the top of the API header with // implementation details. -#ifndef Y_ABSL_TYPES_VARIANT_INTERNAL_H_ -#define Y_ABSL_TYPES_VARIANT_INTERNAL_H_ +#ifndef Y_ABSL_TYPES_INTERNAL_VARIANT_H_ +#define Y_ABSL_TYPES_INTERNAL_VARIANT_H_ #include <cassert> #include <cstddef> @@ -449,7 +449,7 @@ struct FlattenIndices; template <std::size_t HeadSize, std::size_t... TailSize> struct FlattenIndices<HeadSize, TailSize...> { - template<class... SizeType> + template <class... SizeType> static constexpr std::size_t Run(std::size_t head, SizeType... tail) { return head + HeadSize * FlattenIndices<TailSize...>::Run(tail...); } @@ -498,8 +498,8 @@ struct VisitIndicesVariadicImpl<y_absl::index_sequence<N...>, EndIndices...> { }; template <class Op, class... SizeType> - static VisitIndicesResultT<Op, decltype(EndIndices)...> Run( - Op&& op, SizeType... i) { + static VisitIndicesResultT<Op, decltype(EndIndices)...> Run(Op&& op, + SizeType... i) { return VisitIndicesSwitch<NumCasesOfSwitch<EndIndices...>::value>::Run( FlattenedOp<Op>{y_absl::forward<Op>(op)}, FlattenIndices<(EndIndices + std::size_t{1})...>::Run( @@ -683,13 +683,13 @@ struct VariantCoreAccess { variant_internal::IndexOfConstructedType<Left, QualifiedNew>; void operator()(SizeT<NewIndex::value> /*old_i*/ - ) const { + ) const { Access<NewIndex::value>(*left) = y_absl::forward<QualifiedNew>(other); } template <std::size_t OldIndex> void operator()(SizeT<OldIndex> /*old_i*/ - ) const { + ) const { using New = typename y_absl::variant_alternative<NewIndex::value, Left>::type; if (std::is_nothrow_constructible<New, QualifiedNew>::value || @@ -868,18 +868,6 @@ struct IsNeitherSelfNorInPlace<Self, in_place_type_t<T>> : std::false_type {}; template <class Self, std::size_t I> struct IsNeitherSelfNorInPlace<Self, in_place_index_t<I>> : std::false_type {}; -template <class Variant, class T, class = void> -struct ConversionIsPossibleImpl : std::false_type {}; - -template <class Variant, class T> -struct ConversionIsPossibleImpl< - Variant, T, - void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {}))>> - : std::true_type {}; - -template <class Variant, class T> -struct ConversionIsPossible : ConversionIsPossibleImpl<Variant, T>::type {}; - template <class Variant, class T> struct IndexOfConstructedType< Variant, T, @@ -1151,16 +1139,16 @@ struct VariantHelper<variant<Ts...>> { // Type metafunction which returns the element type selected if // OverloadSet::Overload() is well-formed when called with argument type U. template <typename U> - using BestMatch = decltype( - variant_internal::OverloadSet<Ts...>::Overload(std::declval<U>())); + using BestMatch = decltype(variant_internal::OverloadSet<Ts...>::Overload( + std::declval<U>())); // Type metafunction which returns true if OverloadSet::Overload() is // well-formed when called with argument type U. // CanAccept can't be just an alias because there is a MSVC bug on parameter // pack expansion involving decltype. template <typename U> - struct CanAccept : - std::integral_constant<bool, !std::is_void<BestMatch<U>>::value> {}; + struct CanAccept + : std::integral_constant<bool, !std::is_void<BestMatch<U>>::value> {}; // Type metafunction which returns true if Other is an instantiation of // variant, and variants's converting constructor from Other will be @@ -1183,8 +1171,8 @@ struct TrivialMoveOnly { // A union's defaulted copy/move constructor is deleted if any variant member's // copy/move constructor is nontrivial. template <typename T> -struct IsTriviallyMoveConstructible: - std::is_move_constructible<Union<T, TrivialMoveOnly>> {}; +struct IsTriviallyMoveConstructible + : std::is_move_constructible<Union<T, TrivialMoveOnly>> {}; // To guarantee triviality of all special-member functions that can be trivial, // we use a chain of conditional bases for each one. @@ -1419,14 +1407,14 @@ class VariantMoveAssignBaseNontrivial : protected VariantCopyBase<T...> { VariantMoveAssignBaseNontrivial& operator=( VariantMoveAssignBaseNontrivial const&) = default; - VariantMoveAssignBaseNontrivial& - operator=(VariantMoveAssignBaseNontrivial&& other) noexcept( - y_absl::conjunction<std::is_nothrow_move_constructible<T>..., - std::is_nothrow_move_assignable<T>...>::value) { - VisitIndices<sizeof...(T)>::Run( - VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_); - return *this; - } + VariantMoveAssignBaseNontrivial& + operator=(VariantMoveAssignBaseNontrivial&& other) noexcept( + y_absl::conjunction<std::is_nothrow_move_constructible<T>..., + std::is_nothrow_move_assignable<T>...>::value) { + VisitIndices<sizeof...(T)>::Run( + VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_); + return *this; + } protected: using Base::index_; @@ -1450,12 +1438,12 @@ class VariantCopyAssignBaseNontrivial : protected VariantMoveAssignBase<T...> { VariantCopyAssignBaseNontrivial& operator=( VariantCopyAssignBaseNontrivial&&) = default; - VariantCopyAssignBaseNontrivial& operator=( - const VariantCopyAssignBaseNontrivial& other) { - VisitIndices<sizeof...(T)>::Run( - VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_); - return *this; - } + VariantCopyAssignBaseNontrivial& operator=( + const VariantCopyAssignBaseNontrivial& other) { + VisitIndices<sizeof...(T)>::Run( + VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_); + return *this; + } protected: using Base::index_; @@ -1643,4 +1631,4 @@ Y_ABSL_NAMESPACE_END } // namespace y_absl #endif // !defined(Y_ABSL_USES_STD_VARIANT) -#endif // Y_ABSL_TYPES_VARIANT_INTERNAL_H_ +#endif // Y_ABSL_TYPES_INTERNAL_VARIANT_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/types/span.h b/contrib/restricted/abseil-cpp-tstring/y_absl/types/span.h index ec5338601d..90745382dd 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/types/span.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/types/span.h @@ -60,6 +60,7 @@ #include <type_traits> #include <utility> +#include "y_absl/base/attributes.h" #include "y_absl/base/internal/throw_delegate.h" #include "y_absl/base/macros.h" #include "y_absl/base/optimization.h" @@ -160,12 +161,12 @@ class Span { // Used to SFINAE-enable a function when the slice elements are const. template <typename U> - using EnableIfConstView = + using EnableIfValueIsConst = typename std::enable_if<std::is_const<T>::value, U>::type; // Used to SFINAE-enable a function when the slice elements are mutable. template <typename U> - using EnableIfMutableView = + using EnableIfValueIsMutable = typename std::enable_if<!std::is_const<T>::value, U>::type; public: @@ -196,13 +197,34 @@ class Span { // Explicit reference constructor for a mutable `Span<T>` type. Can be // replaced with MakeSpan() to infer the type parameter. template <typename V, typename = EnableIfConvertibleFrom<V>, - typename = EnableIfMutableView<V>> - explicit Span(V& v) noexcept // NOLINT(runtime/references) + typename = EnableIfValueIsMutable<V>, + typename = span_internal::EnableIfNotIsView<V>> + explicit Span( + V& v + Y_ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // NOLINT(runtime/references) : Span(span_internal::GetData(v), v.size()) {} // Implicit reference constructor for a read-only `Span<const T>` type template <typename V, typename = EnableIfConvertibleFrom<V>, - typename = EnableIfConstView<V>> + typename = EnableIfValueIsConst<V>, + typename = span_internal::EnableIfNotIsView<V>> + constexpr Span( + const V& v + Y_ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // NOLINT(runtime/explicit) + : Span(span_internal::GetData(v), v.size()) {} + + // Overloads of the above two functions that are only enabled for view types. + // This is so we can drop the Y_ABSL_ATTRIBUTE_LIFETIME_BOUND annotation. These + // overloads must be made unique by using a different template parameter list + // (hence the = 0 for the IsView enabler). + template <typename V, typename = EnableIfConvertibleFrom<V>, + typename = EnableIfValueIsMutable<V>, + span_internal::EnableIfIsView<V> = 0> + explicit Span(V& v) noexcept // NOLINT(runtime/references) + : Span(span_internal::GetData(v), v.size()) {} + template <typename V, typename = EnableIfConvertibleFrom<V>, + typename = EnableIfValueIsConst<V>, + span_internal::EnableIfIsView<V> = 0> constexpr Span(const V& v) noexcept // NOLINT(runtime/explicit) : Span(span_internal::GetData(v), v.size()) {} @@ -242,7 +264,7 @@ class Span { // Process(ints); // template <typename LazyT = T, - typename = EnableIfConstView<LazyT>> + typename = EnableIfValueIsConst<LazyT>> Span(std::initializer_list<value_type> v Y_ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // NOLINT(runtime/explicit) : Span(v.begin(), v.size()) {} @@ -398,7 +420,7 @@ class Span { // y_absl::MakeSpan(vec).subspan(5); // throws std::out_of_range constexpr Span subspan(size_type pos = 0, size_type len = npos) const { return (pos <= size()) - ? Span(data() + pos, span_internal::Min(size() - pos, len)) + ? Span(data() + pos, (std::min)(size() - pos, len)) : (base_internal::ThrowStdOutOfRange("pos > size()"), Span()); } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/utility/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/utility/ya.make index d53dc4c83a..41ce8373c6 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/utility/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/utility/ya.make @@ -6,9 +6,9 @@ LICENSE(Apache-2.0) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -VERSION(20220623.1) +VERSION(20230125.1) -ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20220623.1.tar.gz) +ORIGINAL_SOURCE(https://github.com/abseil/abseil-cpp/archive/20230125.1.tar.gz) NO_RUNTIME() |