| 1113 | |
| 1114 | template <typename T> |
| 1115 | int snprintf_float(T value, int precision, float_specs specs, |
| 1116 | buffer<char>& buf) { |
| 1117 | // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. |
| 1118 | FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); |
| 1119 | static_assert(!std::is_same<T, float>::value, ""); |
| 1120 | |
| 1121 | // Subtract 1 to account for the difference in precision since we use %e for |
| 1122 | // both general and exponent format. |
| 1123 | if (specs.format == float_format::general || |
| 1124 | specs.format == float_format::exp) |
| 1125 | precision = (precision >= 0 ? precision : 6) - 1; |
| 1126 | |
| 1127 | // Build the format string. |
| 1128 | enum { max_format_size = 7 }; // Ths longest format is "%#.*Le". |
| 1129 | char format[max_format_size]; |
| 1130 | char* format_ptr = format; |
| 1131 | *format_ptr++ = '%'; |
| 1132 | if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; |
| 1133 | if (precision >= 0) { |
| 1134 | *format_ptr++ = '.'; |
| 1135 | *format_ptr++ = '*'; |
| 1136 | } |
| 1137 | if (std::is_same<T, long double>()) *format_ptr++ = 'L'; |
| 1138 | *format_ptr++ = specs.format != float_format::hex |
| 1139 | ? (specs.format == float_format::fixed ? 'f' : 'e') |
| 1140 | : (specs.upper ? 'A' : 'a'); |
| 1141 | *format_ptr = '\0'; |
| 1142 | |
| 1143 | // Format using snprintf. |
| 1144 | auto offset = buf.size(); |
| 1145 | for (;;) { |
| 1146 | auto begin = buf.data() + offset; |
| 1147 | auto capacity = buf.capacity() - offset; |
| 1148 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
| 1149 | if (precision > 100000) |
| 1150 | throw std::runtime_error( |
| 1151 | "fuzz mode - avoid large allocation inside snprintf"); |
| 1152 | #endif |
| 1153 | // Suppress the warning about a nonliteral format string. |
| 1154 | // Cannot use auto becase of a bug in MinGW (#1532). |
| 1155 | int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; |
| 1156 | int result = precision >= 0 |
| 1157 | ? snprintf_ptr(begin, capacity, format, precision, value) |
| 1158 | : snprintf_ptr(begin, capacity, format, value); |
| 1159 | if (result < 0) { |
| 1160 | buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially. |
| 1161 | continue; |
| 1162 | } |
| 1163 | auto size = to_unsigned(result); |
| 1164 | // Size equal to capacity means that the last character was truncated. |
| 1165 | if (size >= capacity) { |
| 1166 | buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'. |
| 1167 | continue; |
| 1168 | } |
| 1169 | auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; |
| 1170 | if (specs.format == float_format::fixed) { |
| 1171 | if (precision == 0) { |
| 1172 | buf.resize(size); |