forked from fmtlib/fmt
Make fill independent on code unit type
This commit is contained in:
@ -301,6 +301,8 @@ template <typename T> struct type_identity {
|
|||||||
};
|
};
|
||||||
template <typename T> using type_identity_t = typename type_identity<T>::type;
|
template <typename T> using type_identity_t = typename type_identity<T>::type;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
using make_unsigned_t = typename std::make_unsigned<T>::type;
|
||||||
|
template <typename T>
|
||||||
using underlying_t = typename std::underlying_type<T>::type;
|
using underlying_t = typename std::underlying_type<T>::type;
|
||||||
|
|
||||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
|
||||||
@ -399,10 +401,9 @@ template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
|
|||||||
|
|
||||||
// Casts a nonnegative integer to unsigned.
|
// Casts a nonnegative integer to unsigned.
|
||||||
template <typename Int>
|
template <typename Int>
|
||||||
FMT_CONSTEXPR auto to_unsigned(Int value) ->
|
FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t<Int> {
|
||||||
typename std::make_unsigned<Int>::type {
|
|
||||||
FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
|
FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
|
||||||
return static_cast<typename std::make_unsigned<Int>::type>(value);
|
return static_cast<make_unsigned_t<Int>>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A heuristic to detect std::string and std::[experimental::]string_view.
|
// A heuristic to detect std::string and std::[experimental::]string_view.
|
||||||
@ -2029,27 +2030,52 @@ using sign_t = sign::type;
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// Workaround an array initialization issue in gcc 4.8.
|
template <typename Char>
|
||||||
template <typename Char> struct fill_t {
|
using unsigned_char = typename conditional_t<std::is_integral<Char>::value,
|
||||||
|
std::make_unsigned<Char>,
|
||||||
|
type_identity<unsigned>>::type;
|
||||||
|
|
||||||
|
struct fill_t {
|
||||||
private:
|
private:
|
||||||
enum { max_size = 4 };
|
enum { max_size = 4 };
|
||||||
Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
|
char data_[max_size] = {' '};
|
||||||
unsigned char size_ = 1;
|
unsigned char size_ = 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
template <typename Char>
|
||||||
FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
|
FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
|
||||||
auto size = s.size();
|
auto size = s.size();
|
||||||
FMT_ASSERT(size <= max_size, "invalid fill");
|
|
||||||
for (size_t i = 0; i < size; ++i) data_[i] = s[i];
|
|
||||||
size_ = static_cast<unsigned char>(size);
|
size_ = static_cast<unsigned char>(size);
|
||||||
|
if (size == 1) {
|
||||||
|
unsigned uchar = static_cast<unsigned_char<Char>>(s[0]);
|
||||||
|
data_[0] = static_cast<char>(uchar);
|
||||||
|
data_[1] = static_cast<char>(uchar >> 8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FMT_ASSERT(size <= max_size, "invalid fill");
|
||||||
|
for (size_t i = 0; i < size; ++i) data_[i] = static_cast<char>(s[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void operator=(char c) {
|
||||||
|
data_[0] = c;
|
||||||
|
size_ = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto size() const -> size_t { return size_; }
|
constexpr auto size() const -> size_t { return size_; }
|
||||||
constexpr auto data() const -> const Char* { return data_; }
|
|
||||||
|
|
||||||
FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
|
template <typename Char> constexpr auto get() const -> Char {
|
||||||
FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
|
using uchar = unsigned char;
|
||||||
return data_[index];
|
return static_cast<Char>(static_cast<uchar>(data_[0]) |
|
||||||
|
(static_cast<uchar>(data_[1]) << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
|
||||||
|
constexpr auto data() const -> const Char* {
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
|
constexpr auto data() const -> const Char* {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@ -2087,7 +2113,7 @@ template <typename Char = char> struct format_specs {
|
|||||||
bool upper : 1; // An uppercase version e.g. 'X' for 'x'.
|
bool upper : 1; // An uppercase version e.g. 'X' for 'x'.
|
||||||
bool alt : 1; // Alternate form ('#').
|
bool alt : 1; // Alternate form ('#').
|
||||||
bool localized : 1;
|
bool localized : 1;
|
||||||
detail::fill_t<Char> fill;
|
detail::fill_t fill;
|
||||||
|
|
||||||
constexpr format_specs()
|
constexpr format_specs()
|
||||||
: width(0),
|
: width(0),
|
||||||
@ -2389,7 +2415,7 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
|
|||||||
if (specs.align == align::none) {
|
if (specs.align == align::none) {
|
||||||
// Ignore 0 if align is specified for compatibility with std::format.
|
// Ignore 0 if align is specified for compatibility with std::format.
|
||||||
specs.align = align::numeric;
|
specs.align = align::numeric;
|
||||||
specs.fill[0] = Char('0');
|
specs.fill = '0';
|
||||||
}
|
}
|
||||||
++begin;
|
++begin;
|
||||||
break;
|
break;
|
||||||
@ -2480,7 +2506,8 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
|
|||||||
}
|
}
|
||||||
auto align = parse_align(to_ascii(*fill_end));
|
auto align = parse_align(to_ascii(*fill_end));
|
||||||
enter_state(state::align, align != align::none);
|
enter_state(state::align, align != align::none);
|
||||||
specs.fill = {begin, to_unsigned(fill_end - begin)};
|
specs.fill =
|
||||||
|
basic_string_view<Char>(begin, to_unsigned(fill_end - begin));
|
||||||
specs.align = align;
|
specs.align = align;
|
||||||
begin = fill_end + 1;
|
begin = fill_end + 1;
|
||||||
}
|
}
|
||||||
|
@ -1711,13 +1711,14 @@ constexpr auto convert_float(T value) -> convert_float_result<T> {
|
|||||||
return static_cast<convert_float_result<T>>(value);
|
return static_cast<convert_float_result<T>>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename Char, typename OutputIt>
|
||||||
FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
|
FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, const fill_t& fill)
|
||||||
const fill_t<Char>& fill) -> OutputIt {
|
-> OutputIt {
|
||||||
auto fill_size = fill.size();
|
auto fill_size = fill.size();
|
||||||
if (fill_size == 1) return detail::fill_n(it, n, fill[0]);
|
if (fill_size == 1) return detail::fill_n(it, n, fill.template get<Char>());
|
||||||
auto data = fill.data();
|
if (const Char* data = fill.template data<Char>()) {
|
||||||
for (size_t i = 0; i < n; ++i) it = copy<Char>(data, data + fill_size, it);
|
for (size_t i = 0; i < n; ++i) it = copy<Char>(data, data + fill_size, it);
|
||||||
|
}
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1737,9 +1738,9 @@ FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs<Char>& specs,
|
|||||||
size_t left_padding = padding >> shifts[specs.align];
|
size_t left_padding = padding >> shifts[specs.align];
|
||||||
size_t right_padding = padding - left_padding;
|
size_t right_padding = padding - left_padding;
|
||||||
auto it = reserve(out, size + padding * specs.fill.size());
|
auto it = reserve(out, size + padding * specs.fill.size());
|
||||||
if (left_padding != 0) it = fill(it, left_padding, specs.fill);
|
if (left_padding != 0) it = fill<Char>(it, left_padding, specs.fill);
|
||||||
it = f(it);
|
it = f(it);
|
||||||
if (right_padding != 0) it = fill(it, right_padding, specs.fill);
|
if (right_padding != 0) it = fill<Char>(it, right_padding, specs.fill);
|
||||||
return base_iterator(out, it);
|
return base_iterator(out, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1788,17 +1789,11 @@ template <typename Char> struct find_escape_result {
|
|||||||
uint32_t cp;
|
uint32_t cp;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
using make_unsigned_char =
|
|
||||||
typename conditional_t<std::is_integral<Char>::value,
|
|
||||||
std::make_unsigned<Char>,
|
|
||||||
type_identity<uint32_t>>::type;
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
auto find_escape(const Char* begin, const Char* end)
|
auto find_escape(const Char* begin, const Char* end)
|
||||||
-> find_escape_result<Char> {
|
-> find_escape_result<Char> {
|
||||||
for (; begin != end; ++begin) {
|
for (; begin != end; ++begin) {
|
||||||
uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
|
uint32_t cp = static_cast<unsigned_char<Char>>(*begin);
|
||||||
if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
|
if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
|
||||||
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
||||||
}
|
}
|
||||||
@ -2385,7 +2380,7 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
|
|||||||
report_error("invalid fill character '{'");
|
report_error("invalid fill character '{'");
|
||||||
return begin;
|
return begin;
|
||||||
}
|
}
|
||||||
specs.fill = {begin, to_unsigned(p - begin)};
|
specs.fill = basic_string_view<Char>(begin, to_unsigned(p - begin));
|
||||||
begin = p + 1;
|
begin = p + 1;
|
||||||
} else {
|
} else {
|
||||||
++begin;
|
++begin;
|
||||||
@ -2464,8 +2459,8 @@ FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
|
|||||||
auto size = str_size + (sign ? 1 : 0);
|
auto size = str_size + (sign ? 1 : 0);
|
||||||
// Replace '0'-padding with space for non-finite values.
|
// Replace '0'-padding with space for non-finite values.
|
||||||
const bool is_zero_fill =
|
const bool is_zero_fill =
|
||||||
specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
|
specs.fill.size() == 1 && specs.fill.template get<Char>() == '0';
|
||||||
if (is_zero_fill) specs.fill[0] = static_cast<Char>(' ');
|
if (is_zero_fill) specs.fill = ' ';
|
||||||
return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
|
return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
|
||||||
if (sign) *it++ = detail::sign<Char>(sign);
|
if (sign) *it++ = detail::sign<Char>(sign);
|
||||||
return copy<Char>(str, str + str_size, it);
|
return copy<Char>(str, str + str_size, it);
|
||||||
@ -4163,7 +4158,7 @@ template <typename T> struct formatter<nested_view<T>> {
|
|||||||
template <typename T> struct nested_formatter {
|
template <typename T> struct nested_formatter {
|
||||||
private:
|
private:
|
||||||
int width_;
|
int width_;
|
||||||
detail::fill_t<char> fill_;
|
detail::fill_t fill_;
|
||||||
align_t align_ : 4;
|
align_t align_ : 4;
|
||||||
formatter<T> formatter_;
|
formatter<T> formatter_;
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
|||||||
}
|
}
|
||||||
fmt_specs.sign = sign::none;
|
fmt_specs.sign = sign::none;
|
||||||
fmt_specs.alt = false;
|
fmt_specs.alt = false;
|
||||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
fmt_specs.fill = ' '; // Ignore '0' flag for char types.
|
||||||
// align::numeric needs to be overwritten here since the '0' flag is
|
// align::numeric needs to be overwritten here since the '0' flag is
|
||||||
// ignored for non-numeric types
|
// ignored for non-numeric types
|
||||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||||
@ -319,7 +319,7 @@ void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
|
|||||||
specs.sign = sign::plus;
|
specs.sign = sign::plus;
|
||||||
break;
|
break;
|
||||||
case '0':
|
case '0':
|
||||||
specs.fill[0] = '0';
|
specs.fill = '0';
|
||||||
break;
|
break;
|
||||||
case ' ':
|
case ' ':
|
||||||
if (specs.sign != sign::plus) specs.sign = sign::space;
|
if (specs.sign != sign::plus) specs.sign = sign::space;
|
||||||
@ -346,7 +346,7 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
|
|||||||
++it;
|
++it;
|
||||||
arg_index = value != -1 ? value : max_value<int>();
|
arg_index = value != -1 ? value : max_value<int>();
|
||||||
} else {
|
} else {
|
||||||
if (c == '0') specs.fill[0] = '0';
|
if (c == '0') specs.fill = '0';
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
// Nonzero value means that we parsed width and don't need to
|
// Nonzero value means that we parsed width and don't need to
|
||||||
// parse it or flags again, so return now.
|
// parse it or flags again, so return now.
|
||||||
@ -477,7 +477,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|||||||
// specified, the '0' flag is ignored
|
// specified, the '0' flag is ignored
|
||||||
if (specs.precision >= 0 && arg.is_integral()) {
|
if (specs.precision >= 0 && arg.is_integral()) {
|
||||||
// Ignore '0' for non-numeric types or if '-' present.
|
// Ignore '0' for non-numeric types or if '-' present.
|
||||||
specs.fill[0] = ' ';
|
specs.fill = ' ';
|
||||||
}
|
}
|
||||||
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
|
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
|
||||||
auto str = arg.visit(get_cstring<Char>());
|
auto str = arg.visit(get_cstring<Char>());
|
||||||
@ -488,12 +488,12 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|||||||
arg = make_arg<basic_printf_context<Char>>(sv);
|
arg = make_arg<basic_printf_context<Char>>(sv);
|
||||||
}
|
}
|
||||||
if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
|
if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
|
||||||
if (specs.fill[0] == '0') {
|
if (specs.fill.template get<Char>() == '0') {
|
||||||
if (arg.is_arithmetic() && specs.align != align::left)
|
if (arg.is_arithmetic() && specs.align != align::left)
|
||||||
specs.align = align::numeric;
|
specs.align = align::numeric;
|
||||||
else
|
else
|
||||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
||||||
// flag is also present.
|
// flag is also present.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse length and convert the argument to the required type.
|
// Parse length and convert the argument to the required type.
|
||||||
|
@ -504,7 +504,7 @@ template <size_t N> constexpr auto parse_test_specs(const char (&s)[N]) {
|
|||||||
|
|
||||||
TEST(core_test, constexpr_parse_format_specs) {
|
TEST(core_test, constexpr_parse_format_specs) {
|
||||||
static_assert(parse_test_specs("<").align == fmt::align::left, "");
|
static_assert(parse_test_specs("<").align == fmt::align::left, "");
|
||||||
static_assert(parse_test_specs("*^").fill[0] == '*', "");
|
static_assert(parse_test_specs("*^").fill.get<char>() == '*', "");
|
||||||
static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
|
static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
|
||||||
static_assert(parse_test_specs("-").sign == fmt::sign::minus, "");
|
static_assert(parse_test_specs("-").sign == fmt::sign::minus, "");
|
||||||
static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
|
static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
|
||||||
|
Reference in New Issue
Block a user