Coverage Report

Created: 2026-04-07 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/trafficserver/lib/swoc/include/swoc/TextView.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright Apache Software Foundation 2019
3
/** @file
4
5
   Class for handling "views" of text. Views presume the memory for the buffer is managed
6
   elsewhere and allow efficient access to segments of the buffer without copies. Views are read
7
   only as the view doesn't own the memory. Along with generic buffer methods are specialized
8
   methods to support better string parsing, particularly token based parsing.
9
10
   This class is based on @c std::string_view and is easily and cheaply converted to and from that
11
   class.
12
*/
13
14
#pragma once
15
#include <bitset>
16
#include <cstdint>
17
#include <iosfwd>
18
#include <memory.h>
19
#include <string>
20
#include <string_view>
21
#include <limits>
22
#include <type_traits>
23
24
#include "swoc/swoc_version.h"
25
#include "swoc/string_view_util.h"
26
27
// For no apparent reason, g++ 11 complains about array bound violations with either suffix_at or
28
// assign, the error message is too vague for me to be sure - it doesn't even provide the location of
29
// the method invocation. I've been using g++ 12 for development and I don't see that error.
30
#if __GNUC__ == 11
31
#pragma GCC diagnostic push
32
#pragma GCC diagnostic ignored "-Warray-bounds"
33
#endif
34
35
namespace swoc { inline namespace SWOC_VERSION_NS {
36
37
class TextView;
38
39
/** A set of characters.
40
 *
41
 */
42
class CharSet {
43
  using self_type = CharSet;
44
45
public:
46
  /** Construct from character sequence.
47
   *
48
   * @param chars Character sequence.
49
   *
50
   * The charset becomes @c true for every character in the sequence.
51
   */
52
  constexpr CharSet(TextView const &chars);
53
54
  /** Check if character is in the charset.
55
   *
56
   * @param c Character to check.
57
   * @return @c true if @a c is in the charset, @c false if not.
58
   */
59
  bool operator()(unsigned char c) const;
60
61
  /** Check if character is in the charset.
62
   *
63
   * @param c Character to check.
64
   * @return @c true if @a c is in the charset, @c false if not.
65
   */
66
  bool operator()(char c) const;
67
68
protected:
69
  std::bitset<std::numeric_limits<unsigned char>::max() + 1> _chars;
70
};
71
72
/** A read only view of a contiguous piece of memory.
73
74
    A @c TextView does not own the memory to which it refers, it is simply a view of part of some
75
    (presumably) larger memory object. The purpose is to allow working in a read only way a specific
76
    part of the memory. A classic example for ATS is working with HTTP header fields and values
77
    which need to be accessed independently but preferably without copying. A @c TextView supports
78
    this style.
79
80
    @note To simplify the interface there is no constructor taking only a character pointer.
81
    Constructors require either a literal string or an explicit length. This avoid ambiguities which
82
    are much more annoying that explicitly calling @c strlen on a character pointer.
83
84
    @internal For construction, assignment operator, and @c assign method, there are a lot of overloads
85
    because users would like to be able to use the same sort of arguments for all of these. This includes
86
    - self / parent type
87
    - @c std::string
88
    - literal string
89
    - C-string pointer
90
    - pointer and count
91
    - begin/end style pointers.
92
    - character containers that have the STL standard @c size and @c data methods.
93
 */
94
class TextView : public std::string_view {
95
  using self_type  = TextView;         ///< Self reference type.
96
  using super_type = std::string_view; ///< Parent type.
97
98
public:
99
  /// Default constructor (empty buffer).
100
107k
  constexpr TextView() noexcept = default;
101
102
  /// Construct from a @c std::string_view or @c TextView
103
  /// @note This provides an user defined conversion from @c std::string_view to @c TextView. The
104
  /// reverse conversion is implicit in @c TextView being a subclass of @c std::string_view.
105
  constexpr TextView(super_type const &that) noexcept;
106
107
  /** Construct from pointer and size.
108
   *
109
   * @param ptr Pointer to first character.
110
   * @param n Number of characters.
111
   *
112
   * If @a n is @c npos then @c ptr is presumed to be a C string and checked for length. If @c ptr
113
   * is @c nullptr the length is 0. Otherwise @c strlen is used to calculate the length.
114
   */
115
  constexpr TextView(char const *ptr, size_t n) noexcept;
116
117
  /** Construct from pointer and size.
118
   *
119
   * @param ptr Pointer to first character.
120
   * @param n Number of characters.
121
   */
122
  constexpr TextView(char const *ptr, unsigned n) noexcept;
123
124
  /** Construct from pointer and size.
125
   *
126
   * @param ptr Pointer to first character.
127
   * @param n Number of characters.
128
   *
129
   * If @a n is negative then @c ptr is presumed to be a C string and checked for length. If @c ptr
130
   * is @c nullptr the length is 0. Otherwise @c strlen is used to calculate the length.
131
   */
132
  constexpr TextView(char const *ptr, ssize_t n) noexcept;
133
134
  /** Construct from pointer and size.
135
   *
136
   * @param ptr Pointer to first character.
137
   * @param n Number of characters.
138
   *
139
   * If @a n is negative then @c ptr is presumed to be a C string and checked for length. If @c ptr
140
   * is @c nullptr the length is 0. Otherwise @c strlen is used to calculate the length.
141
   */
142
  constexpr TextView(char const *ptr, int n) noexcept;
143
144
  /** Construct from a half open range [first, last).
145
   *
146
   * @param first Start of half open range.
147
   * @param last End of half open range.
148
   *
149
   * The character at @a first will be in the view, but the character at @a last will not.
150
   *
151
   * @note @c explicit to avoid interpreting a string initializer list as a view.
152
   *
153
   * @internal For the love of Turing, WHY DID YOU DO THIS?
154
   *
155
   * Well, estemed reader, because the C++ standard doesn't have a better way to support overloads
156
   * that handle character pointers and literal strings differently. If the parameters were simply
157
   * <tt>(char const *, char const *)</tt> then a construct like <tt>{ "really", "broken" }</tt> can
158
   * be interpreted as a @c TextView because the elements implicitly convert to <tt>char const
159
   * *</tt>. This makes no sense and creates some @b very annoying ambiguities for lists of strings
160
   * if there are exactly two in the list. See @c Lexicon for an example.
161
   *
162
   * The template itself does the check to make sure it's a character @b pointer and not an array. Arrays
163
   * are handled by a different constructor so this only disables constructing from two char arrays
164
   * which IMHO makes no sense and should be forbidden.
165
   */
166
  template <typename T>
167
  explicit TextView(
168
    T first,
169
    std::enable_if_t<!std::is_array_v<T> && std::is_pointer_v<T> && std::is_convertible_v<T, char const *>, T> last) noexcept
170
80.0k
    : super_type(first, last - first) {}
171
172
  /** Construct from any character container following STL standards.
173
   *
174
   * @tparam C Container type.
175
   * @param c container
176
   *
177
   * The container type must have the methods @c data and @c size which must return values convertible
178
   * to @c char @c const @c * and @c size_t respectively.
179
   */
180
  template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().data()), char const *> &&
181
                                                      std::is_convertible_v<decltype(std::declval<C>().size()), size_t>,
182
                                                    void>>
183
  constexpr TextView(C const &c);
184
185
  /** Construct from literal string or array.
186
187
      All elements of the array are included in the view unless the last element is nul, in which case it is elided.
188
      If this is inappropriate then a constructor with an explicit size should be used.
189
190
      @code
191
        TextView a("A literal string");
192
      @endcode
193
      The last character in @a a will be 'g'.
194
   */
195
  template <size_t N> constexpr TextView(const char (&s)[N]) noexcept;
196
197
  /** Construct from a C-string.
198
   *
199
   * @param src A pointer to a C-string.
200
   *
201
   * The view does not include the terminating nul.
202
   *
203
   * @internal @a src a reference because it is otherwise ambiguous with the literal constructor.
204
   */
205
0
  TextView(char *&src) : super_type(src, src ? strlen(src) : 0) {}
206
207
  /** Construct from a const C-string.
208
   *
209
   * @param src Pointer to a const C-string.
210
   *
211
   * The view does not include the terminating nul.
212
   *
213
   * @internal @a src a reference because it is otherwise ambiguous with the literal constructor.
214
   */
215
0
  TextView(char const *&src) : super_type(src, src ? strlen(src) : 0) {}
216
217
  /** Construct from nullptr.
218
      This implicitly makes the length 0.
219
  */
220
  constexpr TextView(std::nullptr_t) noexcept;
221
222
  /// Construct from @c std::string, referencing the entire string contents.
223
  /// @internal This can't be @c constexpr because this uses methods in @c std::string that may
224
  /// not be @c constexpr.
225
  TextView(std::string const &str) noexcept;
226
227
  /// Assign a super class instance, @c std::string_view  to @a this.
228
  self_type &operator=(super_type const &that);
229
230
  /// Assign a constant array to @a this.
231
  /// @note If the last character of @a s is a nul byte, it is not included in the view.
232
  template <size_t N> self_type &operator=(const char (&s)[N]);
233
234
  /// Assign from C-string @a s.
235
  self_type &operator=(char *&s);
236
  /// Assign from C-string @a s.
237
  self_type &operator=(char const *&s);
238
239
  /// Assign from a @c std::string.
240
  self_type &operator=(const std::string &s);
241
242
  /** Assign a view of the @a c_str
243
   *
244
   * @param c_str Pointer to C string.
245
   * @return @a this
246
   *
247
   * @note @c c_str must be a null terminated string. The null byte is not included in the view.
248
   */
249
  self_type &assign(char *&c_str);
250
251
  /** Assign a view of the @a c_str
252
   *
253
   * @param c_str Pointer to C string.
254
   * @return @a this
255
   *
256
   * @note @c c_str must be a null terminated string. The null byte is not included in the view.
257
   */
258
  self_type &assign(char const *&c_str);
259
260
  /** Assign from a pointer and size.
261
   *
262
   * @param ptr Pointer to first character of the view.
263
   * @param n Length of the view.
264
   * @return @a this
265
   *
266
   * if @a n is @a npos then @c strlen is used determine the size of the view.
267
   */
268
  self_type &assign(char const *ptr, size_t n);
269
270
  /** Assign the half open view [ @a b , @a e ) to @a this
271
   *
272
   * @param b First character in the view.
273
   * @param e One character after the last character in the view.
274
   * @return @a this
275
   */
276
  self_type &assign(char const *b, char const *e);
277
278
  /// Explicitly set the view from a @c std::string
279
  self_type &assign(std::string const &s);
280
281
  /** Assign literal string or array.
282
283
   * All elements of the array are included in the view unless the last element is nul, in which case it is elided.
284
   * If this is inappropriate then a constructor with an explicit size should be used.
285
   *
286
   * @code
287
   *   tv.assign("A literal string");
288
   * @endcode
289
   * The last character in @a tv will be 'g'.
290
  */
291
  template <size_t N> self_type &assign(const char (&s)[N]) noexcept;
292
293
  /** Assign from any character container following STL standards.
294
   *
295
   * @tparam C Container type.
296
   * @param c container
297
   *
298
   * The container type must have the methods @c data and @c size which must return values convertible
299
   * to @c char @c const @c * and @c size_t respectively.
300
   */
301
  template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().data()), char const *> &&
302
                                                      std::is_convertible_v<decltype(std::declval<C>().size()), size_t>,
303
                                                    void>>
304
  constexpr self_type &
305
  assign(C const &c) {
306
    return this->assign(c.data(), c.size());
307
  }
308
309
  /** Dereference operator.
310
311
      @note This allows the view to be used as if it were a character iterator to a null terminated
312
      string which is handy for several other STL interfaces.
313
314
      @return The first byte in the view, or a nul character if the view is empty.
315
  */
316
  /// @return The first byte in the view.
317
  constexpr char operator*() const;
318
319
  /** Discard the first byte of the view.
320
   *
321
   *  @return @a this.
322
   */
323
  self_type &operator++();
324
325
  /** Discard the first byte of the view.
326
   *
327
   * @return The view before discarding the byte.
328
   */
329
  self_type operator++(int);
330
331
  /** Discard the first @a n bytes of the view.
332
   *
333
   *  Equivalent to @c remove_prefix(n).
334
   *  @return @a this
335
   */
336
  self_type &operator+=(size_t n);
337
338
  /// Check for empty view.
339
  /// @return @c true if the view has a nullptr @b or zero size.
340
  constexpr bool operator!() const noexcept;
341
342
  /// Check for non-empty view.
343
  /// @return @c true if the view refers to a non-empty range of bytes.
344
  explicit constexpr operator bool() const noexcept;
345
346
  /// Clear the view (become an empty view).
347
  self_type &clear();
348
349
  /// Get the offset of the first character for which @a pred is @c true.
350
  template <typename F> size_t find_if(F const &pred) const;
351
  /// Get the offset of the last character for which @a pred is @c true.
352
  template <typename F> size_t rfind_if(F const &pred) const;
353
354
  /** Remove bytes that match @a c from the start of the view.
355
   *
356
   * @return @a this
357
   */
358
  self_type &ltrim(char c);
359
360
  /** Remove bytes from the start of the view that are in @a delimiters.
361
   *
362
   * @return @a this
363
   */
364
  self_type &ltrim(CharSet const &delimiters);
365
366
  /** Remove bytes from the start of the view that are in @a delimiters.
367
   *
368
   * @return @a this
369
   */
370
  self_type &ltrim(std::string_view const &delimiters);
371
372
  /** Remove bytes from the start of the view that are in @a delimiters.
373
   *
374
   * @internal This is needed to avoid collisions with the templated predicate style.
375
   *
376
   * @return @c *this
377
   */
378
  self_type &ltrim(const char *delimiters);
379
380
  /** Remove bytes from the start of the view for which @a pred is @c true.
381
      @a pred must be a functor taking a @c char argument and returning @c bool.
382
      @return @c *this
383
  */
384
  template <typename F> self_type &ltrim_if(F const &pred);
385
386
  /** Remove bytes that match @a c from the end of the view.
387
   *
388
   * @return @a this
389
   */
390
  self_type &rtrim(char c);
391
392
  /** Remove bytes from the end of the view that are in @a delimiters.
393
   *
394
   * @return @a this
395
   */
396
  self_type &rtrim(CharSet const &delimiters);
397
398
  /** Remove bytes from the end of the view that are in @a delimiters.
399
   * @return @a this
400
   */
401
  self_type &rtrim(std::string_view const &delimiters);
402
403
  /** Remove bytes from the end of the view for which @a pred is @c true.
404
   *
405
   * @a pred must be a functor taking a @c char argument and returning @c bool.
406
   *
407
   * @return @c *this
408
   */
409
  template <typename F> self_type &rtrim_if(F const &pred);
410
411
  /** Remove bytes that match @a c from the start and end of this view.
412
   *
413
   * @return @a this
414
   */
415
  self_type &trim(char c);
416
417
  /** Remove bytes from the start and end of the view that are in @a delimiters.
418
   * @return @a this
419
   */
420
  self_type &trim(CharSet const &delimiters);
421
422
  /** Remove bytes from the start and end of the view that are in @a delimiters.
423
   * @return @a this
424
   */
425
  self_type &trim(std::string_view const &delimiters);
426
427
  /** Remove bytes from the start and end of the view that are in @a delimiters.
428
      @internal This is needed to avoid collisions with the templated predicate style.
429
      @return @c *this
430
  */
431
  self_type &trim(const char *delimiters);
432
433
  /** Remove bytes from the start and end of the view for which @a pred is @c true.
434
      @a pred must be a functor taking a @c char argument and returning @c bool.
435
      @return @c *this
436
  */
437
  template <typename F> self_type &trim_if(F const &pred);
438
439
  /** Get a view of the first @a n bytes.
440
   *
441
   * @param n Number of chars in the prefix.
442
   * @return A view of the first @a n characters in @a this, bounded by the size of @a this.
443
   */
444
  constexpr self_type prefix(size_t n) const noexcept;
445
446
  /** Get a view of a prefix bounded by @a c.
447
   *
448
   * @param c Delimiter character.
449
   * @return A view of the prefix bounded by @a c, or all of @a this if @a c is not found.
450
   * @note The character @a c is not included in the returned view.
451
   */
452
  self_type prefix_at(char c) const;
453
454
  /** Get a view of a prefix bounded by a character in @a delimiters.
455
   *
456
   * @param delimiters A set of characters.
457
   *
458
   * @return A view of the prefix bounded by any character in @a delimiters, or empty if none are
459
   * found.
460
   *
461
   * @note The delimiter character is not included in the returned view.
462
   */
463
  self_type prefix_at(std::string_view const &delimiters) const;
464
465
  /** Get a view of a prefix bounded by a character predicate @a pred.
466
   *
467
   * @a pred must be a functor which takes a @c char argument and returns @c bool. Each character in
468
   * @a this is tested by @a pred and the prefix is delimited by the first character for which @a
469
   * pred is @c true.
470
   *
471
   * @param pred A character predicate.
472
   *
473
   * @return A view of the prefix bounded by @a pred or empty if @a pred is not @c true for any
474
   * characer.
475
   *
476
   * @note The deliminting character is not included in the returned view.
477
   */
478
  template <typename F> self_type prefix_if(F const &pred) const;
479
480
  /** Remove bytes from the start of the view.
481
   *
482
   * @param n Number of bytes to remove.
483
   * @return @a this.
484
   */
485
  self_type &remove_prefix(size_t n);
486
487
  /** Remove bytes from the end of the view.
488
   *
489
   * @param n Number of bytes to remove.
490
   * @return @a this.
491
   */
492
  self_type &remove_suffix(size_t n);
493
494
  /** Remove the leading characters of @a this up to and including @a c.
495
   *
496
   * @param c Delimiter character.
497
   * @return @a this.
498
   * @note The first occurrence of character @a c is removed along with all preceding characters, or
499
   * the view is cleared if @a c is not found.
500
   */
501
  self_type &remove_prefix_at(char c);
502
503
  /** Remove the leading characters of @a this up to and including the first character matching @a delimiters.
504
   *
505
   * @param delimiters Characters to match.
506
   * @return @a this.
507
   * @note The first occurrence of any character in @a delimiters is removed along with all preceding
508
   * characters, or the view is cleared if none are found.
509
   */
510
  self_type &remove_prefix_at(std::string_view const &delimiters);
511
512
  /** Remove the leading characters up to and including the character selected by @a pred.
513
   *
514
   * @tparam F Predicate function type.
515
   * @param pred The predicate instance.
516
   * @return @a this.
517
   *
518
   * Characters are removed until @a pred returns @c true. The matching character is also removed.
519
   */
520
  template <typename F> self_type &remove_prefix_if(F const &pred);
521
522
  /** Remove and return a prefix of size @a n.
523
   *
524
   * @param n Size of the prefix.
525
   * @return The first @a n bytes of @a this if @a n is in @a this, otherwise an empty view.
526
   *
527
   * The prefix is removed and returned if the requested prefix is no larger than @a this,
528
   * otherwise @a this is not modified.
529
   *
530
   * @note The character at offset @a n is discarded if @a this is modified.
531
   *
532
   * @see @c take_prefix
533
   */
534
  self_type split_prefix(size_t n);
535
536
  /** Remove and return a prefix bounded by the first occurrence of @a c.
537
   *
538
   * @param c The character to match.
539
   * @return The prefix bounded by @a c if @a c is found, an empty view if not.
540
   *
541
   * The prefix is removed and returned if @a c is found, otherwise @a this is not modified.
542
   *
543
   * @note The delimiter character is discarded if @a this is modified.
544
   *
545
   * @see @c take_prefix
546
   */
547
  self_type split_prefix_at(char c);
548
549
  /** Remove and return a prefix bounded by the first occurrence of any of @a delimiters.
550
   *
551
   * @param delimiters The characters to match.
552
   * @return The prefix bounded by a delimiter if one is found, otherwise an empty view.
553
   *
554
   * The prefix is removed and returned if a @a delimiter is found, otherwise @a this is not modified.
555
   *
556
   * @note The matching character is discarded if @a this is modified.
557
   *
558
   * @see @c take_prefix_at
559
   */
560
  self_type split_prefix_at(std::string_view const &delimiters);
561
562
  /** Remove and return a prefix bounded by the first character that satisfies @a pred.
563
   *
564
   * @tparam F Predicate functor type.
565
   * @param pred A function taking @c char and returning @c bool.
566
   * @return The prefix bounded by the first character satisfying @a pred.
567
   *
568
   * The prefix is removed and returned if a character satisfying @a pred is found, otherwise
569
   * @a this is not modified.
570
   *
571
   * @note The matching character is discarded if @a this is modified.
572
   *
573
   * @see @c take_prefix_if
574
   */
575
  template <typename F> self_type split_prefix_if(F const &pred);
576
577
  /** Remove and return the first @a n characters.
578
   *
579
   * @param n Size of the return prefix.
580
   * @return The first @a n bytes of @a this if @a n is in @a this, otherwise all of @a this.
581
   *
582
   * The prefix is removed and returned if the requested prefix is no larger than @a this,
583
   * otherwise all of @a this is removed and returned.
584
   *
585
   * @note The character at offset @a n is discarded if @a n is within the bounds of @a this.
586
   *
587
   * @see @c split_prefix
588
   */
589
  self_type take_prefix(size_t n);
590
591
  /** Remove and return a prefix bounded by the first occurrence of @a c.
592
   *
593
   * @param c The character to match.
594
   * @return The prefix bounded by @a c if @a c is found, all of @a this if not.
595
   *
596
   * The prefix is removed and returned if @a c is found, otherwise all of @a this is removed and
597
   * returned.
598
   *
599
   * @note The character at offset @a n is discarded if found.
600
   *
601
   * @see @c split_prefix_at
602
   */
603
  self_type take_prefix_at(char c);
604
605
  /** Remove and return a prefix bounded by the first occurrence of any of @a delimiters.
606
   *
607
   * @param delimiters The characters to match.
608
   * @return The prefix bounded by a delimiter if one is found, otherwise all of @a this.
609
   *
610
   * The prefix is removed and returned if a @a delimiter is found, otherwise all of @a this is
611
   * removed and returned.
612
   *
613
   * @note The matching character is discarded if found.
614
   *
615
   * @see @c split_prefix_at
616
   */
617
  self_type take_prefix_at(std::string_view const &delimiters);
618
619
  /** Remove and return a prefix bounded by the first character that satisfies @a pred.
620
   *
621
   * @tparam F Predicate functor type.
622
   * @param pred A function taking @c char and returning @c bool.
623
   * @return The prefix bounded by the first character satisfying @a pred, or all of @a this if none
624
   * is found.
625
   *
626
   * The prefix is removed and returned if a character satisfying @a pred is found, otherwise
627
   * all of @a this is removed and returned.
628
   *
629
   * @note The matching character is discarded if found.
630
   *
631
   * @see @c split_prefix_if
632
   */
633
  template <typename F> self_type take_prefix_if(F const &pred);
634
635
  /** Remove and return a prefix of characters satisfying @a pred
636
   *
637
   * @tparam F Predicate functor type.
638
   * @param pred A function taking @c char and returning @c bool.
639
   * @return The prefix of characters that satisfy @a pred.
640
   *
641
   * The returned prefix is removed from @a this. That prefix may be empty if the first character
642
   * does not satisfy @a pred.
643
   *
644
   * @note This is very similar to @c ltrim_if but returns the removed text instead of the modified
645
   * view.
646
   */
647
  template <typename F> self_type clip_prefix_of(F const &pred);
648
649
  /** Get a view of the last @a n bytes.
650
   *
651
   * @param n Number of chars in the suffix.
652
   * @return A view of the last @a n characters in @a this, bounded by the size of @a this.
653
   */
654
  constexpr self_type suffix(size_t n) const noexcept;
655
656
  /** Get a view of a suffix bounded by @a c.
657
   *
658
   * @param c Delimiter character.
659
   * @return A view of the suffix bounded by @a c, or all of @a this if @a c is not found.
660
   * @note The character @a c is not included in the returned view.
661
   */
662
  self_type suffix_at(char c) const;
663
664
  /** Get a view of a suffix bounded by a character in @a delimiters.
665
   *
666
   * @param delimiters A set of characters.
667
   *
668
   * @return A view of the suffix bounded by any character in @a delimiters, or mepty if none are
669
   * found.
670
   *
671
   * @note The delimiter character is not included in the returned view.
672
   */
673
  self_type suffix_at(std::string_view const &delimiters) const;
674
675
  /** Get a view of a suffix bounded by a character predicate @a pred.
676
   *
677
   * @a pred must be a functor which takes a @c char argument and returns @c bool. Each character in
678
   * @a this is tested by @a pred and the suffix is delimited by the last character for which @a
679
   * pred is @c true.
680
   *
681
   * @param pred A character predicate.
682
   *
683
   * @return A view of the suffix bounded by @a pred or empty if @a pred is not @c true for any
684
   * character.
685
   *
686
   * @note The delimiting character is not included in the returned view.
687
   */
688
  template <typename F> self_type suffix_if(F const &pred) const;
689
690
  /** Remove the trailing characters of @a this up to and including @a c.
691
   *
692
   * @param c Delimiter character.
693
   * @return @a this.
694
   *
695
   * @note The last occurrence of character @a c is removed along with all succeeding characters, or
696
   * the view is cleared if @a c is not found.
697
   */
698
  self_type &remove_suffix_at(char c);
699
700
  /** Remove the trailing characters of @a this up to and including the last character matching @a delimiters.
701
   *
702
   * @param delimiters Characters to match.
703
   * @return @a this.
704
   * @note The first occurrence of any character in @a delimiters is removed along with all preceding
705
   * characters, or the view is cleared if none are found.
706
   */
707
  self_type &remove_suffix_at(std::string_view const &delimiters);
708
709
  /** Remove the trailing characters up to and including the character selected by @a pred.
710
   *
711
   * @tparam F Predicate function type.
712
   * @param pred The predicate instance.
713
   * @return @a this.
714
   *
715
   * If predicate is never true the view is cleared.
716
   */
717
  template <typename F> self_type &remove_suffix_if(F const &pred);
718
719
  /** Remove and return a suffix of size @a n.
720
   *
721
   * @param n Size of the suffix.
722
   * @return The first @a n bytes of @a this if @a n is in @a this, otherwise an empty view.
723
   *
724
   * The prefix is removed and returned if the requested suffix is no larger than @a this,
725
   * otherwise @a this is not modified.
726
   *
727
   * @note The character at offset @a n is discarded if @a this is modified.
728
   *
729
   * @see @c take_suffix
730
   */
731
  self_type split_suffix(size_t n);
732
733
  /** Remove and return a suffix bounded by the last occurrence of @a c.
734
   *
735
   * @param c The character to match.
736
   * @return The suffix bounded by @a c if @a c is found, an empty view if not.
737
   *
738
   * The suffix is removed and returned if @a c is found, otherwise @a this is not modified.
739
   *
740
   * @note The character at offset @a n is discarded if @a this is modified.
741
   *
742
   * @see @c take_suffix_at
743
   */
744
  self_type split_suffix_at(char c);
745
746
  /** Remove and return a suffix bounded by the last occurrence of any of @a delimiters.
747
   *
748
   * @param delimiters The characters to match.
749
   * @return The suffix bounded by a delimiter if found, an empty view if none found.
750
   *
751
   * The suffix is removed and returned if delimiter is found, otherwise @a this is not modified.
752
   *
753
   * @note The delimiter character is discarded if @a this is modified.
754
   *
755
   * @see @c take_suffix_at
756
   */
757
  self_type split_suffix_at(std::string_view const &delimiters);
758
759
  /** Remove and return a suffix bounded by the last character that satisfies @a pred.
760
   *
761
   * @tparam F Predicate functor type.
762
   * @param pred A function taking @c char and returning @c bool.
763
   * @return The suffix bounded by the first character satisfying @a pred if found, otherwise @a this
764
   * is not modified.
765
   *
766
   * The prefix is removed and returned if a character satisfying @a pred if found, otherwise
767
   * @a this is not modified.
768
   *
769
   * @note The matching character is discarded if @a this is modified.
770
   *
771
   * @see @c take_suffix_if
772
   */
773
  template <typename F> self_type split_suffix_if(F const &pred);
774
775
  /** Remove and return a suffix of size @a n.
776
   *
777
   * @param n Size of the suffix.
778
   * @return The first @a n bytes of @a this if @a n is in @a this, otherwise all of @a this.
779
   *
780
   * The returned suffix is removed from @a this, along with the character at offset @a n if present.
781
   *
782
   * @see @c split_suffix
783
   */
784
  self_type take_suffix(size_t n);
785
786
  /** Remove and return a suffix bounded by the last occurrence of @a c.
787
   *
788
   * @param c The character to match.
789
   * @return The suffix bounded by @a c if @a c is found, all of @a this if not.
790
   *
791
   * The returned suffix is removed from @a this, along with the delimiter character if found.
792
   *
793
   * @see @c split_suffix_at
794
   */
795
  self_type take_suffix_at(char c);
796
797
  /** Remove and return a suffix bounded by the last occurrence of any of @a delimiters.
798
   *
799
   * @param delimiters The characters to match.
800
   * @return The suffix bounded by a delimiter if @a c is found, all of @a this if not.
801
   *
802
   * The returned suffix is removed from @a this, along with the delimiter character if found.
803
   *
804
   * @see @c split_suffix_at
805
   */
806
  self_type take_suffix_at(std::string_view const &delimiters);
807
808
  /** Remove and return a suffix bounded by the last character that satisfies @a pred.
809
   *
810
   * @tparam F Predicate functor type.
811
   * @param pred A function taking @c char and returning @c bool.
812
   * @return The suffix bounded by the first character satisfying @a pred if found, otherwise all of @a this.
813
   *
814
   * @note The matching character is discarded if found.
815
   *
816
   * @see @c split_suffix_if
817
   */
818
  template <typename F> self_type take_suffix_if(F const &pred);
819
820
  /** Remove and return a suffix of characters satisfying @a pred
821
   *
822
   * @tparam F Predicate functor type.
823
   * @param pred A function taking @c char and returning @c bool.
824
   * @return The suffix of characters that satisfy @a pred.
825
   *
826
   * The returned suffix is removed from @a this. That suffix may be empty if the last character
827
   * does not satisfy @a pred.
828
   *
829
   * @note This is very similar to @c rtrim_if but returns the removed text instead of the modified
830
   * view.
831
   */
832
  template <typename F> self_type clip_suffix_of(F const &pred);
833
834
  /** Get a view of part of this view.
835
   *
836
   * @param pos Offset of first byte in the new view.
837
   * @param count Number of bytes in the view.
838
   * @return The view starting at @a pos for @a count bytes.
839
   *
840
   * The returned view is clipped by @a this - that is, it will not extend beyond the original view.
841
   * @a count is reduced such that it covers only data in @a this.
842
   *
843
   * @note This is provided primarily for co-variance, i.e. the returned view is a @c TextView
844
   * instead of a @c std::string_view.
845
   */
846
  constexpr self_type substr(size_type pos = 0, size_type count = npos) const noexcept;
847
848
  /** Check if the view begins with a specific @a prefix.
849
   *
850
   * @param prefix String to check against @a this.
851
   * @return @c true if <tt>this->prefix(prefix.size()) == prefix</tt>, @c false otherwise.
852
   * @internal C++20 preview.
853
   */
854
  bool starts_with(std::string_view const &prefix) const noexcept;
855
856
  /** Check if the view begins with a specific @a prefix.
857
   *
858
   * @param prefix String to check against @a this.
859
   * @return @c true if <tt>this->prefix(prefix.size()) == prefix</tt>, @c false otherwise.
860
   * @internal C++20 preview.
861
   */
862
  bool starts_with(char const *prefix) const;
863
864
  /** Check if the view begins with the character @c c.
865
   *
866
   * @param c Character to check.
867
   * @return @c true if the string is non-empty and the first character is @c c.
868
   * @internal C++20 preview.
869
   */
870
  bool starts_with(char c) const noexcept;
871
872
  /** Check if the view begins with a specific @a prefix, ignoring case.
873
   *
874
   * @param prefix String to check against @a this.
875
   * @return @c true if <tt>this->prefix(prefix.size()) == prefix</tt> without regard to case, @c false otherwise.
876
   * @internal C++20 preview.
877
   */
878
  bool starts_with_nocase(std::string_view const &prefix) const noexcept;
879
880
  /** Check if the view begins with a specific @a prefix.
881
   *
882
   * @param prefix String to check against @a this.
883
   * @return @c true if <tt>this->prefix(prefix.size()) == prefix</tt>, @c false otherwise.
884
   * @internal C++20 preview.
885
   */
886
  bool starts_with_nocase(char const *prefix) const;
887
888
  /** Check if the view begins with the character @c c, ignoring case.
889
   *
890
   * @param c Character to check.
891
   * @return @c true if the string is non-empty and the first character is @c c.
892
   * @internal C++20 preview.
893
   */
894
  bool starts_with_nocase(char c) const noexcept;
895
896
  /** Check if the view ends with a specific @a suffix.
897
   *
898
   * @param suffix String to check against @a this.
899
   * @return @c true if <tt>this->suffix(suffix.size()) == suffix</tt>, @c false otherwise.
900
   * @internal C++20 preview.
901
   */
902
  bool ends_with(std::string_view const &suffix) const noexcept;
903
904
  /** Check if the view ends with a specific @a suffix.
905
   *
906
   * @param suffix String to check against @a this.
907
   * @return @c true if <tt>this->suffix(suffix.size()) == suffix</tt>, @c false otherwise.
908
   * @internal C++20 preview.
909
   */
910
  bool ends_with(char const *suffix) const;
911
912
  /** Check the view ends with the character @c c.
913
   *
914
   * @param c Character to check.
915
   * @return @c true if the string is non-empty and the last character is @c c.
916
   * @internal C++20 preview.
917
   */
918
  bool ends_with(char c) const noexcept;
919
920
  /** Check if the view starts with a specific @a suffix, ignoring case.
921
   *
922
   * @param suffix String to check against @a this.
923
   * @return @c true if <tt>this->suffix(suffix.size()) == suffix</tt> without regard to case, @c false otherwise.
924
   * @internal C++20 preview.
925
   */
926
  bool ends_with_nocase(std::string_view const &suffix) const noexcept;
927
928
  /** Check if the view starts with a specific @a suffix, ignoring case.
929
   *
930
   * @param suffix String to check against @a this.
931
   * @return @c true if <tt>this->suffix(suffix.size()) == suffix</tt> without regard to case, @c false otherwise.
932
   * @internal C++20 preview.
933
   */
934
  bool ends_with_nocase(char const *suffix) const;
935
936
  /** Check the view ends with the character @c c, ignoring case.
937
   *
938
   * @param c Character to check.
939
   * @return @c true if the string is non-empty and the last character is @c c.
940
   * @internal C++20 preview.
941
   */
942
  bool ends_with_nocase(char c) const noexcept;
943
944
  // Functors for using this class in STL containers.
945
  /// Ordering functor, lexicographic comparison.
946
  struct LessThan {
947
    /// @return Case sensitive ordering.
948
    bool
949
0
    operator()(self_type const &lhs, self_type const &rhs) const noexcept {
950
0
      return -1 == strcmp(lhs, rhs);
951
0
    }
952
  };
953
954
  /// Ordering functor, case ignoring lexicographic comparison.
955
  struct LessThanNoCase {
956
    /// @return Case insensitive ordering.
957
    bool
958
0
    operator()(self_type const &lhs, self_type const &rhs) const noexcept {
959
0
      return -1 == strcasecmp(lhs, rhs);
960
0
    }
961
  };
962
963
  /// Support for containers that need case insensitive comparisons between views.
964
  struct CaselessEqual {
965
    /// @return @c true if the view contants are equal when compared without regard to case.
966
    bool
967
0
    operator()(self_type const &lhs, self_type const &rhs) const noexcept {
968
0
      return lhs.size() == rhs.size() && 0 == strcasecmp(lhs, rhs);
969
0
    }
970
  };
971
972
  /** A pointer to the first byte.
973
   *
974
   * @return Address of the first byte of the view.
975
   *
976
   * @internal This fixes an error in @c std::string_view where this method is declared to return
977
   * a template parameter instead of the correct @c value_type. The effect is @c string_view::data
978
   * is not considered by the compiler to return <tt>char const *</tt> which makes meta-programming
979
   * painful.
980
   */
981
  constexpr value_type const *data() const noexcept;
982
983
  /** A pointer to past the last byte.
984
   *
985
   * @return Address of the first byte past the end of the view.
986
   *
987
   * This is effectively @c std::string_view::end() except it explicit returns a pointer and not
988
   * (potentially) an iterator class, to match up with @c data().
989
   */
990
  constexpr value_type const *data_end() const noexcept;
991
992
  /// Specialized stream operator implementation.
993
  /// @note Use the standard stream operator unless there is a specific need for this, which is unlikely.
994
  /// @return The stream @a os.
995
  /// @internal Needed because @c std::ostream::write must be used and
996
  /// so alignment / fill have to be explicitly handled.
997
  template <typename Stream> Stream &stream_write(Stream &os, const TextView &b) const;
998
999
  /// @cond OVERLOAD
1000
  // These methods are all overloads of other methods, defined in order to make the API more
1001
  // convenient to use. Mostly these overload @c int for @c size_t so naked numbers work as expected.
1002
  constexpr self_type prefix(int n) const noexcept;
1003
  self_type take_suffix(int n);
1004
  self_type split_prefix(int n);
1005
  constexpr self_type suffix(int n) const noexcept;
1006
  self_type split_suffix(int n);
1007
  /// @endcond
1008
1009
protected:
1010
  /// Initialize a bit mask to mark which characters are in this view.
1011
  static void init_delimiter_set(std::string_view const &delimiters, std::bitset<256> &set);
1012
};
1013
1014
/// Internal table of digit values for characters.
1015
/// This is -1 for characters that are not valid digits.
1016
extern const int8_t svtoi_convert[256];
1017
1018
/** Convert the text in @c TextView @a src to a signed numeric value.
1019
1020
    If @a parsed is non-null then the part of the string actually parsed is placed there.
1021
    @a base sets the conversion base. If not set base 10 is used with two special cases:
1022
1023
    - If the number starts with a literal '0' then it is treated as base 8.
1024
    - If the number starts with the literal characters '0x' or '0X' then it is treated as base 16.
1025
1026
    If @a base is explicitly set then any leading radix indicator is not supported.
1027
*/
1028
intmax_t svtoi(TextView src, TextView *parsed = nullptr, int base = 0);
1029
1030
/** Convert the text in @c TextView @a src to an unsigned numeric value.
1031
1032
    If @a parsed is non-null then the part of the string actually parsed is placed there.
1033
    @a base sets the conversion base. If not set base 10 is used with two special cases:
1034
1035
    - If the number starts with a literal '0' then it is treated as base 8.
1036
    - If the number starts with the literal characters '0x' or '0X' then it is treated as base 16.
1037
1038
    If @a base is explicitly set then any leading radix indicator is not supported.
1039
*/
1040
uintmax_t svtou(TextView src, TextView *parsed = nullptr, int base = 0);
1041
1042
/** Convert the text in @c src to an unsigned numeric value.
1043
 *
1044
 * @tparam N The radix (must be  1..36)
1045
 * @param src The source text. Updated during parsing.
1046
 * @return The converted numeric value.
1047
 *
1048
 * This is a specialized function useful only where conversion performance is critical. It is used
1049
 * inside @c svtoi and @a svtou for the common cases of 8, 10, and 16, therefore normally this isn't much more
1050
 * performant in those cases than just @c svtoi. Because of this only positive values are parsed.
1051
 * If determining the radix from the text or signed value parsing is needed, used @c svtoi.
1052
 *
1053
 * @a src is updated in place to indicate what characters were parsed by removing them from the view
1054
 * Parsing stops on the first invalid digit, so any leading non-digit characters (e.g. whitespace)
1055
 * must already be removed. For overflow, all valid digits are consumed and the maximum value returned.
1056
 */
