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