Coverage Report

Created: 2026-07-30 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmString.hxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#pragma once
4
5
#include "cmConfigure.h" // IWYU pragma: keep
6
7
#include <algorithm>
8
#include <cstddef>
9
#include <initializer_list>
10
#include <memory>
11
#include <ostream>
12
#include <string>
13
#include <type_traits>
14
#include <utility>
15
16
#include <cm/string_view>
17
#include <cmext/string_view>
18
19
#include <cm3p/rapidhash.h>
20
21
namespace cm {
22
23
class String;
24
25
/**
26
 * Trait to convert type T into a String.
27
 * Implementations must derive from 'std::true_type'
28
 * and define an 'into_string' member that accepts
29
 * type T (by value or reference) and returns one of:
30
 *
31
 * - 'std::string' to construct an owned instance.
32
 * - 'cm::string_view' to construct a borrowed or null instances.
33
 *   The buffer from which the view is borrowed must outlive
34
 *   all copies of the resulting String, e.g. static storage.
35
 * - 'cm::String' for already-constructed instances.
36
 */
37
template <typename T>
38
struct IntoString : std::false_type
39
{
40
};
41
42
template <typename T>
43
struct IntoString<T&> : IntoString<T>
44
{
45
};
46
47
template <typename T>
48
struct IntoString<T const> : IntoString<T>
49
{
50
};
51
52
template <typename T>
53
struct IntoString<T const*> : IntoString<T*>
54
{
55
};
56
57
template <typename T, std::string::size_type N>
58
struct IntoString<T const[N]> : IntoString<T[N]>
59
{
60
};
61
62
template <>
63
struct IntoString<char*> : std::true_type
64
{
65
  static String into_string(char const* s);
66
};
67
68
template <>
69
struct IntoString<std::nullptr_t> : std::true_type
70
{
71
0
  static string_view into_string(std::nullptr_t) { return string_view(); }
72
};
73
74
template <std::string::size_type N>
75
struct IntoString<char[N]> : std::true_type
76
{
77
  static std::string into_string(char const (&s)[N])
78
  {
79
    return std::string(s, N - 1);
80
  }
81
};
82
83
template <>
84
struct IntoString<std::string> : std::true_type
85
{
86
98
  static std::string into_string(std::string s) { return s; }
87
};
88
89
template <>
90
struct IntoString<char> : std::true_type
91
{
92
0
  static std::string into_string(char c) { return std::string(1, c); }
93
};
94
95
/**
96
 * Trait to convert type T into a 'cm::string_view'.
97
 * Implementations must derive from 'std::true_type' and
98
 * define a 'view' member that accepts type T (by reference)
99
 * and returns a 'cm::string_view'.
100
 */
101
template <typename T>
102
struct AsStringView : std::false_type
103
{
104
};
105
106
template <typename T>
107
struct AsStringView<T&> : AsStringView<T>
108
{
109
};
110
111
template <typename T>
112
struct AsStringView<T const> : AsStringView<T>
113
{
114
};
115
116
template <typename T>
117
struct AsStringView<T const*> : AsStringView<T*>
118
{
119
};
120
121
template <typename T, std::string::size_type N>
122
struct AsStringView<T const[N]> : AsStringView<T[N]>
123
{
124
};
125
126
template <>
127
struct AsStringView<char*> : std::true_type
128
{
129
0
  static string_view view(char const* s) { return s; }
130
};
131
132
template <std::string::size_type N>
133
struct AsStringView<char[N]> : std::true_type
134
{
135
  static string_view view(char const (&s)[N]) { return string_view(s, N - 1); }
136
};
137
138
template <>
139
struct AsStringView<std::string> : std::true_type
140
{
141
0
  static string_view view(std::string const& s) { return s; }
142
};
143
144
template <>
145
struct AsStringView<char> : std::true_type
146
{
147
  static string_view view(
148
    char const& s) // clazy:exclude=function-args-by-value
149
0
  {
150
0
    return string_view(&s, 1);
151
0
  }
152
};
153
154
template <>
155
struct AsStringView<string_view> : std::true_type
156
{
157
0
  static string_view view(string_view s) { return s; }
158
};
159
160
template <>
161
struct AsStringView<static_string_view> : std::true_type
162
{
163
  static string_view view(
164
    static_string_view const& s) // clazy:exclude=function-args-by-value
165
0
  {
166
0
    return s;
167
0
  }
168
};
169
170
template <>
171
struct AsStringView<String> : std::true_type
172
{
173
  static string_view view(String const& s);
174
};
175
176
/**
177
 * \class String
178
 *
179
 * A custom string type that holds a view of a string buffer
180
 * and optionally shares ownership of the buffer.  Instances
181
 * may have one of the following states:
182
 *
183
 * - null: views and owns nothing.
184
 *   Conversion to 'bool' is 'false'.
185
 *   'data()' and 'c_str()' return nullptr.
186
 *   'size()' returns 0.
187
 *   'str()' returns an empty string.
188
 *
189
 * - borrowed: views a string but does not own it.  This is used
190
 *   to bind to static storage (e.g. string literals) or for
191
 *   temporary instances that do not outlive the borrowed buffer.
192
 *   Copies and substrings still borrow the original buffer.
193
 *   Mutation allocates a new internal string and converts to
194
 *   the 'owned' state.
195
 *   Conversion to 'bool' is 'true'.
196
 *   'c_str()' may internally mutate to the 'owned' state.
197
 *   'str()' internally mutates to the 'owned' state.
198
 *
199
 * - owned: views an immutable 'std::string' instance owned internally.
200
 *   Copies and substrings share ownership of the internal string.
201
 *   Mutation allocates a new internal string.
202
 *   Conversion to 'bool' is 'true'.
203
 */
204
class String
205
{
206
  enum class Private
207
  {
208
  };
209
210
public:
211
  using traits_type = std::string::traits_type;
212
  using value_type = string_view::value_type;
213
  using pointer = string_view::pointer;
214
  using const_pointer = string_view::const_pointer;
215
  using reference = string_view::reference;
216
  using const_reference = string_view::const_reference;
217
  using const_iterator = string_view::const_iterator;
218
  using iterator = string_view::const_iterator;
219
  using const_reverse_iterator = string_view::const_reverse_iterator;
220
  using reverse_iterator = string_view::const_reverse_iterator;
221
  using difference_type = string_view::difference_type;
222
  using size_type = string_view::size_type;
223
224
  static size_type const npos = string_view::npos;
225
226
  /** Construct a null string.  */
227
95
  String() = default;
228
229
  /** Construct from any type implementing the IntoString trait.  */
230
  template <typename T,
231
            typename = typename std::enable_if<IntoString<T>::value>::type>
232
  String(T&& s)
233
98
    : String(IntoString<T>::into_string(std::forward<T>(s)), Private())
234
98
  {
235
98
  }
Unexecuted instantiation: cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Unexecuted instantiation: cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
Unexecuted instantiation: cm::String::String<char*, void>(char*&&)
cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Line
Count
Source
233
98
    : String(IntoString<T>::into_string(std::forward<T>(s)), Private())
234
98
  {
235
98
  }
236
237
  /**
238
   * Construct via static_string_view constructor.
239
   * explicit is required to avoid ambiguous overloaded operators (i.e ==,
240
   * etc...) with the ones provided by string_view.
241
   */
242
  explicit String(static_string_view s)
243
    : String(s, Private())
244
0
  {
245
0
  }
246
  /**
247
   * Construct via string_view constructor.
248
   * explicit is required to avoid ambiguous overloaded operators (i.e ==,
249
   * etc...) with the ones provided by string_view.
250
   */
251
  explicit String(string_view s)
252
97
    : String(std::string(s), Private())
253
97
  {
254
97
  }
255
256
  /** Construct via std::string initializer list constructor.  */
257
  String(std::initializer_list<char> il)
258
    : String(std::string(il))
259
0
  {
260
0
  }
261
262
  /** Construct by copying the specified buffer.  */
263
  String(char const* d, size_type s)
264
0
    : String(std::string(d, s))
265
0
  {
266
0
  }
267
268
  /** Construct by copying from input iterator range.  */
269
  template <typename InputIterator>
270
  String(InputIterator first, InputIterator last)
271
    : String(std::string(first, last))
272
  {
273
  }
274
275
  /** Construct a string with 'n' copies of character 'c'.  */
276
  String(size_type n, char c)
277
    : String(std::string(n, c))
278
0
  {
279
0
  }
280
281
  /** Construct from a substring of another String instance.
282
      This shares ownership of the other string's buffer
283
      but views only a substring.  */
284
  String(String const& s, size_type pos, size_type count = npos)
285
0
    : string_(s.string_)
286
0
    , view_(s.data() + pos, std::min(count, s.size() - pos))
287
0
  {
288
0
  }
289
290
  /** Construct by moving from another String instance.
291
      The other instance is left as a null string.  */
292
  String(String&& s) noexcept
293
94
    : string_(std::move(s.string_))
294
94
    , view_(s.view_)
295
94
  {
296
94
    s.view_ = string_view();
297
94
  }
298
299
  /** Construct by copying from another String instance.
300
      This shares ownership of the other string's buffer.  */
301
0
  String(String const&) noexcept = default;
302
303
392
  ~String() = default;
304
305
  /** Construct by borrowing an externally-owned buffer.  The buffer
306
      must outlive the returned instance and all copies of it.  */
307
8
  static String borrow(string_view v) { return String(v, Private()); }
308
309
  /** Assign by moving from another String instance.
310
      The other instance is left as a null string.  */
311
  String& operator=(String&& s) noexcept
312
98
  {
313
98
    this->string_ = std::move(s.string_);
314
98
    this->view_ = s.view_;
315
98
    s.view_ = string_view();
316
98
    return *this;
317
98
  }
318
319
  /** Assign by copying from another String instance.
320
      This shares ownership of the other string's buffer.  */
321
  String& operator=(String const&) noexcept = default;
322
323
  String& operator=(static_string_view s)
324
0
  {
325
0
    *this = String(s);
326
0
    return *this;
327
0
  }
328
  String& operator=(string_view s)
329
0
  {
330
0
    *this = String(s);
331
0
    return *this;
332
0
  }
333
334
  /** Assign from any type implementing the IntoString trait.  */
335
  template <typename T>
336
  typename // NOLINT(*)
337
    std::enable_if<IntoString<T>::value, String&>::type
338
    operator=(T&& s)
339
0
  {
340
0
    *this = String(std::forward<T>(s));
341
0
    return *this;
342
0
  }
Unexecuted instantiation: _ZN2cm6StringaSINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEENS2_9enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOSA_
Unexecuted instantiation: _ZN2cm6StringaSIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEENS2_9enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOSB_
Unexecuted instantiation: _ZN2cm6StringaSIPcEENSt3__19enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOS5_
343
344
  /** Assign via std::string initializer list constructor.  */
345
  String& operator=(std::initializer_list<char> il)
346
0
  {
347
0
    *this = String(il);
348
0
    return *this;
349
0
  }
350
351
  /** Return true if the instance is not a null string.  */
352
8
  explicit operator bool() const noexcept { return this->data() != nullptr; }
353
354
  /** Return a view of the string.  */
355
126
  string_view view() const noexcept { return this->view_; }
356
0
  operator string_view() const noexcept { return this->view(); }
357
358
  /** Return true if the instance is an empty stringn or null string.  */
359
0
  bool empty() const noexcept { return this->view_.empty(); }
360
361
  /** Return a pointer to the start of the string.  */
362
20
  char const* data() const noexcept { return this->view_.data(); }
363
364
  /** Return the length of the string in bytes.  */
365
6
  size_type size() const noexcept { return this->view_.size(); }
366
0
  size_type length() const noexcept { return this->view_.length(); }
367
368
  /** Return the character at the given position.
369
      No bounds checking is performed.  */
370
0
  char operator[](size_type pos) const noexcept { return this->view_[pos]; }
371
372
  /** Return the character at the given position.
373
      If the position is out of bounds, throws std::out_of_range.  */
374
0
  char at(size_type pos) const { return this->view_.at(pos); }
375
376
0
  char front() const noexcept { return this->view_.front(); }
377
378
0
  char back() const noexcept { return this->view_.back(); }
379
380
  /** Return true if this instance is stable and otherwise false.
381
      An instance is stable if it is in the 'null' state or if it is
382
      an 'owned' state not produced by substring operations, or
383
      after a call to 'stabilize()' or 'str()'.  */
384
  bool is_stable() const;
385
386
  /** If 'is_stable()' does not return true, mutate so it does.  */
387
  void stabilize();
388
389
  /** Get a pointer to a normal std::string if 'is_stable()' returns
390
      true and otherwise nullptr.  The pointer is valid until this
391
      instance is mutated or destroyed.  */
392
  std::string const* str_if_stable() const;
393
394
  /** Get a reference to a normal std::string.  The reference
395
      is valid until this instance is mutated or destroyed.  */
396
  std::string const& str();
397
398
  /** Get a pointer to a C-style null-terminated string
399
      containing the same value as this instance.  The pointer
400
      is valid until this instance is mutated, destroyed,
401
      or str() is called.  */
402
  char const* c_str();
403
404
0
  const_iterator begin() const noexcept { return this->view_.begin(); }
405
0
  const_iterator end() const noexcept { return this->view_.end(); }
406
0
  const_iterator cbegin() const noexcept { return this->begin(); }
407
0
  const_iterator cend() const noexcept { return this->end(); }
408
409
  const_reverse_iterator rbegin() const noexcept
410
0
  {
411
0
    return this->view_.rbegin();
412
0
  }
413
0
  const_reverse_iterator rend() const noexcept { return this->view_.rend(); }
414
0
  const_reverse_iterator crbegin() const noexcept { return this->rbegin(); }
415
0
  const_reverse_iterator crend() const noexcept { return this->rend(); }
416
417
  /** Append to the string using any type that implements the
418
      AsStringView trait.  */
419
  template <typename T>
420
  typename std::enable_if<AsStringView<T>::value, String&>::type operator+=(
421
    T&& s)
422
  {
423
    string_view v = AsStringView<T>::view(std::forward<T>(s));
424
    std::string r;
425
    r.reserve(this->size() + v.size());
426
    r.assign(this->data(), this->size());
427
    r.append(v.data(), v.size());
428
    return *this = std::move(r);
429
  }
430
  template <typename T>
431
  typename std::enable_if<AsStringView<T>::value, String&>::type append(T&& s)
432
0
  {
433
0
    string_view v = AsStringView<T>::view(std::forward<T>(s));
434
0
    std::string r;
435
0
    r.reserve(this->size() + v.size());
436
0
    r.assign(this->data(), this->size());
437
0
    r.append(v.data(), v.size());
438
0
    return *this = std::move(r);
439
0
  }
440
441
  /** Assign to an empty string.  */
442
0
  void clear() { *this = ""_s; }
443
444
  /** Insert 'count' copies of 'ch' at position 'index'.  */
445
  String& insert(size_type index, size_type count, char ch);
446
447
  /** Insert into the string using any type that implements the
448
      AsStringView trait.  */
449
  template <typename T>
450
  typename std::enable_if<AsStringView<T>::value, String&>::type insert(
451
    size_type index, T&& s)
452
0
  {
453
0
    string_view v = AsStringView<T>::view(std::forward<T>(s));
454
0
    std::string r;
455
0
    r.reserve(this->size() + v.size());
456
0
    r.assign(this->data(), this->size());
457
0
    r.insert(index, v.data(), v.size());
458
0
    return *this = std::move(r);
459
0
  }
460
461
  /** Erase 'count' characters starting at position 'index'.  */
462
  String& erase(size_type index = 0, size_type count = npos);
463
464
  void push_back(char ch)
465
0
  {
466
0
    std::string s;
467
0
    s.reserve(this->size() + 1);
468
0
    s.assign(this->data(), this->size());
469
0
    s.push_back(ch);
470
0
    *this = std::move(s);
471
0
  }
472
473
0
  void pop_back() { *this = String(*this, 0, this->size() - 1); }
474
475
  template <typename T>
476
  typename std::enable_if<AsStringView<T>::value, String&>::type replace(
477
    size_type pos, size_type count, T&& s)
478
  {
479
    const_iterator first = this->begin() + pos;
480
    const_iterator last = first + count;
481
    return this->replace(first, last, std::forward<T>(s));
482
  }
483
484
  template <typename InputIterator>
485
  String& replace(const_iterator first, const_iterator last,
486
                  InputIterator first2, InputIterator last2)
487
  {
488
    std::string out;
489
    out.append(this->view_.begin(), first);
490
    out.append(first2, last2);
491
    out.append(last, this->view_.end());
492
    return *this = std::move(out);
493
  }
494
495
  template <typename T>
496
  typename std::enable_if<AsStringView<T>::value, String&>::type replace(
497
    const_iterator first, const_iterator last, T&& s)
498
  {
499
    string_view v = AsStringView<T>::view(std::forward<T>(s));
500
    std::string out;
501
    out.reserve((first - this->view_.begin()) + v.size() +
502
                (this->view_.end() - last));
503
    out.append(this->view_.begin(), first);
504
    out.append(v.data(), v.size());
505
    out.append(last, this->view_.end());
506
    return *this = std::move(out);
507
  }
508
509
  template <typename T>
510
  typename std::enable_if<AsStringView<T>::value, String&>::type replace(
511
    size_type pos, size_type count, T&& s, size_type pos2,
512
    size_type count2 = npos)
513
  {
514
    string_view v = AsStringView<T>::view(std::forward<T>(s));
515
    v = v.substr(pos2, count2);
516
    return this->replace(pos, count, v);
517
  }
518
519
  String& replace(size_type pos, size_type count, size_type count2, char ch)
520
0
  {
521
0
    const_iterator first = this->begin() + pos;
522
0
    const_iterator last = first + count;
523
0
    return this->replace(first, last, count2, ch);
524
0
  }
525
526
  String& replace(const_iterator first, const_iterator last, size_type count2,
527
                  char ch)
528
0
  {
529
0
    std::string out;
530
0
    out.reserve(static_cast<size_type>(first - this->view_.begin()) + count2 +
531
0
                static_cast<size_type>(this->view_.end() - last));
532
0
    out.append(this->view_.begin(), first);
533
0
    out.append(count2, ch);
534
0
    out.append(last, this->view_.end());
535
0
    return *this = std::move(out);
536
0
  }
537
538
  size_type copy(char* dest, size_type count, size_type pos = 0) const;
539
540
0
  void resize(size_type count) { this->resize(count, char()); }
541
542
  void resize(size_type count, char ch)
543
0
  {
544
0
    std::string s;
545
0
    s.reserve(count);
546
0
    if (count <= this->size()) {
547
0
      s.assign(this->data(), count);
548
0
    } else {
549
0
      s.assign(this->data(), this->size());
550
0
      s.resize(count, ch);
551
0
    }
552
0
    *this = std::move(s);
553
0
  }
554
555
  void swap(String& other) noexcept
556
0
  {
557
0
    std::swap(this->string_, other.string_);
558
0
    std::swap(this->view_, other.view_);
559
0
  }
560
561
  /** Return a substring starting at position 'pos' and
562
      consisting of at most 'count' characters.  */
563
  String substr(size_type pos = 0, size_type count = npos) const;
564
565
  template <typename T>
566
  typename std::enable_if<AsStringView<T>::value, int>::type compare(
567
    T&& s) const
568
  {
569
    return this->view_.compare(AsStringView<T>::view(std::forward<T>(s)));
570
  }
571
572
  int compare(size_type pos1, size_type count1, string_view v) const
573
0
  {
574
0
    return this->view_.compare(pos1, count1, v);
575
0
  }
576
577
  int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
578
              size_type count2) const
579
0
  {
580
0
    return this->view_.compare(pos1, count1, v, pos2, count2);
581
0
  }
582
583
  int compare(size_type pos1, size_type count1, char const* s) const
584
0
  {
585
0
    return this->view_.compare(pos1, count1, s);
586
0
  }
587
588
  int compare(size_type pos1, size_type count1, char const* s,
589
              size_type count2) const
590
0
  {
591
0
    return this->view_.compare(pos1, count1, s, count2);
592
0
  }
593
594
  template <typename T>
595
  typename std::enable_if<AsStringView<T>::value, size_type>::type find(
596
    T&& s, size_type pos = 0) const
597
0
  {
598
0
    string_view v = AsStringView<T>::view(std::forward<T>(s));
599
0
    return this->view_.find(v, pos);
600
0
  }
601
602
  size_type find(char const* s, size_type pos, size_type count) const
603
0
  {
604
0
    return this->view_.find(s, pos, count);
605
0
  }
606
607
  template <typename T>
608
  typename std::enable_if<AsStringView<T>::value, size_type>::type rfind(
609
    T&& s, size_type pos = npos) const
610
0
  {
611
0
    string_view v = AsStringView<T>::view(std::forward<T>(s));
612
0
    return this->view_.rfind(v, pos);
613
0
  }
614
615
  size_type rfind(char const* s, size_type pos, size_type count) const
616
0
  {
617
0
    return this->view_.rfind(s, pos, count);
618
0
  }
619
620
  template <typename T>
621
  typename std::enable_if<AsStringView<T>::value, size_type>::type
622
  find_first_of(T&& s, size_type pos = 0) const
623
  {
624
    string_view v = AsStringView<T>::view(std::forward<T>(s));
625
    return this->view_.find_first_of(v, pos);
626
  }
627
628
  size_type find_first_of(char const* s, size_type pos, size_type count) const
629
0
  {
630
0
    return this->view_.find_first_of(s, pos, count);
631
0
  }
632
633
  template <typename T>
634
  typename std::enable_if<AsStringView<T>::value, size_type>::type
635
  find_first_not_of(T&& s, size_type pos = 0) const
636
  {
637
    string_view v = AsStringView<T>::view(std::forward<T>(s));
638
    return this->view_.find_first_not_of(v, pos);
639
  }
640
641
  size_type find_first_not_of(char const* s, size_type pos,
642
                              size_type count) const
643
0
  {
644
0
    return this->view_.find_first_not_of(s, pos, count);
645
0
  }
646
647
  template <typename T>
648
  typename std::enable_if<AsStringView<T>::value, size_type>::type
649
  find_last_of(T&& s, size_type pos = npos) const
650
  {
651
    string_view v = AsStringView<T>::view(std::forward<T>(s));
652
    return this->view_.find_last_of(v, pos);
653
  }
654
655
  size_type find_last_of(char const* s, size_type pos, size_type count) const
656
0
  {
657
0
    return this->view_.find_last_of(s, pos, count);
658
0
  }
659
660
  template <typename T>
661
  typename std::enable_if<AsStringView<T>::value, size_type>::type
662
  find_last_not_of(T&& s, size_type pos = npos) const
663
  {
664
    string_view v = AsStringView<T>::view(std::forward<T>(s));
665
    return this->view_.find_last_not_of(v, pos);
666
  }
667
668
  size_type find_last_not_of(char const* s, size_type pos,
669
                             size_type count) const
670
0
  {
671
0
    return this->view_.find_last_not_of(s, pos, count);
672
0
  }
673
674
private:
675
  // Internal constructor to move from existing String.
676
  String(String&& s, Private) noexcept
677
0
    : String(std::move(s))
678
0
  {
679
0
  }
680
681
  // Internal constructor for dynamically allocated string.
682
  String(std::string&& s, Private);
683
684
  // Internal constructor for view of statically allocated string.
685
  String(string_view v, Private)
686
8
    : view_(v)
687
8
  {
688
8
  }
689
690
  void internally_mutate_to_stable_string();
691
692
  std::shared_ptr<std::string const> string_;
693
  string_view view_;
694
};
695
696
/**
697
 * Trait for comparable types.
698
 */
