/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 <rim(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 <rim(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 <rim(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 <rim(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 <rim_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 |