1057
template <int RADIX>
1058
uintmax_t
1059
2.48k
svto_radix(TextView &src) {
1060
2.48k
  static_assert(1 <= RADIX && RADIX <= 36, "Radix must be in the range 2..36");
1061
2.48k
  static constexpr auto MAX            = std::numeric_limits<uintmax_t>::max();
1062
2.48k
  static constexpr auto OVERFLOW_LIMIT = MAX / RADIX;
1063
2.48k
  uintmax_t zret                       = 0;
1064
2.48k
  uintmax_t v;
1065
22.4k
  while (src.size() && ((v = swoc::svtoi_convert[uint8_t(*src)]) < RADIX)) {
1066
    // Tweaked for performance - need to check range after @a RADIX multiply.
1067
19.9k
    ++src; // Update view iff the character is parsed.
1068
19.9k
    if (zret <= OVERFLOW_LIMIT && v <= (MAX - (zret *= RADIX)) ) {
1069
18.5k
      zret += v;
1070
18.5k
    } else {
1071
1.43k
      zret = MAX; // clamp to max - once set will always hit this case for subsequent input.
1072
1.43k
    }
1073
19.9k
  }
1074
2.48k
  return zret;
1075
2.48k
}
unsigned long swoc::_1_5_15::svto_radix<10>(swoc::_1_5_15::TextView&)
Line
Count
Source
1059
1.77k
svto_radix(TextView &src) {
1060
1.77k
  static_assert(1 <= RADIX && RADIX <= 36, "Radix must be in the range 2..36");
1061
1.77k
  static constexpr auto MAX            = std::numeric_limits<uintmax_t>::max();
1062
1.77k
  static constexpr auto OVERFLOW_LIMIT = MAX / RADIX;
1063
1.77k
  uintmax_t zret                       = 0;
1064
1.77k
  uintmax_t v;
1065
6.08k
  while (src.size() && ((v = swoc::svtoi_convert[uint8_t(*src)]) < RADIX)) {
1066
    // Tweaked for performance - need to check range after @a RADIX multiply.
1067
4.30k
    ++src; // Update view iff the character is parsed.
1068
4.30k
    if (zret <= OVERFLOW_LIMIT && v <= (MAX - (zret *= RADIX)) ) {
1069
3.86k
      zret += v;
1070
3.86k
    } else {
1071
442
      zret = MAX; // clamp to max - once set will always hit this case for subsequent input.
1072
442
    }
1073
4.30k
  }
1074
1.77k
  return zret;
1075
1.77k
}
unsigned long swoc::_1_5_15::svto_radix<16>(swoc::_1_5_15::TextView&)
Line
Count
Source
1059
314
svto_radix(TextView &src) {
1060
314
  static_assert(1 <= RADIX && RADIX <= 36, "Radix must be in the range 2..36");
1061
314
  static constexpr auto MAX            = std::numeric_limits<uintmax_t>::max();
1062
314
  static constexpr auto OVERFLOW_LIMIT = MAX / RADIX;
1063
314
  uintmax_t zret                       = 0;
1064
314
  uintmax_t v;
1065
4.63k
  while (src.size() && ((v = swoc::svtoi_convert[uint8_t(*src)]) < RADIX)) {
1066
    // Tweaked for performance - need to check range after @a RADIX multiply.
1067
4.32k
    ++src; // Update view iff the character is parsed.
1068
4.32k
    if (zret <= OVERFLOW_LIMIT && v <= (MAX - (zret *= RADIX)) ) {
1069
3.89k
      zret += v;
1070
3.89k
    } else {
1071
430
      zret = MAX; // clamp to max - once set will always hit this case for subsequent input.
1072
430
    }
1073
4.32k
  }
1074
314
  return zret;
1075
314
}
unsigned long swoc::_1_5_15::svto_radix<2>(swoc::_1_5_15::TextView&)
Line
Count
Source
1059
177
svto_radix(TextView &src) {
1060
177
  static_assert(1 <= RADIX && RADIX <= 36, "Radix must be in the range 2..36");
1061
177
  static constexpr auto MAX            = std::numeric_limits<uintmax_t>::max();
1062
177
  static constexpr auto OVERFLOW_LIMIT = MAX / RADIX;
1063
177
  uintmax_t zret                       = 0;
1064
177
  uintmax_t v;
1065
8.05k
  while (src.size() && ((v = swoc::svtoi_convert[uint8_t(*src)]) < RADIX)) {
1066
    // Tweaked for performance - need to check range after @a RADIX multiply.
1067
7.87k
    ++src; // Update view iff the character is parsed.
1068
7.87k
    if (zret <= OVERFLOW_LIMIT && v <= (MAX - (zret *= RADIX)) ) {
1069
7.54k
      zret += v;
1070
7.54k
    } else {
1071
330
      zret = MAX; // clamp to max - once set will always hit this case for subsequent input.
1072
330
    }
1073
7.87k
  }
1074
177
  return zret;
1075
177
}
unsigned long swoc::_1_5_15::svto_radix<8>(swoc::_1_5_15::TextView&)
Line
Count
Source
1059
212
svto_radix(TextView &src) {
1060
212
  static_assert(1 <= RADIX && RADIX <= 36, "Radix must be in the range 2..36");
1061
212
  static constexpr auto MAX            = std::numeric_limits<uintmax_t>::max();
1062
212
  static constexpr auto OVERFLOW_LIMIT = MAX / RADIX;
1063
212
  uintmax_t zret                       = 0;
1064
212
  uintmax_t v;
1065
3.64k
  while (src.size() && ((v = swoc::svtoi_convert[uint8_t(*src)]) < RADIX)) {
1066
    // Tweaked for performance - need to check range after @a RADIX multiply.
1067
3.43k
    ++src; // Update view iff the character is parsed.
1068
3.43k
    if (zret <= OVERFLOW_LIMIT && v <= (MAX - (zret *= RADIX)) ) {
1069
3.20k
      zret += v;
1070
3.20k
    } else {
1071
230
      zret = MAX; // clamp to max - once set will always hit this case for subsequent input.
1072
230
    }
1073
3.43k
  }
1074
212
  return zret;
1075
212
}
1076
1077
/// Convenience overload.
1078
/// @see svto_radix(swoc::TextView &src)
1079
template <int N>
1080
uintmax_t
1081
0
svto_radix(TextView &&src) {
1082
0
  return svto_radix<N>(src);
1083
0
}
1084
1085
/** Parse @a text as a floating point number.
1086
 *
1087
 * @param text The input text.
1088
 * @param parsed Parsed text [out]
1089
 * @return The floating point value, or 0.0 if invalid input.
1090
 *
1091
 * If @a parsed is not @a nullptr then the span of characters parsed is put there. This can be
1092
 * used to check if the parse was scuccesful - on a failed parse, it will be empty.
1093
 *
1094
 * @note This should be within 1 epsilon of correct, although it doesn't guarantee picking
1095
 * the closest epsilon. It's more than sufficient for use in configurations, but possibly
1096
 * not for high precision work.
1097
 */
