[libc++] Refactor char_traits

This allows us to reuse workarounds for compilers that don't provide the builtins or constexpr support.

Reviewed By: ldionne, Mordante, #libc

Spies: libcxx-commits

Differential Revision: https://reviews.llvm.org/D139555
This commit is contained in:
Nikolas Klauser 2022-12-07 18:31:14 +01:00
parent 1b41074508
commit 72173469dd
5 changed files with 182 additions and 145 deletions

View File

@ -210,25 +210,22 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char>
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
{return (unsigned char)__c1 < (unsigned char)__c2;}
static _LIBCPP_CONSTEXPR_SINCE_CXX17
int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
static _LIBCPP_CONSTEXPR_SINCE_CXX17 int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
if (__n == 0)
return 0;
return std::__constexpr_memcmp(__s1, __s2, __n);
}
static inline size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s) _NOEXCEPT {
// GCC currently does not support __builtin_strlen during constant evaluation.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
#ifdef _LIBCPP_COMPILER_GCC
if (__libcpp_is_constant_evaluated()) {
size_t __i = 0;
for (; __s[__i] != char_type('\0'); ++__i)
;
return __i;
}
#endif
return __builtin_strlen(__s);
}
static inline size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s) _NOEXCEPT {
return std::__constexpr_strlen(__s);
}
static _LIBCPP_CONSTEXPR_SINCE_CXX17
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
static _LIBCPP_CONSTEXPR_SINCE_CXX17
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
if (__n == 0)
return nullptr;
return std::__constexpr_char_memchr(__s, static_cast<int>(__a), __n);
}
static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@ -261,49 +258,6 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char>
{return int_type(EOF);}
};
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
int
char_traits<char>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
{
if (__n == 0)
return 0;
#if __has_feature(cxx_constexpr_string_builtins)
return __builtin_memcmp(__s1, __s2, __n);
#elif _LIBCPP_STD_VER <= 14
return _VSTD::memcmp(__s1, __s2, __n);
#else
for (; __n; --__n, ++__s1, ++__s2)
{
if (lt(*__s1, *__s2))
return -1;
if (lt(*__s2, *__s1))
return 1;
}
return 0;
#endif
}
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
const char*
char_traits<char>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
{
if (__n == 0)
return nullptr;
#if __has_feature(cxx_constexpr_string_builtins)
return __builtin_char_memchr(__s, to_int_type(__a), __n);
#elif _LIBCPP_STD_VER <= 14
return (const char_type*) _VSTD::memchr(__s, to_int_type(__a), __n);
#else
for (; __n; --__n)
{
if (eq(*__s, __a))
return __s;
++__s;
}
return nullptr;
#endif
}
// char_traits<wchar_t>
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
@ -326,12 +280,22 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<wchar_t>
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
{return __c1 < __c2;}
static _LIBCPP_CONSTEXPR_SINCE_CXX17
int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
static _LIBCPP_CONSTEXPR_SINCE_CXX17
size_t length(const char_type* __s) _NOEXCEPT;
static _LIBCPP_CONSTEXPR_SINCE_CXX17
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
static _LIBCPP_CONSTEXPR_SINCE_CXX17 int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
if (__n == 0)
return 0;
return std::__constexpr_wmemcmp(__s1, __s2, __n);
}
static _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT {
return std::__constexpr_wcslen(__s);
}
static _LIBCPP_CONSTEXPR_SINCE_CXX17
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
if (__n == 0)
return nullptr;
return std::__constexpr_wmemchr(__s, __a, __n);
}
static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@ -363,65 +327,6 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<wchar_t>
static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
{return int_type(WEOF);}
};
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
int
char_traits<wchar_t>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
{
if (__n == 0)
return 0;
#if __has_feature(cxx_constexpr_string_builtins)
return __builtin_wmemcmp(__s1, __s2, __n);
#elif _LIBCPP_STD_VER <= 14
return _VSTD::wmemcmp(__s1, __s2, __n);
#else
for (; __n; --__n, ++__s1, ++__s2)
{
if (lt(*__s1, *__s2))
return -1;
if (lt(*__s2, *__s1))
return 1;
}
return 0;
#endif
}
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
size_t
char_traits<wchar_t>::length(const char_type* __s) _NOEXCEPT
{
#if __has_feature(cxx_constexpr_string_builtins)
return __builtin_wcslen(__s);
#elif _LIBCPP_STD_VER <= 14
return _VSTD::wcslen(__s);
#else
size_t __len = 0;
for (; !eq(*__s, char_type(0)); ++__s)
++__len;
return __len;
#endif
}
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
const wchar_t*
char_traits<wchar_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
{
if (__n == 0)
return nullptr;
#if __has_feature(cxx_constexpr_string_builtins)
return __builtin_wmemchr(__s, __a, __n);
#elif _LIBCPP_STD_VER <= 14
return _VSTD::wmemchr(__s, __a, __n);
#else
for (; __n; --__n)
{
if (eq(*__s, __a))
return __s;
++__s;
}
return nullptr;
#endif
}
#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
#ifndef _LIBCPP_HAS_NO_CHAR8_T
@ -445,8 +350,10 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t>
static inline constexpr bool lt(char_type __c1, char_type __c2) noexcept
{return __c1 < __c2;}
static constexpr
int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
static _LIBCPP_HIDE_FROM_ABI constexpr int
compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
return std::__constexpr_memcmp(__s1, __s2, __n);
}
static constexpr
size_t length(const char_type* __s) _NOEXCEPT;
@ -496,24 +403,6 @@ char_traits<char8_t>::length(const char_type* __s) _NOEXCEPT
return __len;
}
inline constexpr
int
char_traits<char8_t>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
{
#if __has_feature(cxx_constexpr_string_builtins)
return __builtin_memcmp(__s1, __s2, __n);
#else
for (; __n; --__n, ++__s1, ++__s2)
{
if (lt(*__s1, *__s2))
return -1;
if (lt(*__s2, *__s1))
return 1;
}
return 0;
#endif
}
// TODO use '__builtin_char_memchr' if it ever supports char8_t ??
inline constexpr
const char8_t*

