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