1098
double svtod(TextView text, TextView *parsed = nullptr);
1099
// ----------------------------------------------------------
1100
// Inline implementations.
1101
// Note: Why, you may ask, do I use @c TextView::self_type for return type instead of the
1102
// simpler plain @c TextView ? Because otherwise Doxygen can't match up the declaration and
1103
// definition and the reference documentation is messed up. Sigh.
1104
1105
0
inline constexpr CharSet::CharSet(TextView const &chars) {
1106
0
  for (auto c : chars) {
1107
0
    _chars[uint8_t(c)] = true;
1108
0
  }
1109
0
}
1110
1111
inline bool
1112
0
CharSet::operator()(unsigned char c) const {
1113
0
  return _chars[c];
1114
0
}
1115
1116
inline bool
1117
0
CharSet::operator()(char c) const {
1118
0
  return _chars[uint8_t(c)];
1119
0
}
1120
1121
// === TextView Implementation ===
1122
/// @cond TextView_INTERNAL
1123
// Doxygen doesn't match these up well due to various type and template issues.
1124
// @internal If there is more than one overload for numeric types, it's easy to get ambiguity. The only
1125
// fix, unfortunately, is lots of overloads to cover the ambiguous cases.
1126
inline constexpr TextView::TextView(const char *ptr, size_t n) noexcept
1127
58.6k
  : super_type(ptr, n == npos ? (ptr ? ::strlen(ptr) : 0) : n) {}
