Skip to content

Commit 7217346

Browse files
committed
[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
1 parent 1b41074 commit 7217346

File tree

5 files changed

+182
-145
lines changed

5 files changed

+182
-145
lines changed

libcxx/include/__string/char_traits.h

Lines changed: 34 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -210,25 +210,22 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char>
210210
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
211211
{return (unsigned char)__c1 < (unsigned char)__c2;}
212212

213-
static _LIBCPP_CONSTEXPR_SINCE_CXX17
214-
int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
213+
static _LIBCPP_CONSTEXPR_SINCE_CXX17 int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
214+
if (__n == 0)
215+
return 0;
216+
return std::__constexpr_memcmp(__s1, __s2, __n);
217+
}
215218

216-
static inline size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s) _NOEXCEPT {
217-
// GCC currently does not support __builtin_strlen during constant evaluation.
218-
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
219-
#ifdef _LIBCPP_COMPILER_GCC
220-
if (__libcpp_is_constant_evaluated()) {
221-
size_t __i = 0;
222-
for (; __s[__i] != char_type('\0'); ++__i)
223-
;
224-
return __i;
225-
}
226-
#endif
227-
return __builtin_strlen(__s);
228-
}
219+
static inline size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s) _NOEXCEPT {
220+
return std::__constexpr_strlen(__s);
221+
}
229222

230-
static _LIBCPP_CONSTEXPR_SINCE_CXX17
231-
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
223+
static _LIBCPP_CONSTEXPR_SINCE_CXX17
224+
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
225+
if (__n == 0)
226+
return nullptr;
227+
return std::__constexpr_char_memchr(__s, static_cast<int>(__a), __n);
228+
}
232229

233230
static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
234231
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>
261258
{return int_type(EOF);}
262259
};
263260

264-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
265-
int
266-
char_traits<char>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
267-
{
268-
if (__n == 0)
269-
return 0;
270-
#if __has_feature(cxx_constexpr_string_builtins)
271-
return __builtin_memcmp(__s1, __s2, __n);
272-
#elif _LIBCPP_STD_VER <= 14
273-
return _VSTD::memcmp(__s1, __s2, __n);
274-
#else
275-
for (; __n; --__n, ++__s1, ++__s2)
276-
{
277-
if (lt(*__s1, *__s2))
278-
return -1;
279-
if (lt(*__s2, *__s1))
280-
return 1;
281-
}
282-
return 0;
283-
#endif
284-
}
285-
286-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
287-
const char*
288-
char_traits<char>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
289-
{
290-
if (__n == 0)
291-
return nullptr;
292-
#if __has_feature(cxx_constexpr_string_builtins)
293-
return __builtin_char_memchr(__s, to_int_type(__a), __n);
294-
#elif _LIBCPP_STD_VER <= 14
295-
return (const char_type*) _VSTD::memchr(__s, to_int_type(__a), __n);
296-
#else
297-
for (; __n; --__n)
298-
{
299-
if (eq(*__s, __a))
300-
return __s;
301-
++__s;
302-
}
303-
return nullptr;
304-
#endif
305-
}
306-
307261
// char_traits<wchar_t>
308262

309263
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
@@ -326,12 +280,22 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<wchar_t>
326280
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
327281
{return __c1 < __c2;}
328282

329-
static _LIBCPP_CONSTEXPR_SINCE_CXX17
330-
int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
331-
static _LIBCPP_CONSTEXPR_SINCE_CXX17
332-
size_t length(const char_type* __s) _NOEXCEPT;
333-
static _LIBCPP_CONSTEXPR_SINCE_CXX17
334-
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
283+
static _LIBCPP_CONSTEXPR_SINCE_CXX17 int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
284+
if (__n == 0)
285+
return 0;
286+
return std::__constexpr_wmemcmp(__s1, __s2, __n);
287+
}
288+
289+
static _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT {
290+
return std::__constexpr_wcslen(__s);
291+
}
292+
293+
static _LIBCPP_CONSTEXPR_SINCE_CXX17
294+
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
295+
if (__n == 0)
296+
return nullptr;
297+
return std::__constexpr_wmemchr(__s, __a, __n);
298+
}
335299

