[libc++] Make to_chars base 10 header only.

The functions to_chars and from_chars should offer 128-bit support. This
is the first step to implement 128-bit version of to_chars. Before
implementing 128-bit support the current code will be polished.

This moves the code from the dylib to the header in prepartion of

P2291 "Add Constexpr Modifiers to Functions to_chars and from_chars for
Integral Types in <charconv> Header"

Note some more cleanups will be done in follow-up commits
- Remove the _LIBCPP_AVAILABILITY_TO_CHARS from to_chars. With all code
  in the header the availablilty macro is no longer needed. This
  requires enabling the unit tests for additional platforms.
- The code in the dylib can switch to using the header implementation.
  This allows removing the code duplicated in the header and the dylib.

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D125704
This commit is contained in:
Mark de Wever
2022-05-16 17:12:18 +02:00
parent b418ef5cb9
commit a15ae4139c
7 changed files with 191 additions and 9 deletions

View File

@@ -139,6 +139,8 @@ set(files
__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

View File

@@ -0,0 +1,51 @@
// -*- 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 {
template <class = void>
struct __digits_base_10 {
static const char __value[200];
};
template <class _Tp>
const char __digits_base_10<_Tp>::__value[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

View File

@@ -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 <cstdint>
#include <cstring>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#ifndef _LIBCPP_CXX03_LANG
namespace __itoa {
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI char* __append1(char* __buffer, _Tp __value) noexcept {
*__buffer = '0' + static_cast<char>(__value);
return __buffer + 1;
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI char* __append2(char* __buffer, _Tp __value) noexcept {
std::memcpy(__buffer, &__digits_base_10<>::__value[(__value)*2], 2);
return __buffer + 2;
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI char* __append3(char* __buffer, _Tp __value) noexcept {
return __itoa::__append2(__itoa::__append1(__buffer, (__value) / 100), (__value) % 100);
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI char* __append4(char* __buffer, _Tp __value) noexcept {
return __itoa::__append2(__itoa::__append2(__buffer, (__value) / 100), (__value) % 100);
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI char* __append2_no_zeros(char* __buffer, _Tp __value) noexcept {
if (__value < 10)
return __itoa::__append1(__buffer, __value);
else
return __itoa::__append2(__buffer, __value);
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI char* __append4_no_zeros(char* __buffer, _Tp __value) noexcept {
if (__value < 100)
return __itoa::__append2_no_zeros(__buffer, __value);
else if (__value < 1000)
return __itoa::__append3(__buffer, __value);
else
return __itoa::__append4(__buffer, __value);
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI char* __append8_no_zeros(char* __buffer, _Tp __value) noexcept {
if (__value < 10000)
__buffer = __itoa::__append4_no_zeros(__buffer, __value);
else {
__buffer = __itoa::__append4_no_zeros(__buffer, __value / 10000);
__buffer = __itoa::__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 = __itoa::__append8_no_zeros(__buffer, __value);
else {
// __value = aabbbbcccc in decimal
const uint32_t __a = __value / 100000000; // 1 to 42
__value %= 100000000;
__buffer = __itoa::__append2_no_zeros(__buffer, __a);
__buffer = __itoa::__append4(__buffer, __value / 10000);
__buffer = __itoa::__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 = __itoa::__append8_no_zeros(__buffer, static_cast<uint32_t>(__value));
else if (__value < 10000000000000000) {
const uint32_t __v0 = static_cast<uint32_t>(__value / 100000000);
const uint32_t __v1 = static_cast<uint32_t>(__value % 100000000);
__buffer = __itoa::__append8_no_zeros(__buffer, __v0);
__buffer = __itoa::__append4(__buffer, __v1 / 10000);
__buffer = __itoa::__append4(__buffer, __v1 % 10000);
} else {
const uint32_t __a = static_cast<uint32_t>(__value / 10000000000000000); // 1 to 1844
__value %= 10000000000000000;
__buffer = __itoa::__append4_no_zeros(__buffer, __a);
const uint32_t __v0 = static_cast<uint32_t>(__value / 100000000);
const uint32_t __v1 = static_cast<uint32_t>(__value % 100000000);
__buffer = __itoa::__append4(__buffer, __v0 / 10000);
__buffer = __itoa::__append4(__buffer, __v0 % 10000);
__buffer = __itoa::__append4(__buffer, __v1 / 10000);
__buffer = __itoa::__append4(__buffer, __v1 % 10000);
}
return __buffer;
}
} // namespace __itoa
#endif // _LIBCPP_CXX03_LANG
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___CHARCONV_TO_CHARS_BASE_10_H

View File

@@ -82,6 +82,8 @@ namespace std {
#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 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#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 @@ struct _LIBCPP_HIDDEN __traits_base
_LIBCPP_AVAILABILITY_TO_CHARS
static _LIBCPP_HIDE_FROM_ABI char* __convert(_Tp __v, char* __p)
{
return __u64toa(__v, __p);
return __itoa::__base_10_u64(__v, __p);
}
static _LIBCPP_HIDE_FROM_ABI decltype(__pow10_64)& __pow() { return __pow10_64; }
@@ -181,7 +178,7 @@ struct _LIBCPP_HIDDEN
_LIBCPP_AVAILABILITY_TO_CHARS
static _LIBCPP_HIDE_FROM_ABI char* __convert(_Tp __v, char* __p)
{
return __u32toa(__v, __p);
return __itoa::__base_10_u32(__v, __p);
}
static _LIBCPP_HIDE_FROM_ABI decltype(__pow10_32)& __pow() { return __pow10_32; }

View File

@@ -403,6 +403,8 @@ module std [system] {
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" }
}

View File

@@ -85,7 +85,7 @@ append8_no_zeros(char* buffer, T v) noexcept
return buffer;
}
char*
_LIBCPP_FUNC_VIS char*
__u32toa(uint32_t value, char* buffer) noexcept
{
if (value < 100000000)
@@ -106,7 +106,7 @@ __u32toa(uint32_t value, char* buffer) noexcept
return buffer;
}
char*
_LIBCPP_FUNC_VIS char*
__u64toa(uint64_t value, char* buffer) noexcept
{
if (value < 100000000)

View File

@@ -173,6 +173,8 @@ END-SCRIPT
#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'}}