1128
inline constexpr TextView::TextView(const char *ptr, unsigned n) noexcept : super_type(ptr, size_t(n)) {}
1129
inline constexpr TextView::TextView(const char *ptr, ssize_t n) noexcept
1130
  : super_type(ptr, n < 0 ? (ptr ? ::strlen(ptr) : 0) : size_t(n)) {}
1131
inline constexpr TextView::TextView(const char *ptr, int n) noexcept
1132
1.01k
  : super_type(ptr, n < 0 ? (ptr ? ::strlen(ptr) : 0) : size_t(n)) {}
1133
inline constexpr TextView::TextView(std::nullptr_t) noexcept : super_type(nullptr, 0) {}
1134
0
inline TextView::TextView(std::string const &str) noexcept : super_type(str) {}
1135
5.93k
inline constexpr TextView::TextView(super_type const &that) noexcept : super_type(that) {}
1136
938
template <size_t N> constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {}
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<9ul>(char const (&) [9ul])
swoc::_1_5_15::TextView::TextView<4ul>(char const (&) [4ul])
Line
Count
Source
1136
2
template <size_t N> constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {}
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<5ul>(char const (&) [5ul])
swoc::_1_5_15::TextView::TextView<3ul>(char const (&) [3ul])
Line
Count
Source
1136
234
template <size_t N> constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {}
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<70ul>(char const (&) [70ul])
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<38ul>(char const (&) [38ul])
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<16ul>(char const (&) [16ul])
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<19ul>(char const (&) [19ul])
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<22ul>(char const (&) [22ul])
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<89ul>(char const (&) [89ul])
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<57ul>(char const (&) [57ul])
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<110ul>(char const (&) [110ul])
swoc::_1_5_15::TextView::TextView<8ul>(char const (&) [8ul])
Line
Count
Source
1136
234
template <size_t N> constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {}
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<15ul>(char const (&) [15ul])
swoc::_1_5_15::TextView::TextView<6ul>(char const (&) [6ul])
Line
Count
Source
1136
234
template <size_t N> constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {}
swoc::_1_5_15::TextView::TextView<7ul>(char const (&) [7ul])
Line
Count
Source
1136
234
template <size_t N> constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {}
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<11ul>(char const (&) [11ul])
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<25ul>(char const (&) [25ul])
1137
0
template <typename C, typename> constexpr TextView::TextView(C const &c) : super_type(c.data(), c.size()) {}
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<swoc::_1_5_15::MemSpan<char>, void>(swoc::_1_5_15::MemSpan<char> const&)
Unexecuted instantiation: swoc::_1_5_15::TextView::TextView<swoc::_1_5_15::MemSpan<char const>, void>(swoc::_1_5_15::MemSpan<char const> const&)
1138
1139
inline void
1140
0
TextView::init_delimiter_set(std::string_view const &delimiters, std::bitset<256> &set) {
1141
0
  set.reset();
1142
0
  for (char c : delimiters)
1143
0
    set[static_cast<uint8_t>(c)] = true;
1144
0
}
1145
1146
inline auto
1147
9.97k
TextView::clear() -> self_type & {
1148
9.97k
  new (this) self_type();
1149
9.97k
  return *this;
1150
9.97k
}
1151
1152
inline constexpr char
1153
142k
TextView::operator*() const {
1154
142k
  return this->empty() ? char(0) : *(this->data());
1155
142k
}
1156
1157
inline constexpr bool
1158
1.51k
TextView::operator!() const noexcept {
1159
1.51k
  return this->empty();
1160
1.51k
}
1161
1162
44.7k
inline constexpr TextView::operator bool() const noexcept {
1163
44.7k
  return !this->empty();
1164
44.7k
}
1165
1166
inline auto
1167
24.7k
TextView::operator++() -> self_type & {
1168
24.7k
  this->remove_prefix(1);
1169
24.7k
  return *this;
1170
24.7k
}
1171
1172
inline auto
1173
0
TextView::operator++(int) -> self_type {
1174
0
  self_type zret{*this};
1175
0
  this->remove_prefix(1);
1176
0
  return zret;
1177
0
}
1178
1179
inline auto
1180
0
TextView::operator+=(size_t n) -> self_type & {
1181
0
  this->remove_prefix(n);
1182
0
  return *this;
1183
0
}
1184
1185
template <size_t N>
1186
inline auto
1187
TextView::operator=(const char (&s)[N]) -> self_type & {
1188
  return *this = self_type{s, s[N - 1] ? N : N - 1};
1189
}
1190
1191
inline auto
1192
66.3k
TextView::operator=(super_type const &that) -> self_type & {
1193
66.3k
  this->super_type::operator=(that);
1194
66.3k
  return *this;
1195
66.3k
}
1196
1197
inline auto
1198
0
TextView::operator=(char *&s) -> self_type & {
1199
0
  this->super_type::operator=(s);
1200
0
  return *this;
1201
0
}
1202
1203
inline auto
1204
0
TextView::operator=(char const *&s) -> self_type & {
1205
0
  this->super_type::operator=(s);
1206
0
  return *this;
1207
0
}
1208
1209
inline auto
1210
1.03k
TextView::operator=(const std::string &s) -> self_type & {
1211
1.03k
  this->super_type::operator=(s);
1212
1.03k
  return *this;
1213
1.03k
}
1214
1215
inline auto
1216
0
TextView::assign(char *&c_str) -> self_type & {
1217
0
  return this->assign(c_str, strlen(c_str));
1218
0
}
1219
1220
inline auto
1221
0
TextView::assign(char const *&c_str) -> self_type & {
1222
0
  return this->assign(c_str, strlen(c_str));
1223
0
}
1224
1225
inline auto
1226
0
TextView::assign(const std::string &s) -> self_type & {
1227
0
  *this = super_type(s);
1228
0
  return *this;
1229
0
}
1230
1231
inline TextView &
1232
51.7k
TextView::assign(char const *ptr, size_t n) {
1233
51.7k
  *this = super_type(ptr, n == npos ? (ptr ? ::strlen(ptr) : 0) : n);
1234
51.7k
  return *this;
1235
51.7k
}
1236
1237
inline TextView &
1238
13.4k
TextView::assign(char const *b, char const *e) {
1239
13.4k
  *this = super_type(b, e - b);
1240
13.4k
  return *this;
1241
13.4k
}
1242
1243
template <size_t N>
1244
inline auto
1245
TextView::assign(char const (&s)[N]) noexcept -> self_type & {
1246
  return *this = self_type{s, s[N - 1] ? N : N - 1};
1247
}
1248
1249
inline constexpr auto
1250
36.0k
TextView::prefix(size_t n) const noexcept -> self_type {
1251
36.0k
  return {this->data(), std::min(n, this->size())};
1252
36.0k
}
1253
1254
inline constexpr TextView
1255
0
TextView::prefix(int n) const noexcept {
1256
0
  return {this->data(), std::min<size_t>(n, this->size())};
1257
0
}
1258
1259
inline auto
1260
0
TextView::prefix_at(char c) const -> self_type {
1261
0
  self_type zret; // default to empty return.
1262
0
  if (auto n = this->find(c); n != npos) {
1263
0
    zret.assign(this->data(), n);
1264
0
  }
1265
0
  return zret;
1266
0
}
1267
1268
inline TextView
1269
0
TextView::prefix_at(std::string_view const &delimiters) const {
1270
0
  self_type zret; // default to empty return.
1271
0
  if (auto n = this->find_first_of(delimiters); n != npos) {
1272
0
    zret.assign(this->data(), n);
1273
0
  }
1274
0
  return zret;
1275
0
}
1276
1277
template <typename F>
1278
auto
1279
TextView::prefix_if(F const &pred) const -> self_type {
1280
  self_type zret; // default to empty return.
1281
  if (auto n = this->find_if(pred); n != npos) {
1282
    zret.assign(this->data(), n);
1283
  }
1284
  return zret;
1285
}
1286
1287
inline auto
1288
169k
TextView::remove_prefix(size_t n) -> self_type & {
1289
169k
  this->super_type::remove_prefix(std::min(n, this->size()));
1290
169k
  return *this;
1291
169k
}
1292
1293
inline TextView &
1294
0
TextView::remove_prefix_at(char c) {
1295
0
  if (auto n = this->find(c); n != npos) {
1296
0
    this->super_type::remove_prefix(n + 1);
1297
0
  }
1298
0
  return *this;
1299
0
}
1300
1301
inline auto
1302
0
TextView::remove_prefix_at(std::string_view const &delimiters) -> self_type & {
1303
0
  if (auto n = this->find_first_of(delimiters); n != npos) {
1304
0
    this->super_type::remove_prefix(n + 1);
1305
0
  }
1306
0
  return *this;
1307
0
}
1308
1309
template <typename F>
1310
auto
1311
TextView::remove_prefix_if(F const &pred) -> self_type & {
1312
  if (auto n = this->find_if(pred); n != npos) {
1313
    this->super_type::remove_prefix(n + 1);
1314
  }
1315
  return *this;
1316
}
1317
1318
inline TextView
1319
29.3k
TextView::split_prefix(size_t n) {
1320
29.3k
  self_type zret; // default to empty return.
1321
29.3k
  if (n < this->size()) {
1322
26.4k
    zret = this->prefix(n);
1323
26.4k
    this->remove_prefix(std::min(n + 1, this->size()));
1324
26.4k
  }
1325
29.3k
  return zret;
1326
29.3k
}
1327
1328
inline TextView
1329
0
TextView::split_prefix(int n) {
1330
0
  return this->split_prefix(size_t(n));
1331
0
}
1332
1333
inline TextView
1334
29.3k
TextView::split_prefix_at(char c) {
1335
29.3k
  return this->split_prefix(this->find(c));
1336
29.3k
}
1337
1338
inline TextView
1339
0
TextView::split_prefix_at(std::string_view const &delimiters) {
1340
0
  return this->split_prefix(this->find_first_of(delimiters));
1341
0
}
1342
1343
template <typename F>
1344
TextView::self_type
1345
TextView::split_prefix_if(F const &pred) {
1346
  return this->split_prefix(this->find_if(pred));
1347
}
1348
1349
inline TextView
1350
9.58k
TextView::take_prefix(size_t n) {
1351
9.58k
  n              = std::min(n, this->size());
1352
9.58k
  self_type zret = this->prefix(n);
1353
9.58k
  this->remove_prefix(std::min(n + 1, this->size()));
1354
9.58k
  return zret;
1355
9.58k
}
1356
1357
inline TextView
1358
1.83k
TextView::take_prefix_at(char c) {
1359
1.83k
  return this->take_prefix(this->find(c));
1360
1.83k
}
1361
1362
inline TextView
1363
0
TextView::take_prefix_at(std::string_view const &delimiters) {
1364
0
  return this->take_prefix(this->find_first_of(delimiters));
1365
0
}
1366
1367
template <typename F>
1368
auto
1369
TextView::take_prefix_if(F const &pred) -> self_type {
1370
  return this->take_prefix(this->find_if(pred));
1371
}
1372
1373
inline constexpr TextView
1374
19.7k
TextView::suffix(size_t n) const noexcept {
1375
19.7k
  n = std::min(n, this->size());
1376
19.7k
  return {this->data_end() - n, n};
1377
19.7k
}
1378
1379
inline constexpr TextView
1380
19.7k
TextView::suffix(int n) const noexcept {
1381
19.7k
  return this->suffix(size_t(n));
1382
19.7k
}
1383
1384
inline TextView
1385
0
TextView::suffix_at(char c) const {
1386
0
  self_type zret;
1387
0
  if (auto n = this->rfind(c); n != npos && n + 1 < this->size()) {
1388
0
    ++n;
1389
0
    zret.assign(this->data() + n, this->size() - n);
1390
0
  }
1391
0
  return zret;
1392
0
}
1393
1394
inline TextView
1395
0
TextView::suffix_at(std::string_view const &delimiters) const {
1396
0
  self_type zret;
1397
0
  if (auto n = this->find_last_of(delimiters); n != npos) {
1398
0
    ++n;
1399
0
    zret.assign(this->data() + n, this->size() - n);
1400
0
  }
1401
0
  return zret;
1402
0
}
1403
1404
template <typename F>
1405
auto
1406
TextView::suffix_if(F const &pred) const -> self_type {
1407
  self_type zret;
1408
  if (auto n = this->rfind_if(pred); n != npos) {
1409
    ++n;
1410
    zret.assign(this->data() + n, this->size() - n);
1411
  }
1412
  return zret;
1413
}
1414
1415
inline auto
1416
27.9k
TextView::remove_suffix(size_t n) -> self_type & {
1417
27.9k
  this->super_type::remove_suffix(std::min(n, this->size()));
1418
27.9k
  return *this;
1419
27.9k
}
1420
1421
inline TextView &
1422
0
TextView::remove_suffix_at(char c) {
1423
0
  if (auto n = this->rfind(c); n != npos) {
1424
0
    return this->remove_suffix(this->size() - n);
1425
0
  }
1426
0
  return this->clear();
1427
0
}
1428
1429
inline TextView &
1430
0
TextView::remove_suffix_at(std::string_view const &delimiters) {
1431
0
  if (auto n = this->find_last_of(delimiters); n != npos) {
1432
0
    return this->remove_suffix(this->size() - n);
1433
0
  }
1434
0
  return this->clear();
1435
0
}
1436
1437
template <typename F>
1438
TextView::self_type &
1439
TextView::remove_suffix_if(F const &pred) {
1440
  if (auto n = this->rfind_if(pred); n != npos) {
1441
    return this->remove_suffix(this->size() - n);
1442
  }
1443
  return this->clear();
1444
}
1445
1446
inline TextView
1447
0
TextView::split_suffix(size_t n) {
1448
0
  self_type zret;
1449
0
  n    = std::min(n, this->size());
1450
0
  zret = this->suffix(n);
1451
0
  this->remove_suffix(n + 1);
1452
0
  return zret;
1453
0
}
1454
1455
inline auto
1456
0
TextView::split_suffix(int n) -> self_type {
1457
0
  return this->split_suffix(size_t(n));
1458
0
}
1459
1460
inline TextView
1461
0
TextView::split_suffix_at(char c) {
1462
0
  auto idx = this->rfind(c);
1463
0
  return npos == idx ? self_type{} : this->split_suffix(this->size() - (idx + 1));
1464
0
}
1465
1466
inline auto
1467
0
TextView::split_suffix_at(std::string_view const &delimiters) -> self_type {
1468
0
  auto idx = this->find_last_of(delimiters);
1469
0
  return npos == idx ? self_type{} : this->split_suffix(this->size() - (idx + 1));
1470
0
}
1471
1472
template <typename F>
1473
TextView::self_type
1474
TextView::split_suffix_if(F const &pred) {
1475
  return this->split_suffix(this->rfind_if(pred));
1476
}
1477
1478
inline TextView
1479
0
TextView::take_suffix(size_t n) {
1480
0
  self_type zret{*this};
1481
0
  *this = zret.split_prefix(n);
1482
0
  return zret;
1483
0
}
1484
1485
inline TextView
1486
0
TextView::take_suffix(int n) {
1487
0
  return this->take_suffix(size_t(n));
1488
0
}
1489
1490
inline TextView
1491
0
TextView::take_suffix_at(char c) {
1492
0
  return this->take_suffix(this->rfind(c));
1493
0
}
1494
1495
inline TextView
1496
0
TextView::take_suffix_at(std::string_view const &delimiters) {
1497
0
  return this->take_suffix(this->find_last_of(delimiters));
1498
0
}
1499
1500
template <typename F>
1501
TextView::self_type
1502
TextView::take_suffix_if(F const &pred) {
1503
  return this->take_suffix_at(this->rfind_if(pred));
1504
}
1505
1506
template <typename F>
1507
inline size_t
1508
1.63k
TextView::find_if(F const &pred) const {
1509
3.27k
  for (const char *spot = this->data(), *limit = this->data_end(); spot < limit; ++spot)
1510
2.80k
    if (pred(*spot))
1511
1.17k
      return spot - this->data();
1512
468
  return npos;
1513
1.63k
}
1514
1515
template <typename F>
1516
inline size_t
1517
TextView::rfind_if(F const &pred) const {
1518
  for (const char *spot = this->data_end(), *limit = this->data(); spot > limit;)
1519
    if (pred(*--spot))
1520
      return spot - this->data();
1521
  return npos;
1522
}
1523
1524
inline TextView &
1525
0
TextView::ltrim(char c) {
1526
0
  this->remove_prefix(this->find_first_not_of(c));
1527
0
  return *this;
1528
0
}
1529
1530
inline TextView &
1531
0
TextView::rtrim(char c) {
1532
0
  auto n = this->find_last_not_of(c);
1533
0
  this->remove_suffix(this->size() - (n == npos ? 0 : n + 1));
1534
0
  return *this;
1535
0
}
1536
1537
inline TextView &
1538
0
TextView::trim(char c) {
1539
0
  return this->ltrim(c).rtrim(c);
1540
0
}
1541
1542
inline TextView &
1543
0
TextView::ltrim(CharSet const &delimiters) {
1544
0
  const char *spot  = this->data();
1545
0
  const char *limit = this->data_end();
1546
1547
0
  while (spot < limit && delimiters(*spot)) {
1548
0
    ++spot;
1549
0
  }
1550
0
  this->remove_prefix(spot - this->data());
1551
1552
0
  return *this;
1553
0
}
1554
1555
inline TextView &
1556
0
TextView::ltrim(std::string_view const &delimiters) {
1557
0
  return this->ltrim(CharSet(delimiters));
1558
0
}
1559
1560
inline TextView &
1561
0
TextView::ltrim(const char *delimiters) {
1562
0
  return this->ltrim(CharSet(delimiters));
1563
0
}
1564
1565
inline TextView &
1566
0
TextView::rtrim(CharSet const &delimiters) {
1567
0
  const char *spot  = this->data_end();
1568
0
  const char *limit = this->data();
1569
0
  while (limit < spot-- && delimiters(*spot)) {
1570
0
  }
1571
0
1572
0
  this->remove_suffix(this->data_end() - (spot + 1));
1573
0
  return *this;
1574
0
}
1575
1576
inline TextView &
1577
0
TextView::rtrim(std::string_view const &delimiters) {
1578
0
  return this->rtrim(CharSet(delimiters));
1579
0
}
1580
1581
inline TextView &
1582
0
TextView::trim(CharSet const &delimiters) {
1583
0
  const char *spot;
1584
0
  const char *limit;
1585
0
1586
0
  // Do this explicitly, so we don't have to initialize the character set twice.
1587
0
  for (spot = this->data(), limit = this->data_end(); spot < limit && delimiters(*spot); ++spot)
1588
0
    ;
1589
0
  this->remove_prefix(spot - this->data());
1590
0
1591
0
  spot  = this->data_end();
1592
0
  limit = this->data();
1593
0
  while (limit < spot-- && delimiters(*spot)) {
1594
0
  }
1595
0
  this->remove_suffix(this->data_end() - (spot + 1));
1596
0
1597
0
  return *this;
1598
0
}
1599
1600
inline TextView &
1601
0
TextView::trim(std::string_view const &delimiters) {
1602
0
  return this->trim(CharSet(delimiters));
1603
0
}
1604
1605
inline TextView &
1606
0
TextView::trim(const char *delimiters) {
1607
0
  return this->trim(CharSet(delimiters));
1608
0
}
1609
1610
template <typename F>
1611
TextView::self_type &
1612
31.8k
TextView::ltrim_if(F const &pred) {
1613
31.8k
  const char *spot;
1614
31.8k
  const char *limit;
1615
44.2k
  for (spot = this->data(), limit = this->data_end(); spot < limit && pred(*spot); ++spot)
1616
12.3k
    ;
1617
31.8k
  this->remove_prefix(spot - this->data());
1618
31.8k
  return *this;
1619
31.8k
}
swoc::_1_5_15::TextView& swoc::_1_5_15::TextView::ltrim_if<int (*)(int) noexcept>(int (* const&)(int) noexcept)
Line
Count
Source
1612
2.54k
TextView::ltrim_if(F const &pred) {
1613
2.54k
  const char *spot;
1614
2.54k
  const char *limit;
1615
7.19k
  for (spot = this->data(), limit = this->data_end(); spot < limit && pred(*spot); ++spot)
1616
4.64k
    ;
1617
2.54k
  this->remove_prefix(spot - this->data());
1618
2.54k
  return *this;
1619
2.54k
}
swoc::_1_5_15::TextView& swoc::_1_5_15::TextView::ltrim_if<unsigned int (*)(char)>(unsigned int (* const&)(char))
Line
Count
Source
1612
29.3k
TextView::ltrim_if(F const &pred) {
1613
29.3k
  const char *spot;
1614
29.3k
  const char *limit;
1615
37.0k
  for (spot = this->data(), limit = this->data_end(); spot < limit && pred(*spot); ++spot)
1616
7.68k
    ;
1617
29.3k
  this->remove_prefix(spot - this->data());
1618
29.3k
  return *this;
1619
29.3k
}
1620
1621
template <typename F>
1622
TextView::self_type &
1623
27.4k
TextView::rtrim_if(F const &pred) {
1624
27.4k
  const char *spot  = this->data_end();
1625
27.4k
  const char *limit = this->data();
1626
53.8k
  while (limit < spot-- && pred(*spot))
1627
26.3k
    ;
1628
27.4k
  this->remove_suffix(this->data_end() - (spot + 1));
1629
27.4k
  return *this;
1630
27.4k
}
swoc::_1_5_15::TextView& swoc::_1_5_15::TextView::rtrim_if<unsigned int (*)(char)>(unsigned int (* const&)(char))
Line
Count
Source
1623
27.1k
TextView::rtrim_if(F const &pred) {
1624
27.1k
  const char *spot  = this->data_end();
1625
27.1k
  const char *limit = this->data();
1626
51.1k
  while (limit < spot-- && pred(*spot))
1627
24.0k
    ;
1628
27.1k
  this->remove_suffix(this->data_end() - (spot + 1));
1629
27.1k
  return *this;
1630
27.1k
}
swoc::_1_5_15::TextView& swoc::_1_5_15::TextView::rtrim_if<int (*)(int) noexcept>(int (* const&)(int) noexcept)
Line
Count
Source
1623
365
TextView::rtrim_if(F const &pred) {
1624
365
  const char *spot  = this->data_end();
1625
365
  const char *limit = this->data();
1626
2.69k
  while (limit < spot-- && pred(*spot))
1627
2.33k
    ;
1628
365
  this->remove_suffix(this->data_end() - (spot + 1));
1629
365
  return *this;
1630
365
}
1631
1632
template <typename F>
1633
TextView::self_type &
1634
6.85k
TextView::trim_if(F const &pred) {
1635
6.85k
  return this->ltrim_if(pred).rtrim_if(pred);
1636
6.85k
}
swoc::_1_5_15::TextView& swoc::_1_5_15::TextView::trim_if<unsigned int (*)(char)>(unsigned int (* const&)(char))
Line
Count
Source
1634
6.49k
TextView::trim_if(F const &pred) {
1635
6.49k
  return this->ltrim_if(pred).rtrim_if(pred);
1636
6.49k
}
swoc::_1_5_15::TextView& swoc::_1_5_15::TextView::trim_if<int (*)(int) noexcept>(int (* const&)(int) noexcept)
Line
Count
Source
1634
365
TextView::trim_if(F const &pred) {
1635
365
  return this->ltrim_if(pred).rtrim_if(pred);
1636
365
}
1637
1638
constexpr inline auto
1639
642k
TextView::data() const noexcept -> value_type const * {
1640
642k
  return super_type::data();
1641
642k
}
1642
1643
constexpr inline auto
1644
113k
TextView::data_end() const noexcept -> value_type const * {
1645
113k
  return this->data() + this->size();
1646
113k
}
1647
1648
inline constexpr TextView
1649
458
TextView::substr(size_type pos, size_type count) const noexcept {
1650
458
  if (pos >= this->size()) {
1651
0
    return {};
1652
0
  }
1653
458
  count = std::min(this->size() - pos, count);
1654
458
  return {this->data() + pos, count};
1655
458
}
1656
1657
inline bool
1658
5.23k
TextView::starts_with(std::string_view const &prefix) const noexcept {
1659
5.23k
  return this->size() >= prefix.size() && 0 == ::memcmp(this->data(), prefix.data(), prefix.size());
1660
5.23k
}
1661
1662
inline bool
1663
0
TextView::starts_with(char const *prefix) const {
1664
0
  return this->starts_with(super_type(prefix));
1665
0
}
1666
inline bool
1667
0
TextView::starts_with_nocase(char const *prefix) const {
1668
0
  return this->starts_with_nocase(super_type{prefix});
1669
0
}
1670
1671
inline bool
1672
0
TextView::starts_with(char c) const noexcept {
1673
0
  return !this->empty() && c == this->front();
1674
0
}
1675
inline bool
1676
0
TextView::starts_with_nocase(char c) const noexcept {
1677
0
  return !this->empty() && tolower(c) == tolower(this->front());
1678
0
}
1679
1680
inline bool
1681
0
TextView::starts_with_nocase(std::string_view const &prefix) const noexcept {
1682
0
  return this->size() >= prefix.size() && 0 == ::strncasecmp(this->data(), prefix.data(), prefix.size());
1683
0
}
1684
1685
inline bool
1686
0
TextView::ends_with(std::string_view const &suffix) const noexcept {
1687
0
  return this->size() >= suffix.size() && 0 == ::memcmp(this->data_end() - suffix.size(), suffix.data(), suffix.size());
1688
0
}
1689
1690
inline bool
1691
0
TextView::ends_with_nocase(std::string_view const &suffix) const noexcept {
1692
0
  return this->size() >= suffix.size() && 0 == ::strncasecmp(this->data_end() - suffix.size(), suffix.data(), suffix.size());
1693
0
}
1694
1695
inline bool
1696
0
TextView::ends_with(char const *suffix) const {
1697
0
  return this->ends_with(super_type(suffix));
1698
0
}
1699
1700
inline bool
1701
0
TextView::ends_with_nocase(char const *suffix) const {
1702
0
  return this->ends_with_nocase(super_type(suffix));
1703
0
}
1704
1705
inline bool
1706
0
TextView::ends_with(char c) const noexcept {
1707
0
  return !this->empty() && c == this->back();
1708
0
}
1709
inline bool
1710
0
TextView::ends_with_nocase(char c) const noexcept {
1711
0
  return !this->empty() && tolower(c) == tolower(this->back());
1712
0
}
1713
1714
template <typename Stream>
1715
Stream &
1716
0
TextView::stream_write(Stream &os, const TextView &b) const {
1717
  // Local function, avoids extra template work.
1718
0
  static const auto stream_fill = [](Stream &ostream, size_t n) -> Stream & {
1719
0
    static constexpr size_t pad_size = 8;
1720
0
    typename Stream::char_type padding[pad_size];
1721
1722
0
    std::fill_n(padding, pad_size, ostream.fill());
1723
0
    for (; n >= pad_size && ostream.good(); n -= pad_size)
1724
0
      ostream.write(padding, pad_size);
1725
0
    if (n > 0 && ostream.good())
1726
0
      ostream.write(padding, n);
1727
0
    return ostream;
1728
0
  };
1729
1730
0
  const std::size_t w = os.width();
1731
0
  if (w <= b.size()) {
1732
0
    os.write(b.data(), b.size());
1733
0
  } else {
1734
0
    const std::size_t pad_size = w - b.size();
1735
0
    const bool align_left      = (os.flags() & Stream::adjustfield) == Stream::left;
1736
0
    if (!align_left && os.good())
1737
0
      stream_fill(os, pad_size);
1738
0
    if (os.good())
1739
0
      os.write(b.data(), b.size());
1740
0
    if (align_left && os.good())
1741
0
      stream_fill(os, pad_size);
1742
0
  }
1743
0
  return os;
1744
0
}
1745
1746
template <typename F>
1747
TextView::self_type
1748
0
TextView::clip_prefix_of(F const &pred) {
1749
0
  size_t idx = 0;
1750
0
  for (auto spot = this->data(), limit = spot + this->size(); spot < limit && pred(*spot); ++spot, ++idx)
1751
0
    ; // empty
1752
0
  TextView token = this->prefix(idx);
1753
0
  this->remove_prefix(idx);
1754
0
  return token;
1755
0
}
1756
1757
template <typename F>
1758
TextView::self_type
1759
TextView::clip_suffix_of(F const &pred) {
1760
  size_t idx = this->size() - 1;
1761
  for (auto spot = this->data() + idx, limit = this->data(); spot >= limit && pred(*spot); --spot, --idx)
1762
    ; // empty
1763
  TextView token = this->suffix(idx);
1764
  this->remove_suffix(idx);
1765
  return token;
1766
}
1767
/// @endcond TextView_INTERNAL
1768
1769
// Provide an instantiation for @c std::ostream as it's likely this is the only one ever used.
1770
extern template std::ostream &TextView::stream_write(std::ostream &, const TextView &) const;
1771
1772
/** A transform view.
1773
 *
1774
 * @tparam X Transform functor type.
1775
 * @tparam V Source view type.
1776
 *
1777
 * A transform view acts like a view on the original source view @a V with each element transformed by
1778
 * @a X.
1779
 *
1780
 * This is used most commonly with @c std::string_view. For example, if the goal is to handle a
1781
 * piece of text as if it were lower case without changing the actual text, the following would
1782
 * make that possible.
1783
 * @code
1784
 * std:::string_view source; // original text.
1785
 * TransformView<int (*)(int) noexcept, std::string_view> xv(&tolower, source);
1786
 * @endcode
1787
 *
1788
 * To avoid having to figure out the exact signature of the transform, the convenience function
1789
 * @c transform_view_of is provide.
1790
 * @code
1791
 * std::string_view source; // original text.
1792
 * auto xv = transform_view_of(&tolower, source);
1793
 * @endcode
1794
 *
1795
 * This class supports iterators but those should be avoided as use of them makes extra copies of the instance which
1796
 * may be expensive if the functor is expensive. In cases where the functor is a function pointer or a lambda this isn't
1797
 * an issue.
1798
 */