699
template <typename T>
700
struct IsComparable : std::false_type
701
{
702
};
703
704
template <typename T>
705
struct IsComparable<T&> : IsComparable<T>
706
{
707
};
708
709
template <typename T>
710
struct IsComparable<T const> : IsComparable<T>
711
{
712
};
713
714
template <typename T>
715
struct IsComparable<T const*> : IsComparable<T*>
716
{
717
};
718
719
template <typename T, std::string::size_type N>
720
struct IsComparable<T const[N]> : IsComparable<T[N]>
721
{
722
};
723
724
template <>
725
struct IsComparable<char*> : std::true_type
726
{
727
};
728
729
template <std::string::size_type N>
730
struct IsComparable<char[N]> : std::true_type
731
{
732
};
733
734
template <>
735
struct IsComparable<std::string> : std::true_type
736
{
737
};
738
739
template <>
740
struct IsComparable<char> : std::true_type
741
{
742
};
743
744
/** comparison operators */
745
inline bool operator==(String const& l, String const& r)
746
10
{
747
10
  return l.view() == r.view();
748
10
}
749
template <typename L>
750
typename std::enable_if<IsComparable<L>::value, bool>::type operator==(
751
  L&& l, String const& r)
752
{
753
  return AsStringView<L>::view(std::forward<L>(l)) == r.view();
754
}
755
template <typename R>
756
typename std::enable_if<IsComparable<R>::value, bool>::type operator==(
757
  String const& l, R&& r)
