aboutsummaryrefslogtreecommitdiff
path: root/libc/src/stdlib/str_from_util.h
blob: 61e6ba24b38177a3c8a1afe73f72f46ac2ffb0d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//===-- 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 "src/__support/CPP/type_traits.h"
#include "src/__support/macros/config.h"
#include "src/__support/str_to_integer.h"
#include "src/stdio/printf_core/converter_atlas.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/writer.h"

#include <stddef.h>

namespace LIBC_NAMESPACE_DECL {
namespace internal {

template <typename T>
using storage_type = typename fputil::FPBits<T>::StorageType;

template <typename T>
printf_core::FormatSection parse_format_string(const char *__restrict format,
                                               T fp) {
  printf_core::FormatSection section;
  size_t cur_pos = 0;

  // There is no typed conversion function to convert single precision float
  // to hex exponential format, and the function convert_float_hex_exp()
  // requires a double or long double value to work correctly.
  // To work around this, we convert fp to double if it is single precision, and
  // then use that double precision value in the %{A, a} conversion specifiers.
  [[maybe_unused]] double new_fp;
  bool t_is_single_prec_type = cpp::is_same<T, float>::value;
  if (t_is_single_prec_type)
    new_fp = (double)fp;

  if (format[cur_pos] == '%') {
    section.has_conv = true;
    ++cur_pos;

    // handle precision
    section.precision = -1;
    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;
      }
    }

    section.conv_name = format[cur_pos];
    switch (format[cur_pos]) {
    case 'a':
    case 'A':
      if (t_is_single_prec_type)
        section.conv_val_raw = cpp::bit_cast<storage_type<double>>(new_fp);
      else
        section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp);
      break;
    case 'e':
    case 'E':
    case 'f':
    case 'F':
    case 'g':
    case 'G':
      section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp);
      break;
    default:
      section.has_conv = false;
      while (format[cur_pos] != '\0')
        ++cur_pos;
      break;
    }

    if (format[cur_pos] != '\0')
      ++cur_pos;
  } else {
    section.has_conv = false;
    // We are looking for exactly one section, so no more '%'
    while (format[cur_pos] != '\0')
      ++cur_pos;
  }

  section.raw_string = {format, cur_pos};
  return section;
}

template <typename T, printf_core::WriteMode write_mode>
int strfromfloat_convert(printf_core::Writer<write_mode> *writer,
                         const printf_core::FormatSection &section) {
  if (!section.has_conv)
    return writer->write(section.raw_string);

  auto res = static_cast<storage_type<T>>(section.conv_val_raw);

  fputil::FPBits<T> strfromfloat_bits(res);
  if (strfromfloat_bits.is_inf_or_nan())
    return convert_inf_nan(writer, section);

  switch (section.conv_name) {
  case 'f':
  case 'F':
    return convert_float_decimal_typed(writer, section, strfromfloat_bits);
  case 'e':
  case 'E':
    return convert_float_dec_exp_typed(writer, section, strfromfloat_bits);
  case 'a':
  case 'A':
    return convert_float_hex_exp(writer, section);
  case 'g':
  case 'G':
    return convert_float_dec_auto_typed(writer, section, strfromfloat_bits);
  default:
    return writer->write(section.raw_string);
  }
  return -1;
}

} // namespace internal
} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H