1799
template <typename X, typename V> class TransformView {
1800
  using self_type       = TransformView; ///< Self reference type.
1801
  using source_iterator = decltype(V{}.begin());
1802
1803
public:
1804
  using transform_type    = X; ///< Export transform functor type.
1805
  using source_view_type  = V; ///< Export source view type.
1806
  using source_value_type = std::remove_reference_t<decltype(*source_iterator{})>;
1807
  /// Result type of calling the transform on an element of the source view.
1808
  using value_type = std::invoke_result_t<transform_type, source_value_type>;
1809
  /// This class serves as its own iterator.
1810
  using iterator = self_type;
1811
1812
  /** Construct a transform view using transform @a xf on source view @a v.
1813
   *
1814
   * @param xf Transform instance.
1815
   * @param v Source view.
1816
   */
1817
  TransformView(transform_type &&xf, source_view_type const &v);
1818
1819
  /** Construct a transform view using transform @a xf on source view @a v.
1820
   *
1821
   * @param xf Transform instance.
1822
   * @param v Source view.
1823
   */
1824
  TransformView(transform_type const &xf, source_view_type const &v);
1825
1826
  /// Copy constructor.
1827
  TransformView(self_type const &that) = default;
1828
  /// Move constructor.
1829
  TransformView(self_type &&that) = default;
1830
1831
  /// Copy assignment.
1832
  self_type &operator=(self_type const &that) = default;
1833
  /// Move assignment.
1834
  self_type &operator=(self_type &&that) = default;
1835
1836
  /// Equality.
1837
  bool operator==(self_type const &that) const;
1838
  /// Inequality.
1839
  bool operator!=(self_type const &that) const;
1840
1841
  /// Get the current element.
1842
  value_type operator*() const;
1843
  /// Move to next element.
1844
  self_type &operator++();
1845
  /// Move to next element.
1846
  self_type operator++(int);
1847
1848
  /// Check if view is empty.
1849
  bool empty() const;
1850
  /// Check if bool is not empty.
1851
  explicit operator bool() const;
1852
1853
  /// Iterator to first transformed character.
1854
  iterator
1855
  begin() const {
1856
    return *this;
1857
  }
1858
  /// Iterator past last transformed character.
1859
  iterator
1860
  end() const {
1861
    return self_type{_xf, _limit};
1862
  }
1863
1864
protected:
1865
  transform_type _xf;     ///< Per character transform functor.
1866
  source_iterator _spot;  ///< Current location in the source view.
1867
  source_iterator _limit; ///< End of source view.
1868
1869
  /// Special constructor for making an empty instance to serve as the @c end iterator.
1870
  TransformView(transform_type &&xf, source_iterator &&limit) : _xf(xf), _spot(limit), _limit(limit) {}
1871
};
1872
1873
template <typename X, typename V>
1874
TransformView<X, V>::TransformView(transform_type &&xf, source_view_type const &v) : _xf(xf), _spot(v.begin()), _limit(v.end()) {}
1875
1876
template <typename X, typename V>
1877
TransformView<X, V>::TransformView(transform_type const &xf, source_view_type const &v)
1878
336
  : _xf(xf), _spot(v.begin()), _limit(v.end()) {}