758
{
759
  return l.view() == AsStringView<R>::view(std::forward<R>(r));
760
}
761
762
inline bool operator!=(String const& l, String const& r)
763
0
{
764
0
  return l.view() != r.view();
765
0
}
766
template <typename L>
767
typename std::enable_if<IsComparable<L>::value, bool>::type operator!=(
768
  L&& l, String const& r)
769
{
770
  return AsStringView<L>::view(std::forward<L>(l)) != r.view();
771
}
772
template <typename R>
773
typename std::enable_if<IsComparable<R>::value, bool>::type operator!=(
774
  String const& l, R&& r)
775
{
776
  return l.view() != AsStringView<R>::view(std::forward<R>(r));
777
}
778
779
inline bool operator<(String const& l, String const& r)
780
0
{
781
0
  return l.view() < r.view();
782
0
}
783
template <typename L>
784
typename std::enable_if<IsComparable<L>::value, bool>::type operator<(
785
  L&& l, String const& r)
786
{
787
  return AsStringView<L>::view(std::forward<L>(l)) < r.view();
788
}
789
template <typename R>
790
typename std::enable_if<IsComparable<R>::value, bool>::type operator<(
791
  String const& l, R&& r)
792
{
793
  return l.view() < AsStringView<R>::view(std::forward<R>(r));
794
}
795
796
inline bool operator<=(String const& l, String const& r)
797
0
{
798
0
  return l.view() <= r.view();
799
0
}
800
template <typename L>
801
typename std::enable_if<IsComparable<L>::value, bool>::type operator<=(
802
  L&& l, String const& r)