View File

@ -58,6 +58,7 @@ size_t strlen(const char* s);
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__type_traits/is_constant_evaluated.h>
#include <string.h>
@ -99,6 +100,53 @@ using ::memset _LIBCPP_USING_IF_EXISTS;
using ::strerror _LIBCPP_USING_IF_EXISTS;
using ::strlen _LIBCPP_USING_IF_EXISTS;
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const char* __str) {
// GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
#ifdef _LIBCPP_COMPILER_GCC
if (__libcpp_is_constant_evaluated()) {
size_t __i = 0;
for (; __str[__i] != '\0'; ++__i)
;
return __i;
}
#endif
return __builtin_strlen(__str);
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
__constexpr_memcmp(const _Tp* __lhs, const _Tp* __rhs, size_t __count) {
#ifdef _LIBCPP_COMPILER_GCC
if (__libcpp_is_constant_evaluated()) {
for (; __count; --__count, ++__lhs, ++__rhs) {
if (*__lhs < *__rhs)
return -1;
if (*__rhs < *__lhs)
return 1;
}
return 0;
}
#endif
return __builtin_memcmp(__lhs, __rhs, __count);
}
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const char*
__constexpr_char_memchr(const char* __str, int __char, size_t __count) {
#if __has_builtin(__builtin_char_memchr)
return __builtin_char_memchr(__str, __char, __count);
#else
if (!__libcpp_is_constant_evaluated())
return static_cast<const char*>(std::memchr(__str, __char, __count));
for (; __count; --__count) {
if (*__str == __char)
return __str;
++__str;
}
return nullptr;
#endif
}
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_CSTRING

View File

@ -104,6 +104,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__type_traits/is_constant_evaluated.h>
#include <cwctype>
#include <wchar.h>
@ -189,6 +190,55 @@ using ::putwchar _LIBCPP_USING_IF_EXISTS;
using ::vwprintf _LIBCPP_USING_IF_EXISTS;
using ::wprintf _LIBCPP_USING_IF_EXISTS;
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_wcslen(const wchar_t* __str) {
#if __has_builtin(__builtin_wcslen)
return __builtin_wcslen(__str);
#else
if (!__libcpp_is_constant_evaluated())
return std::wcslen(__str);
size_t __len = 0;
for (; *__str != L'\0'; ++__str)
++__len;
return __len;
#endif
}
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
__constexpr_wmemcmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __count) {
#if __has_builtin(__builtin_wmemcmp)
return __builtin_wmemcmp(__lhs, __rhs, __count);
#else
if (!__libcpp_is_constant_evaluated())
return std::wmemcmp(__lhs, __rhs, __count);
for (; __count; --__count, ++__lhs, ++__rhs) {
if (*__lhs < *__rhs)
return -1;
if (*__rhs < *__lhs)
return 1;
}
return 0;
#endif
}
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const wchar_t*
__constexpr_wmemchr(const wchar_t* __str, wchar_t __char, size_t __count) {
#if __has_feature(cxx_constexpr_string_builtins)
return __builtin_wmemchr(__str, __char, __count);
#else
if (!__libcpp_is_constant_evaluated())
return std::wmemchr(__str, __char, __count);
for (; __count; --__count) {
if (*__str == __char)
return __str;
++__str;
}
return nullptr;
#endif
}
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_CWCHAR

View File

@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11
// Check that __constexpr_* cstring functions are actually constexpr
#include <cstring>
static_assert(std::__constexpr_strlen("Banane") == 6, "");
static_assert(std::__constexpr_memcmp("Banane", "Banand", 6) == 1, "");
static_assert(std::__constexpr_memcmp("Banane", "Banane", 6) == 0, "");
static_assert(std::__constexpr_memcmp("Banane", "Bananf", 6) == -1, "");
constexpr bool test_constexpr_wmemchr() {
const char str[] = "Banane";
return std::__constexpr_char_memchr(str, 'n', 6) == str + 2;
}
static_assert(test_constexpr_wmemchr(), "");

View File

@ -0,0 +1,26 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11
// UNSUPPORTED: no-wide-characters
// Check that __constexpr_* cwchar functions are actually constexpr
#include <cwchar>
static_assert(std::__constexpr_wcslen(L"Banane") == 6, "");
static_assert(std::__constexpr_wmemcmp(L"Banane", L"Banand", 6) == 1, "");
static_assert(std::__constexpr_wmemcmp(L"Banane", L"Banane", 6) == 0, "");
static_assert(std::__constexpr_wmemcmp(L"Banane", L"Bananf", 6) == -1, "");
constexpr bool test_constexpr_wmemchr() {
const wchar_t str[] = L"Banane";
return std::__constexpr_wmemchr(str, 'n', 6) == str + 2;
}
static_assert(test_constexpr_wmemchr(), "");