Internalize undocumented basic_writer

This commit is contained in:
Victor Zverovich 2019-07-03 09:52:09 -07:00
parent 83174f2a1f
commit ded0a3bb3d
4 changed files with 447 additions and 453 deletions

View File

@ -803,7 +803,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
basic_writer<range> w(range(ctx.out()));
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(
spec.width_, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::precision_checker>(

View File

@ -164,7 +164,7 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
++error_code_size;
}
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
writer w(out);
internal::writer w(out);
if (message.size() <= inline_buffer_size - error_code_size) {
w.write(message);
w.write(SEP);
@ -245,10 +245,9 @@ template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (precision > 100000) {
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
}
#endif
// Suppress the warning about nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
@ -898,7 +897,7 @@ FMT_FUNC void internal::format_windows_error(internal::buffer<char>& out,
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
writer w(out);
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(utf8_message);
@ -927,7 +926,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
int result =
internal::safe_strerror(error_code, system_message, buf.size());
if (result == 0) {
writer w(out);
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(system_message);

View File

@ -300,7 +300,7 @@ enum { inline_buffer_size = 500 };
A dynamically growing memory buffer for trivially copyable/constructible types
with the first ``SIZE`` elements stored in the object itself.
You can use one of the following typedefs for common character types:
You can use one of the following type aliases for common character types:
+----------------+------------------------------+
| Type | Definition |
@ -340,8 +340,8 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
void grow(std::size_t size) FMT_OVERRIDE;
public:
typedef T value_type;
typedef const T& const_reference;
using value_type = T;
using const_reference = const T&;
explicit basic_memory_buffer(const Allocator& alloc = Allocator())
: Allocator(alloc) {
@ -397,9 +397,7 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
template <typename T, std::size_t SIZE, typename Allocator>
void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (size > 1000) {
throw std::runtime_error("fuzz mode - won't grow that much");
}
if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much");
#endif
std::size_t old_capacity = this->capacity();
std::size_t new_capacity = old_capacity + old_capacity / 2;
@ -416,8 +414,8 @@ void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
if (old_data != store_) Allocator::deallocate(old_data, old_capacity);
}
typedef basic_memory_buffer<char> memory_buffer;
typedef basic_memory_buffer<wchar_t> wmemory_buffer;
using memory_buffer = basic_memory_buffer<char>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
/** A formatting error such as invalid format string. */
class FMT_API format_error : public std::runtime_error {
@ -428,10 +426,6 @@ class FMT_API format_error : public std::runtime_error {
~format_error() FMT_NOEXCEPT;
};
template <typename Range> class basic_writer;
using writer = basic_writer<internal::buffer_range<char>>;
using wwriter = basic_writer<internal::buffer_range<wchar_t>>;
namespace internal {
// A workaround for std::string not having mutable data() until C++17.
@ -1069,9 +1063,8 @@ It grisu_prettify(const char* digits, int size, int exp, It it,
if (params.trailing_zeros) {
*it++ = static_cast<Char>('.');
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (num_zeros > 1000) {
if (num_zeros > 1000)
throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
}
#endif
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
}
@ -1271,6 +1264,428 @@ void arg_map<Context>::init(const basic_format_args<Context>& args) {
}
}
/**
This template provides operations for formatting and writing data into a
character range.
*/
template <typename Range> class basic_writer {
public:
typedef typename Range::value_type char_type;
typedef decltype(std::declval<Range>().begin()) iterator;
typedef basic_format_specs<char_type> format_specs;
private:
iterator out_; // Output iterator.
internal::locale_ref locale_;
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
return internal::reserve(out_, n);
}
template <typename F> struct padded_int_writer {
size_t size_;
string_view prefix;
char_type fill;
std::size_t padding;
F f;
size_t size() const { return size_; }
size_t width() const { return size_; }
template <typename It> void operator()(It&& it) const {
if (prefix.size() != 0)
it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
it = std::fill_n(it, padding, fill);
f(it);
}
};
// Writes an integer in the format
// <left-padding><prefix><numeric-padding><digits><right-padding>
// where <digits> are written by f(it).
template <typename Spec, typename F>
void write_int(int num_digits, string_view prefix, const Spec& spec, F f) {
std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
char_type fill = static_cast<char_type>(spec.fill());
std::size_t padding = 0;
if (spec.align() == ALIGN_NUMERIC) {
if (spec.width() > size) {
padding = spec.width() - size;
size = spec.width();
}
} else if (spec.precision > num_digits) {
size = prefix.size() + internal::to_unsigned(spec.precision);
padding = internal::to_unsigned(spec.precision - num_digits);
fill = static_cast<char_type>('0');
}
align_spec as = spec;
if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
write_padded(as, padded_int_writer<F>{size, prefix, fill, padding, f});
}
// Writes a decimal integer.
template <typename Int> void write_decimal(Int value) {
typedef typename internal::int_traits<Int>::main_type main_type;
main_type abs_value = static_cast<main_type>(value);
bool is_negative = internal::is_negative(value);
if (is_negative) abs_value = 0 - abs_value;
int num_digits = internal::count_digits(abs_value);
auto&& it =
reserve((is_negative ? 1 : 0) + static_cast<size_t>(num_digits));
if (is_negative) *it++ = static_cast<char_type>('-');
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
}
// The handle_int_type_spec handler that writes an integer.
template <typename Int, typename Spec> struct int_writer {
typedef typename internal::int_traits<Int>::main_type unsigned_type;
basic_writer<Range>& writer;
const Spec& spec;
unsigned_type abs_value;
char prefix[4];
unsigned prefix_size;
string_view get_prefix() const { return string_view(prefix, prefix_size); }
int_writer(basic_writer<Range>& w, Int value, const Spec& s)
: writer(w),
spec(s),
abs_value(static_cast<unsigned_type>(value)),
prefix_size(0) {
if (internal::is_negative(value)) {
prefix[0] = '-';
++prefix_size;
abs_value = 0 - abs_value;
} else if (spec.has(SIGN_FLAG)) {
prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' ';
++prefix_size;
}
}
struct dec_writer {
unsigned_type abs_value;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
}
};
void on_dec() {
int num_digits = internal::count_digits(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
dec_writer{abs_value, num_digits});
}
struct hex_writer {
int_writer& self;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits,
self.spec.type != 'x');
}
};
void on_hex() {
if (spec.has(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type);
}
int num_digits = internal::count_digits<4>(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
hex_writer{*this, num_digits});
}
template <int BITS> struct bin_writer {
unsigned_type abs_value;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
}
};
void on_bin() {
if (spec.has(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type);
}
int num_digits = internal::count_digits<1>(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
bin_writer<1>{abs_value, num_digits});
}
void on_oct() {
int num_digits = internal::count_digits<3>(abs_value);
if (spec.has(HASH_FLAG) && spec.precision <= num_digits) {
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
prefix[prefix_size++] = '0';
}
writer.write_int(num_digits, get_prefix(), spec,
bin_writer<3>{abs_value, num_digits});
}
enum { SEP_SIZE = 1 };
struct num_writer {
unsigned_type abs_value;
int size;
char_type sep;
template <typename It> void operator()(It&& it) const {
basic_string_view<char_type> s(&sep, SEP_SIZE);
it = internal::format_decimal<char_type>(
it, abs_value, size, internal::add_thousands_sep<char_type>(s));
}
};
void on_num() {
int num_digits = internal::count_digits(abs_value);
char_type sep = internal::thousands_sep<char_type>(writer.locale_);
int size = num_digits + SEP_SIZE * ((num_digits - 1) / 3);
writer.write_int(size, get_prefix(), spec,
num_writer{abs_value, size, sep});
}
FMT_NORETURN void on_error() {
FMT_THROW(format_error("invalid type specifier"));
}
};
enum { INF_SIZE = 3 }; // This is an enum to workaround a bug in MSVC.
struct inf_or_nan_writer {
char sign;
bool as_percentage;
const char* str;
size_t size() const {
return static_cast<std::size_t>(INF_SIZE + (sign ? 1 : 0) +
(as_percentage ? 1 : 0));
}
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) const {
if (sign) *it++ = static_cast<char_type>(sign);
it = internal::copy_str<char_type>(
str, str + static_cast<std::size_t>(INF_SIZE), it);
if (as_percentage) *it++ = static_cast<char_type>('%');
}
};
struct double_writer {
char sign;
internal::buffer<char>& buffer;
size_t size() const { return buffer.size() + (sign ? 1 : 0); }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) {
if (sign) *it++ = static_cast<char_type>(sign);
it = internal::copy_str<char_type>(buffer.begin(), buffer.end(), it);
}
};
class grisu_writer {
private:
internal::buffer<char>& digits_;
size_t size_;
char sign_;
int exp_;
internal::gen_digits_params params_;
public:
grisu_writer(char sign, internal::buffer<char>& digits, int exp,
const internal::gen_digits_params& params)
: digits_(digits), sign_(sign), exp_(exp), params_(params) {
int num_digits = static_cast<int>(digits.size());
int full_exp = num_digits + exp - 1;
int precision = params.num_digits > 0 ? params.num_digits : 11;
params_.fixed |= full_exp >= -4 && full_exp < precision;
auto it = internal::grisu_prettify<char>(
digits.data(), num_digits, exp, internal::counting_iterator<char>(),
params_);
size_ = it.count();
}
size_t size() const { return size_ + (sign_ ? 1 : 0); }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) {
if (sign_) *it++ = static_cast<char_type>(sign_);
int num_digits = static_cast<int>(digits_.size());
it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_,
it, params_);
}
};
template <typename Char> struct str_writer {
const Char* s;
size_t size_;
size_t size() const { return size_; }
size_t width() const {
return internal::count_code_points(basic_string_view<Char>(s, size_));
}
template <typename It> void operator()(It&& it) const {
it = internal::copy_str<char_type>(s, s + size_, it);
}
};
template <typename UIntPtr> struct pointer_writer {
UIntPtr value;
int num_digits;
size_t size() const { return num_digits + 2; }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) const {
*it++ = static_cast<char_type>('0');
*it++ = static_cast<char_type>('x');
it = internal::format_uint<4, char_type>(it, value, num_digits);
}
};
public:
/** Constructs a ``basic_writer`` object. */
explicit basic_writer(Range out,
internal::locale_ref loc = internal::locale_ref())
: out_(out.begin()), locale_(loc) {}
iterator out() const { return out_; }
// Writes a value in the format
// <left-padding><value><right-padding>
// where <value> is written by f(it).
template <typename F> void write_padded(const align_spec& spec, F&& f) {
unsigned width = spec.width(); // User-perceived width (in code points).
size_t size = f.size(); // The number of code units.
size_t num_code_points = width != 0 ? f.width() : size;
if (width <= num_code_points) return f(reserve(size));
auto&& it = reserve(width + (size - num_code_points));
char_type fill = static_cast<char_type>(spec.fill());
std::size_t padding = width - num_code_points;
if (spec.align() == ALIGN_RIGHT) {
it = std::fill_n(it, padding, fill);
f(it);
} else if (spec.align() == ALIGN_CENTER) {
std::size_t left_padding = padding / 2;
it = std::fill_n(it, left_padding, fill);
f(it);
it = std::fill_n(it, padding - left_padding, fill);
} else {
f(it);
it = std::fill_n(it, padding, fill);
}
}
void write(int value) { write_decimal(value); }
void write(long value) { write_decimal(value); }
void write(long long value) { write_decimal(value); }
void write(unsigned value) { write_decimal(value); }
void write(unsigned long value) { write_decimal(value); }
void write(unsigned long long value) { write_decimal(value); }
// Writes a formatted integer.
template <typename T, typename Spec>
void write_int(T value, const Spec& spec) {
internal::handle_int_type_spec(spec.type,
int_writer<T, Spec>(*this, value, spec));
}
/**
\rst
Formats *value* and writes it to the buffer.
\endrst
*/
template <typename T, typename FormatSpec, typename... FormatSpecs,
FMT_ENABLE_IF(std::is_integral<T>::value)>
void write(T value, FormatSpec spec, FormatSpecs... specs) {
format_specs s(spec, specs...);
s.align_ = ALIGN_RIGHT;
write_int(value, s);
}
void write(double value, const format_specs& spec = format_specs()) {
write_double(value, spec);
}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void write(long double value, const format_specs& spec = format_specs()) {
write_double(value, spec);
}
// Formats a floating-point number (double or long double).
template <typename T> void write_double(T value, const format_specs& spec);
/** Writes a character to the buffer. */
void write(char value) {
auto&& it = reserve(1);
*it++ = value;
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
void write(Char value) {
auto&& it = reserve(1);
*it++ = value;
}
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void write(string_view value) {
auto&& it = reserve(value.size());
it = internal::copy_str<char_type>(value.begin(), value.end(), it);
}
void write(wstring_view value) {
static_assert(std::is_same<char_type, wchar_t>::value, "");
auto&& it = reserve(value.size());
it = std::copy(value.begin(), value.end(), it);
}
// Writes a formatted string.
template <typename Char>
void write(const Char* s, std::size_t size, const align_spec& spec) {
write_padded(spec, str_writer<Char>{s, size});
}
template <typename Char>
void write(basic_string_view<Char> s,
const format_specs& spec = format_specs()) {
const Char* data = s.data();
std::size_t size = s.size();
if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size)
size = internal::to_unsigned(spec.precision);
write(data, size, spec);
}
template <typename UIntPtr>
void write_pointer(UIntPtr value, const align_spec* spec) {
int num_digits = internal::count_digits<4>(value);
auto pw = pointer_writer<UIntPtr>{value, num_digits};
if (!spec) return pw(reserve(num_digits + 2));
align_spec as = *spec;
if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
write_padded(as, pw);
}
};
using writer = basic_writer<buffer_range<char>>;
template <typename Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base {
public:
@ -2162,6 +2577,12 @@ void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
}
} // namespace internal
template <typename Range>
using basic_writer FMT_DEPRECATED = internal::basic_writer<Range>;
using writer FMT_DEPRECATED = internal::writer;
using wwriter FMT_DEPRECATED =
internal::basic_writer<internal::buffer_range<wchar_t>>;
/** The default argument formatter. */
template <typename Range>
class arg_formatter : public internal::arg_formatter_base<Range> {
@ -2265,429 +2686,6 @@ class FMT_API system_error : public std::runtime_error {
FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
fmt::string_view message) FMT_NOEXCEPT;
/**
This template provides operations for formatting and writing data into a
character range.
*/
template <typename Range> class basic_writer {
public:
typedef typename Range::value_type char_type;
typedef decltype(std::declval<Range>().begin()) iterator;
typedef basic_format_specs<char_type> format_specs;
private:
iterator out_; // Output iterator.
internal::locale_ref locale_;
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
return internal::reserve(out_, n);
}
// Writes a value in the format
// <left-padding><value><right-padding>
// where <value> is written by f(it).
template <typename F> void write_padded(const align_spec& spec, F&& f) {
unsigned width = spec.width(); // User-perceived width (in code points).
size_t size = f.size(); // The number of code units.
size_t num_code_points = width != 0 ? f.width() : size;
if (width <= num_code_points) return f(reserve(size));
auto&& it = reserve(width + (size - num_code_points));
char_type fill = static_cast<char_type>(spec.fill());
std::size_t padding = width - num_code_points;
if (spec.align() == ALIGN_RIGHT) {
it = std::fill_n(it, padding, fill);
f(it);
} else if (spec.align() == ALIGN_CENTER) {
std::size_t left_padding = padding / 2;
it = std::fill_n(it, left_padding, fill);
f(it);
it = std::fill_n(it, padding - left_padding, fill);
} else {
f(it);
it = std::fill_n(it, padding, fill);
}
}
template <typename F> struct padded_int_writer {
size_t size_;
string_view prefix;
char_type fill;
std::size_t padding;
F f;
size_t size() const { return size_; }
size_t width() const { return size_; }
template <typename It> void operator()(It&& it) const {
if (prefix.size() != 0)
it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
it = std::fill_n(it, padding, fill);
f(it);
}
};
// Writes an integer in the format
// <left-padding><prefix><numeric-padding><digits><right-padding>
// where <digits> are written by f(it).
template <typename Spec, typename F>
void write_int(int num_digits, string_view prefix, const Spec& spec, F f) {
std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
char_type fill = static_cast<char_type>(spec.fill());
std::size_t padding = 0;
if (spec.align() == ALIGN_NUMERIC) {
if (spec.width() > size) {
padding = spec.width() - size;
size = spec.width();
}
} else if (spec.precision > num_digits) {
size = prefix.size() + internal::to_unsigned(spec.precision);
padding = internal::to_unsigned(spec.precision - num_digits);
fill = static_cast<char_type>('0');
}
align_spec as = spec;
if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
write_padded(as, padded_int_writer<F>{size, prefix, fill, padding, f});
}
// Writes a decimal integer.
template <typename Int> void write_decimal(Int value) {
typedef typename internal::int_traits<Int>::main_type main_type;
main_type abs_value = static_cast<main_type>(value);
bool is_negative = internal::is_negative(value);
if (is_negative) abs_value = 0 - abs_value;
int num_digits = internal::count_digits(abs_value);
auto&& it =
reserve((is_negative ? 1 : 0) + static_cast<size_t>(num_digits));
if (is_negative) *it++ = static_cast<char_type>('-');
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
}
// The handle_int_type_spec handler that writes an integer.
template <typename Int, typename Spec> struct int_writer {
typedef typename internal::int_traits<Int>::main_type unsigned_type;
basic_writer<Range>& writer;
const Spec& spec;
unsigned_type abs_value;
char prefix[4];
unsigned prefix_size;
string_view get_prefix() const { return string_view(prefix, prefix_size); }
int_writer(basic_writer<Range>& w, Int value, const Spec& s)
: writer(w),
spec(s),
abs_value(static_cast<unsigned_type>(value)),
prefix_size(0) {
if (internal::is_negative(value)) {
prefix[0] = '-';
++prefix_size;
abs_value = 0 - abs_value;
} else if (spec.has(SIGN_FLAG)) {
prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' ';
++prefix_size;
}
}
struct dec_writer {
unsigned_type abs_value;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
}
};
void on_dec() {
int num_digits = internal::count_digits(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
dec_writer{abs_value, num_digits});
}
struct hex_writer {
int_writer& self;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits,
self.spec.type != 'x');
}
};
void on_hex() {
if (spec.has(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type);
}
int num_digits = internal::count_digits<4>(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
hex_writer{*this, num_digits});
}
template <int BITS> struct bin_writer {
unsigned_type abs_value;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
}
};
void on_bin() {
if (spec.has(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type);
}
int num_digits = internal::count_digits<1>(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
bin_writer<1>{abs_value, num_digits});
}
void on_oct() {
int num_digits = internal::count_digits<3>(abs_value);
if (spec.has(HASH_FLAG) && spec.precision <= num_digits) {
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
prefix[prefix_size++] = '0';
}
writer.write_int(num_digits, get_prefix(), spec,
bin_writer<3>{abs_value, num_digits});
}
enum { SEP_SIZE = 1 };
struct num_writer {
unsigned_type abs_value;
int size;
char_type sep;
template <typename It> void operator()(It&& it) const {
basic_string_view<char_type> s(&sep, SEP_SIZE);
it = internal::format_decimal<char_type>(
it, abs_value, size, internal::add_thousands_sep<char_type>(s));
}
};
void on_num() {
int num_digits = internal::count_digits(abs_value);
char_type sep = internal::thousands_sep<char_type>(writer.locale_);
int size = num_digits + SEP_SIZE * ((num_digits - 1) / 3);
writer.write_int(size, get_prefix(), spec,
num_writer{abs_value, size, sep});
}
FMT_NORETURN void on_error() {
FMT_THROW(format_error("invalid type specifier"));
}
};
// Writes a formatted integer.
template <typename T, typename Spec>
void write_int(T value, const Spec& spec) {
internal::handle_int_type_spec(spec.type,
int_writer<T, Spec>(*this, value, spec));
}
enum { INF_SIZE = 3 }; // This is an enum to workaround a bug in MSVC.
struct inf_or_nan_writer {
char sign;
bool as_percentage;
const char* str;
size_t size() const {
return static_cast<std::size_t>(INF_SIZE + (sign ? 1 : 0) +
(as_percentage ? 1 : 0));
}
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) const {
if (sign) *it++ = static_cast<char_type>(sign);
it = internal::copy_str<char_type>(
str, str + static_cast<std::size_t>(INF_SIZE), it);
if (as_percentage) *it++ = static_cast<char_type>('%');
}
};
struct double_writer {
char sign;
internal::buffer<char>& buffer;
size_t size() const { return buffer.size() + (sign ? 1 : 0); }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) {
if (sign) *it++ = static_cast<char_type>(sign);
it = internal::copy_str<char_type>(buffer.begin(), buffer.end(), it);
}
};
class grisu_writer {
private:
internal::buffer<char>& digits_;
size_t size_;
char sign_;
int exp_;
internal::gen_digits_params params_;
public:
grisu_writer(char sign, internal::buffer<char>& digits, int exp,
const internal::gen_digits_params& params)
: digits_(digits), sign_(sign), exp_(exp), params_(params) {
int num_digits = static_cast<int>(digits.size());
int full_exp = num_digits + exp - 1;
int precision = params.num_digits > 0 ? params.num_digits : 11;
params_.fixed |= full_exp >= -4 && full_exp < precision;
auto it = internal::grisu_prettify<char>(
digits.data(), num_digits, exp, internal::counting_iterator<char>(),
params_);
size_ = it.count();
}
size_t size() const { return size_ + (sign_ ? 1 : 0); }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) {
if (sign_) *it++ = static_cast<char_type>(sign_);
int num_digits = static_cast<int>(digits_.size());
it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_,
it, params_);
}
};
// Formats a floating-point number (double or long double).
template <typename T> void write_double(T value, const format_specs& spec);
template <typename Char> struct str_writer {
const Char* s;
size_t size_;
size_t size() const { return size_; }
size_t width() const {
return internal::count_code_points(basic_string_view<Char>(s, size_));
}
template <typename It> void operator()(It&& it) const {
it = internal::copy_str<char_type>(s, s + size_, it);
}
};
template <typename UIntPtr> struct pointer_writer {
UIntPtr value;
int num_digits;
size_t size() const { return num_digits + 2; }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) const {
*it++ = static_cast<char_type>('0');
*it++ = static_cast<char_type>('x');
it = internal::format_uint<4, char_type>(it, value, num_digits);
}
};
template <typename Char, typename ErrorHandler>
friend class internal::arg_formatter_base;
public:
/** Constructs a ``basic_writer`` object. */
explicit basic_writer(Range out,
internal::locale_ref loc = internal::locale_ref())
: out_(out.begin()), locale_(loc) {}
iterator out() const { return out_; }
void write(int value) { write_decimal(value); }
void write(long value) { write_decimal(value); }
void write(long long value) { write_decimal(value); }
void write(unsigned value) { write_decimal(value); }
void write(unsigned long value) { write_decimal(value); }
void write(unsigned long long value) { write_decimal(value); }
/**
\rst
Formats *value* and writes it to the buffer.
\endrst
*/
template <typename T, typename FormatSpec, typename... FormatSpecs,
FMT_ENABLE_IF(std::is_integral<T>::value)>
void write(T value, FormatSpec spec, FormatSpecs... specs) {
format_specs s(spec, specs...);
s.align_ = ALIGN_RIGHT;
write_int(value, s);
}
void write(double value, const format_specs& spec = format_specs()) {
write_double(value, spec);
}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void write(long double value, const format_specs& spec = format_specs()) {
write_double(value, spec);
}
/** Writes a character to the buffer. */
void write(char value) {
auto&& it = reserve(1);
*it++ = value;
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
void write(Char value) {
auto&& it = reserve(1);
*it++ = value;
}
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void write(string_view value) {
auto&& it = reserve(value.size());
it = internal::copy_str<char_type>(value.begin(), value.end(), it);
}
void write(wstring_view value) {
static_assert(std::is_same<char_type, wchar_t>::value, "");
auto&& it = reserve(value.size());
it = std::copy(value.begin(), value.end(), it);
}
// Writes a formatted string.
template <typename Char>
void write(const Char* s, std::size_t size, const align_spec& spec) {
write_padded(spec, str_writer<Char>{s, size});
}
template <typename Char>
void write(basic_string_view<Char> s,
const format_specs& spec = format_specs()) {
const Char* data = s.data();
std::size_t size = s.size();
if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size)
size = internal::to_unsigned(spec.precision);
write(data, size, spec);
}
template <typename UIntPtr>
void write_pointer(UIntPtr value, const align_spec* spec) {
int num_digits = internal::count_digits<4>(value);
auto pw = pointer_writer<UIntPtr>{value, num_digits};
if (!spec) return pw(reserve(num_digits + 2));
align_spec as = *spec;
if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
write_padded(as, pw);
}
};
struct float_spec_handler {
char type;
bool upper;
@ -2726,7 +2724,8 @@ struct float_spec_handler {
template <typename Range>
template <typename T>
void basic_writer<Range>::write_double(T value, const format_specs& spec) {
void internal::basic_writer<Range>::write_double(T value,
const format_specs& spec) {
// Check type.
float_spec_handler handler(static_cast<char>(spec.type));
internal::handle_float_type_spec(handler.type, handler);

View File

@ -34,7 +34,7 @@
using std::size_t;
using fmt::basic_memory_buffer;
using fmt::basic_writer;
using fmt::internal::basic_writer;
using fmt::format;
using fmt::format_error;
using fmt::memory_buffer;
@ -100,7 +100,7 @@ template <typename Char, typename T>
::testing::AssertionResult check_write(const T& value, const char* type) {
fmt::basic_memory_buffer<Char> buffer;
using range = fmt::internal::buffer_range<Char>;
fmt::basic_writer<range> writer(buffer);
basic_writer<range> writer(buffer);
writer.write(value);
std::basic_string<Char> actual = to_string(buffer);
std::basic_string<Char> expected;
@ -582,7 +582,7 @@ TEST(StringViewTest, Ctor) {
TEST(WriterTest, Data) {
memory_buffer buf;
fmt::writer w(buf);
fmt::internal::writer w(buf);
w.write(42);
EXPECT_EQ("42", to_string(buf));
}
@ -649,13 +649,13 @@ TEST(WriterTest, WriteLongDouble) {
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
memory_buffer buf;
fmt::writer writer(buf);
fmt::internal::writer writer(buf);
for (int i = 0; i < 100; ++i) writer.write(1.23456789);
}
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
memory_buffer buf;
fmt::writer writer(buf);
fmt::internal::writer writer(buf);
// Fill the buffer.
for (int i = 0; i < fmt::inline_buffer_size; ++i) writer.write(' ');
writer.write(1.2);
@ -671,14 +671,10 @@ TEST(WriterTest, WriteWideChar) { CHECK_WRITE_WCHAR(L'a'); }
TEST(WriterTest, WriteString) {
CHECK_WRITE_CHAR("abc");
CHECK_WRITE_WCHAR("abc");
// The following line shouldn't compile:
// std::declval<fmt::basic_writer<fmt::buffer>>().write(L"abc");
}
TEST(WriterTest, WriteWideString) {
CHECK_WRITE_WCHAR(L"abc");
// The following line shouldn't compile:
// std::declval<fmt::basic_writer<fmt::wbuffer>>().write("abc");
}
TEST(FormatToTest, FormatWithoutArgs) {