diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -130,6 +130,8 @@ __bsd_locale_fallbacks.h __charconv/chars_format.h __charconv/from_chars_result.h + __charconv/tables.h + __charconv/to_chars_base_10.h __charconv/to_chars_result.h __chrono/calendar.h __chrono/convert_to_timespec.h diff --git a/libcxx/include/__charconv/tables.h b/libcxx/include/__charconv/tables.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__charconv/tables.h @@ -0,0 +1,45 @@ +// -*- 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_TABLES +#define _LIBCPP___CHARCONV_TABLES + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#ifndef _LIBCPP_CXX03_LANG + +namespace __itoa { + +static constexpr char __digits_base_10[200] = { + // clang-format off + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', + '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', + '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', + '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', + '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', + '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', + '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', + '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', + '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'}; +// clang-format on + +} // namespace __itoa + +#endif // _LIBCPP_CXX03_LANG + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___CHARCONV_TABLES diff --git a/libcxx/include/__charconv/to_chars_base_10.h b/libcxx/include/__charconv/to_chars_base_10.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__charconv/to_chars_base_10.h @@ -0,0 +1,128 @@ +// -*- 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_BASE_10_H +#define _LIBCPP___CHARCONV_TO_CHARS_BASE_10_H + +#include <__charconv/tables.h> +#include <__config> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#ifndef _LIBCPP_CXX03_LANG + +namespace __itoa { + +template +_LIBCPP_HIDE_FROM_ABI char* __append1(char* __buffer, _Tp __value) noexcept { + *__buffer = '0' + static_cast(__value); + return __buffer + 1; +} + +template +_LIBCPP_HIDE_FROM_ABI char* __append2(char* __buffer, _Tp __value) noexcept { + memcpy(__buffer, &__digits_base_10[(__value)*2], 2); + return __buffer + 2; +} + +template +_LIBCPP_HIDE_FROM_ABI char* __append3(char* __buffer, _Tp __value) noexcept { + return __append2(__append1(__buffer, (__value) / 100), (__value) % 100); +} + +template +_LIBCPP_HIDE_FROM_ABI char* __append4(char* __buffer, _Tp __value) noexcept { + return __append2(__append2(__buffer, (__value) / 100), (__value) % 100); +} + +template +_LIBCPP_HIDE_FROM_ABI char* __append2_no_zeros(char* __buffer, _Tp __value) noexcept { + if (__value < 10) + return __append1(__buffer, __value); + else + return __append2(__buffer, __value); +} + +template +_LIBCPP_HIDE_FROM_ABI char* __append4_no_zeros(char* __buffer, _Tp __value) noexcept { + if (__value < 100) + return __append2_no_zeros(__buffer, __value); + else if (__value < 1000) + return __append3(__buffer, __value); + else + return __append4(__buffer, __value); +} + +template +_LIBCPP_HIDE_FROM_ABI char* __append8_no_zeros(char* __buffer, _Tp __value) noexcept { + if (__value < 10000) + __buffer = __append4_no_zeros(__buffer, __value); + else { + __buffer = __append4_no_zeros(__buffer, __value / 10000); + __buffer = __append4(__buffer, __value % 10000); + } + return __buffer; +} + +_LIBCPP_HIDE_FROM_ABI inline char* __base_10_u32(uint32_t __value, char* __buffer) noexcept { + if (__value < 100000000) + __buffer = __append8_no_zeros(__buffer, __value); + else { + // __value = aabbbbcccc in decimal + const uint32_t __a = __value / 100000000; // 1 to 42 + __value %= 100000000; + + __buffer = __append2_no_zeros(__buffer, __a); + __buffer = __append4(__buffer, __value / 10000); + __buffer = __append4(__buffer, __value % 10000); + } + + return __buffer; +} + +_LIBCPP_HIDE_FROM_ABI inline char* __base_10_u64(uint64_t __value, char* __buffer) noexcept { + if (__value < 100000000) + __buffer = __append8_no_zeros(__buffer, static_cast(__value)); + else if (__value < 10000000000000000) { + const uint32_t __v0 = static_cast(__value / 100000000); + const uint32_t __v1 = static_cast(__value % 100000000); + + __buffer = __append8_no_zeros(__buffer, __v0); + __buffer = __append4(__buffer, __v1 / 10000); + __buffer = __append4(__buffer, __v1 % 10000); + } else { + const uint32_t __a = static_cast(__value / 10000000000000000); // 1 to 1844 + __value %= 10000000000000000; + + __buffer = __append4_no_zeros(__buffer, __a); + + const uint32_t __v0 = static_cast(__value / 100000000); + const uint32_t __v1 = static_cast(__value % 100000000); + __buffer = __append4(__buffer, __v0 / 10000); + __buffer = __append4(__buffer, __v0 % 10000); + __buffer = __append4(__buffer, __v1 / 10000); + __buffer = __append4(__buffer, __v1 % 10000); + } + + return __buffer; +} + +} // namespace __itoa + +#endif // _LIBCPP_CXX03_LANG + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___CHARCONV_TO_CHARS_BASE_10_H diff --git a/libcxx/include/charconv b/libcxx/include/charconv --- a/libcxx/include/charconv +++ b/libcxx/include/charconv @@ -82,6 +82,8 @@ #include <__bits> #include <__charconv/chars_format.h> #include <__charconv/from_chars_result.h> +#include <__charconv/tables.h> +#include <__charconv/to_chars_base_10.h> #include <__charconv/to_chars_result.h> #include <__config> #include <__debug> @@ -105,11 +107,6 @@ #ifndef _LIBCPP_CXX03_LANG -namespace __itoa { -_LIBCPP_AVAILABILITY_TO_CHARS _LIBCPP_FUNC_VIS char* __u64toa(uint64_t __value, char* __buffer) noexcept; -_LIBCPP_AVAILABILITY_TO_CHARS _LIBCPP_FUNC_VIS char* __u32toa(uint32_t __value, char* __buffer) noexcept; -} // namespace __itoa - to_chars_result to_chars(char*, char*, bool, int = 10) = delete; from_chars_result from_chars(const char*, const char*, bool, int = 10) = delete; @@ -160,7 +157,7 @@ _LIBCPP_AVAILABILITY_TO_CHARS static _LIBCPP_HIDE_FROM_ABI char* __convert(_Tp __v, char* __p) { - return __u64toa(__v, __p); + return __base_10_u64(__v, __p); } static _LIBCPP_HIDE_FROM_ABI decltype(__pow10_64)& __pow() { return __pow10_64; } @@ -181,7 +178,7 @@ _LIBCPP_AVAILABILITY_TO_CHARS static _LIBCPP_HIDE_FROM_ABI char* __convert(_Tp __v, char* __p) { - return __u32toa(__v, __p); + return __base_10_u32(__v, __p); } static _LIBCPP_HIDE_FROM_ABI decltype(__pow10_32)& __pow() { return __pow10_32; } diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -396,6 +396,8 @@ module __charconv { module chars_format { private header "__charconv/chars_format.h" } module from_chars_result { private header "__charconv/from_chars_result.h" } + module tables { private header "__charconv/tables.h" } + module to_chars_base_10 { private header "__charconv/to_chars_base_10.h" } module to_chars_result { private header "__charconv/to_chars_result.h" } } diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp --- a/libcxx/src/charconv.cpp +++ b/libcxx/src/charconv.cpp @@ -85,7 +85,7 @@ return buffer; } -char* +_LIBCPP_FUNC_VIS char* __u32toa(uint32_t value, char* buffer) noexcept { if (value < 100000000) @@ -106,7 +106,7 @@ return buffer; } -char* +_LIBCPP_FUNC_VIS char* __u64toa(uint64_t value, char* buffer) noexcept { if (value < 100000000) diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -164,6 +164,8 @@ #include <__bits> // expected-error@*:* {{use of private header from outside its module: '__bits'}} #include <__charconv/chars_format.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/chars_format.h'}} #include <__charconv/from_chars_result.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/from_chars_result.h'}} +#include <__charconv/tables.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/tables.h'}} +#include <__charconv/to_chars_base_10.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/to_chars_base_10.h'}} #include <__charconv/to_chars_result.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/to_chars_result.h'}} #include <__chrono/calendar.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/calendar.h'}} #include <__chrono/convert_to_timespec.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/convert_to_timespec.h'}}