10#include "c4/export.hpp"
11#include "c4/language.hpp"
12#include "c4/error.hpp"
13#include "c4/substr_fwd.hpp"
15C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
16C4_SUPPRESS_WARNING_GCC_CLANG(
"-Wold-style-cast")
17C4_SUPPRESS_WARNING_GCC(
"-Wuseless-cast")
18C4_SUPPRESS_WARNING_GCC(
"-Wtype-limits")
27template<
class T>
struct is_string :
public std::false_type {};
28template<
class T>
struct is_writeable_string :
public std::false_type {};
30template<>
struct is_string<char*> :
public std::true_type {};
31template<>
struct is_writeable_string<char*> :
public std::true_type {};
33template<>
struct is_string<const char*> :
public std::true_type {};
34template<>
struct is_writeable_string<const char*> :
public std::false_type {};
36template<
size_t N>
struct is_string<const char[N]> :
public std::true_type {};
37template<
size_t N>
struct is_writeable_string<const char[N]> :
public std::false_type {};
39template<
size_t N>
struct is_string<char[N]> :
public std::true_type {};
40template<
size_t N>
struct is_writeable_string<char[N]> :
public std::true_type {};
42template<
size_t N>
struct is_string<const char (&)[N]> :
public std::true_type {};
43template<
size_t N>
struct is_writeable_string<const char (&)[N]> :
public std::false_type {};
45template<
size_t N>
struct is_string<char (&)[N]> :
public std::true_type {};
46template<
size_t N>
struct is_writeable_string<char (&)[N]> :
public std::true_type {};
48template<
size_t N>
struct is_string<const char (&&)[N]> :
public std::true_type {};
49template<
size_t N>
struct is_writeable_string<const char (&&)[N]> :
public std::false_type {};
51template<
size_t N>
struct is_string<char (&&)[N]> :
public std::true_type {};
52template<
size_t N>
struct is_writeable_string<char (&&)[N]> :
public std::true_type {};
56template<
class T>
struct remove_restrict {
using type = T; };
57template<
class T>
struct remove_restrict<T *C4_RESTRICT> {
using type = T*; };
58template<
class T>
struct remove_restrict<T &C4_RESTRICT> {
using type = T&; };
60template<
class T>
struct remove_ptrref {
using type = T; };
61template<
class T>
struct remove_ptrref<T*&> {
using type = T*; };
62template<
class T>
struct remove_ptrref<T*
const&> {
using type = T*
const; };
63template<
class T>
struct remove_ptrref<T* C4_RESTRICT &> {
using type = T *C4_RESTRICT; };
64template<
class T>
struct remove_ptrref<T* C4_RESTRICT
const&> {
using type = T *C4_RESTRICT
const; };
66template<
class T>
struct remove_ptrconst {
using type = T; };
67template<
class T>
struct remove_ptrconst<T *
const> {
using type = T*; };
68template<
class T>
struct remove_ptrconst<T *C4_RESTRICT
const> {
using type = T* C4_RESTRICT; };
74struct bare_pointer_type
76 using type =
typename remove_restrict<
77 typename remove_ptrconst<
78 typename remove_ptrref<
85template<
class FromPo
interTypeBare,
class ToValueType>
86struct _is_comp_char_ptr : std::integral_constant<
88 std::is_same<FromPointerTypeBare,
89 typename std::remove_const<ToValueType>::type *>::value
91 std::is_same<FromPointerTypeBare,
92 ToValueType const*>::value> {};
95template<
class FromPo
interTypeBare,
class ToValueType>
96struct _can_borrow_char_ptr : std::integral_constant<
98 _is_comp_char_ptr<FromPointerTypeBare, ToValueType>::value
101 std::is_const<ToValueType>::value
103 ! std::is_const<typename std::remove_pointer<FromPointerTypeBare>::type>::value
108static inline void _do_reverse(C *C4_RESTRICT first, C *C4_RESTRICT last)
noexcept
132 :
public detail::is_string<
133 typename detail::bare_pointer_type<T>::type
141 :
public detail::is_writeable_string<
142 typename detail::bare_pointer_type<T>::type
164template<
class FromPo
interType,
class ToValueType>
166 : detail::_is_comp_char_ptr<
167 typename detail::bare_pointer_type<FromPointerType>::type,
184template<
class FromPo
interType,
class ToValueType>
186 : detail::_can_borrow_char_ptr<
187 typename detail::bare_pointer_type<FromPointerType>::type,
225 using CC =
typename std::add_const<C>::type;
226 using NCC_ =
typename std::remove_const<C>::type;
238 enum :
size_t {
npos = (size_t)-1,
NONE = (
size_t)-1 };
242 C4_ALWAYS_INLINE
operator
243 typename std::enable_if<!std::is_const<U>::value,
ro_substr const&>
::type () const noexcept
265 C4_ALWAYS_INLINE
void clear() noexcept {
str =
nullptr;
len = 0; }
285 C4_ALWAYS_INLINE
basic_substring(C *beg_, C *end_) noexcept :
str(beg_),
len(
static_cast<size_t>(end_ - beg_)) { C4_ASSERT(end_ >= beg_); }
292 template<class CharPtr, typename std::enable_if<can_borrow_char_ptr<CharPtr, C>::value,
int>::type=0>
300 C4_ALWAYS_INLINE
void assign(C (&s_)[N])
noexcept {
str = s_;
len = (N-1); }
303 C4_ALWAYS_INLINE
void assign(C *s_,
size_t len_)
noexcept {
str = s_;
len = len_; C4_ASSERT(
str || !len_); }
307 C4_ALWAYS_INLINE
void assign(C *beg_, C *end_)
noexcept { C4_ASSERT(end_ >= beg_);
str = beg_;
len =
static_cast<size_t>(end_ - beg_); }
314 template<class CharPtr, typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>::type=0>
315 C4_ALWAYS_INLINE
void assign(CharPtr s_)
noexcept
322 len = (s_ ? strlen(s_) : 0);
336 template<class CharPtr, typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>::type=0>
344 len = s_ ? strlen(s_) : 0;
355 C4_ALWAYS_INLINE C4_PURE
bool has_str() const noexcept {
return !
empty() &&
str[0] != C(0); }
356 C4_ALWAYS_INLINE C4_PURE
bool empty() const noexcept {
return (
len == 0 ||
str ==
nullptr); }
357 C4_ALWAYS_INLINE C4_PURE
bool not_empty() const noexcept {
return (
len != 0 &&
str !=
nullptr); }
358 C4_ALWAYS_INLINE C4_PURE
size_t size() const noexcept {
return len; }
366 C4_ALWAYS_INLINE C4_PURE C *
data() noexcept {
return str; }
367 C4_ALWAYS_INLINE C4_PURE C
const*
data() const noexcept {
return str; }
369 C4_ALWAYS_INLINE C4_PURE C & operator[] (
size_t i)
noexcept { C4_ASSERT(i >= 0 && i <
len);
return str[i]; }
370 C4_ALWAYS_INLINE C4_PURE C
const& operator[] (
size_t i)
const noexcept { C4_ASSERT(i >= 0 && i <
len);
return str[i]; }
372 C4_ALWAYS_INLINE C4_PURE C &
front() noexcept { C4_ASSERT(
len > 0 &&
str !=
nullptr);
return *
str; }
373 C4_ALWAYS_INLINE C4_PURE C
const&
front() const noexcept { C4_ASSERT(
len > 0 &&
str !=
nullptr);
return *
str; }
375 C4_ALWAYS_INLINE C4_PURE C &
back() noexcept { C4_ASSERT(
len > 0 &&
str !=
nullptr);
return *(
str +
len - 1); }
376 C4_ALWAYS_INLINE C4_PURE C
const&
back() const noexcept { C4_ASSERT(
len > 0 &&
str !=
nullptr);
return *(
str +
len - 1); }
385 C4_ALWAYS_INLINE C4_PURE
int compare(C
const c)
const noexcept
387 C4_XASSERT((
str !=
nullptr) ||
len == 0);
388 if(C4_LIKELY(
str !=
nullptr &&
len > 0))
389 return (*
str != c) ? *
str - c : (
static_cast<int>(
len) - 1);
394 C4_PURE
int compare(C
const* C4_RESTRICT that,
size_t sz)
const noexcept
396 #if defined(__GNUC__) && (__GNUC__ >= 6)
397 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wnull-dereference")
399 C4_XASSERT(that || sz == 0);
400 C4_XASSERT(
str ||
len == 0);
401 if(C4_LIKELY(
str && that))
404 const size_t min =
len < sz ?
len : sz;
405 for(
size_t i = 0; i < min; ++i)
406 if(
str[i] != that[i])
407 return str[i] < that[i] ? -1 : 1;
418 C4_XASSERT(
len == 0 && sz == 0);
421 return len < sz ? -1 : 1;
422 #if defined(__GNUC__) && (__GNUC__ >= 6)
423 C4_SUPPRESS_WARNING_GCC_POP
427 template<
class CharPtr>
428 C4_ALWAYS_INLINE C4_PURE
auto compare(CharPtr c_str)
const noexcept
429 ->
typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>
::type
431 return compare(c_str, strlen(c_str));
437 return this->
compare(that.str, that.len);
440 C4_ALWAYS_INLINE C4_PURE
bool operator== (std::nullptr_t)
const noexcept {
return str ==
nullptr; }
441 C4_ALWAYS_INLINE C4_PURE
bool operator!= (std::nullptr_t)
const noexcept {
return str !=
nullptr; }
443 C4_ALWAYS_INLINE C4_PURE
bool operator== (C
const c)
const noexcept {
return this->
compare(c) == 0; }
444 C4_ALWAYS_INLINE C4_PURE
bool operator!= (C
const c)
const noexcept {
return this->
compare(c) != 0; }
445 C4_ALWAYS_INLINE C4_PURE
bool operator< (C
const c)
const noexcept {
return this->
compare(c) < 0; }
446 C4_ALWAYS_INLINE C4_PURE
bool operator> (C
const c)
const noexcept {
return this->
compare(c) > 0; }
447 C4_ALWAYS_INLINE C4_PURE
bool operator<= (C
const c)
const noexcept {
return this->
compare(c) <= 0; }
448 C4_ALWAYS_INLINE C4_PURE
bool operator>= (C
const c)
const noexcept {
return this->
compare(c) >= 0; }
450 template<
class U> C4_ALWAYS_INLINE C4_PURE
bool operator== (
basic_substring<U> const that)
const noexcept {
return this->
compare(that) == 0; }
451 template<
class U> C4_ALWAYS_INLINE C4_PURE
bool operator!= (
basic_substring<U> const that)
const noexcept {
return this->
compare(that) != 0; }
452 template<
class U> C4_ALWAYS_INLINE C4_PURE
bool operator< (
basic_substring<U> const that)
const noexcept {
return this->
compare(that) < 0; }
453 template<
class U> C4_ALWAYS_INLINE C4_PURE
bool operator> (
basic_substring<U> const that)
const noexcept {
return this->
compare(that) > 0; }
454 template<
class U> C4_ALWAYS_INLINE C4_PURE
bool operator<= (
basic_substring<U> const that)
const noexcept {
return this->
compare(that) <= 0; }
455 template<
class U> C4_ALWAYS_INLINE C4_PURE
bool operator>= (
basic_substring<U> const that)
const noexcept {
return this->
compare(that) >= 0; }
457 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator== (
const char (&arr)[N])
const noexcept {
return this->
compare(arr, N-1) == 0; }
458 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator!= (
const char (&arr)[N])
const noexcept {
return this->
compare(arr, N-1) != 0; }
459 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator< (
const char (&arr)[N])
const noexcept {
return this->
compare(arr, N-1) < 0; }
460 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator> (
const char (&arr)[N])
const noexcept {
return this->
compare(arr, N-1) > 0; }
461 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator<= (
const char (&arr)[N])
const noexcept {
return this->
compare(arr, N-1) <= 0; }
462 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator>= (
const char (&arr)[N])
const noexcept {
return this->
compare(arr, N-1) >= 0; }
464 template<
class CharPtr> C4_ALWAYS_INLINE C4_PURE
auto operator== (CharPtr c_str)
const noexcept ->
typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>
::type{
return this->
compare(c_str, strlen(c_str)) == 0; }
465 template<
class CharPtr> C4_ALWAYS_INLINE C4_PURE
auto operator!= (CharPtr c_str)
const noexcept ->
typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>
::type{
return this->
compare(c_str, strlen(c_str)) != 0; }
466 template<
class CharPtr> C4_ALWAYS_INLINE C4_PURE
auto operator< (CharPtr c_str)
const noexcept ->
typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>
::type{
return this->
compare(c_str, strlen(c_str)) < 0; }
467 template<
class CharPtr> C4_ALWAYS_INLINE C4_PURE
auto operator> (CharPtr c_str)
const noexcept ->
typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>
::type{
return this->
compare(c_str, strlen(c_str)) > 0; }
468 template<
class CharPtr> C4_ALWAYS_INLINE C4_PURE
auto operator<= (CharPtr c_str)
const noexcept ->
typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>
::type{
return this->
compare(c_str, strlen(c_str)) <= 0; }
469 template<
class CharPtr> C4_ALWAYS_INLINE C4_PURE
auto operator>= (CharPtr c_str)
const noexcept ->
typename std::enable_if<is_compatible_char_ptr<CharPtr, C>::value,
int>
::type{
return this->
compare(c_str, strlen(c_str)) >= 0; }
481 return that.is_super(*
this);
487 if(C4_LIKELY(
len > 0))
488 return that.str >=
str && that.str+that.len <=
str+
len;
490 return that.len == 0 && that.str ==
str &&
str !=
nullptr;
497 return that.str+that.len >
str && that.str <
str+
len;
513 C4_ASSERT((num >= 0 && num <=
len) || (num ==
npos));
515 C4_ASSERT((
first >= 0 &&
first + rnum <=
len) || (num == 0));
532 C4_ASSERT(num <=
len || num ==
npos);
539 C4_ASSERT(num <=
len || num ==
npos);
550 C4_ASSERT(left >= 0 && left <=
len);
551 C4_ASSERT(right >= 0 && right <=
len);
552 C4_ASSERT(left <=
len - right + 1);
559 C4_ASSERT(pos <=
len || pos ==
npos);
560 return (pos !=
npos) ?
568 C4_ASSERT(pos <=
len || pos ==
npos);
569 return (pos !=
npos) ?
577 C4_ASSERT(pos <=
len || pos ==
npos);
578 return (pos !=
npos) ?
586 C4_ASSERT(pos <=
len || pos ==
npos);
587 return (pos !=
npos) ?
598 C4_ASSERT(
is_super(subs) || subs.empty());
599 auto ssb = subs.begin();
602 if(ssb >= b && ssb <= e)
603 return sub(0,
static_cast<size_t>(ssb - b));
612 C4_ASSERT(
is_super(subs) || subs.empty());
613 auto sse = subs.end();
616 if(sse >= b && sse <= e)
617 return sub(
static_cast<size_t>(sse - b),
static_cast<size_t>(e - sse));
660 return sub(0, pos+1);
672 return sub(0, pos+1);
680 return triml(c).trimr(c);
686 return triml(chars).trimr(chars);
714 size_t find(
const C c,
size_t start_pos=0)
const
720 C4_ASSERT(start_pos ==
npos || (start_pos >= 0 && start_pos <=
len));
722 for(
size_t i = start_pos, e =
len - pattern.
len + 1; i < e; ++i)
725 for(
size_t j = 0; j < pattern.
len; ++j)
727 C4_ASSERT(i + j <
len);
728 if(
str[i + j] != pattern.
str[j])
745 size_t count(
const C c,
size_t pos=0)
const
747 C4_ASSERT(pos >= 0 && pos <=
len);
753 pos =
find(c, pos + 1);
761 C4_ASSERT(pos >= 0 && pos <=
len);
782 pos =
find(pattern, pos);
822 for(
size_t i = 0; i <
len; ++i)
825 for(It it = first_span; it != last_span; ++curr, ++it)
827 auto const& chars = *it;
828 if((i + chars.len) >
len)
continue;
830 for(
size_t j = 0; j < chars.len; ++j)
832 C4_ASSERT(i + j <
len);
833 if(
str[i + j] != chars[j])
853 C4_SUPPRESS_WARNING_GCC_PUSH
854 #if defined(__GNUC__) && (__GNUC__ >= 6)
855 C4_SUPPRESS_WARNING_GCC(
"-Wnull-dereference")
857 return len &&
str[0] == c;
858 C4_SUPPRESS_WARNING_GCC_POP
866 for(
size_t i = 0; i < num; ++i)
875 if(
len < pattern.len)
877 for(
size_t i = 0; i < pattern.len; ++i)
878 if(
str[i] != pattern.str[i])
880 return pattern.len > 0;
887 for(
size_t i = 0; i < chars.len; ++i)
888 if(
str[0] == chars.str[i])
901 bool ends_with(
const C c,
const size_t num)
const noexcept
905 for(
size_t i =
len - num; i <
len; ++i)
914 if(
len < pattern.len)
916 for(
size_t i = 0, s =
len-pattern.len; i < pattern.len; ++i)
917 if(
str[s+i] != pattern[i])
919 return pattern.len > 0;
926 for(
size_t i = 0; i < chars.len; ++i)
927 if(
str[
len - 1] == chars[i])
937 C4_ASSERT(start ==
npos || (start >= 0 && start <=
len));
938 for(
size_t i = start; i <
len; ++i)
949 C4_ASSERT(start ==
npos || (start >= 0 && start <=
len));
952 for(
size_t i = start-1; i != size_t(-1); --i)
963 C4_ASSERT(start ==
npos || (start >= 0 && start <=
len));
964 for(
size_t i = start; i <
len; ++i)
966 for(
size_t j = 0; j < chars.
len; ++j)
968 if(
str[i] == chars.
str[j])
978 C4_ASSERT(start ==
npos || (start >= 0 && start <=
len));
981 for(
size_t i = start-1; i != size_t(-1); --i)
983 for(
size_t j = 0; j < chars.
len; ++j)
985 if(
str[i] == chars[j])
996 for(
size_t i = 0; i <
len; ++i)
1006 C4_ASSERT((start >= 0 && start <=
len) || (start ==
len &&
len == 0));
1007 for(
size_t i = start; i <
len; ++i)
1017 for(
size_t i =
len-1; i != size_t(-1); --i)
1027 C4_ASSERT(start ==
npos || (start >= 0 && start <=
len));
1030 for(
size_t i = start-1; i != size_t(-1); --i)
1040 for(
size_t i = 0; i <
len; ++i)
1043 for(
size_t j = 0; j < chars.
len; ++j)
1045 if(
str[i] == chars.
str[j])
1061 C4_ASSERT((start >= 0 && start <=
len) || (start ==
len &&
len == 0));
1062 for(
size_t i = start; i <
len; ++i)
1065 for(
size_t j = 0; j < chars.
len; ++j)
1067 if(
str[i] == chars.
str[j])
1083 for(
size_t i =
len-1; i != size_t(-1); --i)
1086 for(
size_t j = 0; j < chars.
len; ++j)
1088 if(
str[i] == chars.
str[j])
1104 C4_ASSERT(start ==
npos || (start >= 0 && start <=
len));
1107 for(
size_t i = start-1; i != size_t(-1); --i)
1110 for(
size_t j = 0; j < chars.
len; ++j)
1112 if(
str[i] == chars.
str[j])
1138 size_t b =
find(open);
1141 size_t e =
find(close, b+1);
1153 size_t b =
find(open_close);
1155 for(
size_t i = b+1; i <
len; ++i)
1160 if(
str[i-1] != escape)
1162 return range(b, i+1);
1174 size_t b =
find(open);
1176 size_t e, curr = b+1,
count = 0;
1177 const char both[] = {open, close,
'\0'};
1185 else if(
str[e] == close)
1197 constexpr const C dq(
'"'), sq(
'\'');
1198 if(
len >= 2 && (
str[
len - 2] != C(
'\\')) &&
1268 constexpr const ro_substr empty_chars(
" \n\r\t");
1272 auto ret =
sub(pos);
1273 pos = ret.first_of(empty_chars);
1274 return ret.first(pos);
1283 if(ne.
str[0] ==
'-')
1285 size_t skip_start = size_t(ne.
str[0] ==
'+');
1295 size_t skip_start = size_t(ne.
str[0] ==
'+' || ne.
str[0] ==
'-');
1301 C4_ASSERT(!
empty());
1302 if(skip_start ==
len)
1304 C4_ASSERT(skip_start <
len);
1305 if(
len >= skip_start + 3)
1307 if(
str[skip_start] !=
'0')
1309 for(
size_t i = skip_start; i <
len; ++i)
1312 if(c <
'0' || c >
'9')
1318 char next =
str[skip_start + 1];
1319 if(next ==
'x' || next ==
'X')
1322 for(
size_t i = skip_start; i <
len; ++i)
1324 const char c =
str[i];
1330 else if(next ==
'b' || next ==
'B')
1333 for(
size_t i = skip_start; i <
len; ++i)
1335 const char c =
str[i];
1336 if(c !=
'0' && c !=
'1')
1341 else if(next ==
'o' || next ==
'O')
1344 for(
size_t i = skip_start; i <
len; ++i)
1346 const char c =
str[i];
1347 if(c <
'0' || c >
'7')
1355 for(
size_t i = skip_start; i <
len; ++i)
1357 const char c =
str[i];
1358 if(c <
'0' || c >
'9')
1370 const size_t skip_start = (ne.
str[0] ==
'+' || ne.
str[0] ==
'-');
1371 C4_ASSERT(skip_start == 0 || skip_start == 1);
1376 if(ne.
len >= skip_start+3)
1379 if(ne.
str[skip_start] !=
'0')
1381 if(ne.
str[skip_start] ==
'i')
1388 else if(ne.
str[skip_start] ==
'n')
1399 const char next = ne.
str[skip_start + 1];
1401 if(next ==
'x' || next ==
'X')
1404 else if(next ==
'b' || next ==
'B')
1407 else if(next ==
'o' || next ==
'O')
1422 return c ==
' ' || c ==
'\n'
1423 || c ==
']' || c ==
')' || c ==
'}'
1424 || c ==
',' || c ==
';' || c ==
'\r' || c ==
'\t' || c ==
'\0';
1428 static constexpr C4_ALWAYS_INLINE C4_CONST
bool _is_hex_char(
char c)
noexcept
1430 return (c >=
'0' && c <=
'9') || (c >=
'a' && c <=
'f') || (c >=
'A' && c <=
'F');
1435 size_t posend = pos + word.len;
1436 if(
len >= posend &&
sub(pos, word.len) == word)
1438 return first(posend);
1445 bool intchars =
false;
1446 bool fracchars =
false;
1449 for( ; pos <
len; ++pos)
1451 const char c =
str[pos];
1452 if(c >=
'0' && c <=
'9')
1459 goto fractional_part_dec;
1461 else if(c ==
'e' || c ==
'E')
1464 goto power_part_dec;
1480 fractional_part_dec:
1482 C4_ASSERT(
str[pos - 1] ==
'.');
1483 for( ; pos <
len; ++pos)
1485 const char c =
str[pos];
1486 if(c >=
'0' && c <=
'9')
1490 else if(c ==
'e' || c ==
'E')
1493 goto power_part_dec;
1497 return intchars || fracchars ?
first(pos) :
first(0);
1504 return intchars || fracchars ?
1509 C4_ASSERT(
str[pos - 1] ==
'e' ||
str[pos - 1] ==
'E');
1511 if((
len == pos) || ((!intchars) && (!fracchars)))
1513 if(
str[pos] ==
'-' ||
str[pos] ==
'+')
1516 for( ; pos <
len; ++pos)
1518 const char c =
str[pos];
1519 if(c >=
'0' && c <=
'9')
1526 return powchars ? *this :
first(0);
1532 bool intchars =
false;
1533 bool fracchars =
false;
1536 for( ; pos <
len; ++pos)
1538 const char c =
str[pos];
1546 goto fractional_part_hex;
1548 else if(c ==
'p' || c ==
'P')
1551 goto power_part_hex;
1567 fractional_part_hex:
1569 C4_ASSERT(
str[pos - 1] ==
'.');
1570 for( ; pos <
len; ++pos)
1572 const char c =
str[pos];
1577 else if(c ==
'p' || c ==
'P')
1580 goto power_part_hex;
1584 return intchars || fracchars ?
first(pos) :
first(0);
1591 return intchars || fracchars ?
1596 C4_ASSERT(
str[pos - 1] ==
'p' ||
str[pos - 1] ==
'P');
1600 if(
len <= (pos+1) || (
str[pos] !=
'+' &&
str[pos] !=
'-') || ((!intchars) && (!fracchars)))
1606 for( ; pos <
len; ++pos)
1608 const char c =
str[pos];
1609 if(c >=
'0' && c <=
'9')
1622 bool intchars =
false;
1623 bool fracchars =
false;
1626 for( ; pos <
len; ++pos)
1628 const char c =
str[pos];
1629 if(c ==
'0' || c ==
'1')
1636 goto fractional_part_bin;
1638 else if(c ==
'p' || c ==
'P')
1641 goto power_part_bin;
1657 fractional_part_bin:
1659 C4_ASSERT(
str[pos - 1] ==
'.');
1660 for( ; pos <
len; ++pos)
1662 const char c =
str[pos];
1663 if(c ==
'0' || c ==
'1')
1667 else if(c ==
'p' || c ==
'P')
1670 goto power_part_bin;
1674 return intchars || fracchars ?
first(pos) :
first(0);
1681 return intchars || fracchars ?
1686 C4_ASSERT(
str[pos - 1] ==
'p' ||
str[pos - 1] ==
'P');
1690 if(
len <= (pos+1) || (
str[pos] !=
'+' &&
str[pos] !=
'-') || ((!intchars) && (!fracchars)))
1696 for( ; pos <
len; ++pos)
1698 const char c =
str[pos];
1699 if(c >=
'0' && c <=
'9')
1712 bool intchars =
false;
1713 bool fracchars =
false;
1716 for( ; pos <
len; ++pos)
1718 const char c =
str[pos];
1719 if(c >=
'0' && c <=
'7')
1726 goto fractional_part_oct;
1728 else if(c ==
'p' || c ==
'P')
1731 goto power_part_oct;
1747 fractional_part_oct:
1749 C4_ASSERT(
str[pos - 1] ==
'.');
1750 for( ; pos <
len; ++pos)
1752 const char c =
str[pos];
1753 if(c >=
'0' && c <=
'7')
1757 else if(c ==
'p' || c ==
'P')
1760 goto power_part_oct;
1764 return intchars || fracchars ?
first(pos) :
first(0);
1771 return intchars || fracchars ?
1776 C4_ASSERT(
str[pos - 1] ==
'p' ||
str[pos - 1] ==
'P');
1780 if(
len <= (pos+1) || (
str[pos] !=
'+' &&
str[pos] !=
'-') || ((!intchars) && (!fracchars)))
1786 for( ; pos <
len; ++pos)
1788 const char c =
str[pos];
1789 if(c >=
'0' && c <=
'9')
1812 if(C4_LIKELY(*start_pos <
len))
1814 for(
size_t i = *start_pos; i <
len; i++)
1818 out->assign(
str + *start_pos, i - *start_pos);
1823 out->assign(
str + *start_pos,
len - *start_pos);
1824 *start_pos =
len + 1;
1829 bool valid =
len > 0 && (*start_pos ==
len);
1832 out->assign(
str +
len,
size_t(0));
1836 out->assign(
str +
len + 1,
size_t(0));
1838 *start_pos =
len + 1;
1845 struct split_proxy_impl
1877 C4_XASSERT((
m_sep == that.
m_sep) &&
"cannot compare split iterators with different separators");
1891 : m_str(str_), m_start_pos(start_pos), m_sep(sep)
1895 split_iterator_impl begin()
const
1897 auto it = split_iterator_impl(
this, m_start_pos, m_sep);
1900 split_iterator_impl end()
const
1902 size_t pos = m_str.
size() + 1;
1903 auto it = split_iterator_impl(
this, pos, m_sep);
1915 C4_XASSERT((start_pos >= 0 && start_pos <
len) ||
empty());
1928 if(C4_LIKELY(
len > 1))
1935 return sub(pos + 1);
1941 return sub(pos + 1, 0);
1949 auto pos0 =
last_of(sep, ppos);
1981 if(C4_LIKELY(
len > 1))
2007 C4_XASSERT(pos0 > 0);
2009 return sub(0, pos0);
2038 if(ss.find(sep) !=
npos)
2040 if(ss.ends_with(sep))
2048 ss = ss.sub(0, ss.len-1);
2058 auto ss =
pop_left(sep, skip_empty);
2060 if(ss.find(sep) !=
npos)
2062 if(ss.begins_with(sep))
2094 ss = ss.empty() ? *this :
left_of(ss);
2127 template<
typename U=C>
2129 ->
typename std::enable_if< ! std::is_const<U>::value,
void>
::type
2131 for(
size_t i = 0; i <
len; ++i)
2139 template<
typename U=C>
2141 ->
typename std::enable_if< !std::is_const<U>::value,
void>
::type
2143 for(
size_t i = 0; i <
len; ++i)
2153 template<
typename U=C>
2155 ->
typename std::enable_if< !std::is_const<U>::value,
void>
::type
2157 for(
size_t i = 0; i <
len; ++i)
2165 template<
typename U=C>
2167 ->
typename std::enable_if< !std::is_const<U>::value,
void>
::type
2170 size_t num = that.len <=
len ? that.len :
len;
2175 memcpy(
str, that.str,
sizeof(C) * num);
2180 template<
typename U=C>
2182 ->
typename std::enable_if< !std::is_const<U>::value,
void>
::type
2184 C4_ASSERT(ifirst >= 0 && ifirst <=
len);
2185 num = num !=
npos ? num :
len - ifirst;
2186 num = num < that.len ? num : that.len;
2187 C4_ASSERT(ifirst + num >= 0 && ifirst + num <=
len);
2192 memcpy(
str + (
sizeof(C) * ifirst), that.str,
sizeof(C) * num);
2199 template<
typename U=C>
2201 ->
typename std::enable_if< !std::is_const<U>::value,
void>
::type
2203 if(
len == 0)
return;
2204 detail::_do_reverse(
str,
str +
len - 1);
2209 template<
typename U=C>
2211 ->
typename std::enable_if< !std::is_const<U>::value,
void>
::type
2213 C4_ASSERT(ifirst >= 0 && ifirst <=
len);
2214 C4_ASSERT(ifirst + num >= 0 && ifirst + num <=
len);
2215 if(num == 0)
return;
2216 detail::_do_reverse(
str + ifirst,
str + ifirst + num - 1);
2221 template<
typename U=C>
2223 ->
typename std::enable_if< !std::is_const<U>::value,
void>
::type
2225 C4_ASSERT(ifirst >= 0 && ifirst <=
len);
2226 C4_ASSERT(ilast >= 0 && ilast <=
len);
2227 if(ifirst == ilast)
return;
2228 detail::_do_reverse(
str + ifirst,
str + ilast - 1);
2236 template<
typename U=C>
2240 C4_ASSERT(pos >= 0 && pos+num <=
len);
2241 size_t num_to_move =
len - pos - num;
2242 memmove(
str + pos,
str + pos + num,
sizeof(C) * num_to_move);
2247 template<
typename U=C>
2258 template<
typename U=C>
2263 C4_ASSERT(
sub.str >=
str);
2272 template<
typename U=C>
2274 ->
typename std::enable_if< ! std::is_const<U>::value,
size_t>
::type
2276 C4_ASSERT((pos >= 0 && pos <=
len) || pos ==
npos);
2278 while((pos =
find(value, pos)) !=
npos)
2290 template<
typename U=C>
2292 ->
typename std::enable_if< ! std::is_const<U>::value,
size_t>
::type
2294 C4_ASSERT((pos >= 0 && pos <=
len) || pos ==
npos);
2312 C4_ASSERT( ! pattern.
empty());
2313 C4_ASSERT( !
this ->
overlaps(dst));
2314 C4_ASSERT( ! pattern.
overlaps(dst));
2316 C4_ASSERT((pos >= 0 && pos <=
len) || pos ==
npos);
2317 C4_SUPPRESS_WARNING_GCC_PUSH
2318 C4_SUPPRESS_WARNING_GCC(
"-Warray-bounds")
2319 #if (!defined(__clang__)) && (defined(__GNUC__) && (__GNUC__ >= 7))
2320 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
2322 #define _c4append(first, last) \
2324 C4_ASSERT((last) >= (first)); \
2325 size_t num = static_cast<size_t>((last) - (first)); \
2326 if(num > 0 && sz + num <= dst.len) \
2328 memcpy(dst.str + sz, first, num * sizeof(C)); \
2336 size_t e =
find(pattern, b);
2344 b = e + pattern.
size();
2345 }
while(b <
len && b !=
npos);
2348 C4_SUPPRESS_WARNING_GCC_POP
2399template<
class U> C4_ALWAYS_INLINE
auto to_substr(U s)
noexcept
2400 ->
typename std::enable_if<is_compatible_char_ptr<U, char>::value,
substr>::type
2413 ->
typename std::enable_if<is_compatible_char_ptr<U, const char>::value,
csubstr>::type
2439template<
typename C>
inline bool operator== (
const char c,
basic_substring<C> const that)
noexcept {
return that.compare(c) == 0; }
2440template<
typename C>
inline bool operator!= (
const char c,
basic_substring<C> const that)
noexcept {
return that.compare(c) != 0; }
2441template<
typename C>
inline bool operator< (
const char c,
basic_substring<C> const that)
noexcept {
return that.compare(c) > 0; }
2442template<
typename C>
inline bool operator> (
const char c,
basic_substring<C> const that)
noexcept {
return that.compare(c) < 0; }
2443template<
typename C>
inline bool operator<= (
const char c,
basic_substring<C> const that)
noexcept {
return that.compare(c) >= 0; }
2444template<
typename C>
inline bool operator>= (
const char c,
basic_substring<C> const that)
noexcept {
return that.compare(c) <= 0; }
2449template<
typename C,
size_t N>
inline bool operator== (
const char (&arr)[N],
basic_substring<C> const that)
noexcept {
return that.compare(arr, N-1) == 0; }
2450template<
typename C,
size_t N>
inline bool operator!= (
const char (&arr)[N],
basic_substring<C> const that)
noexcept {
return that.compare(arr, N-1) != 0; }
2451template<
typename C,
size_t N>
inline bool operator< (
const char (&arr)[N],
basic_substring<C> const that)
noexcept {
return that.compare(arr, N-1) > 0; }
2452template<
typename C,
size_t N>
inline bool operator> (
const char (&arr)[N],
basic_substring<C> const that)
noexcept {
return that.compare(arr, N-1) < 0; }
2453template<
typename C,
size_t N>
inline bool operator<= (
const char (&arr)[N],
basic_substring<C> const that)
noexcept {
return that.compare(arr, N-1) >= 0; }
2454template<
typename C,
size_t N>
inline bool operator>= (
const char (&arr)[N],
basic_substring<C> const that)
noexcept {
return that.compare(arr, N-1) <= 0; }
2459template<
typename U,
typename C>
inline auto operator== (U c_str,
basic_substring<C> const that)
noexcept ->
typename std::enable_if<is_compatible_char_ptr<U, C>::value,
bool>::type {
return that.compare(c_str, strlen(c_str)) == 0; }
2460template<
typename U,
typename C>
inline auto operator!= (U c_str,
basic_substring<C> const that)
noexcept ->
typename std::enable_if<is_compatible_char_ptr<U, C>::value,
bool>::type {
return that.compare(c_str, strlen(c_str)) != 0; }
2461template<
typename U,
typename C>
inline auto operator< (U c_str,
basic_substring<C> const that)
noexcept ->
typename std::enable_if<is_compatible_char_ptr<U, C>::value,
bool>::type {
return that.compare(c_str, strlen(c_str)) > 0; }
2462template<
typename U,
typename C>
inline auto operator> (U c_str,
basic_substring<C> const that)
noexcept ->
typename std::enable_if<is_compatible_char_ptr<U, C>::value,
bool>::type {
return that.compare(c_str, strlen(c_str)) < 0; }
2463template<
typename U,
typename C>
inline auto operator<= (U c_str,
basic_substring<C> const that)
noexcept ->
typename std::enable_if<is_compatible_char_ptr<U, C>::value,
bool>::type {
return that.compare(c_str, strlen(c_str)) >= 0; }
2464template<
typename U,
typename C>
inline auto operator>= (U c_str,
basic_substring<C> const that)
noexcept ->
typename std::enable_if<is_compatible_char_ptr<U, C>::value,
bool>::type {
return that.compare(c_str, strlen(c_str)) <= 0; }
2477#ifndef C4_SUBSTR_NO_OSTREAM_LSHIFT
2480template<
class OStream,
class C>
2483 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(
"-Wsign-conversion")
2485 C4_SUPPRESS_WARNING_GCC_CLANG_POP
2496C4_SUPPRESS_WARNING_GCC_CLANG_POP
substr to_substr(char(&s)[N]) noexcept
csubstr to_csubstr(const char(&s)[N]) noexcept
basic_substring< char > substr
a mutable string view
basic_substring< const char > csubstr
an immutable string view
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
split_proxy_impl const * m_proxy
bool operator==(split_iterator_impl const &that) const
split_iterator_impl(split_proxy_impl const *proxy, size_t pos, C sep)
a non-owning string-view, consisting of a character pointer and a length.
basic_substring _first_real_span_hex(size_t pos) const noexcept
basic_substring first_uint_span() const
first_of_any_result first_of_any_iter(It first_span, It last_span) const
auto compare(CharPtr c_str) const noexcept -> typename std::enable_if< is_compatible_char_ptr< CharPtr, C >::value, int >::type
bool begins_with_any(ro_substr chars) const noexcept
true if the first character of the string is any of the given chars
size_t first_not_of(ro_substr chars) const
basic_substring gpop_right(C sep=C('/'), bool skip_empty=false) const
greedy pop right.
basic_substring trim(const C c) const
trim the character c left and right
size_t count(const C c, size_t pos=0) const
count the number of occurrences of c
basic_substring _first_real_span_oct(size_t pos) const noexcept
first_of_any_result first_of_any(ro_substr s0, ro_substr s1) const
basic_substring sub(size_t first, size_t num) const noexcept
return [first,first+num[.
basic_substring pair_range(CC open, CC close) const
get the range delimited by an open-close pair of characters.
basic_substring pop_right(C sep=C('/'), bool skip_empty=false) const
pop right: return the first split from the right.
auto reverse_sub(size_t ifirst, size_t num) -> typename std::enable_if< !std::is_const< U >::value, void >::type
revert a subpart in place
basic_substring range(size_t first, size_t last=npos) const noexcept
return [first,last[.
int compare(C const c) const noexcept
basic_substring< NCC_ > rw_substr
size_t first_not_of(const CC c) const
const_iterator begin() const noexcept
size_t last_not_of(const C c, size_t start) const
basic_substring left_of(ro_substr const subs) const noexcept
given subs a substring of the current string, get the portion of the current string to the left of it
auto tolower() -> typename std::enable_if< !std::is_const< U >::value, void >::type
convert the string to lower-case
bool begins_with(const CC c) const noexcept
basic_substring triml(const C c) const
trim left
size_t last_of(const C c, size_t start=npos) const
bool ends_with(const C c, const size_t num) const noexcept
true if the last num characters of the string are c
auto copy_from(ro_substr that) -> typename std::enable_if< !std::is_const< U >::value, void >::type
copy a string to this substr, starting at 0
basic_substring trimr(ro_substr chars) const
trim right ANY of the characters
basic_substring(basic_substring const &) noexcept=default
basic_substring _first_real_span_bin(size_t pos) const noexcept
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2, ro_substr s3, ro_substr s4) const
basic_substring offs(size_t left, size_t right) const noexcept
offset from the ends: return [left,len-right[ ; ie, trim a number of characters from the left and rig...
basic_substring first_real_span() const
auto erase(size_t pos, size_t num) -> typename std::enable_if< !std::is_const< U >::value, basic_substring >::type
erase part of the string.
basic_substring unquoted() const
split_proxy_impl split_proxy
basic_substring first_int_span() const
basic_substring select(ro_substr pattern, size_t pos=0) const
get the substr consisting of the first occurrence of pattern after pos, or an empty substr if none oc...
basic_substring pair_range_esc(CC open_close, CC escape=CC('\\'))
get the range delimited by a single open-close character (eg, quotes).
size_t replace_all(rw_substr dst, ro_substr pattern, ro_substr repl, size_t pos=0) const
replace pattern with repl, and write the result into dst.
size_t count(ro_substr c, size_t pos=0) const
count the number of occurrences of s
basic_substring name_wo_extshort() const
split_proxy split(C sep, size_t start_pos=0) const
a view into the splits
basic_substring name_wo_extlong() const
auto copy_from(ro_substr that, size_t ifirst, size_t num=npos) -> typename std::enable_if< !std::is_const< U >::value, void >::type
copy a string to this substr, starting at a specified given position
auto fill(C val) -> typename std::enable_if< !std::is_const< U >::value, void >::type
fill the entire contents with the given val
size_t last_not_of(ro_substr chars) const
C const * data() const noexcept
constexpr basic_substring() noexcept
basic_substring triml(ro_substr chars) const
trim left ANY of the characters.
basic_substring left_of(size_t pos, bool include_pos) const noexcept
return [0, pos+include_pos[ .
void assign(CharPtr s_) noexcept
Assign from a C-string (zero-terminated string of type const C* or C*).
size_t last_not_of(ro_substr chars, size_t start) const
basic_substring last(size_t num) const noexcept
bool ends_with(const CC c) const noexcept
auto erase_range(size_t first, size_t last) -> typename std::enable_if< !std::is_const< U >::value, basic_substring >::type
size_t first_of(const CC c, size_t start=0) const
basic_substring stripl(ro_substr pattern) const
remove a pattern from the left
basic_substring(CharPtr s_) noexcept
Construct from a C-string (zero-terminated string).
basic_substring right_of(size_t pos, bool include_pos) const noexcept
return [pos+!include_pos, len[
size_t find(const C c, size_t start_pos=0) const
basic_substring trim(ro_substr const chars) const
trim left and right ANY of the characters
auto replace(C value, C repl, size_t pos=0) -> typename std::enable_if< ! std::is_const< U >::value, size_t >::type
replace every occurrence of character value with the character repl
const_iterator end() const noexcept
typename std::add_const< C >::type CC
CC=const char.
basic_substring(C *s_, size_t len_) noexcept
Construct from a pointer and length.
basic_substring extshort() const
basic_substring< CC > ro_substr
basic_substring stripr(ro_substr pattern) const
remove a pattern from the right
size_t first_of(ro_substr chars, size_t start=0) const
operator typename std::enable_if<!std::is_const< U >::value, ro_substr const & >::type() const noexcept
size_t find(ro_substr pattern, size_t start_pos=0) const
void assign(C *s_, size_t len_) noexcept
Assign from a pointer and length.
iterator begin() noexcept
void assign(C(&s_)[N]) noexcept
Assign from an array.
int compare(C const *that, size_t sz) const noexcept
size_t size() const noexcept
basic_substring basename(C sep=C('/')) const
basic_substring(C *beg_, C *end_) noexcept
Construct from two pointers.
bool is_unsigned_integer() const
bool overlaps(ro_substr const that) const noexcept
true if there is overlap of at least one element between that and *this
basic_substring _first_integral_span(size_t skip_start) const
auto replace(ro_substr chars, C repl, size_t pos=0) -> typename std::enable_if< ! std::is_const< U >::value, size_t >::type
replace every occurrence of each character in value with the character repl.
bool not_empty() const noexcept
basic_substring first(size_t num) const noexcept
basic_substring left_of(size_t pos) const noexcept
return [0, pos[ .
bool begins_with(const C c, size_t num) const noexcept
true if the first num characters of the string are c
C const & back() const noexcept
C const & front() const noexcept
basic_substring pair_range_nested(CC open, CC close) const
get the range delimited by an open-close pair of characters, with possibly nested occurrences.
bool has_str() const noexcept
basic_substring sub(size_t first) const noexcept
return [first,len[
basic_substring _first_real_span_dec(size_t pos) const noexcept
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2) const
bool ends_with(ro_substr pattern) const noexcept
true if the string ends with the given pattern
bool next_split(C sep, size_t *start_pos, basic_substring *out) const
returns true if the string has not been exhausted yet, meaning it's ok to call next_split() again.
basic_substring(basic_substring &&) noexcept=default
bool ends_with_any(ro_substr chars) const noexcept
true if the last character of the string is any of the given chars
bool empty() const noexcept
basic_substring right_of(ro_substr const subs) const noexcept
given subs a substring of the current string, get the portion of the current string to the right of i...
size_t first_not_of(const C c, size_t start) const
void assign(C *beg_, C *end_) noexcept
Assign from two pointers.
basic_substring trimr(const C c) const
trim the character c from the right
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2, ro_substr s3) const
constexpr basic_substring(C(&s_)[N]) noexcept
Construct from an array.
basic_substring dirname(C sep=C('/')) const
auto reverse_range(size_t ifirst, size_t ilast) -> typename std::enable_if< !std::is_const< U >::value, void >::type
revert a range in place
bool begins_with(ro_substr pattern) const noexcept
true if the string begins with the given pattern
bool is_super(ro_substr const that) const noexcept
true if that is a substring of *this (ie, from the same buffer)
size_t last_not_of(const CC c) const
static constexpr C4_CONST bool _is_hex_char(char c) noexcept
basic_substring _word_follows(size_t pos, csubstr word) const noexcept
basic_substring gpop_left(C sep=C('/'), bool skip_empty=false) const
greedy pop left.
size_t first_not_of(ro_substr chars, size_t start) const
size_t last_of(ro_substr chars, size_t start=npos) const
typename std::remove_const< C >::type NCC_
NCC_=non const char.
auto toupper() -> typename std::enable_if< ! std::is_const< U >::value, void >::type
convert the string to upper-case
basic_substring first_non_empty_span() const
basic_substring right_of(size_t pos) const noexcept
return [pos+1, len[
basic_substring pop_left(C sep=C('/'), bool skip_empty=false) const
return the first split from the left.
static constexpr C4_CONST bool _is_delim_char(char c) noexcept
bool is_sub(ro_substr const that) const noexcept
true if *this is a substring of that (ie, from the same buffer)
int compare(basic_substring< U > const that) const noexcept
basic_substring select(const C c, size_t pos=0) const
get the substr consisting of the first occurrence of c after pos, or an empty substr if none occurs
basic_substring extlong() const
auto reverse() -> typename std::enable_if< !std::is_const< U >::value, void >::type
reverse in place
auto erase(ro_substr sub) -> typename std::enable_if< !std::is_const< U >::value, basic_substring >::type
erase a part of the string.
a traits class to mark a type as a string type, meaning c4::to_csubstr() can be used directly instead...
a traits class to mark a type as a writeable string type, meaning c4::to_substr() can be used directl...
#define _c4append(first, last)