mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 00:26:42 +00:00

This reverts commit d1156fcb56891fb1a426c3e8331a51d47f98a1b8. This patch fixes the reported incorrect formatting changes and adds tests for them. The performance should be unaffected, since there are no significant changes required to fix the bugs. Specifically, a `>` was changed to a `>=` to also add a `+` in the zero case, and we're checking for zero now before printing the octal and hexadecimal prefixes. Closes #131710
347 lines
11 KiB
C++
347 lines
11 KiB
C++
// -*- C++ -*-
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef _LIBCPP___CHARCONV_TO_CHARS_INTEGRAL_H
|
|
#define _LIBCPP___CHARCONV_TO_CHARS_INTEGRAL_H
|
|
|
|
#include <__algorithm/copy_n.h>
|
|
#include <__assert>
|
|
#include <__bit/countl.h>
|
|
#include <__charconv/tables.h>
|
|
#include <__charconv/to_chars_base_10.h>
|
|
#include <__charconv/to_chars_result.h>
|
|
#include <__charconv/traits.h>
|
|
#include <__config>
|
|
#include <__cstddef/ptrdiff_t.h>
|
|
#include <__system_error/errc.h>
|
|
#include <__type_traits/enable_if.h>
|
|
#include <__type_traits/integral_constant.h>
|
|
#include <__type_traits/is_integral.h>
|
|
#include <__type_traits/is_same.h>
|
|
#include <__type_traits/make_32_64_or_128_bit.h>
|
|
#include <__type_traits/make_unsigned.h>
|
|
#include <__utility/unreachable.h>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
|
|
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
|
# pragma GCC system_header
|
|
#endif
|
|
|
|
_LIBCPP_PUSH_MACROS
|
|
#include <__undef_macros>
|
|
|
|
_LIBCPP_BEGIN_NAMESPACE_STD
|
|
|
|
template <typename _Tp>
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_itoa(char* __first, char* __last, _Tp __value, false_type);
|
|
|
|
template <typename _Tp>
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_itoa(char* __first, char* __last, _Tp __value, true_type) {
|
|
auto __x = std::__to_unsigned_like(__value);
|
|
if (__value < 0 && __first != __last) {
|
|
*__first++ = '-';
|
|
__x = std::__complement(__x);
|
|
}
|
|
|
|
return std::__to_chars_itoa(__first, __last, __x, false_type());
|
|
}
|
|
|
|
template <typename _Tp>
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_itoa(char* __first, char* __last, _Tp __value, false_type) {
|
|
using __tx = __itoa::__traits<_Tp>;
|
|
auto __diff = __last - __first;
|
|
|
|
if (__tx::digits <= __diff || __tx::__width(__value) <= __diff)
|
|
return {__tx::__convert(__first, __value), errc(0)};
|
|
else
|
|
return {__last, errc::value_too_large};
|
|
}
|
|
|
|
# if _LIBCPP_HAS_INT128
|
|
template <>
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_itoa(char* __first, char* __last, __uint128_t __value, false_type) {
|
|
// When the value fits in 64-bits use the 64-bit code path. This reduces
|
|
// the number of expensive calculations on 128-bit values.
|
|
//
|
|
// NOTE the 128-bit code path requires this optimization.
|
|
if (__value <= numeric_limits<uint64_t>::max())
|
|
return __to_chars_itoa(__first, __last, static_cast<uint64_t>(__value), false_type());
|
|
|
|
using __tx = __itoa::__traits<__uint128_t>;
|
|
auto __diff = __last - __first;
|
|
|
|
if (__tx::digits <= __diff || __tx::__width(__value) <= __diff)
|
|
return {__tx::__convert(__first, __value), errc(0)};
|
|
else
|
|
return {__last, errc::value_too_large};
|
|
}
|
|
# endif
|
|
|
|
template <class _Tp, __enable_if_t<!is_signed<_Tp>::value, int> = 0>
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_integral(char* __first, char* __last, _Tp __value, int __base);
|
|
|
|
template <class _Tp, __enable_if_t<is_signed<_Tp>::value, int> = 0>
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_integral(char* __first, char* __last, _Tp __value, int __base) {
|
|
auto __x = std::__to_unsigned_like(__value);
|
|
if (__value < 0 && __first != __last) {
|
|
*__first++ = '-';
|
|
__x = std::__complement(__x);
|
|
}
|
|
|
|
return std::__to_chars_integral(__first, __last, __x, __base);
|
|
}
|
|
|
|
namespace __itoa {
|
|
|
|
template <unsigned _Base>
|
|
struct _LIBCPP_HIDDEN __integral;
|
|
|
|
template <>
|
|
struct _LIBCPP_HIDDEN __integral<2> {
|
|
template <typename _Tp>
|
|
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int __width(_Tp __value) _NOEXCEPT {
|
|
// If value == 0 still need one digit. If the value != this has no
|
|
// effect since the code scans for the most significant bit set. (Note
|
|
// that __libcpp_clz doesn't work for 0.)
|
|
return numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1);
|
|
}
|
|
|
|
template <typename _Tp>
|
|
_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
|
|
__to_chars(char* __first, char* __last, _Tp __value) {
|
|
ptrdiff_t __cap = __last - __first;
|
|
int __n = __width(__value);
|
|
if (__n > __cap)
|
|
return {__last, errc::value_too_large};
|
|
|
|
__last = __first + __n;
|
|
char* __p = __last;
|
|
const unsigned __divisor = 16;
|
|
while (__value > __divisor) {
|
|
unsigned __c = __value % __divisor;
|
|
__value /= __divisor;
|
|
__p -= 4;
|
|
std::copy_n(&__base_2_lut[4 * __c], 4, __p);
|
|
}
|
|
do {
|
|
unsigned __c = __value % 2;
|
|
__value /= 2;
|
|
*--__p = "01"[__c];
|
|
} while (__value != 0);
|
|
return {__last, errc(0)};
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct _LIBCPP_HIDDEN __integral<8> {
|
|
template <typename _Tp>
|
|
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int __width(_Tp __value) _NOEXCEPT {
|
|
// If value == 0 still need one digit. If the value != this has no
|
|
// effect since the code scans for the most significat bit set. (Note
|
|
// that __libcpp_clz doesn't work for 0.)
|
|
return ((numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1)) + 2) / 3;
|
|
}
|
|
|
|
template <typename _Tp>
|
|
_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
|
|
__to_chars(char* __first, char* __last, _Tp __value) {
|
|
ptrdiff_t __cap = __last - __first;
|
|
int __n = __width(__value);
|
|
if (__n > __cap)
|
|
return {__last, errc::value_too_large};
|
|
|
|
__last = __first + __n;
|
|
char* __p = __last;
|
|
unsigned __divisor = 64;
|
|
while (__value > __divisor) {
|
|
unsigned __c = __value % __divisor;
|
|
__value /= __divisor;
|
|
__p -= 2;
|
|
std::copy_n(&__base_8_lut[2 * __c], 2, __p);
|
|
}
|
|
do {
|
|
unsigned __c = __value % 8;
|
|
__value /= 8;
|
|
*--__p = "01234567"[__c];
|
|
} while (__value != 0);
|
|
return {__last, errc(0)};
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct _LIBCPP_HIDDEN __integral<16> {
|
|
template <typename _Tp>
|
|
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int __width(_Tp __value) _NOEXCEPT {
|
|
// If value == 0 still need one digit. If the value != this has no
|
|
// effect since the code scans for the most significat bit set. (Note
|
|
// that __libcpp_clz doesn't work for 0.)
|
|
return (numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1) + 3) / 4;
|
|
}
|
|
|
|
template <typename _Tp>
|
|
_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
|
|
__to_chars(char* __first, char* __last, _Tp __value) {
|
|
ptrdiff_t __cap = __last - __first;
|
|
int __n = __width(__value);
|
|
if (__n > __cap)
|
|
return {__last, errc::value_too_large};
|
|
|
|
__last = __first + __n;
|
|
char* __p = __last;
|
|
unsigned __divisor = 256;
|
|
while (__value > __divisor) {
|
|
unsigned __c = __value % __divisor;
|
|
__value /= __divisor;
|
|
__p -= 2;
|
|
std::copy_n(&__base_16_lut[2 * __c], 2, __p);
|
|
}
|
|
if (__first != __last)
|
|
do {
|
|
unsigned __c = __value % 16;
|
|
__value /= 16;
|
|
*--__p = "0123456789abcdef"[__c];
|
|
} while (__value != 0);
|
|
return {__last, errc(0)};
|
|
}
|
|
};
|
|
|
|
} // namespace __itoa
|
|
|
|
template <unsigned _Base, typename _Tp, __enable_if_t<(sizeof(_Tp) >= sizeof(unsigned)), int> = 0>
|
|
_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __to_chars_integral_width(_Tp __value) {
|
|
return __itoa::__integral<_Base>::__width(__value);
|
|
}
|
|
|
|
template <unsigned _Base, typename _Tp, __enable_if_t<(sizeof(_Tp) < sizeof(unsigned)), int> = 0>
|
|
_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __to_chars_integral_width(_Tp __value) {
|
|
return std::__to_chars_integral_width<_Base>(static_cast<unsigned>(__value));
|
|
}
|
|
|
|
template <unsigned _Base, typename _Tp, __enable_if_t<(sizeof(_Tp) >= sizeof(unsigned)), int> = 0>
|
|
_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_integral(char* __first, char* __last, _Tp __value) {
|
|
return __itoa::__integral<_Base>::__to_chars(__first, __last, __value);
|
|
}
|
|
|
|
template <unsigned _Base, typename _Tp, __enable_if_t<(sizeof(_Tp) < sizeof(unsigned)), int> = 0>
|
|
_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_integral(char* __first, char* __last, _Tp __value) {
|
|
return std::__to_chars_integral<_Base>(__first, __last, static_cast<unsigned>(__value));
|
|
}
|
|
|
|
template <typename _Tp>
|
|
_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __to_chars_integral_width(_Tp __value, unsigned __base) {
|
|
_LIBCPP_ASSERT_INTERNAL(__value >= 0, "The function requires a non-negative value.");
|
|
|
|
unsigned __base_2 = __base * __base;
|
|
unsigned __base_3 = __base_2 * __base;
|
|
unsigned __base_4 = __base_2 * __base_2;
|
|
|
|
int __r = 0;
|
|
while (true) {
|
|
if (__value < __base)
|
|
return __r + 1;
|
|
if (__value < __base_2)
|
|
return __r + 2;
|
|
if (__value < __base_3)
|
|
return __r + 3;
|
|
if (__value < __base_4)
|
|
return __r + 4;
|
|
|
|
__value /= __base_4;
|
|
__r += 4;
|
|
}
|
|
|
|
__libcpp_unreachable();
|
|
}
|
|
|
|
template <class _Tp, __enable_if_t<!is_signed<_Tp>::value, int> >
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_result
|
|
__to_chars_integral(char* __first, char* __last, _Tp __value, int __base) {
|
|
if (__base == 10) [[likely]]
|
|
return std::__to_chars_itoa(__first, __last, __value, false_type());
|
|
|
|
switch (__base) {
|
|
case 2:
|
|
return std::__to_chars_integral<2>(__first, __last, __value);
|
|
case 8:
|
|
return std::__to_chars_integral<8>(__first, __last, __value);
|
|
case 16:
|
|
return std::__to_chars_integral<16>(__first, __last, __value);
|
|
}
|
|
|
|
ptrdiff_t __cap = __last - __first;
|
|
int __n = std::__to_chars_integral_width(__value, __base);
|
|
if (__n > __cap)
|
|
return {__last, errc::value_too_large};
|
|
|
|
__last = __first + __n;
|
|
char* __p = __last;
|
|
do {
|
|
unsigned __c = __value % __base;
|
|
__value /= __base;
|
|
*--__p = "0123456789abcdefghijklmnopqrstuvwxyz"[__c];
|
|
} while (__value != 0);
|
|
return {__last, errc(0)};
|
|
}
|
|
|
|
_LIBCPP_HIDE_FROM_ABI inline _LIBCPP_CONSTEXPR_SINCE_CXX14 char __hex_to_upper(char __c) {
|
|
switch (__c) {
|
|
case 'a':
|
|
return 'A';
|
|
case 'b':
|
|
return 'B';
|
|
case 'c':
|
|
return 'C';
|
|
case 'd':
|
|
return 'D';
|
|
case 'e':
|
|
return 'E';
|
|
case 'f':
|
|
return 'F';
|
|
}
|
|
return __c;
|
|
}
|
|
|
|
#if _LIBCPP_STD_VER >= 17
|
|
|
|
to_chars_result to_chars(char*, char*, bool, int = 10) = delete;
|
|
|
|
template <typename _Tp, __enable_if_t<is_integral<_Tp>::value, int> = 0>
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result
|
|
to_chars(char* __first, char* __last, _Tp __value) {
|
|
using _Type = __make_32_64_or_128_bit_t<_Tp>;
|
|
static_assert(!is_same<_Type, void>::value, "unsupported integral type used in to_chars");
|
|
return std::__to_chars_itoa(__first, __last, static_cast<_Type>(__value), is_signed<_Tp>());
|
|
}
|
|
|
|
template <typename _Tp, __enable_if_t<is_integral<_Tp>::value, int> = 0>
|
|
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result
|
|
to_chars(char* __first, char* __last, _Tp __value, int __base) {
|
|
_LIBCPP_ASSERT_UNCATEGORIZED(2 <= __base && __base <= 36, "base not in [2, 36]");
|
|
|
|
using _Type = __make_32_64_or_128_bit_t<_Tp>;
|
|
return std::__to_chars_integral(__first, __last, static_cast<_Type>(__value), __base);
|
|
}
|
|
|
|
#endif // _LIBCPP_STD_VER >= 17
|
|
|
|
_LIBCPP_END_NAMESPACE_STD
|
|
|
|
_LIBCPP_POP_MACROS
|
|
|
|
#endif // _LIBCPP___CHARCONV_TO_CHARS_INTEGRAL_H
|