1879
1880
template <typename X, typename V>
1881
auto
1882
1.59k
TransformView<X, V>::operator*() const -> value_type {
1883
1.59k
  return _xf(*_spot);
1884
1.59k
}
1885
1886
template <typename X, typename V>
1887
auto
1888
1.59k
TransformView<X, V>::operator++() -> self_type & {
1889
1.59k
  ++_spot;
1890
1.59k
  return *this;
1891
1.59k
}
1892
1893
template <typename X, typename V>
1894
auto
1895
0
TransformView<X, V>::operator++(int) -> self_type {
1896
0
  self_type zret{*this};
1897
0
  ++_spot;
1898
0
  return zret;
1899
0
}
1900
1901
template <typename X, typename V>
1902
bool
1903
TransformView<X, V>::empty() const {
1904
  return _spot == _limit;
1905
}
1906
1907
1.93k
template <typename X, typename V> TransformView<X, V>::operator bool() const {
1908
1.93k
  return _spot != _limit;
1909
1.93k
}
1910
1911
template <typename X, typename V>
1912
bool
1913
TransformView<X, V>::operator==(self_type const &that) const {
1914
  return _spot == that._spot && _limit == that._limit;
1915
}
1916
1917
template <typename X, typename V>
1918
bool
1919
TransformView<X, V>::operator!=(self_type const &that) const {
1920
  return _spot != that._spot || _limit != that._limit;
1921
}
1922
1923
/** Create a transformed view of a source.
1924
 *
1925
 * @tparam X The transform functor type.
1926
 * @tparam V The source type.
1927
 * @param xf The transform.
1928
 * @param src The view source.
1929
 * @return A @c TransformView that applies @a xf to @a src.
1930
 */