803
{
804
  return AsStringView<L>::view(std::forward<L>(l)) <= r.view();
805
}
806
template <typename R>
807
typename std::enable_if<IsComparable<R>::value, bool>::type operator<=(
808
  String const& l, R&& r)
809
{
810
  return l.view() <= AsStringView<R>::view(std::forward<R>(r));
811
}
812
813
inline bool operator>(String const& l, String const& r)
814
0
{
815
0
  return l.view() > r.view();
816
0
}
817
template <typename L>
818
typename std::enable_if<IsComparable<L>::value, bool>::type operator>(
819
  L&& l, String const& r)
820
{
821
  return AsStringView<L>::view(std::forward<L>(l)) > r.view();
822
}
823
template <typename R>
824
typename std::enable_if<IsComparable<R>::value, bool>::type operator>(
825
  String const& l, R&& r)
826
{
827
  return l.view() > AsStringView<R>::view(std::forward<R>(r));
828
}
829
830
inline bool operator>=(String const& l, String const& r)
831
0
{
832
0
  return l.view() >= r.view();
833
0
}
834
template <typename L>
835
typename std::enable_if<IsComparable<L>::value, bool>::type operator>=(
836
  L&& l, String const& r)
837
{
838
  return AsStringView<L>::view(std::forward<L>(l)) >= r.view();
839
}
840
template <typename R>
841
typename std::enable_if<IsComparable<R>::value, bool>::type operator>=(
842
  String const& l, R&& r)
