File indexing completed on 2025-04-19 08:19:40
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_CHARCONV_DETAIL_TO_CHARS_INTEGER_IMPL_HPP
0008 #define BOOST_CHARCONV_DETAIL_TO_CHARS_INTEGER_IMPL_HPP
0009
0010 #include <boost/charconv/detail/config.hpp>
0011 #include <boost/charconv/detail/memcpy.hpp>
0012 #include <boost/charconv/detail/to_chars_result.hpp>
0013 #include <boost/charconv/detail/integer_search_trees.hpp>
0014 #include <boost/charconv/detail/emulated128.hpp>
0015 #include <boost/charconv/detail/apply_sign.hpp>
0016 #include <limits>
0017 #include <system_error>
0018 #include <type_traits>
0019 #include <array>
0020 #include <limits>
0021 #include <utility>
0022 #include <cstring>
0023 #include <cstdio>
0024 #include <cerrno>
0025 #include <cstdint>
0026 #include <climits>
0027 #include <cmath>
0028
0029 namespace boost { namespace charconv { namespace detail {
0030
0031
0032 static constexpr char radix_table[] = {
0033 '0', '0', '0', '1', '0', '2', '0', '3', '0', '4',
0034 '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
0035 '1', '0', '1', '1', '1', '2', '1', '3', '1', '4',
0036 '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
0037 '2', '0', '2', '1', '2', '2', '2', '3', '2', '4',
0038 '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
0039 '3', '0', '3', '1', '3', '2', '3', '3', '3', '4',
0040 '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
0041 '4', '0', '4', '1', '4', '2', '4', '3', '4', '4',
0042 '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
0043 '5', '0', '5', '1', '5', '2', '5', '3', '5', '4',
0044 '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
0045 '6', '0', '6', '1', '6', '2', '6', '3', '6', '4',
0046 '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
0047 '7', '0', '7', '1', '7', '2', '7', '3', '7', '4',
0048 '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
0049 '8', '0', '8', '1', '8', '2', '8', '3', '8', '4',
0050 '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
0051 '9', '0', '9', '1', '9', '2', '9', '3', '9', '4',
0052 '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'
0053 };
0054
0055 static constexpr char digit_table[] = {
0056 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
0057 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
0058 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
0059 'u', 'v', 'w', 'x', 'y', 'z'
0060 };
0061
0062
0063
0064 BOOST_CHARCONV_CONSTEXPR char* decompose32(std::uint32_t value, char* buffer) noexcept
0065 {
0066 constexpr auto mask = (std::uint64_t(1) << 57) - 1;
0067 auto y = value * std::uint64_t(1441151881);
0068
0069 for (std::size_t i {}; i < 10; i += 2)
0070 {
0071 boost::charconv::detail::memcpy(buffer + i, radix_table + static_cast<std::size_t>(y >> 57) * 2, 2);
0072 y &= mask;
0073 y *= 100;
0074 }
0075
0076 return buffer + 10;
0077 }
0078
0079 #ifdef BOOST_MSVC
0080 # pragma warning(push)
0081 # pragma warning(disable: 4127 4146)
0082 #endif
0083
0084 template <typename Integer>
0085 BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_integer_impl(char* first, char* last, Integer value) noexcept
0086 {
0087 using Unsigned_Integer = typename std::make_unsigned<Integer>::type;
0088 Unsigned_Integer unsigned_value {};
0089
0090 char buffer[10] {};
0091 int converted_value_digits {};
0092 bool is_negative = false;
0093
0094 if (first > last)
0095 {
0096 return {last, std::errc::invalid_argument};
0097 }
0098
0099
0100 BOOST_IF_CONSTEXPR (std::is_signed<Integer>::value)
0101 {
0102 if (value < 0)
0103 {
0104 is_negative = true;
0105 unsigned_value = apply_sign(value);
0106 }
0107 else
0108 {
0109 unsigned_value = static_cast<Unsigned_Integer>(value);
0110 }
0111 }
0112 else
0113 {
0114 unsigned_value = static_cast<Unsigned_Integer>(value);
0115 }
0116
0117 const std::ptrdiff_t user_buffer_size = last - first - static_cast<std::ptrdiff_t>(is_negative);
0118
0119
0120
0121
0122
0123
0124
0125
0126 if (std::numeric_limits<Integer>::digits <= std::numeric_limits<std::uint32_t>::digits ||
0127 unsigned_value <= static_cast<Unsigned_Integer>((std::numeric_limits<std::uint32_t>::max)()))
0128 {
0129 const auto converted_value = static_cast<std::uint32_t>(unsigned_value);
0130 converted_value_digits = num_digits(converted_value);
0131
0132 if (converted_value_digits > user_buffer_size)
0133 {
0134 return {last, std::errc::value_too_large};
0135 }
0136
0137 decompose32(converted_value, buffer);
0138
0139 if (is_negative)
0140 {
0141 *first++ = '-';
0142 }
0143
0144 boost::charconv::detail::memcpy(first, buffer + (sizeof(buffer) - static_cast<unsigned>(converted_value_digits)),
0145 static_cast<std::size_t>(converted_value_digits));
0146 }
0147 else if (std::numeric_limits<Integer>::digits <= std::numeric_limits<std::uint64_t>::digits ||
0148 static_cast<std::uint64_t>(unsigned_value) <= (std::numeric_limits<std::uint64_t>::max)())
0149 {
0150 auto converted_value = static_cast<std::uint64_t>(unsigned_value);
0151 converted_value_digits = num_digits(converted_value);
0152
0153 if (converted_value_digits > user_buffer_size)
0154 {
0155 return {last, std::errc::value_too_large};
0156 }
0157
0158 if (is_negative)
0159 {
0160 *first++ = '-';
0161 }
0162
0163
0164 if (num_digits(converted_value) <= 18)
0165 {
0166 const auto x = static_cast<std::uint32_t>(converted_value / UINT64_C(1000000000));
0167 const auto y = static_cast<std::uint32_t>(converted_value % UINT64_C(1000000000));
0168 const int first_value_chars = num_digits(x);
0169
0170 decompose32(x, buffer);
0171 boost::charconv::detail::memcpy(first, buffer + (sizeof(buffer) - static_cast<unsigned>(first_value_chars)),
0172 static_cast<std::size_t>(first_value_chars));
0173
0174 decompose32(y, buffer);
0175 boost::charconv::detail::memcpy(first + first_value_chars, buffer + 1, sizeof(buffer) - 1);
0176 }
0177 else
0178 {
0179 const auto x = static_cast<std::uint32_t>(converted_value / UINT64_C(100000000000));
0180 converted_value -= x * UINT64_C(100000000000);
0181 const auto y = static_cast<std::uint32_t>(converted_value / UINT64_C(100));
0182 const auto z = static_cast<std::uint32_t>(converted_value % UINT64_C(100));
0183
0184 if (converted_value_digits == 19)
0185 {
0186 decompose32(x, buffer);
0187 boost::charconv::detail::memcpy(first, buffer + 2, sizeof(buffer) - 2);
0188
0189 decompose32(y, buffer);
0190 boost::charconv::detail::memcpy(first + 8, buffer + 1, sizeof(buffer) - 1);
0191
0192
0193 boost::charconv::detail::memcpy(first + 17, radix_table + z * 2, 2);
0194 }
0195 else
0196 {
0197 decompose32(x, buffer);
0198 boost::charconv::detail::memcpy(first, buffer + 1, sizeof(buffer) - 1);
0199
0200 decompose32(y, buffer);
0201 boost::charconv::detail::memcpy(first + 9, buffer + 1, sizeof(buffer) - 1);
0202
0203
0204 boost::charconv::detail::memcpy(first + 18, radix_table + z * 2, 2);
0205 }
0206 }
0207 }
0208
0209 return {first + converted_value_digits, std::errc()};
0210 }
0211
0212
0213
0214
0215
0216
0217 template <typename Integer>
0218 BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_128integer_impl(char* first, char* last, Integer value) noexcept
0219 {
0220 #ifdef BOOST_CHARCONV_HAS_INT128
0221 using Unsigned_Integer = boost::uint128_type;
0222 #else
0223 using Unsigned_Integer = uint128;
0224 #endif
0225
0226 Unsigned_Integer unsigned_value {};
0227
0228 const std::ptrdiff_t user_buffer_size = last - first;
0229 BOOST_ATTRIBUTE_UNUSED bool is_negative = false;
0230
0231 if (first > last)
0232 {
0233 return {last, std::errc::invalid_argument};
0234 }
0235
0236
0237 #ifdef BOOST_CHARCONV_HAS_INT128
0238 BOOST_IF_CONSTEXPR (std::is_same<boost::int128_type, Integer>::value)
0239 {
0240 if (value < 0)
0241 {
0242 is_negative = true;
0243 unsigned_value = -(static_cast<Unsigned_Integer>(value));
0244 }
0245 else
0246 {
0247 unsigned_value = static_cast<Unsigned_Integer>(value);
0248 }
0249 }
0250 else
0251 #endif
0252 {
0253 unsigned_value = static_cast<Unsigned_Integer>(value);
0254 }
0255
0256 auto converted_value = static_cast<Unsigned_Integer>(unsigned_value);
0257
0258 const int converted_value_digits = num_digits(converted_value);
0259
0260 if (converted_value_digits > user_buffer_size)
0261 {
0262 return {last, std::errc::value_too_large};
0263 }
0264
0265 if (is_negative)
0266 {
0267 *first++ = '-';
0268 }
0269
0270
0271 if (converted_value < (std::numeric_limits<std::uint64_t>::max)())
0272 {
0273 return to_chars_integer_impl(first, last, static_cast<std::uint64_t>(value));
0274 }
0275
0276 constexpr std::uint32_t ten_9 = UINT32_C(1000000000);
0277 char buffer[5][10] {};
0278 int num_chars[5] {};
0279 int i = 0;
0280
0281 while (converted_value != 0)
0282 {
0283 auto digits = static_cast<std::uint32_t>(converted_value % ten_9);
0284 num_chars[i] = num_digits(digits);
0285 decompose32(digits, buffer[i]);
0286 converted_value = (converted_value - digits) / ten_9;
0287 ++i;
0288 }
0289
0290 --i;
0291 auto offset = static_cast<std::size_t>(num_chars[i]);
0292 boost::charconv::detail::memcpy(first, buffer[i] + 10 - offset, offset);
0293
0294 while (i > 0)
0295 {
0296 --i;
0297 boost::charconv::detail::memcpy(first + offset, buffer[i] + 1, 9);
0298 offset += 9;
0299 }
0300
0301 return {first + converted_value_digits, std::errc()};
0302 }
0303
0304
0305 #if defined(__GNUC__) && __GNUC__ >= 5
0306 # pragma GCC diagnostic push
0307 # pragma GCC diagnostic ignored "-Wconversion"
0308 #elif defined(__clang__)
0309 # pragma clang diagnostic push
0310 # pragma clang diagnostic ignored "-Wconversion"
0311 #endif
0312
0313
0314
0315 template <typename Integer, typename Unsigned_Integer>
0316 BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_integer_impl(char* first, char* last, Integer value, int base) noexcept
0317 {
0318 if (!((first <= last) && (base >= 2 && base <= 36)))
0319 {
0320 return {last, std::errc::invalid_argument};
0321 }
0322
0323 if (value == 0)
0324 {
0325 *first++ = '0';
0326 return {first, std::errc()};
0327 }
0328
0329 Unsigned_Integer unsigned_value {};
0330 const auto unsigned_base = static_cast<Unsigned_Integer>(base);
0331
0332 BOOST_IF_CONSTEXPR (std::is_signed<Integer>::value)
0333 {
0334 if (value < 0)
0335 {
0336 *first++ = '-';
0337 unsigned_value = static_cast<Unsigned_Integer>(detail::apply_sign(value));
0338 }
0339 else
0340 {
0341 unsigned_value = static_cast<Unsigned_Integer>(value);
0342 }
0343 }
0344 else
0345 {
0346 unsigned_value = static_cast<Unsigned_Integer>(value);
0347 }
0348
0349 const std::ptrdiff_t output_length = last - first;
0350
0351 constexpr Unsigned_Integer zero = 48U;
0352 constexpr auto buffer_size = sizeof(Unsigned_Integer) * CHAR_BIT;
0353 char buffer[buffer_size] {};
0354 const char* buffer_end = buffer + buffer_size;
0355 char* end = buffer + buffer_size - 1;
0356
0357
0358 switch (base)
0359 {
0360 case 2:
0361 while (unsigned_value != 0)
0362 {
0363 *end-- = static_cast<char>(zero + (unsigned_value & 1U));
0364 unsigned_value >>= static_cast<Unsigned_Integer>(1);
0365 }
0366 break;
0367
0368 case 4:
0369 while (unsigned_value != 0)
0370 {
0371 *end-- = static_cast<char>(zero + (unsigned_value & 3U));
0372 unsigned_value >>= static_cast<Unsigned_Integer>(2);
0373 }
0374 break;
0375
0376 case 8:
0377 while (unsigned_value != 0)
0378 {
0379 *end-- = static_cast<char>(zero + (unsigned_value & 7U));
0380 unsigned_value >>= static_cast<Unsigned_Integer>(3);
0381 }
0382 break;
0383
0384 case 16:
0385 while (unsigned_value != 0)
0386 {
0387 *end-- = digit_table[unsigned_value & 15U];
0388 unsigned_value >>= static_cast<Unsigned_Integer>(4);
0389 }
0390 break;
0391
0392 case 32:
0393 while (unsigned_value != 0)
0394 {
0395 *end-- = digit_table[unsigned_value & 31U];
0396 unsigned_value >>= static_cast<Unsigned_Integer>(5);
0397 }
0398 break;
0399
0400 default:
0401 while (unsigned_value != 0)
0402 {
0403 *end-- = digit_table[unsigned_value % unsigned_base];
0404 unsigned_value /= unsigned_base;
0405 }
0406 break;
0407 }
0408
0409 const std::ptrdiff_t num_chars = buffer_end - end - 1;
0410
0411 if (num_chars > output_length)
0412 {
0413 return {last, std::errc::value_too_large};
0414 }
0415
0416 boost::charconv::detail::memcpy(first, buffer + (buffer_size - static_cast<unsigned long>(num_chars)),
0417 static_cast<std::size_t>(num_chars));
0418
0419 return {first + num_chars, std::errc()};
0420 }
0421
0422 #if defined(__GNUC__) && __GNUC__ >= 5
0423 # pragma GCC diagnostic pop
0424 #elif defined(__clang__)
0425 # pragma clang diagnostic pop
0426 #endif
0427
0428 #ifdef BOOST_MSVC
0429 # pragma warning(pop)
0430 #endif
0431
0432 template <typename Integer>
0433 BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_int(char* first, char* last, Integer value, int base = 10) noexcept
0434 {
0435 using Unsigned_Integer = typename std::make_unsigned<Integer>::type;
0436 if (base == 10)
0437 {
0438 return to_chars_integer_impl(first, last, value);
0439 }
0440
0441 return to_chars_integer_impl<Integer, Unsigned_Integer>(first, last, value, base);
0442 }
0443
0444 #ifdef BOOST_CHARCONV_HAS_INT128
0445 template <typename Integer>
0446 BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars128(char* first, char* last, Integer value, int base = 10) noexcept
0447 {
0448 if (base == 10)
0449 {
0450 return to_chars_128integer_impl(first, last, value);
0451 }
0452
0453 return to_chars_integer_impl<Integer, boost::uint128_type>(first, last, value, base);
0454 }
0455 #endif
0456
0457 }}}
0458
0459 #endif