336300
static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
337301
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>
363327
static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
364328
{return int_type(WEOF);}
365329
};
366-
367-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
368-
int
369-
char_traits<wchar_t>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
370-
{
371-
if (__n == 0)
372-
return 0;
373-
#if __has_feature(cxx_constexpr_string_builtins)
374-
return __builtin_wmemcmp(__s1, __s2, __n);
375-
#elif _LIBCPP_STD_VER <= 14
376-
return _VSTD::wmemcmp(__s1, __s2, __n);
377-
#else
378-
for (; __n; --__n, ++__s1, ++__s2)
379-
{
380-
if (lt(*__s1, *__s2))
381-
return -1;
382-
if (lt(*__s2, *__s1))
383-
return 1;
384-
}
385-
return 0;
386-
#endif
387-
}
388-
389-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
390-
size_t
391-
char_traits<wchar_t>::length(const char_type* __s) _NOEXCEPT
392-
{
393-
#if __has_feature(cxx_constexpr_string_builtins)
394-
return __builtin_wcslen(__s);
395-
#elif _LIBCPP_STD_VER <= 14
396-
return _VSTD::wcslen(__s);
397-
#else
398-
size_t __len = 0;
399-
for (; !eq(*__s, char_type(0)); ++__s)
400-
++__len;
401-
return __len;
402-
#endif
403-
}
404-
405-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
406-
const wchar_t*
407-
char_traits<wchar_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
408-
{
409-
if (__n == 0)
410-
return nullptr;
411-
#if __has_feature(cxx_constexpr_string_builtins)
412-
return __builtin_wmemchr(__s, __a, __n);
413-
#elif _LIBCPP_STD_VER <= 14
414-
return _VSTD::wmemchr(__s, __a, __n);
415-
#else
416-
for (; __n; --__n)
417-
{
418-
if (eq(*__s, __a))
419-
return __s;
420-
++__s;
421-
}
422-
return nullptr;
423-
#endif
424-
}
425330
#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
426331

427332
#ifndef _LIBCPP_HAS_NO_CHAR8_T
@@ -445,8 +350,10 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t>
445350
static inline constexpr bool lt(char_type __c1, char_type __c2) noexcept
446351
{return __c1 < __c2;}
447352

448-
static constexpr
449-
int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
353+
static _LIBCPP_HIDE_FROM_ABI constexpr int
354+
compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
355+
return std::__constexpr_memcmp(__s1, __s2, __n);
356+
}
450357

451358
static constexpr
452359
size_t length(const char_type* __s) _NOEXCEPT;
@@ -496,24 +403,6 @@ char_traits<char8_t>::length(const char_type* __s) _NOEXCEPT
496403
return __len;
497404
}
498405

499-
inline constexpr
500-
int
501-
char_traits<char8_t>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
502-
{
503-
#if __has_feature(cxx_constexpr_string_builtins)
504-
return __builtin_memcmp(__s1, __s2, __n);
505-
#else
506-
for (; __n; --__n, ++__s1, ++__s2)
507-
{
508-
if (lt(*__s1, *__s2))
509-
return -1;
510-
if (lt(*__s2, *__s1))
511-
return 1;
512-
}
513-
return 0;
514-
#endif
515-
}
516-
517406
// TODO use '__builtin_char_memchr' if it ever supports char8_t ??
518407
inline constexpr
519408
const char8_t*

libcxx/include/cstring

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ size_t strlen(const char* s);
5858

5959
#include <__assert> // all public C++ headers provide the assertion handler
6060
#include <__config>
61+
#include <__type_traits/is_constant_evaluated.h>
6162

6263
#include <string.h>
6364

@@ -99,6 +100,53 @@ using ::memset _LIBCPP_USING_IF_EXISTS;
99100
using ::strerror _LIBCPP_USING_IF_EXISTS;
100101
using ::strlen _LIBCPP_USING_IF_EXISTS;
101102

