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:
Vincent Ambo 2020-11-21 14:43:54 +01:00
parent cc27324d02
commit 082c006c04
854 changed files with 11260 additions and 5296 deletions

View file

@ -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) {

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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) {}
};

View file

@ -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>

View file

@ -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_

View file

@ -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"

View file

@ -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

View file

@ -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;

View file

@ -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;
};

View file

@ -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

View file

@ -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,

View file

@ -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_

View file

@ -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>

View file

@ -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_

View file

@ -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>

View file

@ -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_;
};

View 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_

View 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