1931
template <typename X, typename V>
1932
TransformView<X, V>
1933
336
transform_view_of(X const &xf, V const &src) {
1934
336
  return TransformView<X, V>(xf, src);
1935
336
}
1936
1937
/** Identity transform view.
1938
 *
1939
 * @tparam V The source type.
1940
 *
1941
 * This is a transform that returns the input unmodified. This is convenient when a transform is
1942
 * required in general but not in all cases.
1943
 */
1944
template <typename V> class TransformView<void, V> {
1945
  using self_type = TransformView; ///< Self reference type.
1946
  /// Iterator over source, for internal use.
1947
  using source_iterator = decltype(V{}.begin());
1948
1949
public:
1950
  using source_view_type  = V; ///< Export source view type.
1951
  using source_value_type = std::remove_reference_t<decltype(*source_iterator{})>;
1952
  /// Result type of calling the transform on an element of the source view.
1953
  using value_type = source_value_type;
1954
  /// This class serves as its own iterator.
1955
  using iterator = self_type;
1956
1957
  /** Construct identity transform view from @a v.
1958
   *
1959
   * @param v Source view.
1960
   */
1961
  TransformView(source_view_type const &v) : _spot(v.begin()), _limit(v.end()) {}
1962
1963
  /// Copy constructor.
1964
  TransformView(self_type const &that) = default;
1965
  /// Move constructor.
1966
  TransformView(self_type &&that) = default;
1967
1968
  /// Copy assignment.
1969
  self_type &operator=(self_type const &that) = default;
1970
  /// Move assignment.
1971
  self_type &operator=(self_type &&that) = default;
1972
1973
  /// Equality.
1974
  bool operator==(self_type const &that) const;
1975
  /// Inequality.
1976
  bool operator!=(self_type const &that) const;
1977
1978
  /// Get the current element.
1979
  value_type operator*() const;
1980
1981
  /// Move to next element.
1982
  self_type &operator++();
1983
1984
  /// Move to next element.
1985
  self_type operator++(int);
1986
1987
  /// Check if view is empty.
1988
  bool empty() const;
1989
1990
  /// Check if bool is not empty.
1991
  explicit operator bool() const;
1992
1993
  /// Iterator to first transformed character.
1994
  iterator begin() const;
1995
  /// Iterator past last transformed character.
1996
  iterator end() const;
1997
1998
protected:
1999
  source_iterator _spot;  ///< Current location.
2000
  source_iterator _limit; ///< End marker.
2001
2002
  /// Special constructor for making an empty instance to serve as the @c end iterator.
2003
  explicit TransformView(source_iterator &&limit) : _spot(limit), _limit(limit) {}
2004
};
2005
2006
template <typename V>
2007
auto
2008
0
TransformView<void, V>::operator*() const -> value_type {
2009
0
  return *_spot;
2010
0
}
2011
2012
template <typename V>
2013
auto
2014
0
TransformView<void, V>::operator++() -> self_type & {
2015
0
  ++_spot;
2016
0
  return *this;
2017
0
}
2018
2019
template <typename V>
2020
auto
2021
TransformView<void, V>::operator++(int) -> self_type {
2022
  auto zret{*this};
2023
  ++*this;
2024
  return zret;
2025
}
2026
2027
template <typename V>
2028
bool
2029
TransformView<void, V>::operator==(const self_type &that) const {
2030
  return _spot == that._spot && _limit == that._limit;
2031
}
2032
2033
template <typename V>
2034
bool
2035
TransformView<void, V>::operator!=(const self_type &that) const {
2036
  return _spot != that._spot || _limit != that._limit;
2037
}
2038
2039
template <typename V>
2040
bool
2041
TransformView<void, V>::empty() const {
2042
  return _spot == _limit;
2043
}
2044
2045
0
template <typename V> TransformView<void, V>::operator bool() const {
2046
0
  return _spot != _limit;
2047
0
}
2048
2049
template <typename V>
2050
auto
2051
TransformView<void, V>::begin() const -> self_type {
2052
  return *this;
2053
}
2054
2055
template <typename V>
2056
auto
2057
TransformView<void, V>::end() const -> self_type {
2058
  return self_type{_limit};
2059
}
2060
2061
/// @cond INTERNAL_DETAIL
2062
// Capture @c void transforms and make them identity transforms.
2063
template <typename V>
2064
TransformView<void, V>
2065
0
transform_view_of(V const &v) {
2066
0
  return TransformView<void, V>(v);
2067
0
}
2068
2069
/// @endcond
2070
2071
/** User literals for TextView.
2072
 *
2073
 * - _tv : TextView
2074
 * - _sv : std::string_view
2075
 */