843
{
844
  return l.view() >= AsStringView<R>::view(std::forward<R>(r));
845
}
846
847
std::ostream& operator<<(std::ostream& os, String const& s);
848
std::string& operator+=(std::string& self, String const& s);
849
850
template <typename L, typename R>
851
struct StringOpPlus
852
{
853
  L l;
854
  R r;
855
#if defined(__SUNPRO_CC)
856
  StringOpPlus(L in_l, R in_r)
857
    : l(in_l)
858
    , r(in_r)
859
  {
860
  }
861
#endif
862
  operator std::string() const;
863
  std::string::size_type size() const
864
  {
865
    return this->l.size() + this->r.size();
866
  }
867
};
868
869
template <typename T>
870
struct StringAdd
871
{
872
  static bool const value = AsStringView<T>::value;
873
  using temp_type = string_view;
874
  template <typename S>
875
  static temp_type temp(S&& s)
876
  {
877
    return AsStringView<T>::view(std::forward<S>(s));
878
  }
879
};
880
881
template <typename L, typename R>
882
struct StringAdd<StringOpPlus<L, R>> : std::true_type
883
{
884
  using temp_type = StringOpPlus<L, R> const&;
885
  static temp_type temp(temp_type s) { return s; }
886
};
887
888
template <typename L, typename R>
889
StringOpPlus<L, R>::operator std::string() const
890
{
891
  std::string s;
892
  s.reserve(this->size());
893
  s += *this;
894
  return s;
895
}
896
897
template <typename L, typename R>
898
std::string& operator+=(std::string& s, StringOpPlus<L, R> const& a)
899
{
900
  s.reserve(s.size() + a.size());
901
  s += a.l;
902
  s += a.r;
903
  return s;
904
}
905
906
template <typename L, typename R>
907
String& operator+=(String& s, StringOpPlus<L, R> const& a)
908
{
909
  std::string r;
910
  r.reserve(s.size() + a.size());
911
  r.assign(s.data(), s.size());
912
  r += a.l;
913
  r += a.r;
914
  s = std::move(r);
915
  return s;
916
}
917
918
template <typename L, typename R>
919
std::ostream& operator<<(std::ostream& os, StringOpPlus<L, R> const& a)
920
{
921
  return os << a.l << a.r;
922
}
923
924
template <typename L, typename R>
925
struct IntoString<StringOpPlus<L, R>> : std::true_type
926
{
927
  static std::string into_string(StringOpPlus<L, R> const& a) { return a; }
928
};
929
930
template <typename L, typename R>
931
typename std::enable_if<StringAdd<L>::value && StringAdd<R>::value,
932
                        StringOpPlus<typename StringAdd<L>::temp_type,
