1 #ifndef _C4_SUBSTR_HPP_
2 #define _C4_SUBSTR_HPP_
10 #include "c4/export.hpp"
11 #include "c4/language.hpp"
12 #include "c4/error.hpp"
13 #include "c4/substr_fwd.hpp"
15 C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
16 C4_SUPPRESS_WARNING_GCC_CLANG(
"-Wold-style-cast")
17 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
18 C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
32 static inline void _do_reverse(C *C4_RESTRICT first, C *C4_RESTRICT last)
52 #define C4_REQUIRE_RW(ret_type) \
53 template <typename U=C> \
54 typename std::enable_if< ! std::is_const<U>::value, ret_type>::type
82 using CC =
typename std::add_const<C>::type;
83 using NCC_ =
typename std::remove_const<C>::type;
94 enum :
size_t {
npos = (size_t)-1,
NONE = (
size_t)-1 };
98 C4_ALWAYS_INLINE
operator typename std::enable_if<!std::is_const<U>::value,
ro_substr const&>::type () const noexcept
118 C4_ALWAYS_INLINE
basic_substring& operator= (std::nullptr_t) noexcept { str =
nullptr; len = 0;
return *
this; }
120 C4_ALWAYS_INLINE
void clear() noexcept { str =
nullptr; len = 0; }
133 C4_ALWAYS_INLINE constexpr
basic_substring(C (&s_)[N]) noexcept : str(s_), len(N-1) {}
136 C4_ALWAYS_INLINE
basic_substring(C *s_,
size_t len_) noexcept : str(s_), len(len_) { C4_ASSERT(str || !len_); }
140 C4_ALWAYS_INLINE
basic_substring(C *beg_, C *end_) noexcept : str(beg_), len(
static_cast<size_t>(end_ - beg_)) { C4_ASSERT(end_ >= beg_); }
147 template<class U, typename std::enable_if<std::is_same<U, C*>::value || std::is_same<U, NCC_*>::value,
int>::type=0>
148 C4_ALWAYS_INLINE
basic_substring(U s_) noexcept : str(s_), len(s_ ? strlen(s_) : 0) {}
154 C4_ALWAYS_INLINE
void assign(C (&s_)[N]) noexcept { str = (s_); len = (N-1); }
157 C4_ALWAYS_INLINE
void assign(C *s_,
size_t len_) noexcept { str = s_; len = len_; C4_ASSERT(str || !len_); }
161 C4_ALWAYS_INLINE
void assign(C *beg_, C *end_) noexcept { C4_ASSERT(end_ >= beg_); str = (beg_); len =
static_cast<size_t>(end_ - beg_); }
168 template<class U, typename std::enable_if<std::is_same<U, C*>::value || std::is_same<U, NCC_*>::value,
int>::type=0>
169 C4_ALWAYS_INLINE
void assign(U s_) noexcept { str = (s_); len = (s_ ? strlen(s_) : 0); }
174 C4_ALWAYS_INLINE
basic_substring& operator= (C (&s_)[N]) noexcept { str = (s_); len = (N-1);
return *
this; }
181 template<class U, typename std::enable_if<std::is_same<U, C*>::value || std::is_same<U, NCC_*>::value,
int>::type=0>
182 C4_ALWAYS_INLINE
basic_substring& operator= (U s_) noexcept { str = s_; len = s_ ? strlen(s_) : 0;
return *
this; }
191 C4_ALWAYS_INLINE C4_PURE
bool has_str() const noexcept {
return ! empty() && str[0] != C(0); }
192 C4_ALWAYS_INLINE C4_PURE
bool empty() const noexcept {
return (len == 0 || str ==
nullptr); }
193 C4_ALWAYS_INLINE C4_PURE
bool not_empty() const noexcept {
return (len != 0 && str !=
nullptr); }
194 C4_ALWAYS_INLINE C4_PURE
size_t size() const noexcept {
return len; }
197 C4_ALWAYS_INLINE C4_PURE
iterator end () noexcept {
return str + len; }
202 C4_ALWAYS_INLINE C4_PURE C *
data() noexcept {
return str; }
203 C4_ALWAYS_INLINE C4_PURE C
const*
data() const noexcept {
return str; }
205 C4_ALWAYS_INLINE C4_PURE C & operator[] (
size_t i) noexcept { C4_ASSERT(i >= 0 && i < len);
return str[i]; }
206 C4_ALWAYS_INLINE C4_PURE C
const& operator[] (
size_t i)
const noexcept { C4_ASSERT(i >= 0 && i < len);
return str[i]; }
208 C4_ALWAYS_INLINE C4_PURE C &
front() noexcept { C4_ASSERT(len > 0 && str !=
nullptr);
return *str; }
209 C4_ALWAYS_INLINE C4_PURE C
const&
front() const noexcept { C4_ASSERT(len > 0 && str !=
nullptr);
return *str; }
211 C4_ALWAYS_INLINE C4_PURE C &
back() noexcept { C4_ASSERT(len > 0 && str !=
nullptr);
return *(str + len - 1); }
212 C4_ALWAYS_INLINE C4_PURE C
const&
back() const noexcept { C4_ASSERT(len > 0 && str !=
nullptr);
return *(str + len - 1); }
221 C4_ALWAYS_INLINE C4_PURE
int compare(C
const c)
const noexcept
223 C4_XASSERT((str !=
nullptr) || len == 0);
224 if(C4_LIKELY(str !=
nullptr && len > 0))
225 return (*str != c) ? *str - c : (
static_cast<int>(len) - 1);
230 C4_PURE
int compare(C
const* C4_RESTRICT that,
size_t sz)
const noexcept
232 #if defined(__GNUC__) && (__GNUC__ >= 6)
233 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wnull-dereference")
235 C4_XASSERT(that || sz == 0);
236 C4_XASSERT(str || len == 0);
237 if(C4_LIKELY(str && that))
240 const size_t min = len < sz ? len : sz;
241 for(
size_t i = 0; i < min; ++i)
242 if(str[i] != that[i])
243 return str[i] < that[i] ? -1 : 1;
254 C4_XASSERT(len == 0 && sz == 0);
257 return len < sz ? -1 : 1;
258 #if defined(__GNUC__) && (__GNUC__ >= 6)
259 C4_SUPPRESS_WARNING_GCC_POP
265 C4_ALWAYS_INLINE C4_PURE
bool operator== (std::nullptr_t)
const noexcept {
return str ==
nullptr; }
266 C4_ALWAYS_INLINE C4_PURE
bool operator!= (std::nullptr_t)
const noexcept {
return str !=
nullptr; }
268 C4_ALWAYS_INLINE C4_PURE
bool operator== (C
const c)
const noexcept {
return this->compare(c) == 0; }
269 C4_ALWAYS_INLINE C4_PURE
bool operator!= (C
const c)
const noexcept {
return this->compare(c) != 0; }
270 C4_ALWAYS_INLINE C4_PURE
bool operator< (C
const c)
const noexcept {
return this->compare(c) < 0; }
271 C4_ALWAYS_INLINE C4_PURE
bool operator> (C
const c)
const noexcept {
return this->compare(c) > 0; }
272 C4_ALWAYS_INLINE C4_PURE
bool operator<= (C
const c)
const noexcept {
return this->compare(c) <= 0; }
273 C4_ALWAYS_INLINE C4_PURE
bool operator>= (C
const c)
const noexcept {
return this->compare(c) >= 0; }
282 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator== (
const char (&that)[N])
const noexcept {
return this->compare(that, N-1) == 0; }
283 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator!= (
const char (&that)[N])
const noexcept {
return this->compare(that, N-1) != 0; }
284 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator< (
const char (&that)[N])
const noexcept {
return this->compare(that, N-1) < 0; }
285 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator> (
const char (&that)[N])
const noexcept {
return this->compare(that, N-1) > 0; }
286 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator<= (
const char (&that)[N])
const noexcept {
return this->compare(that, N-1) <= 0; }
287 template<
size_t N> C4_ALWAYS_INLINE C4_PURE
bool operator>= (
const char (&that)[N])
const noexcept {
return this->compare(that, N-1) >= 0; }
299 return that.is_super(*
this);
305 if(C4_LIKELY(len > 0))
306 return that.str >= str && that.str+that.len <= str+len;
308 return that.len == 0 && that.str == str && str !=
nullptr;
315 return that.str+that.len > str && that.str < str+len;
323 C4_ASSERT(first >= 0 && first <= len);
330 C4_ASSERT(first >= 0 && first <= len);
331 C4_ASSERT((num >= 0 && num <= len) || (num ==
npos));
332 size_t rnum = num !=
npos ? num : len - first;
333 C4_ASSERT((first >= 0 && first + rnum <= len) || (num == 0));
340 C4_ASSERT(first >= 0 && first <= len);
341 last = last !=
npos ? last : len;
342 C4_ASSERT(first <= last);
343 C4_ASSERT(last >= 0 && last <= len);
350 C4_ASSERT(num <= len || num ==
npos);
357 C4_ASSERT(num <= len || num ==
npos);
368 C4_ASSERT(
left >= 0 &&
left <= len);
377 C4_ASSERT(pos <= len || pos ==
npos);
378 return (pos !=
npos) ?
386 C4_ASSERT(pos <= len || pos ==
npos);
387 return (pos !=
npos) ?
395 C4_ASSERT(pos <= len || pos ==
npos);
396 return (pos !=
npos) ?
404 C4_ASSERT(pos <= len || pos ==
npos);
405 return (pos !=
npos) ?
406 basic_substring(str + (pos + !include_pos), len - (pos + !include_pos)) :
416 C4_ASSERT(is_super(subs) || subs.empty());
417 auto ssb = subs.begin();
420 if(ssb >= b && ssb <= e)
421 return sub(0,
static_cast<size_t>(ssb - b));
430 C4_ASSERT(is_super(subs) || subs.empty());
431 auto sse = subs.end();
434 if(sse >= b && sse <= e)
435 return sub(
static_cast<size_t>(sse - b),
static_cast<size_t>(e - sse));
452 size_t pos = first_not_of(c);
464 size_t pos = first_not_of(chars);
476 size_t pos = last_not_of(c,
npos);
478 return sub(0, pos+1);
488 size_t pos = last_not_of(chars,
npos);
490 return sub(0, pos+1);
498 return triml(c).
trimr(c);
504 return triml(chars).
trimr(chars);
511 if( ! begins_with(pattern))
513 return sub(pattern.
len < len ? pattern.
len : len);
520 if( ! ends_with(pattern))
522 return left_of(len - (pattern.
len < len ? pattern.
len : len));
532 size_t find(
const C c,
size_t start_pos=0)
const
534 return first_of(c, start_pos);
538 C4_ASSERT(start_pos ==
npos || (start_pos >= 0 && start_pos <= len));
539 if(len < pattern.
len)
return npos;
540 for(
size_t i = start_pos, e = len - pattern.
len + 1; i < e; ++i)
543 for(
size_t j = 0; j < pattern.
len; ++j)
545 C4_ASSERT(i + j < len);
546 if(str[i + j] != pattern.
str[j])
563 size_t count(
const C c,
size_t pos=0)
const
565 C4_ASSERT(pos >= 0 && pos <= len);
571 pos = find(c, pos + 1);
579 C4_ASSERT(pos >= 0 && pos <= len);
585 pos = find(c, pos + c.
len);
600 pos = find(pattern, pos);
610 operator bool()
const {
return which !=
NONE && pos !=
npos; }
616 return first_of_any_iter(&s[0], &s[0] + 2);
622 return first_of_any_iter(&s[0], &s[0] + 3);
628 return first_of_any_iter(&s[0], &s[0] + 4);
634 return first_of_any_iter(&s[0], &s[0] + 5);
640 for(
size_t i = 0; i < len; ++i)
643 for(It it = first_span; it != last_span; ++curr, ++it)
645 auto const& chars = *it;
646 if((i + chars.len) > len)
continue;
648 for(
size_t j = 0; j < chars.len; ++j)
650 C4_ASSERT(i + j < len);
651 if(str[i + j] != chars[j])
671 #if defined(__GNUC__) && (__GNUC__ >= 6)
672 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wnull-dereference")
674 return len > 0 ? str[0] == c :
false;
675 #if defined(__GNUC__) && (__GNUC__ >= 6)
676 C4_SUPPRESS_WARNING_GCC_POP
687 for(
size_t i = 0; i < num; ++i)
700 if(len < pattern.
len)
704 for(
size_t i = 0; i < pattern.
len; ++i)
706 if(str[i] != pattern[i])
721 for(
size_t i = 0; i < chars.
len; ++i)
723 if(str[0] == chars.
str[i])
734 return len > 0 ? str[len-1] == c :
false;
744 for(
size_t i = len - num; i < len; ++i)
757 if(len < pattern.
len)
761 for(
size_t i = 0, s = len-pattern.
len; i < pattern.
len; ++i)
763 if(str[s+i] != pattern[i])
778 for(
size_t i = 0; i < chars.
len; ++i)
780 if(str[len - 1] == chars[i])
793 C4_ASSERT(start ==
npos || (start >= 0 && start <= len));
794 for(
size_t i = start; i < len; ++i)
805 C4_ASSERT(start ==
npos || (start >= 0 && start <= len));
808 for(
size_t i = start-1; i != size_t(-1); --i)
819 C4_ASSERT(start ==
npos || (start >= 0 && start <= len));
820 for(
size_t i = start; i < len; ++i)
822 for(
size_t j = 0; j < chars.
len; ++j)
824 if(str[i] == chars[j])
834 C4_ASSERT(start ==
npos || (start >= 0 && start <= len));
837 for(
size_t i = start-1; i != size_t(-1); --i)
839 for(
size_t j = 0; j < chars.
len; ++j)
841 if(str[i] == chars[j])
852 for(
size_t i = 0; i < len; ++i)
862 C4_ASSERT((start >= 0 && start <= len) || (start == len && len == 0));
863 for(
size_t i = start; i < len; ++i)
873 for(
size_t i = len-1; i != size_t(-1); --i)
883 C4_ASSERT(start ==
npos || (start >= 0 && start <= len));
886 for(
size_t i = start-1; i != size_t(-1); --i)
896 for(
size_t i = 0; i < len; ++i)
899 for(
size_t j = 0; j < chars.
len; ++j)
901 if(str[i] == chars.
str[j])
917 C4_ASSERT((start >= 0 && start <= len) || (start == len && len == 0));
918 for(
size_t i = start; i < len; ++i)
921 for(
size_t j = 0; j < chars.
len; ++j)
923 if(str[i] == chars.
str[j])
939 for(
size_t i = len-1; i != size_t(-1); --i)
942 for(
size_t j = 0; j < chars.
len; ++j)
944 if(str[i] == chars.
str[j])
960 C4_ASSERT(start ==
npos || (start >= 0 && start <= len));
963 for(
size_t i = start-1; i != size_t(-1); --i)
966 for(
size_t j = 0; j < chars.
len; ++j)
968 if(str[i] == chars.
str[j])
994 size_t b = find(open);
997 size_t e = find(close, b+1);
1009 size_t b = find(open_close);
1011 for(
size_t i = b+1; i < len; ++i)
1016 if(str[i-1] != escape)
1018 return range(b, i+1);
1030 size_t b = find(open);
1032 size_t e, curr = b+1, count = 0;
1033 const char both[] = {open, close,
'\0'};
1034 while((e = first_of(both, curr)) !=
npos)
1041 else if(str[e] == close)
1043 if(count == 0)
return range(b, e+1);
1053 constexpr
const C dq(
'"'), sq(
'\'');
1054 if(len >= 2 && (str[len - 2] != C(
'\\')) &&
1055 ((begins_with(sq) && ends_with(sq))
1057 (begins_with(dq) && ends_with(dq))))
1059 return range(1, len -1);
1075 if(empty() || (first_non_empty_span().empty()))
1077 if(first_uint_span() == *
this)
1079 if(first_int_span() == *
this)
1081 if(first_real_span() == *
this)
1090 if(empty() || (first_non_empty_span().empty()))
1092 if(first_real_span() == *
this)
1101 if(empty() || (first_non_empty_span().empty()))
1103 if(first_uint_span() == *
this)
1105 if(first_int_span() == *
this)
1114 if(empty() || (first_non_empty_span().empty()))
1116 if(first_uint_span() == *
this)
1124 constexpr
const ro_substr empty_chars(
" \n\r\t");
1125 size_t pos = first_not_of(empty_chars);
1128 auto ret = sub(pos);
1129 pos = ret.first_of(empty_chars);
1130 return ret.first(pos);
1139 if(ne.
str[0] ==
'-')
1141 size_t skip_start = size_t(ne.
str[0] ==
'+');
1151 size_t skip_start = size_t(ne.
str[0] ==
'+' || ne.
str[0] ==
'-');
1157 C4_ASSERT(!empty());
1158 if(skip_start == len)
1160 C4_ASSERT(skip_start < len);
1161 if(len >= skip_start + 3)
1163 if(str[skip_start] !=
'0')
1165 for(
size_t i = skip_start; i < len; ++i)
1168 if(c < '0' || c >
'9')
1169 return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1174 char next = str[skip_start + 1];
1175 if(next ==
'x' || next ==
'X')
1178 for(
size_t i = skip_start; i < len; ++i)
1180 const char c = str[i];
1181 if( ! _is_hex_char(c))
1182 return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1186 else if(next ==
'b' || next ==
'B')
1189 for(
size_t i = skip_start; i < len; ++i)
1191 const char c = str[i];
1192 if(c !=
'0' && c !=
'1')
1193 return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1197 else if(next ==
'o' || next ==
'O')
1200 for(
size_t i = skip_start; i < len; ++i)
1202 const char c = str[i];
1203 if(c < '0' || c >
'7')
1204 return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1211 for(
size_t i = skip_start; i < len; ++i)
1213 const char c = str[i];
1214 if(c < '0' || c >
'9')
1215 return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1226 const size_t skip_start = (ne.
str[0] ==
'+' || ne.
str[0] ==
'-');
1227 C4_ASSERT(skip_start == 0 || skip_start == 1);
1232 if(ne.
len >= skip_start+3)
1235 if(ne.
str[skip_start] !=
'0')
1237 if(ne.
str[skip_start] ==
'i')
1244 else if(ne.
str[skip_start] ==
'n')
1255 const char next = ne.
str[skip_start + 1];
1257 if(next ==
'x' || next ==
'X')
1260 else if(next ==
'b' || next ==
'B')
1263 else if(next ==
'o' || next ==
'O')
1278 return c ==
' ' || c ==
'\n'
1279 || c ==
']' || c ==
')' || c ==
'}'
1280 || c ==
',' || c ==
';' || c ==
'\r' || c ==
'\t' || c ==
'\0';
1284 static constexpr C4_ALWAYS_INLINE C4_CONST
bool _is_hex_char(
char c) noexcept
1286 return (c >=
'0' && c <=
'9') || (c >=
'a' && c <=
'f') || (c >=
'A' && c <=
'F');
1291 size_t posend = pos + word.
len;
1292 if(len >= posend && sub(pos, word.len) == word)
1293 if(len == posend || _is_delim_char(str[posend]))
1294 return first(posend);
1301 bool intchars =
false;
1302 bool fracchars =
false;
1305 for( ; pos < len; ++pos)
1307 const char c = str[pos];
1308 if(c >=
'0' && c <=
'9')
1315 goto fractional_part_dec;
1317 else if(c ==
'e' || c ==
'E')
1320 goto power_part_dec;
1322 else if(_is_delim_char(c))
1324 return intchars ? first(pos) : first(0);
1336 fractional_part_dec:
1338 C4_ASSERT(str[pos - 1] ==
'.');
1339 for( ; pos < len; ++pos)
1341 const char c = str[pos];
1342 if(c >=
'0' && c <=
'9')
1346 else if(c ==
'e' || c ==
'E')
1349 goto power_part_dec;
1351 else if(_is_delim_char(c))
1353 return intchars || fracchars ? first(pos) : first(0);
1360 return intchars || fracchars ?
1365 C4_ASSERT(str[pos - 1] ==
'e' || str[pos - 1] ==
'E');
1367 if((len == pos) || ((!intchars) && (!fracchars)))
1369 if(str[pos] ==
'-' || str[pos] ==
'+')
1372 for( ; pos < len; ++pos)
1374 const char c = str[pos];
1375 if(c >=
'0' && c <=
'9')
1377 else if(powchars && _is_delim_char(c))
1382 return powchars ? *this : first(0);
1388 bool intchars =
false;
1389 bool fracchars =
false;
1392 for( ; pos < len; ++pos)
1394 const char c = str[pos];
1402 goto fractional_part_hex;
1404 else if(c ==
'p' || c ==
'P')
1407 goto power_part_hex;
1409 else if(_is_delim_char(c))
1411 return intchars ? first(pos) : first(0);
1423 fractional_part_hex:
1425 C4_ASSERT(str[pos - 1] ==
'.');
1426 for( ; pos < len; ++pos)
1428 const char c = str[pos];
1433 else if(c ==
'p' || c ==
'P')
1436 goto power_part_hex;
1438 else if(_is_delim_char(c))
1440 return intchars || fracchars ? first(pos) : first(0);
1447 return intchars || fracchars ?
1452 C4_ASSERT(str[pos - 1] ==
'p' || str[pos - 1] ==
'P');
1456 if(len <= (pos+1) || (str[pos] !=
'+' && str[pos] !=
'-') || ((!intchars) && (!fracchars)))
1462 for( ; pos < len; ++pos)
1464 const char c = str[pos];
1465 if(c >=
'0' && c <=
'9')
1467 else if(powchars && _is_delim_char(c))
1478 bool intchars =
false;
1479 bool fracchars =
false;
1482 for( ; pos < len; ++pos)
1484 const char c = str[pos];
1485 if(c ==
'0' || c ==
'1')
1492 goto fractional_part_bin;
1494 else if(c ==
'p' || c ==
'P')
1497 goto power_part_bin;
1499 else if(_is_delim_char(c))
1501 return intchars ? first(pos) : first(0);
1513 fractional_part_bin:
1515 C4_ASSERT(str[pos - 1] ==
'.');
1516 for( ; pos < len; ++pos)
1518 const char c = str[pos];
1519 if(c ==
'0' || c ==
'1')
1523 else if(c ==
'p' || c ==
'P')
1526 goto power_part_bin;
1528 else if(_is_delim_char(c))
1530 return intchars || fracchars ? first(pos) : first(0);
1537 return intchars || fracchars ?
1542 C4_ASSERT(str[pos - 1] ==
'p' || str[pos - 1] ==
'P');
1546 if(len <= (pos+1) || (str[pos] !=
'+' && str[pos] !=
'-') || ((!intchars) && (!fracchars)))
1552 for( ; pos < len; ++pos)
1554 const char c = str[pos];
1555 if(c >=
'0' && c <=
'9')
1557 else if(powchars && _is_delim_char(c))
1568 bool intchars =
false;
1569 bool fracchars =
false;
1572 for( ; pos < len; ++pos)
1574 const char c = str[pos];
1575 if(c >=
'0' && c <=
'7')
1582 goto fractional_part_oct;
1584 else if(c ==
'p' || c ==
'P')
1587 goto power_part_oct;
1589 else if(_is_delim_char(c))
1591 return intchars ? first(pos) : first(0);
1603 fractional_part_oct:
1605 C4_ASSERT(str[pos - 1] ==
'.');
1606 for( ; pos < len; ++pos)
1608 const char c = str[pos];
1609 if(c >=
'0' && c <=
'7')
1613 else if(c ==
'p' || c ==
'P')
1616 goto power_part_oct;
1618 else if(_is_delim_char(c))
1620 return intchars || fracchars ? first(pos) : first(0);
1627 return intchars || fracchars ?
1632 C4_ASSERT(str[pos - 1] ==
'p' || str[pos - 1] ==
'P');
1636 if(len <= (pos+1) || (str[pos] !=
'+' && str[pos] !=
'-') || ((!intchars) && (!fracchars)))
1642 for( ; pos < len; ++pos)
1644 const char c = str[pos];
1645 if(c >=
'0' && c <=
'9')
1647 else if(powchars && _is_delim_char(c))
1668 if(C4_LIKELY(*start_pos < len))
1670 for(
size_t i = *start_pos; i < len; i++)
1674 out->assign(str + *start_pos, i - *start_pos);
1679 out->assign(str + *start_pos, len - *start_pos);
1680 *start_pos = len + 1;
1685 bool valid = len > 0 && (*start_pos == len);
1686 if(valid && str && str[len-1] == sep)
1688 out->assign(str + len,
size_t(0));
1692 out->assign(str + len + 1,
size_t(0));
1694 *start_pos = len + 1;
1701 struct split_proxy_impl
1711 : m_proxy(proxy), m_pos(pos), m_sep(sep)
1718 m_proxy->m_str.next_split(m_sep, &m_pos, &m_str);
1733 C4_XASSERT((m_sep == that.
m_sep) &&
"cannot compare split iterators with different separators");
1738 return m_pos == that.
m_pos;
1747 : m_str(str_), m_start_pos(start_pos), m_sep(sep)
1751 split_iterator_impl begin()
const
1753 auto it = split_iterator_impl(
this, m_start_pos, m_sep);
1756 split_iterator_impl end()
const
1758 size_t pos = m_str.
size() + 1;
1759 auto it = split_iterator_impl(
this, pos, m_sep);
1771 C4_XASSERT((start_pos >= 0 && start_pos < len) || empty());
1772 auto ss = sub(0, len);
1784 if(C4_LIKELY(len > 1))
1786 auto pos = last_of(sep);
1791 return sub(pos + 1);
1797 return sub(pos + 1, 0);
1799 auto ppos = last_not_of(sep);
1805 auto pos0 = last_of(sep, ppos);
1821 if(begins_with(sep))
1837 if(C4_LIKELY(len > 1))
1839 auto pos = first_of(sep);
1852 auto ppos = first_not_of(sep);
1858 auto pos0 = first_of(sep, ppos);
1863 C4_XASSERT(pos0 > 0);
1865 return sub(0, pos0);
1875 if(begins_with(sep))
1892 auto ss = pop_right(sep, skip_empty);
1894 if(ss.find(sep) !=
npos)
1896 if(ss.ends_with(sep))
1904 ss = ss.sub(0, ss.len-1);
1914 auto ss = pop_left(sep, skip_empty);
1916 if(ss.find(sep) !=
npos)
1918 if(ss.begins_with(sep))
1942 auto ss = pop_right(sep,
true);
1949 auto ss = basename(sep);
1950 ss = ss.empty() ? *this : left_of(ss);
1956 return gpop_left(
'.');
1961 return pop_left(
'.');
1966 return pop_right(
'.');
1971 return gpop_right(
'.');
1985 for(
size_t i = 0; i < len; ++i)
1987 str[i] =
static_cast<C
>(::toupper(str[i]));
1995 for(
size_t i = 0; i < len; ++i)
1997 str[i] =
static_cast<C
>(::tolower(str[i]));
2007 for(
size_t i = 0; i < len; ++i)
2017 C4_ASSERT(!overlaps(that));
2018 size_t num = that.
len <= len ? that.
len : len;
2023 memcpy(str, that.
str,
sizeof(C) * num);
2030 C4_ASSERT(ifirst >= 0 && ifirst <= len);
2031 num = num !=
npos ? num : len - ifirst;
2032 num = num < that.
len ? num : that.
len;
2033 C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len);
2038 memcpy(str + (
sizeof(C) * ifirst), that.
str,
sizeof(C) * num);
2047 if(len == 0)
return;
2048 detail::_do_reverse(str, str + len - 1);
2055 C4_ASSERT(ifirst >= 0 && ifirst <= len);
2056 C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len);
2057 if(num == 0)
return;
2058 detail::_do_reverse(str + ifirst, str + ifirst + num - 1);
2065 C4_ASSERT(ifirst >= 0 && ifirst <= len);
2066 C4_ASSERT(ilast >= 0 && ilast <= len);
2067 if(ifirst == ilast)
return;
2068 detail::_do_reverse(str + ifirst, str + ilast - 1);
2078 C4_ASSERT(pos >= 0 && pos+num <= len);
2079 size_t num_to_move = len - pos - num;
2080 memmove(str + pos, str + pos + num,
sizeof(C) * num_to_move);
2087 C4_ASSERT(first <= last);
2088 return erase(first,
static_cast<size_t>(last-first));
2096 C4_ASSERT(is_super(sub));
2097 C4_ASSERT(sub.
str >= str);
2098 return erase(
static_cast<size_t>(sub.
str - str), sub.
len);
2106 C4_REQUIRE_RW(
size_t)
replace(C value, C repl,
size_t pos=0)
2108 C4_ASSERT((pos >= 0 && pos <= len) || pos ==
npos);
2110 while((pos = find(value, pos)) !=
npos)
2124 C4_ASSERT((pos >= 0 && pos <= len) || pos ==
npos);
2126 while((pos = first_of(chars, pos)) !=
npos)
2142 C4_ASSERT( ! pattern.
empty());
2143 C4_ASSERT( !
this ->overlaps(dst));
2144 C4_ASSERT( ! pattern.
overlaps(dst));
2145 C4_ASSERT( ! repl .overlaps(dst));
2146 C4_ASSERT((pos >= 0 && pos <= len) || pos ==
npos);
2147 C4_SUPPRESS_WARNING_GCC_PUSH
2148 C4_SUPPRESS_WARNING_GCC(
"-Warray-bounds")
2149 #if (!defined(__clang__)) && (defined(__GNUC__) && (__GNUC__ >= 7))
2150 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
2152 #define _c4append(first, last) \
2154 C4_ASSERT((last) >= (first)); \
2155 size_t num = static_cast<size_t>((last) - (first)); \
2156 if(num > 0 && sz + num <= dst.len) \
2158 memcpy(dst.str + sz, first, num * sizeof(C)); \
2166 size_t e = find(pattern, b);
2174 b = e + pattern.
size();
2175 }
while(b < len && b !=
npos);
2178 C4_SUPPRESS_WARNING_GCC_POP
2185 #undef C4_REQUIRE_RW
2202 C4_ALWAYS_INLINE substr
to_substr(substr s) noexcept {
return s; }
2204 C4_ALWAYS_INLINE csubstr
to_csubstr(substr s) noexcept {
return csubstr{s.str, s.len}; }
2206 C4_ALWAYS_INLINE csubstr
to_csubstr(csubstr s) noexcept {
return s; }
2209 template<
size_t N> C4_ALWAYS_INLINE substr
to_substr(
char (&s)[N]) noexcept
2211 return substr(s, N-1);
2213 template<
size_t N> C4_ALWAYS_INLINE csubstr
to_csubstr(
const char (&s)[N]) noexcept
2215 return csubstr(s, N-1);
2222 template<
class U> C4_ALWAYS_INLINE
auto to_substr(U s) noexcept
2223 ->
typename std::enable_if<std::is_same<U, char*>::value, substr>::type
2231 ->
typename std::enable_if<std::is_same<U, const char*>::value || std::is_same<U, char*>::value, csubstr>::type
2239 template<
class T>
struct is_string :
public std::false_type {};
2248 template<>
struct is_string<const char*> :
public std::true_type {};
2251 template<>
struct is_string<char*> :
public std::true_type {};
2254 template<
size_t N>
struct is_string<const char[N]> :
public std::true_type {};
2257 template<
size_t N>
struct is_string<char[N]> :
public std::true_type {};
2260 template<
size_t N>
struct is_string<const char (&)[N]> :
public std::true_type {};
2263 template<
size_t N>
struct is_string<char (&)[N]> :
public std::true_type {};
2266 template<
size_t N>
struct is_string<const char (&&)[N]> :
public std::true_type {};
2269 template<
size_t N>
struct is_string<char (&&)[N]> :
public std::true_type {};
2284 template<
typename C,
size_t N>
inline bool operator< (
const char (&s)[N],
basic_substring<C> const that) noexcept {
return that.compare(s, N-1) > 0; }
2285 template<
typename C,
size_t N>
inline bool operator> (
const char (&s)[N],
basic_substring<C> const that) noexcept {
return that.compare(s, N-1) < 0; }
2306 #ifndef C4_SUBSTR_NO_OSTREAM_LSHIFT
2308 # pragma clang diagnostic push
2309 # pragma clang diagnostic ignored "-Wsign-conversion"
2310 #elif defined(__GNUC__)
2311 # pragma GCC diagnostic push
2312 # pragma GCC diagnostic ignored "-Wsign-conversion"
2316 template<
class OStream,
class C>
2332 # pragma clang diagnostic pop
2333 #elif defined(__GNUC__)
2334 # pragma GCC diagnostic pop
2343 C4_SUPPRESS_WARNING_GCC_CLANG_POP
left_< T > left(T val, size_t width, char padchar=' ')
tag type to mark an argument to be aligned left.
right_< T > right(T val, size_t width, char padchar=' ')
tag function to mark an argument to be aligned right
auto to_csubstr(U s) noexcept -> typename std::enable_if< std::is_same< U, const char * >::value||std::is_same< U, char * >::value, csubstr >::type
auto to_substr(U s) noexcept -> typename std::enable_if< std::is_same< U, char * >::value, substr >::type
bool operator<(const char c, basic_substring< C > const that) noexcept
bool operator!=(const char c, basic_substring< C > const that) noexcept
bool operator==(const char c, basic_substring< C > const that) noexcept
bool operator>(const char c, basic_substring< C > const that) noexcept
bool operator>=(const char c, basic_substring< C > const that) noexcept
bool operator<=(const char c, basic_substring< C > const that) noexcept
OStream & operator<<(OStream &os, basic_substring< C > s)
output the string to a stream
@ npos
a null string position
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
split_proxy_impl const * m_proxy
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
void reverse()
reverse in place
basic_substring first_uint_span() const
get the first span which can be interpreted as an unsigned integer
first_of_any_result first_of_any_iter(It first_span, It last_span) const
int compare(ro_substr const that) const noexcept
basic_substring(U s_) noexcept
Construct from a C-string (zero-terminated string)
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
C const & front() const noexcept
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
bool begins_with(const C c) const
true if the first character of the string is c
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.
basic_substring range(size_t first, size_t last=npos) const noexcept
return [first,last[.
int compare(C const c) const noexcept
C const & back() const noexcept
size_t first_not_of(const C 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
basic_substring triml(const C c) const
trim left
bool ends_with(const C c) const
true if the last character of the string is c
size_t last_of(const C c, size_t start=npos) const
void tolower()
convert the string to lower-case
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
void toupper()
convert the string to upper-case
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
get the first span which can be interpreted as a real (floating-point) number
basic_substring unquoted() const
split_proxy_impl split_proxy
basic_substring first_int_span() const
get the first span which can be interpreted as a signed integer
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
basic_substring erase(ro_substr sub)
erase a part of the string.
size_t last_not_of(ro_substr chars) const
constexpr basic_substring() noexcept
basic_substring triml(ro_substr chars) const
trim left ANY of the characters.
size_t len
the length of the substring
basic_substring left_of(size_t pos, bool include_pos) const noexcept
return [0, pos+include_pos[ .
size_t last_not_of(ro_substr chars, size_t start) const
basic_substring last(size_t num) const noexcept
return the last num elements: [len-num,len[
size_t first_of(const C c, size_t start=0) const
basic_substring stripl(ro_substr pattern) const
remove a pattern from the left
bool ends_with(ro_substr pattern) const
true if the string ends with the given pattern
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
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 erase_range(size_t first, size_t last)
basic_substring extshort() const
void assign(U s_) noexcept
Assign from a C-string (zero-terminated string of type const C* or C*)
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
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
bool ends_with_any(ro_substr chars) const
true if the last character of the string is any of the given chars
void fill(C val)
fill the entire contents with the given val
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
bool not_empty() const noexcept
basic_substring first(size_t num) const noexcept
return the first num elements: [0,num[
basic_substring left_of(size_t pos) const noexcept
return [0, pos[ .
void copy_from(ro_substr that, size_t ifirst, size_t num=npos)
copy a string to this substr, starting at a specified given position
void reverse_range(size_t ifirst, size_t ilast)
revert a range in place
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.
C const * data() const noexcept
size_t replace(C value, C repl, size_t pos=0)
replace every occurrence of character value with the character repl
bool has_str() const noexcept
bool begins_with(ro_substr pattern) const
true if the string begins with the given pattern
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
void copy_from(ro_substr that)
copy a string to this substr, starting at 0
bool begins_with_any(ro_substr chars) const
true if the first character of the string is any of the given chars
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 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
size_t replace(ro_substr chars, C repl, size_t pos=0)
replace every occurrence of each character in value with the character repl.
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 C c) const
bool ends_with(const C c, size_t num) const
true if the last num characters of the string are c
bool begins_with(const C c, size_t num) const
true if the first num characters of the string are c
static constexpr C4_CONST bool _is_hex_char(char c) noexcept
true if the character is in [0-9a-fA-F]
basic_substring erase(size_t pos, size_t num)
erase part of the string.
basic_substring _word_follows(size_t pos, csubstr word) const noexcept
void reverse_sub(size_t ifirst, size_t num)
revert a subpart in place
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.
basic_substring first_non_empty_span() const
get the first span consisting exclusively of non-empty characters
C * str
a restricted pointer to the first character of the substring
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
true if the character is a delimiter character at the end
bool is_sub(ro_substr const that) const noexcept
true if *this is a substring of that (ie, from the same buffer)
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
a traits class to mark a type as a string type (meaning c4::to_csubstr() can be used directly).
a traits class to mark a type as a writeable string type (meaning c4::to_substr() can be used directl...
#define _c4append(first, last)