2076
namespace literals {
2077
/** Literal constructor for @c std::string_view.
2078
 *
2079
 * @param s The source string.
2080
 * @param n Size of the source string.
2081
 * @return A @c string_view
2082
 *
2083
 * @internal This is provided because the STL one does not support @c constexpr which seems
2084
 * rather bizarre to me, but there it is. Update: this depends on the version of the compiler,
2085
 * so hopefully someday this can be removed.
2086
 */
2087
constexpr std::string_view
2088
0
operator""_sv(const char *s, size_t n) {
2089
0
  return {s, n};
2090
0
}
2091
2092
/** Literal constructor for @c swoc::TextView.
2093
 *
2094
 * @param s The source string.
2095
 * @param n Size of the source string.
2096
 * @return A @c string_view
2097
 *
2098
 * @internal This is provided because the STL one does not support @c constexpr which seems
2099
 * rather bizarre to me, but there it is. Update: this depends on the version of the compiler,
2100
 * so hopefully someday this can be removed.
2101
 */
2102
constexpr swoc::TextView
2103
0
operator""_tv(const char *s, size_t n) {
2104
0
  return {s, n};
2105
0
}
2106
} // namespace literals
2107
2108
}} // namespace swoc::SWOC_VERSION_NS
2109
2110
namespace std {
2111
/// Write the contents of @a view to the stream @a os.
2112
ostream &operator<<(ostream &os, const swoc::TextView &view);
2113
2114
/// @cond INTERNAL_DETAIL
2115
/* For interaction with specific STL interfaces, primarily std::filesystem. Along with the
2116
 * dereference operator, this enables a @c TextView to act as a character iterator to a C string
2117
 * even if the internal view is not nul terminated.
2118
 * @note Putting these directly in the class doesn't seem to work.
2119
 */
2120
template <> struct iterator_traits<swoc::TextView> {
2121
  using value_type        = char;
2122
  using pointer_type      = const char *;
2123
  using reference_type    = const char &;
2124
  using difference_type   = ssize_t;
2125
  using iterator_category = forward_iterator_tag;
2126
};
2127
2128
template <typename X, typename V> struct iterator_traits<swoc::TransformView<X, V>> {
2129
  using value_type        = typename swoc::TransformView<X, V>::value_type;
2130
  using pointer_type      = const value_type *;
2131
  using reference_type    = const value_type &;
2132
  using difference_type   = ssize_t;
2133
  using iterator_category = forward_iterator_tag;
2134
};
2135
2136
template <> struct hash<swoc::TextView> {
2137
  static constexpr hash<string_view> super_hash{};
2138
  size_t
2139
0
  operator()(swoc::TextView const &s) const {
2140
0
    return super_hash(s);
2141
0
  }
2142
};
2143
/// @endcond
2144
2145
} // namespace std
2146
2147
#if __GNUC__ == 11
2148
#pragma GCC diagnostic pop
2149
#endif