103+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const char* __str) {
104+
// GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation.
105+
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
106+
#ifdef _LIBCPP_COMPILER_GCC
107+
if (__libcpp_is_constant_evaluated()) {
108+
size_t __i = 0;
109+
for (; __str[__i] != '\0'; ++__i)
110+
;
111+
return __i;
112+
}
113+
#endif
114+
return __builtin_strlen(__str);
115+
}
116+
117+
template <class _Tp>
118+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
119+
__constexpr_memcmp(const _Tp* __lhs, const _Tp* __rhs, size_t __count) {
120+
#ifdef _LIBCPP_COMPILER_GCC
121+
if (__libcpp_is_constant_evaluated()) {
122+
for (; __count; --__count, ++__lhs, ++__rhs) {
123+
if (*__lhs < *__rhs)
124+
return -1;
125+
if (*__rhs < *__lhs)
126+
return 1;
127+
}
128+
return 0;
129+
}
130+
#endif
131+
return __builtin_memcmp(__lhs, __rhs, __count);
132+
}
133+
134+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const char*
135+
__constexpr_char_memchr(const char* __str, int __char, size_t __count) {
136+
#if __has_builtin(__builtin_char_memchr)
137+
return __builtin_char_memchr(__str, __char, __count);
138+
#else
139+
if (!__libcpp_is_constant_evaluated())
140+
return static_cast<const char*>(std::memchr(__str, __char, __count));
141+
for (; __count; --__count) {
142+
if (*__str == __char)
143+
return __str;
144+
++__str;
145+
}
146+
return nullptr;
147+
#endif
148+
}
149+
102150
_LIBCPP_END_NAMESPACE_STD
103151

104152
#endif // _LIBCPP_CSTRING

libcxx/include/cwchar

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
104104

105105
#include <__assert> // all public C++ headers provide the assertion handler
106106
#include <__config>
107+
#include <__type_traits/is_constant_evaluated.h>
107108
#include <cwctype>
108109

109110
#include <wchar.h>
@@ -189,6 +190,55 @@ using ::putwchar _LIBCPP_USING_IF_EXISTS;
189190
using ::vwprintf _LIBCPP_USING_IF_EXISTS;
190191
using ::wprintf _LIBCPP_USING_IF_EXISTS;
191192

193+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_wcslen(const wchar_t* __str) {
194+
#if __has_builtin(__builtin_wcslen)
195+
return __builtin_wcslen(__str);
196+
#else
197+
if (!__libcpp_is_constant_evaluated())
198+
return std::wcslen(__str);
199+
200+
size_t __len = 0;
201+
for (; *__str != L'\0'; ++__str)
202+
++__len;
203+
return __len;
204+
#endif
205+
}
206+
207+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
208+
__constexpr_wmemcmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __count) {
209+
#if __has_builtin(__builtin_wmemcmp)
210+
return __builtin_wmemcmp(__lhs, __rhs, __count);
211+
#else
212+
if (!__libcpp_is_constant_evaluated())
213+
return std::wmemcmp(__lhs, __rhs, __count);
214+
215+
for (; __count; --__count, ++__lhs, ++__rhs) {
216+
if (*__lhs < *__rhs)
217+
return -1;
218+
if (*__rhs < *__lhs)
219+
return 1;
220+
}
221+
return 0;
222+
#endif
223+
}
224+
225+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const wchar_t*
226+
__constexpr_wmemchr(const wchar_t* __str, wchar_t __char, size_t __count) {
227+
#if __has_feature(cxx_constexpr_string_builtins)
228+
return __builtin_wmemchr(__str, __char, __count);
229+
#else
230+
if (!__libcpp_is_constant_evaluated())
231+
return std::wmemchr(__str, __char, __count);
232+
233+
for (; __count; --__count) {
234+
if (*__str == __char)
235+
return __str;
236+
++__str;
237+
}
238+
return nullptr;
239+
#endif
240+
}
241+
192242
_LIBCPP_END_NAMESPACE_STD
193243

194244
#endif // _LIBCPP_CWCHAR
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11
10+
11+
// Check that __constexpr_* cstring functions are actually constexpr
12+
13+
#include <cstring>
14+
15+
static_assert(std::__constexpr_strlen("Banane") == 6, "");
16+
static_assert(std::__constexpr_memcmp("Banane", "Banand", 6) == 1, "");
17+
static_assert(std::__constexpr_memcmp("Banane", "Banane", 6) == 0, "");
18+
static_assert(std::__constexpr_memcmp("Banane", "Bananf", 6) == -1, "");
19+
20+
constexpr bool test_constexpr_wmemchr() {
21+
const char str[] = "Banane";
22+
return std::__constexpr_char_memchr(str, 'n', 6) == str + 2;
23+
}
24+
static_assert(test_constexpr_wmemchr(), "");

0 commit comments

Comments
 (0)