Replace buffer with range
This commit is contained in:
parent
d165d9c483
commit
c3d6c5fc4c
@ -52,8 +52,7 @@
|
|||||||
// Check if exceptions are disabled.
|
// Check if exceptions are disabled.
|
||||||
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
|
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
|
||||||
# define FMT_EXCEPTIONS 0
|
# define FMT_EXCEPTIONS 0
|
||||||
#endif
|
#elif FMT_MSC_VER && !_HAS_EXCEPTIONS
|
||||||
#if FMT_MSC_VER && !_HAS_EXCEPTIONS
|
|
||||||
# define FMT_EXCEPTIONS 0
|
# define FMT_EXCEPTIONS 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef FMT_EXCEPTIONS
|
#ifndef FMT_EXCEPTIONS
|
||||||
@ -565,12 +564,9 @@ class basic_arg {
|
|||||||
public:
|
public:
|
||||||
class handle {
|
class handle {
|
||||||
public:
|
public:
|
||||||
explicit handle(internal::custom_value<Context> custom)
|
explicit handle(internal::custom_value<Context> custom): custom_(custom) {}
|
||||||
: custom_(custom) {}
|
|
||||||
|
|
||||||
void format(basic_buffer<char_type> &buf, Context &ctx) {
|
void format(Context &ctx) { custom_.format(custom_.value, ctx); }
|
||||||
custom_.format(buf, custom_.value, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
internal::custom_value<Context> custom_;
|
internal::custom_value<Context> custom_;
|
||||||
@ -681,7 +677,7 @@ class arg_map {
|
|||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
struct arg {
|
struct arg {
|
||||||
fmt::basic_string_view<char_type> name;
|
basic_string_view<char_type> name;
|
||||||
basic_arg<Context> value;
|
basic_arg<Context> value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -699,7 +695,7 @@ class arg_map {
|
|||||||
~arg_map() { delete [] map_; }
|
~arg_map() { delete [] map_; }
|
||||||
|
|
||||||
const basic_arg<Context>
|
const basic_arg<Context>
|
||||||
*find(const fmt::basic_string_view<char_type> &name) const {
|
*find(const basic_string_view<char_type> &name) const {
|
||||||
// The list is unsorted, so just return the first matching name.
|
// The list is unsorted, so just return the first matching name.
|
||||||
for (auto it = map_, end = map_ + size_; it != end; ++it) {
|
for (auto it = map_, end = map_ + size_; it != end; ++it) {
|
||||||
if (it->name == name)
|
if (it->name == name)
|
||||||
@ -924,8 +920,8 @@ enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
|
|||||||
FMT_API void vprint_colored(Color c, string_view format, format_args args);
|
FMT_API void vprint_colored(Color c, string_view format, format_args args);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Formats a string and prints it to stdout using ANSI escape sequences
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
to specify color (experimental).
|
specify color (experimental).
|
||||||
Example:
|
Example:
|
||||||
print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
|
print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
*/
|
*/
|
||||||
|
@ -342,6 +342,9 @@ class basic_buffer {
|
|||||||
|
|
||||||
virtual ~basic_buffer() {}
|
virtual ~basic_buffer() {}
|
||||||
|
|
||||||
|
T *begin() FMT_NOEXCEPT { return ptr_; }
|
||||||
|
T *end() FMT_NOEXCEPT { return ptr_ + capacity_; }
|
||||||
|
|
||||||
/** Returns the size of this buffer. */
|
/** Returns the size of this buffer. */
|
||||||
std::size_t size() const FMT_NOEXCEPT { return size_; }
|
std::size_t size() const FMT_NOEXCEPT { return size_; }
|
||||||
|
|
||||||
@ -1307,26 +1310,9 @@ class arg_formatter_base {
|
|||||||
FMT_DISALLOW_COPY_AND_ASSIGN(arg_formatter_base);
|
FMT_DISALLOW_COPY_AND_ASSIGN(arg_formatter_base);
|
||||||
|
|
||||||
void write_char(Char value) {
|
void write_char(Char value) {
|
||||||
using pointer_type = typename writer_type::pointer_type;
|
writer_.write_padded(1, specs_, [value](auto &it) {
|
||||||
Char fill = internal::char_traits<Char>::cast(specs_.fill());
|
*it++ = internal::char_traits<Char>::cast(value);
|
||||||
pointer_type out = pointer_type();
|
});
|
||||||
const unsigned character_width = 1;
|
|
||||||
if (specs_.width_ > character_width) {
|
|
||||||
out = writer_.grow_buffer(specs_.width_);
|
|
||||||
if (specs_.align_ == ALIGN_RIGHT) {
|
|
||||||
std::uninitialized_fill_n(out, specs_.width_ - character_width, fill);
|
|
||||||
out += specs_.width_ - character_width;
|
|
||||||
} else if (specs_.align_ == ALIGN_CENTER) {
|
|
||||||
out = writer_.fill_padding(out, specs_.width_,
|
|
||||||
internal::const_check(character_width), fill);
|
|
||||||
} else {
|
|
||||||
std::uninitialized_fill_n(out + character_width,
|
|
||||||
specs_.width_ - character_width, fill);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out = writer_.grow_buffer(character_width);
|
|
||||||
}
|
|
||||||
*out = internal::char_traits<Char>::cast(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_pointer(const void *p) {
|
void write_pointer(const void *p) {
|
||||||
@ -2159,7 +2145,7 @@ FMT_API void format_system_error(fmt::buffer &out, int error_code,
|
|||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
This template provides operations for formatting and writing data into a
|
This template provides operations for formatting and writing data into a
|
||||||
character buffer.
|
character range.
|
||||||
|
|
||||||
You can use one of the following typedefs for common character types:
|
You can use one of the following typedefs for common character types:
|
||||||
|
|
||||||
@ -2173,79 +2159,202 @@ FMT_API void format_system_error(fmt::buffer &out, int error_code,
|
|||||||
|
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename Buffer>
|
template <typename Range>
|
||||||
class basic_writer {
|
class basic_writer {
|
||||||
public:
|
public:
|
||||||
using char_type = typename Buffer::value_type;
|
using char_type = typename Range::value_type;
|
||||||
typedef basic_format_specs<char_type> format_specs;
|
using format_specs = basic_format_specs<char_type>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Output buffer.
|
using iterator = decltype(std::declval<Range>().begin());
|
||||||
Buffer &buffer_;
|
|
||||||
|
// Output range.
|
||||||
|
iterator begin_;
|
||||||
|
decltype(std::declval<Range>().end()) end_;
|
||||||
|
|
||||||
std::unique_ptr<locale_provider> locale_;
|
std::unique_ptr<locale_provider> locale_;
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(basic_writer);
|
FMT_DISALLOW_COPY_AND_ASSIGN(basic_writer);
|
||||||
|
|
||||||
#if FMT_SECURE_SCL
|
#if FMT_SECURE_SCL
|
||||||
typedef stdext::checked_array_iterator<Char*> pointer_type;
|
using pointer_type = stdext::checked_array_iterator<char_type*>;
|
||||||
// Returns pointer value.
|
// Returns pointer value.
|
||||||
static char_type *get(pointer_type p) { return p.base(); }
|
static char_type *get(pointer_type p) { return p.base(); }
|
||||||
#else
|
#else
|
||||||
typedef char_type *pointer_type;
|
using pointer_type = char_type*;
|
||||||
static char_type *get(char_type *p) { return p; }
|
static char_type *get(char_type *p) { return p; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Fills the padding around the content and returns the pointer to the
|
template <typename Category>
|
||||||
// content area.
|
void do_reserve(std::size_t, Category) {}
|
||||||
static pointer_type fill_padding(pointer_type buffer,
|
|
||||||
unsigned total_size, std::size_t content_size, wchar_t fill);
|
|
||||||
|
|
||||||
// Grows the buffer by n characters and returns a pointer to the newly
|
void do_reserve(std::size_t n, std::random_access_iterator_tag) {
|
||||||
// allocated area.
|
(void)(begin_ + n);
|
||||||
pointer_type grow_buffer(std::size_t n) {
|
|
||||||
std::size_t size = buffer_.size();
|
|
||||||
buffer_.resize(size + n);
|
|
||||||
return internal::make_ptr(&buffer_[size], n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes an unsigned decimal integer.
|
// Attempts to reserve space for n characters in the output range.
|
||||||
template <typename UInt>
|
void reserve(std::size_t n) {
|
||||||
char_type *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) {
|
using category = typename std::iterator_traits<iterator>::iterator_category;
|
||||||
unsigned num_digits = internal::count_digits(value);
|
do_reserve(n, category());
|
||||||
char_type *ptr = get(grow_buffer(prefix_size + num_digits));
|
}
|
||||||
internal::format_decimal(ptr + prefix_size, value, num_digits);
|
|
||||||
return ptr;
|
// Writes a value in the format
|
||||||
|
// <left-padding><value><right-padding>
|
||||||
|
// where <value> is written by f(it).
|
||||||
|
template <typename F>
|
||||||
|
void write_padded(std::size_t size, const align_spec &spec, F f);
|
||||||
|
|
||||||
|
// 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(unsigned num_digits, string_view prefix,
|
||||||
|
const Spec &spec, F f) {
|
||||||
|
unsigned size = prefix.size() + num_digits;
|
||||||
|
auto fill = spec.fill();
|
||||||
|
unsigned 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() + spec.precision();
|
||||||
|
padding = spec.precision() - num_digits;
|
||||||
|
fill = '0';
|
||||||
|
}
|
||||||
|
write_padded(size, spec, [prefix, fill, padding, f](auto &it) {
|
||||||
|
if (prefix.size() != 0)
|
||||||
|
it = std::uninitialized_copy_n(prefix.data(), prefix.size(), it);
|
||||||
|
it = std::uninitialized_fill_n(it, padding, fill);
|
||||||
|
f(it);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes a decimal integer.
|
// Writes a decimal integer.
|
||||||
template <typename Int>
|
template <typename Int>
|
||||||
void write_decimal(Int value) {
|
void write_decimal(Int value) {
|
||||||
typedef typename internal::int_traits<Int>::main_type main_type;
|
using main_type = typename internal::int_traits<Int>::main_type;
|
||||||
main_type abs_value = static_cast<main_type>(value);
|
main_type abs_value = static_cast<main_type>(value);
|
||||||
if (internal::is_negative(value)) {
|
bool is_negative = internal::is_negative(value);
|
||||||
|
if (is_negative)
|
||||||
abs_value = 0 - abs_value;
|
abs_value = 0 - abs_value;
|
||||||
*write_unsigned_decimal(abs_value, 1) = '-';
|
unsigned num_digits = internal::count_digits(abs_value);
|
||||||
} else {
|
reserve((is_negative ? 1 : 0) + num_digits);
|
||||||
write_unsigned_decimal(abs_value, 0);
|
if (is_negative)
|
||||||
|
*begin_++ = '-';
|
||||||
|
internal::format_decimal(begin_, abs_value, num_digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The handle_int_type_spec handler that writes an integer.
|
||||||
|
template <typename Int, typename Spec>
|
||||||
|
struct int_writer {
|
||||||
|
using unsigned_type = typename internal::int_traits<Int>::main_type;
|
||||||
|
|
||||||
|
basic_writer<Range> &writer;
|
||||||
|
const Spec &spec;
|
||||||
|
unsigned_type abs_value;
|
||||||
|
char prefix[4];
|
||||||
|
unsigned prefix_size = 0;
|
||||||
|
|
||||||
|
string_view get_prefix() const { return string_view(prefix, prefix_size); }
|
||||||
|
|
||||||
|
// Counts the number of digits in abs_value. BITS = log2(radix).
|
||||||
|
template <unsigned BITS>
|
||||||
|
unsigned count_digits() const {
|
||||||
|
unsigned_type n = abs_value;
|
||||||
|
unsigned num_digits = 0;
|
||||||
|
do {
|
||||||
|
++num_digits;
|
||||||
|
} while ((n >>= BITS) != 0);
|
||||||
|
return num_digits;
|
||||||
|
}
|
||||||
|
|
||||||
|
int_writer(basic_writer<Range> &w, Int value, const Spec &s)
|
||||||
|
: writer(w), spec(s), abs_value(static_cast<unsigned_type>(value)) {
|
||||||
|
if (internal::is_negative(value)) {
|
||||||
|
prefix[0] = '-';
|
||||||
|
++prefix_size;
|
||||||
|
abs_value = 0 - abs_value;
|
||||||
|
} else if (spec.flag(SIGN_FLAG)) {
|
||||||
|
prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' ';
|
||||||
|
++prefix_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare a buffer for integer formatting.
|
void on_dec() {
|
||||||
pointer_type prepare_int_buffer(unsigned num_digits,
|
unsigned num_digits = internal::count_digits(abs_value);
|
||||||
const empty_spec &, const char *prefix, unsigned prefix_size) {
|
writer.write_int(num_digits, get_prefix(), spec,
|
||||||
unsigned size = prefix_size + num_digits;
|
[this, num_digits](auto &it) {
|
||||||
pointer_type p = grow_buffer(size);
|
internal::format_decimal(it, abs_value, 0);
|
||||||
std::uninitialized_copy(prefix, prefix + prefix_size, p);
|
it += num_digits;
|
||||||
return p + size - 1;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Spec>
|
void on_hex() {
|
||||||
pointer_type prepare_int_buffer(unsigned num_digits,
|
if (spec.flag(HASH_FLAG)) {
|
||||||
const Spec &spec, const char *prefix, unsigned prefix_size);
|
prefix[prefix_size++] = '0';
|
||||||
|
prefix[prefix_size++] = spec.type();
|
||||||
|
}
|
||||||
|
writer.write_int(count_digits<4>(), get_prefix(), spec, [this](auto &it) {
|
||||||
|
auto n = abs_value;
|
||||||
|
const char *digits = spec.type() == 'x' ?
|
||||||
|
"0123456789abcdef" : "0123456789ABCDEF";
|
||||||
|
do {
|
||||||
|
*it-- = digits[n & 0xf];
|
||||||
|
} while ((n >>= 4) != 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_bin() {
|
||||||
|
if (spec.flag(HASH_FLAG)) {
|
||||||
|
prefix[prefix_size++] = '0';
|
||||||
|
prefix[prefix_size++] = spec.type();
|
||||||
|
}
|
||||||
|
writer.write_int(count_digits<1>(), get_prefix(), spec, [this](auto &it) {
|
||||||
|
auto n = abs_value;
|
||||||
|
do {
|
||||||
|
*it-- = static_cast<char_type>('0' + (n & 1));
|
||||||
|
} while ((n >>= 1) != 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_oct() {
|
||||||
|
if (spec.flag(HASH_FLAG))
|
||||||
|
prefix[prefix_size++] = '0';
|
||||||
|
writer.write_int(count_digits<3>(), get_prefix(), spec, [this](auto &it) {
|
||||||
|
auto n = abs_value;
|
||||||
|
do {
|
||||||
|
*it-- = static_cast<char_type>('0' + (n & 7));
|
||||||
|
} while ((n >>= 3) != 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_num() {
|
||||||
|
unsigned num_digits = internal::count_digits(abs_value);
|
||||||
|
char_type thousands_sep =
|
||||||
|
internal::thousands_sep<char_type>(writer.locale_.get());
|
||||||
|
basic_string_view<char_type> sep(&thousands_sep, 1);
|
||||||
|
unsigned size = static_cast<unsigned>(
|
||||||
|
num_digits + sep.size() * ((num_digits - 1) / 3));
|
||||||
|
writer.write_int(size, get_prefix(), spec, [this, size, sep](auto &it) {
|
||||||
|
internal::format_decimal(it, abs_value, 0,
|
||||||
|
internal::add_thousands_sep<char_type>(sep));
|
||||||
|
it += size;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_error() {
|
||||||
|
FMT_THROW(format_error("invalid type specifier"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Writes a formatted integer.
|
// Writes a formatted integer.
|
||||||
template <typename T, typename Spec>
|
template <typename T, typename Spec>
|
||||||
void write_int(T value, const Spec& spec);
|
void write_int(T value, const Spec &spec) {
|
||||||
|
internal::handle_int_type_spec(spec.type(),
|
||||||
|
int_writer<T, Spec>(*this, value, spec));
|
||||||
|
}
|
||||||
|
|
||||||
// Formats a floating-point number (double or long double).
|
// Formats a floating-point number (double or long double).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -2253,8 +2362,11 @@ class basic_writer {
|
|||||||
|
|
||||||
// Writes a formatted string.
|
// Writes a formatted string.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
pointer_type write_str(
|
void write_str(const Char *s, std::size_t size, const align_spec &spec) {
|
||||||
const Char *s, std::size_t size, const align_spec &spec);
|
write_padded(size, spec, [s, size](iterator &it) {
|
||||||
|
it = std::uninitialized_copy_n(s, size, it);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void write_str(basic_string_view<Char> str, const format_specs &spec);
|
void write_str(basic_string_view<Char> str, const format_specs &spec);
|
||||||
@ -2272,48 +2384,8 @@ class basic_writer {
|
|||||||
friend class internal::arg_formatter_base;
|
friend class internal::arg_formatter_base;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/** Constructs a ``basic_writer`` object. */
|
||||||
Constructs a ``basic_writer`` object.
|
explicit basic_writer(Range &r) : begin_(r.begin()), end_(r.end()) {}
|
||||||
*/
|
|
||||||
explicit basic_writer(Buffer &b) : buffer_(b) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Destroys the ``basic_writer`` object.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
virtual ~basic_writer() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Returns the total number of characters written.
|
|
||||||
*/
|
|
||||||
std::size_t size() const { return buffer_.size(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
Returns a pointer to the output buffer content. No terminating null
|
|
||||||
character is appended.
|
|
||||||
*/
|
|
||||||
const char_type *data() const FMT_NOEXCEPT { return &buffer_[0]; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
Returns a pointer to the output buffer content with terminating null
|
|
||||||
character appended.
|
|
||||||
*/
|
|
||||||
const char_type *c_str() const {
|
|
||||||
std::size_t size = buffer_.size();
|
|
||||||
buffer_.reserve(size + 1);
|
|
||||||
buffer_[size] = '\0';
|
|
||||||
return &buffer_[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Returns the content of the output buffer as an `std::string`.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
std::basic_string<char_type> str() const {
|
|
||||||
return std::basic_string<char_type>(&buffer_[0], buffer_.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(int value) {
|
void write(int value) {
|
||||||
write_decimal(value);
|
write_decimal(value);
|
||||||
@ -2350,16 +2422,14 @@ class basic_writer {
|
|||||||
write_double(value, format_specs());
|
write_double(value, format_specs());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Writes a character to the buffer. */
|
||||||
Writes a character to the buffer.
|
|
||||||
*/
|
|
||||||
void write(char value) {
|
void write(char value) {
|
||||||
buffer_.push_back(value);
|
*begin_++ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(wchar_t value) {
|
void write(wchar_t value) {
|
||||||
internal::require_wchar<char_type>();
|
internal::require_wchar<char_type>();
|
||||||
buffer_.push_back(value);
|
*begin_++ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2368,251 +2438,66 @@ class basic_writer {
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
void write(string_view value) {
|
void write(string_view value) {
|
||||||
const char *str = value.data();
|
begin_ = std::uninitialized_copy(value.begin(), value.end(), begin_);
|
||||||
buffer_.append(str, str + value.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(basic_string_view<wchar_t> value) {
|
void write(wstring_view value) {
|
||||||
internal::require_wchar<char_type>();
|
internal::require_wchar<char_type>();
|
||||||
const wchar_t *str = value.data();
|
begin_ = std::uninitialized_copy(value.begin(), value.end(), begin_);
|
||||||
buffer_.append(str, str + value.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... FormatSpecs>
|
template <typename... FormatSpecs>
|
||||||
void write(basic_string_view<char_type> str, FormatSpecs... specs) {
|
void write(basic_string_view<char_type> str, FormatSpecs... specs) {
|
||||||
write_str(str, format_specs(specs...));
|
write_str(str, format_specs(specs...));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() FMT_NOEXCEPT { buffer_.resize(0); }
|
|
||||||
|
|
||||||
Buffer &buffer() FMT_NOEXCEPT { return buffer_; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Buffer>
|
template <typename Range>
|
||||||
template <typename Char>
|
template <typename F>
|
||||||
typename basic_writer<Buffer>::pointer_type basic_writer<Buffer>::write_str(
|
void basic_writer<Range>::write_padded(
|
||||||
const Char *s, std::size_t size, const align_spec &spec) {
|
std::size_t size, const align_spec &spec, F f) {
|
||||||
pointer_type out = pointer_type();
|
unsigned width = spec.width();
|
||||||
if (spec.width() > size) {
|
if (width <= size) {
|
||||||
out = grow_buffer(spec.width());
|
reserve(size);
|
||||||
|
return f(begin_);
|
||||||
|
}
|
||||||
|
reserve(width);
|
||||||
char_type fill = internal::char_traits<char_type>::cast(spec.fill());
|
char_type fill = internal::char_traits<char_type>::cast(spec.fill());
|
||||||
|
std::size_t padding = width - size;
|
||||||
if (spec.align() == ALIGN_RIGHT) {
|
if (spec.align() == ALIGN_RIGHT) {
|
||||||
std::uninitialized_fill_n(out, spec.width() - size, fill);
|
begin_ = std::uninitialized_fill_n(begin_, padding, fill);
|
||||||
out += spec.width() - size;
|
f(begin_);
|
||||||
} else if (spec.align() == ALIGN_CENTER) {
|
} else if (spec.align() == ALIGN_CENTER) {
|
||||||
out = fill_padding(out, spec.width(), size, fill);
|
std::size_t left_padding = padding / 2;
|
||||||
|
begin_ = std::uninitialized_fill_n(begin_, left_padding, fill);
|
||||||
|
f(begin_);
|
||||||
|
begin_ = std::uninitialized_fill_n(begin_, padding - left_padding, fill);
|
||||||
} else {
|
} else {
|
||||||
std::uninitialized_fill_n(out + size, spec.width() - size, fill);
|
f(begin_);
|
||||||
|
std::uninitialized_fill_n(begin_, padding, fill);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
out = grow_buffer(size);
|
|
||||||
}
|
|
||||||
std::uninitialized_copy(s, s + size, out);
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Buffer>
|
|
||||||
|
template <typename Range>
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void basic_writer<Buffer>::write_str(
|
void basic_writer<Range>::write_str(
|
||||||
basic_string_view<Char> s, const format_specs &spec) {
|
basic_string_view<Char> s, const format_specs &spec) {
|
||||||
// Check if Char is convertible to char_type.
|
// Check if Char is convertible to char_type.
|
||||||
internal::char_traits<char_type>::convert(Char());
|
internal::char_traits<char_type>::convert(Char());
|
||||||
const Char *str_value = s.data();
|
const Char *data = s.data();
|
||||||
std::size_t str_size = s.size();
|
std::size_t size = s.size();
|
||||||
if (str_size == 0 && !str_value)
|
if (size == 0 && !data)
|
||||||
FMT_THROW(format_error("string pointer is null"));
|
FMT_THROW(format_error("string pointer is null"));
|
||||||
std::size_t precision = static_cast<std::size_t>(spec.precision_);
|
std::size_t precision = static_cast<std::size_t>(spec.precision_);
|
||||||
if (spec.precision_ >= 0 && precision < str_size)
|
if (spec.precision_ >= 0 && precision < size)
|
||||||
str_size = precision;
|
size = precision;
|
||||||
write_str(str_value, str_size, spec);
|
write_str(data, size, spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Buffer>
|
template <typename Range>
|
||||||
typename basic_writer<Buffer>::pointer_type basic_writer<Buffer>::fill_padding(
|
|
||||||
pointer_type buffer, unsigned total_size,
|
|
||||||
std::size_t content_size, wchar_t fill) {
|
|
||||||
std::size_t padding = total_size - content_size;
|
|
||||||
std::size_t left_padding = padding / 2;
|
|
||||||
char_type fill_char = internal::char_traits<char_type>::cast(fill);
|
|
||||||
std::uninitialized_fill_n(buffer, left_padding, fill_char);
|
|
||||||
buffer += left_padding;
|
|
||||||
pointer_type content = buffer;
|
|
||||||
std::uninitialized_fill_n(buffer + content_size,
|
|
||||||
padding - left_padding, fill_char);
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Buffer>
|
|
||||||
template <typename Spec>
|
|
||||||
typename basic_writer<Buffer>::pointer_type
|
|
||||||
basic_writer<Buffer>::prepare_int_buffer(
|
|
||||||
unsigned num_digits, const Spec &spec,
|
|
||||||
const char *prefix, unsigned prefix_size) {
|
|
||||||
unsigned width = spec.width();
|
|
||||||
alignment align = spec.align();
|
|
||||||
char_type fill = internal::char_traits<char_type>::cast(spec.fill());
|
|
||||||
if (spec.precision() > static_cast<int>(num_digits)) {
|
|
||||||
// Octal prefix '0' is counted as a digit, so ignore it if precision
|
|
||||||
// is specified.
|
|
||||||
if (prefix_size > 0 && prefix[prefix_size - 1] == '0')
|
|
||||||
--prefix_size;
|
|
||||||
unsigned number_size =
|
|
||||||
prefix_size + internal::to_unsigned(spec.precision());
|
|
||||||
align_spec subspec(number_size, '0', ALIGN_NUMERIC);
|
|
||||||
if (number_size >= width)
|
|
||||||
return prepare_int_buffer(num_digits, subspec, prefix, prefix_size);
|
|
||||||
buffer_.reserve(width);
|
|
||||||
unsigned fill_size = width - number_size;
|
|
||||||
if (align != ALIGN_LEFT) {
|
|
||||||
pointer_type p = grow_buffer(fill_size);
|
|
||||||
std::uninitialized_fill(p, p + fill_size, fill);
|
|
||||||
}
|
|
||||||
pointer_type result = prepare_int_buffer(
|
|
||||||
num_digits, subspec, prefix, prefix_size);
|
|
||||||
if (align == ALIGN_LEFT) {
|
|
||||||
pointer_type p = grow_buffer(fill_size);
|
|
||||||
std::uninitialized_fill(p, p + fill_size, fill);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
unsigned size = prefix_size + num_digits;
|
|
||||||
if (width <= size) {
|
|
||||||
pointer_type p = grow_buffer(size);
|
|
||||||
std::uninitialized_copy(prefix, prefix + prefix_size, p);
|
|
||||||
return p + size - 1;
|
|
||||||
}
|
|
||||||
pointer_type p = grow_buffer(width);
|
|
||||||
pointer_type end = p + width;
|
|
||||||
if (align == ALIGN_LEFT) {
|
|
||||||
std::uninitialized_copy(prefix, prefix + prefix_size, p);
|
|
||||||
p += size;
|
|
||||||
std::uninitialized_fill(p, end, fill);
|
|
||||||
} else if (align == ALIGN_CENTER) {
|
|
||||||
p = fill_padding(p, width, size, fill);
|
|
||||||
std::uninitialized_copy(prefix, prefix + prefix_size, p);
|
|
||||||
p += size;
|
|
||||||
} else {
|
|
||||||
if (align == ALIGN_NUMERIC) {
|
|
||||||
if (prefix_size != 0) {
|
|
||||||
p = std::uninitialized_copy(prefix, prefix + prefix_size, p);
|
|
||||||
size -= prefix_size;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::uninitialized_copy(prefix, prefix + prefix_size, end - size);
|
|
||||||
}
|
|
||||||
std::uninitialized_fill(p, end - size, fill);
|
|
||||||
p = end;
|
|
||||||
}
|
|
||||||
return p - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Buffer>
|
|
||||||
template <typename T, typename Spec>
|
|
||||||
void basic_writer<Buffer>::write_int(T value, const Spec& spec) {
|
|
||||||
using unsigned_type = typename internal::int_traits<T>::main_type;
|
|
||||||
struct spec_handler {
|
|
||||||
basic_writer<Buffer> &writer;
|
|
||||||
const Spec& spec;
|
|
||||||
unsigned prefix_size = 0;
|
|
||||||
unsigned_type abs_value;
|
|
||||||
char prefix[4] = "";
|
|
||||||
|
|
||||||
spec_handler(basic_writer<Buffer> &w, T val, const Spec& s)
|
|
||||||
: writer(w), spec(s), abs_value(static_cast<unsigned_type>(val)) {
|
|
||||||
if (internal::is_negative(val)) {
|
|
||||||
prefix[0] = '-';
|
|
||||||
++prefix_size;
|
|
||||||
abs_value = 0 - abs_value;
|
|
||||||
} else if (spec.flag(SIGN_FLAG)) {
|
|
||||||
prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' ';
|
|
||||||
++prefix_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_dec() {
|
|
||||||
unsigned num_digits = internal::count_digits(abs_value);
|
|
||||||
pointer_type p =
|
|
||||||
writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1;
|
|
||||||
internal::format_decimal(get(p), abs_value, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_hex() {
|
|
||||||
unsigned_type n = abs_value;
|
|
||||||
if (spec.flag(HASH_FLAG)) {
|
|
||||||
prefix[prefix_size++] = '0';
|
|
||||||
prefix[prefix_size++] = spec.type();
|
|
||||||
}
|
|
||||||
unsigned num_digits = 0;
|
|
||||||
do {
|
|
||||||
++num_digits;
|
|
||||||
} while ((n >>= 4) != 0);
|
|
||||||
char_type *p =
|
|
||||||
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
|
|
||||||
n = abs_value;
|
|
||||||
const char *digits = spec.type() == 'x' ?
|
|
||||||
"0123456789abcdef" : "0123456789ABCDEF";
|
|
||||||
do {
|
|
||||||
*p-- = digits[n & 0xf];
|
|
||||||
} while ((n >>= 4) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_bin() {
|
|
||||||
unsigned_type n = abs_value;
|
|
||||||
if (spec.flag(HASH_FLAG)) {
|
|
||||||
prefix[prefix_size++] = '0';
|
|
||||||
prefix[prefix_size++] = spec.type();
|
|
||||||
}
|
|
||||||
unsigned num_digits = 0;
|
|
||||||
do {
|
|
||||||
++num_digits;
|
|
||||||
} while ((n >>= 1) != 0);
|
|
||||||
char_type *p =
|
|
||||||
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
|
|
||||||
n = abs_value;
|
|
||||||
do {
|
|
||||||
*p-- = static_cast<char_type>('0' + (n & 1));
|
|
||||||
} while ((n >>= 1) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_oct() {
|
|
||||||
unsigned_type n = abs_value;
|
|
||||||
if (spec.flag(HASH_FLAG))
|
|
||||||
prefix[prefix_size++] = '0';
|
|
||||||
unsigned num_digits = 0;
|
|
||||||
do {
|
|
||||||
++num_digits;
|
|
||||||
} while ((n >>= 3) != 0);
|
|
||||||
char_type *p =
|
|
||||||
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
|
|
||||||
n = abs_value;
|
|
||||||
do {
|
|
||||||
*p-- = static_cast<char_type>('0' + (n & 7));
|
|
||||||
} while ((n >>= 3) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_num() {
|
|
||||||
unsigned num_digits = internal::count_digits(abs_value);
|
|
||||||
char_type thousands_sep =
|
|
||||||
internal::thousands_sep<char_type>(writer.locale_.get());
|
|
||||||
fmt::basic_string_view<char_type> sep(&thousands_sep, 1);
|
|
||||||
unsigned size = static_cast<unsigned>(
|
|
||||||
num_digits + sep.size() * ((num_digits - 1) / 3));
|
|
||||||
pointer_type p =
|
|
||||||
writer.prepare_int_buffer(size, spec, prefix, prefix_size) + 1;
|
|
||||||
internal::format_decimal(get(p), abs_value, 0,
|
|
||||||
internal::add_thousands_sep<char_type>(sep));
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_error() {
|
|
||||||
FMT_THROW(format_error("invalid type specifier"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
internal::handle_int_type_spec(spec.type(), spec_handler(*this, value, spec));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Buffer>
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void basic_writer<Buffer>::write_double(T value, const format_specs &spec) {
|
void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||||
// Check type.
|
// Check type.
|
||||||
struct spec_handler {
|
struct spec_handler {
|
||||||
char type;
|
char type;
|
||||||
@ -2667,37 +2552,35 @@ void basic_writer<Buffer>::write_double(T value, const format_specs &spec) {
|
|||||||
if (internal::fputil::isnotanumber(value)) {
|
if (internal::fputil::isnotanumber(value)) {
|
||||||
// Format NaN ourselves because sprintf's output is not consistent
|
// Format NaN ourselves because sprintf's output is not consistent
|
||||||
// across platforms.
|
// across platforms.
|
||||||
std::size_t nan_size = 4;
|
const char *nan = handler.upper ? "NAN" : "nan";
|
||||||
const char *nan = handler.upper ? " NAN" : " nan";
|
std::size_t nan_size = 3;
|
||||||
if (!sign) {
|
reserve(nan_size + (sign ? 1 : 0));
|
||||||
--nan_size;
|
|
||||||
++nan;
|
|
||||||
}
|
|
||||||
pointer_type out = write_str(nan, nan_size, spec);
|
|
||||||
if (sign)
|
if (sign)
|
||||||
*out = sign;
|
*begin_++ = sign;
|
||||||
|
begin_ = std::uninitialized_copy_n(nan, nan_size, begin_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (internal::fputil::isinfinity(value)) {
|
if (internal::fputil::isinfinity(value)) {
|
||||||
// Format infinity ourselves because sprintf's output is not consistent
|
// Format infinity ourselves because sprintf's output is not consistent
|
||||||
// across platforms.
|
// across platforms.
|
||||||
std::size_t inf_size = 4;
|
const char *inf = handler.upper ? "INF" : "inf";
|
||||||
const char *inf = handler.upper ? " INF" : " inf";
|
std::size_t inf_size = 3;
|
||||||
if (!sign) {
|
reserve(inf_size + (sign ? 1 : 0));
|
||||||
--inf_size;
|
|
||||||
++inf;
|
|
||||||
}
|
|
||||||
pointer_type out = write_str(inf, inf_size, spec);
|
|
||||||
if (sign)
|
if (sign)
|
||||||
*out = sign;
|
*begin_++ = sign;
|
||||||
|
begin_ = std::uninitialized_copy_n(inf, inf_size, begin_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t offset = buffer_.size();
|
// TODO: buffered_range that wraps a range and adds necessary buffering if the
|
||||||
|
// latter is not contiguous.
|
||||||
|
basic_memory_buffer<char_type> buffer;
|
||||||
|
|
||||||
|
std::size_t offset = 0;
|
||||||
unsigned width = spec.width();
|
unsigned width = spec.width();
|
||||||
if (sign) {
|
if (sign) {
|
||||||
buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u));
|
buffer.reserve(width > 1u ? width : 1u);
|
||||||
if (width > 0)
|
if (width > 0)
|
||||||
--width;
|
--width;
|
||||||
++offset;
|
++offset;
|
||||||
@ -2733,54 +2616,39 @@ void basic_writer<Buffer>::write_double(T value, const format_specs &spec) {
|
|||||||
unsigned n = 0;
|
unsigned n = 0;
|
||||||
char_type *start = 0;
|
char_type *start = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
std::size_t buffer_size = buffer_.capacity() - offset;
|
std::size_t buffer_size = buffer.capacity() - offset;
|
||||||
#if FMT_MSC_VER
|
#if FMT_MSC_VER
|
||||||
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
|
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
|
||||||
// space for at least one extra character to make the size non-zero.
|
// space for at least one extra character to make the size non-zero.
|
||||||
// Note that the buffer's capacity will increase by more than 1.
|
// Note that the buffer's capacity may increase by more than 1.
|
||||||
if (buffer_size == 0) {
|
if (buffer_size == 0) {
|
||||||
buffer_.reserve(offset + 1);
|
buffer.reserve(offset + 1);
|
||||||
buffer_size = buffer_.capacity() - offset;
|
buffer_size = buffer.capacity() - offset;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
start = &buffer_[offset];
|
start = &buffer[offset];
|
||||||
int result = internal::char_traits<char_type>::format_float(
|
int result = internal::char_traits<char_type>::format_float(
|
||||||
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
|
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
|
||||||
if (result >= 0) {
|
if (result >= 0) {
|
||||||
n = internal::to_unsigned(result);
|
n = internal::to_unsigned(result);
|
||||||
if (offset + n < buffer_.capacity())
|
if (offset + n < buffer.capacity())
|
||||||
break; // The buffer is large enough - continue with formatting.
|
break; // The buffer is large enough - continue with formatting.
|
||||||
buffer_.reserve(offset + n + 1);
|
buffer.reserve(offset + n + 1);
|
||||||
} else {
|
} else {
|
||||||
// If result is negative we ask to increase the capacity by at least 1,
|
// If result is negative we ask to increase the capacity by at least 1,
|
||||||
// but as std::vector, the buffer grows exponentially.
|
// but as std::vector, the buffer grows exponentially.
|
||||||
buffer_.reserve(buffer_.capacity() + 1);
|
buffer.reserve(buffer.capacity() + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sign) {
|
|
||||||
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
|
|
||||||
*start != ' ') {
|
|
||||||
*(start - 1) = sign;
|
|
||||||
sign = 0;
|
|
||||||
} else {
|
|
||||||
*(start - 1) = fill;
|
|
||||||
}
|
|
||||||
++n;
|
|
||||||
}
|
|
||||||
if (spec.align() == ALIGN_CENTER && spec.width() > n) {
|
|
||||||
width = spec.width();
|
|
||||||
pointer_type p = grow_buffer(width);
|
|
||||||
std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(char_type));
|
|
||||||
fill_padding(p, spec.width(), n, fill);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (spec.fill() != ' ' || sign) {
|
|
||||||
while (*start == ' ')
|
|
||||||
*start++ = fill;
|
|
||||||
if (sign)
|
if (sign)
|
||||||
*(start - 1) = sign;
|
++n;
|
||||||
|
write_padded(n, spec, [n, sign, &buffer](auto &it) mutable {
|
||||||
|
if (sign) {
|
||||||
|
*it++ = sign;
|
||||||
|
--n;
|
||||||
}
|
}
|
||||||
grow_buffer(n);
|
it = std::uninitialized_copy_n(buffer.begin(), n, it);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reports a system error without throwing an exception.
|
// Reports a system error without throwing an exception.
|
||||||
|
Loading…
Reference in New Issue
Block a user