933
                                     typename StringAdd<R>::temp_type>>::type
934
operator+(L&& l, R&& r)
935
{
936
  return { StringAdd<L>::temp(std::forward<L>(l)),
937
           StringAdd<R>::temp(std::forward<R>(r)) };
938
}
939
940
template <typename LL, typename LR, typename R>
941
typename std::enable_if<AsStringView<R>::value, bool>::type operator==(
942
  StringOpPlus<LL, LR> const& l, R&& r)
943
{
944
  return std::string(l) == AsStringView<R>::view(std::forward<R>(r));
945
}
946
947
template <typename L, typename RL, typename RR>
948
typename std::enable_if<AsStringView<L>::value, bool>::type operator==(
949
  L&& l, StringOpPlus<RL, RR> const& r)
950
{
951
  return AsStringView<L>::view(std::forward<L>(l)) == std::string(r);
952
}
953
954
} // namespace cm
955
956
namespace std {
957
958
template <>
959
struct hash<cm::String>
960
{
961
  using argument_type = cm::String;
962
  using result_type = size_t;
963
964
  result_type operator()(argument_type const& s) const noexcept
965
106
  {
966
106
    cm::string_view const v = s.view();
967
106
    return static_cast<result_type>(rapidhash(v.data(), v.size()));
968
106
  }
969
};
970
}