Purge basic_writer

This commit is contained in:
Victor Zverovich 2020-05-07 14:15:12 -07:00
parent 2f05054dd3
commit c06851456d
5 changed files with 130 additions and 273 deletions

View File

@ -1100,8 +1100,6 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified. // is not specified.
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf); auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(specs.width, internal::handle_dynamic_spec<internal::width_checker>(specs.width,
width_ref, ctx); width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
@ -1115,8 +1113,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
f.precision = precision; f.precision = precision;
parse_chrono_format(begin, end, f); parse_chrono_format(begin, end, f);
} }
w.write(buf.data(), buf.size(), specs); return internal::write(
return w.out(); ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
} }
}; };

View File

@ -383,9 +383,7 @@ OutputIt format_default(OutputIt out, T value) {
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) { OutputIt format_default(OutputIt out, double value) {
basic_writer<buffer_range<char>> w(out); return internal::write(out, value);
w.write(value);
return w.out();
} }
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>

View File

@ -1561,6 +1561,16 @@ template <typename OutputIt, typename Char, typename UInt> struct int_writer {
} }
}; };
template <typename Char, typename OutputIt>
OutputIt write_bytes(OutputIt out, string_view bytes,
const basic_format_specs<Char>& specs) {
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
const char* data = bytes.data();
return copy_str<Char>(data, data + bytes.size(), it);
});
}
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
OutputIt write_nonfinite(OutputIt out, bool isinf, OutputIt write_nonfinite(OutputIt out, bool isinf,
const basic_format_specs<Char>& specs, const basic_format_specs<Char>& specs,
@ -1577,6 +1587,71 @@ OutputIt write_nonfinite(OutputIt out, bool isinf,
}); });
} }
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt write(OutputIt out, T value, basic_format_specs<Char> specs = {},
locale_ref loc = {}) {
if (const_check(!is_supported_floating_point(value))) return out;
float_specs fspecs = parse_float_type_spec(specs);
fspecs.sign = specs.sign;
if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
fspecs.sign = sign::minus;
value = -value;
} else if (fspecs.sign == sign::minus) {
fspecs.sign = sign::none;
}
if (!std::isfinite(value))
return write_nonfinite(out, std::isinf(value), specs, fspecs);
if (specs.align == align::numeric && fspecs.sign) {
auto it = reserve(out, 1);
*it++ = static_cast<Char>(data::signs[fspecs.sign]);
out = base_iterator(out, it);
fspecs.sign = sign::none;
if (specs.width != 0) --specs.width;
}
memory_buffer buffer;
if (fspecs.format == float_format::hex) {
if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
return write_bytes(out, {buffer.data(), buffer.size()}, specs);
}
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
if (fspecs.format == float_format::exp) {
if (precision == max_value<int>())
FMT_THROW(format_error("number is too big"));
else
++precision;
}
if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
fspecs.use_grisu = use_grisu<T>();
int exp = format_float(promote_float(value), precision, fspecs, buffer);
fspecs.precision = precision;
Char point =
fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
fspecs, point);
return write_padded<align::right>(out, specs, w.size(), w);
}
template <typename StrChar, typename Char, typename OutputIt>
OutputIt write(OutputIt out, basic_string_view<StrChar> s,
const basic_format_specs<Char>& specs = {}) {
auto data = s.data();
auto size = s.size();
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
size = code_point_index(s, to_unsigned(specs.precision));
auto width = specs.width != 0
? count_code_points(basic_string_view<StrChar>(data, size))
: 0;
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, size, width, [=](iterator it) {
return copy_str<Char>(data, data + size, it);
});
}
template <typename Char, typename OutputIt, typename UIntPtr> template <typename Char, typename OutputIt, typename UIntPtr>
OutputIt write_ptr(OutputIt out, UIntPtr value, OutputIt write_ptr(OutputIt out, UIntPtr value,
const basic_format_specs<Char>* specs) { const basic_format_specs<Char>* specs) {
@ -1592,27 +1667,21 @@ OutputIt write_ptr(OutputIt out, UIntPtr value,
: base_iterator(out, write(reserve(out, size))); : base_iterator(out, write(reserve(out, size)));
} }
template <typename Char, typename OutputIt> template <typename T> struct is_integral : std::is_integral<T> {};
OutputIt write_bytes(OutputIt out, string_view bytes, template <> struct is_integral<int128_t> : std::true_type {};
const basic_format_specs<Char>& specs) { template <> struct is_integral<uint128_t> : std::true_type {};
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
const char* data = bytes.data();
return copy_str<Char>(data, data + bytes.size(), it);
});
}
// This template provides operations for formatting and writing data into a template <typename Range, typename ErrorHandler = internal::error_handler>
// character range. class arg_formatter_base {
template <typename Range> class basic_writer {
public: public:
using char_type = typename Range::value_type; using char_type = typename Range::value_type;
using iterator = typename Range::iterator; using iterator = typename Range::iterator;
using format_specs = basic_format_specs<char_type>; using format_specs = basic_format_specs<char_type>;
protected: private:
iterator out_; // Output iterator. iterator out_; // Output iterator.
locale_ref locale_; locale_ref locale_;
format_specs* specs_;
// Attempts to reserve space for n extra characters in the output range. // Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_. // Returns a pointer to the reserved range or a reference to out_.
@ -1620,6 +1689,27 @@ template <typename Range> class basic_writer {
return internal::reserve(out_, n); return internal::reserve(out_, n);
} }
using reserve_iterator = remove_reference_t<decltype(
internal::reserve(std::declval<iterator&>(), 0))>;
struct char_writer {
char_type value;
size_t size() const { return 1; }
template <typename It> It operator()(It it) const {
*it++ = value;
return it;
}
};
void write_char(char_type value) {
if (specs_)
out_ = write_padded(out_, *specs_, 1, char_writer{value});
else
write(value);
}
// Writes a decimal integer. // Writes a decimal integer.
template <typename Int> void write_decimal(Int value) { template <typename Int> void write_decimal(Int value) {
auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value); auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
@ -1632,15 +1722,6 @@ template <typename Range> class basic_writer {
it = format_decimal<char_type>(it, abs_value, num_digits); it = format_decimal<char_type>(it, abs_value, num_digits);
} }
using reserve_iterator = remove_reference_t<decltype(
internal::reserve(std::declval<iterator&>(), 0))>;
public:
explicit basic_writer(Range out, locale_ref loc = locale_ref())
: out_(out.begin()), locale_(loc) {}
iterator& out() { return out_; }
void write(int value) { write_decimal(value); } void write(int value) { write_decimal(value); }
void write(long value) { write_decimal(value); } void write(long value) { write_decimal(value); }
void write(long long value) { write_decimal(value); } void write(long long value) { write_decimal(value); }
@ -1661,55 +1742,6 @@ template <typename Range> class basic_writer {
out_ = w.out; out_ = w.out;
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void write(T value, format_specs specs = {}) {
if (const_check(!is_supported_floating_point(value))) return;
float_specs fspecs = parse_float_type_spec(specs);
fspecs.sign = specs.sign;
if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
fspecs.sign = sign::minus;
value = -value;
} else if (fspecs.sign == sign::minus) {
fspecs.sign = sign::none;
}
if (!std::isfinite(value)) {
out_ = write_nonfinite(out_, std::isinf(value), specs, fspecs);
return;
}
if (specs.align == align::numeric && fspecs.sign) {
auto&& it = reserve(1);
*it++ = static_cast<char_type>(data::signs[fspecs.sign]);
fspecs.sign = sign::none;
if (specs.width != 0) --specs.width;
}
memory_buffer buffer;
if (fspecs.format == float_format::hex) {
if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
out_ = write_bytes(out_, {buffer.data(), buffer.size()}, specs);
return;
}
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
if (fspecs.format == float_format::exp) {
if (precision == max_value<int>())
FMT_THROW(format_error("number is too big"));
else
++precision;
}
if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
fspecs.use_grisu = use_grisu<T>();
int exp = format_float(promote_float(value), precision, fspecs, buffer);
fspecs.precision = precision;
char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
: static_cast<char_type>('.');
float_writer<char_type> w(buffer.data(), static_cast<int>(buffer.size()),
exp, fspecs, point);
out_ = write_padded<align::right>(out_, specs, w.size(), w);
}
void write(char value) { void write(char value) {
auto&& it = reserve(1); auto&& it = reserve(1);
*it++ = value; *it++ = value;
@ -1743,49 +1775,7 @@ template <typename Range> class basic_writer {
template <typename Char> template <typename Char>
void write(basic_string_view<Char> s, const format_specs& specs = {}) { void write(basic_string_view<Char> s, const format_specs& specs = {}) {
const Char* data = s.data(); out_ = internal::write(out_, s, specs);
std::size_t size = s.size();
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
size = code_point_index(s, to_unsigned(specs.precision));
write(data, size, specs);
}
};
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_t> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base : private basic_writer<Range> {
public:
using char_type = typename Range::value_type;
using iterator = typename Range::iterator;
using format_specs = basic_format_specs<char_type>;
private:
using writer_type = basic_writer<Range>;
using writer_type::out_;
format_specs* specs_;
using writer_type::write;
using writer_type::write_int;
struct char_writer {
char_type value;
size_t size() const { return 1; }
template <typename It> It operator()(It it) const {
*it++ = value;
return it;
}
};
void write_char(char_type value) {
if (specs_)
out() = write_padded(out(), *specs_, 1, char_writer{value});
else
write(value);
} }
void write_pointer(const void* p) { void write_pointer(const void* p) {
@ -1793,8 +1783,7 @@ class arg_formatter_base : private basic_writer<Range> {
} }
protected: protected:
using writer_type::out; iterator out() { return out_; }
writer_type& writer() { return *this; }
format_specs* specs() { return specs_; } format_specs* specs() { return specs_; }
void write(bool value) { void write(bool value) {
@ -1814,7 +1803,7 @@ class arg_formatter_base : private basic_writer<Range> {
public: public:
arg_formatter_base(Range r, format_specs* s, locale_ref loc) arg_formatter_base(Range r, format_specs* s, locale_ref loc)
: basic_writer<Range>(r, loc), specs_(s) {} : out_(r.begin()), locale_(loc), specs_(s) {}
iterator operator()(monostate) { iterator operator()(monostate) {
FMT_ASSERT(false, "invalid argument type"); FMT_ASSERT(false, "invalid argument type");
@ -1844,8 +1833,9 @@ class arg_formatter_base : private basic_writer<Range> {
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) { iterator operator()(T value) {
auto specs = specs_ ? *specs_ : format_specs();
if (const_check(is_supported_floating_point(value))) if (const_check(is_supported_floating_point(value)))
write(value, specs_ ? *specs_ : format_specs()); out_ = internal::write(out_, value, specs, locale_);
else else
FMT_ASSERT(false, "unsupported float argument type"); FMT_ASSERT(false, "unsupported float argument type");
return out(); return out();
@ -2503,7 +2493,7 @@ template <typename Handler, typename Char> struct id_adapter {
template <bool IS_CONSTEXPR, typename Char, typename Handler> template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str, FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
Handler&& handler) { Handler&& handler) {
struct pfs_writer { struct writer {
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) { FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
if (begin == end) return; if (begin == end) return;
for (;;) { for (;;) {
@ -3336,21 +3326,21 @@ extern template FMT_API char thousands_sep_impl<char>(locale_ref loc);
extern template FMT_API wchar_t thousands_sep_impl<wchar_t>(locale_ref loc); extern template FMT_API wchar_t thousands_sep_impl<wchar_t>(locale_ref loc);
extern template FMT_API char decimal_point_impl(locale_ref loc); extern template FMT_API char decimal_point_impl(locale_ref loc);
extern template FMT_API wchar_t decimal_point_impl(locale_ref loc); extern template FMT_API wchar_t decimal_point_impl(locale_ref loc);
extern template extern template int format_float<double>(double value, int precision,
int format_float<double>(double value, int precision, float_specs specs, float_specs specs, buffer<char>& buf);
buffer<char>& buf); extern template int format_float<long double>(long double value, int precision,
extern template float_specs specs,
int format_float<long double>(long double value, int precision, buffer<char>& buf);
float_specs specs, buffer<char>& buf);
int snprintf_float(float value, int precision, float_specs specs, int snprintf_float(float value, int precision, float_specs specs,
buffer<char>& buf) = delete; buffer<char>& buf) = delete;
extern template extern template int snprintf_float<double>(double value, int precision,
int snprintf_float<double>(double value, int precision, float_specs specs, float_specs specs,
buffer<char>& buf); buffer<char>& buf);
extern template extern template int snprintf_float<long double>(long double value,
int snprintf_float<long double>(long double value, int precision, int precision,
float_specs specs, buffer<char>& buf); float_specs specs,
} buffer<char>& buf);
} // namespace internal
#endif #endif
template <typename S, typename Char = char_t<S>, template <typename S, typename Char = char_t<S>,

View File

@ -328,7 +328,9 @@ template <typename T> struct printf_formatter {
} }
}; };
/** This template formats data and writes the output to a writer. */ /**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context { template <typename OutputIt, typename Char> class basic_printf_context {
public: public:
/** The character type for the output. */ /** The character type for the output. */
@ -358,9 +360,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
public: public:
/** /**
\rst \rst
Constructs a ``printf_context`` object. References to the arguments and Constructs a ``printf_context`` object. References to the arguments are
the writer are stored in the context object so make sure they have stored in the context object so make sure they have appropriate lifetimes.
appropriate lifetimes.
\endrst \endrst
*/ */
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,

View File

@ -35,8 +35,6 @@
#include "util.h" #include "util.h"
#undef ERROR #undef ERROR
#undef min
#undef max
using std::size_t; using std::size_t;
@ -47,7 +45,6 @@ using fmt::memory_buffer;
using fmt::string_view; using fmt::string_view;
using fmt::wmemory_buffer; using fmt::wmemory_buffer;
using fmt::wstring_view; using fmt::wstring_view;
using fmt::internal::basic_writer;
using fmt::internal::max_value; using fmt::internal::max_value;
using testing::Return; using testing::Return;
@ -102,47 +99,6 @@ void std_format(long double value, std::wstring& result) {
result = buffer; result = buffer;
} }
#endif #endif
// Checks if writing value to BasicWriter<Char> produces the same result
// as writing it to std::basic_ostringstream<Char>.
template <typename Char, typename T>
::testing::AssertionResult check_write(const T& value, const char* type) {
fmt::basic_memory_buffer<Char> buffer;
using range = fmt::buffer_range<Char>;
basic_writer<range> writer(buffer);
writer.write(value);
std::basic_string<Char> actual = to_string(buffer);
std::basic_string<Char> expected;
std_format(value, expected);
if (expected == actual) return ::testing::AssertionSuccess();
return ::testing::AssertionFailure()
<< "Value of: (Writer<" << type << ">() << value).str()\n"
<< " Actual: " << actual << "\n"
<< "Expected: " << expected << "\n";
}
struct AnyWriteChecker {
template <typename T>
::testing::AssertionResult operator()(const char*, const T& value) const {
::testing::AssertionResult result = check_write<char>(value, "char");
return result ? check_write<wchar_t>(value, "wchar_t") : result;
}
};
template <typename Char> struct WriteChecker {
template <typename T>
::testing::AssertionResult operator()(const char*, const T& value) const {
return check_write<Char>(value, "char");
}
};
// Checks if writing value to BasicWriter produces the same result
// as writing it to std::ostringstream both for char and wchar_t.
#define CHECK_WRITE(value) EXPECT_PRED_FORMAT1(AnyWriteChecker(), value)
#define CHECK_WRITE_CHAR(value) EXPECT_PRED_FORMAT1(WriteChecker<char>(), value)
#define CHECK_WRITE_WCHAR(value) \
EXPECT_PRED_FORMAT1(WriteChecker<wchar_t>(), value)
} // namespace } // namespace
struct uint32_pair { struct uint32_pair {
@ -479,92 +435,6 @@ TEST(StringViewTest, Ctor) {
EXPECT_EQ(4u, string_view(std::string("defg")).size()); EXPECT_EQ(4u, string_view(std::string("defg")).size());
} }
TEST(WriterTest, WriteInt) {
CHECK_WRITE(42);
CHECK_WRITE(-42);
CHECK_WRITE(static_cast<short>(12));
CHECK_WRITE(34u);
CHECK_WRITE(std::numeric_limits<int>::min());
CHECK_WRITE(max_value<int>());
CHECK_WRITE(max_value<unsigned>());
}
TEST(WriterTest, WriteLong) {
CHECK_WRITE(56l);
CHECK_WRITE(78ul);
CHECK_WRITE(std::numeric_limits<long>::min());
CHECK_WRITE(max_value<long>());
CHECK_WRITE(max_value<unsigned long>());
}
TEST(WriterTest, WriteLongLong) {
CHECK_WRITE(56ll);
CHECK_WRITE(78ull);
CHECK_WRITE(std::numeric_limits<long long>::min());
CHECK_WRITE(max_value<long long>());
CHECK_WRITE(max_value<unsigned long long>());
}
TEST(WriterTest, WriteDouble) {
CHECK_WRITE(4.2);
CHECK_WRITE(-4.2);
auto min = std::numeric_limits<double>::min();
auto max = max_value<double>();
if (fmt::internal::use_grisu<double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
} else {
CHECK_WRITE(min);
CHECK_WRITE(max);
}
}
TEST(WriterTest, WriteLongDouble) {
CHECK_WRITE(4.2l);
CHECK_WRITE_CHAR(-4.2l);
std::wstring str;
std_format(4.2l, str);
if (str[0] != '-')
CHECK_WRITE_WCHAR(-4.2l);
else
fmt::print("warning: long double formatting with std::swprintf is broken");
auto min = std::numeric_limits<long double>::min();
auto max = max_value<long double>();
if (fmt::internal::use_grisu<long double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
} else {
CHECK_WRITE(min);
CHECK_WRITE(max);
}
}
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
memory_buffer buf;
for (int i = 0; i < 100; ++i) fmt::format_to(buf, "{}", 1.23456789);
}
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
memory_buffer buf;
// Fill the buffer.
for (int i = 0; i < fmt::inline_buffer_size; ++i) fmt::format_to(buf, " ");
fmt::format_to(buf, "{}", 1.2);
fmt::string_view sv(buf.data(), buf.size());
sv.remove_prefix(fmt::inline_buffer_size);
EXPECT_EQ("1.2", sv);
}
TEST(WriterTest, WriteChar) { CHECK_WRITE('a'); }
TEST(WriterTest, WriteWideChar) { CHECK_WRITE_WCHAR(L'a'); }
TEST(WriterTest, WriteString) {
CHECK_WRITE_CHAR("abc");
CHECK_WRITE_WCHAR("abc");
}
TEST(WriterTest, WriteWideString) { CHECK_WRITE_WCHAR(L"abc"); }
TEST(FormatToTest, FormatWithoutArgs) { TEST(FormatToTest, FormatWithoutArgs) {
std::string s; std::string s;
fmt::format_to(std::back_inserter(s), "test"); fmt::format_to(std::back_inserter(s), "test");