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