blob: 87d77100d9c98f0908166f26aaf46280d32d056d [file]
//===-- Implementation header for strfromx() utilitites -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// According to the C23 standard, any input character sequences except a
// precision specifier and the usual floating point formats, namely
// %{a,A,e,E,f,F,g,G}, are not allowed and any code that does otherwise results
// in undefined behaviour(including use of a '%%' conversion specifier); which
// in this case is that the buffer string is simply populated with the format
// string. The case of the input being nullptr should be handled in the calling
// function (strfromf, strfromd, strfroml) itself.
#ifndef LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H
#define LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H
#include "hdr/types/size_t.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/__support/printf_core/converter_atlas.h"
#include "src/__support/printf_core/core_structs.h"
#include "src/__support/printf_core/error_mapper.h"
#include "src/__support/printf_core/writer.h"
#include "src/__support/str_to_integer.h"
namespace LIBC_NAMESPACE_DECL {
namespace internal {
template <typename T, printf_core::OverflowMode overflow_mode>
LIBC_INLINE int strfromfloat_convert(printf_core::Writer<overflow_mode> *writer,
const char *__restrict format, T fp) {
printf_core::FormatSection section = {};
size_t cur_pos = 0;
if (format[cur_pos] == '%') {
++cur_pos;
// handle precision
if (format[cur_pos] == '.') {
++cur_pos;
section.precision = 0;
// The standard does not allow the '*' (asterisk) operator for strfromx()
// functions
if (internal::isdigit(format[cur_pos])) {
auto result = internal::strtointeger<int>(format + cur_pos, 10);
section.precision += result.value;
cur_pos += result.parsed_len;
}
}
char n = format[cur_pos];
section.conv_name = n;
section.has_conv = n == 'f' || n == 'F' || n == 'e' || n == 'E' ||
n == 'a' || n == 'A' || n == 'g' || n == 'G';
}
if (!section.has_conv)
return writer->write(format);
fputil::FPBits<T> strfromfloat_bits(fp);
if (strfromfloat_bits.is_inf_or_nan())
return convert_inf_nan(
writer,
printf_core::InfNanFPBitsProperties{
.is_negative = strfromfloat_bits.is_neg(),
.mantissa_is_zero = strfromfloat_bits.get_mantissa() == 0,
},
section);
switch (section.conv_name) {
case 'f':
case 'F':
return printf_core::convert_finite_float_decimal_typed(writer, section,
strfromfloat_bits);
case 'e':
case 'E':
return printf_core::convert_finite_float_dec_exp_typed(writer, section,
strfromfloat_bits);
case 'a':
case 'A':
// There is no typed conversion function to convert single precision float
// to hex exponential format, and the convert_finite_float_hex_exp()
// requires a double or long double value to work correctly.
if constexpr (cpp::is_same_v<T, float>) {
return printf_core::convert_finite_float_hex_exp(
writer,
printf_core::get_float_hex_exp_fp_bits_properties_typed(
fputil::FPBits<double>(static_cast<double>(fp))),
section);
} else {
return printf_core::convert_finite_float_hex_exp(
writer,
printf_core::get_float_hex_exp_fp_bits_properties_typed(
strfromfloat_bits),
section);
}
case 'g':
case 'G':
return printf_core::convert_finite_float_dec_auto_typed(writer, section,
strfromfloat_bits);
}
__builtin_unreachable();
}
template <typename T>
LIBC_INLINE int strfromfloat_impl(char *__restrict s, size_t n,
const char *__restrict format, T fp) {
LIBC_ASSERT(s != nullptr);
printf_core::Writer writer =
printf_core::make_drop_overflow_writer(s, (n > 0 ? n - 1 : 0));
int result = strfromfloat_convert(&writer, format, fp);
if (result < 0)
return result;
if (n > 0) {
printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
wb.buff[wb.buff_cur] = '\0';
}
if (writer.get_chars_written() >
static_cast<size_t>(cpp::numeric_limits<int>::max())) {
libc_errno =
printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
return -1;
}
return static_cast<int>(writer.get_chars_written());
}
} // namespace internal
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H