Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/rapidyaml/include/c4/substr.hpp
4261 views
1
#ifndef _C4_SUBSTR_HPP_
2
#define _C4_SUBSTR_HPP_
3
4
/** @file substr.hpp read+write string views */
5
6
#include <string.h>
7
#include <ctype.h>
8
#include <type_traits>
9
10
#include "c4/config.hpp"
11
#include "c4/error.hpp"
12
#include "c4/substr_fwd.hpp"
13
14
#ifdef __clang__
15
# pragma clang diagnostic push
16
# pragma clang diagnostic ignored "-Wold-style-cast"
17
#elif defined(__GNUC__)
18
# pragma GCC diagnostic push
19
# pragma GCC diagnostic ignored "-Wtype-limits" // disable warnings on size_t>=0, used heavily in assertions below. These assertions are a preparation step for providing the index type as a template parameter.
20
# pragma GCC diagnostic ignored "-Wuseless-cast"
21
# pragma GCC diagnostic ignored "-Wold-style-cast"
22
#endif
23
24
25
namespace c4 {
26
27
28
//-----------------------------------------------------------------------------
29
//-----------------------------------------------------------------------------
30
//-----------------------------------------------------------------------------
31
32
namespace detail {
33
34
template<typename C>
35
static inline void _do_reverse(C *C4_RESTRICT first, C *C4_RESTRICT last)
36
{
37
while(last > first)
38
{
39
C tmp = *last;
40
*last-- = *first;
41
*first++ = tmp;
42
}
43
}
44
45
} // namespace detail
46
47
48
//-----------------------------------------------------------------------------
49
//-----------------------------------------------------------------------------
50
//-----------------------------------------------------------------------------
51
52
// utility macros to deuglify SFINAE code; undefined after the class.
53
// https://stackoverflow.com/questions/43051882/how-to-disable-a-class-member-funrtion-for-certain-template-types
54
#define C4_REQUIRE_RW(ret_type) \
55
template <typename U=C> \
56
typename std::enable_if< ! std::is_const<U>::value, ret_type>::type
57
58
59
/** a non-owning string-view, consisting of a character pointer
60
* and a length.
61
*
62
* @note The pointer is explicitly restricted.
63
*
64
* @see to_substr()
65
* @see to_csubstr()
66
*/
67
template<class C>
68
struct C4CORE_EXPORT basic_substring
69
{
70
public:
71
72
/** a restricted pointer to the first character of the substring */
73
C * C4_RESTRICT str;
74
/** the length of the substring */
75
size_t len;
76
77
public:
78
79
/** @name Types */
80
/** @{ */
81
82
using CC = typename std::add_const<C>::type; //!< CC=const char
83
using NCC_ = typename std::remove_const<C>::type; //!< NCC_=non const char
84
85
using ro_substr = basic_substring<CC>;
86
using rw_substr = basic_substring<NCC_>;
87
88
using char_type = C;
89
using size_type = size_t;
90
91
using iterator = C*;
92
using const_iterator = CC*;
93
94
enum : size_t { npos = (size_t)-1, NONE = (size_t)-1 };
95
96
/// convert automatically to substring of const C
97
template<class U=C>
98
C4_ALWAYS_INLINE operator typename std::enable_if<!std::is_const<U>::value, ro_substr const&>::type () const noexcept
99
{
100
return *(ro_substr const*)this; // don't call the str+len ctor because it does a check
101
}
102
103
/** @} */
104
105
public:
106
107
/** @name Default construction and assignment */
108
/** @{ */
109
110
C4_ALWAYS_INLINE constexpr basic_substring() noexcept : str(), len() {}
111
112
C4_ALWAYS_INLINE basic_substring(basic_substring const&) noexcept = default;
113
C4_ALWAYS_INLINE basic_substring(basic_substring &&) noexcept = default;
114
C4_ALWAYS_INLINE basic_substring(std::nullptr_t) noexcept : str(nullptr), len(0) {}
115
116
C4_ALWAYS_INLINE basic_substring& operator= (basic_substring const&) noexcept = default;
117
C4_ALWAYS_INLINE basic_substring& operator= (basic_substring &&) noexcept = default;
118
C4_ALWAYS_INLINE basic_substring& operator= (std::nullptr_t) noexcept { str = nullptr; len = 0; return *this; }
119
120
C4_ALWAYS_INLINE void clear() noexcept { str = nullptr; len = 0; }
121
122
/** @} */
123
124
public:
125
126
/** @name Construction and assignment from characters with the same type */
127
/** @{ */
128
129
/** Construct from an array.
130
* @warning the input string need not be zero terminated, but the
131
* length is taken as if the string was zero terminated */
132
template<size_t N>
133
C4_ALWAYS_INLINE constexpr basic_substring(C (&s_)[N]) noexcept : str(s_), len(N-1) {}
134
/** Construct from a pointer and length.
135
* @warning the input string need not be zero terminated. */
136
C4_ALWAYS_INLINE basic_substring(C *s_, size_t len_) noexcept : str(s_), len(len_) { C4_ASSERT(str || !len_); }
137
/** Construct from two pointers.
138
* @warning the end pointer MUST BE larger than or equal to the begin pointer
139
* @warning the input string need not be zero terminated */
140
C4_ALWAYS_INLINE basic_substring(C *beg_, C *end_) noexcept : str(beg_), len(static_cast<size_t>(end_ - beg_)) { C4_ASSERT(end_ >= beg_); }
141
/** Construct from a C-string (zero-terminated string)
142
* @warning the input string MUST BE zero terminated.
143
* @warning will call strlen()
144
* @note this overload uses SFINAE to prevent it from overriding the array ctor
145
* @see For a more detailed explanation on why the plain overloads cannot
146
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
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) {}
149
150
/** Assign from an array.
151
* @warning the input string need not be zero terminated, but the
152
* length is taken as if the string was zero terminated */
153
template<size_t N>
154
C4_ALWAYS_INLINE void assign(C (&s_)[N]) noexcept { str = (s_); len = (N-1); }
155
/** Assign from a pointer and length.
156
* @warning the input string need not be zero terminated. */
157
C4_ALWAYS_INLINE void assign(C *s_, size_t len_) noexcept { str = s_; len = len_; C4_ASSERT(str || !len_); }
158
/** Assign from two pointers.
159
* @warning the end pointer MUST BE larger than or equal to the begin pointer
160
* @warning the input string need not be zero terminated. */
161
C4_ALWAYS_INLINE void assign(C *beg_, C *end_) noexcept { C4_ASSERT(end_ >= beg_); str = (beg_); len = static_cast<size_t>(end_ - beg_); }
162
/** Assign from a C-string (zero-terminated string)
163
* @warning the input string must be zero terminated.
164
* @warning will call strlen()
165
* @note this overload uses SFINAE to prevent it from overriding the array ctor
166
* @see For a more detailed explanation on why the plain overloads cannot
167
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
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); }
170
171
/** Assign from an array.
172
* @warning the input string need not be zero terminated. */
173
template<size_t N>
174
C4_ALWAYS_INLINE basic_substring& operator= (C (&s_)[N]) noexcept { str = (s_); len = (N-1); return *this; }
175
/** Assign from a C-string (zero-terminated string)
176
* @warning the input string MUST BE zero terminated.
177
* @warning will call strlen()
178
* @note this overload uses SFINAE to prevent it from overriding the array ctor
179
* @see For a more detailed explanation on why the plain overloads cannot
180
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
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; }
183
184
/** @} */
185
186
public:
187
188
/** @name Standard accessor methods */
189
/** @{ */
190
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; }
195
196
C4_ALWAYS_INLINE C4_PURE iterator begin() noexcept { return str; }
197
C4_ALWAYS_INLINE C4_PURE iterator end () noexcept { return str + len; }
198
199
C4_ALWAYS_INLINE C4_PURE const_iterator begin() const noexcept { return str; }
200
C4_ALWAYS_INLINE C4_PURE const_iterator end () const noexcept { return str + len; }
201
202
C4_ALWAYS_INLINE C4_PURE C * data() noexcept { return str; }
203
C4_ALWAYS_INLINE C4_PURE C const* data() const noexcept { return str; }
204
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]; }
207
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; }
210
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); }
213
214
/** @} */
215
216
public:
217
218
/** @name Comparison methods */
219
/** @{ */
220
221
C4_PURE int compare(C const c) const noexcept
222
{
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);
226
else
227
return -1;
228
}
229
230
C4_PURE int compare(const char *C4_RESTRICT that, size_t sz) const noexcept
231
{
232
C4_XASSERT(that || sz == 0);
233
C4_XASSERT(str || len == 0);
234
if(C4_LIKELY(str && that))
235
{
236
{
237
const size_t min = len < sz ? len : sz;
238
for(size_t i = 0; i < min; ++i)
239
if(str[i] != that[i])
240
return str[i] < that[i] ? -1 : 1;
241
}
242
if(len < sz)
243
return -1;
244
else if(len == sz)
245
return 0;
246
else
247
return 1;
248
}
249
else if(len == sz)
250
{
251
C4_XASSERT(len == 0 && sz == 0);
252
return 0;
253
}
254
return len < sz ? -1 : 1;
255
}
256
257
C4_ALWAYS_INLINE C4_PURE int compare(ro_substr const that) const noexcept { return this->compare(that.str, that.len); }
258
259
C4_ALWAYS_INLINE C4_PURE bool operator== (std::nullptr_t) const noexcept { return str == nullptr; }
260
C4_ALWAYS_INLINE C4_PURE bool operator!= (std::nullptr_t) const noexcept { return str != nullptr; }
261
262
C4_ALWAYS_INLINE C4_PURE bool operator== (C const c) const noexcept { return this->compare(c) == 0; }
263
C4_ALWAYS_INLINE C4_PURE bool operator!= (C const c) const noexcept { return this->compare(c) != 0; }
264
C4_ALWAYS_INLINE C4_PURE bool operator< (C const c) const noexcept { return this->compare(c) < 0; }
265
C4_ALWAYS_INLINE C4_PURE bool operator> (C const c) const noexcept { return this->compare(c) > 0; }
266
C4_ALWAYS_INLINE C4_PURE bool operator<= (C const c) const noexcept { return this->compare(c) <= 0; }
267
C4_ALWAYS_INLINE C4_PURE bool operator>= (C const c) const noexcept { return this->compare(c) >= 0; }
268
269
template<class U> C4_ALWAYS_INLINE C4_PURE bool operator== (basic_substring<U> const that) const noexcept { return this->compare(that) == 0; }
270
template<class U> C4_ALWAYS_INLINE C4_PURE bool operator!= (basic_substring<U> const that) const noexcept { return this->compare(that) != 0; }
271
template<class U> C4_ALWAYS_INLINE C4_PURE bool operator< (basic_substring<U> const that) const noexcept { return this->compare(that) < 0; }
272
template<class U> C4_ALWAYS_INLINE C4_PURE bool operator> (basic_substring<U> const that) const noexcept { return this->compare(that) > 0; }
273
template<class U> C4_ALWAYS_INLINE C4_PURE bool operator<= (basic_substring<U> const that) const noexcept { return this->compare(that) <= 0; }
274
template<class U> C4_ALWAYS_INLINE C4_PURE bool operator>= (basic_substring<U> const that) const noexcept { return this->compare(that) >= 0; }
275
276
template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator== (const char (&that)[N]) const noexcept { return this->compare(that, N-1) == 0; }
277
template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator!= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) != 0; }
278
template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator< (const char (&that)[N]) const noexcept { return this->compare(that, N-1) < 0; }
279
template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator> (const char (&that)[N]) const noexcept { return this->compare(that, N-1) > 0; }
280
template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator<= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) <= 0; }
281
template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator>= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) >= 0; }
282
283
/** @} */
284
285
public:
286
287
/** @name Sub-selection methods */
288
/** @{ */
289
290
/** true if *this is a substring of that (ie, from the same buffer) */
291
C4_ALWAYS_INLINE C4_PURE bool is_sub(ro_substr const that) const noexcept
292
{
293
return that.is_super(*this);
294
}
295
296
/** true if that is a substring of *this (ie, from the same buffer) */
297
C4_ALWAYS_INLINE C4_PURE bool is_super(ro_substr const that) const noexcept
298
{
299
if(C4_LIKELY(len > 0))
300
return that.str >= str && that.str+that.len <= str+len;
301
else
302
return that.len == 0 && that.str == str && str != nullptr;
303
}
304
305
/** true if there is overlap of at least one element between that and *this */
306
C4_ALWAYS_INLINE C4_PURE bool overlaps(ro_substr const that) const noexcept
307
{
308
// thanks @timwynants
309
return that.str+that.len > str && that.str < str+len;
310
}
311
312
public:
313
314
/** return [first,len[ */
315
C4_ALWAYS_INLINE C4_PURE basic_substring sub(size_t first) const noexcept
316
{
317
C4_ASSERT(first >= 0 && first <= len);
318
return basic_substring(str + first, len - first);
319
}
320
321
/** return [first,first+num[. If num==npos, return [first,len[ */
322
C4_ALWAYS_INLINE C4_PURE basic_substring sub(size_t first, size_t num) const noexcept
323
{
324
C4_ASSERT(first >= 0 && first <= len);
325
C4_ASSERT((num >= 0 && num <= len) || (num == npos));
326
size_t rnum = num != npos ? num : len - first;
327
C4_ASSERT((first >= 0 && first + rnum <= len) || (num == 0));
328
return basic_substring(str + first, rnum);
329
}
330
331
/** return [first,last[. If last==npos, return [first,len[ */
332
C4_ALWAYS_INLINE C4_PURE basic_substring range(size_t first, size_t last=npos) const noexcept
333
{
334
C4_ASSERT(first >= 0 && first <= len);
335
last = last != npos ? last : len;
336
C4_ASSERT(first <= last);
337
C4_ASSERT(last >= 0 && last <= len);
338
return basic_substring(str + first, last - first);
339
}
340
341
/** return the first @p num elements: [0,num[*/
342
C4_ALWAYS_INLINE C4_PURE basic_substring first(size_t num) const noexcept
343
{
344
C4_ASSERT(num <= len || num == npos);
345
return basic_substring(str, num != npos ? num : len);
346
}
347
348
/** return the last @num elements: [len-num,len[*/
349
C4_ALWAYS_INLINE C4_PURE basic_substring last(size_t num) const noexcept
350
{
351
C4_ASSERT(num <= len || num == npos);
352
return num != npos ?
353
basic_substring(str + len - num, num) :
354
*this;
355
}
356
357
/** offset from the ends: return [left,len-right[ ; ie, trim a
358
number of characters from the left and right. This is
359
equivalent to python's negative list indices. */
360
C4_ALWAYS_INLINE C4_PURE basic_substring offs(size_t left, size_t right) const noexcept
361
{
362
C4_ASSERT(left >= 0 && left <= len);
363
C4_ASSERT(right >= 0 && right <= len);
364
C4_ASSERT(left <= len - right + 1);
365
return basic_substring(str + left, len - right - left);
366
}
367
368
/** return [0, pos[ . Same as .first(pos), but provided for compatibility with .right_of() */
369
C4_ALWAYS_INLINE C4_PURE basic_substring left_of(size_t pos) const noexcept
370
{
371
C4_ASSERT(pos <= len || pos == npos);
372
return (pos != npos) ?
373
basic_substring(str, pos) :
374
*this;
375
}
376
377
/** return [0, pos+include_pos[ . Same as .first(pos+1), but provided for compatibility with .right_of() */
378
C4_ALWAYS_INLINE C4_PURE basic_substring left_of(size_t pos, bool include_pos) const noexcept
379
{
380
C4_ASSERT(pos <= len || pos == npos);
381
return (pos != npos) ?
382
basic_substring(str, pos+include_pos) :
383
*this;
384
}
385
386
/** return [pos+1, len[ */
387
C4_ALWAYS_INLINE C4_PURE basic_substring right_of(size_t pos) const noexcept
388
{
389
C4_ASSERT(pos <= len || pos == npos);
390
return (pos != npos) ?
391
basic_substring(str + (pos + 1), len - (pos + 1)) :
392
basic_substring(str + len, size_t(0));
393
}
394
395
/** return [pos+!include_pos, len[ */
396
C4_ALWAYS_INLINE C4_PURE basic_substring right_of(size_t pos, bool include_pos) const noexcept
397
{
398
C4_ASSERT(pos <= len || pos == npos);
399
return (pos != npos) ?
400
basic_substring(str + (pos + !include_pos), len - (pos + !include_pos)) :
401
basic_substring(str + len, size_t(0));
402
}
403
404
public:
405
406
/** given @p subs a substring of the current string, get the
407
* portion of the current string to the left of it */
408
C4_ALWAYS_INLINE C4_PURE basic_substring left_of(ro_substr const subs) const noexcept
409
{
410
C4_ASSERT(is_super(subs) || subs.empty());
411
auto ssb = subs.begin();
412
auto b = begin();
413
auto e = end();
414
if(ssb >= b && ssb <= e)
415
return sub(0, static_cast<size_t>(ssb - b));
416
else
417
return sub(0, 0);
418
}
419
420
/** given @p subs a substring of the current string, get the
421
* portion of the current string to the right of it */
422
C4_ALWAYS_INLINE C4_PURE basic_substring right_of(ro_substr const subs) const noexcept
423
{
424
C4_ASSERT(is_super(subs) || subs.empty());
425
auto sse = subs.end();
426
auto b = begin();
427
auto e = end();
428
if(sse >= b && sse <= e)
429
return sub(static_cast<size_t>(sse - b), static_cast<size_t>(e - sse));
430
else
431
return sub(0, 0);
432
}
433
434
/** @} */
435
436
public:
437
438
/** @name Removing characters (trim()) / patterns (strip()) from the tips of the string */
439
/** @{ */
440
441
/** trim left */
442
basic_substring triml(const C c) const
443
{
444
if( ! empty())
445
{
446
size_t pos = first_not_of(c);
447
if(pos != npos)
448
return sub(pos);
449
}
450
return sub(0, 0);
451
}
452
/** trim left ANY of the characters.
453
* @see stripl() to remove a pattern from the left */
454
basic_substring triml(ro_substr chars) const
455
{
456
if( ! empty())
457
{
458
size_t pos = first_not_of(chars);
459
if(pos != npos)
460
return sub(pos);
461
}
462
return sub(0, 0);
463
}
464
465
/** trim the character c from the right */
466
basic_substring trimr(const C c) const
467
{
468
if( ! empty())
469
{
470
size_t pos = last_not_of(c, npos);
471
if(pos != npos)
472
return sub(0, pos+1);
473
}
474
return sub(0, 0);
475
}
476
/** trim right ANY of the characters
477
* @see stripr() to remove a pattern from the right */
478
basic_substring trimr(ro_substr chars) const
479
{
480
if( ! empty())
481
{
482
size_t pos = last_not_of(chars, npos);
483
if(pos != npos)
484
return sub(0, pos+1);
485
}
486
return sub(0, 0);
487
}
488
489
/** trim the character c left and right */
490
basic_substring trim(const C c) const
491
{
492
return triml(c).trimr(c);
493
}
494
/** trim left and right ANY of the characters
495
* @see strip() to remove a pattern from the left and right */
496
basic_substring trim(ro_substr const chars) const
497
{
498
return triml(chars).trimr(chars);
499
}
500
501
/** remove a pattern from the left
502
* @see triml() to remove characters*/
503
basic_substring stripl(ro_substr pattern) const
504
{
505
if( ! begins_with(pattern))
506
return *this;
507
return sub(pattern.len < len ? pattern.len : len);
508
}
509
510
/** remove a pattern from the right
511
* @see trimr() to remove characters*/
512
basic_substring stripr(ro_substr pattern) const
513
{
514
if( ! ends_with(pattern))
515
return *this;
516
return left_of(len - (pattern.len < len ? pattern.len : len));
517
}
518
519
/** @} */
520
521
public:
522
523
/** @name Lookup methods */
524
/** @{ */
525
526
inline size_t find(const C c, size_t start_pos=0) const
527
{
528
return first_of(c, start_pos);
529
}
530
inline size_t find(ro_substr pattern, size_t start_pos=0) const
531
{
532
C4_ASSERT(start_pos == npos || (start_pos >= 0 && start_pos <= len));
533
if(len < pattern.len) return npos;
534
for(size_t i = start_pos, e = len - pattern.len + 1; i < e; ++i)
535
{
536
bool gotit = true;
537
for(size_t j = 0; j < pattern.len; ++j)
538
{
539
C4_ASSERT(i + j < len);
540
if(str[i + j] != pattern.str[j])
541
{
542
gotit = false;
543
break;
544
}
545
}
546
if(gotit)
547
{
548
return i;
549
}
550
}
551
return npos;
552
}
553
554
public:
555
556
/** count the number of occurrences of c */
557
inline size_t count(const C c, size_t pos=0) const
558
{
559
C4_ASSERT(pos >= 0 && pos <= len);
560
size_t num = 0;
561
pos = find(c, pos);
562
while(pos != npos)
563
{
564
++num;
565
pos = find(c, pos + 1);
566
}
567
return num;
568
}
569
570
/** count the number of occurrences of s */
571
inline size_t count(ro_substr c, size_t pos=0) const
572
{
573
C4_ASSERT(pos >= 0 && pos <= len);
574
size_t num = 0;
575
pos = find(c, pos);
576
while(pos != npos)
577
{
578
++num;
579
pos = find(c, pos + c.len);
580
}
581
return num;
582
}
583
584
/** get the substr consisting of the first occurrence of @p c after @p pos, or an empty substr if none occurs */
585
inline basic_substring select(const C c, size_t pos=0) const
586
{
587
pos = find(c, pos);
588
return pos != npos ? sub(pos, 1) : basic_substring();
589
}
590
591
/** get the substr consisting of the first occurrence of @p pattern after @p pos, or an empty substr if none occurs */
592
inline basic_substring select(ro_substr pattern, size_t pos=0) const
593
{
594
pos = find(pattern, pos);
595
return pos != npos ? sub(pos, pattern.len) : basic_substring();
596
}
597
598
public:
599
600
struct first_of_any_result
601
{
602
size_t which;
603
size_t pos;
604
inline operator bool() const { return which != NONE && pos != npos; }
605
};
606
607
first_of_any_result first_of_any(ro_substr s0, ro_substr s1) const
608
{
609
ro_substr s[2] = {s0, s1};
610
return first_of_any_iter(&s[0], &s[0] + 2);
611
}
612
613
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2) const
614
{
615
ro_substr s[3] = {s0, s1, s2};
616
return first_of_any_iter(&s[0], &s[0] + 3);
617
}
618
619
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2, ro_substr s3) const
620
{
621
ro_substr s[4] = {s0, s1, s2, s3};
622
return first_of_any_iter(&s[0], &s[0] + 4);
623
}
624
625
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2, ro_substr s3, ro_substr s4) const
626
{
627
ro_substr s[5] = {s0, s1, s2, s3, s4};
628
return first_of_any_iter(&s[0], &s[0] + 5);
629
}
630
631
template<class It>
632
first_of_any_result first_of_any_iter(It first_span, It last_span) const
633
{
634
for(size_t i = 0; i < len; ++i)
635
{
636
size_t curr = 0;
637
for(It it = first_span; it != last_span; ++curr, ++it)
638
{
639
auto const& chars = *it;
640
if((i + chars.len) > len) continue;
641
bool gotit = true;
642
for(size_t j = 0; j < chars.len; ++j)
643
{
644
C4_ASSERT(i + j < len);
645
if(str[i + j] != chars[j])
646
{
647
gotit = false;
648
break;
649
}
650
}
651
if(gotit)
652
{
653
return {curr, i};
654
}
655
}
656
}
657
return {NONE, npos};
658
}
659
660
public:
661
662
/** true if the first character of the string is @p c */
663
bool begins_with(const C c) const
664
{
665
return len > 0 ? str[0] == c : false;
666
}
667
668
/** true if the first @p num characters of the string are @p c */
669
bool begins_with(const C c, size_t num) const
670
{
671
if(len < num)
672
{
673
return false;
674
}
675
for(size_t i = 0; i < num; ++i)
676
{
677
if(str[i] != c)
678
{
679
return false;
680
}
681
}
682
return true;
683
}
684
685
/** true if the string begins with the given @p pattern */
686
bool begins_with(ro_substr pattern) const
687
{
688
if(len < pattern.len)
689
{
690
return false;
691
}
692
for(size_t i = 0; i < pattern.len; ++i)
693
{
694
if(str[i] != pattern[i])
695
{
696
return false;
697
}
698
}
699
return true;
700
}
701
702
/** true if the first character of the string is any of the given @p chars */
703
bool begins_with_any(ro_substr chars) const
704
{
705
if(len == 0)
706
{
707
return false;
708
}
709
for(size_t i = 0; i < chars.len; ++i)
710
{
711
if(str[0] == chars.str[i])
712
{
713
return true;
714
}
715
}
716
return false;
717
}
718
719
/** true if the last character of the string is @p c */
720
bool ends_with(const C c) const
721
{
722
return len > 0 ? str[len-1] == c : false;
723
}
724
725
/** true if the last @p num characters of the string are @p c */
726
bool ends_with(const C c, size_t num) const
727
{
728
if(len < num)
729
{
730
return false;
731
}
732
for(size_t i = len - num; i < len; ++i)
733
{
734
if(str[i] != c)
735
{
736
return false;
737
}
738
}
739
return true;
740
}
741
742
/** true if the string ends with the given @p pattern */
743
bool ends_with(ro_substr pattern) const
744
{
745
if(len < pattern.len)
746
{
747
return false;
748
}
749
for(size_t i = 0, s = len-pattern.len; i < pattern.len; ++i)
750
{
751
if(str[s+i] != pattern[i])
752
{
753
return false;
754
}
755
}
756
return true;
757
}
758
759
/** true if the last character of the string is any of the given @p chars */
760
bool ends_with_any(ro_substr chars) const
761
{
762
if(len == 0)
763
{
764
return false;
765
}
766
for(size_t i = 0; i < chars.len; ++i)
767
{
768
if(str[len - 1] == chars[i])
769
{
770
return true;
771
}
772
}
773
return false;
774
}
775
776
public:
777
778
/** @return the first position where c is found in the string, or npos if none is found */
779
size_t first_of(const C c, size_t start=0) const
780
{
781
C4_ASSERT(start == npos || (start >= 0 && start <= len));
782
for(size_t i = start; i < len; ++i)
783
{
784
if(str[i] == c)
785
return i;
786
}
787
return npos;
788
}
789
790
/** @return the last position where c is found in the string, or npos if none is found */
791
size_t last_of(const C c, size_t start=npos) const
792
{
793
C4_ASSERT(start == npos || (start >= 0 && start <= len));
794
if(start == npos)
795
start = len;
796
for(size_t i = start-1; i != size_t(-1); --i)
797
{
798
if(str[i] == c)
799
return i;
800
}
801
return npos;
802
}
803
804
/** @return the first position where ANY of the chars is found in the string, or npos if none is found */
805
size_t first_of(ro_substr chars, size_t start=0) const
806
{
807
C4_ASSERT(start == npos || (start >= 0 && start <= len));
808
for(size_t i = start; i < len; ++i)
809
{
810
for(size_t j = 0; j < chars.len; ++j)
811
{
812
if(str[i] == chars[j])
813
return i;
814
}
815
}
816
return npos;
817
}
818
819
/** @return the last position where ANY of the chars is found in the string, or npos if none is found */
820
size_t last_of(ro_substr chars, size_t start=npos) const
821
{
822
C4_ASSERT(start == npos || (start >= 0 && start <= len));
823
if(start == npos)
824
start = len;
825
for(size_t i = start-1; i != size_t(-1); --i)
826
{
827
for(size_t j = 0; j < chars.len; ++j)
828
{
829
if(str[i] == chars[j])
830
return i;
831
}
832
}
833
return npos;
834
}
835
836
public:
837
838
size_t first_not_of(const C c, size_t start=0) const
839
{
840
C4_ASSERT((start >= 0 && start <= len) || (start == len && len == 0));
841
for(size_t i = start; i < len; ++i)
842
{
843
if(str[i] != c)
844
return i;
845
}
846
return npos;
847
}
848
849
size_t last_not_of(const C c, size_t start=npos) const
850
{
851
C4_ASSERT(start == npos || (start >= 0 && start <= len));
852
if(start == npos)
853
start = len;
854
for(size_t i = start-1; i != size_t(-1); --i)
855
{
856
if(str[i] != c)
857
return i;
858
}
859
return npos;
860
}
861
862
size_t first_not_of(ro_substr chars, size_t start=0) const
863
{
864
C4_ASSERT((start >= 0 && start <= len) || (start == len && len == 0));
865
for(size_t i = start; i < len; ++i)
866
{
867
bool gotit = true;
868
for(size_t j = 0; j < chars.len; ++j)
869
{
870
if(str[i] == chars.str[j])
871
{
872
gotit = false;
873
break;
874
}
875
}
876
if(gotit)
877
{
878
return i;
879
}
880
}
881
return npos;
882
}
883
884
size_t last_not_of(ro_substr chars, size_t start=npos) const
885
{
886
C4_ASSERT(start == npos || (start >= 0 && start <= len));
887
if(start == npos)
888
start = len;
889
for(size_t i = start-1; i != size_t(-1); --i)
890
{
891
bool gotit = true;
892
for(size_t j = 0; j < chars.len; ++j)
893
{
894
if(str[i] == chars.str[j])
895
{
896
gotit = false;
897
break;
898
}
899
}
900
if(gotit)
901
{
902
return i;
903
}
904
}
905
return npos;
906
}
907
908
/** @} */
909
910
public:
911
912
/** @name Range lookup methods */
913
/** @{ */
914
915
/** get the range delimited by an open-close pair of characters.
916
* @note There must be no nested pairs.
917
* @note No checks for escapes are performed. */
918
basic_substring pair_range(CC open, CC close) const
919
{
920
size_t b = find(open);
921
if(b == npos)
922
return basic_substring();
923
size_t e = find(close, b+1);
924
if(e == npos)
925
return basic_substring();
926
basic_substring ret = range(b, e+1);
927
C4_ASSERT(ret.sub(1).find(open) == npos);
928
return ret;
929
}
930
931
/** get the range delimited by a single open-close character (eg, quotes).
932
* @note The open-close character can be escaped. */
933
basic_substring pair_range_esc(CC open_close, CC escape=CC('\\'))
934
{
935
size_t b = find(open_close);
936
if(b == npos) return basic_substring();
937
for(size_t i = b+1; i < len; ++i)
938
{
939
CC c = str[i];
940
if(c == open_close)
941
{
942
if(str[i-1] != escape)
943
{
944
return range(b, i+1);
945
}
946
}
947
}
948
return basic_substring();
949
}
950
951
/** get the range delimited by an open-close pair of characters,
952
* with possibly nested occurrences. No checks for escapes are
953
* performed. */
954
basic_substring pair_range_nested(CC open, CC close) const
955
{
956
size_t b = find(open);
957
if(b == npos) return basic_substring();
958
size_t e, curr = b+1, count = 0;
959
const char both[] = {open, close, '\0'};
960
while((e = first_of(both, curr)) != npos)
961
{
962
if(str[e] == open)
963
{
964
++count;
965
curr = e+1;
966
}
967
else if(str[e] == close)
968
{
969
if(count == 0) return range(b, e+1);
970
--count;
971
curr = e+1;
972
}
973
}
974
return basic_substring();
975
}
976
977
basic_substring unquoted() const
978
{
979
constexpr const C dq('"'), sq('\'');
980
if(len >= 2 && (str[len - 2] != C('\\')) &&
981
((begins_with(sq) && ends_with(sq))
982
||
983
(begins_with(dq) && ends_with(dq))))
984
{
985
return range(1, len -1);
986
}
987
return *this;
988
}
989
990
/** @} */
991
992
public:
993
994
/** @name Number-matching query methods */
995
/** @{ */
996
997
/** @return true if the substring contents are a floating-point or integer number.
998
* @note any leading or trailing whitespace will return false. */
999
bool is_number() const
1000
{
1001
if(empty() || (first_non_empty_span().empty()))
1002
return false;
1003
if(first_uint_span() == *this)
1004
return true;
1005
if(first_int_span() == *this)
1006
return true;
1007
if(first_real_span() == *this)
1008
return true;
1009
return false;
1010
}
1011
1012
/** @return true if the substring contents are a real number.
1013
* @note any leading or trailing whitespace will return false. */
1014
bool is_real() const
1015
{
1016
if(empty() || (first_non_empty_span().empty()))
1017
return false;
1018
if(first_real_span() == *this)
1019
return true;
1020
return false;
1021
}
1022
1023
/** @return true if the substring contents are an integer number.
1024
* @note any leading or trailing whitespace will return false. */
1025
bool is_integer() const
1026
{
1027
if(empty() || (first_non_empty_span().empty()))
1028
return false;
1029
if(first_uint_span() == *this)
1030
return true;
1031
if(first_int_span() == *this)
1032
return true;
1033
return false;
1034
}
1035
1036
/** @return true if the substring contents are an unsigned integer number.
1037
* @note any leading or trailing whitespace will return false. */
1038
bool is_unsigned_integer() const
1039
{
1040
if(empty() || (first_non_empty_span().empty()))
1041
return false;
1042
if(first_uint_span() == *this)
1043
return true;
1044
return false;
1045
}
1046
1047
/** get the first span consisting exclusively of non-empty characters */
1048
basic_substring first_non_empty_span() const
1049
{
1050
constexpr const ro_substr empty_chars(" \n\r\t");
1051
size_t pos = first_not_of(empty_chars);
1052
if(pos == npos)
1053
return first(0);
1054
auto ret = sub(pos);
1055
pos = ret.first_of(empty_chars);
1056
return ret.first(pos);
1057
}
1058
1059
/** get the first span which can be interpreted as an unsigned integer */
1060
basic_substring first_uint_span() const
1061
{
1062
basic_substring ne = first_non_empty_span();
1063
if(ne.empty())
1064
return ne;
1065
if(ne.str[0] == '-')
1066
return first(0);
1067
size_t skip_start = size_t(ne.str[0] == '+');
1068
return ne._first_integral_span(skip_start);
1069
}
1070
1071
/** get the first span which can be interpreted as a signed integer */
1072
basic_substring first_int_span() const
1073
{
1074
basic_substring ne = first_non_empty_span();
1075
if(ne.empty())
1076
return ne;
1077
size_t skip_start = size_t(ne.str[0] == '+' || ne.str[0] == '-');
1078
return ne._first_integral_span(skip_start);
1079
}
1080
1081
basic_substring _first_integral_span(size_t skip_start) const
1082
{
1083
C4_ASSERT(!empty());
1084
if(skip_start == len)
1085
return first(0);
1086
C4_ASSERT(skip_start < len);
1087
if(len >= skip_start + 3)
1088
{
1089
if(str[skip_start] != '0')
1090
{
1091
for(size_t i = skip_start; i < len; ++i)
1092
{
1093
char c = str[i];
1094
if(c < '0' || c > '9')
1095
return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1096
}
1097
}
1098
else
1099
{
1100
char next = str[skip_start + 1];
1101
if(next == 'x' || next == 'X')
1102
{
1103
skip_start += 2;
1104
for(size_t i = skip_start; i < len; ++i)
1105
{
1106
const char c = str[i];
1107
if( ! _is_hex_char(c))
1108
return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1109
}
1110
return *this;
1111
}
1112
else if(next == 'b' || next == 'B')
1113
{
1114
skip_start += 2;
1115
for(size_t i = skip_start; i < len; ++i)
1116
{
1117
const char c = str[i];
1118
if(c != '0' && c != '1')
1119
return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1120
}
1121
return *this;
1122
}
1123
else if(next == 'o' || next == 'O')
1124
{
1125
skip_start += 2;
1126
for(size_t i = skip_start; i < len; ++i)
1127
{
1128
const char c = str[i];
1129
if(c < '0' || c > '7')
1130
return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1131
}
1132
return *this;
1133
}
1134
}
1135
}
1136
// must be a decimal, or it is not a an number
1137
for(size_t i = skip_start; i < len; ++i)
1138
{
1139
const char c = str[i];
1140
if(c < '0' || c > '9')
1141
return i > skip_start && _is_delim_char(c) ? first(i) : first(0);
1142
}
1143
return *this;
1144
}
1145
1146
/** get the first span which can be interpreted as a real (floating-point) number */
1147
basic_substring first_real_span() const
1148
{
1149
basic_substring ne = first_non_empty_span();
1150
if(ne.empty())
1151
return ne;
1152
size_t skip_start = (ne.str[0] == '+' || ne.str[0] == '-');
1153
C4_ASSERT(skip_start == 0 || skip_start == 1);
1154
// if we have at least three digits after the leading sign, it
1155
// can be decimal, or hex, or bin or oct. Ex:
1156
// non-decimal: 0x0, 0b0, 0o0
1157
// decimal: 1.0, 10., 1e1, 100, inf, nan, infinity
1158
if(ne.len >= skip_start+3)
1159
{
1160
// if it does not have leading 0, it must be decimal, or it is not a real
1161
if(ne.str[skip_start] != '0')
1162
{
1163
if(ne.str[skip_start] == 'i') // is it infinity or inf?
1164
{
1165
basic_substring word = ne._word_follows(skip_start + 1, "nfinity");
1166
if(word.len)
1167
return word;
1168
return ne._word_follows(skip_start + 1, "nf");
1169
}
1170
else if(ne.str[skip_start] == 'n') // is it nan?
1171
{
1172
return ne._word_follows(skip_start + 1, "an");
1173
}
1174
else // must be a decimal, or it is not a real
1175
{
1176
return ne._first_real_span_dec(skip_start);
1177
}
1178
}
1179
else // starts with 0. is it 0x, 0b or 0o?
1180
{
1181
const char next = ne.str[skip_start + 1];
1182
// hexadecimal
1183
if(next == 'x' || next == 'X')
1184
return ne._first_real_span_hex(skip_start + 2);
1185
// binary
1186
else if(next == 'b' || next == 'B')
1187
return ne._first_real_span_bin(skip_start + 2);
1188
// octal
1189
else if(next == 'o' || next == 'O')
1190
return ne._first_real_span_oct(skip_start + 2);
1191
// none of the above. may still be a decimal.
1192
else
1193
return ne._first_real_span_dec(skip_start); // do not skip the 0.
1194
}
1195
}
1196
// less than 3 chars after the leading sign. It is either a
1197
// decimal or it is not a real. (cannot be any of 0x0, etc).
1198
return ne._first_real_span_dec(skip_start);
1199
}
1200
1201
/** true if the character is a delimiter character *at the end* */
1202
static constexpr C4_ALWAYS_INLINE C4_CONST bool _is_delim_char(char c) noexcept
1203
{
1204
return c == ' ' || c == '\n'
1205
|| c == ']' || c == ')' || c == '}'
1206
|| c == ',' || c == ';' || c == '\r' || c == '\t' || c == '\0';
1207
}
1208
1209
/** true if the character is in [0-9a-fA-F] */
1210
static constexpr C4_ALWAYS_INLINE C4_CONST bool _is_hex_char(char c) noexcept
1211
{
1212
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
1213
}
1214
1215
C4_NO_INLINE C4_PURE basic_substring _word_follows(size_t pos, csubstr word) const noexcept
1216
{
1217
size_t posend = pos + word.len;
1218
if(len >= posend && sub(pos, word.len) == word)
1219
if(len == posend || _is_delim_char(str[posend]))
1220
return first(posend);
1221
return first(0);
1222
}
1223
1224
// this function is declared inside the class to avoid a VS error with __declspec(dllimport)
1225
C4_NO_INLINE C4_PURE basic_substring _first_real_span_dec(size_t pos) const noexcept
1226
{
1227
bool intchars = false;
1228
bool fracchars = false;
1229
bool powchars;
1230
// integral part
1231
for( ; pos < len; ++pos)
1232
{
1233
const char c = str[pos];
1234
if(c >= '0' && c <= '9')
1235
{
1236
intchars = true;
1237
}
1238
else if(c == '.')
1239
{
1240
++pos;
1241
goto fractional_part_dec;
1242
}
1243
else if(c == 'e' || c == 'E')
1244
{
1245
++pos;
1246
goto power_part_dec;
1247
}
1248
else if(_is_delim_char(c))
1249
{
1250
return intchars ? first(pos) : first(0);
1251
}
1252
else
1253
{
1254
return first(0);
1255
}
1256
}
1257
// no . or p were found; this is either an integral number
1258
// or not a number at all
1259
return intchars ?
1260
*this :
1261
first(0);
1262
fractional_part_dec:
1263
C4_ASSERT(pos > 0);
1264
C4_ASSERT(str[pos - 1] == '.');
1265
for( ; pos < len; ++pos)
1266
{
1267
const char c = str[pos];
1268
if(c >= '0' && c <= '9')
1269
{
1270
fracchars = true;
1271
}
1272
else if(c == 'e' || c == 'E')
1273
{
1274
++pos;
1275
goto power_part_dec;
1276
}
1277
else if(_is_delim_char(c))
1278
{
1279
return intchars || fracchars ? first(pos) : first(0);
1280
}
1281
else
1282
{
1283
return first(0);
1284
}
1285
}
1286
return intchars || fracchars ?
1287
*this :
1288
first(0);
1289
power_part_dec:
1290
C4_ASSERT(pos > 0);
1291
C4_ASSERT(str[pos - 1] == 'e' || str[pos - 1] == 'E');
1292
// either a + or a - is expected here, followed by more chars.
1293
// also, using (pos+1) in this check will cause an early
1294
// return when no more chars follow the sign.
1295
if(len <= (pos+1) || ((!intchars) && (!fracchars)))
1296
return first(0);
1297
++pos; // this was the sign.
1298
// ... so the (pos+1) ensures that we enter the loop and
1299
// hence that there exist chars in the power part
1300
powchars = false;
1301
for( ; pos < len; ++pos)
1302
{
1303
const char c = str[pos];
1304
if(c >= '0' && c <= '9')
1305
powchars = true;
1306
else if(powchars && _is_delim_char(c))
1307
return first(pos);
1308
else
1309
return first(0);
1310
}
1311
return *this;
1312
}
1313
1314
// this function is declared inside the class to avoid a VS error with __declspec(dllimport)
1315
C4_NO_INLINE C4_PURE basic_substring _first_real_span_hex(size_t pos) const noexcept
1316
{
1317
bool intchars = false;
1318
bool fracchars = false;
1319
bool powchars;
1320
// integral part
1321
for( ; pos < len; ++pos)
1322
{
1323
const char c = str[pos];
1324
if(_is_hex_char(c))
1325
{
1326
intchars = true;
1327
}
1328
else if(c == '.')
1329
{
1330
++pos;
1331
goto fractional_part_hex;
1332
}
1333
else if(c == 'p' || c == 'P')
1334
{
1335
++pos;
1336
goto power_part_hex;
1337
}
1338
else if(_is_delim_char(c))
1339
{
1340
return intchars ? first(pos) : first(0);
1341
}
1342
else
1343
{
1344
return first(0);
1345
}
1346
}
1347
// no . or p were found; this is either an integral number
1348
// or not a number at all
1349
return intchars ?
1350
*this :
1351
first(0);
1352
fractional_part_hex:
1353
C4_ASSERT(pos > 0);
1354
C4_ASSERT(str[pos - 1] == '.');
1355
for( ; pos < len; ++pos)
1356
{
1357
const char c = str[pos];
1358
if(_is_hex_char(c))
1359
{
1360
fracchars = true;
1361
}
1362
else if(c == 'p' || c == 'P')
1363
{
1364
++pos;
1365
goto power_part_hex;
1366
}
1367
else if(_is_delim_char(c))
1368
{
1369
return intchars || fracchars ? first(pos) : first(0);
1370
}
1371
else
1372
{
1373
return first(0);
1374
}
1375
}
1376
return intchars || fracchars ?
1377
*this :
1378
first(0);
1379
power_part_hex:
1380
C4_ASSERT(pos > 0);
1381
C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P');
1382
// either a + or a - is expected here, followed by more chars.
1383
// also, using (pos+1) in this check will cause an early
1384
// return when no more chars follow the sign.
1385
if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars)))
1386
return first(0);
1387
++pos; // this was the sign.
1388
// ... so the (pos+1) ensures that we enter the loop and
1389
// hence that there exist chars in the power part
1390
powchars = false;
1391
for( ; pos < len; ++pos)
1392
{
1393
const char c = str[pos];
1394
if(c >= '0' && c <= '9')
1395
powchars = true;
1396
else if(powchars && _is_delim_char(c))
1397
return first(pos);
1398
else
1399
return first(0);
1400
}
1401
return *this;
1402
}
1403
1404
// this function is declared inside the class to avoid a VS error with __declspec(dllimport)
1405
C4_NO_INLINE C4_PURE basic_substring _first_real_span_bin(size_t pos) const noexcept
1406
{
1407
bool intchars = false;
1408
bool fracchars = false;
1409
bool powchars;
1410
// integral part
1411
for( ; pos < len; ++pos)
1412
{
1413
const char c = str[pos];
1414
if(c == '0' || c == '1')
1415
{
1416
intchars = true;
1417
}
1418
else if(c == '.')
1419
{
1420
++pos;
1421
goto fractional_part_bin;
1422
}
1423
else if(c == 'p' || c == 'P')
1424
{
1425
++pos;
1426
goto power_part_bin;
1427
}
1428
else if(_is_delim_char(c))
1429
{
1430
return intchars ? first(pos) : first(0);
1431
}
1432
else
1433
{
1434
return first(0);
1435
}
1436
}
1437
// no . or p were found; this is either an integral number
1438
// or not a number at all
1439
return intchars ?
1440
*this :
1441
first(0);
1442
fractional_part_bin:
1443
C4_ASSERT(pos > 0);
1444
C4_ASSERT(str[pos - 1] == '.');
1445
for( ; pos < len; ++pos)
1446
{
1447
const char c = str[pos];
1448
if(c == '0' || c == '1')
1449
{
1450
fracchars = true;
1451
}
1452
else if(c == 'p' || c == 'P')
1453
{
1454
++pos;
1455
goto power_part_bin;
1456
}
1457
else if(_is_delim_char(c))
1458
{
1459
return intchars || fracchars ? first(pos) : first(0);
1460
}
1461
else
1462
{
1463
return first(0);
1464
}
1465
}
1466
return intchars || fracchars ?
1467
*this :
1468
first(0);
1469
power_part_bin:
1470
C4_ASSERT(pos > 0);
1471
C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P');
1472
// either a + or a - is expected here, followed by more chars.
1473
// also, using (pos+1) in this check will cause an early
1474
// return when no more chars follow the sign.
1475
if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars)))
1476
return first(0);
1477
++pos; // this was the sign.
1478
// ... so the (pos+1) ensures that we enter the loop and
1479
// hence that there exist chars in the power part
1480
powchars = false;
1481
for( ; pos < len; ++pos)
1482
{
1483
const char c = str[pos];
1484
if(c >= '0' && c <= '9')
1485
powchars = true;
1486
else if(powchars && _is_delim_char(c))
1487
return first(pos);
1488
else
1489
return first(0);
1490
}
1491
return *this;
1492
}
1493
1494
// this function is declared inside the class to avoid a VS error with __declspec(dllimport)
1495
C4_NO_INLINE C4_PURE basic_substring _first_real_span_oct(size_t pos) const noexcept
1496
{
1497
bool intchars = false;
1498
bool fracchars = false;
1499
bool powchars;
1500
// integral part
1501
for( ; pos < len; ++pos)
1502
{
1503
const char c = str[pos];
1504
if(c >= '0' && c <= '7')
1505
{
1506
intchars = true;
1507
}
1508
else if(c == '.')
1509
{
1510
++pos;
1511
goto fractional_part_oct;
1512
}
1513
else if(c == 'p' || c == 'P')
1514
{
1515
++pos;
1516
goto power_part_oct;
1517
}
1518
else if(_is_delim_char(c))
1519
{
1520
return intchars ? first(pos) : first(0);
1521
}
1522
else
1523
{
1524
return first(0);
1525
}
1526
}
1527
// no . or p were found; this is either an integral number
1528
// or not a number at all
1529
return intchars ?
1530
*this :
1531
first(0);
1532
fractional_part_oct:
1533
C4_ASSERT(pos > 0);
1534
C4_ASSERT(str[pos - 1] == '.');
1535
for( ; pos < len; ++pos)
1536
{
1537
const char c = str[pos];
1538
if(c >= '0' && c <= '7')
1539
{
1540
fracchars = true;
1541
}
1542
else if(c == 'p' || c == 'P')
1543
{
1544
++pos;
1545
goto power_part_oct;
1546
}
1547
else if(_is_delim_char(c))
1548
{
1549
return intchars || fracchars ? first(pos) : first(0);
1550
}
1551
else
1552
{
1553
return first(0);
1554
}
1555
}
1556
return intchars || fracchars ?
1557
*this :
1558
first(0);
1559
power_part_oct:
1560
C4_ASSERT(pos > 0);
1561
C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P');
1562
// either a + or a - is expected here, followed by more chars.
1563
// also, using (pos+1) in this check will cause an early
1564
// return when no more chars follow the sign.
1565
if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars)))
1566
return first(0);
1567
++pos; // this was the sign.
1568
// ... so the (pos+1) ensures that we enter the loop and
1569
// hence that there exist chars in the power part
1570
powchars = false;
1571
for( ; pos < len; ++pos)
1572
{
1573
const char c = str[pos];
1574
if(c >= '0' && c <= '9')
1575
powchars = true;
1576
else if(powchars && _is_delim_char(c))
1577
return first(pos);
1578
else
1579
return first(0);
1580
}
1581
return *this;
1582
}
1583
1584
/** @} */
1585
1586
public:
1587
1588
/** @name Splitting methods */
1589
/** @{ */
1590
1591
/** returns true if the string has not been exhausted yet, meaning
1592
* it's ok to call next_split() again. When no instance of sep
1593
* exists in the string, returns the full string. When the input
1594
* is an empty string, the output string is the empty string. */
1595
bool next_split(C sep, size_t *C4_RESTRICT start_pos, basic_substring *C4_RESTRICT out) const
1596
{
1597
if(C4_LIKELY(*start_pos < len))
1598
{
1599
for(size_t i = *start_pos; i < len; i++)
1600
{
1601
if(str[i] == sep)
1602
{
1603
out->assign(str + *start_pos, i - *start_pos);
1604
*start_pos = i+1;
1605
return true;
1606
}
1607
}
1608
out->assign(str + *start_pos, len - *start_pos);
1609
*start_pos = len + 1;
1610
return true;
1611
}
1612
else
1613
{
1614
bool valid = len > 0 && (*start_pos == len);
1615
if(valid && str && str[len-1] == sep)
1616
{
1617
out->assign(str + len, size_t(0)); // the cast is needed to prevent overload ambiguity
1618
}
1619
else
1620
{
1621
out->assign(str + len + 1, size_t(0)); // the cast is needed to prevent overload ambiguity
1622
}
1623
*start_pos = len + 1;
1624
return valid;
1625
}
1626
}
1627
1628
private:
1629
1630
struct split_proxy_impl
1631
{
1632
struct split_iterator_impl
1633
{
1634
split_proxy_impl const* m_proxy;
1635
basic_substring m_str;
1636
size_t m_pos;
1637
NCC_ m_sep;
1638
1639
split_iterator_impl(split_proxy_impl const* proxy, size_t pos, C sep)
1640
: m_proxy(proxy), m_pos(pos), m_sep(sep)
1641
{
1642
_tick();
1643
}
1644
1645
void _tick()
1646
{
1647
m_proxy->m_str.next_split(m_sep, &m_pos, &m_str);
1648
}
1649
1650
split_iterator_impl& operator++ () { _tick(); return *this; }
1651
split_iterator_impl operator++ (int) { split_iterator_impl it = *this; _tick(); return it; }
1652
1653
basic_substring& operator* () { return m_str; }
1654
basic_substring* operator-> () { return &m_str; }
1655
1656
bool operator!= (split_iterator_impl const& that) const
1657
{
1658
return !(this->operator==(that));
1659
}
1660
bool operator== (split_iterator_impl const& that) const
1661
{
1662
C4_XASSERT((m_sep == that.m_sep) && "cannot compare split iterators with different separators");
1663
if(m_str.size() != that.m_str.size())
1664
return false;
1665
if(m_str.data() != that.m_str.data())
1666
return false;
1667
return m_pos == that.m_pos;
1668
}
1669
};
1670
1671
basic_substring m_str;
1672
size_t m_start_pos;
1673
C m_sep;
1674
1675
split_proxy_impl(basic_substring str_, size_t start_pos, C sep)
1676
: m_str(str_), m_start_pos(start_pos), m_sep(sep)
1677
{
1678
}
1679
1680
split_iterator_impl begin() const
1681
{
1682
auto it = split_iterator_impl(this, m_start_pos, m_sep);
1683
return it;
1684
}
1685
split_iterator_impl end() const
1686
{
1687
size_t pos = m_str.size() + 1;
1688
auto it = split_iterator_impl(this, pos, m_sep);
1689
return it;
1690
}
1691
};
1692
1693
public:
1694
1695
using split_proxy = split_proxy_impl;
1696
1697
/** a view into the splits */
1698
split_proxy split(C sep, size_t start_pos=0) const
1699
{
1700
C4_XASSERT((start_pos >= 0 && start_pos < len) || empty());
1701
auto ss = sub(0, len);
1702
auto it = split_proxy(ss, start_pos, sep);
1703
return it;
1704
}
1705
1706
public:
1707
1708
/** pop right: return the first split from the right. Use
1709
* gpop_left() to get the reciprocal part.
1710
*/
1711
basic_substring pop_right(C sep=C('/'), bool skip_empty=false) const
1712
{
1713
if(C4_LIKELY(len > 1))
1714
{
1715
auto pos = last_of(sep);
1716
if(pos != npos)
1717
{
1718
if(pos + 1 < len) // does not end with sep
1719
{
1720
return sub(pos + 1); // return from sep to end
1721
}
1722
else // the string ends with sep
1723
{
1724
if( ! skip_empty)
1725
{
1726
return sub(pos + 1, 0);
1727
}
1728
auto ppos = last_not_of(sep); // skip repeated seps
1729
if(ppos == npos) // the string is all made of seps
1730
{
1731
return sub(0, 0);
1732
}
1733
// find the previous sep
1734
auto pos0 = last_of(sep, ppos);
1735
if(pos0 == npos) // only the last sep exists
1736
{
1737
return sub(0); // return the full string (because skip_empty is true)
1738
}
1739
++pos0;
1740
return sub(pos0);
1741
}
1742
}
1743
else // no sep was found, return the full string
1744
{
1745
return *this;
1746
}
1747
}
1748
else if(len == 1)
1749
{
1750
if(begins_with(sep))
1751
{
1752
return sub(0, 0);
1753
}
1754
return *this;
1755
}
1756
else // an empty string
1757
{
1758
return basic_substring();
1759
}
1760
}
1761
1762
/** return the first split from the left. Use gpop_right() to get
1763
* the reciprocal part. */
1764
basic_substring pop_left(C sep = C('/'), bool skip_empty=false) const
1765
{
1766
if(C4_LIKELY(len > 1))
1767
{
1768
auto pos = first_of(sep);
1769
if(pos != npos)
1770
{
1771
if(pos > 0) // does not start with sep
1772
{
1773
return sub(0, pos); // return everything up to it
1774
}
1775
else // the string starts with sep
1776
{
1777
if( ! skip_empty)
1778
{
1779
return sub(0, 0);
1780
}
1781
auto ppos = first_not_of(sep); // skip repeated seps
1782
if(ppos == npos) // the string is all made of seps
1783
{
1784
return sub(0, 0);
1785
}
1786
// find the next sep
1787
auto pos0 = first_of(sep, ppos);
1788
if(pos0 == npos) // only the first sep exists
1789
{
1790
return sub(0); // return the full string (because skip_empty is true)
1791
}
1792
C4_XASSERT(pos0 > 0);
1793
// return everything up to the second sep
1794
return sub(0, pos0);
1795
}
1796
}
1797
else // no sep was found, return the full string
1798
{
1799
return sub(0);
1800
}
1801
}
1802
else if(len == 1)
1803
{
1804
if(begins_with(sep))
1805
{
1806
return sub(0, 0);
1807
}
1808
return sub(0);
1809
}
1810
else // an empty string
1811
{
1812
return basic_substring();
1813
}
1814
}
1815
1816
public:
1817
1818
/** greedy pop left. eg, csubstr("a/b/c").gpop_left('/')="c" */
1819
basic_substring gpop_left(C sep = C('/'), bool skip_empty=false) const
1820
{
1821
auto ss = pop_right(sep, skip_empty);
1822
ss = left_of(ss);
1823
if(ss.find(sep) != npos)
1824
{
1825
if(ss.ends_with(sep))
1826
{
1827
if(skip_empty)
1828
{
1829
ss = ss.trimr(sep);
1830
}
1831
else
1832
{
1833
ss = ss.sub(0, ss.len-1); // safe to subtract because ends_with(sep) is true
1834
}
1835
}
1836
}
1837
return ss;
1838
}
1839
1840
/** greedy pop right. eg, csubstr("a/b/c").gpop_right('/')="a" */
1841
basic_substring gpop_right(C sep = C('/'), bool skip_empty=false) const
1842
{
1843
auto ss = pop_left(sep, skip_empty);
1844
ss = right_of(ss);
1845
if(ss.find(sep) != npos)
1846
{
1847
if(ss.begins_with(sep))
1848
{
1849
if(skip_empty)
1850
{
1851
ss = ss.triml(sep);
1852
}
1853
else
1854
{
1855
ss = ss.sub(1);
1856
}
1857
}
1858
}
1859
return ss;
1860
}
1861
1862
/** @} */
1863
1864
public:
1865
1866
/** @name Path-like manipulation methods */
1867
/** @{ */
1868
1869
basic_substring basename(C sep=C('/')) const
1870
{
1871
auto ss = pop_right(sep, /*skip_empty*/true);
1872
ss = ss.trimr(sep);
1873
return ss;
1874
}
1875
1876
basic_substring dirname(C sep=C('/')) const
1877
{
1878
auto ss = basename(sep);
1879
ss = ss.empty() ? *this : left_of(ss);
1880
return ss;
1881
}
1882
1883
C4_ALWAYS_INLINE basic_substring name_wo_extshort() const
1884
{
1885
return gpop_left('.');
1886
}
1887
1888
C4_ALWAYS_INLINE basic_substring name_wo_extlong() const
1889
{
1890
return pop_left('.');
1891
}
1892
1893
C4_ALWAYS_INLINE basic_substring extshort() const
1894
{
1895
return pop_right('.');
1896
}
1897
1898
C4_ALWAYS_INLINE basic_substring extlong() const
1899
{
1900
return gpop_right('.');
1901
}
1902
1903
/** @} */
1904
1905
public:
1906
1907
/** @name Content-modification methods (only for non-const C) */
1908
/** @{ */
1909
1910
/** convert the string to upper-case
1911
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
1912
C4_REQUIRE_RW(void) toupper()
1913
{
1914
for(size_t i = 0; i < len; ++i)
1915
{
1916
str[i] = static_cast<C>(::toupper(str[i]));
1917
}
1918
}
1919
1920
/** convert the string to lower-case
1921
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
1922
C4_REQUIRE_RW(void) tolower()
1923
{
1924
for(size_t i = 0; i < len; ++i)
1925
{
1926
str[i] = static_cast<C>(::tolower(str[i]));
1927
}
1928
}
1929
1930
public:
1931
1932
/** fill the entire contents with the given @p val
1933
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
1934
C4_REQUIRE_RW(void) fill(C val)
1935
{
1936
for(size_t i = 0; i < len; ++i)
1937
{
1938
str[i] = val;
1939
}
1940
}
1941
1942
public:
1943
1944
/** set the current substring to a copy of the given csubstr
1945
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
1946
C4_REQUIRE_RW(void) copy_from(ro_substr that, size_t ifirst=0, size_t num=npos)
1947
{
1948
C4_ASSERT(ifirst >= 0 && ifirst <= len);
1949
num = num != npos ? num : len - ifirst;
1950
num = num < that.len ? num : that.len;
1951
C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len);
1952
// calling memcpy with null strings is undefined behavior
1953
// and will wreak havoc in calling code's branches.
1954
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
1955
if(num)
1956
memcpy(str + sizeof(C) * ifirst, that.str, sizeof(C) * num);
1957
}
1958
1959
public:
1960
1961
/** reverse in place
1962
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
1963
C4_REQUIRE_RW(void) reverse()
1964
{
1965
if(len == 0) return;
1966
detail::_do_reverse(str, str + len - 1);
1967
}
1968
1969
/** revert a subpart in place
1970
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
1971
C4_REQUIRE_RW(void) reverse_sub(size_t ifirst, size_t num)
1972
{
1973
C4_ASSERT(ifirst >= 0 && ifirst <= len);
1974
C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len);
1975
if(num == 0) return;
1976
detail::_do_reverse(str + ifirst, str + ifirst + num - 1);
1977
}
1978
1979
/** revert a range in place
1980
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
1981
C4_REQUIRE_RW(void) reverse_range(size_t ifirst, size_t ilast)
1982
{
1983
C4_ASSERT(ifirst >= 0 && ifirst <= len);
1984
C4_ASSERT(ilast >= 0 && ilast <= len);
1985
if(ifirst == ilast) return;
1986
detail::_do_reverse(str + ifirst, str + ilast - 1);
1987
}
1988
1989
public:
1990
1991
/** erase part of the string. eg, with char s[] = "0123456789",
1992
* substr(s).erase(3, 2) = "01256789", and s is now "01245678989"
1993
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
1994
C4_REQUIRE_RW(basic_substring) erase(size_t pos, size_t num)
1995
{
1996
C4_ASSERT(pos >= 0 && pos+num <= len);
1997
size_t num_to_move = len - pos - num;
1998
memmove(str + pos, str + pos + num, sizeof(C) * num_to_move);
1999
return basic_substring{str, len - num};
2000
}
2001
2002
/** @note this method requires that the string memory is writeable and is SFINAEd out for const C */
2003
C4_REQUIRE_RW(basic_substring) erase_range(size_t first, size_t last)
2004
{
2005
C4_ASSERT(first <= last);
2006
return erase(first, static_cast<size_t>(last-first));
2007
}
2008
2009
/** erase a part of the string.
2010
* @note @p sub must be a substring of this string
2011
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
2012
C4_REQUIRE_RW(basic_substring) erase(ro_substr sub)
2013
{
2014
C4_ASSERT(is_super(sub));
2015
C4_ASSERT(sub.str >= str);
2016
return erase(static_cast<size_t>(sub.str - str), sub.len);
2017
}
2018
2019
public:
2020
2021
/** replace every occurrence of character @p value with the character @p repl
2022
* @return the number of characters that were replaced
2023
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
2024
C4_REQUIRE_RW(size_t) replace(C value, C repl, size_t pos=0)
2025
{
2026
C4_ASSERT((pos >= 0 && pos <= len) || pos == npos);
2027
size_t did_it = 0;
2028
while((pos = find(value, pos)) != npos)
2029
{
2030
str[pos++] = repl;
2031
++did_it;
2032
}
2033
return did_it;
2034
}
2035
2036
/** replace every occurrence of each character in @p value with
2037
* the character @p repl.
2038
* @return the number of characters that were replaced
2039
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
2040
C4_REQUIRE_RW(size_t) replace(ro_substr chars, C repl, size_t pos=0)
2041
{
2042
C4_ASSERT((pos >= 0 && pos <= len) || pos == npos);
2043
size_t did_it = 0;
2044
while((pos = first_of(chars, pos)) != npos)
2045
{
2046
str[pos++] = repl;
2047
++did_it;
2048
}
2049
return did_it;
2050
}
2051
2052
/** replace @p pattern with @p repl, and write the result into
2053
* @dst. pattern and repl don't need equal sizes.
2054
*
2055
* @return the required size for dst. No overflow occurs if
2056
* dst.len is smaller than the required size; this can be used to
2057
* determine the required size for an existing container. */
2058
size_t replace_all(rw_substr dst, ro_substr pattern, ro_substr repl, size_t pos=0) const
2059
{
2060
C4_ASSERT( ! pattern.empty()); //!< @todo relax this precondition
2061
C4_ASSERT( ! this ->overlaps(dst)); //!< @todo relax this precondition
2062
C4_ASSERT( ! pattern.overlaps(dst));
2063
C4_ASSERT( ! repl .overlaps(dst));
2064
C4_ASSERT((pos >= 0 && pos <= len) || pos == npos);
2065
C4_SUPPRESS_WARNING_GCC_PUSH
2066
C4_SUPPRESS_WARNING_GCC("-Warray-bounds") // gcc11 has a false positive here
2067
#if (!defined(__clang__)) && (defined(__GNUC__) && (__GNUC__ >= 7))
2068
C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc11 has a false positive here
2069
#endif
2070
#define _c4append(first, last) \
2071
{ \
2072
C4_ASSERT((last) >= (first)); \
2073
size_t num = static_cast<size_t>((last) - (first)); \
2074
if(num > 0 && sz + num <= dst.len) \
2075
{ \
2076
memcpy(dst.str + sz, first, num * sizeof(C)); \
2077
} \
2078
sz += num; \
2079
}
2080
size_t sz = 0;
2081
size_t b = pos;
2082
_c4append(str, str + pos);
2083
do {
2084
size_t e = find(pattern, b);
2085
if(e == npos)
2086
{
2087
_c4append(str + b, str + len);
2088
break;
2089
}
2090
_c4append(str + b, str + e);
2091
_c4append(repl.begin(), repl.end());
2092
b = e + pattern.size();
2093
} while(b < len && b != npos);
2094
return sz;
2095
#undef _c4append
2096
C4_SUPPRESS_WARNING_GCC_POP
2097
}
2098
2099
/** @} */
2100
2101
}; // template class basic_substring
2102
2103
2104
#undef C4_REQUIRE_RW
2105
2106
2107
//-----------------------------------------------------------------------------
2108
//-----------------------------------------------------------------------------
2109
//-----------------------------------------------------------------------------
2110
2111
2112
/** @name Adapter functions. to_substr() and to_csubstr() is used in
2113
* generic code like format(), and allow adding construction of
2114
* substrings from new types like containers. */
2115
/** @{ */
2116
2117
2118
/** neutral version for use in generic code */
2119
C4_ALWAYS_INLINE substr to_substr(substr s) noexcept { return s; }
2120
/** neutral version for use in generic code */
2121
C4_ALWAYS_INLINE csubstr to_csubstr(substr s) noexcept { return s; }
2122
/** neutral version for use in generic code */
2123
C4_ALWAYS_INLINE csubstr to_csubstr(csubstr s) noexcept { return s; }
2124
2125
2126
template<size_t N>
2127
C4_ALWAYS_INLINE substr
2128
to_substr(char (&s)[N]) noexcept { substr ss(s, N-1); return ss; }
2129
template<size_t N>
2130
C4_ALWAYS_INLINE csubstr
2131
to_csubstr(const char (&s)[N]) noexcept { csubstr ss(s, N-1); return ss; }
2132
2133
2134
/** @note this overload uses SFINAE to prevent it from overriding the array overload
2135
* @see For a more detailed explanation on why the plain overloads cannot
2136
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
2137
template<class U>
2138
C4_ALWAYS_INLINE typename std::enable_if<std::is_same<U, char*>::value, substr>::type
2139
to_substr(U s) noexcept { substr ss(s); return ss; }
2140
/** @note this overload uses SFINAE to prevent it from overriding the array overload
2141
* @see For a more detailed explanation on why the plain overloads cannot
2142
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
2143
template<class U>
2144
C4_ALWAYS_INLINE typename std::enable_if<std::is_same<U, const char*>::value || std::is_same<U, char*>::value, csubstr>::type
2145
to_csubstr(U s) noexcept { csubstr ss(s); return ss; }
2146
2147
2148
/** @} */
2149
2150
2151
//-----------------------------------------------------------------------------
2152
//-----------------------------------------------------------------------------
2153
//-----------------------------------------------------------------------------
2154
2155
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; }
2156
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; }
2157
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; }
2158
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; }
2159
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; }
2160
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; }
2161
2162
template<typename C> inline bool operator== (const char c, basic_substring<C> const that) noexcept { return that.compare(c) == 0; }
2163
template<typename C> inline bool operator!= (const char c, basic_substring<C> const that) noexcept { return that.compare(c) != 0; }
2164
template<typename C> inline bool operator< (const char c, basic_substring<C> const that) noexcept { return that.compare(c) > 0; }
2165
template<typename C> inline bool operator> (const char c, basic_substring<C> const that) noexcept { return that.compare(c) < 0; }
2166
template<typename C> inline bool operator<= (const char c, basic_substring<C> const that) noexcept { return that.compare(c) >= 0; }
2167
template<typename C> inline bool operator>= (const char c, basic_substring<C> const that) noexcept { return that.compare(c) <= 0; }
2168
2169
2170
//-----------------------------------------------------------------------------
2171
//-----------------------------------------------------------------------------
2172
//-----------------------------------------------------------------------------
2173
2174
/** @define C4_SUBSTR_NO_OSTREAM_LSHIFT doctest does not deal well with
2175
* template operator<<
2176
* @see https://github.com/onqtam/doctest/pull/431 */
2177
#ifndef C4_SUBSTR_NO_OSTREAM_LSHIFT
2178
#ifdef __clang__
2179
# pragma clang diagnostic push
2180
# pragma clang diagnostic ignored "-Wsign-conversion"
2181
#elif defined(__GNUC__)
2182
# pragma GCC diagnostic push
2183
# pragma GCC diagnostic ignored "-Wsign-conversion"
2184
#endif
2185
2186
/** output the string to a stream */
2187
template<class OStream, class C>
2188
inline OStream& operator<< (OStream& os, basic_substring<C> s)
2189
{
2190
os.write(s.str, s.len);
2191
return os;
2192
}
2193
2194
// this causes ambiguity
2195
///** this is used by google test */
2196
//template<class OStream, class C>
2197
//inline void PrintTo(basic_substring<C> s, OStream* os)
2198
//{
2199
// os->write(s.str, s.len);
2200
//}
2201
2202
#ifdef __clang__
2203
# pragma clang diagnostic pop
2204
#elif defined(__GNUC__)
2205
# pragma GCC diagnostic pop
2206
#endif
2207
#endif // !C4_SUBSTR_NO_OSTREAM_LSHIFT
2208
2209
} // namespace c4
2210
2211
2212
#ifdef __clang__
2213
# pragma clang diagnostic pop
2214
#elif defined(__GNUC__)
2215
# pragma GCC diagnostic pop
2216
#endif
2217
2218
#endif /* _C4_SUBSTR_HPP_ */
2219
2220