diff options
author | heretic <heretic@yandex-team.ru> | 2022-02-10 16:45:43 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:43 +0300 |
commit | 397cbe258b9e064f49c4ca575279f02f39fef76e (patch) | |
tree | a0b0eb3cca6a14e4e8ea715393637672fa651284 /contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal | |
parent | 43f5a35593ebc9f6bcea619bb170394ea7ae468e (diff) | |
download | ydb-397cbe258b9e064f49c4ca575279f02f39fef76e.tar.gz |
Restoring authorship annotation for <heretic@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal')
32 files changed, 3953 insertions, 3953 deletions
diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_cord_internal/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_cord_internal/ya.make index 42b7b6cd5e..5b0a4c0695 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_cord_internal/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_cord_internal/ya.make @@ -1,42 +1,42 @@ -# Generated by devtools/yamaker. - -LIBRARY() - -WITHOUT_LICENSE_TEXTS() - -OWNER( - somov - g:cpp-contrib -) - -LICENSE(Apache-2.0) - -PEERDIR( - contrib/restricted/abseil-cpp-tstring/y_absl/base - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_wait - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/throw_delegate - contrib/restricted/abseil-cpp-tstring/y_absl/base/log_severity - contrib/restricted/abseil-cpp-tstring/y_absl/numeric - contrib/restricted/abseil-cpp-tstring/y_absl/strings +# Generated by devtools/yamaker. + +LIBRARY() + +WITHOUT_LICENSE_TEXTS() + +OWNER( + somov + g:cpp-contrib +) + +LICENSE(Apache-2.0) + +PEERDIR( + contrib/restricted/abseil-cpp-tstring/y_absl/base + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_wait + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/throw_delegate + contrib/restricted/abseil-cpp-tstring/y_absl/base/log_severity + contrib/restricted/abseil-cpp-tstring/y_absl/numeric + contrib/restricted/abseil-cpp-tstring/y_absl/strings contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_strings_internal -) - -ADDINCL( - GLOBAL contrib/restricted/abseil-cpp-tstring -) - -NO_COMPILER_WARNINGS() - +) + +ADDINCL( + GLOBAL contrib/restricted/abseil-cpp-tstring +) + +NO_COMPILER_WARNINGS() + SRCDIR(contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal) - -SRCS( + +SRCS( cord_internal.cc cord_rep_btree.cc cord_rep_btree_navigator.cc cord_rep_btree_reader.cc cord_rep_consume.cc cord_rep_ring.cc -) - -END() +) + +END() diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_strings_internal/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_strings_internal/ya.make index 4e57fc75f6..f1c30b2590 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_strings_internal/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_strings_internal/ya.make @@ -4,17 +4,17 @@ LIBRARY() WITHOUT_LICENSE_TEXTS() -OWNER( - somov - g:cpp-contrib -) +OWNER( + somov + g:cpp-contrib +) LICENSE(Apache-2.0) PEERDIR( - contrib/restricted/abseil-cpp-tstring/y_absl/base + contrib/restricted/abseil-cpp-tstring/y_absl/base contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_wait + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_wait contrib/restricted/abseil-cpp-tstring/y_absl/base/log_severity ) 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 25428e304c..873125a571 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 @@ -72,7 +72,7 @@ class Charmap { CharMaskForWord(x, 2), CharMaskForWord(x, 3)); } - // Containing all the chars in the C-string 's'. + // 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) { 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 72a4fa188b..2b6742a64e 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 @@ -208,7 +208,7 @@ int BigUnsigned<max_words>::ReadDigits(const char* begin, const char* end, ++dropped_digits; } if (begin < end && *std::prev(end) == '.') { - // If the string ends in '.', either before or after dropping zeroes, then + // If the string ends in '.', either before or after dropping zeroes, then // drop the decimal point and look for more digits to drop. dropped_digits = 0; --end; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_bigint.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_bigint.h index a77aab14dd..75df41a2e1 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_bigint.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/charconv_bigint.h @@ -66,7 +66,7 @@ class BigUnsigned { static_cast<uint32_t>(v >> 32)} {} // Constructs a BigUnsigned from the given string_view containing a decimal - // value. If the input string is not a decimal integer, constructs a 0 + // value. If the input string is not a decimal integer, constructs a 0 // instead. explicit BigUnsigned(y_absl::string_view sv) : size_(0), words_{} { // Check for valid input, returning a 0 otherwise. This is reasonable @@ -210,7 +210,7 @@ class BigUnsigned { return words_[index]; } - // Returns this integer as a decimal string. This is not used in the decimal- + // Returns this integer as a decimal string. This is not used in the decimal- // to-binary conversion; it is intended to aid in testing. TString ToString() const; 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 f0f78eb68c..cf92a22d56 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 @@ -246,8 +246,8 @@ constexpr int DigitMagnitude<16>() { // ConsumeDigits does not protect against overflow on *out; max_digits must // be chosen with respect to type T to avoid the possibility of overflow. template <int base, typename T> -int ConsumeDigits(const char* begin, const char* end, int max_digits, T* out, - bool* dropped_nonzero_digit) { +int ConsumeDigits(const char* begin, const char* end, int max_digits, T* out, + bool* dropped_nonzero_digit) { if (base == 10) { assert(max_digits <= std::numeric_limits<T>::digits10); } else if (base == 16) { @@ -282,7 +282,7 @@ int ConsumeDigits(const char* begin, const char* end, int max_digits, T* out, *dropped_nonzero_digit = true; } *out = accumulator; - return static_cast<int>(begin - original_begin); + return static_cast<int>(begin - original_begin); } // Returns true if `v` is one of the chars allowed inside parentheses following @@ -302,7 +302,7 @@ bool ParseInfinityOrNan(const char* begin, const char* end, switch (*begin) { case 'i': case 'I': { - // An infinity string consists of the characters "inf" or "infinity", + // An infinity string consists of the characters "inf" or "infinity", // case insensitive. if (strings_internal::memcasecmp(begin + 1, "nf", 2) != 0) { return false; @@ -326,7 +326,7 @@ bool ParseInfinityOrNan(const char* begin, const char* end, } out->type = strings_internal::FloatType::kNan; out->end = begin + 3; - // NaN is allowed to be followed by a parenthesized string, consisting of + // NaN is allowed to be followed by a parenthesized string, consisting of // only the characters [a-zA-Z0-9_]. Match that if it's present. begin += 3; if (begin < end && *begin == '(') { @@ -372,7 +372,7 @@ strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end, int exponent_adjustment = 0; bool mantissa_is_inexact = false; - int pre_decimal_digits = ConsumeDigits<base>( + int pre_decimal_digits = ConsumeDigits<base>( begin, end, MantissaDigitsMax<base>(), &mantissa, &mantissa_is_inexact); begin += pre_decimal_digits; int digits_left; @@ -398,14 +398,14 @@ strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end, while (begin < end && *begin == '0') { ++begin; } - int zeros_skipped = static_cast<int>(begin - begin_zeros); + int zeros_skipped = static_cast<int>(begin - begin_zeros); if (zeros_skipped >= DigitLimit<base>()) { // refuse to parse pathological inputs return result; } exponent_adjustment -= static_cast<int>(zeros_skipped); } - int post_decimal_digits = ConsumeDigits<base>( + int post_decimal_digits = ConsumeDigits<base>( begin, end, digits_left, &mantissa, &mantissa_is_inexact); begin += post_decimal_digits; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_internal.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_internal.cc index 6fc39985d8..d32e638883 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_internal.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_internal.cc @@ -1,89 +1,89 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include "y_absl/strings/internal/cord_internal.h" - -#include <atomic> -#include <cassert> -#include <memory> - -#include "y_absl/container/inlined_vector.h" +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "y_absl/strings/internal/cord_internal.h" + +#include <atomic> +#include <cassert> +#include <memory> + +#include "y_absl/container/inlined_vector.h" #include "y_absl/strings/internal/cord_rep_btree.h" -#include "y_absl/strings/internal/cord_rep_flat.h" -#include "y_absl/strings/internal/cord_rep_ring.h" - -namespace y_absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { - +#include "y_absl/strings/internal/cord_rep_flat.h" +#include "y_absl/strings/internal/cord_rep_ring.h" + +namespace y_absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + ABSL_CONST_INIT std::atomic<bool> cord_btree_enabled(kCordEnableBtreeDefault); -ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled( - kCordEnableRingBufferDefault); -ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled( - kCordShallowSubcordsDefault); +ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled( + kCordEnableRingBufferDefault); +ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled( + kCordShallowSubcordsDefault); ABSL_CONST_INIT std::atomic<bool> cord_btree_exhaustive_validation(false); - -void CordRep::Destroy(CordRep* rep) { - assert(rep != nullptr); - - y_absl::InlinedVector<CordRep*, Constants::kInlinedVectorSize> pending; - while (true) { - assert(!rep->refcount.IsImmortal()); - if (rep->tag == CONCAT) { - CordRepConcat* rep_concat = rep->concat(); - CordRep* right = rep_concat->right; - if (!right->refcount.Decrement()) { - pending.push_back(right); - } - CordRep* left = rep_concat->left; - delete rep_concat; - rep = nullptr; - if (!left->refcount.Decrement()) { - rep = left; - continue; - } + +void CordRep::Destroy(CordRep* rep) { + assert(rep != nullptr); + + y_absl::InlinedVector<CordRep*, Constants::kInlinedVectorSize> pending; + while (true) { + assert(!rep->refcount.IsImmortal()); + if (rep->tag == CONCAT) { + CordRepConcat* rep_concat = rep->concat(); + CordRep* right = rep_concat->right; + if (!right->refcount.Decrement()) { + pending.push_back(right); + } + CordRep* left = rep_concat->left; + delete rep_concat; + rep = nullptr; + if (!left->refcount.Decrement()) { + rep = left; + continue; + } } else if (rep->tag == BTREE) { CordRepBtree::Destroy(rep->btree()); rep = nullptr; - } else if (rep->tag == RING) { - CordRepRing::Destroy(rep->ring()); - rep = nullptr; - } else if (rep->tag == EXTERNAL) { - CordRepExternal::Delete(rep); - rep = nullptr; - } else if (rep->tag == SUBSTRING) { - CordRepSubstring* rep_substring = rep->substring(); - CordRep* child = rep_substring->child; - delete rep_substring; - rep = nullptr; - if (!child->refcount.Decrement()) { - rep = child; - continue; - } - } else { - CordRepFlat::Delete(rep); - rep = nullptr; - } - - if (!pending.empty()) { - rep = pending.back(); - pending.pop_back(); - } else { - break; - } - } -} - -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace y_absl + } else if (rep->tag == RING) { + CordRepRing::Destroy(rep->ring()); + rep = nullptr; + } else if (rep->tag == EXTERNAL) { + CordRepExternal::Delete(rep); + rep = nullptr; + } else if (rep->tag == SUBSTRING) { + CordRepSubstring* rep_substring = rep->substring(); + CordRep* child = rep_substring->child; + delete rep_substring; + rep = nullptr; + if (!child->refcount.Decrement()) { + rep = child; + continue; + } + } else { + CordRepFlat::Delete(rep); + rep = nullptr; + } + + if (!pending.empty()) { + rep = pending.back(); + pending.pop_back(); + } else { + break; + } + } +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace y_absl 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 82f5ac7b81..02b215f61f 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 @@ -1,4 +1,4 @@ -// Copyright 2021 The Abseil Authors. +// Copyright 2021 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,11 +21,11 @@ #include <cstdint> #include <type_traits> -#include "y_absl/base/config.h" -#include "y_absl/base/internal/endian.h" -#include "y_absl/base/internal/invoke.h" -#include "y_absl/base/optimization.h" -#include "y_absl/container/internal/compressed_tuple.h" +#include "y_absl/base/config.h" +#include "y_absl/base/internal/endian.h" +#include "y_absl/base/internal/invoke.h" +#include "y_absl/base/optimization.h" +#include "y_absl/container/internal/compressed_tuple.h" #include "y_absl/meta/type_traits.h" #include "y_absl/strings/string_view.h" @@ -33,19 +33,19 @@ namespace y_absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { -class CordzInfo; - -// Default feature enable states for cord ring buffers -enum CordFeatureDefaults { +class CordzInfo; + +// Default feature enable states for cord ring buffers +enum CordFeatureDefaults { kCordEnableBtreeDefault = true, - kCordEnableRingBufferDefault = false, - kCordShallowSubcordsDefault = false -}; - + kCordEnableRingBufferDefault = false, + kCordShallowSubcordsDefault = false +}; + extern std::atomic<bool> cord_btree_enabled; -extern std::atomic<bool> cord_ring_buffer_enabled; -extern std::atomic<bool> shallow_subcords_enabled; - +extern std::atomic<bool> cord_ring_buffer_enabled; +extern std::atomic<bool> shallow_subcords_enabled; + // `cord_btree_exhaustive_validation` can be set to force exhaustive validation // in debug assertions, and code that calls `IsValid()` explicitly. By default, // assertions should be relatively cheap and AssertValid() can easily lead to @@ -56,48 +56,48 @@ inline void enable_cord_btree(bool enable) { cord_btree_enabled.store(enable, std::memory_order_relaxed); } -inline void enable_cord_ring_buffer(bool enable) { - cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed); -} - -inline void enable_shallow_subcords(bool enable) { - shallow_subcords_enabled.store(enable, std::memory_order_relaxed); -} - -enum Constants { - // The inlined size to use with y_absl::InlinedVector. - // - // Note: The InlinedVectors in this file (and in cord.h) do not need to use - // the same value for their inlined size. The fact that they do is historical. - // It may be desirable for each to use a different inlined size optimized for - // that InlinedVector's usage. - // - // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for - // the inlined vector size (47 exists for backward compatibility). - kInlinedVectorSize = 47, - - // Prefer copying blocks of at most this size, otherwise reference count. - kMaxBytesToCopy = 511 -}; - +inline void enable_cord_ring_buffer(bool enable) { + cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed); +} + +inline void enable_shallow_subcords(bool enable) { + shallow_subcords_enabled.store(enable, std::memory_order_relaxed); +} + +enum Constants { + // The inlined size to use with y_absl::InlinedVector. + // + // Note: The InlinedVectors in this file (and in cord.h) do not need to use + // the same value for their inlined size. The fact that they do is historical. + // It may be desirable for each to use a different inlined size optimized for + // that InlinedVector's usage. + // + // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for + // the inlined vector size (47 exists for backward compatibility). + kInlinedVectorSize = 47, + + // Prefer copying blocks of at most this size, otherwise reference count. + kMaxBytesToCopy = 511 +}; + // 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 { public: constexpr RefcountAndFlags() : count_{kRefIncrement} {} - struct Immortal {}; + struct Immortal {}; explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {} struct WithCrc {}; explicit constexpr RefcountAndFlags(WithCrc) : count_(kCrcFlag | kRefIncrement) {} - // Increments the reference count. Imposes no memory ordering. - inline void Increment() { - count_.fetch_add(kRefIncrement, std::memory_order_relaxed); - } + // Increments the reference count. Imposes no memory ordering. + inline void Increment() { + count_.fetch_add(kRefIncrement, std::memory_order_relaxed); + } // Asserts that the current refcount is greater than 0. If the refcount is - // greater than 1, decrements the reference count. + // greater than 1, decrements the reference count. // // Returns false if there are no references outstanding; true otherwise. // Inserts barriers to ensure that state written before this method returns @@ -106,24 +106,24 @@ class RefcountAndFlags { inline bool Decrement() { int32_t refcount = count_.load(std::memory_order_acquire) & kRefcountMask; assert(refcount > 0 || refcount & kImmortalFlag); - return refcount != kRefIncrement && + return refcount != kRefIncrement && (count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) & kRefcountMask) != kRefIncrement; } // Same as Decrement but expect that refcount is greater than 1. inline bool DecrementExpectHighRefcount() { - int32_t refcount = + int32_t refcount = count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) & kRefcountMask; assert(refcount > 0 || refcount & kImmortalFlag); - return refcount != kRefIncrement; + return refcount != kRefIncrement; } // Returns the current reference count using acquire semantics. - inline int32_t Get() const { + inline int32_t Get() const { return count_.load(std::memory_order_acquire) >> kNumFlags; - } + } // Returns true if the referenced object carries a CRC value. bool HasCrc() const { @@ -151,22 +151,22 @@ class RefcountAndFlags { // // When this returns true, there are no other references, and data sinks // may safely adopt the children of the CordRep. - inline bool IsOne() { + inline bool IsOne() { return (count_.load(std::memory_order_acquire) & kRefcountMask) == kRefIncrement; - } + } - bool IsImmortal() const { + bool IsImmortal() const { return (count_.load(std::memory_order_relaxed) & kImmortalFlag) != 0; - } - + } + private: // We reserve the bottom bits for flags. // kImmortalBit indicates that this entity should never be collected; it is // used for the StringConstant constructor to avoid collecting immutable // constant cords. // kReservedFlag is reserved for future use. - enum { + enum { kNumFlags = 2, kImmortalFlag = 0x1, @@ -178,8 +178,8 @@ class RefcountAndFlags { // purposes of equality. (A refcount of 0 or 1 does not count as 0 or 1 // if the immortal bit is set.) kRefcountMask = ~kCrcFlag, - }; - + }; + std::atomic<int32_t> count_; }; @@ -189,30 +189,30 @@ class RefcountAndFlags { // functions in the base class. struct CordRepConcat; -struct CordRepExternal; -struct CordRepFlat; +struct CordRepExternal; +struct CordRepFlat; struct CordRepSubstring; -class CordRepRing; +class CordRepRing; class CordRepBtree; -// Various representations that we allow -enum CordRepKind { - CONCAT = 0, +// Various representations that we allow +enum CordRepKind { + CONCAT = 0, SUBSTRING = 1, BTREE = 2, - RING = 3, + RING = 3, EXTERNAL = 4, - - // We have different tags for different sized flat arrays, + + // We have different tags for different sized flat arrays, // starting with FLAT, and limited to MAX_FLAT_TAG. The 225 value is based on - // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed - // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well - // as the Tag <---> Size logic so that FLAT stil represents the minimum flat - // allocation size. (32 bytes as of now). + // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed + // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well + // as the Tag <---> Size logic so that FLAT stil represents the minimum flat + // allocation size. (32 bytes as of now). FLAT = 5, MAX_FLAT_TAG = 225 -}; - +}; + // There are various locations where we want to check if some rep is a 'plain' // data edge, i.e. an external or flat rep. By having FLAT == EXTERNAL + 1, we // can perform this check in a single branch as 'tag >= EXTERNAL' @@ -225,13 +225,13 @@ static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive"); static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive"); struct CordRep { - CordRep() = default; + CordRep() = default; constexpr CordRep(RefcountAndFlags::Immortal immortal, size_t l) - : length(l), refcount(immortal), tag(EXTERNAL), storage{} {} - + : 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. - size_t length; + size_t length; RefcountAndFlags refcount; // If tag < FLAT, it represents CordRepKind and indicates the type of node. // Otherwise, the node type is CordRepFlat and the tag is the encoded size. @@ -255,32 +255,32 @@ struct CordRep { constexpr bool IsFlat() const { return tag >= FLAT; } constexpr bool IsBtree() const { return tag == BTREE; } - inline CordRepRing* ring(); - inline const CordRepRing* ring() const; + inline CordRepRing* ring(); + inline const CordRepRing* ring() const; inline CordRepConcat* concat(); inline const CordRepConcat* concat() const; inline CordRepSubstring* substring(); inline const CordRepSubstring* substring() const; inline CordRepExternal* external(); inline const CordRepExternal* external() const; - inline CordRepFlat* flat(); - inline const CordRepFlat* flat() const; + inline CordRepFlat* flat(); + inline const CordRepFlat* flat() const; inline CordRepBtree* btree(); inline const CordRepBtree* btree() const; - - // -------------------------------------------------------------------- - // Memory management - - // Destroys the provided `rep`. - static void Destroy(CordRep* rep); - - // Increments the reference count of `rep`. - // Requires `rep` to be a non-null pointer value. - static inline CordRep* Ref(CordRep* rep); - - // Decrements the reference count of `rep`. Destroys rep if count reaches - // zero. Requires `rep` to be a non-null pointer value. - static inline void Unref(CordRep* rep); + + // -------------------------------------------------------------------- + // Memory management + + // Destroys the provided `rep`. + static void Destroy(CordRep* rep); + + // Increments the reference count of `rep`. + // Requires `rep` to be a non-null pointer value. + static inline CordRep* Ref(CordRep* rep); + + // Decrements the reference count of `rep`. Destroys rep if count reaches + // zero. Requires `rep` to be a non-null pointer value. + static inline void Unref(CordRep* rep); }; struct CordRepConcat : public CordRep { @@ -296,151 +296,151 @@ struct CordRepSubstring : public CordRep { CordRep* child; }; -// Type for function pointer that will invoke the releaser function and also -// delete the `CordRepExternalImpl` corresponding to the passed in -// `CordRepExternal`. -using ExternalReleaserInvoker = void (*)(CordRepExternal*); +// Type for function pointer that will invoke the releaser function and also +// delete the `CordRepExternalImpl` corresponding to the passed in +// `CordRepExternal`. +using ExternalReleaserInvoker = void (*)(CordRepExternal*); // External CordReps are allocated together with a type erased releaser. The // releaser is stored in the memory directly following the CordRepExternal. -struct CordRepExternal : public CordRep { - CordRepExternal() = default; - explicit constexpr CordRepExternal(y_absl::string_view str) +struct CordRepExternal : public CordRep { + CordRepExternal() = default; + explicit constexpr CordRepExternal(y_absl::string_view str) : CordRep(RefcountAndFlags::Immortal{}, str.size()), - base(str.data()), - releaser_invoker(nullptr) {} - + base(str.data()), + releaser_invoker(nullptr) {} + const char* base; // Pointer to function that knows how to call and destroy the releaser. ExternalReleaserInvoker releaser_invoker; - - // Deletes (releases) the external rep. + + // Deletes (releases) the external rep. // Requires rep != nullptr and rep->IsExternal() - static void Delete(CordRep* rep); + static void Delete(CordRep* rep); }; -struct Rank1 {}; -struct Rank0 : Rank1 {}; - -template <typename Releaser, typename = ::y_absl::base_internal::invoke_result_t< - Releaser, y_absl::string_view>> -void InvokeReleaser(Rank0, Releaser&& releaser, y_absl::string_view data) { - ::y_absl::base_internal::invoke(std::forward<Releaser>(releaser), data); -} - -template <typename Releaser, - typename = ::y_absl::base_internal::invoke_result_t<Releaser>> -void InvokeReleaser(Rank1, Releaser&& releaser, y_absl::string_view) { - ::y_absl::base_internal::invoke(std::forward<Releaser>(releaser)); -} - -// We use CompressedTuple so that we can benefit from EBCO. -template <typename Releaser> -struct CordRepExternalImpl - : public CordRepExternal, - public ::y_absl::container_internal::CompressedTuple<Releaser> { - // The extra int arg is so that we can avoid interfering with copy/move - // constructors while still benefitting from perfect forwarding. - template <typename T> - CordRepExternalImpl(T&& releaser, int) - : CordRepExternalImpl::CompressedTuple(std::forward<T>(releaser)) { - this->releaser_invoker = &Release; - } - - ~CordRepExternalImpl() { - InvokeReleaser(Rank0{}, std::move(this->template get<0>()), - y_absl::string_view(base, length)); - } - - static void Release(CordRepExternal* rep) { - delete static_cast<CordRepExternalImpl*>(rep); - } -}; - -inline void CordRepExternal::Delete(CordRep* rep) { +struct Rank1 {}; +struct Rank0 : Rank1 {}; + +template <typename Releaser, typename = ::y_absl::base_internal::invoke_result_t< + Releaser, y_absl::string_view>> +void InvokeReleaser(Rank0, Releaser&& releaser, y_absl::string_view data) { + ::y_absl::base_internal::invoke(std::forward<Releaser>(releaser), data); +} + +template <typename Releaser, + typename = ::y_absl::base_internal::invoke_result_t<Releaser>> +void InvokeReleaser(Rank1, Releaser&& releaser, y_absl::string_view) { + ::y_absl::base_internal::invoke(std::forward<Releaser>(releaser)); +} + +// We use CompressedTuple so that we can benefit from EBCO. +template <typename Releaser> +struct CordRepExternalImpl + : public CordRepExternal, + public ::y_absl::container_internal::CompressedTuple<Releaser> { + // The extra int arg is so that we can avoid interfering with copy/move + // constructors while still benefitting from perfect forwarding. + template <typename T> + CordRepExternalImpl(T&& releaser, int) + : CordRepExternalImpl::CompressedTuple(std::forward<T>(releaser)) { + this->releaser_invoker = &Release; + } + + ~CordRepExternalImpl() { + InvokeReleaser(Rank0{}, std::move(this->template get<0>()), + y_absl::string_view(base, length)); + } + + static void Release(CordRepExternal* rep) { + delete static_cast<CordRepExternalImpl*>(rep); + } +}; + +inline void CordRepExternal::Delete(CordRep* rep) { assert(rep != nullptr && rep->IsExternal()); - auto* rep_external = static_cast<CordRepExternal*>(rep); - assert(rep_external->releaser_invoker != nullptr); - rep_external->releaser_invoker(rep_external); -} - -template <typename Str> -struct ConstInitExternalStorage { - ABSL_CONST_INIT static CordRepExternal value; -}; - -template <typename Str> -CordRepExternal ConstInitExternalStorage<Str>::value(Str::value); - -enum { - kMaxInline = 15, -}; - -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 -// 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. -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) { -#if defined(ABSL_IS_BIG_ENDIAN) - return value; -#else - return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8); -#endif -} - -class InlineData { - public: + auto* rep_external = static_cast<CordRepExternal*>(rep); + assert(rep_external->releaser_invoker != nullptr); + rep_external->releaser_invoker(rep_external); +} + +template <typename Str> +struct ConstInitExternalStorage { + ABSL_CONST_INIT static CordRepExternal value; +}; + +template <typename Str> +CordRepExternal ConstInitExternalStorage<Str>::value(Str::value); + +enum { + kMaxInline = 15, +}; + +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 +// 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. +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) { +#if defined(ABSL_IS_BIG_ENDIAN) + return value; +#else + return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8); +#endif +} + +class InlineData { + public: // DefaultInitType forces the use of the default initialization constructor. enum DefaultInitType { kDefaultInit }; - // kNullCordzInfo holds the big 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} {} + // kNullCordzInfo holds the big 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))} {} - - // 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; } - - // Returns true if the current instance holds a tree value. - bool is_tree() const { return (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; - } - + 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))} {} + + // 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; } + + // Returns true if the current instance holds a tree value. + bool is_tree() const { return (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; + } + // Returns true if either of the provided instances hold a cordz_info value. // This method is more efficient than the equivalent `data1.is_profiled() || // data2.is_profiled()`. Requires both arguments to hold a tree. @@ -451,170 +451,170 @@ class InlineData { kNullCordzInfo; } - // Returns the cordz_info sampling instance for this instance, or nullptr - // if the current instance is not sampled and does not have CordzInfo data. - // 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(as_tree_.cordz_info)); - assert(info & 1); - return reinterpret_cast<CordzInfo*>(info - 1); - } - - // Sets the current cordz_info sampling instance for this instance, or nullptr - // if the current instance is not sampled and does not have CordzInfo data. - // Requires the current instance to hold a tree value. - void set_cordz_info(CordzInfo* cordz_info) { - assert(is_tree()); - intptr_t info = reinterpret_cast<intptr_t>(cordz_info) | 1; - as_tree_.cordz_info = y_absl::big_endian::FromHost64(info); - } - - // Resets the current cordz_info to null / empty. - void clear_cordz_info() { - assert(is_tree()); - as_tree_.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_; - } - - // Returns a mutable pointer to the character data inside this instance. - // Should be used for 'write only' operations setting an inlined value. - // Applications can set the value of inlined data either before or after - // setting the inlined size, i.e., both of the below are valid: - // - // // Set inlined data and inline size - // memcpy(data_.as_chars(), data, size); - // data_.set_inline_size(size); - // - // // Set inlined size and inline data - // data_.set_inline_size(size); - // memcpy(data_.as_chars(), data, size); - // - // 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_; } - - // 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; - } - - // 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; - } - - // Set the tree value of this instance to 'rep`. - // Requires the current instance to already hold a tree value. - // Does not affect the value of cordz_info. - void set_tree(CordRep* rep) { - assert(is_tree()); - as_tree_.rep = 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; - } - - // 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) { - ABSL_ASSERT(size <= kMaxInline); - tag() = static_cast<char>(size << 1); - } - - 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 { - 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]; } - - // 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`. + // Returns the cordz_info sampling instance for this instance, or nullptr + // if the current instance is not sampled and does not have CordzInfo data. + // 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(as_tree_.cordz_info)); + assert(info & 1); + return reinterpret_cast<CordzInfo*>(info - 1); + } + + // Sets the current cordz_info sampling instance for this instance, or nullptr + // if the current instance is not sampled and does not have CordzInfo data. + // Requires the current instance to hold a tree value. + void set_cordz_info(CordzInfo* cordz_info) { + assert(is_tree()); + intptr_t info = reinterpret_cast<intptr_t>(cordz_info) | 1; + as_tree_.cordz_info = y_absl::big_endian::FromHost64(info); + } + + // Resets the current cordz_info to null / empty. + void clear_cordz_info() { + assert(is_tree()); + as_tree_.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_; + } + + // Returns a mutable pointer to the character data inside this instance. + // Should be used for 'write only' operations setting an inlined value. + // Applications can set the value of inlined data either before or after + // setting the inlined size, i.e., both of the below are valid: + // + // // Set inlined data and inline size + // memcpy(data_.as_chars(), data, size); + // data_.set_inline_size(size); + // + // // Set inlined size and inline data + // data_.set_inline_size(size); + // memcpy(data_.as_chars(), data, size); + // + // 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_; } + + // 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; + } + + // 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; + } + + // Set the tree value of this instance to 'rep`. + // Requires the current instance to already hold a tree value. + // Does not affect the value of cordz_info. + void set_tree(CordRep* rep) { + assert(is_tree()); + as_tree_.rep = 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; + } + + // 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) { + ABSL_ASSERT(size <= kMaxInline); + tag() = static_cast<char>(size << 1); + } + + 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 { + 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]; } + + // 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_; - }; -}; - -static_assert(sizeof(InlineData) == kMaxInline + 1, ""); - -inline CordRepConcat* CordRep::concat() { + char as_chars_[kMaxInline + 1]; + AsTree as_tree_; + }; +}; + +static_assert(sizeof(InlineData) == kMaxInline + 1, ""); + +inline CordRepConcat* CordRep::concat() { assert(IsConcat()); - return static_cast<CordRepConcat*>(this); -} - -inline const CordRepConcat* CordRep::concat() const { + return static_cast<CordRepConcat*>(this); +} + +inline const CordRepConcat* CordRep::concat() const { assert(IsConcat()); - return static_cast<const CordRepConcat*>(this); -} - -inline CordRepSubstring* CordRep::substring() { + return static_cast<const CordRepConcat*>(this); +} + +inline CordRepSubstring* CordRep::substring() { assert(IsSubstring()); - return static_cast<CordRepSubstring*>(this); -} - -inline const CordRepSubstring* CordRep::substring() const { + return static_cast<CordRepSubstring*>(this); +} + +inline const CordRepSubstring* CordRep::substring() const { assert(IsSubstring()); - return static_cast<const CordRepSubstring*>(this); -} - -inline CordRepExternal* CordRep::external() { + return static_cast<const CordRepSubstring*>(this); +} + +inline CordRepExternal* CordRep::external() { assert(IsExternal()); - return static_cast<CordRepExternal*>(this); -} - -inline const CordRepExternal* CordRep::external() const { + return static_cast<CordRepExternal*>(this); +} + +inline const CordRepExternal* CordRep::external() const { assert(IsExternal()); - return static_cast<const CordRepExternal*>(this); -} - -inline CordRep* CordRep::Ref(CordRep* rep) { - assert(rep != nullptr); - rep->refcount.Increment(); - return rep; -} - -inline void CordRep::Unref(CordRep* rep) { - assert(rep != nullptr); - // Expect refcount to be 0. Avoiding the cost of an atomic decrement should - // typically outweigh the cost of an extra branch checking for ref == 1. - if (ABSL_PREDICT_FALSE(!rep->refcount.DecrementExpectHighRefcount())) { - Destroy(rep); - } -} - + return static_cast<const CordRepExternal*>(this); +} + +inline CordRep* CordRep::Ref(CordRep* rep) { + assert(rep != nullptr); + rep->refcount.Increment(); + return rep; +} + +inline void CordRep::Unref(CordRep* rep) { + assert(rep != nullptr); + // Expect refcount to be 0. Avoiding the cost of an atomic decrement should + // typically outweigh the cost of an extra branch checking for ref == 1. + if (ABSL_PREDICT_FALSE(!rep->refcount.DecrementExpectHighRefcount())) { + Destroy(rep); + } +} + } // namespace cord_internal - + ABSL_NAMESPACE_END } // namespace y_absl #endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_flat.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_flat.h index 976613031c..5557022553 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_flat.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_flat.h @@ -1,146 +1,146 @@ -// Copyright 2020 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ -#define ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ - -#include <cassert> -#include <cstddef> -#include <cstdint> -#include <memory> - -#include "y_absl/strings/internal/cord_internal.h" - -namespace y_absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { - -// Note: all constants below are never ODR used and internal to cord, we define -// these as static constexpr to avoid 'in struct' definition and usage clutter. - -// Largest and smallest flat node lengths we are willing to allocate -// Flat allocation size is stored in tag, which currently can encode sizes up -// to 4K, encoded as multiple of either 8 or 32 bytes. -// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc. -// kMinFlatSize is bounded by tag needing to be at least FLAT * 8 bytes, and -// ideally a 'nice' size aligning with allocation and cacheline sizes like 32. -// kMaxFlatSize is bounded by the size resulting in a computed tag no greater -// than MAX_FLAT_TAG. MAX_FLAT_TAG provides for additional 'high' tag values. -static constexpr size_t kFlatOverhead = offsetof(CordRep, storage); -static constexpr size_t kMinFlatSize = 32; -static constexpr size_t kMaxFlatSize = 4096; -static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; -static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead; - -constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) { +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <memory> + +#include "y_absl/strings/internal/cord_internal.h" + +namespace y_absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Note: all constants below are never ODR used and internal to cord, we define +// these as static constexpr to avoid 'in struct' definition and usage clutter. + +// Largest and smallest flat node lengths we are willing to allocate +// Flat allocation size is stored in tag, which currently can encode sizes up +// to 4K, encoded as multiple of either 8 or 32 bytes. +// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc. +// kMinFlatSize is bounded by tag needing to be at least FLAT * 8 bytes, and +// ideally a 'nice' size aligning with allocation and cacheline sizes like 32. +// kMaxFlatSize is bounded by the size resulting in a computed tag no greater +// than MAX_FLAT_TAG. MAX_FLAT_TAG provides for additional 'high' tag values. +static constexpr size_t kFlatOverhead = offsetof(CordRep, storage); +static constexpr size_t kMinFlatSize = 32; +static constexpr size_t kMaxFlatSize = 4096; +static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; +static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead; + +constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) { return static_cast<uint8_t>((size <= 1024) ? size / 8 + 1 : 129 + size / 32 - 1024 / 32); -} - +} + static_assert(kMinFlatSize / 8 + 1 >= FLAT, ""); -static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, ""); - -// Helper functions for rounded div, and rounding to exact sizes. -constexpr size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; } -constexpr size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; } - -// Returns the size to the nearest equal or larger value that can be -// expressed exactly as a tag value. -inline size_t RoundUpForTag(size_t size) { - return RoundUp(size, (size <= 1024) ? 8 : 32); -} - -// Converts the allocated size to a tag, rounding down if the size -// does not exactly match a 'tag expressible' size value. The result is -// undefined if the size exceeds the maximum size that can be encoded in -// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>). -inline uint8_t AllocatedSizeToTag(size_t size) { - const uint8_t tag = AllocatedSizeToTagUnchecked(size); - assert(tag <= MAX_FLAT_TAG); - return tag; -} - -// Converts the provided tag to the corresponding allocated size -constexpr size_t TagToAllocatedSize(uint8_t tag) { +static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, ""); + +// Helper functions for rounded div, and rounding to exact sizes. +constexpr size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; } +constexpr size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; } + +// Returns the size to the nearest equal or larger value that can be +// expressed exactly as a tag value. +inline size_t RoundUpForTag(size_t size) { + return RoundUp(size, (size <= 1024) ? 8 : 32); +} + +// Converts the allocated size to a tag, rounding down if the size +// does not exactly match a 'tag expressible' size value. The result is +// undefined if the size exceeds the maximum size that can be encoded in +// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>). +inline uint8_t AllocatedSizeToTag(size_t size) { + const uint8_t tag = AllocatedSizeToTagUnchecked(size); + assert(tag <= MAX_FLAT_TAG); + return tag; +} + +// Converts the provided tag to the corresponding allocated size +constexpr size_t TagToAllocatedSize(uint8_t tag) { return (tag <= 129) ? ((tag - 1) * 8) : (1024 + (tag - 129) * 32); -} - -// Converts the provided tag to the corresponding available data length -constexpr size_t TagToLength(uint8_t tag) { - return TagToAllocatedSize(tag) - kFlatOverhead; -} - -// Enforce that kMaxFlatSize maps to a well-known exact tag value. +} + +// Converts the provided tag to the corresponding available data length +constexpr size_t TagToLength(uint8_t tag) { + return TagToAllocatedSize(tag) - kFlatOverhead; +} + +// Enforce that kMaxFlatSize maps to a well-known exact tag value. static_assert(TagToAllocatedSize(225) == kMaxFlatSize, "Bad tag logic"); - -struct CordRepFlat : public CordRep { - // Creates a new flat node. - static CordRepFlat* New(size_t len) { - if (len <= kMinFlatLength) { - len = kMinFlatLength; - } else if (len > kMaxFlatLength) { - len = kMaxFlatLength; - } - - // Round size up so it matches a size we can exactly express in a tag. - const size_t size = RoundUpForTag(len + kFlatOverhead); - void* const raw_rep = ::operator new(size); - CordRepFlat* rep = new (raw_rep) CordRepFlat(); - rep->tag = AllocatedSizeToTag(size); - return rep; - } - - // Deletes a CordRepFlat instance created previously through a call to New(). - // Flat CordReps are allocated and constructed with raw ::operator new and - // placement new, and must be destructed and deallocated accordingly. - static void Delete(CordRep*rep) { - assert(rep->tag >= FLAT && rep->tag <= MAX_FLAT_TAG); - -#if defined(__cpp_sized_deallocation) - size_t size = TagToAllocatedSize(rep->tag); - rep->~CordRep(); - ::operator delete(rep, size); -#else - rep->~CordRep(); - ::operator delete(rep); -#endif - } - - // Returns a pointer to the data inside this flat rep. + +struct CordRepFlat : public CordRep { + // Creates a new flat node. + static CordRepFlat* New(size_t len) { + if (len <= kMinFlatLength) { + len = kMinFlatLength; + } else if (len > kMaxFlatLength) { + len = kMaxFlatLength; + } + + // Round size up so it matches a size we can exactly express in a tag. + const size_t size = RoundUpForTag(len + kFlatOverhead); + void* const raw_rep = ::operator new(size); + CordRepFlat* rep = new (raw_rep) CordRepFlat(); + rep->tag = AllocatedSizeToTag(size); + return rep; + } + + // Deletes a CordRepFlat instance created previously through a call to New(). + // Flat CordReps are allocated and constructed with raw ::operator new and + // placement new, and must be destructed and deallocated accordingly. + static void Delete(CordRep*rep) { + assert(rep->tag >= FLAT && rep->tag <= MAX_FLAT_TAG); + +#if defined(__cpp_sized_deallocation) + size_t size = TagToAllocatedSize(rep->tag); + rep->~CordRep(); + ::operator delete(rep, size); +#else + rep->~CordRep(); + ::operator delete(rep); +#endif + } + + // Returns a pointer to the data inside this flat rep. char* Data() { return reinterpret_cast<char*>(storage); } const char* Data() const { return reinterpret_cast<const char*>(storage); } - - // Returns the maximum capacity (payload size) of this instance. - size_t Capacity() const { return TagToLength(tag); } - - // Returns the allocated size (payload + overhead) of this instance. - size_t AllocatedSize() const { return TagToAllocatedSize(tag); } -}; - -// Now that CordRepFlat is defined, we can define CordRep's helper casts: -inline CordRepFlat* CordRep::flat() { - assert(tag >= FLAT && tag <= MAX_FLAT_TAG); - return reinterpret_cast<CordRepFlat*>(this); -} - -inline const CordRepFlat* CordRep::flat() const { - assert(tag >= FLAT && tag <= MAX_FLAT_TAG); - return reinterpret_cast<const CordRepFlat*>(this); -} - -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace y_absl - -#endif // ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ + + // Returns the maximum capacity (payload size) of this instance. + size_t Capacity() const { return TagToLength(tag); } + + // Returns the allocated size (payload + overhead) of this instance. + size_t AllocatedSize() const { return TagToAllocatedSize(tag); } +}; + +// Now that CordRepFlat is defined, we can define CordRep's helper casts: +inline CordRepFlat* CordRep::flat() { + assert(tag >= FLAT && tag <= MAX_FLAT_TAG); + return reinterpret_cast<CordRepFlat*>(this); +} + +inline const CordRepFlat* CordRep::flat() const { + assert(tag >= FLAT && tag <= MAX_FLAT_TAG); + return reinterpret_cast<const CordRepFlat*>(this); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring.cc b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring.cc index 06c7e75bd8..97dff05363 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring.cc +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring.cc @@ -1,771 +1,771 @@ -// Copyright 2020 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include "y_absl/strings/internal/cord_rep_ring.h" - -#include <cassert> -#include <cstddef> -#include <cstdint> -#include <iostream> -#include <limits> -#include <memory> -#include <util/generic/string.h> - -#include "y_absl/base/internal/raw_logging.h" -#include "y_absl/base/internal/throw_delegate.h" -#include "y_absl/base/macros.h" -#include "y_absl/container/inlined_vector.h" -#include "y_absl/strings/internal/cord_internal.h" +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "y_absl/strings/internal/cord_rep_ring.h" + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <iostream> +#include <limits> +#include <memory> +#include <util/generic/string.h> + +#include "y_absl/base/internal/raw_logging.h" +#include "y_absl/base/internal/throw_delegate.h" +#include "y_absl/base/macros.h" +#include "y_absl/container/inlined_vector.h" +#include "y_absl/strings/internal/cord_internal.h" #include "y_absl/strings/internal/cord_rep_consume.h" -#include "y_absl/strings/internal/cord_rep_flat.h" - -namespace y_absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { - -namespace { - -using index_type = CordRepRing::index_type; - -enum class Direction { kForward, kReversed }; - -inline bool IsFlatOrExternal(CordRep* rep) { +#include "y_absl/strings/internal/cord_rep_flat.h" + +namespace y_absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +namespace { + +using index_type = CordRepRing::index_type; + +enum class Direction { kForward, kReversed }; + +inline bool IsFlatOrExternal(CordRep* rep) { return rep->IsFlat() || rep->IsExternal(); -} - -// Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise. -inline void CheckCapacity(size_t n, size_t extra) { - if (ABSL_PREDICT_FALSE(extra > CordRepRing::kMaxCapacity - n)) { - base_internal::ThrowStdLengthError("Maximum capacity exceeded"); - } -} - -// Creates a flat from the provided string data, allocating up to `extra` -// capacity in the returned flat depending on kMaxFlatLength limitations. -// Requires `len` to be less or equal to `kMaxFlatLength` -CordRepFlat* CreateFlat(const char* s, size_t n, size_t extra = 0) { // NOLINT - assert(n <= kMaxFlatLength); - auto* rep = CordRepFlat::New(n + extra); - rep->length = n; - memcpy(rep->Data(), s, n); - return rep; -} - -// Unrefs the entries in `[head, tail)`. -// Requires all entries to be a FLAT or EXTERNAL node. -void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) { - rep->ForEach(head, tail, [rep](index_type ix) { - CordRep* child = rep->entry_child(ix); - if (!child->refcount.Decrement()) { - if (child->tag >= FLAT) { - CordRepFlat::Delete(child->flat()); - } else { - CordRepExternal::Delete(child->external()); - } - } - }); -} - -} // namespace - -std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) { - // Note: 'pos' values are defined as size_t (for overflow reasons), but that - // prints really awkward for small prepended values such as -5. ssize_t is not - // portable (POSIX), so we use ptrdiff_t instead to cast to signed values. - s << " CordRepRing(" << &rep << ", length = " << rep.length - << ", head = " << rep.head_ << ", tail = " << rep.tail_ - << ", cap = " << rep.capacity_ << ", rc = " << rep.refcount.Get() - << ", begin_pos_ = " << static_cast<ptrdiff_t>(rep.begin_pos_) << ") {\n"; - CordRepRing::index_type head = rep.head(); - do { - CordRep* child = rep.entry_child(head); - s << " entry[" << head << "] length = " << rep.entry_length(head) - << ", child " << child << ", clen = " << child->length - << ", tag = " << static_cast<int>(child->tag) - << ", rc = " << child->refcount.Get() - << ", offset = " << rep.entry_data_offset(head) - << ", end_pos = " << static_cast<ptrdiff_t>(rep.entry_end_pos(head)) - << "\n"; - head = rep.advance(head); - } while (head != rep.tail()); - return s << "}\n"; -} - -void CordRepRing::AddDataOffset(index_type index, size_t n) { - entry_data_offset()[index] += static_cast<offset_type>(n); -} - -void CordRepRing::SubLength(index_type index, size_t n) { - entry_end_pos()[index] -= n; -} - -class CordRepRing::Filler { - public: - Filler(CordRepRing* rep, index_type pos) : rep_(rep), head_(pos), pos_(pos) {} - - index_type head() const { return head_; } - index_type pos() const { return pos_; } - - void Add(CordRep* child, size_t offset, pos_type end_pos) { - rep_->entry_end_pos()[pos_] = end_pos; - rep_->entry_child()[pos_] = child; - rep_->entry_data_offset()[pos_] = static_cast<offset_type>(offset); - pos_ = rep_->advance(pos_); - } - - private: - CordRepRing* rep_; - index_type head_; - index_type pos_; -}; - -constexpr size_t CordRepRing::kMaxCapacity; // NOLINT: needed for c++11 - -bool CordRepRing::IsValid(std::ostream& output) const { - if (capacity_ == 0) { - output << "capacity == 0"; - return false; - } - - if (head_ >= capacity_ || tail_ >= capacity_) { - output << "head " << head_ << " and/or tail " << tail_ << "exceed capacity " - << capacity_; - return false; - } - - const index_type back = retreat(tail_); - size_t pos_length = Distance(begin_pos_, entry_end_pos(back)); - if (pos_length != length) { - output << "length " << length << " does not match positional length " - << pos_length << " from begin_pos " << begin_pos_ << " and entry[" - << back << "].end_pos " << entry_end_pos(back); - return false; - } - - index_type head = head_; - pos_type begin_pos = begin_pos_; - do { - pos_type end_pos = entry_end_pos(head); - size_t entry_length = Distance(begin_pos, end_pos); - if (entry_length == 0) { - output << "entry[" << head << "] has an invalid length " << entry_length - << " from begin_pos " << begin_pos << " and end_pos " << end_pos; - return false; - } - - CordRep* child = entry_child(head); - if (child == nullptr) { - output << "entry[" << head << "].child == nullptr"; - return false; - } - if (child->tag < FLAT && child->tag != EXTERNAL) { - output << "entry[" << head << "].child has an invalid tag " - << static_cast<int>(child->tag); - return false; - } - - size_t offset = entry_data_offset(head); - if (offset >= child->length || entry_length > child->length - offset) { - output << "entry[" << head << "] has offset " << offset - << " and entry length " << entry_length +} + +// Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise. +inline void CheckCapacity(size_t n, size_t extra) { + if (ABSL_PREDICT_FALSE(extra > CordRepRing::kMaxCapacity - n)) { + base_internal::ThrowStdLengthError("Maximum capacity exceeded"); + } +} + +// Creates a flat from the provided string data, allocating up to `extra` +// capacity in the returned flat depending on kMaxFlatLength limitations. +// Requires `len` to be less or equal to `kMaxFlatLength` +CordRepFlat* CreateFlat(const char* s, size_t n, size_t extra = 0) { // NOLINT + assert(n <= kMaxFlatLength); + auto* rep = CordRepFlat::New(n + extra); + rep->length = n; + memcpy(rep->Data(), s, n); + return rep; +} + +// Unrefs the entries in `[head, tail)`. +// Requires all entries to be a FLAT or EXTERNAL node. +void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) { + rep->ForEach(head, tail, [rep](index_type ix) { + CordRep* child = rep->entry_child(ix); + if (!child->refcount.Decrement()) { + if (child->tag >= FLAT) { + CordRepFlat::Delete(child->flat()); + } else { + CordRepExternal::Delete(child->external()); + } + } + }); +} + +} // namespace + +std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) { + // Note: 'pos' values are defined as size_t (for overflow reasons), but that + // prints really awkward for small prepended values such as -5. ssize_t is not + // portable (POSIX), so we use ptrdiff_t instead to cast to signed values. + s << " CordRepRing(" << &rep << ", length = " << rep.length + << ", head = " << rep.head_ << ", tail = " << rep.tail_ + << ", cap = " << rep.capacity_ << ", rc = " << rep.refcount.Get() + << ", begin_pos_ = " << static_cast<ptrdiff_t>(rep.begin_pos_) << ") {\n"; + CordRepRing::index_type head = rep.head(); + do { + CordRep* child = rep.entry_child(head); + s << " entry[" << head << "] length = " << rep.entry_length(head) + << ", child " << child << ", clen = " << child->length + << ", tag = " << static_cast<int>(child->tag) + << ", rc = " << child->refcount.Get() + << ", offset = " << rep.entry_data_offset(head) + << ", end_pos = " << static_cast<ptrdiff_t>(rep.entry_end_pos(head)) + << "\n"; + head = rep.advance(head); + } while (head != rep.tail()); + return s << "}\n"; +} + +void CordRepRing::AddDataOffset(index_type index, size_t n) { + entry_data_offset()[index] += static_cast<offset_type>(n); +} + +void CordRepRing::SubLength(index_type index, size_t n) { + entry_end_pos()[index] -= n; +} + +class CordRepRing::Filler { + public: + Filler(CordRepRing* rep, index_type pos) : rep_(rep), head_(pos), pos_(pos) {} + + index_type head() const { return head_; } + index_type pos() const { return pos_; } + + void Add(CordRep* child, size_t offset, pos_type end_pos) { + rep_->entry_end_pos()[pos_] = end_pos; + rep_->entry_child()[pos_] = child; + rep_->entry_data_offset()[pos_] = static_cast<offset_type>(offset); + pos_ = rep_->advance(pos_); + } + + private: + CordRepRing* rep_; + index_type head_; + index_type pos_; +}; + +constexpr size_t CordRepRing::kMaxCapacity; // NOLINT: needed for c++11 + +bool CordRepRing::IsValid(std::ostream& output) const { + if (capacity_ == 0) { + output << "capacity == 0"; + return false; + } + + if (head_ >= capacity_ || tail_ >= capacity_) { + output << "head " << head_ << " and/or tail " << tail_ << "exceed capacity " + << capacity_; + return false; + } + + const index_type back = retreat(tail_); + size_t pos_length = Distance(begin_pos_, entry_end_pos(back)); + if (pos_length != length) { + output << "length " << length << " does not match positional length " + << pos_length << " from begin_pos " << begin_pos_ << " and entry[" + << back << "].end_pos " << entry_end_pos(back); + return false; + } + + index_type head = head_; + pos_type begin_pos = begin_pos_; + do { + pos_type end_pos = entry_end_pos(head); + size_t entry_length = Distance(begin_pos, end_pos); + if (entry_length == 0) { + output << "entry[" << head << "] has an invalid length " << entry_length + << " from begin_pos " << begin_pos << " and end_pos " << end_pos; + return false; + } + + CordRep* child = entry_child(head); + if (child == nullptr) { + output << "entry[" << head << "].child == nullptr"; + return false; + } + if (child->tag < FLAT && child->tag != EXTERNAL) { + output << "entry[" << head << "].child has an invalid tag " + << static_cast<int>(child->tag); + return false; + } + + size_t offset = entry_data_offset(head); + if (offset >= child->length || entry_length > child->length - offset) { + output << "entry[" << head << "] has offset " << offset + << " and entry length " << entry_length << " which are outside of the child's length of " << child->length; - return false; - } - - begin_pos = end_pos; - head = advance(head); - } while (head != tail_); - - return true; -} - -#ifdef EXTRA_CORD_RING_VALIDATION -CordRepRing* CordRepRing::Validate(CordRepRing* rep, const char* file, - int line) { - if (!rep->IsValid(std::cerr)) { - std::cerr << "\nERROR: CordRepRing corrupted"; - if (line) std::cerr << " at line " << line; - if (file) std::cerr << " in file " << file; - std::cerr << "\nContent = " << *rep; - abort(); - } - return rep; -} -#endif // EXTRA_CORD_RING_VALIDATION - -CordRepRing* CordRepRing::New(size_t capacity, size_t extra) { - CheckCapacity(capacity, extra); - - size_t size = AllocSize(capacity += extra); - void* mem = ::operator new(size); - auto* rep = new (mem) CordRepRing(static_cast<index_type>(capacity)); - rep->tag = RING; - rep->capacity_ = static_cast<index_type>(capacity); - rep->begin_pos_ = 0; - return rep; -} - -void CordRepRing::SetCapacityForTesting(size_t capacity) { - // Adjust for the changed layout - assert(capacity <= capacity_); - assert(head() == 0 || head() < tail()); - memmove(Layout::Partial(capacity).Pointer<1>(data_) + head(), - Layout::Partial(capacity_).Pointer<1>(data_) + head(), - entries() * sizeof(Layout::ElementType<1>)); - memmove(Layout::Partial(capacity, capacity).Pointer<2>(data_) + head(), - Layout::Partial(capacity_, capacity_).Pointer<2>(data_) + head(), - entries() * sizeof(Layout::ElementType<2>)); - capacity_ = static_cast<index_type>(capacity); -} - -void CordRepRing::Delete(CordRepRing* rep) { + return false; + } + + begin_pos = end_pos; + head = advance(head); + } while (head != tail_); + + return true; +} + +#ifdef EXTRA_CORD_RING_VALIDATION +CordRepRing* CordRepRing::Validate(CordRepRing* rep, const char* file, + int line) { + if (!rep->IsValid(std::cerr)) { + std::cerr << "\nERROR: CordRepRing corrupted"; + if (line) std::cerr << " at line " << line; + if (file) std::cerr << " in file " << file; + std::cerr << "\nContent = " << *rep; + abort(); + } + return rep; +} +#endif // EXTRA_CORD_RING_VALIDATION + +CordRepRing* CordRepRing::New(size_t capacity, size_t extra) { + CheckCapacity(capacity, extra); + + size_t size = AllocSize(capacity += extra); + void* mem = ::operator new(size); + auto* rep = new (mem) CordRepRing(static_cast<index_type>(capacity)); + rep->tag = RING; + rep->capacity_ = static_cast<index_type>(capacity); + rep->begin_pos_ = 0; + return rep; +} + +void CordRepRing::SetCapacityForTesting(size_t capacity) { + // Adjust for the changed layout + assert(capacity <= capacity_); + assert(head() == 0 || head() < tail()); + memmove(Layout::Partial(capacity).Pointer<1>(data_) + head(), + Layout::Partial(capacity_).Pointer<1>(data_) + head(), + entries() * sizeof(Layout::ElementType<1>)); + memmove(Layout::Partial(capacity, capacity).Pointer<2>(data_) + head(), + Layout::Partial(capacity_, capacity_).Pointer<2>(data_) + head(), + entries() * sizeof(Layout::ElementType<2>)); + capacity_ = static_cast<index_type>(capacity); +} + +void CordRepRing::Delete(CordRepRing* rep) { assert(rep != nullptr && rep->IsRing()); -#if defined(__cpp_sized_deallocation) - size_t size = AllocSize(rep->capacity_); - rep->~CordRepRing(); - ::operator delete(rep, size); -#else - rep->~CordRepRing(); - ::operator delete(rep); -#endif -} - -void CordRepRing::Destroy(CordRepRing* rep) { - UnrefEntries(rep, rep->head(), rep->tail()); - Delete(rep); -} - -template <bool ref> -void CordRepRing::Fill(const CordRepRing* src, index_type head, - index_type tail) { - this->length = src->length; - head_ = 0; - tail_ = advance(0, src->entries(head, tail)); - begin_pos_ = src->begin_pos_; - - // TODO(mvels): there may be opportunities here for large buffers. - auto* dst_pos = entry_end_pos(); - auto* dst_child = entry_child(); - auto* dst_offset = entry_data_offset(); - src->ForEach(head, tail, [&](index_type index) { - *dst_pos++ = src->entry_end_pos(index); - CordRep* child = src->entry_child(index); - *dst_child++ = ref ? CordRep::Ref(child) : child; - *dst_offset++ = src->entry_data_offset(index); - }); -} - -CordRepRing* CordRepRing::Copy(CordRepRing* rep, index_type head, - index_type tail, size_t extra) { - CordRepRing* newrep = CordRepRing::New(rep->entries(head, tail), extra); - newrep->Fill<true>(rep, head, tail); - CordRep::Unref(rep); - return newrep; -} - -CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) { - // Get current number of entries, and check for max capacity. - size_t entries = rep->entries(); - +#if defined(__cpp_sized_deallocation) + size_t size = AllocSize(rep->capacity_); + rep->~CordRepRing(); + ::operator delete(rep, size); +#else + rep->~CordRepRing(); + ::operator delete(rep); +#endif +} + +void CordRepRing::Destroy(CordRepRing* rep) { + UnrefEntries(rep, rep->head(), rep->tail()); + Delete(rep); +} + +template <bool ref> +void CordRepRing::Fill(const CordRepRing* src, index_type head, + index_type tail) { + this->length = src->length; + head_ = 0; + tail_ = advance(0, src->entries(head, tail)); + begin_pos_ = src->begin_pos_; + + // TODO(mvels): there may be opportunities here for large buffers. + auto* dst_pos = entry_end_pos(); + auto* dst_child = entry_child(); + auto* dst_offset = entry_data_offset(); + src->ForEach(head, tail, [&](index_type index) { + *dst_pos++ = src->entry_end_pos(index); + CordRep* child = src->entry_child(index); + *dst_child++ = ref ? CordRep::Ref(child) : child; + *dst_offset++ = src->entry_data_offset(index); + }); +} + +CordRepRing* CordRepRing::Copy(CordRepRing* rep, index_type head, + index_type tail, size_t extra) { + CordRepRing* newrep = CordRepRing::New(rep->entries(head, tail), extra); + newrep->Fill<true>(rep, head, tail); + CordRep::Unref(rep); + return newrep; +} + +CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) { + // Get current number of entries, and check for max capacity. + size_t entries = rep->entries(); + if (!rep->refcount.IsMutable()) { return Copy(rep, rep->head(), rep->tail(), extra); - } else if (entries + extra > rep->capacity()) { + } else if (entries + extra > rep->capacity()) { const size_t min_grow = rep->capacity() + rep->capacity() / 2; const size_t min_extra = (std::max)(extra, min_grow - entries); - CordRepRing* newrep = CordRepRing::New(entries, min_extra); - newrep->Fill<false>(rep, rep->head(), rep->tail()); - CordRepRing::Delete(rep); - return newrep; - } else { - return rep; - } -} - -Span<char> CordRepRing::GetAppendBuffer(size_t size) { + CordRepRing* newrep = CordRepRing::New(entries, min_extra); + newrep->Fill<false>(rep, rep->head(), rep->tail()); + CordRepRing::Delete(rep); + return newrep; + } else { + return rep; + } +} + +Span<char> CordRepRing::GetAppendBuffer(size_t size) { assert(refcount.IsMutable()); - index_type back = retreat(tail_); - CordRep* child = entry_child(back); + index_type back = retreat(tail_); + CordRep* child = entry_child(back); if (child->tag >= FLAT && child->refcount.IsMutable()) { - size_t capacity = child->flat()->Capacity(); - pos_type end_pos = entry_end_pos(back); - size_t data_offset = entry_data_offset(back); - size_t entry_length = Distance(entry_begin_pos(back), end_pos); - size_t used = data_offset + entry_length; - if (size_t n = (std::min)(capacity - used, size)) { - child->length = data_offset + entry_length + n; - entry_end_pos()[back] = end_pos + n; - this->length += n; - return {child->flat()->Data() + used, n}; - } - } - return {nullptr, 0}; -} - -Span<char> CordRepRing::GetPrependBuffer(size_t size) { + size_t capacity = child->flat()->Capacity(); + pos_type end_pos = entry_end_pos(back); + size_t data_offset = entry_data_offset(back); + size_t entry_length = Distance(entry_begin_pos(back), end_pos); + size_t used = data_offset + entry_length; + if (size_t n = (std::min)(capacity - used, size)) { + child->length = data_offset + entry_length + n; + entry_end_pos()[back] = end_pos + n; + this->length += n; + return {child->flat()->Data() + used, n}; + } + } + return {nullptr, 0}; +} + +Span<char> CordRepRing::GetPrependBuffer(size_t size) { assert(refcount.IsMutable()); - CordRep* child = entry_child(head_); - size_t data_offset = entry_data_offset(head_); + CordRep* child = entry_child(head_); + size_t data_offset = entry_data_offset(head_); if (data_offset && child->refcount.IsMutable() && child->tag >= FLAT) { - size_t n = (std::min)(data_offset, size); - this->length += n; - begin_pos_ -= n; - data_offset -= n; - entry_data_offset()[head_] = static_cast<offset_type>(data_offset); - return {child->flat()->Data() + data_offset, n}; - } - return {nullptr, 0}; -} - -CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset, + size_t n = (std::min)(data_offset, size); + this->length += n; + begin_pos_ -= n; + data_offset -= n; + entry_data_offset()[head_] = static_cast<offset_type>(data_offset); + return {child->flat()->Data() + data_offset, n}; + } + return {nullptr, 0}; +} + +CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset, size_t len, size_t extra) { - CordRepRing* rep = CordRepRing::New(1, extra); - rep->head_ = 0; - rep->tail_ = rep->advance(0); + CordRepRing* rep = CordRepRing::New(1, extra); + rep->head_ = 0; + rep->tail_ = rep->advance(0); rep->length = len; rep->entry_end_pos()[0] = len; - rep->entry_child()[0] = child; - rep->entry_data_offset()[0] = static_cast<offset_type>(offset); - return Validate(rep); -} - -CordRepRing* CordRepRing::CreateSlow(CordRep* child, size_t extra) { - CordRepRing* rep = nullptr; + rep->entry_child()[0] = child; + rep->entry_data_offset()[0] = static_cast<offset_type>(offset); + return Validate(rep); +} + +CordRepRing* CordRepRing::CreateSlow(CordRep* child, size_t extra) { + CordRepRing* rep = nullptr; Consume(child, [&](CordRep* child_arg, size_t offset, size_t len) { if (IsFlatOrExternal(child_arg)) { rep = rep ? AppendLeaf(rep, child_arg, offset, len) : CreateFromLeaf(child_arg, offset, len, extra); - } else if (rep) { + } else if (rep) { rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len); } else if (offset == 0 && child_arg->length == len) { rep = Mutable(child_arg->ring(), extra); - } else { + } else { rep = SubRing(child_arg->ring(), offset, len, extra); - } - }); - return Validate(rep, nullptr, __LINE__); -} - -CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) { - size_t length = child->length; - if (IsFlatOrExternal(child)) { - return CreateFromLeaf(child, 0, length, extra); - } + } + }); + return Validate(rep, nullptr, __LINE__); +} + +CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) { + size_t length = child->length; + if (IsFlatOrExternal(child)) { + return CreateFromLeaf(child, 0, length, extra); + } if (child->IsRing()) { - return Mutable(child->ring(), extra); - } - return CreateSlow(child, extra); -} - -template <CordRepRing::AddMode mode> -CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring, + return Mutable(child->ring(), extra); + } + return CreateSlow(child, extra); +} + +template <CordRepRing::AddMode mode> +CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring, size_t offset, size_t len) { - assert(offset < ring->length); - constexpr bool append = mode == AddMode::kAppend; - Position head = ring->Find(offset); + assert(offset < ring->length); + constexpr bool append = mode == AddMode::kAppend; + Position head = ring->Find(offset); Position tail = ring->FindTail(head.index, offset + len); - const index_type entries = ring->entries(head.index, tail.index); - - rep = Mutable(rep, entries); - - // The delta for making ring[head].end_pos into 'len - offset' - const pos_type delta_length = + const index_type entries = ring->entries(head.index, tail.index); + + rep = Mutable(rep, entries); + + // The delta for making ring[head].end_pos into 'len - offset' + const pos_type delta_length = (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - len) - - ring->entry_begin_pos(head.index) - head.offset; - - // Start filling at `tail`, or `entries` before `head` - Filler filler(rep, append ? rep->tail_ : rep->retreat(rep->head_, entries)); - - if (ring->refcount.IsOne()) { - // Copy entries from source stealing the ref and adjusting the end position. - // Commit the filler as this is no-op. - ring->ForEach(head.index, tail.index, [&](index_type ix) { - filler.Add(ring->entry_child(ix), ring->entry_data_offset(ix), - ring->entry_end_pos(ix) + delta_length); - }); - - // Unref entries we did not copy over, and delete source. - if (head.index != ring->head_) UnrefEntries(ring, ring->head_, head.index); - if (tail.index != ring->tail_) UnrefEntries(ring, tail.index, ring->tail_); - CordRepRing::Delete(ring); - } else { - ring->ForEach(head.index, tail.index, [&](index_type ix) { - CordRep* child = ring->entry_child(ix); - filler.Add(child, ring->entry_data_offset(ix), - ring->entry_end_pos(ix) + delta_length); - CordRep::Ref(child); - }); - CordRepRing::Unref(ring); - } - - if (head.offset) { - // Increase offset of first 'source' entry appended or prepended. - // This is always the entry in `filler.head()` - rep->AddDataOffset(filler.head(), head.offset); - } - - if (tail.offset) { - // Reduce length of last 'source' entry appended or prepended. - // This is always the entry tailed by `filler.pos()` - rep->SubLength(rep->retreat(filler.pos()), tail.offset); - } - - // Commit changes + ring->entry_begin_pos(head.index) - head.offset; + + // Start filling at `tail`, or `entries` before `head` + Filler filler(rep, append ? rep->tail_ : rep->retreat(rep->head_, entries)); + + if (ring->refcount.IsOne()) { + // Copy entries from source stealing the ref and adjusting the end position. + // Commit the filler as this is no-op. + ring->ForEach(head.index, tail.index, [&](index_type ix) { + filler.Add(ring->entry_child(ix), ring->entry_data_offset(ix), + ring->entry_end_pos(ix) + delta_length); + }); + + // Unref entries we did not copy over, and delete source. + if (head.index != ring->head_) UnrefEntries(ring, ring->head_, head.index); + if (tail.index != ring->tail_) UnrefEntries(ring, tail.index, ring->tail_); + CordRepRing::Delete(ring); + } else { + ring->ForEach(head.index, tail.index, [&](index_type ix) { + CordRep* child = ring->entry_child(ix); + filler.Add(child, ring->entry_data_offset(ix), + ring->entry_end_pos(ix) + delta_length); + CordRep::Ref(child); + }); + CordRepRing::Unref(ring); + } + + if (head.offset) { + // Increase offset of first 'source' entry appended or prepended. + // This is always the entry in `filler.head()` + rep->AddDataOffset(filler.head(), head.offset); + } + + if (tail.offset) { + // Reduce length of last 'source' entry appended or prepended. + // This is always the entry tailed by `filler.pos()` + rep->SubLength(rep->retreat(filler.pos()), tail.offset); + } + + // Commit changes rep->length += len; - if (append) { - rep->tail_ = filler.pos(); - } else { - rep->head_ = filler.head(); + if (append) { + rep->tail_ = filler.pos(); + } else { + rep->head_ = filler.head(); rep->begin_pos_ -= len; - } - - return Validate(rep); -} - -CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) { + } + + return Validate(rep); +} + +CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) { Consume(child, [&rep](CordRep* child_arg, size_t offset, size_t len) { if (child_arg->IsRing()) { rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len); - } else { + } else { rep = AppendLeaf(rep, child_arg, offset, len); - } - }); - return rep; -} - -CordRepRing* CordRepRing::AppendLeaf(CordRepRing* rep, CordRep* child, + } + }); + return rep; +} + +CordRepRing* CordRepRing::AppendLeaf(CordRepRing* rep, CordRep* child, size_t offset, size_t len) { - rep = Mutable(rep, 1); - index_type back = rep->tail_; - const pos_type begin_pos = rep->begin_pos_ + rep->length; - rep->tail_ = rep->advance(rep->tail_); + rep = Mutable(rep, 1); + index_type back = rep->tail_; + const pos_type begin_pos = rep->begin_pos_ + rep->length; + rep->tail_ = rep->advance(rep->tail_); rep->length += len; rep->entry_end_pos()[back] = begin_pos + len; - rep->entry_child()[back] = child; - rep->entry_data_offset()[back] = static_cast<offset_type>(offset); - return Validate(rep, nullptr, __LINE__); -} - -CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) { - size_t length = child->length; - if (IsFlatOrExternal(child)) { - return AppendLeaf(rep, child, 0, length); - } + rep->entry_child()[back] = child; + rep->entry_data_offset()[back] = static_cast<offset_type>(offset); + return Validate(rep, nullptr, __LINE__); +} + +CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) { + size_t length = child->length; + if (IsFlatOrExternal(child)) { + return AppendLeaf(rep, child, 0, length); + } if (child->IsRing()) { - return AddRing<AddMode::kAppend>(rep, child->ring(), 0, length); - } - return AppendSlow(rep, child); -} - -CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) { + return AddRing<AddMode::kAppend>(rep, child->ring(), 0, length); + } + return AppendSlow(rep, child); +} + +CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) { ReverseConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) { if (IsFlatOrExternal(child_arg)) { rep = PrependLeaf(rep, child_arg, offset, len); - } else { + } else { rep = AddRing<AddMode::kPrepend>(rep, child_arg->ring(), offset, len); - } - }); - return Validate(rep); -} - -CordRepRing* CordRepRing::PrependLeaf(CordRepRing* rep, CordRep* child, + } + }); + return Validate(rep); +} + +CordRepRing* CordRepRing::PrependLeaf(CordRepRing* rep, CordRep* child, size_t offset, size_t len) { - rep = Mutable(rep, 1); - index_type head = rep->retreat(rep->head_); - pos_type end_pos = rep->begin_pos_; - rep->head_ = head; + rep = Mutable(rep, 1); + index_type head = rep->retreat(rep->head_); + pos_type end_pos = rep->begin_pos_; + rep->head_ = head; rep->length += len; rep->begin_pos_ -= len; - rep->entry_end_pos()[head] = end_pos; - rep->entry_child()[head] = child; - rep->entry_data_offset()[head] = static_cast<offset_type>(offset); - return Validate(rep); -} - -CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) { - size_t length = child->length; - if (IsFlatOrExternal(child)) { - return PrependLeaf(rep, child, 0, length); - } + rep->entry_end_pos()[head] = end_pos; + rep->entry_child()[head] = child; + rep->entry_data_offset()[head] = static_cast<offset_type>(offset); + return Validate(rep); +} + +CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) { + size_t length = child->length; + if (IsFlatOrExternal(child)) { + return PrependLeaf(rep, child, 0, length); + } if (child->IsRing()) { - return AddRing<AddMode::kPrepend>(rep, child->ring(), 0, length); - } - return PrependSlow(rep, child); -} - -CordRepRing* CordRepRing::Append(CordRepRing* rep, y_absl::string_view data, - size_t extra) { + return AddRing<AddMode::kPrepend>(rep, child->ring(), 0, length); + } + return PrependSlow(rep, child); +} + +CordRepRing* CordRepRing::Append(CordRepRing* rep, y_absl::string_view data, + size_t extra) { if (rep->refcount.IsMutable()) { - Span<char> avail = rep->GetAppendBuffer(data.length()); - if (!avail.empty()) { - memcpy(avail.data(), data.data(), avail.length()); - data.remove_prefix(avail.length()); - } - } - if (data.empty()) return Validate(rep); - - const size_t flats = (data.length() - 1) / kMaxFlatLength + 1; - rep = Mutable(rep, flats); - - Filler filler(rep, rep->tail_); - pos_type pos = rep->begin_pos_ + rep->length; - - while (data.length() >= kMaxFlatLength) { - auto* flat = CreateFlat(data.data(), kMaxFlatLength); - filler.Add(flat, 0, pos += kMaxFlatLength); - data.remove_prefix(kMaxFlatLength); - } - - if (data.length()) { - auto* flat = CreateFlat(data.data(), data.length(), extra); - filler.Add(flat, 0, pos += data.length()); - } - - rep->length = pos - rep->begin_pos_; - rep->tail_ = filler.pos(); - - return Validate(rep); -} - -CordRepRing* CordRepRing::Prepend(CordRepRing* rep, y_absl::string_view data, - size_t extra) { + Span<char> avail = rep->GetAppendBuffer(data.length()); + if (!avail.empty()) { + memcpy(avail.data(), data.data(), avail.length()); + data.remove_prefix(avail.length()); + } + } + if (data.empty()) return Validate(rep); + + const size_t flats = (data.length() - 1) / kMaxFlatLength + 1; + rep = Mutable(rep, flats); + + Filler filler(rep, rep->tail_); + pos_type pos = rep->begin_pos_ + rep->length; + + while (data.length() >= kMaxFlatLength) { + auto* flat = CreateFlat(data.data(), kMaxFlatLength); + filler.Add(flat, 0, pos += kMaxFlatLength); + data.remove_prefix(kMaxFlatLength); + } + + if (data.length()) { + auto* flat = CreateFlat(data.data(), data.length(), extra); + filler.Add(flat, 0, pos += data.length()); + } + + rep->length = pos - rep->begin_pos_; + rep->tail_ = filler.pos(); + + return Validate(rep); +} + +CordRepRing* CordRepRing::Prepend(CordRepRing* rep, y_absl::string_view data, + size_t extra) { if (rep->refcount.IsMutable()) { - Span<char> avail = rep->GetPrependBuffer(data.length()); - if (!avail.empty()) { - const char* tail = data.data() + data.length() - avail.length(); - memcpy(avail.data(), tail, avail.length()); - data.remove_suffix(avail.length()); - } - } - if (data.empty()) return rep; - - const size_t flats = (data.length() - 1) / kMaxFlatLength + 1; - rep = Mutable(rep, flats); - pos_type pos = rep->begin_pos_; - Filler filler(rep, rep->retreat(rep->head_, static_cast<index_type>(flats))); - - size_t first_size = data.size() - (flats - 1) * kMaxFlatLength; - CordRepFlat* flat = CordRepFlat::New(first_size + extra); - flat->length = first_size + extra; - memcpy(flat->Data() + extra, data.data(), first_size); - data.remove_prefix(first_size); - filler.Add(flat, extra, pos); - pos -= first_size; - - while (!data.empty()) { - assert(data.size() >= kMaxFlatLength); - flat = CreateFlat(data.data(), kMaxFlatLength); - filler.Add(flat, 0, pos); - pos -= kMaxFlatLength; - data.remove_prefix(kMaxFlatLength); - } - - rep->head_ = filler.head(); - rep->length += rep->begin_pos_ - pos; - rep->begin_pos_ = pos; - - return Validate(rep); -} - -// 32 entries is 32 * sizeof(pos_type) = 4 cache lines on x86 -static constexpr index_type kBinarySearchThreshold = 32; -static constexpr index_type kBinarySearchEndCount = 8; - -template <bool wrap> -CordRepRing::index_type CordRepRing::FindBinary(index_type head, - index_type tail, - size_t offset) const { - index_type count = tail + (wrap ? capacity_ : 0) - head; - do { - count = (count - 1) / 2; - assert(count < entries(head, tail_)); - index_type mid = wrap ? advance(head, count) : head + count; - index_type after_mid = wrap ? advance(mid) : mid + 1; - bool larger = (offset >= entry_end_offset(mid)); - head = larger ? after_mid : head; - tail = larger ? tail : mid; - assert(head != tail); - } while (ABSL_PREDICT_TRUE(count > kBinarySearchEndCount)); - return head; -} - -CordRepRing::Position CordRepRing::FindSlow(index_type head, - size_t offset) const { - index_type tail = tail_; - - // Binary search until we are good for linear search - // Optimize for branchless / non wrapping ops - if (tail > head) { - index_type count = tail - head; - if (count > kBinarySearchThreshold) { - head = FindBinary<false>(head, tail, offset); - } - } else { - index_type count = capacity_ + tail - head; - if (count > kBinarySearchThreshold) { - head = FindBinary<true>(head, tail, offset); - } - } - - pos_type pos = entry_begin_pos(head); - pos_type end_pos = entry_end_pos(head); - while (offset >= Distance(begin_pos_, end_pos)) { - head = advance(head); - pos = end_pos; - end_pos = entry_end_pos(head); - } - - return {head, offset - Distance(begin_pos_, pos)}; -} - -CordRepRing::Position CordRepRing::FindTailSlow(index_type head, - size_t offset) const { - index_type tail = tail_; - const size_t tail_offset = offset - 1; - - // Binary search until we are good for linear search - // Optimize for branchless / non wrapping ops - if (tail > head) { - index_type count = tail - head; - if (count > kBinarySearchThreshold) { - head = FindBinary<false>(head, tail, tail_offset); - } - } else { - index_type count = capacity_ + tail - head; - if (count > kBinarySearchThreshold) { - head = FindBinary<true>(head, tail, tail_offset); - } - } - - size_t end_offset = entry_end_offset(head); - while (tail_offset >= end_offset) { - head = advance(head); - end_offset = entry_end_offset(head); - } - - return {advance(head), end_offset - offset}; -} - -char CordRepRing::GetCharacter(size_t offset) const { - assert(offset < length); - - Position pos = Find(offset); - size_t data_offset = entry_data_offset(pos.index) + pos.offset; - return GetRepData(entry_child(pos.index))[data_offset]; -} - -CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset, + Span<char> avail = rep->GetPrependBuffer(data.length()); + if (!avail.empty()) { + const char* tail = data.data() + data.length() - avail.length(); + memcpy(avail.data(), tail, avail.length()); + data.remove_suffix(avail.length()); + } + } + if (data.empty()) return rep; + + const size_t flats = (data.length() - 1) / kMaxFlatLength + 1; + rep = Mutable(rep, flats); + pos_type pos = rep->begin_pos_; + Filler filler(rep, rep->retreat(rep->head_, static_cast<index_type>(flats))); + + size_t first_size = data.size() - (flats - 1) * kMaxFlatLength; + CordRepFlat* flat = CordRepFlat::New(first_size + extra); + flat->length = first_size + extra; + memcpy(flat->Data() + extra, data.data(), first_size); + data.remove_prefix(first_size); + filler.Add(flat, extra, pos); + pos -= first_size; + + while (!data.empty()) { + assert(data.size() >= kMaxFlatLength); + flat = CreateFlat(data.data(), kMaxFlatLength); + filler.Add(flat, 0, pos); + pos -= kMaxFlatLength; + data.remove_prefix(kMaxFlatLength); + } + + rep->head_ = filler.head(); + rep->length += rep->begin_pos_ - pos; + rep->begin_pos_ = pos; + + return Validate(rep); +} + +// 32 entries is 32 * sizeof(pos_type) = 4 cache lines on x86 +static constexpr index_type kBinarySearchThreshold = 32; +static constexpr index_type kBinarySearchEndCount = 8; + +template <bool wrap> +CordRepRing::index_type CordRepRing::FindBinary(index_type head, + index_type tail, + size_t offset) const { + index_type count = tail + (wrap ? capacity_ : 0) - head; + do { + count = (count - 1) / 2; + assert(count < entries(head, tail_)); + index_type mid = wrap ? advance(head, count) : head + count; + index_type after_mid = wrap ? advance(mid) : mid + 1; + bool larger = (offset >= entry_end_offset(mid)); + head = larger ? after_mid : head; + tail = larger ? tail : mid; + assert(head != tail); + } while (ABSL_PREDICT_TRUE(count > kBinarySearchEndCount)); + return head; +} + +CordRepRing::Position CordRepRing::FindSlow(index_type head, + size_t offset) const { + index_type tail = tail_; + + // Binary search until we are good for linear search + // Optimize for branchless / non wrapping ops + if (tail > head) { + index_type count = tail - head; + if (count > kBinarySearchThreshold) { + head = FindBinary<false>(head, tail, offset); + } + } else { + index_type count = capacity_ + tail - head; + if (count > kBinarySearchThreshold) { + head = FindBinary<true>(head, tail, offset); + } + } + + pos_type pos = entry_begin_pos(head); + pos_type end_pos = entry_end_pos(head); + while (offset >= Distance(begin_pos_, end_pos)) { + head = advance(head); + pos = end_pos; + end_pos = entry_end_pos(head); + } + + return {head, offset - Distance(begin_pos_, pos)}; +} + +CordRepRing::Position CordRepRing::FindTailSlow(index_type head, + size_t offset) const { + index_type tail = tail_; + const size_t tail_offset = offset - 1; + + // Binary search until we are good for linear search + // Optimize for branchless / non wrapping ops + if (tail > head) { + index_type count = tail - head; + if (count > kBinarySearchThreshold) { + head = FindBinary<false>(head, tail, tail_offset); + } + } else { + index_type count = capacity_ + tail - head; + if (count > kBinarySearchThreshold) { + head = FindBinary<true>(head, tail, tail_offset); + } + } + + size_t end_offset = entry_end_offset(head); + while (tail_offset >= end_offset) { + head = advance(head); + end_offset = entry_end_offset(head); + } + + return {advance(head), end_offset - offset}; +} + +char CordRepRing::GetCharacter(size_t offset) const { + assert(offset < length); + + Position pos = Find(offset); + size_t data_offset = entry_data_offset(pos.index) + pos.offset; + return GetRepData(entry_child(pos.index))[data_offset]; +} + +CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset, size_t len, size_t extra) { - assert(offset <= rep->length); + assert(offset <= rep->length); assert(offset <= rep->length - len); - + if (len == 0) { - CordRep::Unref(rep); - return nullptr; - } - - // Find position of first byte - Position head = rep->Find(offset); + CordRep::Unref(rep); + return nullptr; + } + + // Find position of first byte + Position head = rep->Find(offset); Position tail = rep->FindTail(head.index, offset + len); - const size_t new_entries = rep->entries(head.index, tail.index); - + const size_t new_entries = rep->entries(head.index, tail.index); + if (rep->refcount.IsMutable() && extra <= (rep->capacity() - new_entries)) { - // We adopt a privately owned rep and no extra entries needed. - if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); - if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); - rep->head_ = head.index; - rep->tail_ = tail.index; - } else { - // Copy subset to new rep - rep = Copy(rep, head.index, tail.index, extra); - head.index = rep->head_; - tail.index = rep->tail_; - } - - // Adjust begin_pos and length + // We adopt a privately owned rep and no extra entries needed. + if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); + if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); + rep->head_ = head.index; + rep->tail_ = tail.index; + } else { + // Copy subset to new rep + rep = Copy(rep, head.index, tail.index, extra); + head.index = rep->head_; + tail.index = rep->tail_; + } + + // Adjust begin_pos and length rep->length = len; - rep->begin_pos_ += offset; - - // Adjust head and tail blocks - if (head.offset) { - rep->AddDataOffset(head.index, head.offset); - } - if (tail.offset) { - rep->SubLength(rep->retreat(tail.index), tail.offset); - } - - return Validate(rep); -} - -CordRepRing* CordRepRing::RemovePrefix(CordRepRing* rep, size_t len, - size_t extra) { - assert(len <= rep->length); - if (len == rep->length) { - CordRep::Unref(rep); - return nullptr; - } - - Position head = rep->Find(len); + rep->begin_pos_ += offset; + + // Adjust head and tail blocks + if (head.offset) { + rep->AddDataOffset(head.index, head.offset); + } + if (tail.offset) { + rep->SubLength(rep->retreat(tail.index), tail.offset); + } + + return Validate(rep); +} + +CordRepRing* CordRepRing::RemovePrefix(CordRepRing* rep, size_t len, + size_t extra) { + assert(len <= rep->length); + if (len == rep->length) { + CordRep::Unref(rep); + return nullptr; + } + + Position head = rep->Find(len); if (rep->refcount.IsMutable()) { - if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); - rep->head_ = head.index; - } else { - rep = Copy(rep, head.index, rep->tail_, extra); - head.index = rep->head_; - } - - // Adjust begin_pos and length - rep->length -= len; - rep->begin_pos_ += len; - - // Adjust head block - if (head.offset) { - rep->AddDataOffset(head.index, head.offset); - } - - return Validate(rep); -} - -CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len, - size_t extra) { - assert(len <= rep->length); - - if (len == rep->length) { - CordRep::Unref(rep); - return nullptr; - } - - Position tail = rep->FindTail(rep->length - len); + if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); + rep->head_ = head.index; + } else { + rep = Copy(rep, head.index, rep->tail_, extra); + head.index = rep->head_; + } + + // Adjust begin_pos and length + rep->length -= len; + rep->begin_pos_ += len; + + // Adjust head block + if (head.offset) { + rep->AddDataOffset(head.index, head.offset); + } + + return Validate(rep); +} + +CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len, + size_t extra) { + assert(len <= rep->length); + + if (len == rep->length) { + CordRep::Unref(rep); + return nullptr; + } + + Position tail = rep->FindTail(rep->length - len); if (rep->refcount.IsMutable()) { - // We adopt a privately owned rep, scrub. - if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); - rep->tail_ = tail.index; - } else { - // Copy subset to new rep - rep = Copy(rep, rep->head_, tail.index, extra); - tail.index = rep->tail_; - } - - // Adjust length - rep->length -= len; - - // Adjust tail block - if (tail.offset) { - rep->SubLength(rep->retreat(tail.index), tail.offset); - } - - return Validate(rep); -} - -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace y_absl + // We adopt a privately owned rep, scrub. + if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); + rep->tail_ = tail.index; + } else { + // Copy subset to new rep + rep = Copy(rep, rep->head_, tail.index, extra); + tail.index = rep->tail_; + } + + // Adjust length + rep->length -= len; + + // Adjust tail block + if (tail.offset) { + rep->SubLength(rep->retreat(tail.index), tail.offset); + } + + return Validate(rep); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace y_absl diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring.h index 5f9784d8da..a6a4890ab4 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring.h @@ -1,233 +1,233 @@ -// Copyright 2020 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ -#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ - -#include <cassert> -#include <cstddef> -#include <cstdint> -#include <iosfwd> -#include <limits> -#include <memory> - -#include "y_absl/container/internal/layout.h" -#include "y_absl/strings/internal/cord_internal.h" -#include "y_absl/strings/internal/cord_rep_flat.h" - -namespace y_absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { - -// All operations modifying a ring buffer are implemented as static methods -// requiring a CordRepRing instance with a reference adopted by the method. -// -// The methods return the modified ring buffer, which may be equal to the input -// if the input was not shared, and having large enough capacity to accommodate -// any newly added node(s). Otherwise, a copy of the input rep with the new -// node(s) added is returned. -// -// Any modification on non shared ring buffers with enough capacity will then -// require minimum atomic operations. Caller should where possible provide -// reasonable `extra` hints for both anticipated extra `flat` byte space, as -// well as anticipated extra nodes required for complex operations. -// -// Example of code creating a ring buffer, adding some data to it, -// and discarding the buffer when done: -// -// void FunWithRings() { -// // Create ring with 3 flats -// CordRep* flat = CreateFlat("Hello"); -// CordRepRing* ring = CordRepRing::Create(flat, 2); -// ring = CordRepRing::Append(ring, CreateFlat(" ")); -// ring = CordRepRing::Append(ring, CreateFlat("world")); -// DoSomethingWithRing(ring); -// CordRep::Unref(ring); -// } -// -// Example of code Copying an existing ring buffer and modifying it: -// -// void MoreFunWithRings(CordRepRing* src) { -// CordRepRing* ring = CordRep::Ref(src)->ring(); -// ring = CordRepRing::Append(ring, CreateFlat("Hello")); -// ring = CordRepRing::Append(ring, CreateFlat(" ")); -// ring = CordRepRing::Append(ring, CreateFlat("world")); -// DoSomethingWithRing(ring); -// CordRep::Unref(ring); -// } -// -class CordRepRing : public CordRep { - public: - // `pos_type` represents a 'logical position'. A CordRepRing instance has a - // `begin_pos` (default 0), and each node inside the buffer will have an - // `end_pos` which is the `end_pos` of the previous node (or `begin_pos`) plus - // this node's length. The purpose is to allow for a binary search on this - // position, while allowing O(1) prepend and append operations. - using pos_type = size_t; - - // `index_type` is the type for the `head`, `tail` and `capacity` indexes. - // Ring buffers are limited to having no more than four billion entries. - using index_type = uint32_t; - - // `offset_type` is the type for the data offset inside a child rep's data. - using offset_type = uint32_t; - - // Position holds the node index and relative offset into the node for - // some physical offset in the contained data as returned by the Find() - // and FindTail() methods. - struct Position { - index_type index; - size_t offset; - }; - - // The maximum # of child nodes that can be hosted inside a CordRepRing. - static constexpr size_t kMaxCapacity = (std::numeric_limits<uint32_t>::max)(); - - // CordRepring can not be default constructed, moved, copied or assigned. - CordRepRing() = delete; - CordRepRing(const CordRepRing&) = delete; - CordRepRing& operator=(const CordRepRing&) = delete; - - // Returns true if this instance is valid, false if some or all of the - // invariants are broken. Intended for debug purposes only. - // `output` receives an explanation of the broken invariants. - bool IsValid(std::ostream& output) const; - - // Returns the size in bytes for a CordRepRing with `capacity' entries. - static constexpr size_t AllocSize(size_t capacity); - - // Returns the distance in bytes from `pos` to `end_pos`. - static constexpr size_t Distance(pos_type pos, pos_type end_pos); - - // Creates a new ring buffer from the provided `rep`. Adopts a reference - // on `rep`. The returned ring buffer has a capacity of at least `extra + 1` - static CordRepRing* Create(CordRep* child, size_t extra = 0); - - // `head`, `tail` and `capacity` indexes defining the ring buffer boundaries. - index_type head() const { return head_; } - index_type tail() const { return tail_; } - index_type capacity() const { return capacity_; } - - // Returns the number of entries in this instance. - index_type entries() const { return entries(head_, tail_); } - - // Returns the logical begin position of this instance. - pos_type begin_pos() const { return begin_pos_; } - - // Returns the number of entries for a given head-tail range. - // Requires `head` and `tail` values to be less than `capacity()`. - index_type entries(index_type head, index_type tail) const { - assert(head < capacity_ && tail < capacity_); - return tail - head + ((tail > head) ? 0 : capacity_); - } - - // Returns the logical end position of entry `index`. - pos_type const& entry_end_pos(index_type index) const { - assert(IsValidIndex(index)); - return Layout::Partial().Pointer<0>(data_)[index]; - } - - // Returns the child pointer of entry `index`. - CordRep* const& entry_child(index_type index) const { - assert(IsValidIndex(index)); - return Layout::Partial(capacity()).Pointer<1>(data_)[index]; - } - - // Returns the data offset of entry `index` - offset_type const& entry_data_offset(index_type index) const { - assert(IsValidIndex(index)); - return Layout::Partial(capacity(), capacity()).Pointer<2>(data_)[index]; - } - - // Appends the provided child node to the `rep` instance. - // Adopts a reference from `rep` and `child` which may not be null. - // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node - // containing a FLAT or EXTERNAL node, then flat or external the node is added - // 'as is', with an offset added for the SUBSTRING case. - // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING or - // CONCAT tree, then all child nodes not excluded by any start offset or - // length values are added recursively. - static CordRepRing* Append(CordRepRing* rep, CordRep* child); - - // Appends the provided string data to the `rep` instance. - // This function will attempt to utilize any remaining capacity in the last - // node of the input if that node is not shared (directly or indirectly), and - // of type FLAT. Remaining data will be added as one or more FLAT nodes. - // Any last node added to the ring buffer will be allocated with up to - // `extra` bytes of capacity for (anticipated) subsequent append actions. - static CordRepRing* Append(CordRepRing* rep, string_view data, - size_t extra = 0); - - // Prepends the provided child node to the `rep` instance. - // Adopts a reference from `rep` and `child` which may not be null. - // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node - // containing a FLAT or EXTERNAL node, then flat or external the node is - // prepended 'as is', with an optional offset added for the SUBSTRING case. - // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING - // or CONCAT tree, then all child nodes not excluded by any start offset or - // length values are added recursively. - static CordRepRing* Prepend(CordRepRing* rep, CordRep* child); - - // Prepends the provided string data to the `rep` instance. - // This function will attempt to utilize any remaining capacity in the first - // node of the input if that node is not shared (directly or indirectly), and - // of type FLAT. Remaining data will be added as one or more FLAT nodes. - // Any first node prepnded to the ring buffer will be allocated with up to - // `extra` bytes of capacity for (anticipated) subsequent prepend actions. - static CordRepRing* Prepend(CordRepRing* rep, string_view data, - size_t extra = 0); - - // Returns a span referencing potentially unused capacity in the last node. - // The returned span may be empty if no such capacity is available, or if the - // current instance is shared. Else, a span of size `n <= size` is returned. - // If non empty, the ring buffer is adjusted to the new length, with the newly - // added capacity left uninitialized. Callers should assign a value to the - // entire span before any other operations on this instance. - Span<char> GetAppendBuffer(size_t size); - - // Returns a span referencing potentially unused capacity in the first node. - // This function is identical to GetAppendBuffer except that it returns a span - // referencing up to `size` capacity directly before the existing data. - Span<char> GetPrependBuffer(size_t size); - +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <iosfwd> +#include <limits> +#include <memory> + +#include "y_absl/container/internal/layout.h" +#include "y_absl/strings/internal/cord_internal.h" +#include "y_absl/strings/internal/cord_rep_flat.h" + +namespace y_absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// All operations modifying a ring buffer are implemented as static methods +// requiring a CordRepRing instance with a reference adopted by the method. +// +// The methods return the modified ring buffer, which may be equal to the input +// if the input was not shared, and having large enough capacity to accommodate +// any newly added node(s). Otherwise, a copy of the input rep with the new +// node(s) added is returned. +// +// Any modification on non shared ring buffers with enough capacity will then +// require minimum atomic operations. Caller should where possible provide +// reasonable `extra` hints for both anticipated extra `flat` byte space, as +// well as anticipated extra nodes required for complex operations. +// +// Example of code creating a ring buffer, adding some data to it, +// and discarding the buffer when done: +// +// void FunWithRings() { +// // Create ring with 3 flats +// CordRep* flat = CreateFlat("Hello"); +// CordRepRing* ring = CordRepRing::Create(flat, 2); +// ring = CordRepRing::Append(ring, CreateFlat(" ")); +// ring = CordRepRing::Append(ring, CreateFlat("world")); +// DoSomethingWithRing(ring); +// CordRep::Unref(ring); +// } +// +// Example of code Copying an existing ring buffer and modifying it: +// +// void MoreFunWithRings(CordRepRing* src) { +// CordRepRing* ring = CordRep::Ref(src)->ring(); +// ring = CordRepRing::Append(ring, CreateFlat("Hello")); +// ring = CordRepRing::Append(ring, CreateFlat(" ")); +// ring = CordRepRing::Append(ring, CreateFlat("world")); +// DoSomethingWithRing(ring); +// CordRep::Unref(ring); +// } +// +class CordRepRing : public CordRep { + public: + // `pos_type` represents a 'logical position'. A CordRepRing instance has a + // `begin_pos` (default 0), and each node inside the buffer will have an + // `end_pos` which is the `end_pos` of the previous node (or `begin_pos`) plus + // this node's length. The purpose is to allow for a binary search on this + // position, while allowing O(1) prepend and append operations. + using pos_type = size_t; + + // `index_type` is the type for the `head`, `tail` and `capacity` indexes. + // Ring buffers are limited to having no more than four billion entries. + using index_type = uint32_t; + + // `offset_type` is the type for the data offset inside a child rep's data. + using offset_type = uint32_t; + + // Position holds the node index and relative offset into the node for + // some physical offset in the contained data as returned by the Find() + // and FindTail() methods. + struct Position { + index_type index; + size_t offset; + }; + + // The maximum # of child nodes that can be hosted inside a CordRepRing. + static constexpr size_t kMaxCapacity = (std::numeric_limits<uint32_t>::max)(); + + // CordRepring can not be default constructed, moved, copied or assigned. + CordRepRing() = delete; + CordRepRing(const CordRepRing&) = delete; + CordRepRing& operator=(const CordRepRing&) = delete; + + // Returns true if this instance is valid, false if some or all of the + // invariants are broken. Intended for debug purposes only. + // `output` receives an explanation of the broken invariants. + bool IsValid(std::ostream& output) const; + + // Returns the size in bytes for a CordRepRing with `capacity' entries. + static constexpr size_t AllocSize(size_t capacity); + + // Returns the distance in bytes from `pos` to `end_pos`. + static constexpr size_t Distance(pos_type pos, pos_type end_pos); + + // Creates a new ring buffer from the provided `rep`. Adopts a reference + // on `rep`. The returned ring buffer has a capacity of at least `extra + 1` + static CordRepRing* Create(CordRep* child, size_t extra = 0); + + // `head`, `tail` and `capacity` indexes defining the ring buffer boundaries. + index_type head() const { return head_; } + index_type tail() const { return tail_; } + index_type capacity() const { return capacity_; } + + // Returns the number of entries in this instance. + index_type entries() const { return entries(head_, tail_); } + + // Returns the logical begin position of this instance. + pos_type begin_pos() const { return begin_pos_; } + + // Returns the number of entries for a given head-tail range. + // Requires `head` and `tail` values to be less than `capacity()`. + index_type entries(index_type head, index_type tail) const { + assert(head < capacity_ && tail < capacity_); + return tail - head + ((tail > head) ? 0 : capacity_); + } + + // Returns the logical end position of entry `index`. + pos_type const& entry_end_pos(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial().Pointer<0>(data_)[index]; + } + + // Returns the child pointer of entry `index`. + CordRep* const& entry_child(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial(capacity()).Pointer<1>(data_)[index]; + } + + // Returns the data offset of entry `index` + offset_type const& entry_data_offset(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial(capacity(), capacity()).Pointer<2>(data_)[index]; + } + + // Appends the provided child node to the `rep` instance. + // Adopts a reference from `rep` and `child` which may not be null. + // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node + // containing a FLAT or EXTERNAL node, then flat or external the node is added + // 'as is', with an offset added for the SUBSTRING case. + // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING or + // CONCAT tree, then all child nodes not excluded by any start offset or + // length values are added recursively. + static CordRepRing* Append(CordRepRing* rep, CordRep* child); + + // Appends the provided string data to the `rep` instance. + // This function will attempt to utilize any remaining capacity in the last + // node of the input if that node is not shared (directly or indirectly), and + // of type FLAT. Remaining data will be added as one or more FLAT nodes. + // Any last node added to the ring buffer will be allocated with up to + // `extra` bytes of capacity for (anticipated) subsequent append actions. + static CordRepRing* Append(CordRepRing* rep, string_view data, + size_t extra = 0); + + // Prepends the provided child node to the `rep` instance. + // Adopts a reference from `rep` and `child` which may not be null. + // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node + // containing a FLAT or EXTERNAL node, then flat or external the node is + // prepended 'as is', with an optional offset added for the SUBSTRING case. + // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING + // or CONCAT tree, then all child nodes not excluded by any start offset or + // length values are added recursively. + static CordRepRing* Prepend(CordRepRing* rep, CordRep* child); + + // Prepends the provided string data to the `rep` instance. + // This function will attempt to utilize any remaining capacity in the first + // node of the input if that node is not shared (directly or indirectly), and + // of type FLAT. Remaining data will be added as one or more FLAT nodes. + // Any first node prepnded to the ring buffer will be allocated with up to + // `extra` bytes of capacity for (anticipated) subsequent prepend actions. + static CordRepRing* Prepend(CordRepRing* rep, string_view data, + size_t extra = 0); + + // Returns a span referencing potentially unused capacity in the last node. + // The returned span may be empty if no such capacity is available, or if the + // current instance is shared. Else, a span of size `n <= size` is returned. + // If non empty, the ring buffer is adjusted to the new length, with the newly + // added capacity left uninitialized. Callers should assign a value to the + // entire span before any other operations on this instance. + Span<char> GetAppendBuffer(size_t size); + + // Returns a span referencing potentially unused capacity in the first node. + // This function is identical to GetAppendBuffer except that it returns a span + // referencing up to `size` capacity directly before the existing data. + Span<char> GetPrependBuffer(size_t size); + // Returns a cord ring buffer containing `len` bytes of data starting at - // `offset`. If the input is not shared, this function will remove all head - // and tail child nodes outside of the requested range, and adjust the new - // head and tail nodes as required. If the input is shared, this function - // returns a new instance sharing some or all of the nodes from the input. + // `offset`. If the input is not shared, this function will remove all head + // and tail child nodes outside of the requested range, and adjust the new + // head and tail nodes as required. If the input is shared, this function + // returns a new instance sharing some or all of the nodes from the input. static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t len, - size_t extra = 0); - + size_t extra = 0); + // Returns a cord ring buffer with the first `len` bytes removed. - // If the input is not shared, this function will remove all head child nodes - // fully inside the first `length` bytes, and adjust the new head as required. - // If the input is shared, this function returns a new instance sharing some - // or all of the nodes from the input. + // If the input is not shared, this function will remove all head child nodes + // fully inside the first `length` bytes, and adjust the new head as required. + // If the input is shared, this function returns a new instance sharing some + // or all of the nodes from the input. static CordRepRing* RemoveSuffix(CordRepRing* r, size_t len, - size_t extra = 0); - + size_t extra = 0); + // Returns a cord ring buffer with the last `len` bytes removed. - // If the input is not shared, this function will remove all head child nodes - // fully inside the first `length` bytes, and adjust the new head as required. - // If the input is shared, this function returns a new instance sharing some - // or all of the nodes from the input. - static CordRepRing* RemovePrefix(CordRepRing* r, size_t len, - size_t extra = 0); - - // Returns the character at `offset`. Requires that `offset < length`. - char GetCharacter(size_t offset) const; - + // If the input is not shared, this function will remove all head child nodes + // fully inside the first `length` bytes, and adjust the new head as required. + // If the input is shared, this function returns a new instance sharing some + // or all of the nodes from the input. + static CordRepRing* RemovePrefix(CordRepRing* r, size_t len, + size_t extra = 0); + + // Returns the character at `offset`. Requires that `offset < length`. + char GetCharacter(size_t offset) const; + // Returns true if this instance manages a single contiguous buffer, in which // case the (optional) output parameter `fragment` is set. Otherwise, the // function returns false, and `fragment` is left unchanged. @@ -240,345 +240,345 @@ class CordRepRing : public CordRep { // returns false, and `fragment` is left unchanged. bool IsFlat(size_t offset, size_t len, y_absl::string_view* fragment) const; - // Testing only: set capacity to requested capacity. - void SetCapacityForTesting(size_t capacity); - - // Returns the CordRep data pointer for the provided CordRep. - // Requires that the provided `rep` is either a FLAT or EXTERNAL CordRep. - static const char* GetLeafData(const CordRep* rep); - - // Returns the CordRep data pointer for the provided CordRep. - // Requires that `rep` is either a FLAT, EXTERNAL, or SUBSTRING CordRep. - static const char* GetRepData(const CordRep* rep); - - // Advances the provided position, wrapping around capacity as needed. - // Requires `index` < capacity() - inline index_type advance(index_type index) const; - - // Advances the provided position by 'n`, wrapping around capacity as needed. - // Requires `index` < capacity() and `n` <= capacity. - inline index_type advance(index_type index, index_type n) const; - - // Retreats the provided position, wrapping around 0 as needed. - // Requires `index` < capacity() - inline index_type retreat(index_type index) const; - - // Retreats the provided position by 'n', wrapping around 0 as needed. - // Requires `index` < capacity() - inline index_type retreat(index_type index, index_type n) const; - - // Returns the logical begin position of entry `index` - pos_type const& entry_begin_pos(index_type index) const { - return (index == head_) ? begin_pos_ : entry_end_pos(retreat(index)); - } - - // Returns the physical start offset of entry `index` - size_t entry_start_offset(index_type index) const { - return Distance(begin_pos_, entry_begin_pos(index)); - } - - // Returns the physical end offset of entry `index` - size_t entry_end_offset(index_type index) const { - return Distance(begin_pos_, entry_end_pos(index)); - } - - // Returns the data length for entry `index` - size_t entry_length(index_type index) const { - return Distance(entry_begin_pos(index), entry_end_pos(index)); - } - - // Returns the data for entry `index` - y_absl::string_view entry_data(index_type index) const; - - // Returns the position for `offset` as {index, prefix}. `index` holds the - // index of the entry at the specified offset and `prefix` holds the relative - // offset inside that entry. - // Requires `offset` < length. - // - // For example we can implement GetCharacter(offset) as: - // char GetCharacter(size_t offset) { - // Position pos = this->Find(offset); - // return this->entry_data(pos.pos)[pos.offset]; - // } - inline Position Find(size_t offset) const; - - // Find starting at `head` - inline Position Find(index_type head, size_t offset) const; - - // Returns the tail position for `offset` as {tail index, suffix}. - // `tail index` holds holds the index of the entry holding the offset directly - // before 'offset` advanced by one. 'suffix` holds the relative offset from - // that relative offset in the entry to the end of the entry. - // For example, FindTail(length) will return {tail(), 0}, FindTail(length - 5) - // will return {retreat(tail), 5)} provided the preceding entry contains at - // least 5 bytes of data. - // Requires offset >= 1 && offset <= length. - // - // This function is very useful in functions that need to clip the end of some - // ring buffer such as 'RemovePrefix'. - // For example, we could implement RemovePrefix for non shared instances as: - // void RemoveSuffix(size_t n) { - // Position pos = FindTail(length - n); - // UnrefEntries(pos.pos, this->tail_); - // this->tail_ = pos.pos; - // entry(retreat(pos.pos)).end_pos -= pos.offset; - // } - inline Position FindTail(size_t offset) const; - - // Find tail starting at `head` - inline Position FindTail(index_type head, size_t offset) const; - - // Invokes f(index_type index) for each entry inside the range [head, tail> - template <typename F> - void ForEach(index_type head, index_type tail, F&& f) const { - index_type n1 = (tail > head) ? tail : capacity_; - for (index_type i = head; i < n1; ++i) f(i); - if (tail <= head) { - for (index_type i = 0; i < tail; ++i) f(i); - } - } - - // Invokes f(index_type index) for each entry inside this instance. - template <typename F> - void ForEach(F&& f) const { - ForEach(head_, tail_, std::forward<F>(f)); - } - - // Dump this instance's data tp stream `s` in human readable format, excluding - // the actual data content itself. Intended for debug purposes only. - friend std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); - - private: - enum class AddMode { kAppend, kPrepend }; - - using Layout = container_internal::Layout<pos_type, CordRep*, offset_type>; - - class Filler; - class Transaction; - class CreateTransaction; - - static constexpr size_t kLayoutAlignment = Layout::Partial().Alignment(); - - // Creates a new CordRepRing. - explicit CordRepRing(index_type capacity) : capacity_(capacity) {} - - // Returns true if `index` is a valid index into this instance. - bool IsValidIndex(index_type index) const; - - // Debug use only: validates the provided CordRepRing invariants. - // Verification of all CordRepRing methods can be enabled by defining - // EXTRA_CORD_RING_VALIDATION, i.e.: `--copts=-DEXTRA_CORD_RING_VALIDATION` - // Verification is VERY expensive, so only do it for debugging purposes. - static CordRepRing* Validate(CordRepRing* rep, const char* file = nullptr, - int line = 0); - - // Allocates a CordRepRing large enough to hold `capacity + extra' entries. - // The returned capacity may be larger if the allocated memory allows for it. - // The maximum capacity of a CordRepRing is capped at kMaxCapacity. - // Throws `std::length_error` if `capacity + extra' exceeds kMaxCapacity. - static CordRepRing* New(size_t capacity, size_t extra); - - // Deallocates (but does not destroy) the provided ring buffer. - static void Delete(CordRepRing* rep); - - // Destroys the provided ring buffer, decrementing the reference count of all - // contained child CordReps. The provided 1\`rep` should have a ref count of + // Testing only: set capacity to requested capacity. + void SetCapacityForTesting(size_t capacity); + + // Returns the CordRep data pointer for the provided CordRep. + // Requires that the provided `rep` is either a FLAT or EXTERNAL CordRep. + static const char* GetLeafData(const CordRep* rep); + + // Returns the CordRep data pointer for the provided CordRep. + // Requires that `rep` is either a FLAT, EXTERNAL, or SUBSTRING CordRep. + static const char* GetRepData(const CordRep* rep); + + // Advances the provided position, wrapping around capacity as needed. + // Requires `index` < capacity() + inline index_type advance(index_type index) const; + + // Advances the provided position by 'n`, wrapping around capacity as needed. + // Requires `index` < capacity() and `n` <= capacity. + inline index_type advance(index_type index, index_type n) const; + + // Retreats the provided position, wrapping around 0 as needed. + // Requires `index` < capacity() + inline index_type retreat(index_type index) const; + + // Retreats the provided position by 'n', wrapping around 0 as needed. + // Requires `index` < capacity() + inline index_type retreat(index_type index, index_type n) const; + + // Returns the logical begin position of entry `index` + pos_type const& entry_begin_pos(index_type index) const { + return (index == head_) ? begin_pos_ : entry_end_pos(retreat(index)); + } + + // Returns the physical start offset of entry `index` + size_t entry_start_offset(index_type index) const { + return Distance(begin_pos_, entry_begin_pos(index)); + } + + // Returns the physical end offset of entry `index` + size_t entry_end_offset(index_type index) const { + return Distance(begin_pos_, entry_end_pos(index)); + } + + // Returns the data length for entry `index` + size_t entry_length(index_type index) const { + return Distance(entry_begin_pos(index), entry_end_pos(index)); + } + + // Returns the data for entry `index` + y_absl::string_view entry_data(index_type index) const; + + // Returns the position for `offset` as {index, prefix}. `index` holds the + // index of the entry at the specified offset and `prefix` holds the relative + // offset inside that entry. + // Requires `offset` < length. + // + // For example we can implement GetCharacter(offset) as: + // char GetCharacter(size_t offset) { + // Position pos = this->Find(offset); + // return this->entry_data(pos.pos)[pos.offset]; + // } + inline Position Find(size_t offset) const; + + // Find starting at `head` + inline Position Find(index_type head, size_t offset) const; + + // Returns the tail position for `offset` as {tail index, suffix}. + // `tail index` holds holds the index of the entry holding the offset directly + // before 'offset` advanced by one. 'suffix` holds the relative offset from + // that relative offset in the entry to the end of the entry. + // For example, FindTail(length) will return {tail(), 0}, FindTail(length - 5) + // will return {retreat(tail), 5)} provided the preceding entry contains at + // least 5 bytes of data. + // Requires offset >= 1 && offset <= length. + // + // This function is very useful in functions that need to clip the end of some + // ring buffer such as 'RemovePrefix'. + // For example, we could implement RemovePrefix for non shared instances as: + // void RemoveSuffix(size_t n) { + // Position pos = FindTail(length - n); + // UnrefEntries(pos.pos, this->tail_); + // this->tail_ = pos.pos; + // entry(retreat(pos.pos)).end_pos -= pos.offset; + // } + inline Position FindTail(size_t offset) const; + + // Find tail starting at `head` + inline Position FindTail(index_type head, size_t offset) const; + + // Invokes f(index_type index) for each entry inside the range [head, tail> + template <typename F> + void ForEach(index_type head, index_type tail, F&& f) const { + index_type n1 = (tail > head) ? tail : capacity_; + for (index_type i = head; i < n1; ++i) f(i); + if (tail <= head) { + for (index_type i = 0; i < tail; ++i) f(i); + } + } + + // Invokes f(index_type index) for each entry inside this instance. + template <typename F> + void ForEach(F&& f) const { + ForEach(head_, tail_, std::forward<F>(f)); + } + + // Dump this instance's data tp stream `s` in human readable format, excluding + // the actual data content itself. Intended for debug purposes only. + friend std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); + + private: + enum class AddMode { kAppend, kPrepend }; + + using Layout = container_internal::Layout<pos_type, CordRep*, offset_type>; + + class Filler; + class Transaction; + class CreateTransaction; + + static constexpr size_t kLayoutAlignment = Layout::Partial().Alignment(); + + // Creates a new CordRepRing. + explicit CordRepRing(index_type capacity) : capacity_(capacity) {} + + // Returns true if `index` is a valid index into this instance. + bool IsValidIndex(index_type index) const; + + // Debug use only: validates the provided CordRepRing invariants. + // Verification of all CordRepRing methods can be enabled by defining + // EXTRA_CORD_RING_VALIDATION, i.e.: `--copts=-DEXTRA_CORD_RING_VALIDATION` + // Verification is VERY expensive, so only do it for debugging purposes. + static CordRepRing* Validate(CordRepRing* rep, const char* file = nullptr, + int line = 0); + + // Allocates a CordRepRing large enough to hold `capacity + extra' entries. + // The returned capacity may be larger if the allocated memory allows for it. + // The maximum capacity of a CordRepRing is capped at kMaxCapacity. + // Throws `std::length_error` if `capacity + extra' exceeds kMaxCapacity. + static CordRepRing* New(size_t capacity, size_t extra); + + // Deallocates (but does not destroy) the provided ring buffer. + static void Delete(CordRepRing* rep); + + // Destroys the provided ring buffer, decrementing the reference count of all + // contained child CordReps. The provided 1\`rep` should have a ref count of // one (pre decrement destroy call observing `refcount.IsOne()`) or zero // (post decrement destroy call observing `!refcount.Decrement()`). - static void Destroy(CordRepRing* rep); - - // Returns a mutable reference to the logical end position array. - pos_type* entry_end_pos() { - return Layout::Partial().Pointer<0>(data_); - } - - // Returns a mutable reference to the child pointer array. - CordRep** entry_child() { - return Layout::Partial(capacity()).Pointer<1>(data_); - } - - // Returns a mutable reference to the data offset array. - offset_type* entry_data_offset() { - return Layout::Partial(capacity(), capacity()).Pointer<2>(data_); - } - - // Find implementations for the non fast path 0 / length cases. - Position FindSlow(index_type head, size_t offset) const; - Position FindTailSlow(index_type head, size_t offset) const; - - // Finds the index of the first node that is inside a reasonable distance - // of the node at `offset` from which we can continue with a linear search. - template <bool wrap> - index_type FindBinary(index_type head, index_type tail, size_t offset) const; - - // Fills the current (initialized) instance from the provided source, copying - // entries [head, tail). Adds a reference to copied entries if `ref` is true. - template <bool ref> - void Fill(const CordRepRing* src, index_type head, index_type tail); - - // Create a copy of 'rep', copying all entries [head, tail), allocating room - // for `extra` entries. Adds a reference on all copied entries. - static CordRepRing* Copy(CordRepRing* rep, index_type head, index_type tail, - size_t extra = 0); - - // Returns a Mutable CordRepRing reference from `rep` with room for at least - // `extra` additional nodes. Adopts a reference count from `rep`. - // This function will return `rep` if, and only if: - // - rep.entries + extra <= rep.capacity - // - rep.refcount == 1 - // Otherwise, this function will create a new copy of `rep` with additional - // capacity to satisfy `extra` extra nodes, and unref the old `rep` instance. - // - // If a new CordRepRing can not be allocated, or the new capacity would exceed - // the maxmimum capacity, then the input is consumed only, and an exception is - // thrown. - static CordRepRing* Mutable(CordRepRing* rep, size_t extra); - - // Slow path for Append(CordRepRing* rep, CordRep* child). This function is - // exercised if the provided `child` in Append() is not a leaf node, i.e., a - // ring buffer or old (concat) cord tree. - static CordRepRing* AppendSlow(CordRepRing* rep, CordRep* child); - - // Appends the provided leaf node. Requires `child` to be FLAT or EXTERNAL. - static CordRepRing* AppendLeaf(CordRepRing* rep, CordRep* child, - size_t offset, size_t length); - - // Prepends the provided leaf node. Requires `child` to be FLAT or EXTERNAL. - static CordRepRing* PrependLeaf(CordRepRing* rep, CordRep* child, - size_t offset, size_t length); - - // Slow path for Prepend(CordRepRing* rep, CordRep* child). This function is - // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a - // ring buffer or old (concat) cord tree. - static CordRepRing* PrependSlow(CordRepRing* rep, CordRep* child); - - // Slow path for Create(CordRep* child, size_t extra). This function is - // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a - // ring buffer or old (concat) cord tree. - static CordRepRing* CreateSlow(CordRep* child, size_t extra); - - // Creates a new ring buffer from the provided `child` leaf node. Requires - // `child` to be FLAT or EXTERNAL. on `rep`. - // The returned ring buffer has a capacity of at least `1 + extra` - static CordRepRing* CreateFromLeaf(CordRep* child, size_t offset, - size_t length, size_t extra); - - // Appends or prepends (depending on AddMode) the ring buffer in `ring' to + static void Destroy(CordRepRing* rep); + + // Returns a mutable reference to the logical end position array. + pos_type* entry_end_pos() { + return Layout::Partial().Pointer<0>(data_); + } + + // Returns a mutable reference to the child pointer array. + CordRep** entry_child() { + return Layout::Partial(capacity()).Pointer<1>(data_); + } + + // Returns a mutable reference to the data offset array. + offset_type* entry_data_offset() { + return Layout::Partial(capacity(), capacity()).Pointer<2>(data_); + } + + // Find implementations for the non fast path 0 / length cases. + Position FindSlow(index_type head, size_t offset) const; + Position FindTailSlow(index_type head, size_t offset) const; + + // Finds the index of the first node that is inside a reasonable distance + // of the node at `offset` from which we can continue with a linear search. + template <bool wrap> + index_type FindBinary(index_type head, index_type tail, size_t offset) const; + + // Fills the current (initialized) instance from the provided source, copying + // entries [head, tail). Adds a reference to copied entries if `ref` is true. + template <bool ref> + void Fill(const CordRepRing* src, index_type head, index_type tail); + + // Create a copy of 'rep', copying all entries [head, tail), allocating room + // for `extra` entries. Adds a reference on all copied entries. + static CordRepRing* Copy(CordRepRing* rep, index_type head, index_type tail, + size_t extra = 0); + + // Returns a Mutable CordRepRing reference from `rep` with room for at least + // `extra` additional nodes. Adopts a reference count from `rep`. + // This function will return `rep` if, and only if: + // - rep.entries + extra <= rep.capacity + // - rep.refcount == 1 + // Otherwise, this function will create a new copy of `rep` with additional + // capacity to satisfy `extra` extra nodes, and unref the old `rep` instance. + // + // If a new CordRepRing can not be allocated, or the new capacity would exceed + // the maxmimum capacity, then the input is consumed only, and an exception is + // thrown. + static CordRepRing* Mutable(CordRepRing* rep, size_t extra); + + // Slow path for Append(CordRepRing* rep, CordRep* child). This function is + // exercised if the provided `child` in Append() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* AppendSlow(CordRepRing* rep, CordRep* child); + + // Appends the provided leaf node. Requires `child` to be FLAT or EXTERNAL. + static CordRepRing* AppendLeaf(CordRepRing* rep, CordRep* child, + size_t offset, size_t length); + + // Prepends the provided leaf node. Requires `child` to be FLAT or EXTERNAL. + static CordRepRing* PrependLeaf(CordRepRing* rep, CordRep* child, + size_t offset, size_t length); + + // Slow path for Prepend(CordRepRing* rep, CordRep* child). This function is + // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* PrependSlow(CordRepRing* rep, CordRep* child); + + // Slow path for Create(CordRep* child, size_t extra). This function is + // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* CreateSlow(CordRep* child, size_t extra); + + // Creates a new ring buffer from the provided `child` leaf node. Requires + // `child` to be FLAT or EXTERNAL. on `rep`. + // The returned ring buffer has a capacity of at least `1 + extra` + static CordRepRing* CreateFromLeaf(CordRep* child, size_t offset, + size_t length, size_t extra); + + // Appends or prepends (depending on AddMode) the ring buffer in `ring' to // `rep` starting at `offset` with length `len`. - template <AddMode mode> - static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring, + template <AddMode mode> + static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring, size_t offset, size_t len); - - // Increases the data offset for entry `index` by `n`. - void AddDataOffset(index_type index, size_t n); - - // Descreases the length for entry `index` by `n`. - void SubLength(index_type index, size_t n); - - index_type head_; - index_type tail_; - index_type capacity_; - pos_type begin_pos_; - - alignas(kLayoutAlignment) char data_[kLayoutAlignment]; - - friend struct CordRep; -}; - -constexpr size_t CordRepRing::AllocSize(size_t capacity) { - return sizeof(CordRepRing) - sizeof(data_) + - Layout(capacity, capacity, capacity).AllocSize(); -} - -inline constexpr size_t CordRepRing::Distance(pos_type pos, pos_type end_pos) { - return (end_pos - pos); -} - -inline const char* CordRepRing::GetLeafData(const CordRep* rep) { - return rep->tag != EXTERNAL ? rep->flat()->Data() : rep->external()->base; -} - -inline const char* CordRepRing::GetRepData(const CordRep* rep) { - if (rep->tag >= FLAT) return rep->flat()->Data(); - if (rep->tag == EXTERNAL) return rep->external()->base; - return GetLeafData(rep->substring()->child) + rep->substring()->start; -} - -inline CordRepRing::index_type CordRepRing::advance(index_type index) const { - assert(index < capacity_); - return ++index == capacity_ ? 0 : index; -} - -inline CordRepRing::index_type CordRepRing::advance(index_type index, - index_type n) const { - assert(index < capacity_ && n <= capacity_); - return (index += n) >= capacity_ ? index - capacity_ : index; -} - -inline CordRepRing::index_type CordRepRing::retreat(index_type index) const { - assert(index < capacity_); - return (index > 0 ? index : capacity_) - 1; -} - -inline CordRepRing::index_type CordRepRing::retreat(index_type index, - index_type n) const { - assert(index < capacity_ && n <= capacity_); - return index >= n ? index - n : capacity_ - n + index; -} - -inline y_absl::string_view CordRepRing::entry_data(index_type index) const { - size_t data_offset = entry_data_offset(index); - return {GetRepData(entry_child(index)) + data_offset, entry_length(index)}; -} - -inline bool CordRepRing::IsValidIndex(index_type index) const { - if (index >= capacity_) return false; - return (tail_ > head_) ? (index >= head_ && index < tail_) - : (index >= head_ || index < tail_); -} - -#ifndef EXTRA_CORD_RING_VALIDATION -inline CordRepRing* CordRepRing::Validate(CordRepRing* rep, - const char* /*file*/, int /*line*/) { - return rep; -} -#endif - -inline CordRepRing::Position CordRepRing::Find(size_t offset) const { - assert(offset < length); - return (offset == 0) ? Position{head_, 0} : FindSlow(head_, offset); -} - -inline CordRepRing::Position CordRepRing::Find(index_type head, - size_t offset) const { - assert(offset < length); - assert(IsValidIndex(head) && offset >= entry_start_offset(head)); - return (offset == 0) ? Position{head_, 0} : FindSlow(head, offset); -} - -inline CordRepRing::Position CordRepRing::FindTail(size_t offset) const { - assert(offset > 0 && offset <= length); - return (offset == length) ? Position{tail_, 0} : FindTailSlow(head_, offset); -} - -inline CordRepRing::Position CordRepRing::FindTail(index_type head, - size_t offset) const { - assert(offset > 0 && offset <= length); - assert(IsValidIndex(head) && offset >= entry_start_offset(head) + 1); - return (offset == length) ? Position{tail_, 0} : FindTailSlow(head, offset); -} - -// Now that CordRepRing is defined, we can define CordRep's helper casts: -inline CordRepRing* CordRep::ring() { + + // Increases the data offset for entry `index` by `n`. + void AddDataOffset(index_type index, size_t n); + + // Descreases the length for entry `index` by `n`. + void SubLength(index_type index, size_t n); + + index_type head_; + index_type tail_; + index_type capacity_; + pos_type begin_pos_; + + alignas(kLayoutAlignment) char data_[kLayoutAlignment]; + + friend struct CordRep; +}; + +constexpr size_t CordRepRing::AllocSize(size_t capacity) { + return sizeof(CordRepRing) - sizeof(data_) + + Layout(capacity, capacity, capacity).AllocSize(); +} + +inline constexpr size_t CordRepRing::Distance(pos_type pos, pos_type end_pos) { + return (end_pos - pos); +} + +inline const char* CordRepRing::GetLeafData(const CordRep* rep) { + return rep->tag != EXTERNAL ? rep->flat()->Data() : rep->external()->base; +} + +inline const char* CordRepRing::GetRepData(const CordRep* rep) { + if (rep->tag >= FLAT) return rep->flat()->Data(); + if (rep->tag == EXTERNAL) return rep->external()->base; + return GetLeafData(rep->substring()->child) + rep->substring()->start; +} + +inline CordRepRing::index_type CordRepRing::advance(index_type index) const { + assert(index < capacity_); + return ++index == capacity_ ? 0 : index; +} + +inline CordRepRing::index_type CordRepRing::advance(index_type index, + index_type n) const { + assert(index < capacity_ && n <= capacity_); + return (index += n) >= capacity_ ? index - capacity_ : index; +} + +inline CordRepRing::index_type CordRepRing::retreat(index_type index) const { + assert(index < capacity_); + return (index > 0 ? index : capacity_) - 1; +} + +inline CordRepRing::index_type CordRepRing::retreat(index_type index, + index_type n) const { + assert(index < capacity_ && n <= capacity_); + return index >= n ? index - n : capacity_ - n + index; +} + +inline y_absl::string_view CordRepRing::entry_data(index_type index) const { + size_t data_offset = entry_data_offset(index); + return {GetRepData(entry_child(index)) + data_offset, entry_length(index)}; +} + +inline bool CordRepRing::IsValidIndex(index_type index) const { + if (index >= capacity_) return false; + return (tail_ > head_) ? (index >= head_ && index < tail_) + : (index >= head_ || index < tail_); +} + +#ifndef EXTRA_CORD_RING_VALIDATION +inline CordRepRing* CordRepRing::Validate(CordRepRing* rep, + const char* /*file*/, int /*line*/) { + return rep; +} +#endif + +inline CordRepRing::Position CordRepRing::Find(size_t offset) const { + assert(offset < length); + return (offset == 0) ? Position{head_, 0} : FindSlow(head_, offset); +} + +inline CordRepRing::Position CordRepRing::Find(index_type head, + size_t offset) const { + assert(offset < length); + assert(IsValidIndex(head) && offset >= entry_start_offset(head)); + return (offset == 0) ? Position{head_, 0} : FindSlow(head, offset); +} + +inline CordRepRing::Position CordRepRing::FindTail(size_t offset) const { + assert(offset > 0 && offset <= length); + return (offset == length) ? Position{tail_, 0} : FindTailSlow(head_, offset); +} + +inline CordRepRing::Position CordRepRing::FindTail(index_type head, + size_t offset) const { + assert(offset > 0 && offset <= length); + assert(IsValidIndex(head) && offset >= entry_start_offset(head) + 1); + return (offset == length) ? Position{tail_, 0} : FindTailSlow(head, offset); +} + +// Now that CordRepRing is defined, we can define CordRep's helper casts: +inline CordRepRing* CordRep::ring() { assert(IsRing()); - return static_cast<CordRepRing*>(this); -} - -inline const CordRepRing* CordRep::ring() const { + return static_cast<CordRepRing*>(this); +} + +inline const CordRepRing* CordRep::ring() const { assert(IsRing()); - return static_cast<const CordRepRing*>(this); -} - + return static_cast<const CordRepRing*>(this); +} + inline bool CordRepRing::IsFlat(y_absl::string_view* fragment) const { if (entries() == 1) { if (fragment) *fragment = entry_data(head()); @@ -598,10 +598,10 @@ inline bool CordRepRing::IsFlat(size_t offset, size_t len, return false; } -std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); - -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace y_absl - -#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ +std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring_reader.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring_reader.h index 3f64d04fae..98ea8c170a 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring_reader.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cord_rep_ring_reader.h @@ -1,118 +1,118 @@ -// Copyright 2021 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ -#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ - -#include <cassert> -#include <cstddef> -#include <cstdint> - -#include "y_absl/strings/internal/cord_internal.h" -#include "y_absl/strings/internal/cord_rep_ring.h" -#include "y_absl/strings/string_view.h" - -namespace y_absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { - -// CordRepRingReader provides basic navigation over CordRepRing data. -class CordRepRingReader { - public: - // Returns true if this instance is not empty. - explicit operator bool() const { return ring_ != nullptr; } - - // Returns the ring buffer reference for this instance, or nullptr if empty. - CordRepRing* ring() const { return ring_; } - - // Returns the current node index inside the ring buffer for this instance. - // The returned value is undefined if this instance is empty. - CordRepRing::index_type index() const { return index_; } - +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ + +#include <cassert> +#include <cstddef> +#include <cstdint> + +#include "y_absl/strings/internal/cord_internal.h" +#include "y_absl/strings/internal/cord_rep_ring.h" +#include "y_absl/strings/string_view.h" + +namespace y_absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepRingReader provides basic navigation over CordRepRing data. +class CordRepRingReader { + public: + // Returns true if this instance is not empty. + explicit operator bool() const { return ring_ != nullptr; } + + // Returns the ring buffer reference for this instance, or nullptr if empty. + CordRepRing* ring() const { return ring_; } + + // Returns the current node index inside the ring buffer for this instance. + // The returned value is undefined if this instance is empty. + CordRepRing::index_type index() const { return index_; } + // Returns the current node inside the ring buffer for this instance. // The returned value is undefined if this instance is empty. CordRep* node() const { return ring_->entry_child(index_); } - // Returns the length of the referenced ring buffer. - // Requires the current instance to be non empty. - size_t length() const { - assert(ring_); - return ring_->length; - } - - // Returns the end offset of the last navigated-to chunk, which represents the - // total bytes 'consumed' relative to the start of the ring. The returned - // value is never zero. For example, initializing a reader with a ring buffer - // with a first chunk of 19 bytes will return consumed() = 19. - // Requires the current instance to be non empty. - size_t consumed() const { - assert(ring_); - return ring_->entry_end_offset(index_); - } - - // Returns the number of bytes remaining beyond the last navigated-to chunk. - // Requires the current instance to be non empty. - size_t remaining() const { - assert(ring_); - return length() - consumed(); - } - - // Resets this instance to an empty value - void Reset() { ring_ = nullptr; } - - // Resets this instance to the start of `ring`. `ring` must not be null. - // Returns a reference into the first chunk of the provided ring. - y_absl::string_view Reset(CordRepRing* ring) { - assert(ring); - ring_ = ring; - index_ = ring_->head(); - return ring_->entry_data(index_); - } - - // Navigates to the next chunk inside the reference ring buffer. - // Returns a reference into the navigated-to chunk. - // Requires remaining() to be non zero. - y_absl::string_view Next() { - assert(remaining()); - index_ = ring_->advance(index_); - return ring_->entry_data(index_); - } - - // Navigates to the chunk at offset `offset`. - // Returns a reference into the navigated-to chunk, adjusted for the relative - // position of `offset` into that chunk. For example, calling Seek(13) on a - // ring buffer containing 2 chunks of 10 and 20 bytes respectively will return - // a string view into the second chunk starting at offset 3 with a size of 17. - // Requires `offset` to be less than `length()` - y_absl::string_view Seek(size_t offset) { - assert(offset < length()); - size_t current = ring_->entry_end_offset(index_); - CordRepRing::index_type hint = (offset >= current) ? index_ : ring_->head(); - const CordRepRing::Position head = ring_->Find(hint, offset); - index_ = head.index; - auto data = ring_->entry_data(head.index); - data.remove_prefix(head.offset); - return data; - } - - private: - CordRepRing* ring_ = nullptr; - CordRepRing::index_type index_; -}; - -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace y_absl - -#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ + // Returns the length of the referenced ring buffer. + // Requires the current instance to be non empty. + size_t length() const { + assert(ring_); + return ring_->length; + } + + // Returns the end offset of the last navigated-to chunk, which represents the + // total bytes 'consumed' relative to the start of the ring. The returned + // value is never zero. For example, initializing a reader with a ring buffer + // with a first chunk of 19 bytes will return consumed() = 19. + // Requires the current instance to be non empty. + size_t consumed() const { + assert(ring_); + return ring_->entry_end_offset(index_); + } + + // Returns the number of bytes remaining beyond the last navigated-to chunk. + // Requires the current instance to be non empty. + size_t remaining() const { + assert(ring_); + return length() - consumed(); + } + + // Resets this instance to an empty value + void Reset() { ring_ = nullptr; } + + // Resets this instance to the start of `ring`. `ring` must not be null. + // Returns a reference into the first chunk of the provided ring. + y_absl::string_view Reset(CordRepRing* ring) { + assert(ring); + ring_ = ring; + index_ = ring_->head(); + return ring_->entry_data(index_); + } + + // Navigates to the next chunk inside the reference ring buffer. + // Returns a reference into the navigated-to chunk. + // Requires remaining() to be non zero. + y_absl::string_view Next() { + assert(remaining()); + index_ = ring_->advance(index_); + return ring_->entry_data(index_); + } + + // Navigates to the chunk at offset `offset`. + // Returns a reference into the navigated-to chunk, adjusted for the relative + // position of `offset` into that chunk. For example, calling Seek(13) on a + // ring buffer containing 2 chunks of 10 and 20 bytes respectively will return + // a string view into the second chunk starting at offset 3 with a size of 17. + // Requires `offset` to be less than `length()` + y_absl::string_view Seek(size_t offset) { + assert(offset < length()); + size_t current = ring_->entry_end_offset(index_); + CordRepRing::index_type hint = (offset >= current) ? index_ : ring_->head(); + const CordRepRing::Position head = ring_->Find(hint, offset); + index_ = head.index; + auto data = ring_->entry_data(head.index); + data.remove_prefix(head.offset); + return data; + } + + private: + CordRepRing* ring_ = nullptr; + CordRepRing::index_type index_; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions/ya.make index 06e99346da..d2522532e2 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions/ya.make @@ -2,13 +2,13 @@ LIBRARY() -WITHOUT_LICENSE_TEXTS() - -OWNER( - somov - g:cpp-contrib -) +WITHOUT_LICENSE_TEXTS() +OWNER( + somov + g:cpp-contrib +) + LICENSE(Apache-2.0) PEERDIR( diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle/ya.make index e181217139..0220a75bd1 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_handle/ya.make @@ -1,47 +1,47 @@ -# Generated by devtools/yamaker. - -LIBRARY() - -WITHOUT_LICENSE_TEXTS() - -OWNER( - somov - g:cpp-contrib -) - -LICENSE(Apache-2.0) - -PEERDIR( - contrib/restricted/abseil-cpp-tstring/y_absl/base +# Generated by devtools/yamaker. + +LIBRARY() + +WITHOUT_LICENSE_TEXTS() + +OWNER( + somov + g:cpp-contrib +) + +LICENSE(Apache-2.0) + +PEERDIR( + contrib/restricted/abseil-cpp-tstring/y_absl/base contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_wait - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/throw_delegate - contrib/restricted/abseil-cpp-tstring/y_absl/base/log_severity + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_wait + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/throw_delegate + contrib/restricted/abseil-cpp-tstring/y_absl/base/log_severity contrib/restricted/abseil-cpp-tstring/y_absl/debugging contrib/restricted/abseil-cpp-tstring/y_absl/debugging/stacktrace contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize contrib/restricted/abseil-cpp-tstring/y_absl/demangle - contrib/restricted/abseil-cpp-tstring/y_absl/numeric - contrib/restricted/abseil-cpp-tstring/y_absl/strings + contrib/restricted/abseil-cpp-tstring/y_absl/numeric + contrib/restricted/abseil-cpp-tstring/y_absl/strings contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_strings_internal contrib/restricted/abseil-cpp-tstring/y_absl/synchronization contrib/restricted/abseil-cpp-tstring/y_absl/synchronization/internal contrib/restricted/abseil-cpp-tstring/y_absl/time contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time contrib/restricted/abseil-cpp-tstring/y_absl/time/time_zone -) - -ADDINCL( - GLOBAL contrib/restricted/abseil-cpp-tstring -) - -NO_COMPILER_WARNINGS() - +) + +ADDINCL( + GLOBAL contrib/restricted/abseil-cpp-tstring +) + +NO_COMPILER_WARNINGS() + SRCDIR(contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal) - -SRCS( + +SRCS( cordz_handle.cc -) - -END() +) + +END() diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info/ya.make index 930eaa8b05..4d749dc50b 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_info/ya.make @@ -1,30 +1,30 @@ -# Generated by devtools/yamaker. - -LIBRARY() - -WITHOUT_LICENSE_TEXTS() - -OWNER( - somov - g:cpp-contrib -) - -LICENSE(Apache-2.0) - -PEERDIR( - contrib/restricted/abseil-cpp-tstring/y_absl/base +# Generated by devtools/yamaker. + +LIBRARY() + +WITHOUT_LICENSE_TEXTS() + +OWNER( + somov + g:cpp-contrib +) + +LICENSE(Apache-2.0) + +PEERDIR( + contrib/restricted/abseil-cpp-tstring/y_absl/base contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/low_level_alloc - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_wait - contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/throw_delegate - contrib/restricted/abseil-cpp-tstring/y_absl/base/log_severity + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/spinlock_wait + contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/throw_delegate + contrib/restricted/abseil-cpp-tstring/y_absl/base/log_severity contrib/restricted/abseil-cpp-tstring/y_absl/debugging contrib/restricted/abseil-cpp-tstring/y_absl/debugging/stacktrace contrib/restricted/abseil-cpp-tstring/y_absl/debugging/symbolize contrib/restricted/abseil-cpp-tstring/y_absl/demangle - contrib/restricted/abseil-cpp-tstring/y_absl/numeric + contrib/restricted/abseil-cpp-tstring/y_absl/numeric contrib/restricted/abseil-cpp-tstring/y_absl/profiling/internal/exponential_biased - contrib/restricted/abseil-cpp-tstring/y_absl/strings + contrib/restricted/abseil-cpp-tstring/y_absl/strings contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_cord_internal contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/absl_strings_internal contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/cordz_functions @@ -34,18 +34,18 @@ PEERDIR( contrib/restricted/abseil-cpp-tstring/y_absl/time contrib/restricted/abseil-cpp-tstring/y_absl/time/civil_time contrib/restricted/abseil-cpp-tstring/y_absl/time/time_zone -) - -ADDINCL( - GLOBAL contrib/restricted/abseil-cpp-tstring -) - -NO_COMPILER_WARNINGS() - +) + +ADDINCL( + GLOBAL contrib/restricted/abseil-cpp-tstring +) + +NO_COMPILER_WARNINGS() + SRCDIR(contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal) - -SRCS( + +SRCS( cordz_info.cc -) - -END() +) + +END() diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/numbers_test_common.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/numbers_test_common.h index 12aec3ac11..022633ac82 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/numbers_test_common.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/numbers_test_common.h @@ -170,7 +170,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() { {"0x1234", true, 16, 0x1234}, - // Base-10 string version. + // Base-10 string version. {"1234", true, 0, 1234}, {nullptr, false, 0, 0}, }}; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/.yandex_meta/licenses.list.txt b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/.yandex_meta/licenses.list.txt index 33d60b3d2b..95cff783b8 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/.yandex_meta/licenses.list.txt +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/.yandex_meta/licenses.list.txt @@ -1,20 +1,20 @@ -====================Apache-2.0==================== -// 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. - - -====================COPYRIGHT==================== -// Copyright 2017 The Abseil Authors. - - -====================COPYRIGHT==================== -// Copyright 2020 The Abseil Authors. +====================Apache-2.0==================== +// 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. + + +====================COPYRIGHT==================== +// Copyright 2017 The Abseil Authors. + + +====================COPYRIGHT==================== +// Copyright 2020 The Abseil Authors. 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 8d5c3b61ac..dbd80e7052 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 @@ -1,18 +1,18 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. // +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// // POSIX spec: // http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html // @@ -26,7 +26,7 @@ #include "y_absl/base/port.h" #include "y_absl/strings/internal/str_format/float_conversion.h" -#include "y_absl/strings/numbers.h" +#include "y_absl/strings/numbers.h" namespace y_absl { ABSL_NAMESPACE_BEGIN @@ -61,179 +61,179 @@ struct IsSigned<y_absl::int128> : std::true_type {}; template <> struct IsSigned<y_absl::uint128> : std::false_type {}; -// Integral digit printer. -// Call one of the PrintAs* routines after construction once. -// Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results. -class IntDigits { +// Integral digit printer. +// Call one of the PrintAs* routines after construction once. +// Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results. +class IntDigits { public: - // Print the unsigned integer as octal. - // Supports unsigned integral types and uint128. + // Print the unsigned integer as octal. + // Supports unsigned integral types and uint128. template <typename T> - void PrintAsOct(T v) { - static_assert(!IsSigned<T>::value, ""); - char *p = storage_ + sizeof(storage_); - do { - *--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7)); - v >>= 3; - } while (v); - start_ = p; - size_ = storage_ + sizeof(storage_) - p; - } - - // Print the signed or unsigned integer as decimal. - // Supports all integral types. - template <typename T> - void PrintAsDec(T v) { - static_assert(std::is_integral<T>::value, ""); - start_ = storage_; - size_ = numbers_internal::FastIntToBuffer(v, storage_) - storage_; - } - - void PrintAsDec(int128 v) { - auto u = static_cast<uint128>(v); - bool add_neg = false; - if (v < 0) { - add_neg = true; - u = uint128{} - u; + void PrintAsOct(T v) { + static_assert(!IsSigned<T>::value, ""); + char *p = storage_ + sizeof(storage_); + do { + *--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7)); + v >>= 3; + } while (v); + start_ = p; + size_ = storage_ + sizeof(storage_) - p; + } + + // Print the signed or unsigned integer as decimal. + // Supports all integral types. + template <typename T> + void PrintAsDec(T v) { + static_assert(std::is_integral<T>::value, ""); + start_ = storage_; + size_ = numbers_internal::FastIntToBuffer(v, storage_) - storage_; + } + + void PrintAsDec(int128 v) { + auto u = static_cast<uint128>(v); + bool add_neg = false; + if (v < 0) { + add_neg = true; + u = uint128{} - u; } - PrintAsDec(u, add_neg); + PrintAsDec(u, add_neg); } - void PrintAsDec(uint128 v, bool add_neg = false) { - // This function can be sped up if needed. We can call FastIntToBuffer - // twice, or fix FastIntToBuffer to support uint128. - char *p = storage_ + sizeof(storage_); - do { - p -= 2; - numbers_internal::PutTwoDigits(static_cast<size_t>(v % 100), p); - v /= 100; - } while (v); - if (p[0] == '0') { - // We printed one too many hexits. - ++p; - } - if (add_neg) { - *--p = '-'; - } - size_ = storage_ + sizeof(storage_) - p; - start_ = p; + void PrintAsDec(uint128 v, bool add_neg = false) { + // This function can be sped up if needed. We can call FastIntToBuffer + // twice, or fix FastIntToBuffer to support uint128. + char *p = storage_ + sizeof(storage_); + do { + p -= 2; + numbers_internal::PutTwoDigits(static_cast<size_t>(v % 100), p); + v /= 100; + } while (v); + if (p[0] == '0') { + // We printed one too many hexits. + ++p; + } + if (add_neg) { + *--p = '-'; + } + size_ = storage_ + sizeof(storage_) - p; + start_ = p; } - // Print the unsigned integer as hex using lowercase. - // Supports unsigned integral types and uint128. + // Print the unsigned integer as hex using lowercase. + // Supports unsigned integral types and uint128. template <typename T> - void PrintAsHexLower(T v) { - static_assert(!IsSigned<T>::value, ""); - char *p = storage_ + sizeof(storage_); - - do { - p -= 2; - constexpr const char* table = numbers_internal::kHexTable; - std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2); - if (sizeof(T) == 1) break; - v >>= 8; - } while (v); - if (p[0] == '0') { - // We printed one too many digits. - ++p; + void PrintAsHexLower(T v) { + static_assert(!IsSigned<T>::value, ""); + char *p = storage_ + sizeof(storage_); + + do { + p -= 2; + constexpr const char* table = numbers_internal::kHexTable; + std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2); + if (sizeof(T) == 1) break; + v >>= 8; + } while (v); + if (p[0] == '0') { + // We printed one too many digits. + ++p; } - start_ = p; - size_ = storage_ + sizeof(storage_) - p; - } + start_ = p; + size_ = storage_ + sizeof(storage_) - p; + } - // Print the unsigned integer as hex using uppercase. - // Supports unsigned integral types and uint128. + // Print the unsigned integer as hex using uppercase. + // Supports unsigned integral types and uint128. template <typename T> - void PrintAsHexUpper(T v) { - static_assert(!IsSigned<T>::value, ""); - char *p = storage_ + sizeof(storage_); - - // kHexTable is only lowercase, so do it manually for uppercase. - do { - *--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15]; - v >>= 4; - } while (v); - start_ = p; - size_ = storage_ + sizeof(storage_) - p; + void PrintAsHexUpper(T v) { + static_assert(!IsSigned<T>::value, ""); + char *p = storage_ + sizeof(storage_); + + // kHexTable is only lowercase, so do it manually for uppercase. + do { + *--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15]; + v >>= 4; + } while (v); + start_ = p; + size_ = storage_ + sizeof(storage_) - p; } - // The printed value including the '-' sign if available. - // For inputs of value `0`, this will return "0" - string_view with_neg_and_zero() const { return {start_, size_}; } - - // The printed value not including the '-' sign. - // For inputs of value `0`, this will return "". - string_view without_neg_or_zero() const { - static_assert('-' < '0', "The check below verifies both."); - size_t advance = start_[0] <= '0' ? 1 : 0; - return {start_ + advance, size_ - advance}; + // The printed value including the '-' sign if available. + // For inputs of value `0`, this will return "0" + string_view with_neg_and_zero() const { return {start_, size_}; } + + // The printed value not including the '-' sign. + // For inputs of value `0`, this will return "". + string_view without_neg_or_zero() const { + static_assert('-' < '0', "The check below verifies both."); + size_t advance = start_[0] <= '0' ? 1 : 0; + return {start_ + advance, size_ - advance}; } - bool is_negative() const { return start_[0] == '-'; } + bool is_negative() const { return start_[0] == '-'; } - private: - const char *start_; - size_t size_; - // Max size: 128 bit value as octal -> 43 digits, plus sign char - char storage_[128 / 3 + 1 + 1]; + private: + const char *start_; + size_t size_; + // Max size: 128 bit value as octal -> 43 digits, plus sign char + char storage_[128 / 3 + 1 + 1]; }; // Note: 'o' conversions do not have a base indicator, it's just that // the '#' flag is specified to modify the precision for 'o' conversions. -string_view BaseIndicator(const IntDigits &as_digits, - const FormatConversionSpecImpl conv) { - // always show 0x for %p. - bool alt = conv.has_alt_flag() || - conv.conversion_char() == FormatConversionCharInternal::p; - bool hex = (conv.conversion_char() == FormatConversionCharInternal::x || - conv.conversion_char() == FormatConversionCharInternal::X || - conv.conversion_char() == FormatConversionCharInternal::p); +string_view BaseIndicator(const IntDigits &as_digits, + const FormatConversionSpecImpl conv) { + // always show 0x for %p. + bool alt = conv.has_alt_flag() || + conv.conversion_char() == FormatConversionCharInternal::p; + bool hex = (conv.conversion_char() == FormatConversionCharInternal::x || + conv.conversion_char() == FormatConversionCharInternal::X || + conv.conversion_char() == FormatConversionCharInternal::p); // From the POSIX description of '#' flag: // "For x or X conversion specifiers, a non-zero result shall have // 0x (or 0X) prefixed to it." - if (alt && hex && !as_digits.without_neg_or_zero().empty()) { - return conv.conversion_char() == FormatConversionCharInternal::X ? "0X" - : "0x"; + if (alt && hex && !as_digits.without_neg_or_zero().empty()) { + return conv.conversion_char() == FormatConversionCharInternal::X ? "0X" + : "0x"; } return {}; } -string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) { - if (conv.conversion_char() == FormatConversionCharInternal::d || - conv.conversion_char() == FormatConversionCharInternal::i) { +string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) { + if (conv.conversion_char() == FormatConversionCharInternal::d || + conv.conversion_char() == FormatConversionCharInternal::i) { if (neg) return "-"; - if (conv.has_show_pos_flag()) return "+"; - if (conv.has_sign_col_flag()) return " "; + if (conv.has_show_pos_flag()) return "+"; + if (conv.has_sign_col_flag()) return " "; } return {}; } -bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv, +bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { size_t fill = 0; if (conv.width() >= 0) fill = conv.width(); ReducePadding(1, &fill); - if (!conv.has_left_flag()) sink->Append(fill, ' '); + if (!conv.has_left_flag()) sink->Append(fill, ' '); sink->Append(1, v); - if (conv.has_left_flag()) sink->Append(fill, ' '); + if (conv.has_left_flag()) sink->Append(fill, ' '); return true; } -bool ConvertIntImplInnerSlow(const IntDigits &as_digits, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +bool ConvertIntImplInnerSlow(const IntDigits &as_digits, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { // 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(); - string_view formatted = as_digits.without_neg_or_zero(); + string_view formatted = as_digits.without_neg_or_zero(); ReducePadding(formatted, &fill); - string_view sign = SignColumn(as_digits.is_negative(), conv); + string_view sign = SignColumn(as_digits.is_negative(), conv); ReducePadding(sign, &fill); - string_view base_indicator = BaseIndicator(as_digits, conv); + string_view base_indicator = BaseIndicator(as_digits, conv); ReducePadding(base_indicator, &fill); int precision = conv.precision(); @@ -241,8 +241,8 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits, if (!precision_specified) precision = 1; - if (conv.has_alt_flag() && - conv.conversion_char() == FormatConversionCharInternal::o) { + if (conv.has_alt_flag() && + conv.conversion_char() == FormatConversionCharInternal::o) { // From POSIX description of the '#' (alt) flag: // "For o conversion, it increases the precision (if necessary) to // force the first digit of the result to be zero." @@ -255,13 +255,13 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits, size_t num_zeroes = Excess(formatted.size(), precision); ReducePadding(num_zeroes, &fill); - size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; - size_t num_right_spaces = conv.has_left_flag() ? fill : 0; + size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; + size_t num_right_spaces = conv.has_left_flag() ? fill : 0; // From POSIX description of the '0' (zero) flag: // "For d, i, o, u, x, and X conversion specifiers, if a precision // is specified, the '0' flag is ignored." - if (!precision_specified && conv.has_zero_flag()) { + if (!precision_specified && conv.has_zero_flag()) { num_zeroes += num_left_spaces; num_left_spaces = 0; } @@ -276,97 +276,97 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits, } template <typename T> -bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - using U = typename MakeUnsigned<T>::type; - IntDigits as_digits; - - // 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); - - case static_cast<uint8_t>(FormatConversionCharInternal::o): - as_digits.PrintAsOct(static_cast<U>(v)); - break; - - case static_cast<uint8_t>(FormatConversionCharInternal::x): - as_digits.PrintAsHexLower(static_cast<U>(v)); - break; - case static_cast<uint8_t>(FormatConversionCharInternal::X): - as_digits.PrintAsHexUpper(static_cast<U>(v)); - break; - - case static_cast<uint8_t>(FormatConversionCharInternal::u): - as_digits.PrintAsDec(static_cast<U>(v)); - break; - - case static_cast<uint8_t>(FormatConversionCharInternal::d): - case static_cast<uint8_t>(FormatConversionCharInternal::i): - as_digits.PrintAsDec(v); - break; - - case static_cast<uint8_t>(FormatConversionCharInternal::a): - case static_cast<uint8_t>(FormatConversionCharInternal::e): - case static_cast<uint8_t>(FormatConversionCharInternal::f): - case static_cast<uint8_t>(FormatConversionCharInternal::g): - case static_cast<uint8_t>(FormatConversionCharInternal::A): - case static_cast<uint8_t>(FormatConversionCharInternal::E): - case static_cast<uint8_t>(FormatConversionCharInternal::F): - case static_cast<uint8_t>(FormatConversionCharInternal::G): - return ConvertFloatImpl(static_cast<double>(v), conv, sink); - - default: - ABSL_INTERNAL_ASSUME(false); +bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + using U = typename MakeUnsigned<T>::type; + IntDigits as_digits; + + // 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); + + case static_cast<uint8_t>(FormatConversionCharInternal::o): + as_digits.PrintAsOct(static_cast<U>(v)); + break; + + case static_cast<uint8_t>(FormatConversionCharInternal::x): + as_digits.PrintAsHexLower(static_cast<U>(v)); + break; + case static_cast<uint8_t>(FormatConversionCharInternal::X): + as_digits.PrintAsHexUpper(static_cast<U>(v)); + break; + + case static_cast<uint8_t>(FormatConversionCharInternal::u): + as_digits.PrintAsDec(static_cast<U>(v)); + break; + + case static_cast<uint8_t>(FormatConversionCharInternal::d): + case static_cast<uint8_t>(FormatConversionCharInternal::i): + as_digits.PrintAsDec(v); + break; + + case static_cast<uint8_t>(FormatConversionCharInternal::a): + case static_cast<uint8_t>(FormatConversionCharInternal::e): + case static_cast<uint8_t>(FormatConversionCharInternal::f): + case static_cast<uint8_t>(FormatConversionCharInternal::g): + case static_cast<uint8_t>(FormatConversionCharInternal::A): + case static_cast<uint8_t>(FormatConversionCharInternal::E): + case static_cast<uint8_t>(FormatConversionCharInternal::F): + case static_cast<uint8_t>(FormatConversionCharInternal::G): + return ConvertFloatImpl(static_cast<double>(v), conv, sink); + + default: + ABSL_INTERNAL_ASSUME(false); } - if (conv.is_basic()) { - sink->Append(as_digits.with_neg_and_zero()); - return true; + if (conv.is_basic()) { + sink->Append(as_digits.with_neg_and_zero()); + return true; } - return ConvertIntImplInnerSlow(as_digits, conv, sink); + return ConvertIntImplInnerSlow(as_digits, conv, sink); } template <typename T> -bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return FormatConversionCharIsFloat(conv.conversion_char()) && +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, +inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { - if (conv.is_basic()) { + if (conv.is_basic()) { sink->Append(v); return true; } return sink->PutPaddedString(v, conv.width(), conv.precision(), - conv.has_left_flag()); + conv.has_left_flag()); } } // namespace // ==================== Strings ==================== -StringConvertResult FormatConvertImpl(const TString &v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +StringConvertResult FormatConvertImpl(const TString &v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } -StringConvertResult FormatConvertImpl(string_view v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +StringConvertResult FormatConvertImpl(string_view v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } -ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> -FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - if (conv.conversion_char() == FormatConversionCharInternal::p) +ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> +FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + if (conv.conversion_char() == FormatConversionCharInternal::p) return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; size_t len; if (v == nullptr) { @@ -381,99 +381,99 @@ FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, } // ==================== Raw pointers ==================== -ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( - VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { +ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( + VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { if (!v.value) { sink->Append("(nil)"); return {true}; } - IntDigits as_digits; - as_digits.PrintAsHexLower(v.value); - return {ConvertIntImplInnerSlow(as_digits, conv, sink)}; + IntDigits as_digits; + as_digits.PrintAsHexLower(v.value); + return {ConvertIntImplInnerSlow(as_digits, conv, sink)}; } // ==================== Floats ==================== -FloatingConvertResult FormatConvertImpl(float v, - const FormatConversionSpecImpl conv, +FloatingConvertResult FormatConvertImpl(float v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } -FloatingConvertResult FormatConvertImpl(double v, - const FormatConversionSpecImpl conv, +FloatingConvertResult FormatConvertImpl(double v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } FloatingConvertResult FormatConvertImpl(long double v, - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } // ==================== Chars ==================== -IntegralConvertResult FormatConvertImpl(char v, - const FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(char v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(signed char v, - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned char v, - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } // ==================== Ints ==================== IntegralConvertResult FormatConvertImpl(short v, // NOLINT - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } -IntegralConvertResult FormatConvertImpl(int v, - const FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(int v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } -IntegralConvertResult FormatConvertImpl(unsigned v, - const FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(unsigned v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(long v, // NOLINT - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(long long v, // NOLINT - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(y_absl::int128 v, - const FormatConversionSpecImpl conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(y_absl::uint128 v, - const FormatConversionSpecImpl conv, + 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 59b7bcc727..fee261cbe3 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 @@ -1,17 +1,17 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ @@ -24,7 +24,7 @@ #include <memory> #include <sstream> #include <util/generic/string.h> -#include <util/stream/str.h> +#include <util/stream/str.h> #include <type_traits> #include "y_absl/base/port.h" @@ -40,52 +40,52 @@ class Cord; class FormatCountCapture; class FormatSink; -template <y_absl::FormatConversionCharSet C> -struct FormatConvertResult; -class FormatConversionSpec; - +template <y_absl::FormatConversionCharSet C> +struct FormatConvertResult; +class FormatConversionSpec; + namespace str_format_internal { template <typename T, typename = void> struct HasUserDefinedConvert : std::false_type {}; template <typename T> -struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert( - std::declval<const T&>(), - std::declval<const FormatConversionSpec&>(), - std::declval<FormatSink*>()))>> - : std::true_type {}; - -void AbslFormatConvert(); // Stops the lexical name lookup -template <typename T> -auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink) - -> decltype(AbslFormatConvert(v, - std::declval<const FormatConversionSpec&>(), - std::declval<FormatSink*>())) { - using FormatConversionSpecT = - y_absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>; - using FormatSinkT = - y_absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; - auto fcs = conv.Wrap<FormatConversionSpecT>(); - auto fs = sink->Wrap<FormatSinkT>(); - return AbslFormatConvert(v, fcs, &fs); -} +struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert( + std::declval<const T&>(), + std::declval<const FormatConversionSpec&>(), + std::declval<FormatSink*>()))>> + : std::true_type {}; +void AbslFormatConvert(); // Stops the lexical name lookup template <typename T> +auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) + -> decltype(AbslFormatConvert(v, + std::declval<const FormatConversionSpec&>(), + std::declval<FormatSink*>())) { + using FormatConversionSpecT = + y_absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>; + using FormatSinkT = + y_absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + auto fcs = conv.Wrap<FormatConversionSpecT>(); + auto fs = sink->Wrap<FormatSinkT>(); + return AbslFormatConvert(v, fcs, &fs); +} + +template <typename T> class StreamedWrapper; // If 'v' can be converted (in the printf sense) according to 'conv', // then convert it, appending to `sink` and return `true`. // Otherwise fail and return `false`. -// AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v' -// as an extension mechanism. These FormatConvertImpl functions are the default -// implementations. -// The ADL search is augmented via the 'Sink*' parameter, which also -// serves as a disambiguator to reject possible unintended 'AbslFormatConvert' -// functions in the namespaces associated with 'v'. - +// AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v' +// as an extension mechanism. These FormatConvertImpl functions are the default +// implementations. +// The ADL search is augmented via the 'Sink*' parameter, which also +// serves as a disambiguator to reject possible unintended 'AbslFormatConvert' +// functions in the namespaces associated with 'v'. + // Raw pointers. struct VoidPtr { VoidPtr() = default; @@ -96,33 +96,33 @@ struct VoidPtr { uintptr_t value; }; -template <FormatConversionCharSet C> -struct ArgConvertResult { - bool value; -}; - -template <FormatConversionCharSet C> -constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) { - return C; -} - -template <FormatConversionCharSet C> -constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) { - return C; -} - -using StringConvertResult = - ArgConvertResult<FormatConversionCharSetInternal::s>; -ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( - VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); - +template <FormatConversionCharSet C> +struct ArgConvertResult { + bool value; +}; + +template <FormatConversionCharSet C> +constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) { + return C; +} + +template <FormatConversionCharSet C> +constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) { + return C; +} + +using StringConvertResult = + ArgConvertResult<FormatConversionCharSetInternal::s>; +ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( + VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); + // Strings. -StringConvertResult FormatConvertImpl(const TString& v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -StringConvertResult FormatConvertImpl(string_view v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); +StringConvertResult FormatConvertImpl(const TString& v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +StringConvertResult FormatConvertImpl(string_view v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); #if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW) inline StringConvertResult FormatConvertImpl(std::string_view v, FormatConversionSpecImpl conv, @@ -131,17 +131,17 @@ inline StringConvertResult FormatConvertImpl(std::string_view v, } #endif // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW -ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> -FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, - FormatSinkImpl* sink); - -template <class AbslCord, typename std::enable_if<std::is_same< - AbslCord, y_absl::Cord>::value>::type* = nullptr> -StringConvertResult FormatConvertImpl(const AbslCord& value, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink) { - bool is_left = conv.has_left_flag(); +ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> +FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, + FormatSinkImpl* sink); + +template <class AbslCord, typename std::enable_if<std::is_same< + AbslCord, y_absl::Cord>::value>::type* = nullptr> +StringConvertResult FormatConvertImpl(const AbslCord& value, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + bool is_left = conv.has_left_flag(); size_t space_remaining = 0; int width = conv.width(); @@ -174,63 +174,63 @@ StringConvertResult FormatConvertImpl(const AbslCord& value, return {true}; } -using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::c, - FormatConversionCharSetInternal::kNumeric, - FormatConversionCharSetInternal::kStar)>; -using FloatingConvertResult = - ArgConvertResult<FormatConversionCharSetInternal::kFloating>; +using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::c, + FormatConversionCharSetInternal::kNumeric, + FormatConversionCharSetInternal::kStar)>; +using FloatingConvertResult = + ArgConvertResult<FormatConversionCharSetInternal::kFloating>; // Floats. -FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, +FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -FloatingConvertResult FormatConvertImpl(double v, FormatConversionSpecImpl conv, +FloatingConvertResult FormatConvertImpl(double v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -FloatingConvertResult FormatConvertImpl(long double v, - FormatConversionSpecImpl conv, +FloatingConvertResult FormatConvertImpl(long double v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Chars. -IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(signed char v, - FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(signed char v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned char v, - FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(unsigned char v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Ints. IntegralConvertResult FormatConvertImpl(short v, // NOLINT - FormatConversionSpecImpl conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT - FormatConversionSpecImpl conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(int v, FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(int v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned v, - FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(unsigned v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(long v, // NOLINT - FormatConversionSpecImpl conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT - FormatConversionSpecImpl conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(long long v, // NOLINT - FormatConversionSpecImpl conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT - FormatConversionSpecImpl conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(uint128 v, - FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(uint128 v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0> -IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, +IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { return FormatConvertImpl(static_cast<int>(v), conv, sink); } @@ -241,41 +241,41 @@ template <typename T> typename std::enable_if<std::is_enum<T>::value && !HasUserDefinedConvert<T>::value, IntegralConvertResult>::type -FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); +FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); template <typename T> -StringConvertResult FormatConvertImpl(const StreamedWrapper<T>& v, - FormatConversionSpecImpl conv, - FormatSinkImpl* out) { - TString buf; - TStringOutput oss(buf); +StringConvertResult FormatConvertImpl(const StreamedWrapper<T>& v, + FormatConversionSpecImpl conv, + FormatSinkImpl* out) { + TString buf; + TStringOutput oss(buf); oss << v.v_; - if (!buf) return {false}; - return str_format_internal::FormatConvertImpl(buf, conv, out); + if (!buf) return {false}; + return str_format_internal::FormatConvertImpl(buf, conv, out); } // Use templates and dependent types to delay evaluation of the function // until after FormatCountCapture is fully defined. struct FormatCountCaptureHelper { template <class T = int> - static ArgConvertResult<FormatConversionCharSetInternal::n> ConvertHelper( - const FormatCountCapture& v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink) { + static ArgConvertResult<FormatConversionCharSetInternal::n> ConvertHelper( + const FormatCountCapture& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { const y_absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; - if (conv.conversion_char() != - str_format_internal::FormatConversionCharInternal::n) { - return {false}; - } + if (conv.conversion_char() != + str_format_internal::FormatConversionCharInternal::n) { + return {false}; + } *v2.p_ = static_cast<int>(sink->size()); return {true}; } }; template <class T = int> -ArgConvertResult<FormatConversionCharSetInternal::n> FormatConvertImpl( - const FormatCountCapture& v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink) { +ArgConvertResult<FormatConversionCharSetInternal::n> FormatConvertImpl( + const FormatCountCapture& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { return FormatCountCaptureHelper::ConvertHelper(v, conv, sink); } @@ -284,13 +284,13 @@ ArgConvertResult<FormatConversionCharSetInternal::n> FormatConvertImpl( struct FormatArgImplFriend { template <typename Arg> static bool ToInt(Arg arg, int* out) { - // A value initialized FormatConversionSpecImpl has a `none` conv, which - // tells the dispatcher to run the `int` conversion. + // A value initialized FormatConversionSpecImpl has a `none` conv, which + // tells the dispatcher to run the `int` conversion. return arg.dispatcher_(arg.data_, {}, out); } template <typename Arg> - static bool Convert(Arg arg, FormatConversionSpecImpl conv, + static bool Convert(Arg arg, FormatConversionSpecImpl conv, FormatSinkImpl* out) { return arg.dispatcher_(arg.data_, conv, out); } @@ -301,15 +301,15 @@ 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*>())){}); -} - +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*>())){}); +} + // A type-erased handle to a format argument. class FormatArgImpl { private: @@ -323,7 +323,7 @@ class FormatArgImpl { char buf[kInlinedSpace]; }; - using Dispatcher = bool (*)(Data, FormatConversionSpecImpl, void* out); + using Dispatcher = bool (*)(Data, FormatConversionSpecImpl, void* out); template <typename T> struct store_by_value @@ -465,20 +465,20 @@ class FormatArgImpl { } template <typename T> - static bool Dispatch(Data arg, FormatConversionSpecImpl spec, void* out) { + static bool Dispatch(Data arg, FormatConversionSpecImpl spec, void* out) { // A `none` conv indicates that we want the `int` conversion. - if (ABSL_PREDICT_FALSE(spec.conversion_char() == - FormatConversionCharInternal::kNone)) { + if (ABSL_PREDICT_FALSE(spec.conversion_char() == + FormatConversionCharInternal::kNone)) { return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), std::is_enum<T>()); } - if (ABSL_PREDICT_FALSE(!Contains(ArgumentToConv<T>(), - spec.conversion_char()))) { - return false; - } + if (ABSL_PREDICT_FALSE(!Contains(ArgumentToConv<T>(), + spec.conversion_char()))) { + return false; + } return str_format_internal::FormatConvertImpl( - Manager<T>::Value(arg), spec, - static_cast<FormatSinkImpl*>(out)) + Manager<T>::Value(arg), spec, + static_cast<FormatSinkImpl*>(out)) .value; } @@ -486,9 +486,9 @@ class FormatArgImpl { Dispatcher dispatcher_; }; -#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ - E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \ - void*) +#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ + E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \ + void*) #define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \ 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 211ce25dea..5a62088bff 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 @@ -1,17 +1,17 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "y_absl/strings/internal/str_format/bind.h" #include <cerrno> @@ -160,7 +160,7 @@ class SummarizingConverter { << FormatConversionSpecImplFriend::FlagsToString(bound); if (bound.width() >= 0) ss << bound.width(); if (bound.precision() >= 0) ss << "." << bound.precision(); - ss << bound.conversion_char() << "}"; + ss << bound.conversion_char() << "}"; Append(ss.str()); return true; } @@ -234,7 +234,7 @@ int FprintF(std::FILE* output, const UntypedFormatSpecImpl format, errno = sink.error(); return -1; } - if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) { + if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) { errno = EFBIG; return -1; } 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 3966610710..f0e73a6db2 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 @@ -1,17 +1,17 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ @@ -33,7 +33,7 @@ class UntypedFormatSpec; namespace str_format_internal { -class BoundConversion : public FormatConversionSpecImpl { +class BoundConversion : public FormatConversionSpecImpl { public: const FormatArgImpl* arg() const { return arg_; } void set_arg(const FormatArgImpl* a) { arg_ = a; } @@ -74,7 +74,7 @@ class UntypedFormatSpecImpl { size_t size_; }; -template <typename T, FormatConversionCharSet...> +template <typename T, FormatConversionCharSet...> struct MakeDependent { using type = T; }; @@ -82,7 +82,7 @@ struct MakeDependent { // Implicitly convertible from `const char*`, `string_view`, and the // `ExtendedParsedFormat` type. This abstraction allows all format functions to // operate on any without providing too many overloads. -template <FormatConversionCharSet... Args> +template <FormatConversionCharSet... Args> class FormatSpecTemplate : public MakeDependent<UntypedFormatSpec, Args...>::type { using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type; @@ -90,11 +90,11 @@ class FormatSpecTemplate public: #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - // Honeypot overload for when the string is not constexpr. + // Honeypot overload for when the string is not constexpr. // We use the 'unavailable' attribute to give a better compiler error than // just 'method is deleted'. FormatSpecTemplate(...) // NOLINT - __attribute__((unavailable("Format string is not constexpr."))); + __attribute__((unavailable("Format string is not constexpr."))); // Honeypot overload for when the format is constexpr and invalid. // We use the 'unavailable' attribute to give a better compiler error than @@ -119,11 +119,11 @@ class FormatSpecTemplate // Good format overload. FormatSpecTemplate(const char* s) // NOLINT - __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) + __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) : Base(s) {} FormatSpecTemplate(string_view s) // NOLINT - __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) + __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) : Base(s) {} #else // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER @@ -133,11 +133,11 @@ class FormatSpecTemplate #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - template < - FormatConversionCharSet... C, - typename = typename std::enable_if<sizeof...(C) == sizeof...(Args)>::type, - typename = typename std::enable_if<AllOf(Contains(Args, - C)...)>::type> + template < + FormatConversionCharSet... C, + typename = typename std::enable_if<sizeof...(C) == sizeof...(Args)>::type, + typename = typename std::enable_if<AllOf(Contains(Args, + C)...)>::type> FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT : Base(&pc) {} }; @@ -204,9 +204,9 @@ class StreamedWrapper { private: template <typename S> - friend ArgConvertResult<FormatConversionCharSetInternal::s> FormatConvertImpl( - const StreamedWrapper<S>& v, FormatConversionSpecImpl conv, - FormatSinkImpl* out); + friend ArgConvertResult<FormatConversionCharSetInternal::s> 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 7c530d2507..ba3b3d02f3 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 @@ -1,17 +1,17 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ @@ -46,14 +46,14 @@ constexpr bool ContainsChar(const char* chars, char c) { // A constexpr compatible list of Convs. struct ConvList { - const FormatConversionCharSet* array; + 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{}; + // 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 { @@ -64,7 +64,7 @@ struct ConvList { template <size_t count> struct ConvListT { // Make sure the array has size > 0. - FormatConversionCharSet list[count ? count : 1]; + FormatConversionCharSet list[count ? count : 1]; }; constexpr char GetChar(string_view str, size_t index) { @@ -317,7 +317,7 @@ class FormatParser { ConvList args_; }; -template <FormatConversionCharSet... C> +template <FormatConversionCharSet... C> constexpr bool ValidFormatImpl(string_view format) { return FormatParser(format, {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)}) 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 f2a4169ae7..13c452cb8e 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 @@ -33,40 +33,40 @@ TString FlagsToString(Flags v) { return s; } -#define ABSL_INTERNAL_X_VAL(id) \ - constexpr y_absl::FormatConversionChar FormatConversionCharInternal::id; -ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) -#undef ABSL_INTERNAL_X_VAL -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr y_absl::FormatConversionChar FormatConversionCharInternal::kNone; - -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - constexpr FormatConversionCharSet FormatConversionCharSetInternal::c; -ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) -#undef ABSL_INTERNAL_CHAR_SET_CASE - -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar; -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral; -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating; -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric; -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer; - -bool FormatSinkImpl::PutPaddedString(string_view value, int width, - int precision, bool left) { +#define ABSL_INTERNAL_X_VAL(id) \ + constexpr y_absl::FormatConversionChar FormatConversionCharInternal::id; +ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) +#undef ABSL_INTERNAL_X_VAL +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr y_absl::FormatConversionChar FormatConversionCharInternal::kNone; + +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + constexpr FormatConversionCharSet FormatConversionCharSetInternal::c; +ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) +#undef ABSL_INTERNAL_CHAR_SET_CASE + +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric; +// NOLINTNEXTLINE(readability-redundant-declaration) +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; - size_t n = value.size(); - if (precision >= 0) n = std::min(n, static_cast<size_t>(precision)); - string_view shown(value.data(), n); + if (width >= 0) space_remaining = width; + size_t n = value.size(); + if (precision >= 0) n = std::min(n, static_cast<size_t>(precision)); + string_view shown(value.data(), n); space_remaining = Excess(shown.size(), space_remaining); - if (!left) Append(space_remaining, ' '); + if (!left) Append(space_remaining, ' '); Append(shown); - if (left) Append(space_remaining, ' '); + if (left) Append(space_remaining, ' '); return true; } 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 e5de5cb6a1..1548d56578 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 @@ -24,16 +24,16 @@ #include "y_absl/base/config.h" #include "y_absl/base/port.h" -#include "y_absl/meta/type_traits.h" +#include "y_absl/meta/type_traits.h" #include "y_absl/strings/internal/str_format/output.h" #include "y_absl/strings/string_view.h" namespace y_absl { ABSL_NAMESPACE_BEGIN - -enum class FormatConversionChar : uint8_t; -enum class FormatConversionCharSet : uint64_t; - + +enum class FormatConversionChar : uint8_t; +enum class FormatConversionCharSet : uint64_t; + namespace str_format_internal { class FormatRawSinkImpl { @@ -107,7 +107,7 @@ class FormatSinkImpl { size_t size() const { return size_; } // Put 'v' to 'sink' with specified width, precision, and left flag. - bool PutPaddedString(string_view v, int width, int precision, bool left); + bool PutPaddedString(string_view v, int width, int precision, bool left); template <typename T> T Wrap() { @@ -158,7 +158,7 @@ inline std::ostream& operator<<(std::ostream& os, Flags v) { // clang-format off #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ /* text */ \ - X_VAL(c) X_SEP X_VAL(s) X_SEP \ + X_VAL(c) X_SEP X_VAL(s) X_SEP \ /* ints */ \ X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \ X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \ @@ -167,39 +167,39 @@ inline std::ostream& operator<<(std::ostream& os, Flags v) { X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \ /* misc */ \ X_VAL(n) X_SEP X_VAL(p) -// clang-format on - -// This type should not be referenced, it exists only to provide labels -// internally that match the values declared in FormatConversionChar in -// str_format.h. This is meant to allow internal libraries to use the same -// declared interface type as the public interface -// (y_absl::StrFormatConversionChar) while keeping the definition in a public -// header. -// Internal libraries should use the form -// `FormatConversionCharInternal::c`, `FormatConversionCharInternal::kNone` for -// comparisons. Use in switch statements is not recommended due to a bug in how -// gcc 4.9 -Wswitch handles declared but undefined enums. -struct FormatConversionCharInternal { - FormatConversionCharInternal() = delete; - - private: - // clang-format off - enum class Enum : uint8_t { - c, s, // text +// clang-format on + +// This type should not be referenced, it exists only to provide labels +// internally that match the values declared in FormatConversionChar in +// str_format.h. This is meant to allow internal libraries to use the same +// declared interface type as the public interface +// (y_absl::StrFormatConversionChar) while keeping the definition in a public +// header. +// Internal libraries should use the form +// `FormatConversionCharInternal::c`, `FormatConversionCharInternal::kNone` for +// comparisons. Use in switch statements is not recommended due to a bug in how +// gcc 4.9 -Wswitch handles declared but undefined enums. +struct FormatConversionCharInternal { + FormatConversionCharInternal() = delete; + + private: + // clang-format off + enum class Enum : uint8_t { + c, s, // text d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float n, p, // misc - kNone - }; - // clang-format on - public: -#define ABSL_INTERNAL_X_VAL(id) \ - static constexpr FormatConversionChar id = \ - static_cast<FormatConversionChar>(Enum::id); - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) -#undef ABSL_INTERNAL_X_VAL - static constexpr FormatConversionChar kNone = - static_cast<FormatConversionChar>(Enum::kNone); + kNone + }; + // clang-format on + public: +#define ABSL_INTERNAL_X_VAL(id) \ + static constexpr FormatConversionChar id = \ + static_cast<FormatConversionChar>(Enum::id); + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) +#undef ABSL_INTERNAL_X_VAL + static constexpr FormatConversionChar kNone = + static_cast<FormatConversionChar>(Enum::kNone); }; // clang-format on @@ -207,54 +207,54 @@ inline FormatConversionChar FormatConversionCharFromChar(char c) { switch (c) { #define ABSL_INTERNAL_X_VAL(id) \ case #id[0]: \ - return FormatConversionCharInternal::id; + return FormatConversionCharInternal::id; ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) #undef ABSL_INTERNAL_X_VAL } - return FormatConversionCharInternal::kNone; + return FormatConversionCharInternal::kNone; } inline bool FormatConversionCharIsUpper(FormatConversionChar c) { - if (c == FormatConversionCharInternal::X || - c == FormatConversionCharInternal::F || - c == FormatConversionCharInternal::E || - c == FormatConversionCharInternal::G || - c == FormatConversionCharInternal::A) { - return true; - } else { - return false; + if (c == FormatConversionCharInternal::X || + c == FormatConversionCharInternal::F || + c == FormatConversionCharInternal::E || + c == FormatConversionCharInternal::G || + c == FormatConversionCharInternal::A) { + return true; + } else { + return false; } } inline bool FormatConversionCharIsFloat(FormatConversionChar c) { - if (c == FormatConversionCharInternal::a || - c == FormatConversionCharInternal::e || - c == FormatConversionCharInternal::f || - c == FormatConversionCharInternal::g || - c == FormatConversionCharInternal::A || - c == FormatConversionCharInternal::E || - c == FormatConversionCharInternal::F || - c == FormatConversionCharInternal::G) { - return true; - } else { - return false; + if (c == FormatConversionCharInternal::a || + c == FormatConversionCharInternal::e || + c == FormatConversionCharInternal::f || + c == FormatConversionCharInternal::g || + c == FormatConversionCharInternal::A || + c == FormatConversionCharInternal::E || + c == FormatConversionCharInternal::F || + c == FormatConversionCharInternal::G) { + return true; + } else { + return false; } } inline char FormatConversionCharToChar(FormatConversionChar c) { - if (c == FormatConversionCharInternal::kNone) { - return '\0'; - -#define ABSL_INTERNAL_X_VAL(e) \ - } else if (c == FormatConversionCharInternal::e) { \ + if (c == FormatConversionCharInternal::kNone) { + return '\0'; + +#define ABSL_INTERNAL_X_VAL(e) \ + } else if (c == FormatConversionCharInternal::e) { \ return #e[0]; #define ABSL_INTERNAL_X_SEP - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, - ABSL_INTERNAL_X_SEP) - } else { - return '\0'; - } - + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, + ABSL_INTERNAL_X_SEP) + } else { + return '\0'; + } + #undef ABSL_INTERNAL_X_VAL #undef ABSL_INTERNAL_X_SEP } @@ -268,7 +268,7 @@ inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) { struct FormatConversionSpecImplFriend; -class FormatConversionSpecImpl { +class FormatConversionSpecImpl { public: // Width and precison are not specified, no flags are set. bool is_basic() const { return flags_ == Flags::kBasic; } @@ -285,7 +285,7 @@ class FormatConversionSpecImpl { FormatConversionChar conversion_char() const { // Keep this field first in the struct . It generates better code when // accessing it when ConversionSpec is passed by value in registers. - static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, ""); + static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, ""); return conv_; } @@ -296,65 +296,65 @@ class FormatConversionSpecImpl { // negative value. int precision() const { return precision_; } - template <typename T> - T Wrap() { - return T(*this); - } + template <typename T> + T Wrap() { + return T(*this); + } private: friend struct str_format_internal::FormatConversionSpecImplFriend; - FormatConversionChar conv_ = FormatConversionCharInternal::kNone; + FormatConversionChar conv_ = FormatConversionCharInternal::kNone; Flags flags_; int width_; int precision_; }; struct FormatConversionSpecImplFriend final { - static void SetFlags(Flags f, FormatConversionSpecImpl* conv) { + static void SetFlags(Flags f, FormatConversionSpecImpl* conv) { conv->flags_ = f; } static void SetConversionChar(FormatConversionChar c, - FormatConversionSpecImpl* conv) { + FormatConversionSpecImpl* conv) { conv->conv_ = c; } - static void SetWidth(int w, FormatConversionSpecImpl* conv) { - conv->width_ = w; - } - static void SetPrecision(int p, FormatConversionSpecImpl* conv) { + static void SetWidth(int w, FormatConversionSpecImpl* conv) { + conv->width_ = w; + } + static void SetPrecision(int p, FormatConversionSpecImpl* conv) { conv->precision_ = p; } - static TString FlagsToString(const FormatConversionSpecImpl& spec) { + static TString FlagsToString(const FormatConversionSpecImpl& spec) { return str_format_internal::FlagsToString(spec.flags_); } }; -// Type safe OR operator. -// We need this for two reasons: -// 1. operator| on enums makes them decay to integers and the result is an -// integer. We need the result to stay as an enum. -// 2. We use "enum class" which would not work even if we accepted the decay. -constexpr FormatConversionCharSet FormatConversionCharSetUnion( - FormatConversionCharSet a) { - return a; -} - -template <typename... CharSet> -constexpr FormatConversionCharSet FormatConversionCharSetUnion( - FormatConversionCharSet a, CharSet... rest) { - return static_cast<FormatConversionCharSet>( - static_cast<uint64_t>(a) | - static_cast<uint64_t>(FormatConversionCharSetUnion(rest...))); -} - -constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) { - return uint64_t{1} << (1 + static_cast<uint8_t>(c)); -} - -constexpr uint64_t FormatConversionCharToConvInt(char conv) { +// Type safe OR operator. +// We need this for two reasons: +// 1. operator| on enums makes them decay to integers and the result is an +// integer. We need the result to stay as an enum. +// 2. We use "enum class" which would not work even if we accepted the decay. +constexpr FormatConversionCharSet FormatConversionCharSetUnion( + FormatConversionCharSet a) { + return a; +} + +template <typename... CharSet> +constexpr FormatConversionCharSet FormatConversionCharSetUnion( + FormatConversionCharSet a, CharSet... rest) { + return static_cast<FormatConversionCharSet>( + static_cast<uint64_t>(a) | + static_cast<uint64_t>(FormatConversionCharSetUnion(rest...))); +} + +constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) { + return uint64_t{1} << (1 + static_cast<uint8_t>(c)); +} + +constexpr uint64_t FormatConversionCharToConvInt(char conv) { return -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - conv == #c[0] \ - ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \ +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + conv == #c[0] \ + ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \ : ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) #undef ABSL_INTERNAL_CHAR_SET_CASE @@ -363,29 +363,29 @@ constexpr uint64_t FormatConversionCharToConvInt(char conv) { : 0; } -constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) { - return static_cast<FormatConversionCharSet>( - FormatConversionCharToConvInt(conv)); -} - -struct FormatConversionCharSetInternal { -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - static constexpr FormatConversionCharSet c = \ - FormatConversionCharToConvValue(#c[0]); +constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) { + return static_cast<FormatConversionCharSet>( + FormatConversionCharToConvInt(conv)); +} + +struct FormatConversionCharSetInternal { +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + static constexpr FormatConversionCharSet c = \ + FormatConversionCharToConvValue(#c[0]); ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) #undef ABSL_INTERNAL_CHAR_SET_CASE // Used for width/precision '*' specification. - static constexpr FormatConversionCharSet kStar = - FormatConversionCharToConvValue('*'); - - static constexpr FormatConversionCharSet kIntegral = - FormatConversionCharSetUnion(d, i, u, o, x, X); - static constexpr FormatConversionCharSet kFloating = - FormatConversionCharSetUnion(a, e, f, g, A, E, F, G); - static constexpr FormatConversionCharSet kNumeric = - FormatConversionCharSetUnion(kIntegral, kFloating); - static constexpr FormatConversionCharSet kPointer = p; + static constexpr FormatConversionCharSet kStar = + FormatConversionCharToConvValue('*'); + + static constexpr FormatConversionCharSet kIntegral = + FormatConversionCharSetUnion(d, i, u, o, x, X); + static constexpr FormatConversionCharSet kFloating = + FormatConversionCharSetUnion(a, e, f, g, A, E, F, G); + static constexpr FormatConversionCharSet kNumeric = + FormatConversionCharSetUnion(kIntegral, kFloating); + static constexpr FormatConversionCharSet kPointer = p; }; // Type safe OR operator. @@ -395,29 +395,29 @@ struct FormatConversionCharSetInternal { // 2. We use "enum class" which would not work even if we accepted the decay. constexpr FormatConversionCharSet operator|(FormatConversionCharSet a, FormatConversionCharSet b) { - return FormatConversionCharSetUnion(a, b); -} - -// Overloaded conversion functions to support y_absl::ParsedFormat. -// Get a conversion with a single character in it. -constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) { - return static_cast<FormatConversionCharSet>( - FormatConversionCharToConvValue(c)); + return FormatConversionCharSetUnion(a, b); } +// Overloaded conversion functions to support y_absl::ParsedFormat. // Get a conversion with a single character in it. -constexpr FormatConversionCharSet ToFormatConversionCharSet( - FormatConversionCharSet c) { - return c; +constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) { + return static_cast<FormatConversionCharSet>( + FormatConversionCharToConvValue(c)); } -template <typename T> -void ToFormatConversionCharSet(T) = delete; - +// Get a conversion with a single character in it. +constexpr FormatConversionCharSet ToFormatConversionCharSet( + FormatConversionCharSet c) { + return c; +} + +template <typename T> +void ToFormatConversionCharSet(T) = delete; + // Checks whether `c` exists in `set`. constexpr bool Contains(FormatConversionCharSet set, char c) { - return (static_cast<uint64_t>(set) & - static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0; + return (static_cast<uint64_t>(set) & + static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0; } // Checks whether all the characters in `c` are contained in `set` @@ -427,10 +427,10 @@ constexpr bool Contains(FormatConversionCharSet set, static_cast<uint64_t>(c); } -// Checks whether all the characters in `c` are contained in `set` -constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) { - return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0; -} +// Checks whether all the characters in `c` are contained in `set` +constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) { + return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0; +} // Return capacity - used, clipped to a minimum of 0. inline size_t Excess(size_t used, size_t capacity) { 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 c49062538d..af48a71cde 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 @@ -1,38 +1,38 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "y_absl/strings/internal/str_format/float_conversion.h" #include <string.h> - + #include <algorithm> #include <cassert> #include <cmath> -#include <limits> +#include <limits> #include <util/generic/string.h> -#include "y_absl/base/attributes.h" +#include "y_absl/base/attributes.h" #include "y_absl/base/config.h" -#include "y_absl/base/optimization.h" -#include "y_absl/functional/function_ref.h" -#include "y_absl/meta/type_traits.h" -#include "y_absl/numeric/bits.h" -#include "y_absl/numeric/int128.h" -#include "y_absl/numeric/internal/representation.h" -#include "y_absl/strings/numbers.h" -#include "y_absl/types/optional.h" -#include "y_absl/types/span.h" +#include "y_absl/base/optimization.h" +#include "y_absl/functional/function_ref.h" +#include "y_absl/meta/type_traits.h" +#include "y_absl/numeric/bits.h" +#include "y_absl/numeric/int128.h" +#include "y_absl/numeric/internal/representation.h" +#include "y_absl/strings/numbers.h" +#include "y_absl/types/optional.h" +#include "y_absl/types/span.h" namespace y_absl { ABSL_NAMESPACE_BEGIN @@ -40,905 +40,905 @@ namespace str_format_internal { namespace { -using ::y_absl::numeric_internal::IsDoubleDouble; - -// The code below wants to avoid heap allocations. -// To do so it needs to allocate memory on the stack. -// `StackArray` will allocate memory on the stack in the form of a uint32_t -// array and call the provided callback with said memory. -// It will allocate memory in increments of 512 bytes. We could allocate the -// largest needed unconditionally, but that is more than we need in most of -// cases. This way we use less stack in the common cases. -class StackArray { - using Func = y_absl::FunctionRef<void(y_absl::Span<uint32_t>)>; - static constexpr size_t kStep = 512 / sizeof(uint32_t); - // 5 steps is 2560 bytes, which is enough to hold a long double with the - // largest/smallest exponents. - // The operations below will static_assert their particular maximum. - static constexpr size_t kNumSteps = 5; - - // We do not want this function to be inlined. - // Otherwise the caller will allocate the stack space unnecessarily for all - // the variants even though it only calls one. - template <size_t steps> - ABSL_ATTRIBUTE_NOINLINE static void RunWithCapacityImpl(Func f) { - uint32_t values[steps * kStep]{}; - f(y_absl::MakeSpan(values)); - } - - public: - static constexpr size_t kMaxCapacity = kStep * kNumSteps; - - static void RunWithCapacity(size_t capacity, Func f) { - assert(capacity <= kMaxCapacity); - const size_t step = (capacity + kStep - 1) / kStep; - assert(step <= kNumSteps); - switch (step) { - case 1: - return RunWithCapacityImpl<1>(f); - case 2: - return RunWithCapacityImpl<2>(f); - case 3: - return RunWithCapacityImpl<3>(f); - case 4: - return RunWithCapacityImpl<4>(f); - case 5: - return RunWithCapacityImpl<5>(f); - } - - assert(false && "Invalid capacity"); - } -}; - -// Calculates `10 * (*v) + carry` and stores the result in `*v` and returns -// the carry. -template <typename Int> -inline Int MultiplyBy10WithCarry(Int *v, Int carry) { - using BiggerInt = y_absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; - BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + carry; - *v = static_cast<Int>(tmp); - return static_cast<Int>(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) { - 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 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; -} - -using MaxFloatType = - typename std::conditional<IsDoubleDouble(), double, long double>::type; - -// Generates the decimal representation for an integer of the form `v * 2^exp`, -// where `v` and `exp` are both positive integers. -// It generates the digits from the left (ie the most significant digit first) -// to allow for direct printing into the sink. -// -// Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`. -class BinaryToDecimal { - static constexpr int 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; - } - - public: - // Run the conversion for `v * 2^exp` and call `f(binary_to_decimal)`. - // This function will allocate enough stack space to perform the conversion. - static void RunConversion(uint128 v, int exp, - y_absl::FunctionRef<void(BinaryToDecimal)> f) { - assert(exp > 0); - assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent); - static_assert( - static_cast<int>(StackArray::kMaxCapacity) >= - ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent), - ""); - - StackArray::RunWithCapacity( - ChunksNeeded(exp), - [=](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()); - } - - // See the current block of digits. - y_absl::string_view CurrentDigits() const { - return y_absl::string_view(digits_ + kDigitsPerChunk - size_, size_); - } - - // Advance the current view of digits. - // Returns `false` when no more digits are available. - bool AdvanceDigits() { - if (decimal_start_ >= decimal_end_) return false; - - uint32_t w = data_[decimal_start_++]; - for (size_ = 0; size_ < kDigitsPerChunk; w /= 10) { - digits_[kDigitsPerChunk - ++size_] = w % 10 + '0'; - } - return true; - } - - private: - BinaryToDecimal(y_absl::Span<uint32_t> data, uint128 v, int exp) : data_(data) { - // We need to print the digits directly into the sink object without - // buffering them all first. To do this we need two things: - // - to know the total number of digits to do padding when necessary - // - to generate the decimal digits from the left. - // - // In order to do this, we do a two pass conversion. - // On the first pass we convert the binary representation of the value into - // a decimal representation in which each uint32_t chunk holds up to 9 - // decimal digits. In the second pass we take each decimal-holding-uint32_t - // value and generate the ascii decimal digits into `digits_`. - // - // The binary and decimal representations actually share the same memory - // region. As we go converting the chunks from binary to decimal we free - // them up and reuse them for the decimal representation. One caveat is that - // 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; - 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); - for (v >>= (32 - offset); v; v >>= 32) - data_[++chunk_index] = static_cast<uint32_t>(v); - - while (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. - 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}); - 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; - - --decimal_start_; - assert(decimal_start_ != chunk_index); - data_[decimal_start_] = carry; - } - - // Fill the first set of digits. The first chunk might not be complete, so - // handle differently. - for (uint32_t first = data_[decimal_start_++]; first != 0; first /= 10) { - digits_[kDigitsPerChunk - ++size_] = first % 10 + '0'; - } - } - - private: - static constexpr int kDigitsPerChunk = 9; - - int decimal_start_; - int decimal_end_; - - char digits_[kDigitsPerChunk]; - int size_ = 0; - - y_absl::Span<uint32_t> data_; -}; - -// Converts a value of the form `x * 2^-exp` into a sequence of decimal digits. -// Requires `-exp < 0` and -// `-exp >= limits<MaxFloatType>::min_exponent - limits<MaxFloatType>::digits`. -class FractionalDigitGenerator { - public: - // Run the conversion for `v * 2^exp` and call `f(generator)`. - // This function will allocate enough stack space to perform the conversion. - static void RunConversion( - uint128 v, int exp, y_absl::FunctionRef<void(FractionalDigitGenerator)> f) { - using Limits = std::numeric_limits<MaxFloatType>; - assert(-exp < 0); - assert(-exp >= Limits::min_exponent - 128); - 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)); - }); - } - - // Returns true if there are any more non-zero digits left. - bool HasMoreDigits() const { return next_digit_ != 0 || chunk_index_ >= 0; } - - // Returns true if the remainder digits are greater than 5000... - bool IsGreaterThanHalf() const { - return next_digit_ > 5 || (next_digit_ == 5 && chunk_index_ >= 0); - } - // Returns true if the remainder digits are exactly 5000... - bool IsExactlyHalf() const { return next_digit_ == 5 && chunk_index_ < 0; } - - struct Digits { - int digit_before_nine; - int num_nines; - }; - - // Get the next set of digits. - // They are composed by a non-9 digit followed by a runs of zero or more 9s. - Digits GetDigits() { - Digits digits{next_digit_, 0}; - - next_digit_ = GetOneDigit(); - while (next_digit_ == 9) { - ++digits.num_nines; - next_digit_ = GetOneDigit(); - } - - return digits; - } - - private: - // Return the next digit. - int GetOneDigit() { - if (chunk_index_ < 0) return 0; - - uint32_t carry = 0; - for (int i = chunk_index_; i >= 0; --i) { - carry = MultiplyBy10WithCarry(&data_[i], carry); - } - // If the lowest chunk is now empty, remove it from view. - if (data_[chunk_index_] == 0) --chunk_index_; - return carry; - } - - FractionalDigitGenerator(y_absl::Span<uint32_t> data, uint128 v, int exp) - : chunk_index_(exp / 32), data_(data) { - const int offset = exp % 32; - // Right shift `v` by `exp` bits. - data_[chunk_index_] = 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) - 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_; - y_absl::Span<uint32_t> data_; -}; - -// Count the number of leading zero bits. -int LeadingZeros(uint64_t v) { return countl_zero(v); } -int LeadingZeros(uint128 v) { - auto high = static_cast<uint64_t>(v >> 64); - auto low = static_cast<uint64_t>(v); - return high != 0 ? countl_zero(high) : 64 + countl_zero(low); -} - -// Round up the text digits starting at `p`. -// The buffer must have an extra digit that is known to not need rounding. -// This is done below by having an extra '0' digit on the left. -void RoundUp(char *p) { - while (*p == '9' || *p == '.') { - if (*p == '9') *p = '0'; - --p; - } - ++*p; -} - -// Check the previous digit and round up or down to follow the round-to-even -// policy. -void RoundToEven(char *p) { - if (*p == '.') --p; - if (*p % 2 == 1) RoundUp(p); -} - -// Simple integral decimal digit printing for values that fit in 64-bits. -// Returns the pointer to the last written digit. -char *PrintIntegralDigitsFromRightFast(uint64_t v, char *p) { - do { - *--p = DivideBy10WithCarry(&v, 0) + '0'; - } while (v != 0); - return p; -} - -// Simple integral decimal digit printing for values that fit in 128-bits. -// Returns the pointer to the last written digit. -char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) { - auto high = static_cast<uint64_t>(v >> 64); - auto low = static_cast<uint64_t>(v); - - while (high != 0) { - uint64_t carry = DivideBy10WithCarry(&high, 0); - carry = DivideBy10WithCarry(&low, carry); - *--p = carry + '0'; - } - return PrintIntegralDigitsFromRightFast(low, p); -} - -// Simple fractional decimal digit printing for values that fir in 64-bits after -// 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 *p = start; - v <<= (64 - exp); - while (precision > 0) { - if (!v) return p; - *p++ = MultiplyBy10WithCarry(&v, uint64_t{0}) + '0'; - --precision; - } - - // We need to round. - if (v < 0x8000000000000000) { - // We round down, so nothing to do. - } else if (v > 0x8000000000000000) { - // We round up. - RoundUp(p - 1); - } else { - RoundToEven(p - 1); - } - - assert(precision == 0); - // Precision can only be zero here. - return p; -} - -// Simple fractional decimal digit printing for values that fir in 128-bits -// 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 *p = start; - v <<= (128 - exp); - auto high = static_cast<uint64_t>(v >> 64); - auto low = static_cast<uint64_t>(v); - - // 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}); - carry = MultiplyBy10WithCarry(&high, carry); - - *p++ = carry + '0'; - --precision; - } - - // Now `low` is empty, so use a faster approach for the rest of the digits. - // This block is pretty much the same as the main loop for the 64-bit case - // above. - while (precision > 0) { - if (!high) return p; - *p++ = MultiplyBy10WithCarry(&high, uint64_t{0}) + '0'; - --precision; - } - - // We need to round. - if (high < 0x8000000000000000) { - // We round down, so nothing to do. - } else if (high > 0x8000000000000000 || low != 0) { - // We round up. - RoundUp(p - 1); - } else { - RoundToEven(p - 1); - } - - assert(precision == 0); - // Precision can only be zero here. - return p; -} - -struct FormatState { - char sign_char; - int precision; - const FormatConversionSpecImpl &conv; - FormatSinkImpl *sink; - - // In `alt` mode (flag #) we keep the `.` even if there are no fractional - // digits. In non-alt mode, we strip it. - bool ShouldPrintDot() const { return precision != 0 || conv.has_alt_flag(); } -}; - -struct Padding { - int left_spaces; - int zeros; - int right_spaces; -}; - -Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { - if (state.conv.width() < 0 || - static_cast<size_t>(state.conv.width()) <= total_size) { - return {0, 0, 0}; - } - int missing_chars = state.conv.width() - total_size; - if (state.conv.has_left_flag()) { - return {0, 0, missing_chars}; - } else if (state.conv.has_zero_flag()) { - return {0, missing_chars, 0}; - } else { - return {missing_chars, 0, 0}; - } -} - -void FinalPrint(const FormatState &state, y_absl::string_view data, - int padding_offset, int trailing_zeros, - y_absl::string_view data_postfix) { - if (state.conv.width() < 0) { - // No width specified. Fast-path. - if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); - state.sink->Append(data); - state.sink->Append(trailing_zeros, '0'); - state.sink->Append(data_postfix); - return; - } - - auto padding = ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + - data.size() + data_postfix.size() + - static_cast<size_t>(trailing_zeros), - state); - - state.sink->Append(padding.left_spaces, ' '); - if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); - // Padding in general needs to be inserted somewhere in the middle of `data`. - state.sink->Append(data.substr(0, padding_offset)); - state.sink->Append(padding.zeros, '0'); - state.sink->Append(data.substr(padding_offset)); - state.sink->Append(trailing_zeros, '0'); - state.sink->Append(data_postfix); - state.sink->Append(padding.right_spaces, ' '); -} - -// Fastpath %f formatter for when the shifted value fits in a simple integral -// type. -// Prints `v*2^exp` with the options from `state`. -template <typename Int> -void FormatFFast(Int v, int exp, const FormatState &state) { - constexpr int input_bits = sizeof(Int) * 8; - - static constexpr size_t integral_size = - /* in case we need to round up an extra digit */ 1 + - /* decimal digits for uint128 */ 40 + 1; - char buffer[integral_size + /* . */ 1 + /* max digits uint128 */ 128]; - buffer[integral_size] = '.'; - char *const integral_digits_end = buffer + integral_size; - char *integral_digits_start; - char *const fractional_digits_start = buffer + integral_size + 1; - char *fractional_digits_end = fractional_digits_start; - - if (exp >= 0) { - const int total_bits = input_bits - LeadingZeros(v) + exp; - integral_digits_start = - total_bits <= 64 - ? PrintIntegralDigitsFromRightFast(static_cast<uint64_t>(v) << exp, - integral_digits_end) - : PrintIntegralDigitsFromRightFast(static_cast<uint128>(v) << exp, - integral_digits_end); - } else { - exp = -exp; - - integral_digits_start = PrintIntegralDigitsFromRightFast( - exp < input_bits ? v >> exp : 0, integral_digits_end); - // PrintFractionalDigits may pull a carried 1 all the way up through the - // integral portion. - integral_digits_start[-1] = '0'; - - fractional_digits_end = - exp <= 64 ? PrintFractionalDigitsFast(v, fractional_digits_start, exp, - state.precision) - : PrintFractionalDigitsFast(static_cast<uint128>(v), - fractional_digits_start, exp, - state.precision); - // There was a carry, so include the first digit too. - if (integral_digits_start[-1] != '0') --integral_digits_start; - } - - size_t size = 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)), - /*data_postfix=*/""); -} - -// Slow %f formatter for when the shifted value does not fit in a uint128, and -// `exp > 0`. -// Prints `v*2^exp` with the options from `state`. -// This one is guaranteed to not have fractional digits, so we don't have to -// worry about anything after the `.`. -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); - - 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); - state.sink->Append(padding.zeros, '0'); - - do { - state.sink->Append(btd.CurrentDigits()); - } while (btd.AdvanceDigits()); - - if (state.ShouldPrintDot()) state.sink->Append(1, '.'); - state.sink->Append(state.precision, '0'); - state.sink->Append(padding.right_spaces, ' '); - }); -} - -// Slow %f formatter for when the shifted value does not fit in a uint128, and -// `exp < 0`. -// Prints `v*2^exp` with the options from `state`. -// This one is guaranteed to be < 1.0, so we don't have to worry about integral -// 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); - auto padding = - ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state); - padding.zeros += 1; - state.sink->Append(padding.left_spaces, ' '); - if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); - state.sink->Append(padding.zeros, '0'); - - if (state.ShouldPrintDot()) state.sink->Append(1, '.'); - - // Print digits - int digits_to_go = state.precision; - - FractionalDigitGenerator::RunConversion( - v, exp, [&](FractionalDigitGenerator digit_gen) { - // There are no digits to print here. - if (state.precision == 0) return; - - // We go one digit at a time, while keeping track of runs of nines. - // The runs of nines are used to perform rounding when necessary. - - while (digits_to_go > 0 && digit_gen.HasMoreDigits()) { - auto digits = digit_gen.GetDigits(); - - // Now we have a digit and a run of nines. - // See if we can print them all. - if (digits.num_nines + 1 < digits_to_go) { - // We don't have to round yet, so print them. - state.sink->Append(1, digits.digit_before_nine + '0'); - state.sink->Append(digits.num_nines, '9'); - digits_to_go -= digits.num_nines + 1; - - } else { - // We can't print all the nines, see where we have to truncate. - - bool round_up = false; - if (digits.num_nines + 1 > digits_to_go) { - // We round up at a nine. No need to print them. - round_up = true; - } else { - // We can fit all the nines, but truncate just after it. - if (digit_gen.IsGreaterThanHalf()) { - round_up = true; - } else if (digit_gen.IsExactlyHalf()) { - // Round to even - round_up = - digits.num_nines != 0 || digits.digit_before_nine % 2 == 1; - } - } - - if (round_up) { - state.sink->Append(1, digits.digit_before_nine + '1'); - --digits_to_go; - // The rest will be zeros. - } else { - state.sink->Append(1, digits.digit_before_nine + '0'); - state.sink->Append(digits_to_go - 1, '9'); - digits_to_go = 0; - } - return; - } - } - }); - - state.sink->Append(digits_to_go, '0'); - state.sink->Append(padding.right_spaces, ' '); -} - -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; - - // Fallback to the slow stack-based approach if we can't do it in a 64 or - // 128 bit state. - if (ABSL_PREDICT_FALSE(total_bits > 128)) { - return FormatFPositiveExpSlow(mantissa, exp, state); - } - } else { - // Fallback to the slow stack-based approach if we can't do it in a 64 or - // 128 bit state. - if (ABSL_PREDICT_FALSE(exp < -128)) { - return FormatFNegativeExpSlow(mantissa, -exp, state); - } - } - return FormatFFast(mantissa, exp, 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) { - constexpr Int mask_low_nibble = Int{0xf}; - int shift = nibble_index * 4; - n &= mask_low_nibble << shift; - return static_cast<uint8_t>((n >> shift) & 0xf); -} - -// 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; - 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, - // ASan will complain if we try to shift the 1 beyond the limits of the Int, - // 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))); - 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; - static const Int ones = ~Int{0}; - return ones >> std::max(0, 4 * (kNumNibbles - nibble_index - 1)); -} - -// 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); -} - -template <typename Int> -Int MoveToNibble(uint8_t nibble, int nibble_index) { - return Int{nibble} << (4 * nibble_index); -} - -// Given mantissa size, find optimal # of mantissa bits to put in initial digit. -// -// In the hex representation we keep a single hex digit to the left of the dot. -// However, the question as to how many bits of the mantissa should be put into -// that hex digit in theory is arbitrary, but in practice it is optimal to -// choose based on the size of the mantissa. E.g., for a `double`, there are 53 -// mantissa bits, so that means that we should put 1 bit to the left of the dot, -// thereby leaving 52 bits to the right, which is evenly divisible by four and -// thus all fractional digits represent actual precision. For a `long double`, -// on the other hand, there are 64 bits of mantissa, thus we can use all four -// bits for the initial hex digit and still have a number left over (60) that is -// a multiple of four. Once again, the goal is to have all fractional digits -// represent real precision. -template <typename Float> -constexpr int HexFloatLeadingDigitSizeInBits() { - return std::numeric_limits<Float>::digits % 4 > 0 - ? std::numeric_limits<Float>::digits % 4 - : 4; -} - -// This function captures the rounding behavior of glibc for hex float -// representations. E.g. when rounding 0x1.ab800000 to a precision of .2 -// ("%.2a") glibc will round up because it rounds toward the even number (since -// 0xb is an odd number, it will round up to 0xc). However, when rounding at a -// 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, - 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) { - return false; - } - int rounding_nibble_idx = final_nibble_displayed - 1; - constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; - assert(final_nibble_displayed <= kTotalNibbles); - Int mantissa_up_to_rounding_nibble_inclusive = - mantissa & MaskUpToNibbleInclusive<Int>(rounding_nibble_idx); - Int eight = MoveToNibble<Int>(8, rounding_nibble_idx); - if (mantissa_up_to_rounding_nibble_inclusive != eight) { - return mantissa_up_to_rounding_nibble_inclusive > eight; - } - // Nibble in question == 8. - uint8_t round_if_odd = (final_nibble_displayed == kTotalNibbles) - ? leading - : GetNibble(mantissa, final_nibble_displayed); - return round_if_odd % 2 == 1; -} - -// Stores values associated with a Float type needed by the FormatA -// implementation in order to avoid templatizing that function by the Float -// type. -struct HexFloatTypeParams { - template <typename Float> - explicit HexFloatTypeParams(Float) - : min_exponent(std::numeric_limits<Float>::min_exponent - 1), - leading_digit_size_bits(HexFloatLeadingDigitSizeInBits<Float>()) { - assert(leading_digit_size_bits >= 1 && leading_digit_size_bits <= 4); - } - - int min_exponent; - int leading_digit_size_bits; -}; - -// Hex Float Rounding. First check if we need to round; if so, then we do that -// by manipulating (incrementing) the mantissa, that way we can later print the -// mantissa digits by iterating through them in the same way regardless of -// whether a rounding happened. -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; - // Index of the last nibble that we could display given precision. - int final_nibble_displayed = - precision_specified ? std::max(0, (kTotalNibbles - state.precision)) : 0; - if (HexFloatNeedsRoundUp(*mantissa, final_nibble_displayed, *leading)) { - // Need to round up. - bool overflow = IncrementNibble(final_nibble_displayed, mantissa); - *leading += (overflow ? 1 : 0); - if (ABSL_PREDICT_FALSE(*leading > 15)) { - // We have overflowed the leading digit. This would mean that we would - // need two hex digits to the left of the dot, which is not allowed. So - // adjust the mantissa and exponent so that the result is always 1.0eXXX. - *leading = 1; - *mantissa = 0; - *exp += 4; - } - } - // Now that we have handled a possible round-up we can go ahead and zero out - // all the nibbles of the mantissa that we won't need. - if (precision_specified) { - *mantissa &= ~MaskUpToNibbleExclusive<Int>(final_nibble_displayed); - } -} - -template <typename Int> -void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading, - Int *mantissa, int *exp) { - constexpr int kIntBits = sizeof(Int) * 8; - static const Int kHighIntBit = Int{1} << (kIntBits - 1); - const int 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)) { - if (ABSL_PREDICT_FALSE(*exp - 1 < float_traits.min_exponent)) { - *mantissa >>= (float_traits.min_exponent - *exp); - *exp = float_traits.min_exponent; - return; - } - *mantissa <<= 1; - --*exp; - } - // 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; -} - -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; - // Did the user specify a precision explicitly? - const bool precision_specified = state.conv.precision() >= 0; - - // ========== Normalize/Denormalize ========== - exp += kIntBits; // make all digits fractional digits. - // This holds the (up to four) bits of leading digit, i.e., the '1' in the - // number 0x1.e6fp+2. It's always > 0 unless number is zero or denormal. - uint8_t leading = 0; - FormatANormalize(float_traits, &leading, &mantissa, &exp); - - // =============== Rounding ================== - // Check if we need to round; if so, then we do that by manipulating - // (incrementing) the mantissa before beginning to print characters. - FormatARound(precision_specified, state, &leading, &mantissa, &exp); - - // ============= Format Result =============== - // This buffer holds the "0x1.ab1de3" portion of "0x1.ab1de3pe+2". Compute the - // size with long double which is the largest of the floats. - constexpr size_t kBufSizeForHexFloatRepr = - 2 // 0x - + std::numeric_limits<MaxFloatType>::digits / 4 // number of hex digits - + 1 // round up - + 1; // "." (dot) - char digits_buffer[kBufSizeForHexFloatRepr]; - char *digits_iter = digits_buffer; - const char *const digits = - static_cast<const char *>("0123456789ABCDEF0123456789abcdef") + - (uppercase ? 0 : 16); - - // =============== Hex Prefix ================ - *digits_iter++ = '0'; - *digits_iter++ = uppercase ? 'X' : 'x'; - - // ========== Non-Fractional Digit =========== - *digits_iter++ = digits[leading]; - - // ================== Dot ==================== - // There are three reasons we might need a dot. Keep in mind that, at this - // point, the mantissa holds only the fractional part. - if ((precision_specified && state.precision > 0) || - (!precision_specified && mantissa > 0) || state.conv.has_alt_flag()) { - *digits_iter++ = '.'; - } - - // ============ Fractional Digits ============ - int 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); - - // =============== Exponent ================== - constexpr size_t kBufSizeForExpDecRepr = - numbers_internal::kFastToBufferSize // requred for FastIntToBuffer - + 1 // 'p' or 'P' - + 1; // '+' or '-' - char exp_buffer[kBufSizeForExpDecRepr]; - exp_buffer[0] = uppercase ? 'P' : 'p'; - exp_buffer[1] = exp >= 0 ? '+' : '-'; - 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 -} - -char *CopyStringTo(y_absl::string_view v, char *out) { +using ::y_absl::numeric_internal::IsDoubleDouble; + +// The code below wants to avoid heap allocations. +// To do so it needs to allocate memory on the stack. +// `StackArray` will allocate memory on the stack in the form of a uint32_t +// array and call the provided callback with said memory. +// It will allocate memory in increments of 512 bytes. We could allocate the +// largest needed unconditionally, but that is more than we need in most of +// cases. This way we use less stack in the common cases. +class StackArray { + using Func = y_absl::FunctionRef<void(y_absl::Span<uint32_t>)>; + static constexpr size_t kStep = 512 / sizeof(uint32_t); + // 5 steps is 2560 bytes, which is enough to hold a long double with the + // largest/smallest exponents. + // The operations below will static_assert their particular maximum. + static constexpr size_t kNumSteps = 5; + + // We do not want this function to be inlined. + // Otherwise the caller will allocate the stack space unnecessarily for all + // the variants even though it only calls one. + template <size_t steps> + ABSL_ATTRIBUTE_NOINLINE static void RunWithCapacityImpl(Func f) { + uint32_t values[steps * kStep]{}; + f(y_absl::MakeSpan(values)); + } + + public: + static constexpr size_t kMaxCapacity = kStep * kNumSteps; + + static void RunWithCapacity(size_t capacity, Func f) { + assert(capacity <= kMaxCapacity); + const size_t step = (capacity + kStep - 1) / kStep; + assert(step <= kNumSteps); + switch (step) { + case 1: + return RunWithCapacityImpl<1>(f); + case 2: + return RunWithCapacityImpl<2>(f); + case 3: + return RunWithCapacityImpl<3>(f); + case 4: + return RunWithCapacityImpl<4>(f); + case 5: + return RunWithCapacityImpl<5>(f); + } + + assert(false && "Invalid capacity"); + } +}; + +// Calculates `10 * (*v) + carry` and stores the result in `*v` and returns +// the carry. +template <typename Int> +inline Int MultiplyBy10WithCarry(Int *v, Int carry) { + using BiggerInt = y_absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; + BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + carry; + *v = static_cast<Int>(tmp); + return static_cast<Int>(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) { + 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 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; +} + +using MaxFloatType = + typename std::conditional<IsDoubleDouble(), double, long double>::type; + +// Generates the decimal representation for an integer of the form `v * 2^exp`, +// where `v` and `exp` are both positive integers. +// It generates the digits from the left (ie the most significant digit first) +// to allow for direct printing into the sink. +// +// Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`. +class BinaryToDecimal { + static constexpr int 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; + } + + public: + // Run the conversion for `v * 2^exp` and call `f(binary_to_decimal)`. + // This function will allocate enough stack space to perform the conversion. + static void RunConversion(uint128 v, int exp, + y_absl::FunctionRef<void(BinaryToDecimal)> f) { + assert(exp > 0); + assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent); + static_assert( + static_cast<int>(StackArray::kMaxCapacity) >= + ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent), + ""); + + StackArray::RunWithCapacity( + ChunksNeeded(exp), + [=](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()); + } + + // See the current block of digits. + y_absl::string_view CurrentDigits() const { + return y_absl::string_view(digits_ + kDigitsPerChunk - size_, size_); + } + + // Advance the current view of digits. + // Returns `false` when no more digits are available. + bool AdvanceDigits() { + if (decimal_start_ >= decimal_end_) return false; + + uint32_t w = data_[decimal_start_++]; + for (size_ = 0; size_ < kDigitsPerChunk; w /= 10) { + digits_[kDigitsPerChunk - ++size_] = w % 10 + '0'; + } + return true; + } + + private: + BinaryToDecimal(y_absl::Span<uint32_t> data, uint128 v, int exp) : data_(data) { + // We need to print the digits directly into the sink object without + // buffering them all first. To do this we need two things: + // - to know the total number of digits to do padding when necessary + // - to generate the decimal digits from the left. + // + // In order to do this, we do a two pass conversion. + // On the first pass we convert the binary representation of the value into + // a decimal representation in which each uint32_t chunk holds up to 9 + // decimal digits. In the second pass we take each decimal-holding-uint32_t + // value and generate the ascii decimal digits into `digits_`. + // + // The binary and decimal representations actually share the same memory + // region. As we go converting the chunks from binary to decimal we free + // them up and reuse them for the decimal representation. One caveat is that + // 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; + 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); + for (v >>= (32 - offset); v; v >>= 32) + data_[++chunk_index] = static_cast<uint32_t>(v); + + while (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. + 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}); + 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; + + --decimal_start_; + assert(decimal_start_ != chunk_index); + data_[decimal_start_] = carry; + } + + // Fill the first set of digits. The first chunk might not be complete, so + // handle differently. + for (uint32_t first = data_[decimal_start_++]; first != 0; first /= 10) { + digits_[kDigitsPerChunk - ++size_] = first % 10 + '0'; + } + } + + private: + static constexpr int kDigitsPerChunk = 9; + + int decimal_start_; + int decimal_end_; + + char digits_[kDigitsPerChunk]; + int size_ = 0; + + y_absl::Span<uint32_t> data_; +}; + +// Converts a value of the form `x * 2^-exp` into a sequence of decimal digits. +// Requires `-exp < 0` and +// `-exp >= limits<MaxFloatType>::min_exponent - limits<MaxFloatType>::digits`. +class FractionalDigitGenerator { + public: + // Run the conversion for `v * 2^exp` and call `f(generator)`. + // This function will allocate enough stack space to perform the conversion. + static void RunConversion( + uint128 v, int exp, y_absl::FunctionRef<void(FractionalDigitGenerator)> f) { + using Limits = std::numeric_limits<MaxFloatType>; + assert(-exp < 0); + assert(-exp >= Limits::min_exponent - 128); + 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)); + }); + } + + // Returns true if there are any more non-zero digits left. + bool HasMoreDigits() const { return next_digit_ != 0 || chunk_index_ >= 0; } + + // Returns true if the remainder digits are greater than 5000... + bool IsGreaterThanHalf() const { + return next_digit_ > 5 || (next_digit_ == 5 && chunk_index_ >= 0); + } + // Returns true if the remainder digits are exactly 5000... + bool IsExactlyHalf() const { return next_digit_ == 5 && chunk_index_ < 0; } + + struct Digits { + int digit_before_nine; + int num_nines; + }; + + // Get the next set of digits. + // They are composed by a non-9 digit followed by a runs of zero or more 9s. + Digits GetDigits() { + Digits digits{next_digit_, 0}; + + next_digit_ = GetOneDigit(); + while (next_digit_ == 9) { + ++digits.num_nines; + next_digit_ = GetOneDigit(); + } + + return digits; + } + + private: + // Return the next digit. + int GetOneDigit() { + if (chunk_index_ < 0) return 0; + + uint32_t carry = 0; + for (int i = chunk_index_; i >= 0; --i) { + carry = MultiplyBy10WithCarry(&data_[i], carry); + } + // If the lowest chunk is now empty, remove it from view. + if (data_[chunk_index_] == 0) --chunk_index_; + return carry; + } + + FractionalDigitGenerator(y_absl::Span<uint32_t> data, uint128 v, int exp) + : chunk_index_(exp / 32), data_(data) { + const int offset = exp % 32; + // Right shift `v` by `exp` bits. + data_[chunk_index_] = 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) + 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_; + y_absl::Span<uint32_t> data_; +}; + +// Count the number of leading zero bits. +int LeadingZeros(uint64_t v) { return countl_zero(v); } +int LeadingZeros(uint128 v) { + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + return high != 0 ? countl_zero(high) : 64 + countl_zero(low); +} + +// Round up the text digits starting at `p`. +// The buffer must have an extra digit that is known to not need rounding. +// This is done below by having an extra '0' digit on the left. +void RoundUp(char *p) { + while (*p == '9' || *p == '.') { + if (*p == '9') *p = '0'; + --p; + } + ++*p; +} + +// Check the previous digit and round up or down to follow the round-to-even +// policy. +void RoundToEven(char *p) { + if (*p == '.') --p; + if (*p % 2 == 1) RoundUp(p); +} + +// Simple integral decimal digit printing for values that fit in 64-bits. +// Returns the pointer to the last written digit. +char *PrintIntegralDigitsFromRightFast(uint64_t v, char *p) { + do { + *--p = DivideBy10WithCarry(&v, 0) + '0'; + } while (v != 0); + return p; +} + +// Simple integral decimal digit printing for values that fit in 128-bits. +// Returns the pointer to the last written digit. +char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) { + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + + while (high != 0) { + uint64_t carry = DivideBy10WithCarry(&high, 0); + carry = DivideBy10WithCarry(&low, carry); + *--p = carry + '0'; + } + return PrintIntegralDigitsFromRightFast(low, p); +} + +// Simple fractional decimal digit printing for values that fir in 64-bits after +// 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 *p = start; + v <<= (64 - exp); + while (precision > 0) { + if (!v) return p; + *p++ = MultiplyBy10WithCarry(&v, uint64_t{0}) + '0'; + --precision; + } + + // We need to round. + if (v < 0x8000000000000000) { + // We round down, so nothing to do. + } else if (v > 0x8000000000000000) { + // We round up. + RoundUp(p - 1); + } else { + RoundToEven(p - 1); + } + + assert(precision == 0); + // Precision can only be zero here. + return p; +} + +// Simple fractional decimal digit printing for values that fir in 128-bits +// 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 *p = start; + v <<= (128 - exp); + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + + // 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}); + carry = MultiplyBy10WithCarry(&high, carry); + + *p++ = carry + '0'; + --precision; + } + + // Now `low` is empty, so use a faster approach for the rest of the digits. + // This block is pretty much the same as the main loop for the 64-bit case + // above. + while (precision > 0) { + if (!high) return p; + *p++ = MultiplyBy10WithCarry(&high, uint64_t{0}) + '0'; + --precision; + } + + // We need to round. + if (high < 0x8000000000000000) { + // We round down, so nothing to do. + } else if (high > 0x8000000000000000 || low != 0) { + // We round up. + RoundUp(p - 1); + } else { + RoundToEven(p - 1); + } + + assert(precision == 0); + // Precision can only be zero here. + return p; +} + +struct FormatState { + char sign_char; + int precision; + const FormatConversionSpecImpl &conv; + FormatSinkImpl *sink; + + // In `alt` mode (flag #) we keep the `.` even if there are no fractional + // digits. In non-alt mode, we strip it. + bool ShouldPrintDot() const { return precision != 0 || conv.has_alt_flag(); } +}; + +struct Padding { + int left_spaces; + int zeros; + int right_spaces; +}; + +Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { + if (state.conv.width() < 0 || + static_cast<size_t>(state.conv.width()) <= total_size) { + return {0, 0, 0}; + } + int missing_chars = state.conv.width() - total_size; + if (state.conv.has_left_flag()) { + return {0, 0, missing_chars}; + } else if (state.conv.has_zero_flag()) { + return {0, missing_chars, 0}; + } else { + return {missing_chars, 0, 0}; + } +} + +void FinalPrint(const FormatState &state, y_absl::string_view data, + int padding_offset, int trailing_zeros, + y_absl::string_view data_postfix) { + if (state.conv.width() < 0) { + // No width specified. Fast-path. + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(data); + state.sink->Append(trailing_zeros, '0'); + state.sink->Append(data_postfix); + return; + } + + auto padding = ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + + data.size() + data_postfix.size() + + static_cast<size_t>(trailing_zeros), + state); + + state.sink->Append(padding.left_spaces, ' '); + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + // Padding in general needs to be inserted somewhere in the middle of `data`. + state.sink->Append(data.substr(0, padding_offset)); + state.sink->Append(padding.zeros, '0'); + state.sink->Append(data.substr(padding_offset)); + state.sink->Append(trailing_zeros, '0'); + state.sink->Append(data_postfix); + state.sink->Append(padding.right_spaces, ' '); +} + +// Fastpath %f formatter for when the shifted value fits in a simple integral +// type. +// Prints `v*2^exp` with the options from `state`. +template <typename Int> +void FormatFFast(Int v, int exp, const FormatState &state) { + constexpr int input_bits = sizeof(Int) * 8; + + static constexpr size_t integral_size = + /* in case we need to round up an extra digit */ 1 + + /* decimal digits for uint128 */ 40 + 1; + char buffer[integral_size + /* . */ 1 + /* max digits uint128 */ 128]; + buffer[integral_size] = '.'; + char *const integral_digits_end = buffer + integral_size; + char *integral_digits_start; + char *const fractional_digits_start = buffer + integral_size + 1; + char *fractional_digits_end = fractional_digits_start; + + if (exp >= 0) { + const int total_bits = input_bits - LeadingZeros(v) + exp; + integral_digits_start = + total_bits <= 64 + ? PrintIntegralDigitsFromRightFast(static_cast<uint64_t>(v) << exp, + integral_digits_end) + : PrintIntegralDigitsFromRightFast(static_cast<uint128>(v) << exp, + integral_digits_end); + } else { + exp = -exp; + + integral_digits_start = PrintIntegralDigitsFromRightFast( + exp < input_bits ? v >> exp : 0, integral_digits_end); + // PrintFractionalDigits may pull a carried 1 all the way up through the + // integral portion. + integral_digits_start[-1] = '0'; + + fractional_digits_end = + exp <= 64 ? PrintFractionalDigitsFast(v, fractional_digits_start, exp, + state.precision) + : PrintFractionalDigitsFast(static_cast<uint128>(v), + fractional_digits_start, exp, + state.precision); + // There was a carry, so include the first digit too. + if (integral_digits_start[-1] != '0') --integral_digits_start; + } + + size_t size = 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)), + /*data_postfix=*/""); +} + +// Slow %f formatter for when the shifted value does not fit in a uint128, and +// `exp > 0`. +// Prints `v*2^exp` with the options from `state`. +// This one is guaranteed to not have fractional digits, so we don't have to +// worry about anything after the `.`. +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); + + 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); + state.sink->Append(padding.zeros, '0'); + + do { + state.sink->Append(btd.CurrentDigits()); + } while (btd.AdvanceDigits()); + + if (state.ShouldPrintDot()) state.sink->Append(1, '.'); + state.sink->Append(state.precision, '0'); + state.sink->Append(padding.right_spaces, ' '); + }); +} + +// Slow %f formatter for when the shifted value does not fit in a uint128, and +// `exp < 0`. +// Prints `v*2^exp` with the options from `state`. +// This one is guaranteed to be < 1.0, so we don't have to worry about integral +// 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); + auto padding = + ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state); + padding.zeros += 1; + state.sink->Append(padding.left_spaces, ' '); + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(padding.zeros, '0'); + + if (state.ShouldPrintDot()) state.sink->Append(1, '.'); + + // Print digits + int digits_to_go = state.precision; + + FractionalDigitGenerator::RunConversion( + v, exp, [&](FractionalDigitGenerator digit_gen) { + // There are no digits to print here. + if (state.precision == 0) return; + + // We go one digit at a time, while keeping track of runs of nines. + // The runs of nines are used to perform rounding when necessary. + + while (digits_to_go > 0 && digit_gen.HasMoreDigits()) { + auto digits = digit_gen.GetDigits(); + + // Now we have a digit and a run of nines. + // See if we can print them all. + if (digits.num_nines + 1 < digits_to_go) { + // We don't have to round yet, so print them. + state.sink->Append(1, digits.digit_before_nine + '0'); + state.sink->Append(digits.num_nines, '9'); + digits_to_go -= digits.num_nines + 1; + + } else { + // We can't print all the nines, see where we have to truncate. + + bool round_up = false; + if (digits.num_nines + 1 > digits_to_go) { + // We round up at a nine. No need to print them. + round_up = true; + } else { + // We can fit all the nines, but truncate just after it. + if (digit_gen.IsGreaterThanHalf()) { + round_up = true; + } else if (digit_gen.IsExactlyHalf()) { + // Round to even + round_up = + digits.num_nines != 0 || digits.digit_before_nine % 2 == 1; + } + } + + if (round_up) { + state.sink->Append(1, digits.digit_before_nine + '1'); + --digits_to_go; + // The rest will be zeros. + } else { + state.sink->Append(1, digits.digit_before_nine + '0'); + state.sink->Append(digits_to_go - 1, '9'); + digits_to_go = 0; + } + return; + } + } + }); + + state.sink->Append(digits_to_go, '0'); + state.sink->Append(padding.right_spaces, ' '); +} + +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; + + // Fallback to the slow stack-based approach if we can't do it in a 64 or + // 128 bit state. + if (ABSL_PREDICT_FALSE(total_bits > 128)) { + return FormatFPositiveExpSlow(mantissa, exp, state); + } + } else { + // Fallback to the slow stack-based approach if we can't do it in a 64 or + // 128 bit state. + if (ABSL_PREDICT_FALSE(exp < -128)) { + return FormatFNegativeExpSlow(mantissa, -exp, state); + } + } + return FormatFFast(mantissa, exp, 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) { + constexpr Int mask_low_nibble = Int{0xf}; + int shift = nibble_index * 4; + n &= mask_low_nibble << shift; + return static_cast<uint8_t>((n >> shift) & 0xf); +} + +// 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; + 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, + // ASan will complain if we try to shift the 1 beyond the limits of the Int, + // 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))); + 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; + static const Int ones = ~Int{0}; + return ones >> std::max(0, 4 * (kNumNibbles - nibble_index - 1)); +} + +// 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); +} + +template <typename Int> +Int MoveToNibble(uint8_t nibble, int nibble_index) { + return Int{nibble} << (4 * nibble_index); +} + +// Given mantissa size, find optimal # of mantissa bits to put in initial digit. +// +// In the hex representation we keep a single hex digit to the left of the dot. +// However, the question as to how many bits of the mantissa should be put into +// that hex digit in theory is arbitrary, but in practice it is optimal to +// choose based on the size of the mantissa. E.g., for a `double`, there are 53 +// mantissa bits, so that means that we should put 1 bit to the left of the dot, +// thereby leaving 52 bits to the right, which is evenly divisible by four and +// thus all fractional digits represent actual precision. For a `long double`, +// on the other hand, there are 64 bits of mantissa, thus we can use all four +// bits for the initial hex digit and still have a number left over (60) that is +// a multiple of four. Once again, the goal is to have all fractional digits +// represent real precision. +template <typename Float> +constexpr int HexFloatLeadingDigitSizeInBits() { + return std::numeric_limits<Float>::digits % 4 > 0 + ? std::numeric_limits<Float>::digits % 4 + : 4; +} + +// This function captures the rounding behavior of glibc for hex float +// representations. E.g. when rounding 0x1.ab800000 to a precision of .2 +// ("%.2a") glibc will round up because it rounds toward the even number (since +// 0xb is an odd number, it will round up to 0xc). However, when rounding at a +// 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, + 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) { + return false; + } + int rounding_nibble_idx = final_nibble_displayed - 1; + constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; + assert(final_nibble_displayed <= kTotalNibbles); + Int mantissa_up_to_rounding_nibble_inclusive = + mantissa & MaskUpToNibbleInclusive<Int>(rounding_nibble_idx); + Int eight = MoveToNibble<Int>(8, rounding_nibble_idx); + if (mantissa_up_to_rounding_nibble_inclusive != eight) { + return mantissa_up_to_rounding_nibble_inclusive > eight; + } + // Nibble in question == 8. + uint8_t round_if_odd = (final_nibble_displayed == kTotalNibbles) + ? leading + : GetNibble(mantissa, final_nibble_displayed); + return round_if_odd % 2 == 1; +} + +// Stores values associated with a Float type needed by the FormatA +// implementation in order to avoid templatizing that function by the Float +// type. +struct HexFloatTypeParams { + template <typename Float> + explicit HexFloatTypeParams(Float) + : min_exponent(std::numeric_limits<Float>::min_exponent - 1), + leading_digit_size_bits(HexFloatLeadingDigitSizeInBits<Float>()) { + assert(leading_digit_size_bits >= 1 && leading_digit_size_bits <= 4); + } + + int min_exponent; + int leading_digit_size_bits; +}; + +// Hex Float Rounding. First check if we need to round; if so, then we do that +// by manipulating (incrementing) the mantissa, that way we can later print the +// mantissa digits by iterating through them in the same way regardless of +// whether a rounding happened. +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; + // Index of the last nibble that we could display given precision. + int final_nibble_displayed = + precision_specified ? std::max(0, (kTotalNibbles - state.precision)) : 0; + if (HexFloatNeedsRoundUp(*mantissa, final_nibble_displayed, *leading)) { + // Need to round up. + bool overflow = IncrementNibble(final_nibble_displayed, mantissa); + *leading += (overflow ? 1 : 0); + if (ABSL_PREDICT_FALSE(*leading > 15)) { + // We have overflowed the leading digit. This would mean that we would + // need two hex digits to the left of the dot, which is not allowed. So + // adjust the mantissa and exponent so that the result is always 1.0eXXX. + *leading = 1; + *mantissa = 0; + *exp += 4; + } + } + // Now that we have handled a possible round-up we can go ahead and zero out + // all the nibbles of the mantissa that we won't need. + if (precision_specified) { + *mantissa &= ~MaskUpToNibbleExclusive<Int>(final_nibble_displayed); + } +} + +template <typename Int> +void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading, + Int *mantissa, int *exp) { + constexpr int kIntBits = sizeof(Int) * 8; + static const Int kHighIntBit = Int{1} << (kIntBits - 1); + const int 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)) { + if (ABSL_PREDICT_FALSE(*exp - 1 < float_traits.min_exponent)) { + *mantissa >>= (float_traits.min_exponent - *exp); + *exp = float_traits.min_exponent; + return; + } + *mantissa <<= 1; + --*exp; + } + // 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; +} + +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; + // Did the user specify a precision explicitly? + const bool precision_specified = state.conv.precision() >= 0; + + // ========== Normalize/Denormalize ========== + exp += kIntBits; // make all digits fractional digits. + // This holds the (up to four) bits of leading digit, i.e., the '1' in the + // number 0x1.e6fp+2. It's always > 0 unless number is zero or denormal. + uint8_t leading = 0; + FormatANormalize(float_traits, &leading, &mantissa, &exp); + + // =============== Rounding ================== + // Check if we need to round; if so, then we do that by manipulating + // (incrementing) the mantissa before beginning to print characters. + FormatARound(precision_specified, state, &leading, &mantissa, &exp); + + // ============= Format Result =============== + // This buffer holds the "0x1.ab1de3" portion of "0x1.ab1de3pe+2". Compute the + // size with long double which is the largest of the floats. + constexpr size_t kBufSizeForHexFloatRepr = + 2 // 0x + + std::numeric_limits<MaxFloatType>::digits / 4 // number of hex digits + + 1 // round up + + 1; // "." (dot) + char digits_buffer[kBufSizeForHexFloatRepr]; + char *digits_iter = digits_buffer; + const char *const digits = + static_cast<const char *>("0123456789ABCDEF0123456789abcdef") + + (uppercase ? 0 : 16); + + // =============== Hex Prefix ================ + *digits_iter++ = '0'; + *digits_iter++ = uppercase ? 'X' : 'x'; + + // ========== Non-Fractional Digit =========== + *digits_iter++ = digits[leading]; + + // ================== Dot ==================== + // There are three reasons we might need a dot. Keep in mind that, at this + // point, the mantissa holds only the fractional part. + if ((precision_specified && state.precision > 0) || + (!precision_specified && mantissa > 0) || state.conv.has_alt_flag()) { + *digits_iter++ = '.'; + } + + // ============ Fractional Digits ============ + int 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); + + // =============== Exponent ================== + constexpr size_t kBufSizeForExpDecRepr = + numbers_internal::kFastToBufferSize // requred for FastIntToBuffer + + 1 // 'p' or 'P' + + 1; // '+' or '-' + char exp_buffer[kBufSizeForExpDecRepr]; + exp_buffer[0] = uppercase ? 'P' : 'p'; + exp_buffer[1] = exp >= 0 ? '+' : '-'; + 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 +} + +char *CopyStringTo(y_absl::string_view v, char *out) { std::memcpy(out, v.data(), v.size()); return out + v.size(); } template <typename Float> -bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv, +bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { int w = conv.width() >= 0 ? conv.width() : 0; int p = conv.precision() >= 0 ? conv.precision() : -1; @@ -951,17 +951,17 @@ bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv, if (std::is_same<long double, Float>()) { *fp++ = 'L'; } - *fp++ = FormatConversionCharToChar(conv.conversion_char()); + *fp++ = FormatConversionCharToChar(conv.conversion_char()); *fp = 0; assert(fp < fmt + sizeof(fmt)); } TString space(512, '\0'); - y_absl::string_view result; + y_absl::string_view result; while (true) { 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(), n); break; } space.resize(n + 1); @@ -1014,24 +1014,24 @@ enum class FormatStyle { Fixed, Precision }; // Otherwise, return false. template <typename Float> bool ConvertNonNumericFloats(char sign_char, Float v, - const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { + const FormatConversionSpecImpl &conv, + FormatSinkImpl *sink) { char text[4], *ptr = text; - if (sign_char != '\0') *ptr++ = sign_char; + if (sign_char != '\0') *ptr++ = sign_char; if (std::isnan(v)) { - ptr = std::copy_n( - FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3, - ptr); + ptr = std::copy_n( + FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3, + ptr); } else if (std::isinf(v)) { - ptr = std::copy_n( - FormatConversionCharIsUpper(conv.conversion_char()) ? "INF" : "inf", 3, - ptr); + ptr = std::copy_n( + FormatConversionCharIsUpper(conv.conversion_char()) ? "INF" : "inf", 3, + ptr); } else { return false; } return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, - conv.has_left_flag()); + conv.has_left_flag()); } // Round up the last digit of the value. @@ -1091,12 +1091,12 @@ constexpr bool CanFitMantissa() { template <typename Float> struct Decomposed { - using MantissaType = - y_absl::conditional_t<std::is_same<long double, Float>::value, uint128, - uint64_t>; - static_assert(std::numeric_limits<Float>::digits <= sizeof(MantissaType) * 8, - ""); - MantissaType mantissa; + using MantissaType = + y_absl::conditional_t<std::is_same<long double, Float>::value, uint128, + uint64_t>; + static_assert(std::numeric_limits<Float>::digits <= sizeof(MantissaType) * 8, + ""); + MantissaType mantissa; int exponent; }; @@ -1107,8 +1107,8 @@ Decomposed<Float> Decompose(Float v) { Float m = std::frexp(v, &exp); m = std::ldexp(m, std::numeric_limits<Float>::digits); exp -= std::numeric_limits<Float>::digits; - - return {static_cast<typename Decomposed<Float>::MantissaType>(m), exp}; + + return {static_cast<typename Decomposed<Float>::MantissaType>(m), exp}; } // Print 'digits' as decimal. @@ -1277,32 +1277,32 @@ bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out, return false; } -void WriteBufferToSink(char sign_char, y_absl::string_view str, - const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { +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; - if (conv.has_left_flag()) { + if (conv.has_left_flag()) { right_spaces = missing_chars; - } else if (conv.has_zero_flag()) { + } else if (conv.has_zero_flag()) { zeros = missing_chars; } else { left_spaces = missing_chars; } sink->Append(left_spaces, ' '); - if (sign_char != '\0') sink->Append(1, sign_char); + if (sign_char != '\0') sink->Append(1, sign_char); sink->Append(zeros, '0'); sink->Append(str); sink->Append(right_spaces, ' '); } template <typename Float> -bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, +bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { // Print the sign or the sign column. Float abs_v = v; @@ -1310,9 +1310,9 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, if (std::signbit(abs_v)) { sign_char = '-'; abs_v = -abs_v; - } else if (conv.has_show_pos_flag()) { + } else if (conv.has_show_pos_flag()) { sign_char = '+'; - } else if (conv.has_sign_col_flag()) { + } else if (conv.has_sign_col_flag()) { sign_char = ' '; } @@ -1329,91 +1329,91 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, Buffer buffer; - FormatConversionChar c = conv.conversion_char(); - - if (c == FormatConversionCharInternal::f || - c == FormatConversionCharInternal::F) { - FormatF(decomposed.mantissa, decomposed.exponent, - {sign_char, precision, conv, sink}); - return true; - } else if (c == FormatConversionCharInternal::e || - c == FormatConversionCharInternal::E) { - if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, - &exp)) { - return FallbackToSnprintf(v, conv, sink); - } - if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); - PrintExponent( - exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', - &buffer); - } else if (c == FormatConversionCharInternal::g || - c == FormatConversionCharInternal::G) { - precision = std::max(0, precision - 1); - if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, - &exp)) { - return FallbackToSnprintf(v, conv, sink); - } - if (precision + 1 > exp && exp >= -4) { - if (exp < 0) { - // Have 1.23456, needs 0.00123456 - // Move the first digit - buffer.begin[1] = *buffer.begin; - // Add some zeros - for (; exp < -1; ++exp) *buffer.begin-- = '0'; - *buffer.begin-- = '.'; - *buffer.begin = '0'; - } else if (exp > 0) { - // Have 1.23456, needs 1234.56 - // Move the '.' exp positions to the right. - std::rotate(buffer.begin + 1, buffer.begin + 2, buffer.begin + exp + 2); + FormatConversionChar c = conv.conversion_char(); + + if (c == FormatConversionCharInternal::f || + c == FormatConversionCharInternal::F) { + FormatF(decomposed.mantissa, decomposed.exponent, + {sign_char, precision, conv, sink}); + return true; + } else if (c == FormatConversionCharInternal::e || + c == FormatConversionCharInternal::E) { + if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, + &exp)) { + return FallbackToSnprintf(v, conv, sink); + } + if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); + PrintExponent( + exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', + &buffer); + } else if (c == FormatConversionCharInternal::g || + c == FormatConversionCharInternal::G) { + precision = std::max(0, precision - 1); + if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, + &exp)) { + return FallbackToSnprintf(v, conv, sink); + } + if (precision + 1 > exp && exp >= -4) { + if (exp < 0) { + // Have 1.23456, needs 0.00123456 + // Move the first digit + buffer.begin[1] = *buffer.begin; + // Add some zeros + for (; exp < -1; ++exp) *buffer.begin-- = '0'; + *buffer.begin-- = '.'; + *buffer.begin = '0'; + } else if (exp > 0) { + // Have 1.23456, needs 1234.56 + // Move the '.' exp positions to the right. + std::rotate(buffer.begin + 1, buffer.begin + 2, buffer.begin + exp + 2); } - exp = 0; - } - if (!conv.has_alt_flag()) { - while (buffer.back() == '0') buffer.pop_back(); - if (buffer.back() == '.') buffer.pop_back(); - } - if (exp) { - PrintExponent( - exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', - &buffer); - } - } else if (c == FormatConversionCharInternal::a || - c == FormatConversionCharInternal::A) { - bool uppercase = (c == FormatConversionCharInternal::A); - FormatA(HexFloatTypeParams(Float{}), decomposed.mantissa, - decomposed.exponent, uppercase, {sign_char, precision, conv, sink}); - return true; - } else { - return false; + exp = 0; + } + if (!conv.has_alt_flag()) { + while (buffer.back() == '0') buffer.pop_back(); + if (buffer.back() == '.') buffer.pop_back(); + } + if (exp) { + PrintExponent( + exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', + &buffer); + } + } else if (c == FormatConversionCharInternal::a || + c == FormatConversionCharInternal::A) { + bool uppercase = (c == FormatConversionCharInternal::A); + FormatA(HexFloatTypeParams(Float{}), decomposed.mantissa, + decomposed.exponent, uppercase, {sign_char, precision, conv, sink}); + return true; + } else { + return false; } WriteBufferToSink(sign_char, - y_absl::string_view(buffer.begin, buffer.end - buffer.begin), - conv, sink); + y_absl::string_view(buffer.begin, buffer.end - buffer.begin), + conv, sink); return true; } } // namespace -bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, +bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { - if (IsDoubleDouble()) { - // This is the `double-double` representation of `long double`. We do not - // handle it natively. Fallback to snprintf. - return FallbackToSnprintf(v, conv, sink); - } - + if (IsDoubleDouble()) { + // This is the `double-double` representation of `long double`. We do not + // handle it natively. Fallback to snprintf. + return FallbackToSnprintf(v, conv, sink); + } + return FloatToSink(v, conv, sink); } -bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, +bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { - return FloatToSink(static_cast<double>(v), conv, sink); + return FloatToSink(static_cast<double>(v), conv, sink); } -bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, +bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { return FloatToSink(v, conv, sink); } diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.h index d93a415756..44e8ee2da2 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/float_conversion.h @@ -1,17 +1,17 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ @@ -21,13 +21,13 @@ namespace y_absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, +bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); -bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, +bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); -bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, +bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); } // namespace str_format_internal diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/output.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/output.h index 8fc46fbafa..ae997ae7f7 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/output.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/output.h @@ -82,11 +82,11 @@ inline void AbslFormatFlush(BufferRawSink* sink, string_view v) { sink->Write(v); } -// This is a SFINAE to get a better compiler error message when the type -// is not supported. +// This is a SFINAE to get a better compiler error message when the type +// is not supported. template <typename T> -auto InvokeFlush(T* out, string_view s) -> decltype(AbslFormatFlush(out, s)) { - AbslFormatFlush(out, s); +auto InvokeFlush(T* out, string_view s) -> decltype(AbslFormatFlush(out, s)) { + AbslFormatFlush(out, s); } } // namespace str_format_internal 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 af07e32fe5..c48cdc9ea3 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 @@ -1,17 +1,17 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "y_absl/strings/internal/str_format/parser.h" #include <assert.h> @@ -31,7 +31,7 @@ namespace y_absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -using CC = FormatConversionCharInternal; +using CC = FormatConversionCharInternal; using LM = LengthMod; // Abbreviations to fit in the table below. @@ -299,17 +299,17 @@ struct ParsedFormatBase::ParsedFormatConsumer { char* data_pos; }; -ParsedFormatBase::ParsedFormatBase( - string_view format, bool allow_ignored, - std::initializer_list<FormatConversionCharSet> convs) +ParsedFormatBase::ParsedFormatBase( + string_view format, bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs) : data_(format.empty() ? nullptr : new char[format.size()]) { has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) || !MatchesConversions(allow_ignored, convs); } bool ParsedFormatBase::MatchesConversions( - bool allow_ignored, - std::initializer_list<FormatConversionCharSet> convs) const { + bool allow_ignored, + 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() || 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 ba614bb8b4..4fcdc28c5f 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 @@ -1,17 +1,17 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ @@ -78,7 +78,7 @@ struct UnboundConversion { Flags flags = Flags::kBasic; LengthMod length_mod = LengthMod::none; - FormatConversionChar conv = FormatConversionCharInternal::kNone; + FormatConversionChar conv = FormatConversionCharInternal::kNone; }; // Consume conversion spec prefix (not including '%') of [p, end) if valid. @@ -94,7 +94,7 @@ const char* ConsumeUnboundConversion(const char* p, const char* end, // conversions. class ConvTag { public: - constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT + 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)) {} @@ -106,11 +106,11 @@ class ConvTag { bool is_length() const { return (tag_ & 0xC0) == 0x80; } bool is_flags() const { return (tag_ & 0xE0) == 0xC0; } - FormatConversionChar as_conv() const { + FormatConversionChar as_conv() const { assert(is_conv()); assert(!is_length()); assert(!is_flags()); - return static_cast<FormatConversionChar>(tag_); + return static_cast<FormatConversionChar>(tag_); } LengthMod as_length() const { assert(!is_conv()); @@ -165,7 +165,7 @@ bool ParseFormatString(string_view src, Consumer consumer) { auto tag = GetTagForChar(percent[1]); if (tag.is_conv()) { if (ABSL_PREDICT_FALSE(next_arg < 0)) { - // This indicates an error in the format string. + // This indicates an error in the format string. // The only way to get `next_arg < 0` here is to have a positional // argument first which sets next_arg to -1 and then a non-positional // argument. @@ -208,9 +208,9 @@ constexpr bool EnsureConstexpr(string_view s) { class ParsedFormatBase { public: - explicit ParsedFormatBase( - string_view format, bool allow_ignored, - std::initializer_list<FormatConversionCharSet> convs); + explicit ParsedFormatBase( + string_view format, bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs); ParsedFormatBase(const ParsedFormatBase& other) { *this = other; } @@ -257,9 +257,9 @@ class ParsedFormatBase { private: // Returns whether the conversions match and if !allow_ignored it verifies // that all conversions are used by the format. - bool MatchesConversions( - bool allow_ignored, - std::initializer_list<FormatConversionCharSet> convs) const; + bool MatchesConversions( + bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs) const; struct ParsedFormatConsumer; @@ -304,14 +304,14 @@ class ParsedFormatBase { // This is the only API that allows the user to pass a runtime specified format // string. These factory functions will return NULL if the format does not match // the conversions requested by the user. -template <FormatConversionCharSet... C> +template <FormatConversionCharSet... C> class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase { public: explicit ExtendedParsedFormat(string_view format) #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER __attribute__(( enable_if(str_format_internal::EnsureConstexpr(format), - "Format string is not constexpr."), + "Format string is not constexpr."), enable_if(str_format_internal::ValidFormatImpl<C...>(format), "Format specified does not match the template arguments."))) #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/ya.make b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/ya.make index ff8069cd0f..2c855146e6 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/ya.make +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/str_format/ya.make @@ -2,15 +2,15 @@ LIBRARY() -OWNER( - somov - g:cpp-contrib -) +OWNER( + somov + g:cpp-contrib +) LICENSE(Apache-2.0) -LICENSE_TEXTS(.yandex_meta/licenses.list.txt) - +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + PEERDIR( contrib/restricted/abseil-cpp-tstring/y_absl/base contrib/restricted/abseil-cpp-tstring/y_absl/base/internal/raw_logging 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 237864c0ed..874f6f1d45 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 @@ -51,9 +51,9 @@ ABSL_NAMESPACE_BEGIN namespace strings_internal { // This class is implicitly constructible from everything that y_absl::string_view -// is implicitly constructible from, except for rvalue strings. This means it -// can be used as a function parameter in places where passing a temporary -// string might cause memory lifetime issues. +// is implicitly constructible from, except for rvalue strings. This means it +// can be used as a function parameter in places where passing a temporary +// string might cause memory lifetime issues. class ConvertibleToStringView { public: ConvertibleToStringView(const char* s) // NOLINT(runtime/explicit) @@ -65,8 +65,8 @@ class ConvertibleToStringView { : value_(s) {} // Disable conversion from rvalue strings. - ConvertibleToStringView(TString&& s) = delete; - ConvertibleToStringView(const TString&& s) = delete; + ConvertibleToStringView(TString&& s) = delete; + ConvertibleToStringView(const TString&& s) = delete; y_absl::string_view value() const { return value_; } @@ -251,11 +251,11 @@ struct SplitterIsConvertibleTo // the split strings: only strings for which the predicate returns true will be // kept. A Predicate object is any unary functor that takes an y_absl::string_view // and returns bool. -// -// The StringType parameter can be either string_view or string, depending on -// whether the Splitter refers to a string stored elsewhere, or if the string -// resides inside the Splitter itself. -template <typename Delimiter, typename Predicate, typename StringType> +// +// The StringType parameter can be either string_view or string, depending on +// whether the Splitter refers to a string stored elsewhere, or if the string +// resides inside the Splitter itself. +template <typename Delimiter, typename Predicate, typename StringType> class Splitter { public: using DelimiterType = Delimiter; @@ -263,12 +263,12 @@ class Splitter { using const_iterator = strings_internal::SplitIterator<Splitter>; using value_type = typename std::iterator_traits<const_iterator>::value_type; - Splitter(StringType input_text, Delimiter d, Predicate p) + Splitter(StringType input_text, Delimiter d, Predicate p) : text_(std::move(input_text)), delimiter_(std::move(d)), predicate_(std::move(p)) {} - y_absl::string_view text() const { return text_; } + y_absl::string_view text() const { return text_; } const Delimiter& delimiter() const { return delimiter_; } const Predicate& predicate() const { return predicate_; } @@ -318,7 +318,7 @@ class Splitter { Container operator()(const Splitter& splitter) const { Container c; auto it = std::inserter(c, c.end()); - for (const auto& sp : splitter) { + for (const auto& sp : splitter) { *it++ = ValueType(sp); } return c; @@ -418,7 +418,7 @@ class Splitter { static iterator ToIter(iterator iter) { return iter; } }; - StringType text_; + StringType text_; Delimiter delimiter_; Predicate predicate_; }; diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/string_constant.h b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/string_constant.h index b18e821b49..df4fc0357e 100644 --- a/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/string_constant.h +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/strings/internal/string_constant.h @@ -1,64 +1,64 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ -#define ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ - -#include "y_absl/meta/type_traits.h" -#include "y_absl/strings/string_view.h" - -namespace y_absl { -ABSL_NAMESPACE_BEGIN -namespace strings_internal { - -// StringConstant<T> represents a compile time string constant. -// It can be accessed via its `y_absl::string_view value` static member. -// It is guaranteed that the `string_view` returned has constant `.data()`, -// constant `.size()` and constant `value[i]` for all `0 <= i < .size()` -// -// The `T` is an opaque type. It is guaranteed that different string constants -// will have different values of `T`. This allows users to associate the string -// constant with other static state at compile time. -// -// Instances should be made using the `MakeStringConstant()` factory function -// below. -template <typename T> -struct StringConstant { - static constexpr y_absl::string_view value = T{}(); - constexpr y_absl::string_view operator()() const { return value; } - - // Check to be sure `view` points to constant data. - // Otherwise, it can't be constant evaluated. - static_assert(value.empty() || 2 * value[0] != 1, - "The input string_view must point to constant data."); -}; - -template <typename T> -constexpr y_absl::string_view StringConstant<T>::value; // NOLINT - -// Factory function for `StringConstant` instances. -// It supports callables that have a constexpr default constructor and a -// constexpr operator(). -// It must return an `y_absl::string_view` or `const char*` pointing to constant -// data. This is validated at compile time. -template <typename T> -constexpr StringConstant<T> MakeStringConstant(T) { - return {}; -} - -} // namespace strings_internal -ABSL_NAMESPACE_END -} // namespace y_absl - -#endif // ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ +#define ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ + +#include "y_absl/meta/type_traits.h" +#include "y_absl/strings/string_view.h" + +namespace y_absl { +ABSL_NAMESPACE_BEGIN +namespace strings_internal { + +// StringConstant<T> represents a compile time string constant. +// It can be accessed via its `y_absl::string_view value` static member. +// It is guaranteed that the `string_view` returned has constant `.data()`, +// constant `.size()` and constant `value[i]` for all `0 <= i < .size()` +// +// The `T` is an opaque type. It is guaranteed that different string constants +// will have different values of `T`. This allows users to associate the string +// constant with other static state at compile time. +// +// Instances should be made using the `MakeStringConstant()` factory function +// below. +template <typename T> +struct StringConstant { + static constexpr y_absl::string_view value = T{}(); + constexpr y_absl::string_view operator()() const { return value; } + + // Check to be sure `view` points to constant data. + // Otherwise, it can't be constant evaluated. + static_assert(value.empty() || 2 * value[0] != 1, + "The input string_view must point to constant data."); +}; + +template <typename T> +constexpr y_absl::string_view StringConstant<T>::value; // NOLINT + +// Factory function for `StringConstant` instances. +// It supports callables that have a constexpr default constructor and a +// constexpr operator(). +// It must return an `y_absl::string_view` or `const char*` pointing to constant +// data. This is validated at compile time. +template <typename T> +constexpr StringConstant<T> MakeStringConstant(T) { + return {}; +} + +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ |