Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions include/frg/formatting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace frg FRG_VISIBILITY {

template<typename T, typename Char = char>
concept SinkFor = requires (T t, const Char *str, Char c, size_t n) {
t.append(str, n, n);
t.append(str, n);
t.append(str);
t.append(c);
Expand Down Expand Up @@ -158,7 +159,8 @@ namespace _fmt_basics {
void print_digits(S &sink, T number, bool negative, int radix,
int width, int precision, char padding, bool left_justify,
bool group_thousands, bool always_sign, bool plus_becomes_space,
bool use_capitals, locale_options<Char> locale_opts) {
bool use_capitals, bool alt_conversion, locale_options<Char> locale_opts) {
using P = frg::FormatterPolicy<Char>;
const char *digits = use_capitals ? "0123456789ABCDEF" : "0123456789abcdef";
char buffer[64];

Expand Down Expand Up @@ -197,13 +199,18 @@ namespace _fmt_basics {
}
};

T remaining_num = number;

// print the number in reverse order and determine #digits.
do {
FRG_ASSERT(k < 64); // TODO: variable number of digits
buffer[k++] = digits[number % radix];
number /= radix;
buffer[k++] = digits[remaining_num % radix];
remaining_num /= radix;
step_grouping();
} while(number);
} while(remaining_num);

if (radix == 8 && alt_conversion && k >= precision && number != 0)
precision = k + 1;

if (k < precision)
for (int i = 0; i < precision - k; i++)
Expand All @@ -218,6 +225,11 @@ namespace _fmt_basics {
if(negative || always_sign || plus_becomes_space)
extra++;

if (radix == 16 && alt_conversion && number)
extra += generic_strlen(use_capitals ? P::hexPrefixUpper : P::hexPrefix);
else if (radix == 2 && alt_conversion && number)
extra += generic_strlen(use_capitals ? P::binPrefixUpper : P::binPrefix);

int final_width = max(k, precision) + extra;

if(!left_justify && final_width < width && padding != '0')
Expand All @@ -231,6 +243,11 @@ namespace _fmt_basics {
else if(plus_becomes_space)
sink.append(' ');

if (radix == 16 && alt_conversion && number)
sink.append(use_capitals ? P::hexPrefixUpper : P::hexPrefix);
if (radix == 2 && alt_conversion && number)
sink.append(use_capitals ? P::binPrefixUpper : P::binPrefix);

if(!left_justify && final_width < width && padding == '0')
for(int i = 0; i < width - final_width; i++)
sink.append(padding);
Expand Down Expand Up @@ -261,18 +278,18 @@ namespace _fmt_basics {
int precision = 1, char padding = ' ', bool left_justify = false,
bool group_thousands = false, bool always_sign = false,
bool plus_becomes_space = false, bool use_capitals = false,
locale_options<Char> locale_opts = {}) {
bool alt_conversion = false, locale_options<Char> locale_opts = {}) {
if(number < 0) {
// This is valid in C (N3220 6.2.6.2) and C++ ([N4950 basic.fundamental 6.8.2.3])
using UnsignedT = std::make_unsigned_t<T>;
auto absv = ~static_cast<UnsignedT>(number) + 1;
print_digits<S, UnsignedT, Char>(sink, absv, true, radix, width, precision, padding,
left_justify, group_thousands, always_sign, plus_becomes_space, use_capitals,
locale_opts);
alt_conversion, locale_opts);
}else{
print_digits<S, T, Char>(sink, number, false, radix, width, precision, padding,
left_justify, group_thousands, always_sign, plus_becomes_space, use_capitals,
locale_opts);
alt_conversion, locale_opts);
}
}

Expand Down Expand Up @@ -341,7 +358,8 @@ namespace _fmt_basics {
void print_float(S &sink, T number, int width = 0, optional<int> precision = 6,
Char padding = ' ', bool left_justify = false, bool alt_conversion = false,
bool use_capitals = false, bool group_thousands = false, bool use_compact = false,
bool exponential_form = false, bool print_hexfloat = false, locale_options<Char> locale_opts = {}) {
bool exponential_form = false, bool print_hexfloat = false, bool always_sign = false,
locale_options<Char> locale_opts = {}) {
using P = frg::FormatterPolicy<Char>;

auto textLength = [](int i, unsigned base = 10, bool ignoreSign = false) {
Expand All @@ -360,7 +378,7 @@ namespace _fmt_basics {

bool inf = __builtin_isinf(number), nan = __builtin_isnan(number);
if (inf || nan) {
auto total_length = 3 + has_sign;
auto total_length = 3 + (has_sign || always_sign);
auto pad_length = width > total_length ? width - total_length : 0;
if (!left_justify) {
while (pad_length > 0) {
Expand All @@ -371,6 +389,8 @@ namespace _fmt_basics {

if (has_sign)
sink.append('-');
else if (always_sign)
sink.append('+');

if (inf)
sink.append(use_capitals ? P::infUpper : P::inf);
Expand Down Expand Up @@ -427,7 +447,7 @@ namespace _fmt_basics {
}
}

auto int_length = has_sign + 3;
auto int_length = (has_sign || always_sign) + 3;
int frac_length = (shift_by >> 2) - trailingZeroes;
auto exp_length = 2 + textLength(exp, 10, true);

Expand Down Expand Up @@ -456,6 +476,8 @@ namespace _fmt_basics {

if (has_sign)
sink.append('-');
else if (always_sign)
sink.append('+');

if (use_capitals)
sink.append(number == 0.0 ? (P::hexPrefixUpperZero) : (P::hexPrefixUpperOne));
Expand Down Expand Up @@ -580,7 +602,7 @@ namespace _fmt_basics {
auto decimal_point_length = print_decimal_point ? generic_strlen(locale_opts.decimal_point) : 0;

// Plus one for the decimal point
int total_length = has_sign + int_length + group_sep_length + decimal_point_length + *precision;
int total_length = (has_sign || always_sign) + int_length + group_sep_length + decimal_point_length + *precision;

// Handle the exponent in the style of `e+09`
if (exponential_form)
Expand All @@ -597,8 +619,10 @@ namespace _fmt_basics {

if (has_sign)
sink.append('-');
else if (always_sign)
sink.append('+');

print_int<S, decltype(integralDigits), Char>(sink, integralDigits, 10, 0, 1, {}, false, group_thousands, false, false, false, locale_opts);
print_int<S, decltype(integralDigits), Char>(sink, integralDigits, 10, 0, 1, {}, false, group_thousands, false, false, false, false, locale_opts);

if (print_decimal_point)
sink.append(locale_opts.decimal_point);
Expand Down
12 changes: 12 additions & 0 deletions include/frg/logging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ struct stack_buffer_logger {
}
}

void append(const char *str, size_t n, size_t m) {
return append(str, frg::min(n, m));
}

private:
stack_buffer_logger *_logger;
char _buffer[Limit];
Expand Down Expand Up @@ -150,6 +154,10 @@ struct container_logger {
}
}

void append(const char *str, size_t n, size_t m) {
return append(str, frg::min(n, m));
}

private:
Container &cont_;
};
Expand Down Expand Up @@ -177,6 +185,10 @@ struct ostream_out {
output.write(str, n);
}

void append(const char *str, size_t n, size_t m) {
return append(str, frg::min(n, m));
}

void append(const char *str) {
output << str;
}
Expand Down
55 changes: 24 additions & 31 deletions include/frg/printf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,9 @@ void do_printf_chars(S &sink, Char t, format_options opts,
sink.append(P::hexPrefix);
_fmt_basics::print_int<S, uintptr_t, Char>(sink, (uintptr_t)pop_arg<void*>(vsp, &opts), 16);
break;
case 'C':
FRG_ASSERT(szmod == printf_size_mod::long_size);
[[fallthrough]];
case 'c':
FRG_ASSERT(!opts.fill_zeros);
FRG_ASSERT(!opts.alt_conversion);
Expand All @@ -691,6 +694,9 @@ void do_printf_chars(S &sink, Char t, format_options opts,
sink.append(pop_arg<char>(vsp, &opts));
}
break;
case 'S':
FRG_ASSERT(szmod == printf_size_mod::long_size);
[[fallthrough]];
case 's': {
FRG_ASSERT(!opts.fill_zeros);
FRG_ASSERT(!opts.alt_conversion);
Expand Down Expand Up @@ -727,20 +733,22 @@ void do_printf_chars(S &sink, Char t, format_options opts,
if(!s)
s = L"(null)";

int length;
if(opts.precision)
length = generic_strnlen(s, *opts.precision);
else
length = generic_strlen(s);
int length = generic_strlen(s);

if(opts.left_justify) {
sink.append(s, length);
if (opts.precision)
sink.append(s, length, *opts.precision);
else
sink.append(s, length);
for(int i = length; i < opts.minimum_width; i++)
sink.append(' ');
}else{
for(int i = length; i < opts.minimum_width; i++)
sink.append(' ');
sink.append(s, length);
if (opts.precision)
sink.append(s, length, *opts.precision);
else
sink.append(s, length);
}
} else {
FRG_ASSERT(!"Requested wchar_t output on Sink that does not support it.");
Expand All @@ -755,8 +763,6 @@ void do_printf_chars(S &sink, Char t, format_options opts,
template<typename Char, Sink S>
void do_printf_ints(S &sink, Char t, format_options opts,
printf_size_mod szmod, va_struct *vsp, locale_options<Char> locale_opts = {}) {
using P = FormatterPolicy<Char>;

auto pad_to_min = [&] {
bool put_sign = opts.always_sign;

Expand Down Expand Up @@ -798,24 +804,19 @@ void do_printf_ints(S &sink, Char t, format_options opts,
_fmt_basics::print_int<S, long, Char>(sink, number, 10, opts.minimum_width,
opts.precision ? *opts.precision : 1, opts.fill_zeros ? '0' : ' ',
opts.left_justify, opts.group_thousands, opts.always_sign,
opts.plus_becomes_space, false, locale_opts);
opts.plus_becomes_space, false, false, locale_opts);
}
} break;
case 'b':
case 'B' : {
auto print = [&] (auto number) {
if (number && opts.alt_conversion) {
opts.minimum_width -= 2;
sink.append(t == 'b' ? P::binPrefix : P::binPrefixUpper);
}

if(opts.precision && *opts.precision == 0 && !number) {
pad_to_min();
}else{
_fmt_basics::print_int<S, decltype(number), Char>(sink, number, 2, opts.minimum_width,
opts.precision ? *opts.precision : 1, opts.fill_zeros ? '0' : ' ',
opts.left_justify, false, opts.always_sign, opts.plus_becomes_space,
false, locale_opts);
t == 'B', opts.alt_conversion, locale_opts);
}
};

Expand All @@ -838,18 +839,13 @@ void do_printf_ints(S &sink, Char t, format_options opts,
} break;
case 'o': {
auto print = [&] (auto number) {
if (number && opts.alt_conversion) {
opts.minimum_width -= 1;
sink.append('0');
}

if(opts.precision && *opts.precision == 0 && !number) {
pad_to_min();
}else{
_fmt_basics::print_int<S, decltype(number), Char>(sink, number, 8, opts.minimum_width,
opts.precision ? *opts.precision : 1, opts.fill_zeros ? '0' : ' ',
opts.left_justify, false, opts.always_sign, opts.plus_becomes_space,
false, locale_opts);
false, opts.alt_conversion, locale_opts);
}
};

Expand All @@ -873,18 +869,13 @@ void do_printf_ints(S &sink, Char t, format_options opts,
case 'x':
case 'X': {
auto print = [&] (auto number) {
if (number && opts.alt_conversion) {
opts.minimum_width -= 2;
sink.append(t == 'x' ? P::hexPrefix : P::hexPrefixUpper);
}

if(opts.precision && *opts.precision == 0 && !number) {
pad_to_min();
}else{
_fmt_basics::print_int<S, decltype(number), Char>(sink, number, 16, opts.minimum_width,
opts.precision ? *opts.precision : 1, opts.fill_zeros ? '0' : ' ',
opts.left_justify, false, opts.always_sign, opts.plus_becomes_space,
t == 'X', locale_opts);
t == 'X', opts.alt_conversion, locale_opts);
}
};

Expand Down Expand Up @@ -914,7 +905,7 @@ void do_printf_ints(S &sink, Char t, format_options opts,
_fmt_basics::print_int<S, decltype(number), Char>(sink, number, 10, opts.minimum_width,
opts.precision ? *opts.precision : 1, opts.fill_zeros ? '0' : ' ',
opts.left_justify, opts.group_thousands, opts.always_sign,
opts.plus_becomes_space, false, locale_opts);
opts.plus_becomes_space, false, false, locale_opts);
}
};

Expand Down Expand Up @@ -983,7 +974,8 @@ void do_printf_floats(S &sink, Char t, format_options opts,
sink, pop_arg<long double>(vsp, &opts),
opts.minimum_width, opts.precision,
Char(opts.fill_zeros ? '0' : ' '), opts.left_justify, opts.alt_conversion,
use_capitals, opts.group_thousands, use_compact, exponent_form, print_hexfloat, locale_opts);
use_capitals, opts.group_thousands, use_compact, exponent_form, print_hexfloat,
opts.always_sign, locale_opts);
break;
}
#endif
Expand All @@ -992,7 +984,8 @@ void do_printf_floats(S &sink, Char t, format_options opts,
sink, pop_arg<double>(vsp, &opts),
opts.minimum_width, opts.precision,
Char(opts.fill_zeros ? '0' : ' '), opts.left_justify, opts.alt_conversion, use_capitals,
opts.group_thousands, use_compact, exponent_form, print_hexfloat, locale_opts);
opts.group_thousands, use_compact, exponent_form, print_hexfloat,
opts.always_sign, locale_opts);
break;
default:
FRG_ASSERT(!"Unexpected printf terminal");
Expand Down
1 change: 1 addition & 0 deletions tests/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ TEST(formatting, printf) {
do_test("1.100000", "%f", 1.1);
do_test("3.140000", "%f", 3.14);
do_test("0.0", "%.1f", 0.0);
do_test("+0.0", "%+.1f", 0.0);

// Test %g
do_test("0", "%g", 0.0);
Expand Down
Loading