#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H
#include "include/llvm-libc-macros/stdfix-macros.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/fixed_point/fx_bits.h"
#include "src/__support/fixed_point/fx_rep.h"
#include "src/__support/integer_to_string.h"
#include "src/__support/libc_assert.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/converter_utils.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/writer.h"
#include <inttypes.h>
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
uint32_t result = 1;
LIBC_ASSERT(exponent < 11);
for (uint32_t i = 0; i < exponent; ++i)
result *= 10;
return result;
}
#define READ_FX_BITS(TYPE) \
do { \
auto fixed_bits = fixed_point::FXBits<TYPE>( \
fixed_point::FXRep<TYPE>::StorageType(to_conv.conv_val_raw)); \
integral = fixed_bits.get_integral(); \
fractional = fixed_bits.get_fraction(); \
exponent = fixed_bits.get_exponent(); \
is_negative = fixed_bits.get_sign(); \
} while (false)
#define APPLY_FX_LENGTH_MODIFIER(LENGTH_MODIFIER) \
do { \
if (to_conv.conv_name == 'r') { \
READ_FX_BITS(LENGTH_MODIFIER fract); \
} else if (to_conv.conv_name == 'R') { \
READ_FX_BITS(unsigned LENGTH_MODIFIER fract); \
} else if (to_conv.conv_name == 'k') { \
READ_FX_BITS(LENGTH_MODIFIER accum); \
} else if (to_conv.conv_name == 'K') { \
READ_FX_BITS(unsigned LENGTH_MODIFIER accum); \
} else { \
LIBC_ASSERT(false && "Invalid conversion name passed to convert_fixed"); \
return FIXED_POINT_CONVERSION_ERROR; \
} \
} while (false)
LIBC_INLINE int convert_fixed(Writer *writer, const FormatSection &to_conv) {
using LARep = fixed_point::FXRep<unsigned long accum>;
using StorageType = LARep::StorageType;
const char a = (to_conv.conv_name & 32) | 'A';
FormatFlags flags = to_conv.flags;
bool is_negative;
int exponent;
StorageType integral;
StorageType fractional;
if (to_conv.length_modifier == LengthModifier::h) {
APPLY_FX_LENGTH_MODIFIER(short);
} else if (to_conv.length_modifier == LengthModifier::l) {
APPLY_FX_LENGTH_MODIFIER(long);
} else {
APPLY_FX_LENGTH_MODIFIER();
}
LIBC_ASSERT(static_cast<size_t>(exponent) <=
(sizeof(StorageType) - sizeof(uint32_t)) * CHAR_BIT &&
"StorageType must be large enough to hold the fractional "
"component multiplied by a 32 bit number.");
const size_t precision = to_conv.precision < 0 ? 6 : to_conv.precision;
bool has_decimal_point =
(precision > 0) || ((flags & FormatFlags::ALTERNATE_FORM) != 0);
constexpr size_t MAX_FRACTION_DIGITS = LARep::FRACTION_LEN;
char fraction_digits[MAX_FRACTION_DIGITS];
size_t valid_fraction_digits = 0;
while (fractional > 0) {
uint32_t cur_digits = 0;
constexpr uint32_t TEN_EXP_NINE = 1000000000;
constexpr size_t DIGITS_PER_BLOCK = 9;
fractional = fractional * TEN_EXP_NINE;
cur_digits = static_cast<uint32_t>(fractional >> exponent);
fractional = fractional % (StorageType(1) << exponent);
const IntegerToString<uint32_t> cur_fractional_digits(cur_digits +
TEN_EXP_NINE);
for (size_t i = 0;
i < DIGITS_PER_BLOCK && valid_fraction_digits < MAX_FRACTION_DIGITS;
++i, ++valid_fraction_digits)
fraction_digits[valid_fraction_digits] =
cur_fractional_digits.view()[i + 1];
if (valid_fraction_digits >= MAX_FRACTION_DIGITS) {
LIBC_ASSERT(fractional == 0 && "If the fraction digit buffer is full, "
"there should be no remaining digits.");
A visual explanation of what this assert is checking:
32 digits (max for 32 bit fract)
+------------------------------++--+--- must be zero
| || |
123456789012345678901234567890120000
| || || || |
+-------++-------++-------++-------+
9 digit blocks
*/
LIBC_ASSERT(cur_digits % const_ten_exp(
DIGITS_PER_BLOCK -
(MAX_FRACTION_DIGITS % DIGITS_PER_BLOCK)) ==
0 &&
"Digits after the MAX_FRACTION_DIGITS should all be zero.");
valid_fraction_digits = MAX_FRACTION_DIGITS;
}
}
if (precision < valid_fraction_digits) {
RoundDirection round;
char first_digit_after = fraction_digits[precision];
if (first_digit_after > '5') {
round = RoundDirection::Up;
} else if (first_digit_after < '5') {
round = RoundDirection::Down;
} else {
round = RoundDirection::Even;
for (size_t cur_digit_index = precision + 1;
cur_digit_index + 1 < valid_fraction_digits; ++cur_digit_index) {
if (fraction_digits[cur_digit_index] != '0') {
round = RoundDirection::Up;
break;
}
}
}
if (round == RoundDirection::Up || round == RoundDirection::Even) {
bool keep_rounding = true;
int digit_to_round = static_cast<int>(precision) - 1;
for (; digit_to_round >= 0 && keep_rounding; --digit_to_round) {
keep_rounding = false;
char cur_digit = fraction_digits[digit_to_round];
if (round == RoundDirection::Even && ((cur_digit - '0') % 2) == 0) {
break;
}
fraction_digits[digit_to_round] += 1;
if (cur_digit == '9') {
fraction_digits[digit_to_round] = '0';
keep_rounding = true;
}
}
if (keep_rounding &&
(round == RoundDirection::Up ||
(round == RoundDirection::Even && ((integral % 2) == 1)))) {
++integral;
}
}
valid_fraction_digits = precision;
}
const IntegerToString<StorageType> integral_str(integral);
size_t trailing_zeroes = 0;
int padding;
if (precision > valid_fraction_digits)
trailing_zeroes = precision - (valid_fraction_digits);
constexpr cpp::string_view DECIMAL_POINT(".");
char sign_char = 0;
if (a == 'A') {
flags = FormatFlags(flags &
~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX));
}
if (is_negative)
sign_char = '-';
else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
sign_char = '+';
else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX)
sign_char = ' ';
padding = static_cast<int>(to_conv.min_width - (sign_char > 0 ? 1 : 0) -
integral_str.size() -
static_cast<int>(has_decimal_point) -
valid_fraction_digits - trailing_zeroes);
if (padding < 0)
padding = 0;
if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) {
if (sign_char > 0)
RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
RET_IF_RESULT_NEGATIVE(writer->write(integral_str.view()));
if (has_decimal_point)
RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
if (valid_fraction_digits > 0)
RET_IF_RESULT_NEGATIVE(
writer->write({fraction_digits, valid_fraction_digits}));
if (trailing_zeroes > 0)
RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
if (padding > 0)
RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
} else {
if ((padding > 0) &&
((flags & FormatFlags::LEADING_ZEROES) != FormatFlags::LEADING_ZEROES))
RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
if (sign_char > 0)
RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
if ((padding > 0) &&
((flags & FormatFlags::LEADING_ZEROES) == FormatFlags::LEADING_ZEROES))
RET_IF_RESULT_NEGATIVE(writer->write('0', padding));
RET_IF_RESULT_NEGATIVE(writer->write(integral_str.view()));
if (has_decimal_point)
RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
if (valid_fraction_digits > 0)
RET_IF_RESULT_NEGATIVE(
writer->write({fraction_digits, valid_fraction_digits}));
if (trailing_zeroes > 0)
RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
}
return WRITE_OK;
}
}
}
#endif