diff --git a/AK/PrintfImplementation.h b/AK/PrintfImplementation.h index 2d6d5fbce1..1da4294c23 100644 --- a/AK/PrintfImplementation.h +++ b/AK/PrintfImplementation.h @@ -20,96 +20,73 @@ extern "C" size_t strlen(const char*); namespace PrintfImplementation { template -ALWAYS_INLINE int print_hex(PutChFunc putch, CharType*& bufptr, T number, bool upper_case, bool alternate_form, bool left_pad, bool zero_pad, u8 field_width) +ALWAYS_INLINE int print_hex(PutChFunc putch, CharType*& bufptr, T number, bool upper_case, bool alternate_form, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision) { - int ret = 0; + constexpr const char* printf_hex_digits_lower = "0123456789abcdef"; + constexpr const char* printf_hex_digits_upper = "0123456789ABCDEF"; - int digits = 0; + u32 digits = 0; for (T n = number; n > 0; n >>= 4) ++digits; if (digits == 0) digits = 1; - if (left_pad) { - int stop_at = field_width - digits; - if (alternate_form) - stop_at -= 2; + bool not_zero = number != 0; - while (ret < stop_at) { - putch(bufptr, ' '); - ++ret; - } - } - - if (alternate_form) { - putch(bufptr, '0'); - putch(bufptr, 'x'); - ret += 2; - field_width += 2; - } - - if (zero_pad) { - while (ret < field_width - digits) { - putch(bufptr, '0'); - ++ret; - } - } - - if (number == 0) { - putch(bufptr, '0'); - ++ret; - } else { - u8 shift_count = digits * 4; - while (shift_count) { - constexpr const char* printf_hex_digits_lower = "0123456789abcdef"; - constexpr const char* printf_hex_digits_upper = "0123456789ABCDEF"; - - shift_count -= 4; - putch(bufptr, - upper_case - ? printf_hex_digits_upper[(number >> shift_count) & 0x0f] - : printf_hex_digits_lower[(number >> shift_count) & 0x0f]); - ++ret; - } - } - - return ret; -} - -template -ALWAYS_INLINE int print_number(PutChFunc putch, CharType*& bufptr, u32 number, bool left_pad, bool zero_pad, u32 field_width) -{ - u32 divisor = 1000000000; - char ch; - char padding = 1; char buf[16]; char* p = buf; - for (;;) { - ch = '0' + (number / divisor); - number %= divisor; - if (ch != '0') - padding = 0; - if (!padding || divisor == 1) - *(p++) = ch; - if (divisor == 1) - break; - divisor /= 10; + if (!(has_precision && precision == 0 && !not_zero)) { + if (number == 0) { + (*p++) = '0'; + if (precision > 0) + precision--; + } else { + u8 shift_count = digits * 4; + while (shift_count) { + shift_count -= 4; + (*p++) = upper_case + ? printf_hex_digits_upper[(number >> shift_count) & 0x0f] + : printf_hex_digits_lower[(number >> shift_count) & 0x0f]; + if (precision > 0) + precision--; + } + } } size_t numlen = p - buf; - if (!field_width || field_width < numlen) - field_width = numlen; + + if (!field_width || field_width < (numlen + has_precision * precision + (alternate_form * 2 * not_zero))) + field_width = numlen + has_precision * precision + alternate_form * 2 * not_zero; + + if ((zero_pad && !has_precision) && (alternate_form && not_zero)) { + putch(bufptr, '0'); + putch(bufptr, 'x'); + } + if (!left_pad) { - for (unsigned i = 0; i < field_width - numlen; ++i) { - putch(bufptr, zero_pad ? '0' : ' '); + for (unsigned i = 0; i < field_width - numlen - has_precision * precision - alternate_form * 2 * not_zero; ++i) { + putch(bufptr, (zero_pad && !has_precision) ? '0' : ' '); } } + + if (!(zero_pad && !has_precision) && (alternate_form && not_zero)) { + putch(bufptr, '0'); + putch(bufptr, 'x'); + } + + if (has_precision) { + for (u32 i = 0; i < precision; ++i) { + putch(bufptr, '0'); + } + } + for (unsigned i = 0; i < numlen; ++i) { putch(bufptr, buf[i]); } + if (left_pad) { - for (unsigned i = 0; i < field_width - numlen; ++i) { + for (unsigned i = 0; i < field_width - numlen - has_precision * precision - alternate_form * 2 * not_zero; ++i) { putch(bufptr, ' '); } } @@ -118,39 +95,62 @@ ALWAYS_INLINE int print_number(PutChFunc putch, CharType*& bufptr, u32 number, b } template -ALWAYS_INLINE int print_u64(PutChFunc putch, CharType*& bufptr, u64 number, bool left_pad, bool zero_pad, u32 field_width) +ALWAYS_INLINE int print_decimal(PutChFunc putch, CharType*& bufptr, u64 number, bool sign, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision) { u64 divisor = 10000000000000000000LLU; char ch; char padding = 1; - char buf[16]; + char buf[21]; char* p = buf; - for (;;) { - ch = '0' + (number / divisor); - number %= divisor; - if (ch != '0') - padding = 0; - if (!padding || divisor == 1) - *(p++) = ch; - if (divisor == 1) - break; - divisor /= 10; + if (!(has_precision && precision == 0 && number == 0)) { + for (;;) { + ch = '0' + (number / divisor); + number %= divisor; + if (ch != '0') + padding = 0; + if (!padding || divisor == 1) { + *(p++) = ch; + if (precision > 0) + precision--; + } + if (divisor == 1) + break; + divisor /= 10; + } } size_t numlen = p - buf; - if (!field_width || field_width < numlen) - field_width = numlen; + + if (!field_width || field_width < (numlen + has_precision * precision + (sign || always_sign))) + field_width = numlen + has_precision * precision + (sign || always_sign); + + if ((zero_pad && !has_precision) && (sign || always_sign)) { + putch(bufptr, sign ? '-' : '+'); + } + if (!left_pad) { - for (unsigned i = 0; i < field_width - numlen; ++i) { - putch(bufptr, zero_pad ? '0' : ' '); + for (unsigned i = 0; i < field_width - numlen - has_precision * precision - (sign || always_sign); ++i) { + putch(bufptr, (zero_pad && !has_precision) ? '0' : ' '); } } + + if (!(zero_pad && !has_precision) && (sign || always_sign)) { + putch(bufptr, sign ? '-' : '+'); + } + + if (has_precision) { + for (u32 i = 0; i < precision; ++i) { + putch(bufptr, '0'); + } + } + for (unsigned i = 0; i < numlen; ++i) { putch(bufptr, buf[i]); } + if (left_pad) { - for (unsigned i = 0; i < field_width - numlen; ++i) { + for (unsigned i = 0; i < field_width - numlen - has_precision * precision - (sign || always_sign); ++i) { putch(bufptr, ' '); } } @@ -159,40 +159,40 @@ ALWAYS_INLINE int print_u64(PutChFunc putch, CharType*& bufptr, u64 number, bool } template -ALWAYS_INLINE int print_double(PutChFunc putch, CharType*& bufptr, double number, bool left_pad, bool zero_pad, u32 field_width, u32 fraction_length) +ALWAYS_INLINE int print_double(PutChFunc putch, CharType*& bufptr, double number, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, u32 precision) { int length = 0; - if (number < 0) { - putch(bufptr, '-'); + u32 whole_width = (field_width >= precision + 1) ? field_width - precision - 1 : 0; + + bool sign = number < 0; + + if (sign) + number = -number; + + length = print_decimal(putch, bufptr, (i64)number, sign, always_sign, left_pad, zero_pad, whole_width, false, 1); + if (precision > 0) { + putch(bufptr, '.'); length++; - number = 0 - number; + double fraction = number - (i64)number; + + for (u32 i = 0; i < precision; ++i) + fraction = fraction * 10; + + return length + print_decimal(putch, bufptr, (i64)fraction, false, false, false, true, precision, false, 1); } - length = print_u64(putch, bufptr, (i64)number, left_pad, zero_pad, field_width); - putch(bufptr, '.'); - length++; - double fraction = number - (i64)number; - - for (u32 i = 0; i < fraction_length; ++i) - fraction = fraction * 10; - - return length + print_u64(putch, bufptr, (i64)fraction, false, true, fraction_length); + return length; } template -ALWAYS_INLINE int print_i64(PutChFunc putch, CharType*& bufptr, i64 number, bool left_pad, bool zero_pad, u32 field_width) +ALWAYS_INLINE int print_i64(PutChFunc putch, CharType*& bufptr, i64 number, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision) { - // FIXME: This won't work if there is padding. ' -17' becomes '- 17'. - if (number < 0) { - putch(bufptr, '-'); - return print_u64(putch, bufptr, 0 - number, left_pad, zero_pad, field_width) + 1; - } - return print_u64(putch, bufptr, number, left_pad, zero_pad, field_width); + return print_decimal(putch, bufptr, (number < 0) ? 0 - number : number, number < 0, always_sign, left_pad, zero_pad, field_width, has_precision, precision); } template -ALWAYS_INLINE int print_octal_number(PutChFunc putch, CharType*& bufptr, u32 number, bool left_pad, bool zero_pad, u32 field_width) +ALWAYS_INLINE int print_octal_number(PutChFunc putch, CharType*& bufptr, u64 number, bool alternate_form, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision) { u32 divisor = 134217728; char ch; @@ -200,31 +200,52 @@ ALWAYS_INLINE int print_octal_number(PutChFunc putch, CharType*& bufptr, u32 num char buf[32]; char* p = buf; - for (;;) { - ch = '0' + (number / divisor); - number %= divisor; - if (ch != '0') - padding = 0; - if (!padding || divisor == 1) - *(p++) = ch; - if (divisor == 1) - break; - divisor /= 8; + if (alternate_form) { + (*p++) = '0'; + if (precision > 0) + precision--; + } + + if (!(has_precision && precision == 0 && number == 0)) { + for (;;) { + ch = '0' + (number / divisor); + number %= divisor; + if (ch != '0') + padding = 0; + if (!padding || divisor == 1) { + *(p++) = ch; + if (precision > 0) + precision--; + } + if (divisor == 1) + break; + divisor /= 8; + } } size_t numlen = p - buf; - if (!field_width || field_width < numlen) - field_width = numlen; + + if (!field_width || field_width < (numlen + has_precision * precision)) + field_width = numlen + has_precision * precision; + if (!left_pad) { - for (unsigned i = 0; i < field_width - numlen; ++i) { - putch(bufptr, zero_pad ? '0' : ' '); + for (unsigned i = 0; i < field_width - numlen - has_precision * precision; ++i) { + putch(bufptr, (zero_pad && !has_precision) ? '0' : ' '); } } + + if (has_precision) { + for (u32 i = 0; i < precision; ++i) { + putch(bufptr, '0'); + } + } + for (unsigned i = 0; i < numlen; ++i) { putch(bufptr, buf[i]); } + if (left_pad) { - for (unsigned i = 0; i < field_width - numlen; ++i) { + for (unsigned i = 0; i < field_width - numlen - has_precision * precision; ++i) { putch(bufptr, ' '); } } @@ -233,10 +254,10 @@ ALWAYS_INLINE int print_octal_number(PutChFunc putch, CharType*& bufptr, u32 num } template -ALWAYS_INLINE int print_string(PutChFunc putch, CharType*& bufptr, const char* str, size_t len, bool left_pad, size_t field_width, bool dot, size_t fraction_length, bool has_fraction) +ALWAYS_INLINE int print_string(PutChFunc putch, CharType*& bufptr, const char* str, size_t len, bool left_pad, size_t field_width, bool dot, size_t precision, bool has_fraction) { if (has_fraction) - len = min(len, fraction_length); + len = min(len, precision); if (!dot && (!field_width || field_width < len)) field_width = len; @@ -261,15 +282,9 @@ ALWAYS_INLINE int print_string(PutChFunc putch, CharType*& bufptr, const char* s } template -ALWAYS_INLINE int print_signed_number(PutChFunc putch, CharType*& bufptr, int number, bool left_pad, bool zero_pad, u32 field_width, bool always_sign) +ALWAYS_INLINE int print_signed_number(PutChFunc putch, CharType*& bufptr, int number, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision) { - if (number < 0) { - putch(bufptr, '-'); - return print_number(putch, bufptr, 0 - number, left_pad, zero_pad, field_width) + 1; - } - if (always_sign) - putch(bufptr, '+'); - return print_number(putch, bufptr, number, left_pad, zero_pad, field_width) + always_sign; + return print_decimal(putch, bufptr, (number < 0) ? 0 - number : number, number < 0, always_sign, left_pad, zero_pad, field_width, has_precision, precision); } struct ModifierState { @@ -277,8 +292,8 @@ struct ModifierState { bool zero_pad { false }; bool dot { false }; unsigned field_width { 0 }; - bool has_fraction_length { false }; - unsigned fraction_length { 6 }; + bool has_precision { false }; + unsigned precision { 6 }; unsigned long_qualifiers { 0 }; bool size_qualifier { false }; bool alternate_form { 0 }; @@ -299,14 +314,14 @@ struct PrintfImpl { const char* sp = NextArgument()(ap); if (!sp) sp = "(null)"; - return print_string(m_putch, m_bufptr, sp, strlen(sp), state.left_pad, state.field_width, state.dot, state.fraction_length, state.has_fraction_length); + return print_string(m_putch, m_bufptr, sp, strlen(sp), state.left_pad, state.field_width, state.dot, state.precision, state.has_precision); } ALWAYS_INLINE int format_d(const ModifierState& state, ArgumentListRefT ap) const { if (state.long_qualifiers >= 2) - return print_i64(m_putch, m_bufptr, NextArgument()(ap), state.left_pad, state.zero_pad, state.field_width); + return print_i64(m_putch, m_bufptr, NextArgument()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); - return print_signed_number(m_putch, m_bufptr, NextArgument()(ap), state.left_pad, state.zero_pad, state.field_width, state.always_sign); + return print_signed_number(m_putch, m_bufptr, NextArgument()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); } ALWAYS_INLINE int format_i(const ModifierState& state, ArgumentListRefT ap) const { @@ -315,16 +330,16 @@ struct PrintfImpl { ALWAYS_INLINE int format_u(const ModifierState& state, ArgumentListRefT ap) const { if (state.long_qualifiers >= 2) - return print_u64(m_putch, m_bufptr, NextArgument()(ap), state.left_pad, state.zero_pad, state.field_width); - return print_number(m_putch, m_bufptr, NextArgument()(ap), state.left_pad, state.zero_pad, state.field_width); + return print_decimal(m_putch, m_bufptr, NextArgument()(ap), false, false, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); + return print_decimal(m_putch, m_bufptr, NextArgument()(ap), false, false, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); } ALWAYS_INLINE int format_Q(const ModifierState& state, ArgumentListRefT ap) const { - return print_u64(m_putch, m_bufptr, NextArgument()(ap), state.left_pad, state.zero_pad, state.field_width); + return print_decimal(m_putch, m_bufptr, NextArgument()(ap), false, false, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); } ALWAYS_INLINE int format_q(const ModifierState& state, ArgumentListRefT ap) const { - return print_hex(m_putch, m_bufptr, NextArgument()(ap), false, false, state.left_pad, state.zero_pad, 16); + return print_hex(m_putch, m_bufptr, NextArgument()(ap), false, false, state.left_pad, state.zero_pad, 16, false, 1); } ALWAYS_INLINE int format_g(const ModifierState& state, ArgumentListRefT ap) const { @@ -332,26 +347,23 @@ struct PrintfImpl { } ALWAYS_INLINE int format_f(const ModifierState& state, ArgumentListRefT ap) const { - return print_double(m_putch, m_bufptr, NextArgument()(ap), state.left_pad, state.zero_pad, state.field_width, state.fraction_length); + return print_double(m_putch, m_bufptr, NextArgument()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.precision); } ALWAYS_INLINE int format_o(const ModifierState& state, ArgumentListRefT ap) const { - if (state.alternate_form) - m_putch(m_bufptr, '0'); - - return (state.alternate_form ? 1 : 0) + print_octal_number(m_putch, m_bufptr, NextArgument()(ap), state.left_pad, state.zero_pad, state.field_width); + return print_octal_number(m_putch, m_bufptr, NextArgument()(ap), state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); } ALWAYS_INLINE int format_x(const ModifierState& state, ArgumentListRefT ap) const { if (state.long_qualifiers >= 2) - return print_hex(m_putch, m_bufptr, NextArgument()(ap), false, state.alternate_form, state.left_pad, state.zero_pad, state.field_width); - return print_hex(m_putch, m_bufptr, NextArgument()(ap), false, state.alternate_form, state.left_pad, state.zero_pad, state.field_width); + return print_hex(m_putch, m_bufptr, NextArgument()(ap), false, state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); + return print_hex(m_putch, m_bufptr, NextArgument()(ap), false, state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); } ALWAYS_INLINE int format_X(const ModifierState& state, ArgumentListRefT ap) const { if (state.long_qualifiers >= 2) - return print_hex(m_putch, m_bufptr, NextArgument()(ap), true, state.alternate_form, state.left_pad, state.zero_pad, state.field_width); - return print_hex(m_putch, m_bufptr, NextArgument()(ap), true, state.alternate_form, state.left_pad, state.zero_pad, state.field_width); + return print_hex(m_putch, m_bufptr, NextArgument()(ap), true, state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); + return print_hex(m_putch, m_bufptr, NextArgument()(ap), true, state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision); } ALWAYS_INLINE int format_n(const ModifierState&, ArgumentListRefT ap) const { @@ -360,11 +372,11 @@ struct PrintfImpl { } ALWAYS_INLINE int format_p(const ModifierState&, ArgumentListRefT ap) const { - return print_hex(m_putch, m_bufptr, NextArgument()(ap), false, true, false, true, 8); + return print_hex(m_putch, m_bufptr, NextArgument()(ap), false, true, false, true, 8, false, 1); } ALWAYS_INLINE int format_P(const ModifierState&, ArgumentListRefT ap) const { - return print_hex(m_putch, m_bufptr, NextArgument()(ap), true, true, false, true, 8); + return print_hex(m_putch, m_bufptr, NextArgument()(ap), true, true, false, true, 8, false, 1); } ALWAYS_INLINE int format_percent(const ModifierState&, ArgumentListRefT) const { @@ -374,7 +386,7 @@ struct PrintfImpl { ALWAYS_INLINE int format_c(const ModifierState& state, ArgumentListRefT ap) const { char c = NextArgument()(ap); - return print_string(m_putch, m_bufptr, &c, 1, state.left_pad, state.field_width, state.dot, state.fraction_length, state.has_fraction_length); + return print_string(m_putch, m_bufptr, &c, 1, state.left_pad, state.field_width, state.dot, state.precision, state.has_precision); } ALWAYS_INLINE int format_unrecognized(CharType format_op, const CharType* fmt, const ModifierState&, ArgumentListRefT) const { @@ -429,7 +441,7 @@ ALWAYS_INLINE int printf_internal(PutChFunc putch, IdentityType* buffe if (*(p + 1)) goto one_more; } - if (!state.zero_pad && !state.field_width && !state.has_fraction_length && *p == '0') { + if (!state.zero_pad && !state.field_width && !state.dot && *p == '0') { state.zero_pad = true; if (*(p + 1)) goto one_more; @@ -441,22 +453,21 @@ ALWAYS_INLINE int printf_internal(PutChFunc putch, IdentityType* buffe if (*(p + 1)) goto one_more; } else { - if (!state.has_fraction_length) { - state.has_fraction_length = true; - state.zero_pad = true; - state.fraction_length = 0; + if (!state.has_precision) { + state.has_precision = true; + state.precision = 0; } - state.fraction_length *= 10; - state.fraction_length += *p - '0'; + state.precision *= 10; + state.precision += *p - '0'; if (*(p + 1)) goto one_more; } } if (*p == '*') { if (state.dot) { - state.has_fraction_length = true; + state.has_precision = true; state.zero_pad = true; - state.fraction_length = NextArgument()(ap); + state.precision = NextArgument()(ap); } else { state.field_width = NextArgument()(ap); }