merge(3p/absl): subtree merge of Abseil up to e19260f
... notably, this includes Abseil's own StatusOr type, which conflicted with our implementation (that was taken from TensorFlow). Change-Id: Ie7d6764b64055caaeb8dc7b6b9d066291e6b538f
This commit is contained in:
parent
cc27324d02
commit
082c006c04
854 changed files with 11260 additions and 5296 deletions
|
|
@ -69,6 +69,61 @@ TEST(BigUnsigned, ShiftLeft) {
|
|||
// And we should have fully rotated all bits off by now:
|
||||
EXPECT_EQ(a, BigUnsigned<84>(0u));
|
||||
}
|
||||
{
|
||||
// Bit shifting large and small numbers by large and small offsets.
|
||||
// Intended to exercise bounds-checking corner on ShiftLeft() (directly
|
||||
// and under asan).
|
||||
|
||||
// 2**(32*84)-1
|
||||
const BigUnsigned<84> all_bits_one(
|
||||
"1474444211396924248063325089479706787923460402125687709454567433186613"
|
||||
"6228083464060749874845919674257665016359189106695900028098437021384227"
|
||||
"3285029708032466536084583113729486015826557532750465299832071590813090"
|
||||
"2011853039837649252477307070509704043541368002938784757296893793903797"
|
||||
"8180292336310543540677175225040919704702800559606097685920595947397024"
|
||||
"8303316808753252115729411497720357971050627997031988036134171378490368"
|
||||
"6008000778741115399296162550786288457245180872759047016734959330367829"
|
||||
"5235612397427686310674725251378116268607113017720538636924549612987647"
|
||||
"5767411074510311386444547332882472126067840027882117834454260409440463"
|
||||
"9345147252664893456053258463203120637089916304618696601333953616715125"
|
||||
"2115882482473279040772264257431663818610405673876655957323083702713344"
|
||||
"4201105427930770976052393421467136557055");
|
||||
const BigUnsigned<84> zero(0u);
|
||||
const BigUnsigned<84> one(1u);
|
||||
// in bounds shifts
|
||||
for (int i = 1; i < 84*32; ++i) {
|
||||
// shifting all_bits_one to the left should result in a smaller number,
|
||||
// since the high bits rotate off and the low bits are replaced with
|
||||
// zeroes.
|
||||
BigUnsigned<84> big_shifted = all_bits_one;
|
||||
big_shifted.ShiftLeft(i);
|
||||
EXPECT_GT(all_bits_one, big_shifted);
|
||||
// Shifting 1 to the left should instead result in a larger number.
|
||||
BigUnsigned<84> small_shifted = one;
|
||||
small_shifted.ShiftLeft(i);
|
||||
EXPECT_LT(one, small_shifted);
|
||||
}
|
||||
// Shifting by zero or a negative number has no effect
|
||||
for (int no_op_shift : {0, -1, -84 * 32, std::numeric_limits<int>::min()}) {
|
||||
BigUnsigned<84> big_shifted = all_bits_one;
|
||||
big_shifted.ShiftLeft(no_op_shift);
|
||||
EXPECT_EQ(all_bits_one, big_shifted);
|
||||
BigUnsigned<84> small_shifted = one;
|
||||
big_shifted.ShiftLeft(no_op_shift);
|
||||
EXPECT_EQ(one, small_shifted);
|
||||
}
|
||||
// Shifting by an amount greater than the number of bits should result in
|
||||
// zero.
|
||||
for (int out_of_bounds_shift :
|
||||
{84 * 32, 84 * 32 + 1, std::numeric_limits<int>::max()}) {
|
||||
BigUnsigned<84> big_shifted = all_bits_one;
|
||||
big_shifted.ShiftLeft(out_of_bounds_shift);
|
||||
EXPECT_EQ(zero, big_shifted);
|
||||
BigUnsigned<84> small_shifted = one;
|
||||
small_shifted.ShiftLeft(out_of_bounds_shift);
|
||||
EXPECT_EQ(zero, small_shifted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BigUnsigned, MultiplyByUint32) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
std::size_t 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 @@ std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits,
|
|||
*dropped_nonzero_digit = true;
|
||||
}
|
||||
*out = accumulator;
|
||||
return begin - original_begin;
|
||||
return static_cast<int>(begin - original_begin);
|
||||
}
|
||||
|
||||
// Returns true if `v` is one of the chars allowed inside parentheses following
|
||||
|
|
@ -372,7 +372,7 @@ strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end,
|
|||
|
||||
int exponent_adjustment = 0;
|
||||
bool mantissa_is_inexact = false;
|
||||
std::size_t 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;
|
||||
}
|
||||
std::size_t zeros_skipped = 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);
|
||||
}
|
||||
std::size_t post_decimal_digits = ConsumeDigits<base>(
|
||||
int post_decimal_digits = ConsumeDigits<base>(
|
||||
begin, end, digits_left, &mantissa, &mantissa_is_inexact);
|
||||
begin += post_decimal_digits;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/internal/invoke.h"
|
||||
#include "absl/container/internal/compressed_tuple.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
|
|
@ -31,14 +33,17 @@ namespace cord_internal {
|
|||
// Wraps std::atomic for reference counting.
|
||||
class Refcount {
|
||||
public:
|
||||
Refcount() : count_{1} {}
|
||||
~Refcount() {}
|
||||
constexpr Refcount() : count_{kRefIncrement} {}
|
||||
struct Immortal {};
|
||||
explicit constexpr Refcount(Immortal) : count_(kImmortalTag) {}
|
||||
|
||||
// Increments the reference count by 1. Imposes no memory ordering.
|
||||
inline void Increment() { count_.fetch_add(1, 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 by 1.
|
||||
// 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
|
||||
|
|
@ -46,19 +51,24 @@ class Refcount {
|
|||
// false.
|
||||
inline bool Decrement() {
|
||||
int32_t refcount = count_.load(std::memory_order_acquire);
|
||||
assert(refcount > 0);
|
||||
return refcount != 1 && count_.fetch_sub(1, std::memory_order_acq_rel) != 1;
|
||||
assert(refcount > 0 || refcount & kImmortalTag);
|
||||
return refcount != kRefIncrement &&
|
||||
count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) !=
|
||||
kRefIncrement;
|
||||
}
|
||||
|
||||
// Same as Decrement but expect that refcount is greater than 1.
|
||||
inline bool DecrementExpectHighRefcount() {
|
||||
int32_t refcount = count_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
assert(refcount > 0);
|
||||
return refcount != 1;
|
||||
int32_t refcount =
|
||||
count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel);
|
||||
assert(refcount > 0 || refcount & kImmortalTag);
|
||||
return refcount != kRefIncrement;
|
||||
}
|
||||
|
||||
// Returns the current reference count using acquire semantics.
|
||||
inline int32_t Get() const { return count_.load(std::memory_order_acquire); }
|
||||
inline int32_t Get() const {
|
||||
return count_.load(std::memory_order_acquire) >> kImmortalShift;
|
||||
}
|
||||
|
||||
// Returns whether the atomic integer is 1.
|
||||
// If the reference count is used in the conventional way, a
|
||||
|
|
@ -68,9 +78,27 @@ class Refcount {
|
|||
// performs the memory barrier needed for the owning thread
|
||||
// to act on the object, knowing that it has exclusive access to the
|
||||
// object.
|
||||
inline bool IsOne() { return count_.load(std::memory_order_acquire) == 1; }
|
||||
inline bool IsOne() {
|
||||
return count_.load(std::memory_order_acquire) == kRefIncrement;
|
||||
}
|
||||
|
||||
bool IsImmortal() const {
|
||||
return (count_.load(std::memory_order_relaxed) & kImmortalTag) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
// We reserve the bottom bit to tag a reference count as immortal.
|
||||
// By making it `1` we ensure that we never reach `0` when adding/subtracting
|
||||
// `2`, thus it never looks as if it should be destroyed.
|
||||
// These are used for the StringConstant constructor where we do not increase
|
||||
// the refcount at construction time (due to constinit requirements) but we
|
||||
// will still decrease it at destruction time to avoid branching on Unref.
|
||||
enum {
|
||||
kImmortalShift = 1,
|
||||
kRefIncrement = 1 << kImmortalShift,
|
||||
kImmortalTag = kRefIncrement - 1
|
||||
};
|
||||
|
||||
std::atomic<int32_t> count_;
|
||||
};
|
||||
|
||||
|
|
@ -83,7 +111,22 @@ struct CordRepConcat;
|
|||
struct CordRepSubstring;
|
||||
struct CordRepExternal;
|
||||
|
||||
// Various representations that we allow
|
||||
enum CordRepKind {
|
||||
CONCAT = 0,
|
||||
EXTERNAL = 1,
|
||||
SUBSTRING = 2,
|
||||
|
||||
// We have different tags for different sized flat arrays,
|
||||
// starting with FLAT
|
||||
FLAT = 3,
|
||||
};
|
||||
|
||||
struct CordRep {
|
||||
CordRep() = default;
|
||||
constexpr CordRep(Refcount::Immortal immortal, size_t l)
|
||||
: length(l), refcount(immortal), tag(EXTERNAL), data{} {}
|
||||
|
||||
// The following three fields have to be less than 32 bytes since
|
||||
// that is the smallest supported flat node size.
|
||||
size_t length;
|
||||
|
|
@ -114,35 +157,112 @@ struct CordRepSubstring : public CordRep {
|
|||
CordRep* child;
|
||||
};
|
||||
|
||||
// TODO(strel): replace the following logic (and related functions in cord.cc)
|
||||
// with container_internal::Layout.
|
||||
|
||||
// Alignment requirement for CordRepExternal so that the type erased releaser
|
||||
// will be stored at a suitably aligned address.
|
||||
constexpr size_t ExternalRepAlignment() {
|
||||
#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
return __STDCPP_DEFAULT_NEW_ALIGNMENT__;
|
||||
#else
|
||||
return alignof(max_align_t);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Type for function pointer that will invoke and destroy the type-erased
|
||||
// releaser function object. Accepts a pointer to the releaser and the
|
||||
// `string_view` that were passed in to `NewExternalRep` below. The return value
|
||||
// is the size of the `Releaser` type.
|
||||
using ExternalReleaserInvoker = size_t (*)(void*, absl::string_view);
|
||||
// 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 alignas(ExternalRepAlignment()) CordRepExternal : public CordRep {
|
||||
struct CordRepExternal : public CordRep {
|
||||
CordRepExternal() = default;
|
||||
explicit constexpr CordRepExternal(absl::string_view str)
|
||||
: CordRep(Refcount::Immortal{}, str.size()),
|
||||
base(str.data()),
|
||||
releaser_invoker(nullptr) {}
|
||||
|
||||
const char* base;
|
||||
// Pointer to function that knows how to call and destroy the releaser.
|
||||
ExternalReleaserInvoker releaser_invoker;
|
||||
};
|
||||
|
||||
// TODO(strel): look into removing, it doesn't seem like anything relies on this
|
||||
static_assert(sizeof(CordRepConcat) == sizeof(CordRepSubstring), "");
|
||||
struct Rank1 {};
|
||||
struct Rank0 : Rank1 {};
|
||||
|
||||
template <typename Releaser, typename = ::absl::base_internal::invoke_result_t<
|
||||
Releaser, absl::string_view>>
|
||||
void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) {
|
||||
::absl::base_internal::invoke(std::forward<Releaser>(releaser), data);
|
||||
}
|
||||
|
||||
template <typename Releaser,
|
||||
typename = ::absl::base_internal::invoke_result_t<Releaser>>
|
||||
void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) {
|
||||
::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 ::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>()),
|
||||
absl::string_view(base, length));
|
||||
}
|
||||
|
||||
static void Release(CordRepExternal* rep) {
|
||||
delete static_cast<CordRepExternalImpl*>(rep);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Str>
|
||||
struct ConstInitExternalStorage {
|
||||
ABSL_CONST_INIT static CordRepExternal value;
|
||||
};
|
||||
|
||||
template <typename Str>
|
||||
CordRepExternal ConstInitExternalStorage<Str>::value(Str::value);
|
||||
|
||||
enum {
|
||||
kMaxInline = 15,
|
||||
// Tag byte & kMaxInline means we are storing a pointer.
|
||||
kTreeFlag = 1 << 4,
|
||||
// Tag byte & kProfiledFlag means we are profiling the Cord.
|
||||
kProfiledFlag = 1 << 5
|
||||
};
|
||||
|
||||
// If the data has length <= kMaxInline, we store it in `as_chars`, and
|
||||
// store the size in `tagged_size`.
|
||||
// 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`.
|
||||
struct AsTree {
|
||||
absl::cord_internal::CordRep* rep;
|
||||
char padding[kMaxInline + 1 - sizeof(absl::cord_internal::CordRep*) - 1];
|
||||
char tagged_size;
|
||||
};
|
||||
|
||||
constexpr char GetOrNull(absl::string_view data, size_t pos) {
|
||||
return pos < data.size() ? data[pos] : '\0';
|
||||
}
|
||||
|
||||
union InlineData {
|
||||
constexpr InlineData() : as_chars{} {}
|
||||
explicit constexpr InlineData(AsTree tree) : as_tree(tree) {}
|
||||
explicit constexpr InlineData(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())} {}
|
||||
|
||||
AsTree as_tree;
|
||||
char as_chars[kMaxInline + 1];
|
||||
};
|
||||
static_assert(sizeof(InlineData) == kMaxInline + 1, "");
|
||||
static_assert(sizeof(AsTree) == sizeof(InlineData), "");
|
||||
static_assert(offsetof(AsTree, tagged_size) == kMaxInline, "");
|
||||
|
||||
} // namespace cord_internal
|
||||
ABSL_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
//
|
||||
// POSIX spec:
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
|
||||
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
|
||||
|
||||
|
|
@ -25,10 +39,12 @@ class Cord;
|
|||
class FormatCountCapture;
|
||||
class FormatSink;
|
||||
|
||||
namespace str_format_internal {
|
||||
|
||||
template <absl::FormatConversionCharSet C>
|
||||
struct FormatConvertResult;
|
||||
class FormatConversionSpec;
|
||||
|
||||
namespace str_format_internal {
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct HasUserDefinedConvert : std::false_type {};
|
||||
|
||||
|
|
@ -39,6 +55,22 @@ struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert(
|
|||
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 =
|
||||
absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>;
|
||||
using FormatSinkT =
|
||||
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;
|
||||
|
||||
|
|
@ -46,6 +78,13 @@ class StreamedWrapper;
|
|||
// 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'.
|
||||
|
||||
// Raw pointers.
|
||||
struct VoidPtr {
|
||||
VoidPtr() = default;
|
||||
|
|
@ -61,6 +100,11 @@ struct ArgConvertResult {
|
|||
bool value;
|
||||
};
|
||||
|
||||
template <FormatConversionCharSet C>
|
||||
constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) {
|
||||
return C;
|
||||
}
|
||||
|
||||
template <FormatConversionCharSet C>
|
||||
constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) {
|
||||
return C;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@
|
|||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/strings/internal/str_format/arg.h"
|
||||
|
||||
#include <ostream>
|
||||
|
|
@ -23,8 +29,17 @@ class FormatArgImplTest : public ::testing::Test {
|
|||
enum Color { kRed, kGreen, kBlue };
|
||||
|
||||
static const char *hi() { return "hi"; }
|
||||
|
||||
struct X {};
|
||||
|
||||
X x_;
|
||||
};
|
||||
|
||||
inline FormatConvertResult<FormatConversionCharSet{}> AbslFormatConvert(
|
||||
const FormatArgImplTest::X &, const FormatConversionSpec &, FormatSink *) {
|
||||
return {false};
|
||||
}
|
||||
|
||||
TEST_F(FormatArgImplTest, ToInt) {
|
||||
int out = 0;
|
||||
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out));
|
||||
|
|
@ -59,6 +74,7 @@ TEST_F(FormatArgImplTest, ToInt) {
|
|||
FormatArgImpl(static_cast<int *>(nullptr)), &out));
|
||||
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out));
|
||||
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out));
|
||||
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(x_), &out));
|
||||
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out));
|
||||
EXPECT_EQ(2, out);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#include "absl/strings/internal/str_format/bind.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
|
@ -221,7 +235,7 @@ int FprintF(std::FILE* output, const UntypedFormatSpecImpl format,
|
|||
errno = sink.error();
|
||||
return -1;
|
||||
}
|
||||
if (sink.count() > std::numeric_limits<int>::max()) {
|
||||
if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
||||
errno = EFBIG;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
|
||||
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
|
||||
|
||||
|
|
@ -119,10 +133,11 @@ class FormatSpecTemplate
|
|||
|
||||
#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
|
||||
|
||||
template <FormatConversionCharSet... C,
|
||||
typename = typename std::enable_if<
|
||||
AllOf(sizeof...(C) == sizeof...(Args), 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) {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#include "absl/strings/internal/str_format/bind.h"
|
||||
|
||||
#include <string.h>
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
|
||||
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -12,6 +26,7 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/strings/internal/str_format/bind.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace absl {
|
||||
|
|
@ -19,6 +34,13 @@ ABSL_NAMESPACE_BEGIN
|
|||
namespace str_format_internal {
|
||||
namespace {
|
||||
|
||||
struct NativePrintfTraits {
|
||||
bool hex_float_has_glibc_rounding;
|
||||
bool hex_float_prefers_denormal_repr;
|
||||
bool hex_float_uses_minimal_precision_when_not_specified;
|
||||
bool hex_float_optimizes_leading_digit_bit_count;
|
||||
};
|
||||
|
||||
template <typename T, size_t N>
|
||||
size_t ArraySize(T (&)[N]) {
|
||||
return N;
|
||||
|
|
@ -118,6 +140,63 @@ std::string StrPrint(const char *format, ...) {
|
|||
return result;
|
||||
}
|
||||
|
||||
NativePrintfTraits VerifyNativeImplementationImpl() {
|
||||
NativePrintfTraits result;
|
||||
|
||||
// >>> hex_float_has_glibc_rounding. To have glibc's rounding behavior we need
|
||||
// to meet three requirements:
|
||||
//
|
||||
// - The threshold for rounding up is 8 (for e.g. MSVC uses 9).
|
||||
// - If the digits lower than than the 8 are non-zero then we round up.
|
||||
// - If the digits lower than the 8 are all zero then we round toward even.
|
||||
//
|
||||
// The numbers below represent all the cases covering {below,at,above} the
|
||||
// threshold (8) with both {zero,non-zero} lower bits and both {even,odd}
|
||||
// preceding digits.
|
||||
const double d0079 = 65657.0; // 0x1.0079p+16
|
||||
const double d0179 = 65913.0; // 0x1.0179p+16
|
||||
const double d0080 = 65664.0; // 0x1.0080p+16
|
||||
const double d0180 = 65920.0; // 0x1.0180p+16
|
||||
const double d0081 = 65665.0; // 0x1.0081p+16
|
||||
const double d0181 = 65921.0; // 0x1.0181p+16
|
||||
result.hex_float_has_glibc_rounding =
|
||||
StartsWith(StrPrint("%.2a", d0079), "0x1.00") &&
|
||||
StartsWith(StrPrint("%.2a", d0179), "0x1.01") &&
|
||||
StartsWith(StrPrint("%.2a", d0080), "0x1.00") &&
|
||||
StartsWith(StrPrint("%.2a", d0180), "0x1.02") &&
|
||||
StartsWith(StrPrint("%.2a", d0081), "0x1.01") &&
|
||||
StartsWith(StrPrint("%.2a", d0181), "0x1.02");
|
||||
|
||||
// >>> hex_float_prefers_denormal_repr. Formatting `denormal` on glibc yields
|
||||
// "0x0.0000000000001p-1022", whereas on std libs that don't use denormal
|
||||
// representation it would either be 0x1p-1074 or 0x1.0000000000000-1074.
|
||||
const double denormal = std::numeric_limits<double>::denorm_min();
|
||||
result.hex_float_prefers_denormal_repr =
|
||||
StartsWith(StrPrint("%a", denormal), "0x0.0000000000001");
|
||||
|
||||
// >>> hex_float_uses_minimal_precision_when_not_specified. Some (non-glibc)
|
||||
// libs will format the following as "0x1.0079000000000p+16".
|
||||
result.hex_float_uses_minimal_precision_when_not_specified =
|
||||
(StrPrint("%a", d0079) == "0x1.0079p+16");
|
||||
|
||||
// >>> hex_float_optimizes_leading_digit_bit_count. The number 1.5, when
|
||||
// formatted by glibc should yield "0x1.8p+0" for `double` and "0xcp-3" for
|
||||
// `long double`, i.e., number of bits in the leading digit is adapted to the
|
||||
// number of bits in the mantissa.
|
||||
const double d_15 = 1.5;
|
||||
const long double ld_15 = 1.5;
|
||||
result.hex_float_optimizes_leading_digit_bit_count =
|
||||
StartsWith(StrPrint("%a", d_15), "0x1.8") &&
|
||||
StartsWith(StrPrint("%La", ld_15), "0xc");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const NativePrintfTraits &VerifyNativeImplementation() {
|
||||
static NativePrintfTraits native_traits = VerifyNativeImplementationImpl();
|
||||
return native_traits;
|
||||
}
|
||||
|
||||
class FormatConvertTest : public ::testing::Test { };
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -474,6 +553,68 @@ TEST_F(FormatConvertTest, Uint128) {
|
|||
}
|
||||
}
|
||||
|
||||
template <typename Floating>
|
||||
void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats) {
|
||||
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
|
||||
// Reserve the space to ensure we don't allocate memory in the output itself.
|
||||
std::string str_format_result;
|
||||
str_format_result.reserve(1 << 20);
|
||||
std::string string_printf_result;
|
||||
string_printf_result.reserve(1 << 20);
|
||||
|
||||
const char *const kFormats[] = {
|
||||
"%", "%.3", "%8.5", "%500", "%.5000", "%.60", "%.30", "%03",
|
||||
"%+", "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"};
|
||||
|
||||
for (const char *fmt : kFormats) {
|
||||
for (char f : {'f', 'F', //
|
||||
'g', 'G', //
|
||||
'a', 'A', //
|
||||
'e', 'E'}) {
|
||||
std::string fmt_str = std::string(fmt) + f;
|
||||
|
||||
if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F' &&
|
||||
f != 'a' && f != 'A') {
|
||||
// This particular test takes way too long with snprintf.
|
||||
// Disable for the case we are not implementing natively.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((f == 'a' || f == 'A') &&
|
||||
!native_traits.hex_float_has_glibc_rounding) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Floating d : floats) {
|
||||
if (!native_traits.hex_float_prefers_denormal_repr &&
|
||||
(f == 'a' || f == 'A') && std::fpclassify(d) == FP_SUBNORMAL) {
|
||||
continue;
|
||||
}
|
||||
int i = -10;
|
||||
FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
|
||||
UntypedFormatSpecImpl format(fmt_str);
|
||||
|
||||
string_printf_result.clear();
|
||||
StrAppend(&string_printf_result, fmt_str.c_str(), d, i);
|
||||
str_format_result.clear();
|
||||
|
||||
{
|
||||
AppendPack(&str_format_result, format, absl::MakeSpan(args));
|
||||
}
|
||||
|
||||
if (string_printf_result != str_format_result) {
|
||||
// We use ASSERT_EQ here because failures are usually correlated and a
|
||||
// bug would print way too many failed expectations causing the test
|
||||
// to time out.
|
||||
ASSERT_EQ(string_printf_result, str_format_result)
|
||||
<< fmt_str << " " << StrPrint("%.18g", d) << " "
|
||||
<< StrPrint("%a", d) << " " << StrPrint("%.50f", d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(FormatConvertTest, Float) {
|
||||
#ifdef _MSC_VER
|
||||
// MSVC has a different rounding policy than us so we can't test our
|
||||
|
|
@ -481,9 +622,62 @@ TEST_F(FormatConvertTest, Float) {
|
|||
return;
|
||||
#endif // _MSC_VER
|
||||
|
||||
const char *const kFormats[] = {
|
||||
"%", "%.3", "%8.5", "%500", "%.5000", "%.60", "%.30", "%03",
|
||||
"%+", "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"};
|
||||
std::vector<float> floats = {0.0f,
|
||||
-0.0f,
|
||||
.9999999f,
|
||||
9999999.f,
|
||||
std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::min(),
|
||||
-std::numeric_limits<float>::min(),
|
||||
std::numeric_limits<float>::lowest(),
|
||||
-std::numeric_limits<float>::lowest(),
|
||||
std::numeric_limits<float>::epsilon(),
|
||||
std::numeric_limits<float>::epsilon() + 1.0f,
|
||||
std::numeric_limits<float>::infinity(),
|
||||
-std::numeric_limits<float>::infinity()};
|
||||
|
||||
// Some regression tests.
|
||||
floats.push_back(0.999999989f);
|
||||
|
||||
if (std::numeric_limits<float>::has_denorm != std::denorm_absent) {
|
||||
floats.push_back(std::numeric_limits<float>::denorm_min());
|
||||
floats.push_back(-std::numeric_limits<float>::denorm_min());
|
||||
}
|
||||
|
||||
for (float base :
|
||||
{1.f, 12.f, 123.f, 1234.f, 12345.f, 123456.f, 1234567.f, 12345678.f,
|
||||
123456789.f, 1234567890.f, 12345678901.f, 12345678.f, 12345678.f}) {
|
||||
for (int exp = -123; exp <= 123; ++exp) {
|
||||
for (int sign : {1, -1}) {
|
||||
floats.push_back(sign * std::ldexp(base, exp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int exp = -300; exp <= 300; ++exp) {
|
||||
const float all_ones_mantissa = 0xffffff;
|
||||
floats.push_back(std::ldexp(all_ones_mantissa, exp));
|
||||
}
|
||||
|
||||
// Remove duplicates to speed up the logic below.
|
||||
std::sort(floats.begin(), floats.end());
|
||||
floats.erase(std::unique(floats.begin(), floats.end()), floats.end());
|
||||
|
||||
#ifndef __APPLE__
|
||||
// Apple formats NaN differently (+nan) vs. (nan)
|
||||
floats.push_back(std::nan(""));
|
||||
#endif
|
||||
|
||||
TestWithMultipleFormatsHelper(floats);
|
||||
}
|
||||
|
||||
TEST_F(FormatConvertTest, Double) {
|
||||
#ifdef _MSC_VER
|
||||
// MSVC has a different rounding policy than us so we can't test our
|
||||
// implementation against the native one there.
|
||||
return;
|
||||
#endif // _MSC_VER
|
||||
|
||||
std::vector<double> doubles = {0.0,
|
||||
-0.0,
|
||||
|
|
@ -554,52 +748,10 @@ TEST_F(FormatConvertTest, Float) {
|
|||
doubles.push_back(std::nan(""));
|
||||
#endif
|
||||
|
||||
// Reserve the space to ensure we don't allocate memory in the output itself.
|
||||
std::string str_format_result;
|
||||
str_format_result.reserve(1 << 20);
|
||||
std::string string_printf_result;
|
||||
string_printf_result.reserve(1 << 20);
|
||||
|
||||
for (const char *fmt : kFormats) {
|
||||
for (char f : {'f', 'F', //
|
||||
'g', 'G', //
|
||||
'a', 'A', //
|
||||
'e', 'E'}) {
|
||||
std::string fmt_str = std::string(fmt) + f;
|
||||
|
||||
if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') {
|
||||
// This particular test takes way too long with snprintf.
|
||||
// Disable for the case we are not implementing natively.
|
||||
continue;
|
||||
}
|
||||
|
||||
for (double d : doubles) {
|
||||
int i = -10;
|
||||
FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
|
||||
UntypedFormatSpecImpl format(fmt_str);
|
||||
|
||||
string_printf_result.clear();
|
||||
StrAppend(&string_printf_result, fmt_str.c_str(), d, i);
|
||||
str_format_result.clear();
|
||||
|
||||
{
|
||||
AppendPack(&str_format_result, format, absl::MakeSpan(args));
|
||||
}
|
||||
|
||||
if (string_printf_result != str_format_result) {
|
||||
// We use ASSERT_EQ here because failures are usually correlated and a
|
||||
// bug would print way too many failed expectations causing the test
|
||||
// to time out.
|
||||
ASSERT_EQ(string_printf_result, str_format_result)
|
||||
<< fmt_str << " " << StrPrint("%.18g", d) << " "
|
||||
<< StrPrint("%a", d) << " " << StrPrint("%.1080f", d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TestWithMultipleFormatsHelper(doubles);
|
||||
}
|
||||
|
||||
TEST_F(FormatConvertTest, FloatRound) {
|
||||
TEST_F(FormatConvertTest, DoubleRound) {
|
||||
std::string s;
|
||||
const auto format = [&](const char *fmt, double d) -> std::string & {
|
||||
s.clear();
|
||||
|
|
@ -704,6 +856,193 @@ TEST_F(FormatConvertTest, FloatRound) {
|
|||
"1837869002408041296803276054561138153076171875");
|
||||
}
|
||||
|
||||
TEST_F(FormatConvertTest, DoubleRoundA) {
|
||||
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
|
||||
std::string s;
|
||||
const auto format = [&](const char *fmt, double d) -> std::string & {
|
||||
s.clear();
|
||||
FormatArgImpl args[1] = {FormatArgImpl(d)};
|
||||
AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
|
||||
if (native_traits.hex_float_has_glibc_rounding) {
|
||||
EXPECT_EQ(StrPrint(fmt, d), s);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
// 0x1.00018000p+100
|
||||
const double on_boundary_odd = 1267679614447900152596896153600.0;
|
||||
EXPECT_EQ(format("%.0a", on_boundary_odd), "0x1p+100");
|
||||
EXPECT_EQ(format("%.1a", on_boundary_odd), "0x1.0p+100");
|
||||
EXPECT_EQ(format("%.2a", on_boundary_odd), "0x1.00p+100");
|
||||
EXPECT_EQ(format("%.3a", on_boundary_odd), "0x1.000p+100");
|
||||
EXPECT_EQ(format("%.4a", on_boundary_odd), "0x1.0002p+100"); // round
|
||||
EXPECT_EQ(format("%.5a", on_boundary_odd), "0x1.00018p+100");
|
||||
EXPECT_EQ(format("%.6a", on_boundary_odd), "0x1.000180p+100");
|
||||
|
||||
// 0x1.00028000p-2
|
||||
const double on_boundary_even = 0.250009536743164062500;
|
||||
EXPECT_EQ(format("%.0a", on_boundary_even), "0x1p-2");
|
||||
EXPECT_EQ(format("%.1a", on_boundary_even), "0x1.0p-2");
|
||||
EXPECT_EQ(format("%.2a", on_boundary_even), "0x1.00p-2");
|
||||
EXPECT_EQ(format("%.3a", on_boundary_even), "0x1.000p-2");
|
||||
EXPECT_EQ(format("%.4a", on_boundary_even), "0x1.0002p-2"); // no round
|
||||
EXPECT_EQ(format("%.5a", on_boundary_even), "0x1.00028p-2");
|
||||
EXPECT_EQ(format("%.6a", on_boundary_even), "0x1.000280p-2");
|
||||
|
||||
// 0x1.00018001p+1
|
||||
const double slightly_over = 2.00004577683284878730773925781250;
|
||||
EXPECT_EQ(format("%.0a", slightly_over), "0x1p+1");
|
||||
EXPECT_EQ(format("%.1a", slightly_over), "0x1.0p+1");
|
||||
EXPECT_EQ(format("%.2a", slightly_over), "0x1.00p+1");
|
||||
EXPECT_EQ(format("%.3a", slightly_over), "0x1.000p+1");
|
||||
EXPECT_EQ(format("%.4a", slightly_over), "0x1.0002p+1");
|
||||
EXPECT_EQ(format("%.5a", slightly_over), "0x1.00018p+1");
|
||||
EXPECT_EQ(format("%.6a", slightly_over), "0x1.000180p+1");
|
||||
|
||||
// 0x1.00017fffp+0
|
||||
const double slightly_under = 1.000022887950763106346130371093750;
|
||||
EXPECT_EQ(format("%.0a", slightly_under), "0x1p+0");
|
||||
EXPECT_EQ(format("%.1a", slightly_under), "0x1.0p+0");
|
||||
EXPECT_EQ(format("%.2a", slightly_under), "0x1.00p+0");
|
||||
EXPECT_EQ(format("%.3a", slightly_under), "0x1.000p+0");
|
||||
EXPECT_EQ(format("%.4a", slightly_under), "0x1.0001p+0");
|
||||
EXPECT_EQ(format("%.5a", slightly_under), "0x1.00018p+0");
|
||||
EXPECT_EQ(format("%.6a", slightly_under), "0x1.000180p+0");
|
||||
EXPECT_EQ(format("%.7a", slightly_under), "0x1.0001800p+0");
|
||||
|
||||
// 0x1.1b3829ac28058p+3
|
||||
const double hex_value = 8.85060580848964661981881363317370414733886718750;
|
||||
EXPECT_EQ(format("%.0a", hex_value), "0x1p+3");
|
||||
EXPECT_EQ(format("%.1a", hex_value), "0x1.2p+3");
|
||||
EXPECT_EQ(format("%.2a", hex_value), "0x1.1bp+3");
|
||||
EXPECT_EQ(format("%.3a", hex_value), "0x1.1b4p+3");
|
||||
EXPECT_EQ(format("%.4a", hex_value), "0x1.1b38p+3");
|
||||
EXPECT_EQ(format("%.5a", hex_value), "0x1.1b383p+3");
|
||||
EXPECT_EQ(format("%.6a", hex_value), "0x1.1b382ap+3");
|
||||
EXPECT_EQ(format("%.7a", hex_value), "0x1.1b3829bp+3");
|
||||
EXPECT_EQ(format("%.8a", hex_value), "0x1.1b3829acp+3");
|
||||
EXPECT_EQ(format("%.9a", hex_value), "0x1.1b3829ac3p+3");
|
||||
EXPECT_EQ(format("%.10a", hex_value), "0x1.1b3829ac28p+3");
|
||||
EXPECT_EQ(format("%.11a", hex_value), "0x1.1b3829ac280p+3");
|
||||
EXPECT_EQ(format("%.12a", hex_value), "0x1.1b3829ac2806p+3");
|
||||
EXPECT_EQ(format("%.13a", hex_value), "0x1.1b3829ac28058p+3");
|
||||
EXPECT_EQ(format("%.14a", hex_value), "0x1.1b3829ac280580p+3");
|
||||
EXPECT_EQ(format("%.15a", hex_value), "0x1.1b3829ac2805800p+3");
|
||||
EXPECT_EQ(format("%.16a", hex_value), "0x1.1b3829ac28058000p+3");
|
||||
EXPECT_EQ(format("%.17a", hex_value), "0x1.1b3829ac280580000p+3");
|
||||
EXPECT_EQ(format("%.18a", hex_value), "0x1.1b3829ac2805800000p+3");
|
||||
EXPECT_EQ(format("%.19a", hex_value), "0x1.1b3829ac28058000000p+3");
|
||||
EXPECT_EQ(format("%.20a", hex_value), "0x1.1b3829ac280580000000p+3");
|
||||
EXPECT_EQ(format("%.21a", hex_value), "0x1.1b3829ac2805800000000p+3");
|
||||
|
||||
// 0x1.0818283848586p+3
|
||||
const double hex_value2 = 8.2529488658208371987257123691961169242858886718750;
|
||||
EXPECT_EQ(format("%.0a", hex_value2), "0x1p+3");
|
||||
EXPECT_EQ(format("%.1a", hex_value2), "0x1.1p+3");
|
||||
EXPECT_EQ(format("%.2a", hex_value2), "0x1.08p+3");
|
||||
EXPECT_EQ(format("%.3a", hex_value2), "0x1.082p+3");
|
||||
EXPECT_EQ(format("%.4a", hex_value2), "0x1.0818p+3");
|
||||
EXPECT_EQ(format("%.5a", hex_value2), "0x1.08183p+3");
|
||||
EXPECT_EQ(format("%.6a", hex_value2), "0x1.081828p+3");
|
||||
EXPECT_EQ(format("%.7a", hex_value2), "0x1.0818284p+3");
|
||||
EXPECT_EQ(format("%.8a", hex_value2), "0x1.08182838p+3");
|
||||
EXPECT_EQ(format("%.9a", hex_value2), "0x1.081828385p+3");
|
||||
EXPECT_EQ(format("%.10a", hex_value2), "0x1.0818283848p+3");
|
||||
EXPECT_EQ(format("%.11a", hex_value2), "0x1.08182838486p+3");
|
||||
EXPECT_EQ(format("%.12a", hex_value2), "0x1.081828384858p+3");
|
||||
EXPECT_EQ(format("%.13a", hex_value2), "0x1.0818283848586p+3");
|
||||
EXPECT_EQ(format("%.14a", hex_value2), "0x1.08182838485860p+3");
|
||||
EXPECT_EQ(format("%.15a", hex_value2), "0x1.081828384858600p+3");
|
||||
EXPECT_EQ(format("%.16a", hex_value2), "0x1.0818283848586000p+3");
|
||||
EXPECT_EQ(format("%.17a", hex_value2), "0x1.08182838485860000p+3");
|
||||
EXPECT_EQ(format("%.18a", hex_value2), "0x1.081828384858600000p+3");
|
||||
EXPECT_EQ(format("%.19a", hex_value2), "0x1.0818283848586000000p+3");
|
||||
EXPECT_EQ(format("%.20a", hex_value2), "0x1.08182838485860000000p+3");
|
||||
EXPECT_EQ(format("%.21a", hex_value2), "0x1.081828384858600000000p+3");
|
||||
}
|
||||
|
||||
TEST_F(FormatConvertTest, LongDoubleRoundA) {
|
||||
if (std::numeric_limits<long double>::digits % 4 != 0) {
|
||||
// This test doesn't really make sense to run on platforms where a long
|
||||
// double has a different mantissa size (mod 4) than Prod, since then the
|
||||
// leading digit will be formatted differently.
|
||||
return;
|
||||
}
|
||||
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
|
||||
std::string s;
|
||||
const auto format = [&](const char *fmt, long double d) -> std::string & {
|
||||
s.clear();
|
||||
FormatArgImpl args[1] = {FormatArgImpl(d)};
|
||||
AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
|
||||
if (native_traits.hex_float_has_glibc_rounding &&
|
||||
native_traits.hex_float_optimizes_leading_digit_bit_count) {
|
||||
EXPECT_EQ(StrPrint(fmt, d), s);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
// 0x8.8p+4
|
||||
const long double on_boundary_even = 136.0;
|
||||
EXPECT_EQ(format("%.0La", on_boundary_even), "0x8p+4");
|
||||
EXPECT_EQ(format("%.1La", on_boundary_even), "0x8.8p+4");
|
||||
EXPECT_EQ(format("%.2La", on_boundary_even), "0x8.80p+4");
|
||||
EXPECT_EQ(format("%.3La", on_boundary_even), "0x8.800p+4");
|
||||
EXPECT_EQ(format("%.4La", on_boundary_even), "0x8.8000p+4");
|
||||
EXPECT_EQ(format("%.5La", on_boundary_even), "0x8.80000p+4");
|
||||
EXPECT_EQ(format("%.6La", on_boundary_even), "0x8.800000p+4");
|
||||
|
||||
// 0x9.8p+4
|
||||
const long double on_boundary_odd = 152.0;
|
||||
EXPECT_EQ(format("%.0La", on_boundary_odd), "0xap+4");
|
||||
EXPECT_EQ(format("%.1La", on_boundary_odd), "0x9.8p+4");
|
||||
EXPECT_EQ(format("%.2La", on_boundary_odd), "0x9.80p+4");
|
||||
EXPECT_EQ(format("%.3La", on_boundary_odd), "0x9.800p+4");
|
||||
EXPECT_EQ(format("%.4La", on_boundary_odd), "0x9.8000p+4");
|
||||
EXPECT_EQ(format("%.5La", on_boundary_odd), "0x9.80000p+4");
|
||||
EXPECT_EQ(format("%.6La", on_boundary_odd), "0x9.800000p+4");
|
||||
|
||||
// 0x8.80001p+24
|
||||
const long double slightly_over = 142606352.0;
|
||||
EXPECT_EQ(format("%.0La", slightly_over), "0x9p+24");
|
||||
EXPECT_EQ(format("%.1La", slightly_over), "0x8.8p+24");
|
||||
EXPECT_EQ(format("%.2La", slightly_over), "0x8.80p+24");
|
||||
EXPECT_EQ(format("%.3La", slightly_over), "0x8.800p+24");
|
||||
EXPECT_EQ(format("%.4La", slightly_over), "0x8.8000p+24");
|
||||
EXPECT_EQ(format("%.5La", slightly_over), "0x8.80001p+24");
|
||||
EXPECT_EQ(format("%.6La", slightly_over), "0x8.800010p+24");
|
||||
|
||||
// 0x8.7ffffp+24
|
||||
const long double slightly_under = 142606320.0;
|
||||
EXPECT_EQ(format("%.0La", slightly_under), "0x8p+24");
|
||||
EXPECT_EQ(format("%.1La", slightly_under), "0x8.8p+24");
|
||||
EXPECT_EQ(format("%.2La", slightly_under), "0x8.80p+24");
|
||||
EXPECT_EQ(format("%.3La", slightly_under), "0x8.800p+24");
|
||||
EXPECT_EQ(format("%.4La", slightly_under), "0x8.8000p+24");
|
||||
EXPECT_EQ(format("%.5La", slightly_under), "0x8.7ffffp+24");
|
||||
EXPECT_EQ(format("%.6La", slightly_under), "0x8.7ffff0p+24");
|
||||
EXPECT_EQ(format("%.7La", slightly_under), "0x8.7ffff00p+24");
|
||||
|
||||
// 0xc.0828384858688000p+128
|
||||
const long double eights = 4094231060438608800781871108094404067328.0;
|
||||
EXPECT_EQ(format("%.0La", eights), "0xcp+128");
|
||||
EXPECT_EQ(format("%.1La", eights), "0xc.1p+128");
|
||||
EXPECT_EQ(format("%.2La", eights), "0xc.08p+128");
|
||||
EXPECT_EQ(format("%.3La", eights), "0xc.083p+128");
|
||||
EXPECT_EQ(format("%.4La", eights), "0xc.0828p+128");
|
||||
EXPECT_EQ(format("%.5La", eights), "0xc.08284p+128");
|
||||
EXPECT_EQ(format("%.6La", eights), "0xc.082838p+128");
|
||||
EXPECT_EQ(format("%.7La", eights), "0xc.0828385p+128");
|
||||
EXPECT_EQ(format("%.8La", eights), "0xc.08283848p+128");
|
||||
EXPECT_EQ(format("%.9La", eights), "0xc.082838486p+128");
|
||||
EXPECT_EQ(format("%.10La", eights), "0xc.0828384858p+128");
|
||||
EXPECT_EQ(format("%.11La", eights), "0xc.08283848587p+128");
|
||||
EXPECT_EQ(format("%.12La", eights), "0xc.082838485868p+128");
|
||||
EXPECT_EQ(format("%.13La", eights), "0xc.0828384858688p+128");
|
||||
EXPECT_EQ(format("%.14La", eights), "0xc.08283848586880p+128");
|
||||
EXPECT_EQ(format("%.15La", eights), "0xc.082838485868800p+128");
|
||||
EXPECT_EQ(format("%.16La", eights), "0xc.0828384858688000p+128");
|
||||
}
|
||||
|
||||
// We don't actually store the results. This is just to exercise the rest of the
|
||||
// machinery.
|
||||
struct NullSink {
|
||||
|
|
@ -735,6 +1074,7 @@ TEST_F(FormatConvertTest, LongDouble) {
|
|||
// implementation against the native one there.
|
||||
return;
|
||||
#endif // _MSC_VER
|
||||
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
|
||||
const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000",
|
||||
"%.60", "%+", "% ", "%-10"};
|
||||
|
||||
|
|
@ -777,12 +1117,20 @@ TEST_F(FormatConvertTest, LongDouble) {
|
|||
'e', 'E'}) {
|
||||
std::string fmt_str = std::string(fmt) + 'L' + f;
|
||||
|
||||
if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') {
|
||||
if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F' &&
|
||||
f != 'a' && f != 'A') {
|
||||
// This particular test takes way too long with snprintf.
|
||||
// Disable for the case we are not implementing natively.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f == 'a' || f == 'A') {
|
||||
if (!native_traits.hex_float_has_glibc_rounding ||
|
||||
!native_traits.hex_float_optimizes_leading_digit_bit_count) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto d : doubles) {
|
||||
FormatArgImpl arg(d);
|
||||
UntypedFormatSpecImpl format(fmt_str);
|
||||
|
|
@ -797,7 +1145,8 @@ TEST_F(FormatConvertTest, LongDouble) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(FormatConvertTest, IntAsFloat) {
|
||||
TEST_F(FormatConvertTest, IntAsDouble) {
|
||||
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
|
||||
const int kMin = std::numeric_limits<int>::min();
|
||||
const int kMax = std::numeric_limits<int>::max();
|
||||
const int ia[] = {
|
||||
|
|
@ -813,14 +1162,17 @@ TEST_F(FormatConvertTest, IntAsFloat) {
|
|||
const char *fmt;
|
||||
};
|
||||
const double dx = static_cast<double>(fx);
|
||||
const Expectation kExpect[] = {
|
||||
{ __LINE__, StrPrint("%f", dx), "%f" },
|
||||
{ __LINE__, StrPrint("%12f", dx), "%12f" },
|
||||
{ __LINE__, StrPrint("%.12f", dx), "%.12f" },
|
||||
{ __LINE__, StrPrint("%12a", dx), "%12a" },
|
||||
{ __LINE__, StrPrint("%.12a", dx), "%.12a" },
|
||||
std::vector<Expectation> expect = {
|
||||
{__LINE__, StrPrint("%f", dx), "%f"},
|
||||
{__LINE__, StrPrint("%12f", dx), "%12f"},
|
||||
{__LINE__, StrPrint("%.12f", dx), "%.12f"},
|
||||
{__LINE__, StrPrint("%.12a", dx), "%.12a"},
|
||||
};
|
||||
for (const Expectation &e : kExpect) {
|
||||
if (native_traits.hex_float_uses_minimal_precision_when_not_specified) {
|
||||
Expectation ex = {__LINE__, StrPrint("%12a", dx), "%12a"};
|
||||
expect.push_back(ex);
|
||||
}
|
||||
for (const Expectation &e : expect) {
|
||||
SCOPED_TRACE(e.line);
|
||||
SCOPED_TRACE(e.fmt);
|
||||
UntypedFormatSpecImpl format(e.fmt);
|
||||
|
|
@ -865,6 +1217,25 @@ TEST_F(FormatConvertTest, ExpectedFailures) {
|
|||
EXPECT_TRUE(FormatFails("%*d", ""));
|
||||
}
|
||||
|
||||
// Sanity check to make sure that we are testing what we think we're testing on
|
||||
// e.g. the x86_64+glibc platform.
|
||||
TEST_F(FormatConvertTest, GlibcHasCorrectTraits) {
|
||||
#if !defined(__GLIBC__) || !defined(__x86_64__)
|
||||
return;
|
||||
#endif
|
||||
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
|
||||
// If one of the following tests break then it is either because the above PP
|
||||
// macro guards failed to exclude a new platform (likely) or because something
|
||||
// has changed in the implemention of glibc sprintf float formatting behavior.
|
||||
// If the latter, then the code that computes these flags needs to be
|
||||
// revisited and/or possibly the StrFormat implementation.
|
||||
EXPECT_TRUE(native_traits.hex_float_has_glibc_rounding);
|
||||
EXPECT_TRUE(native_traits.hex_float_prefers_denormal_repr);
|
||||
EXPECT_TRUE(
|
||||
native_traits.hex_float_uses_minimal_precision_when_not_specified);
|
||||
EXPECT_TRUE(native_traits.hex_float_optimizes_leading_digit_bit_count);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace str_format_internal
|
||||
ABSL_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -33,6 +33,29 @@ std::string Flags::ToString() const {
|
|||
return s;
|
||||
}
|
||||
|
||||
#define ABSL_INTERNAL_X_VAL(id) \
|
||||
constexpr absl::FormatConversionChar FormatConversionCharInternal::id;
|
||||
ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
|
||||
#undef ABSL_INTERNAL_X_VAL
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
constexpr 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;
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@
|
|||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
namespace str_format_internal {
|
||||
|
||||
enum class FormatConversionChar : uint8_t;
|
||||
enum class FormatConversionCharSet : uint64_t;
|
||||
|
||||
namespace str_format_internal {
|
||||
|
||||
class FormatRawSinkImpl {
|
||||
public:
|
||||
// Implicitly convert from any type that provides the hook function as
|
||||
|
|
@ -361,14 +361,12 @@ struct FormatConversionCharSetInternal {
|
|||
static constexpr FormatConversionCharSet kStar =
|
||||
FormatConversionCharToConvValue('*');
|
||||
|
||||
// Some predefined values (TODO(matthewbr), delete any that are unused).
|
||||
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 kString = s;
|
||||
static constexpr FormatConversionCharSet kPointer = p;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -80,4 +80,19 @@ TEST(FormatExtensionTest, SinkAppendChars) {
|
|||
EXPECT_EQ(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FormatExtensionTest, VerifyEnumEquality) {
|
||||
#define X_VAL(id) \
|
||||
EXPECT_EQ(absl::FormatConversionChar::id, \
|
||||
absl::str_format_internal::FormatConversionCharInternal::id);
|
||||
ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, );
|
||||
#undef X_VAL
|
||||
|
||||
#define X_VAL(id) \
|
||||
EXPECT_EQ(absl::FormatConversionCharSet::id, \
|
||||
absl::str_format_internal::FormatConversionCharSetInternal::id);
|
||||
ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, );
|
||||
#undef X_VAL
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#include "absl/strings/internal/str_format/float_conversion.h"
|
||||
|
||||
#include <string.h>
|
||||
|
|
@ -15,6 +29,7 @@
|
|||
#include "absl/functional/function_ref.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
|
|
@ -119,7 +134,7 @@ class BinaryToDecimal {
|
|||
assert(exp > 0);
|
||||
assert(exp <= std::numeric_limits<long double>::max_exponent);
|
||||
static_assert(
|
||||
StackArray::kMaxCapacity >=
|
||||
static_cast<int>(StackArray::kMaxCapacity) >=
|
||||
ChunksNeeded(std::numeric_limits<long double>::max_exponent),
|
||||
"");
|
||||
|
||||
|
|
@ -204,7 +219,7 @@ class BinaryToDecimal {
|
|||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t kDigitsPerChunk = 9;
|
||||
static constexpr int kDigitsPerChunk = 9;
|
||||
|
||||
int decimal_start_;
|
||||
int decimal_end_;
|
||||
|
|
@ -441,8 +456,10 @@ struct Padding {
|
|||
};
|
||||
|
||||
Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) {
|
||||
if (state.conv.width() < 0 || state.conv.width() <= total_size)
|
||||
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};
|
||||
|
|
@ -453,26 +470,31 @@ Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) {
|
|||
}
|
||||
}
|
||||
|
||||
void FinalPrint(absl::string_view data, int trailing_zeros,
|
||||
const FormatState &state) {
|
||||
void FinalPrint(const FormatState &state, absl::string_view data,
|
||||
int padding_offset, int trailing_zeros,
|
||||
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() +
|
||||
static_cast<size_t>(trailing_zeros),
|
||||
state);
|
||||
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);
|
||||
state.sink->Append(data.substr(padding_offset));
|
||||
state.sink->Append(trailing_zeros, '0');
|
||||
state.sink->Append(data_postfix);
|
||||
state.sink->Append(padding.right_spaces, ' ');
|
||||
}
|
||||
|
||||
|
|
@ -525,10 +547,11 @@ void FormatFFast(Int v, int exp, const FormatState &state) {
|
|||
// 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(absl::string_view(integral_digits_start, size),
|
||||
FinalPrint(state, absl::string_view(integral_digits_start, size),
|
||||
/*padding_offset=*/0,
|
||||
static_cast<int>(state.precision - (fractional_digits_end -
|
||||
fractional_digits_start)),
|
||||
state);
|
||||
/*data_postfix=*/"");
|
||||
}
|
||||
|
||||
// Slow %f formatter for when the shifted value does not fit in a uint128, and
|
||||
|
|
@ -655,6 +678,255 @@ void FormatF(Int mantissa, int exp, const FormatState &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<long double>::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(absl::string_view v, char *out) {
|
||||
std::memcpy(out, v.data(), v.size());
|
||||
return out + v.size();
|
||||
|
|
@ -1103,7 +1375,10 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv,
|
|||
}
|
||||
} else if (c == FormatConversionCharInternal::a ||
|
||||
c == FormatConversionCharInternal::A) {
|
||||
return FallbackToSnprintf(v, conv, sink);
|
||||
bool uppercase = (c == FormatConversionCharInternal::A);
|
||||
FormatA(HexFloatTypeParams(Float{}), decomposed.mantissa,
|
||||
decomposed.exponent, uppercase, {sign_char, precision, conv, sink});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1131,7 +1406,7 @@ bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv,
|
|||
|
||||
bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv,
|
||||
FormatSinkImpl *sink) {
|
||||
return FloatToSink(v, conv, sink);
|
||||
return FloatToSink(static_cast<double>(v), conv, sink);
|
||||
}
|
||||
|
||||
bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv,
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
|
||||
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#include "absl/strings/internal/str_format/parser.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
|
||||
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
|
||||
#include "absl/strings/internal/str_format/parser.h"
|
||||
|
||||
#include <string.h>
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ ABSL_NAMESPACE_BEGIN
|
|||
namespace strings_internal {
|
||||
|
||||
// This class is implicitly constructible from everything that absl::string_view
|
||||
// is implicitly constructible from. If it's constructed from a temporary
|
||||
// string, the data is moved into a data member so its lifetime matches that of
|
||||
// the ConvertibleToStringView instance.
|
||||
// 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,41 +65,12 @@ class ConvertibleToStringView {
|
|||
: value_(s) {}
|
||||
|
||||
// Matches rvalue strings and moves their data to a member.
|
||||
ConvertibleToStringView(std::string&& s) // NOLINT(runtime/explicit)
|
||||
: copy_(std::move(s)), value_(copy_) {}
|
||||
|
||||
ConvertibleToStringView(const ConvertibleToStringView& other)
|
||||
: copy_(other.copy_),
|
||||
value_(other.IsSelfReferential() ? copy_ : other.value_) {}
|
||||
|
||||
ConvertibleToStringView(ConvertibleToStringView&& other) {
|
||||
StealMembers(std::move(other));
|
||||
}
|
||||
|
||||
ConvertibleToStringView& operator=(ConvertibleToStringView other) {
|
||||
StealMembers(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
ConvertibleToStringView(std::string&& s) = delete;
|
||||
ConvertibleToStringView(const std::string&& s) = delete;
|
||||
|
||||
absl::string_view value() const { return value_; }
|
||||
|
||||
private:
|
||||
// Returns true if ctsp's value refers to its internal copy_ member.
|
||||
bool IsSelfReferential() const { return value_.data() == copy_.data(); }
|
||||
|
||||
void StealMembers(ConvertibleToStringView&& other) {
|
||||
if (other.IsSelfReferential()) {
|
||||
copy_ = std::move(other.copy_);
|
||||
value_ = copy_;
|
||||
other.value_ = other.copy_;
|
||||
} else {
|
||||
value_ = other.value_;
|
||||
}
|
||||
}
|
||||
|
||||
// Holds the data moved from temporary std::string arguments. Declared first
|
||||
// so that 'value' can refer to 'copy_'.
|
||||
std::string copy_;
|
||||
absl::string_view value_;
|
||||
};
|
||||
|
||||
|
|
@ -273,7 +244,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 absl::string_view
|
||||
// and returns bool.
|
||||
template <typename Delimiter, typename Predicate>
|
||||
//
|
||||
// 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;
|
||||
|
|
@ -281,12 +256,12 @@ class Splitter {
|
|||
using const_iterator = strings_internal::SplitIterator<Splitter>;
|
||||
using value_type = typename std::iterator_traits<const_iterator>::value_type;
|
||||
|
||||
Splitter(ConvertibleToStringView 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)) {}
|
||||
|
||||
absl::string_view text() const { return text_.value(); }
|
||||
absl::string_view text() const { return text_; }
|
||||
const Delimiter& delimiter() const { return delimiter_; }
|
||||
const Predicate& predicate() const { return predicate_; }
|
||||
|
||||
|
|
@ -336,7 +311,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;
|
||||
|
|
@ -401,7 +376,7 @@ class Splitter {
|
|||
Container m;
|
||||
typename Container::iterator it;
|
||||
bool insert = true;
|
||||
for (const auto sp : splitter) {
|
||||
for (const auto& sp : splitter) {
|
||||
if (insert) {
|
||||
it = Inserter<Container>::Insert(&m, First(sp), Second());
|
||||
} else {
|
||||
|
|
@ -443,7 +418,7 @@ class Splitter {
|
|||
};
|
||||
};
|
||||
|
||||
ConvertibleToStringView text_;
|
||||
StringType text_;
|
||||
Delimiter delimiter_;
|
||||
Predicate predicate_;
|
||||
};
|
||||
|
|
|
|||
70
third_party/abseil_cpp/absl/strings/internal/string_constant.h
vendored
Normal file
70
third_party/abseil_cpp/absl/strings/internal/string_constant.h
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// 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 "absl/meta/type_traits.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace strings_internal {
|
||||
|
||||
// StringConstant<T> represents a compile time string constant.
|
||||
// It can be accessed via its `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 {
|
||||
private:
|
||||
// Returns true if `view` points to constant data.
|
||||
// Otherwise, it can't be constant evaluated.
|
||||
static constexpr bool ValidateConstant(absl::string_view view) {
|
||||
return view.empty() || 2 * view[0] != 1;
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr absl::string_view value = T{}();
|
||||
constexpr absl::string_view operator()() const { return value; }
|
||||
|
||||
static_assert(ValidateConstant(value),
|
||||
"The input string_view must point to constant data.");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr 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 `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 absl
|
||||
|
||||
#endif // ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_
|
||||
60
third_party/abseil_cpp/absl/strings/internal/string_constant_test.cc
vendored
Normal file
60
third_party/abseil_cpp/absl/strings/internal/string_constant_test.cc
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2020 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/strings/internal/string_constant.h"
|
||||
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::strings_internal::MakeStringConstant;
|
||||
|
||||
struct Callable {
|
||||
constexpr absl::string_view operator()() const {
|
||||
return absl::string_view("Callable", 8);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(StringConstant, Traits) {
|
||||
constexpr auto str = MakeStringConstant(Callable{});
|
||||
using T = decltype(str);
|
||||
|
||||
EXPECT_TRUE(std::is_empty<T>::value);
|
||||
EXPECT_TRUE(std::is_trivial<T>::value);
|
||||
EXPECT_TRUE(absl::is_trivially_default_constructible<T>::value);
|
||||
EXPECT_TRUE(absl::is_trivially_copy_constructible<T>::value);
|
||||
EXPECT_TRUE(absl::is_trivially_move_constructible<T>::value);
|
||||
EXPECT_TRUE(absl::is_trivially_destructible<T>::value);
|
||||
}
|
||||
|
||||
TEST(StringConstant, MakeFromCallable) {
|
||||
constexpr auto str = MakeStringConstant(Callable{});
|
||||
using T = decltype(str);
|
||||
EXPECT_EQ(Callable{}(), T::value);
|
||||
EXPECT_EQ(Callable{}(), str());
|
||||
}
|
||||
|
||||
TEST(StringConstant, MakeFromStringConstant) {
|
||||
// We want to make sure the StringConstant itself is a valid input to the
|
||||
// factory function.
|
||||
constexpr auto str = MakeStringConstant(Callable{});
|
||||
constexpr auto str2 = MakeStringConstant(str);
|
||||
using T = decltype(str2);
|
||||
EXPECT_EQ(Callable{}(), T::value);
|
||||
EXPECT_EQ(Callable{}(), str2());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Loading…
Add table
Add a link
Reference in a new issue