| // SPDX-License-Identifier: Apache-2.0 | |
| // Copyright Apache Software Foundation 2019 | |
| /** @file | |
| Class for handling "views" of text. Views presume the memory for the buffer is managed | |
| elsewhere and allow efficient access to segments of the buffer without copies. Views are read | |
| only as the view doesn't own the memory. Along with generic buffer methods are specialized | |
| methods to support better string parsing, particularly token based parsing. | |
| This class is based on @c std::string_view and is easily and cheaply converted to and from that | |
| class. | |
| */ | |
| #pragma once | |
| #include <bitset> | |
| #include <cstdint> | |
| #include <iosfwd> | |
| #include <memory.h> | |
| #include <string> | |
| #include <string_view> | |
| #include <limits> | |
| #include <type_traits> | |
| #include "swoc/swoc_version.h" | |
| #include "swoc/string_view_util.h" | |
| // For no apparent reason, g++ 11 complains about array bound violations with either suffix_at or | |
| // assign, the error message is too vague for me to be sure - it doesn't even provide the location of | |
| // the method invocation. I've been using g++ 12 for development and I don't see that error. | |
| #if __GNUC__ == 11 | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Warray-bounds" | |
| #endif | |
| namespace swoc { inline namespace SWOC_VERSION_NS { | |
| class TextView; | |
| /** A set of characters. | |
| * | |
| */ | |
| class CharSet { | |
| using self_type = CharSet; | |
| public: | |
| /** Construct from character sequence. | |
| * | |
| * @param chars Character sequence. | |
| * | |
| * The charset becomes @c true for every character in the sequence. | |
| */ | |
| constexpr CharSet(TextView const &chars); | |
| /** Check if character is in the charset. | |
| * | |
| * @param c Character to check. | |
| * @return @c true if @a c is in the charset, @c false if not. | |
| */ | |
| bool operator()(unsigned char c) const; | |
| /** Check if character is in the charset. | |
| * | |
| * @param c Character to check. | |
| * @return @c true if @a c is in the charset, @c false if not. | |
| */ | |
| bool operator()(char c) const; | |
| protected: | |
| std::bitset<std::numeric_limits<unsigned char>::max() + 1> _chars; | |
| }; | |
| /** A read only view of a contiguous piece of memory. | |
| A @c TextView does not own the memory to which it refers, it is simply a view of part of some | |
| (presumably) larger memory object. The purpose is to allow working in a read only way a specific | |
| part of the memory. A classic example for ATS is working with HTTP header fields and values | |
| which need to be accessed independently but preferably without copying. A @c TextView supports | |
| this style. | |
| @note To simplify the interface there is no constructor taking only a character pointer. | |
| Constructors require either a literal string or an explicit length. This avoid ambiguities which | |
| are much more annoying that explicitly calling @c strlen on a character pointer. | |
| @internal For construction, assignment operator, and @c assign method, there are a lot of overloads | |
| because users would like to be able to use the same sort of arguments for all of these. This includes | |
| - self / parent type | |
| - @c std::string | |
| - literal string | |
| - C-string pointer | |
| - pointer and count | |
| - begin/end style pointers. | |
| - character containers that have the STL standard @c size and @c data methods. | |
| */ | |
| class TextView : public std::string_view { | |
| using self_type = TextView; ///< Self reference type. | |
| using super_type = std::string_view; ///< Parent type. | |
| public: | |
| /// Default constructor (empty buffer). | |
| constexpr TextView() noexcept = default; | |
| /// Construct from a @c std::string_view or @c TextView | |
| /// @note This provides an user defined conversion from @c std::string_view to @c TextView. The | |
| /// reverse conversion is implicit in @c TextView being a subclass of @c std::string_view. | |
| constexpr TextView(super_type const &that) noexcept; | |
| /** Construct from pointer and size. | |
| * | |
| * @param ptr Pointer to first character. | |
| * @param n Number of characters. | |
| * | |
| * If @a n is @c npos then @c ptr is presumed to be a C string and checked for length. If @c ptr | |
| * is @c nullptr the length is 0. Otherwise @c strlen is used to calculate the length. | |
| */ | |
| constexpr TextView(char const *ptr, size_t n) noexcept; | |
| /** Construct from pointer and size. | |
| * | |
| * @param ptr Pointer to first character. | |
| * @param n Number of characters. | |
| */ | |
| constexpr TextView(char const *ptr, unsigned n) noexcept; | |
| /** Construct from pointer and size. | |
| * | |
| * @param ptr Pointer to first character. | |
| * @param n Number of characters. | |
| * | |
| * If @a n is negative then @c ptr is presumed to be a C string and checked for length. If @c ptr | |
| * is @c nullptr the length is 0. Otherwise @c strlen is used to calculate the length. | |
| */ | |
| constexpr TextView(char const *ptr, ssize_t n) noexcept; | |
| /** Construct from pointer and size. | |
| * | |
| * @param ptr Pointer to first character. | |
| * @param n Number of characters. | |
| * | |
| * If @a n is negative then @c ptr is presumed to be a C string and checked for length. If @c ptr | |
| * is @c nullptr the length is 0. Otherwise @c strlen is used to calculate the length. | |
| */ | |
| constexpr TextView(char const *ptr, int n) noexcept; | |
| /** Construct from a half open range [first, last). | |
| * | |
| * @param first Start of half open range. | |
| * @param last End of half open range. | |
| * | |
| * The character at @a first will be in the view, but the character at @a last will not. | |
| * | |
| * @note @c explicit to avoid interpreting a string initializer list as a view. | |
| * | |
| * @internal For the love of Turing, WHY DID YOU DO THIS? | |
| * | |
| * Well, estemed reader, because the C++ standard doesn't have a better way to support overloads | |
| * that handle character pointers and literal strings differently. If the parameters were simply | |
| * <tt>(char const *, char const *)</tt> then a construct like <tt>{ "really", "broken" }</tt> can | |
| * be interpreted as a @c TextView because the elements implicitly convert to <tt>char const | |
| * *</tt>. This makes no sense and creates some @b very annoying ambiguities for lists of strings | |
| * if there are exactly two in the list. See @c Lexicon for an example. | |
| * | |
| * The template itself does the check to make sure it's a character @b pointer and not an array. Arrays | |
| * are handled by a different constructor so this only disables constructing from two char arrays | |
| * which IMHO makes no sense and should be forbidden. | |
| */ | |
| template <typename T> | |
| explicit TextView( | |
| T first, | |
| std::enable_if_t<!std::is_array_v<T> && std::is_pointer_v<T> && std::is_convertible_v<T, char const *>, T> last) noexcept | |
| : super_type(first, last - first) {} | |
| /** Construct from any character container following STL standards. | |
| * | |
| * @tparam C Container type. | |
| * @param c container | |
| * | |
| * The container type must have the methods @c data and @c size which must return values convertible | |
| * to @c char @c const @c * and @c size_t respectively. | |
| */ | |
| template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().data()), char const *> && | |
| std::is_convertible_v<decltype(std::declval<C>().size()), size_t>, | |
| void>> | |
| constexpr TextView(C const &c); | |
| /** Construct from literal string or array. | |
| All elements of the array are included in the view unless the last element is nul, in which case it is elided. | |
| If this is inappropriate then a constructor with an explicit size should be used. | |
| @code | |
| TextView a("A literal string"); | |
| @endcode | |
| The last character in @a a will be 'g'. | |
| */ | |
| template <size_t N> constexpr TextView(const char (&s)[N]) noexcept; | |
| /** Construct from a C-string. | |
| * | |
| * @param src A pointer to a C-string. | |
| * | |
| * The view does not include the terminating nul. | |
| * | |
| * @internal @a src a reference because it is otherwise ambiguous with the literal constructor. | |
| */ | |
| TextView(char *&src) : super_type(src, src ? strlen(src) : 0) {} | |
| /** Construct from a const C-string. | |
| * | |
| * @param src Pointer to a const C-string. | |
| * | |
| * The view does not include the terminating nul. | |
| * | |
| * @internal @a src a reference because it is otherwise ambiguous with the literal constructor. | |
| */ | |
| TextView(char const *&src) : super_type(src, src ? strlen(src) : 0) {} | |
| /** Construct from nullptr. | |
| This implicitly makes the length 0. | |
| */ | |
| constexpr TextView(std::nullptr_t) noexcept; | |
| /// Construct from @c std::string, referencing the entire string contents. | |
| /// @internal This can't be @c constexpr because this uses methods in @c std::string that may | |
| /// not be @c constexpr. | |
| TextView(std::string const &str) noexcept; | |
| /// Assign a super class instance, @c std::string_view to @a this. | |
| self_type &operator=(super_type const &that); | |
| /// Assign a constant array to @a this. | |
| /// @note If the last character of @a s is a nul byte, it is not included in the view. | |
| template <size_t N> self_type &operator=(const char (&s)[N]); | |
| /// Assign from C-string @a s. | |
| self_type &operator=(char *&s); | |
| /// Assign from C-string @a s. | |
| self_type &operator=(char const *&s); | |
| /// Assign from a @c std::string. | |
| self_type &operator=(const std::string &s); | |
| /** Assign a view of the @a c_str | |
| * | |
| * @param c_str Pointer to C string. | |
| * @return @a this | |
| * | |
| * @note @c c_str must be a null terminated string. The null byte is not included in the view. | |
| */ | |
| self_type &assign(char *&c_str); | |
| /** Assign a view of the @a c_str | |
| * | |
| * @param c_str Pointer to C string. | |
| * @return @a this | |
| * | |
| * @note @c c_str must be a null terminated string. The null byte is not included in the view. | |
| */ | |
| self_type &assign(char const *&c_str); | |
| /** Assign from a pointer and size. | |
| * | |
| * @param ptr Pointer to first character of the view. | |
| * @param n Length of the view. | |
| * @return @a this | |
| * | |
| * if @a n is @a npos then @c strlen is used determine the size of the view. | |
| */ | |
| self_type &assign(char const *ptr, size_t n); | |
| /** Assign the half open view [ @a b , @a e ) to @a this | |
| * | |
| * @param b First character in the view. | |
| * @param e One character after the last character in the view. | |
| * @return @a this | |
| */ | |
| self_type &assign(char const *b, char const *e); | |
| /// Explicitly set the view from a @c std::string | |
| self_type &assign(std::string const &s); | |
| /** Assign literal string or array. | |
| * All elements of the array are included in the view unless the last element is nul, in which case it is elided. | |
| * If this is inappropriate then a constructor with an explicit size should be used. | |
| * | |
| * @code | |
| * tv.assign("A literal string"); | |
| * @endcode | |
| * The last character in @a tv will be 'g'. | |
| */ | |
| template <size_t N> self_type &assign(const char (&s)[N]) noexcept; | |
| /** Assign from any character container following STL standards. | |
| * | |
| * @tparam C Container type. | |
| * @param c container | |
| * | |
| * The container type must have the methods @c data and @c size which must return values convertible | |
| * to @c char @c const @c * and @c size_t respectively. | |
| */ | |
| template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().data()), char const *> && | |
| std::is_convertible_v<decltype(std::declval<C>().size()), size_t>, | |
| void>> | |
| constexpr self_type & | |
| assign(C const &c) { | |
| return this->assign(c.data(), c.size()); | |
| } | |
| /** Dereference operator. | |
| @note This allows the view to be used as if it were a character iterator to a null terminated | |
| string which is handy for several other STL interfaces. | |
| @return The first byte in the view, or a nul character if the view is empty. | |
| */ | |
| /// @return The first byte in the view. | |
| constexpr char operator*() const; | |
| /** Discard the first byte of the view. | |
| * | |
| * @return @a this. | |
| */ | |
| self_type &operator++(); | |
| /** Discard the first byte of the view. | |
| * | |
| * @return The view before discarding the byte. | |
| */ | |
| self_type operator++(int); | |
| /** Discard the first @a n bytes of the view. | |
| * | |
| * Equivalent to @c remove_prefix(n). | |
| * @return @a this | |
| */ | |
| self_type &operator+=(size_t n); | |
| /// Check for empty view. | |
| /// @return @c true if the view has a nullptr @b or zero size. | |
| constexpr bool operator!() const noexcept; | |
| /// Check for non-empty view. | |
| /// @return @c true if the view refers to a non-empty range of bytes. | |
| explicit constexpr operator bool() const noexcept; | |
| /// Clear the view (become an empty view). | |
| self_type &clear(); | |
| /// Get the offset of the first character for which @a pred is @c true. | |
| template <typename F> size_t find_if(F const &pred) const; | |
| /// Get the offset of the last character for which @a pred is @c true. | |
| template <typename F> size_t rfind_if(F const &pred) const; | |
| /** Remove bytes that match @a c from the start of the view. | |
| * | |
| * @return @a this | |
| */ | |
| self_type <rim(char c); | |
| /** Remove bytes from the start of the view that are in @a delimiters. | |
| * | |
| * @return @a this | |
| */ | |
| self_type <rim(CharSet const &delimiters); | |
| /** Remove bytes from the start of the view that are in @a delimiters. | |
| * | |
| * @return @a this | |
| */ | |
| self_type <rim(std::string_view const &delimiters); | |
| /** Remove bytes from the start of the view that are in @a delimiters. | |
| * | |
| * @internal This is needed to avoid collisions with the templated predicate style. | |
| * | |
| * @return @c *this | |
| */ | |
| self_type <rim(const char *delimiters); | |
| /** Remove bytes from the start of the view for which @a pred is @c true. | |
| @a pred must be a functor taking a @c char argument and returning @c bool. | |
| @return @c *this | |
| */ | |
| template <typename F> self_type <rim_if(F const &pred); | |
| /** Remove bytes that match @a c from the end of the view. | |
| * | |
| * @return @a this | |
| */ | |
| self_type &rtrim(char c); | |
| /** Remove bytes from the end of the view that are in @a delimiters. | |
| * | |
| * @return @a this | |
| */ | |
| self_type &rtrim(CharSet const &delimiters); | |
| /** Remove bytes from the end of the view that are in @a delimiters. | |
| * @return @a this | |
| */ | |
| self_type &rtrim(std::string_view const &delimiters); | |
| /** Remove bytes from the end of the view for which @a pred is @c true. | |
| * | |
| * @a pred must be a functor taking a @c char argument and returning @c bool. | |
| * | |
| * @return @c *this | |
| */ | |
| template <typename F> self_type &rtrim_if(F const &pred); | |
| /** Remove bytes that match @a c from the start and end of this view. | |
| * | |
| * @return @a this | |
| */ | |
| self_type &trim(char c); | |
| /** Remove bytes from the start and end of the view that are in @a delimiters. | |
| * @return @a this | |
| */ | |
| self_type &trim(CharSet const &delimiters); | |
| /** Remove bytes from the start and end of the view that are in @a delimiters. | |
| * @return @a this | |
| */ | |
| self_type &trim(std::string_view const &delimiters); | |
| /** Remove bytes from the start and end of the view that are in @a delimiters. | |
| @internal This is needed to avoid collisions with the templated predicate style. | |
| @return @c *this | |
| */ | |
| self_type &trim(const char *delimiters); | |
| /** Remove bytes from the start and end of the view for which @a pred is @c true. | |
| @a pred must be a functor taking a @c char argument and returning @c bool. | |
| @return @c *this | |
| */ | |
| template <typename F> self_type &trim_if(F const &pred); | |
| /** Get a view of the first @a n bytes. | |
| * | |
| * @param n Number of chars in the prefix. | |
| * @return A view of the first @a n characters in @a this, bounded by the size of @a this. | |
| */ | |
| constexpr self_type prefix(size_t n) const noexcept; | |
| /** Get a view of a prefix bounded by @a c. | |
| * | |
| * @param c Delimiter character. | |
| * @return A view of the prefix bounded by @a c, or all of @a this if @a c is not found. | |
| * @note The character @a c is not included in the returned view. | |
| */ | |
| self_type prefix_at(char c) const; | |
| /** Get a view of a prefix bounded by a character in @a delimiters. | |
| * | |
| * @param delimiters A set of characters. | |
| * | |
| * @return A view of the prefix bounded by any character in @a delimiters, or empty if none are | |
| * found. | |
| * | |
| * @note The delimiter character is not included in the returned view. | |
| */ | |
| self_type prefix_at(std::string_view const &delimiters) const; | |
| /** Get a view of a prefix bounded by a character predicate @a pred. | |
| * | |
| * @a pred must be a functor which takes a @c char argument and returns @c bool. Each character in | |
| * @a this is tested by @a pred and the prefix is delimited by the first character for which @a | |
| * pred is @c true. | |
| * | |
| * @param pred A character predicate. | |
| * | |
| * @return A view of the prefix bounded by @a pred or empty if @a pred is not @c true for any | |
| * characer. | |
| * | |
| * @note The deliminting character is not included in the returned view. | |
| */ | |
| template <typename F> self_type prefix_if(F const &pred) const; | |
| /** Remove bytes from the start of the view. | |
| * | |
| * @param n Number of bytes to remove. | |
| * @return @a this. | |
| */ | |
| self_type &remove_prefix(size_t n); | |
| /** Remove bytes from the end of the view. | |
| * | |
| * @param n Number of bytes to remove. | |
| * @return @a this. | |
| */ | |
| self_type &remove_suffix(size_t n); | |
| /** Remove the leading characters of @a this up to and including @a c. | |
| * | |
| * @param c Delimiter character. | |
| * @return @a this. | |
| * @note The first occurrence of character @a c is removed along with all preceding characters, or | |
| * the view is cleared if @a c is not found. | |
| */ | |
| self_type &remove_prefix_at(char c); | |
| /** Remove the leading characters of @a this up to and including the first character matching @a delimiters. | |
| * | |
| * @param delimiters Characters to match. | |
| * @return @a this. | |
| * @note The first occurrence of any character in @a delimiters is removed along with all preceding | |
| * characters, or the view is cleared if none are found. | |
| */ | |
| self_type &remove_prefix_at(std::string_view const &delimiters); | |
| /** Remove the leading characters up to and including the character selected by @a pred. | |
| * | |
| * @tparam F Predicate function type. | |
| * @param pred The predicate instance. | |
| * @return @a this. | |
| * | |
| * Characters are removed until @a pred returns @c true. The matching character is also removed. | |
| */ | |
| template <typename F> self_type &remove_prefix_if(F const &pred); | |
| /** Remove and return a prefix of size @a n. | |
| * | |
| * @param n Size of the prefix. | |
| * @return The first @a n bytes of @a this if @a n is in @a this, otherwise an empty view. | |
| * | |
| * The prefix is removed and returned if the requested prefix is no larger than @a this, | |
| * otherwise @a this is not modified. | |
| * | |
| * @note The character at offset @a n is discarded if @a this is modified. | |
| * | |
| * @see @c take_prefix | |
| */ | |
| self_type split_prefix(size_t n); | |
| /** Remove and return a prefix bounded by the first occurrence of @a c. | |
| * | |
| * @param c The character to match. | |
| * @return The prefix bounded by @a c if @a c is found, an empty view if not. | |
| * | |
| * The prefix is removed and returned if @a c is found, otherwise @a this is not modified. | |
| * | |
| * @note The delimiter character is discarded if @a this is modified. | |
| * | |
| * @see @c take_prefix | |
| */ | |
| self_type split_prefix_at(char c); | |
| /** Remove and return a prefix bounded by the first occurrence of any of @a delimiters. | |
| * | |
| * @param delimiters The characters to match. | |
| * @return The prefix bounded by a delimiter if one is found, otherwise an empty view. | |
| * | |
| * The prefix is removed and returned if a @a delimiter is found, otherwise @a this is not modified. | |
| * | |
| * @note The matching character is discarded if @a this is modified. | |
| * | |
| * @see @c take_prefix_at | |
| */ | |
| self_type split_prefix_at(std::string_view const &delimiters); | |
| /** Remove and return a prefix bounded by the first character that satisfies @a pred. | |
| * | |
| * @tparam F Predicate functor type. | |
| * @param pred A function taking @c char and returning @c bool. | |
| * @return The prefix bounded by the first character satisfying @a pred. | |
| * | |
| * The prefix is removed and returned if a character satisfying @a pred is found, otherwise | |
| * @a this is not modified. | |
| * | |
| * @note The matching character is discarded if @a this is modified. | |
| * | |
| * @see @c take_prefix_if | |
| */ | |
| template <typename F> self_type split_prefix_if(F const &pred); | |
| /** Remove and return the first @a n characters. | |
| * | |
| * @param n Size of the return prefix. | |
| * @return The first @a n bytes of @a this if @a n is in @a this, otherwise all of @a this. | |
| * | |
| * The prefix is removed and returned if the requested prefix is no larger than @a this, | |
| * otherwise all of @a this is removed and returned. | |
| * | |
| * @note The character at offset @a n is discarded if @a n is within the bounds of @a this. | |
| * | |
| * @see @c split_prefix | |
| */ | |
| self_type take_prefix(size_t n); | |
| /** Remove and return a prefix bounded by the first occurrence of @a c. | |
| * | |
| * @param c The character to match. | |
| * @return The prefix bounded by @a c if @a c is found, all of @a this if not. | |
| * | |
| * The prefix is removed and returned if @a c is found, otherwise all of @a this is removed and | |
| * returned. | |
| * | |
| * @note The character at offset @a n is discarded if found. | |
| * | |
| * @see @c split_prefix_at | |
| */ | |
| self_type take_prefix_at(char c); | |
| /** Remove and return a prefix bounded by the first occurrence of any of @a delimiters. | |
| * | |
| * @param delimiters The characters to match. | |
| * @return The prefix bounded by a delimiter if one is found, otherwise all of @a this. | |
| * | |
| * The prefix is removed and returned if a @a delimiter is found, otherwise all of @a this is | |
| * removed and returned. | |
| * | |
| * @note The matching character is discarded if found. | |
| * | |
| * @see @c split_prefix_at | |
| */ | |
| self_type take_prefix_at(std::string_view const &delimiters); | |
| /** Remove and return a prefix bounded by the first character that satisfies @a pred. | |
| * | |
| * @tparam F Predicate functor type. | |
| * @param pred A function taking @c char and returning @c bool. | |
| * @return The prefix bounded by the first character satisfying @a pred, or all of @a this if none | |
| * is found. | |
| * | |
| * The prefix is removed and returned if a character satisfying @a pred is found, otherwise | |
| * all of @a this is removed and returned. | |
| * | |
| * @note The matching character is discarded if found. | |
| * | |
| * @see @c split_prefix_if | |
| */ | |
| template <typename F> self_type take_prefix_if(F const &pred); | |
| /** Remove and return a prefix of characters satisfying @a pred | |
| * | |
| * @tparam F Predicate functor type. | |
| * @param pred A function taking @c char and returning @c bool. | |
| * @return The prefix of characters that satisfy @a pred. | |
| * | |
| * The returned prefix is removed from @a this. That prefix may be empty if the first character | |
| * does not satisfy @a pred. | |
| * | |
| * @note This is very similar to @c ltrim_if but returns the removed text instead of the modified | |
| * view. | |
| */ | |
| template <typename F> self_type clip_prefix_of(F const &pred); | |
| /** Get a view of the last @a n bytes. | |
| * | |
| * @param n Number of chars in the suffix. | |
| * @return A view of the last @a n characters in @a this, bounded by the size of @a this. | |
| */ | |
| constexpr self_type suffix(size_t n) const noexcept; | |
| /** Get a view of a suffix bounded by @a c. | |
| * | |
| * @param c Delimiter character. | |
| * @return A view of the suffix bounded by @a c, or all of @a this if @a c is not found. | |
| * @note The character @a c is not included in the returned view. | |
| */ | |
| self_type suffix_at(char c) const; | |
| /** Get a view of a suffix bounded by a character in @a delimiters. | |
| * | |
| * @param delimiters A set of characters. | |
| * | |
| * @return A view of the suffix bounded by any character in @a delimiters, or mepty if none are | |
| * found. | |
| * | |
| * @note The delimiter character is not included in the returned view. | |
| */ | |
| self_type suffix_at(std::string_view const &delimiters) const; | |
| /** Get a view of a suffix bounded by a character predicate @a pred. | |
| * | |
| * @a pred must be a functor which takes a @c char argument and returns @c bool. Each character in | |
| * @a this is tested by @a pred and the suffix is delimited by the last character for which @a | |
| * pred is @c true. | |
| * | |
| * @param pred A character predicate. | |
| * | |
| * @return A view of the suffix bounded by @a pred or empty if @a pred is not @c true for any | |
| * character. | |
| * | |
| * @note The delimiting character is not included in the returned view. | |
| */ | |
| template <typename F> self_type suffix_if(F const &pred) const; | |
| /** Remove the trailing characters of @a this up to and including @a c. | |
| * | |
| * @param c Delimiter character. | |
| * @return @a this. | |
| * | |
| * @note The last occurrence of character @a c is removed along with all succeeding characters, or | |
| * the view is cleared if @a c is not found. | |
| */ | |
| self_type &remove_suffix_at(char c); | |
| /** Remove the trailing characters of @a this up to and including the last character matching @a delimiters. | |
| * | |
| * @param delimiters Characters to match. | |
| * @return @a this. | |
| * @note The first occurrence of any character in @a delimiters is removed along with all preceding | |
| * characters, or the view is cleared if none are found. | |
| */ | |
| self_type &remove_suffix_at(std::string_view const &delimiters); | |
| /** Remove the trailing characters up to and including the character selected by @a pred. | |
| * | |
| * @tparam F Predicate function type. | |
| * @param pred The predicate instance. | |
| * @return @a this. | |
| * | |
| * If predicate is never true the view is cleared. | |
| */ | |
| template <typename F> self_type &remove_suffix_if(F const &pred); | |
| /** Remove and return a suffix of size @a n. | |
| * | |
| * @param n Size of the suffix. | |
| * @return The first @a n bytes of @a this if @a n is in @a this, otherwise an empty view. | |
| * | |
| * The prefix is removed and returned if the requested suffix is no larger than @a this, | |
| * otherwise @a this is not modified. | |
| * | |
| * @note The character at offset @a n is discarded if @a this is modified. | |
| * | |
| * @see @c take_suffix | |
| */ | |
| self_type split_suffix(size_t n); | |
| /** Remove and return a suffix bounded by the last occurrence of @a c. | |
| * | |
| * @param c The character to match. | |
| * @return The suffix bounded by @a c if @a c is found, an empty view if not. | |
| * | |
| * The suffix is removed and returned if @a c is found, otherwise @a this is not modified. | |
| * | |
| * @note The character at offset @a n is discarded if @a this is modified. | |
| * | |
| * @see @c take_suffix_at | |
| */ | |
| self_type split_suffix_at(char c); | |
| /** Remove and return a suffix bounded by the last occurrence of any of @a delimiters. | |
| * | |
| * @param delimiters The characters to match. | |
| * @return The suffix bounded by a delimiter if found, an empty view if none found. | |
| * | |
| * The suffix is removed and returned if delimiter is found, otherwise @a this is not modified. | |
| * | |
| * @note The delimiter character is discarded if @a this is modified. | |
| * | |
| * @see @c take_suffix_at | |
| */ | |
| self_type split_suffix_at(std::string_view const &delimiters); | |
| /** Remove and return a suffix bounded by the last character that satisfies @a pred. | |
| * | |
| * @tparam F Predicate functor type. | |
| * @param pred A function taking @c char and returning @c bool. | |
| * @return The suffix bounded by the first character satisfying @a pred if found, otherwise @a this | |
| * is not modified. | |
| * | |
| * The prefix is removed and returned if a character satisfying @a pred if found, otherwise | |
| * @a this is not modified. | |
| * | |
| * @note The matching character is discarded if @a this is modified. | |
| * | |
| * @see @c take_suffix_if | |
| */ | |
| template <typename F> self_type split_suffix_if(F const &pred); | |
| /** Remove and return a suffix of size @a n. | |
| * | |
| * @param n Size of the suffix. | |
| * @return The first @a n bytes of @a this if @a n is in @a this, otherwise all of @a this. | |
| * | |
| * The returned suffix is removed from @a this, along with the character at offset @a n if present. | |
| * | |
| * @see @c split_suffix | |
| */ | |
| self_type take_suffix(size_t n); | |
| /** Remove and return a suffix bounded by the last occurrence of @a c. | |
| * | |
| * @param c The character to match. | |
| * @return The suffix bounded by @a c if @a c is found, all of @a this if not. | |
| * | |
| * The returned suffix is removed from @a this, along with the delimiter character if found. | |
| * | |
| * @see @c split_suffix_at | |
| */ | |
| self_type take_suffix_at(char c); | |
| /** Remove and return a suffix bounded by the last occurrence of any of @a delimiters. | |
| * | |
| * @param delimiters The characters to match. | |
| * @return The suffix bounded by a delimiter if @a c is found, all of @a this if not. | |
| * | |
| * The returned suffix is removed from @a this, along with the delimiter character if found. | |
| * | |
| * @see @c split_suffix_at | |
| */ | |
| self_type take_suffix_at(std::string_view const &delimiters); | |
| /** Remove and return a suffix bounded by the last character that satisfies @a pred. | |
| * | |
| * @tparam F Predicate functor type. | |
| * @param pred A function taking @c char and returning @c bool. | |
| * @return The suffix bounded by the first character satisfying @a pred if found, otherwise all of @a this. | |
| * | |
| * @note The matching character is discarded if found. | |
| * | |
| * @see @c split_suffix_if | |
| */ | |
| template <typename F> self_type take_suffix_if(F const &pred); | |
| /** Remove and return a suffix of characters satisfying @a pred | |
| * | |
| * @tparam F Predicate functor type. | |
| * @param pred A function taking @c char and returning @c bool. | |
| * @return The suffix of characters that satisfy @a pred. | |
| * | |
| * The returned suffix is removed from @a this. That suffix may be empty if the last character | |
| * does not satisfy @a pred. | |
| * | |
| * @note This is very similar to @c rtrim_if but returns the removed text instead of the modified | |
| * view. | |
| */ | |
| template <typename F> self_type clip_suffix_of(F const &pred); | |
| /** Get a view of part of this view. | |
| * | |
| * @param pos Offset of first byte in the new view. | |
| * @param count Number of bytes in the view. | |
| * @return The view starting at @a pos for @a count bytes. | |
| * | |
| * The returned view is clipped by @a this - that is, it will not extend beyond the original view. | |
| * @a count is reduced such that it covers only data in @a this. | |
| * | |
| * @note This is provided primarily for co-variance, i.e. the returned view is a @c TextView | |
| * instead of a @c std::string_view. | |
| */ | |
| constexpr self_type substr(size_type pos = 0, size_type count = npos) const noexcept; | |
| /** Check if the view begins with a specific @a prefix. | |
| * | |
| * @param prefix String to check against @a this. | |
| * @return @c true if <tt>this->prefix(prefix.size()) == prefix</tt>, @c false otherwise. | |
| * @internal C++20 preview. | |
| */ | |
| bool starts_with(std::string_view const &prefix) const noexcept; | |
| /** Check if the view begins with a specific @a prefix. | |
| * | |
| * @param prefix String to check against @a this. | |
| * @return @c true if <tt>this->prefix(prefix.size()) == prefix</tt>, @c false otherwise. | |
| * @internal C++20 preview. | |
| */ | |
| bool starts_with(char const *prefix) const; | |
| /** Check if the view begins with the character @c c. | |
| * | |
| * @param c Character to check. | |
| * @return @c true if the string is non-empty and the first character is @c c. | |
| * @internal C++20 preview. | |
| */ | |
| bool starts_with(char c) const noexcept; | |
| /** Check if the view begins with a specific @a prefix, ignoring case. | |
| * | |
| * @param prefix String to check against @a this. | |
| * @return @c true if <tt>this->prefix(prefix.size()) == prefix</tt> without regard to case, @c false otherwise. | |
| * @internal C++20 preview. | |
| */ | |
| bool starts_with_nocase(std::string_view const &prefix) const noexcept; | |
| /** Check if the view begins with a specific @a prefix. | |
| * | |
| * @param prefix String to check against @a this. | |
| * @return @c true if <tt>this->prefix(prefix.size()) == prefix</tt>, @c false otherwise. | |
| * @internal C++20 preview. | |
| */ | |
| bool starts_with_nocase(char const *prefix) const; | |
| /** Check if the view begins with the character @c c, ignoring case. | |
| * | |
| * @param c Character to check. | |
| * @return @c true if the string is non-empty and the first character is @c c. | |
| * @internal C++20 preview. | |
| */ | |
| bool starts_with_nocase(char c) const noexcept; | |
| /** Check if the view ends with a specific @a suffix. | |
| * | |
| * @param suffix String to check against @a this. | |
| * @return @c true if <tt>this->suffix(suffix.size()) == suffix</tt>, @c false otherwise. | |
| * @internal C++20 preview. | |
| */ | |
| bool ends_with(std::string_view const &suffix) const noexcept; | |
| /** Check if the view ends with a specific @a suffix. | |
| * | |
| * @param suffix String to check against @a this. | |
| * @return @c true if <tt>this->suffix(suffix.size()) == suffix</tt>, @c false otherwise. | |
| * @internal C++20 preview. | |
| */ | |
| bool ends_with(char const *suffix) const; | |
| /** Check the view ends with the character @c c. | |
| * | |
| * @param c Character to check. | |
| * @return @c true if the string is non-empty and the last character is @c c. | |
| * @internal C++20 preview. | |
| */ | |
| bool ends_with(char c) const noexcept; | |
| /** Check if the view starts with a specific @a suffix, ignoring case. | |
| * | |
| * @param suffix String to check against @a this. | |
| * @return @c true if <tt>this->suffix(suffix.size()) == suffix</tt> without regard to case, @c false otherwise. | |
| * @internal C++20 preview. | |
| */ | |
| bool ends_with_nocase(std::string_view const &suffix) const noexcept; | |
| /** Check if the view starts with a specific @a suffix, ignoring case. | |
| * | |
| * @param suffix String to check against @a this. | |
| * @return @c true if <tt>this->suffix(suffix.size()) == suffix</tt> without regard to case, @c false otherwise. | |
| * @internal C++20 preview. | |
| */ | |
| bool ends_with_nocase(char const *suffix) const; | |
| /** Check the view ends with the character @c c, ignoring case. | |
| * | |
| * @param c Character to check. | |
| * @return @c true if the string is non-empty and the last character is @c c. | |
| * @internal C++20 preview. | |
| */ | |
| bool ends_with_nocase(char c) const noexcept; | |
| // Functors for using this class in STL containers. | |
| /// Ordering functor, lexicographic comparison. | |
| struct LessThan { | |
| /// @return Case sensitive ordering. | |
| bool | |
| operator()(self_type const &lhs, self_type const &rhs) const noexcept { | |
| return -1 == strcmp(lhs, rhs); | |
| } | |
| }; | |
| /// Ordering functor, case ignoring lexicographic comparison. | |
| struct LessThanNoCase { | |
| /// @return Case insensitive ordering. | |
| bool | |
| operator()(self_type const &lhs, self_type const &rhs) const noexcept { | |
| return -1 == strcasecmp(lhs, rhs); | |
| } | |
| }; | |
| /// Support for containers that need case insensitive comparisons between views. | |
| struct CaselessEqual { | |
| /// @return @c true if the view contants are equal when compared without regard to case. | |
| bool | |
| operator()(self_type const &lhs, self_type const &rhs) const noexcept { | |
| return lhs.size() == rhs.size() && 0 == strcasecmp(lhs, rhs); | |
| } | |
| }; | |
| /** A pointer to the first byte. | |
| * | |
| * @return Address of the first byte of the view. | |
| * | |
| * @internal This fixes an error in @c std::string_view where this method is declared to return | |
| * a template parameter instead of the correct @c value_type. The effect is @c string_view::data | |
| * is not considered by the compiler to return <tt>char const *</tt> which makes meta-programming | |
| * painful. | |
| */ | |
| constexpr value_type const *data() const noexcept; | |
| /** A pointer to past the last byte. | |
| * | |
| * @return Address of the first byte past the end of the view. | |
| * | |
| * This is effectively @c std::string_view::end() except it explicit returns a pointer and not | |
| * (potentially) an iterator class, to match up with @c data(). | |
| */ | |
| constexpr value_type const *data_end() const noexcept; | |
| /// Specialized stream operator implementation. | |
| /// @note Use the standard stream operator unless there is a specific need for this, which is unlikely. | |
| /// @return The stream @a os. | |
| /// @internal Needed because @c std::ostream::write must be used and | |
| /// so alignment / fill have to be explicitly handled. | |
| template <typename Stream> Stream &stream_write(Stream &os, const TextView &b) const; | |
| /// @cond OVERLOAD | |
| // These methods are all overloads of other methods, defined in order to make the API more | |
| // convenient to use. Mostly these overload @c int for @c size_t so naked numbers work as expected. | |
| constexpr self_type prefix(int n) const noexcept; | |
| self_type take_suffix(int n); | |
| self_type split_prefix(int n); | |
| constexpr self_type suffix(int n) const noexcept; | |
| self_type split_suffix(int n); | |
| /// @endcond | |
| protected: | |
| /// Initialize a bit mask to mark which characters are in this view. | |
| static void init_delimiter_set(std::string_view const &delimiters, std::bitset<256> &set); | |
| }; | |
| /// Internal table of digit values for characters. | |
| /// This is -1 for characters that are not valid digits. | |
| extern const int8_t svtoi_convert[256]; | |
| /** Convert the text in @c TextView @a src to a signed numeric value. | |
| If @a parsed is non-null then the part of the string actually parsed is placed there. | |
| @a base sets the conversion base. If not set base 10 is used with two special cases: | |
| - If the number starts with a literal '0' then it is treated as base 8. | |
| - If the number starts with the literal characters '0x' or '0X' then it is treated as base 16. | |
| If @a base is explicitly set then any leading radix indicator is not supported. | |
| */ | |
| intmax_t svtoi(TextView src, TextView *parsed = nullptr, int base = 0); | |
| /** Convert the text in @c TextView @a src to an unsigned numeric value. | |
| If @a parsed is non-null then the part of the string actually parsed is placed there. | |
| @a base sets the conversion base. If not set base 10 is used with two special cases: | |
| - If the number starts with a literal '0' then it is treated as base 8. | |
| - If the number starts with the literal characters '0x' or '0X' then it is treated as base 16. | |
| If @a base is explicitly set then any leading radix indicator is not supported. | |
| */ | |
| uintmax_t svtou(TextView src, TextView *parsed = nullptr, int base = 0); | |
| /** Convert the text in @c src to an unsigned numeric value. | |
| * | |
| * @tparam N The radix (must be 1..36) | |
| * @param src The source text. Updated during parsing. | |
| * @return The converted numeric value. | |
| * | |
| * This is a specialized function useful only where conversion performance is critical. It is used | |
| * inside @c svtoi and @a svtou for the common cases of 8, 10, and 16, therefore normally this isn't much more | |
| * performant in those cases than just @c svtoi. Because of this only positive values are parsed. | |
| * If determining the radix from the text or signed value parsing is needed, used @c svtoi. | |
| * | |
| * @a src is updated in place to indicate what characters were parsed by removing them from the view | |
| * Parsing stops on the first invalid digit, so any leading non-digit characters (e.g. whitespace) | |
| * must already be removed. For overflow, all valid digits are consumed and the maximum value returned. | |
| */ | |
| template <int RADIX> | |
| uintmax_t | |
| svto_radix(TextView &src) { | |
| static_assert(1 <= RADIX && RADIX <= 36, "Radix must be in the range 2..36"); | |
| static constexpr auto MAX = std::numeric_limits<uintmax_t>::max(); | |
| static constexpr auto OVERFLOW_LIMIT = MAX / RADIX; | |
| uintmax_t zret = 0; | |
| uintmax_t v; | |
| while (src.size() && ((v = swoc::svtoi_convert[uint8_t(*src)]) < RADIX)) { | |
| // Tweaked for performance - need to check range after @a RADIX multiply. | |
| ++src; // Update view iff the character is parsed. | |
| if (zret <= OVERFLOW_LIMIT && v <= (MAX - (zret *= RADIX)) ) { | |
| zret += v; | |
| } else { | |
| zret = MAX; // clamp to max - once set will always hit this case for subsequent input. | |
| } | |
| } | |
| return zret; | |
| } | |
| /// Convenience overload. | |
| /// @see svto_radix(swoc::TextView &src) | |
| template <int N> | |
| uintmax_t | |
| svto_radix(TextView &&src) { | |
| return svto_radix<N>(src); | |
| } | |
| /** Parse @a text as a floating point number. | |
| * | |
| * @param text The input text. | |
| * @param parsed Parsed text [out] | |
| * @return The floating point value, or 0.0 if invalid input. | |
| * | |
| * If @a parsed is not @a nullptr then the span of characters parsed is put there. This can be | |
| * used to check if the parse was scuccesful - on a failed parse, it will be empty. | |
| * | |
| * @note This should be within 1 epsilon of correct, although it doesn't guarantee picking | |
| * the closest epsilon. It's more than sufficient for use in configurations, but possibly | |
| * not for high precision work. | |
| */ | |
| double svtod(TextView text, TextView *parsed = nullptr); | |
| // ---------------------------------------------------------- | |
| // Inline implementations. | |
| // Note: Why, you may ask, do I use @c TextView::self_type for return type instead of the | |
| // simpler plain @c TextView ? Because otherwise Doxygen can't match up the declaration and | |
| // definition and the reference documentation is messed up. Sigh. | |
| inline constexpr CharSet::CharSet(TextView const &chars) { | |
| for (auto c : chars) { | |
| _chars[uint8_t(c)] = true; | |
| } | |
| } | |
| inline bool | |
| CharSet::operator()(unsigned char c) const { | |
| return _chars[c]; | |
| } | |
| inline bool | |
| CharSet::operator()(char c) const { | |
| return _chars[uint8_t(c)]; | |
| } | |
| // === TextView Implementation === | |
| /// @cond TextView_INTERNAL | |
| // Doxygen doesn't match these up well due to various type and template issues. | |
| // @internal If there is more than one overload for numeric types, it's easy to get ambiguity. The only | |
| // fix, unfortunately, is lots of overloads to cover the ambiguous cases. | |
| inline constexpr TextView::TextView(const char *ptr, size_t n) noexcept | |
| : super_type(ptr, n == npos ? (ptr ? ::strlen(ptr) : 0) : n) {} | |
| inline constexpr TextView::TextView(const char *ptr, unsigned n) noexcept : super_type(ptr, size_t(n)) {} | |
| inline constexpr TextView::TextView(const char *ptr, ssize_t n) noexcept | |
| : super_type(ptr, n < 0 ? (ptr ? ::strlen(ptr) : 0) : size_t(n)) {} | |
| inline constexpr TextView::TextView(const char *ptr, int n) noexcept | |
| : super_type(ptr, n < 0 ? (ptr ? ::strlen(ptr) : 0) : size_t(n)) {} | |
| inline constexpr TextView::TextView(std::nullptr_t) noexcept : super_type(nullptr, 0) {} | |
| inline TextView::TextView(std::string const &str) noexcept : super_type(str) {} | |
| inline constexpr TextView::TextView(super_type const &that) noexcept : super_type(that) {} | |
| template <size_t N> constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {} | |
| template <typename C, typename> constexpr TextView::TextView(C const &c) : super_type(c.data(), c.size()) {} | |
| inline void | |
| TextView::init_delimiter_set(std::string_view const &delimiters, std::bitset<256> &set) { | |
| set.reset(); | |
| for (char c : delimiters) | |
| set[static_cast<uint8_t>(c)] = true; | |
| } | |
| inline auto | |
| TextView::clear() -> self_type & { | |
| new (this) self_type(); | |
| return *this; | |
| } | |
| inline constexpr char | |
| TextView::operator*() const { | |
| return this->empty() ? char(0) : *(this->data()); | |
| } | |
| inline constexpr bool | |
| TextView::operator!() const noexcept { | |
| return this->empty(); | |
| } | |
| inline constexpr TextView::operator bool() const noexcept { | |
| return !this->empty(); | |
| } | |
| inline auto | |
| TextView::operator++() -> self_type & { | |
| this->remove_prefix(1); | |
| return *this; | |
| } | |
| inline auto | |
| TextView::operator++(int) -> self_type { | |
| self_type zret{*this}; | |
| this->remove_prefix(1); | |
| return zret; | |
| } | |
| inline auto | |
| TextView::operator+=(size_t n) -> self_type & { | |
| this->remove_prefix(n); | |
| return *this; | |
| } | |
| template <size_t N> | |
| inline auto | |
| TextView::operator=(const char (&s)[N]) -> self_type & { | |
| return *this = self_type{s, s[N - 1] ? N : N - 1}; | |
| } | |
| inline auto | |
| TextView::operator=(super_type const &that) -> self_type & { | |
| this->super_type::operator=(that); | |
| return *this; | |
| } | |
| inline auto | |
| TextView::operator=(char *&s) -> self_type & { | |
| this->super_type::operator=(s); | |
| return *this; | |
| } | |
| inline auto | |
| TextView::operator=(char const *&s) -> self_type & { | |
| this->super_type::operator=(s); | |
| return *this; | |
| } | |
| inline auto | |
| TextView::operator=(const std::string &s) -> self_type & { | |
| this->super_type::operator=(s); | |
| return *this; | |
| } | |
| inline auto | |
| TextView::assign(char *&c_str) -> self_type & { | |
| return this->assign(c_str, strlen(c_str)); | |
| } | |
| inline auto | |
| TextView::assign(char const *&c_str) -> self_type & { | |
| return this->assign(c_str, strlen(c_str)); | |
| } | |
| inline auto | |
| TextView::assign(const std::string &s) -> self_type & { | |
| *this = super_type(s); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::assign(char const *ptr, size_t n) { | |
| *this = super_type(ptr, n == npos ? (ptr ? ::strlen(ptr) : 0) : n); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::assign(char const *b, char const *e) { | |
| *this = super_type(b, e - b); | |
| return *this; | |
| } | |
| template <size_t N> | |
| inline auto | |
| TextView::assign(char const (&s)[N]) noexcept -> self_type & { | |
| return *this = self_type{s, s[N - 1] ? N : N - 1}; | |
| } | |
| inline constexpr auto | |
| TextView::prefix(size_t n) const noexcept -> self_type { | |
| return {this->data(), std::min(n, this->size())}; | |
| } | |
| inline constexpr TextView | |
| TextView::prefix(int n) const noexcept { | |
| return {this->data(), std::min<size_t>(n, this->size())}; | |
| } | |
| inline auto | |
| TextView::prefix_at(char c) const -> self_type { | |
| self_type zret; // default to empty return. | |
| if (auto n = this->find(c); n != npos) { | |
| zret.assign(this->data(), n); | |
| } | |
| return zret; | |
| } | |
| inline TextView | |
| TextView::prefix_at(std::string_view const &delimiters) const { | |
| self_type zret; // default to empty return. | |
| if (auto n = this->find_first_of(delimiters); n != npos) { | |
| zret.assign(this->data(), n); | |
| } | |
| return zret; | |
| } | |
| template <typename F> | |
| auto | |
| TextView::prefix_if(F const &pred) const -> self_type { | |
| self_type zret; // default to empty return. | |
| if (auto n = this->find_if(pred); n != npos) { | |
| zret.assign(this->data(), n); | |
| } | |
| return zret; | |
| } | |
| inline auto | |
| TextView::remove_prefix(size_t n) -> self_type & { | |
| this->super_type::remove_prefix(std::min(n, this->size())); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::remove_prefix_at(char c) { | |
| if (auto n = this->find(c); n != npos) { | |
| this->super_type::remove_prefix(n + 1); | |
| } | |
| return *this; | |
| } | |
| inline auto | |
| TextView::remove_prefix_at(std::string_view const &delimiters) -> self_type & { | |
| if (auto n = this->find_first_of(delimiters); n != npos) { | |
| this->super_type::remove_prefix(n + 1); | |
| } | |
| return *this; | |
| } | |
| template <typename F> | |
| auto | |
| TextView::remove_prefix_if(F const &pred) -> self_type & { | |
| if (auto n = this->find_if(pred); n != npos) { | |
| this->super_type::remove_prefix(n + 1); | |
| } | |
| return *this; | |
| } | |
| inline TextView | |
| TextView::split_prefix(size_t n) { | |
| self_type zret; // default to empty return. | |
| if (n < this->size()) { | |
| zret = this->prefix(n); | |
| this->remove_prefix(std::min(n + 1, this->size())); | |
| } | |
| return zret; | |
| } | |
| inline TextView | |
| TextView::split_prefix(int n) { | |
| return this->split_prefix(size_t(n)); | |
| } | |
| inline TextView | |
| TextView::split_prefix_at(char c) { | |
| return this->split_prefix(this->find(c)); | |
| } | |
| inline TextView | |
| TextView::split_prefix_at(std::string_view const &delimiters) { | |
| return this->split_prefix(this->find_first_of(delimiters)); | |
| } | |
| template <typename F> | |
| TextView::self_type | |
| TextView::split_prefix_if(F const &pred) { | |
| return this->split_prefix(this->find_if(pred)); | |
| } | |
| inline TextView | |
| TextView::take_prefix(size_t n) { | |
| n = std::min(n, this->size()); | |
| self_type zret = this->prefix(n); | |
| this->remove_prefix(std::min(n + 1, this->size())); | |
| return zret; | |
| } | |
| inline TextView | |
| TextView::take_prefix_at(char c) { | |
| return this->take_prefix(this->find(c)); | |
| } | |
| inline TextView | |
| TextView::take_prefix_at(std::string_view const &delimiters) { | |
| return this->take_prefix(this->find_first_of(delimiters)); | |
| } | |
| template <typename F> | |
| auto | |
| TextView::take_prefix_if(F const &pred) -> self_type { | |
| return this->take_prefix(this->find_if(pred)); | |
| } | |
| inline constexpr TextView | |
| TextView::suffix(size_t n) const noexcept { | |
| n = std::min(n, this->size()); | |
| return {this->data_end() - n, n}; | |
| } | |
| inline constexpr TextView | |
| TextView::suffix(int n) const noexcept { | |
| return this->suffix(size_t(n)); | |
| } | |
| inline TextView | |
| TextView::suffix_at(char c) const { | |
| self_type zret; | |
| if (auto n = this->rfind(c); n != npos && n + 1 < this->size()) { | |
| ++n; | |
| zret.assign(this->data() + n, this->size() - n); | |
| } | |
| return zret; | |
| } | |
| inline TextView | |
| TextView::suffix_at(std::string_view const &delimiters) const { | |
| self_type zret; | |
| if (auto n = this->find_last_of(delimiters); n != npos) { | |
| ++n; | |
| zret.assign(this->data() + n, this->size() - n); | |
| } | |
| return zret; | |
| } | |
| template <typename F> | |
| auto | |
| TextView::suffix_if(F const &pred) const -> self_type { | |
| self_type zret; | |
| if (auto n = this->rfind_if(pred); n != npos) { | |
| ++n; | |
| zret.assign(this->data() + n, this->size() - n); | |
| } | |
| return zret; | |
| } | |
| inline auto | |
| TextView::remove_suffix(size_t n) -> self_type & { | |
| this->super_type::remove_suffix(std::min(n, this->size())); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::remove_suffix_at(char c) { | |
| if (auto n = this->rfind(c); n != npos) { | |
| return this->remove_suffix(this->size() - n); | |
| } | |
| return this->clear(); | |
| } | |
| inline TextView & | |
| TextView::remove_suffix_at(std::string_view const &delimiters) { | |
| if (auto n = this->find_last_of(delimiters); n != npos) { | |
| return this->remove_suffix(this->size() - n); | |
| } | |
| return this->clear(); | |
| } | |
| template <typename F> | |
| TextView::self_type & | |
| TextView::remove_suffix_if(F const &pred) { | |
| if (auto n = this->rfind_if(pred); n != npos) { | |
| return this->remove_suffix(this->size() - n); | |
| } | |
| return this->clear(); | |
| } | |
| inline TextView | |
| TextView::split_suffix(size_t n) { | |
| self_type zret; | |
| n = std::min(n, this->size()); | |
| zret = this->suffix(n); | |
| this->remove_suffix(n + 1); | |
| return zret; | |
| } | |
| inline auto | |
| TextView::split_suffix(int n) -> self_type { | |
| return this->split_suffix(size_t(n)); | |
| } | |
| inline TextView | |
| TextView::split_suffix_at(char c) { | |
| auto idx = this->rfind(c); | |
| return npos == idx ? self_type{} : this->split_suffix(this->size() - (idx + 1)); | |
| } | |
| inline auto | |
| TextView::split_suffix_at(std::string_view const &delimiters) -> self_type { | |
| auto idx = this->find_last_of(delimiters); | |
| return npos == idx ? self_type{} : this->split_suffix(this->size() - (idx + 1)); | |
| } | |
| template <typename F> | |
| TextView::self_type | |
| TextView::split_suffix_if(F const &pred) { | |
| return this->split_suffix(this->rfind_if(pred)); | |
| } | |
| inline TextView | |
| TextView::take_suffix(size_t n) { | |
| self_type zret{*this}; | |
| *this = zret.split_prefix(n); | |
| return zret; | |
| } | |
| inline TextView | |
| TextView::take_suffix(int n) { | |
| return this->take_suffix(size_t(n)); | |
| } | |
| inline TextView | |
| TextView::take_suffix_at(char c) { | |
| return this->take_suffix(this->rfind(c)); | |
| } | |
| inline TextView | |
| TextView::take_suffix_at(std::string_view const &delimiters) { | |
| return this->take_suffix(this->find_last_of(delimiters)); | |
| } | |
| template <typename F> | |
| TextView::self_type | |
| TextView::take_suffix_if(F const &pred) { | |
| return this->take_suffix_at(this->rfind_if(pred)); | |
| } | |
| template <typename F> | |
| inline size_t | |
| TextView::find_if(F const &pred) const { | |
| for (const char *spot = this->data(), *limit = this->data_end(); spot < limit; ++spot) | |
| if (pred(*spot)) | |
| return spot - this->data(); | |
| return npos; | |
| } | |
| template <typename F> | |
| inline size_t | |
| TextView::rfind_if(F const &pred) const { | |
| for (const char *spot = this->data_end(), *limit = this->data(); spot > limit;) | |
| if (pred(*--spot)) | |
| return spot - this->data(); | |
| return npos; | |
| } | |
| inline TextView & | |
| TextView::ltrim(char c) { | |
| this->remove_prefix(this->find_first_not_of(c)); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::rtrim(char c) { | |
| auto n = this->find_last_not_of(c); | |
| this->remove_suffix(this->size() - (n == npos ? 0 : n + 1)); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::trim(char c) { | |
| return this->ltrim(c).rtrim(c); | |
| } | |
| inline TextView & | |
| TextView::ltrim(CharSet const &delimiters) { | |
| const char *spot = this->data(); | |
| const char *limit = this->data_end(); | |
| while (spot < limit && delimiters(*spot)) { | |
| ++spot; | |
| } | |
| this->remove_prefix(spot - this->data()); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::ltrim(std::string_view const &delimiters) { | |
| return this->ltrim(CharSet(delimiters)); | |
| } | |
| inline TextView & | |
| TextView::ltrim(const char *delimiters) { | |
| return this->ltrim(CharSet(delimiters)); | |
| } | |
| inline TextView & | |
| TextView::rtrim(CharSet const &delimiters) { | |
| const char *spot = this->data_end(); | |
| const char *limit = this->data(); | |
| while (limit < spot-- && delimiters(*spot)) { | |
| } | |
| this->remove_suffix(this->data_end() - (spot + 1)); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::rtrim(std::string_view const &delimiters) { | |
| return this->rtrim(CharSet(delimiters)); | |
| } | |
| inline TextView & | |
| TextView::trim(CharSet const &delimiters) { | |
| const char *spot; | |
| const char *limit; | |
| // Do this explicitly, so we don't have to initialize the character set twice. | |
| for (spot = this->data(), limit = this->data_end(); spot < limit && delimiters(*spot); ++spot) | |
| ; | |
| this->remove_prefix(spot - this->data()); | |
| spot = this->data_end(); | |
| limit = this->data(); | |
| while (limit < spot-- && delimiters(*spot)) { | |
| } | |
| this->remove_suffix(this->data_end() - (spot + 1)); | |
| return *this; | |
| } | |
| inline TextView & | |
| TextView::trim(std::string_view const &delimiters) { | |
| return this->trim(CharSet(delimiters)); | |
| } | |
| inline TextView & | |
| TextView::trim(const char *delimiters) { | |
| return this->trim(CharSet(delimiters)); | |
| } | |
| template <typename F> | |
| TextView::self_type & | |
| TextView::ltrim_if(F const &pred) { | |
| const char *spot; | |
| const char *limit; | |
| for (spot = this->data(), limit = this->data_end(); spot < limit && pred(*spot); ++spot) | |
| ; | |
| this->remove_prefix(spot - this->data()); | |
| return *this; | |
| } | |
| template <typename F> | |
| TextView::self_type & | |
| TextView::rtrim_if(F const &pred) { | |
| const char *spot = this->data_end(); | |
| const char *limit = this->data(); | |
| while (limit < spot-- && pred(*spot)) | |
| ; | |
| this->remove_suffix(this->data_end() - (spot + 1)); | |
| return *this; | |
| } | |
| template <typename F> | |
| TextView::self_type & | |
| TextView::trim_if(F const &pred) { | |
| return this->ltrim_if(pred).rtrim_if(pred); | |
| } | |
| constexpr inline auto | |
| TextView::data() const noexcept -> value_type const * { | |
| return super_type::data(); | |
| } | |
| constexpr inline auto | |
| TextView::data_end() const noexcept -> value_type const * { | |
| return this->data() + this->size(); | |
| } | |
| inline constexpr TextView | |
| TextView::substr(size_type pos, size_type count) const noexcept { | |
| if (pos >= this->size()) { | |
| return {}; | |
| } | |
| count = std::min(this->size() - pos, count); | |
| return {this->data() + pos, count}; | |
| } | |
| inline bool | |
| TextView::starts_with(std::string_view const &prefix) const noexcept { | |
| return this->size() >= prefix.size() && 0 == ::memcmp(this->data(), prefix.data(), prefix.size()); | |
| } | |
| inline bool | |
| TextView::starts_with(char const *prefix) const { | |
| return this->starts_with(super_type(prefix)); | |
| } | |
| inline bool | |
| TextView::starts_with_nocase(char const *prefix) const { | |
| return this->starts_with_nocase(super_type{prefix}); | |
| } | |
| inline bool | |
| TextView::starts_with(char c) const noexcept { | |
| return !this->empty() && c == this->front(); | |
| } | |
| inline bool | |
| TextView::starts_with_nocase(char c) const noexcept { | |
| return !this->empty() && tolower(c) == tolower(this->front()); | |
| } | |
| inline bool | |
| TextView::starts_with_nocase(std::string_view const &prefix) const noexcept { | |
| return this->size() >= prefix.size() && 0 == ::strncasecmp(this->data(), prefix.data(), prefix.size()); | |
| } | |
| inline bool | |
| TextView::ends_with(std::string_view const &suffix) const noexcept { | |
| return this->size() >= suffix.size() && 0 == ::memcmp(this->data_end() - suffix.size(), suffix.data(), suffix.size()); | |
| } | |
| inline bool | |
| TextView::ends_with_nocase(std::string_view const &suffix) const noexcept { | |
| return this->size() >= suffix.size() && 0 == ::strncasecmp(this->data_end() - suffix.size(), suffix.data(), suffix.size()); | |
| } | |
| inline bool | |
| TextView::ends_with(char const *suffix) const { | |
| return this->ends_with(super_type(suffix)); | |
| } | |
| inline bool | |
| TextView::ends_with_nocase(char const *suffix) const { | |
| return this->ends_with_nocase(super_type(suffix)); | |
| } | |
| inline bool | |
| TextView::ends_with(char c) const noexcept { | |
| return !this->empty() && c == this->back(); | |
| } | |
| inline bool | |
| TextView::ends_with_nocase(char c) const noexcept { | |
| return !this->empty() && tolower(c) == tolower(this->back()); | |
| } | |
| template <typename Stream> | |
| Stream & | |
| TextView::stream_write(Stream &os, const TextView &b) const { | |
| // Local function, avoids extra template work. | |
| static const auto stream_fill = [](Stream &ostream, size_t n) -> Stream & { | |
| static constexpr size_t pad_size = 8; | |
| typename Stream::char_type padding[pad_size]; | |
| std::fill_n(padding, pad_size, ostream.fill()); | |
| for (; n >= pad_size && ostream.good(); n -= pad_size) | |
| ostream.write(padding, pad_size); | |
| if (n > 0 && ostream.good()) | |
| ostream.write(padding, n); | |
| return ostream; | |
| }; | |
| const std::size_t w = os.width(); | |
| if (w <= b.size()) { | |
| os.write(b.data(), b.size()); | |
| } else { | |
| const std::size_t pad_size = w - b.size(); | |
| const bool align_left = (os.flags() & Stream::adjustfield) == Stream::left; | |
| if (!align_left && os.good()) | |
| stream_fill(os, pad_size); | |
| if (os.good()) | |
| os.write(b.data(), b.size()); | |
| if (align_left && os.good()) | |
| stream_fill(os, pad_size); | |
| } | |
| return os; | |
| } | |
| template <typename F> | |
| TextView::self_type | |
| TextView::clip_prefix_of(F const &pred) { | |
| size_t idx = 0; | |
| for (auto spot = this->data(), limit = spot + this->size(); spot < limit && pred(*spot); ++spot, ++idx) | |
| ; // empty | |
| TextView token = this->prefix(idx); | |
| this->remove_prefix(idx); | |
| return token; | |
| } | |
| template <typename F> | |
| TextView::self_type | |
| TextView::clip_suffix_of(F const &pred) { | |
| size_t idx = this->size() - 1; | |
| for (auto spot = this->data() + idx, limit = this->data(); spot >= limit && pred(*spot); --spot, --idx) | |
| ; // empty | |
| TextView token = this->suffix(idx); | |
| this->remove_suffix(idx); | |
| return token; | |
| } | |
| /// @endcond TextView_INTERNAL | |
| // Provide an instantiation for @c std::ostream as it's likely this is the only one ever used. | |
| extern template std::ostream &TextView::stream_write(std::ostream &, const TextView &) const; | |
| /** A transform view. | |
| * | |
| * @tparam X Transform functor type. | |
| * @tparam V Source view type. | |
| * | |
| * A transform view acts like a view on the original source view @a V with each element transformed by | |
| * @a X. | |
| * | |
| * This is used most commonly with @c std::string_view. For example, if the goal is to handle a | |
| * piece of text as if it were lower case without changing the actual text, the following would | |
| * make that possible. | |
| * @code | |
| * std:::string_view source; // original text. | |
| * TransformView<int (*)(int) noexcept, std::string_view> xv(&tolower, source); | |
| * @endcode | |
| * | |
| * To avoid having to figure out the exact signature of the transform, the convenience function | |
| * @c transform_view_of is provide. | |
| * @code | |
| * std::string_view source; // original text. | |
| * auto xv = transform_view_of(&tolower, source); | |
| * @endcode | |
| * | |
| * This class supports iterators but those should be avoided as use of them makes extra copies of the instance which | |
| * may be expensive if the functor is expensive. In cases where the functor is a function pointer or a lambda this isn't | |
| * an issue. | |
| */ | |
| template <typename X, typename V> class TransformView { | |
| using self_type = TransformView; ///< Self reference type. | |
| using source_iterator = decltype(V{}.begin()); | |
| public: | |
| using transform_type = X; ///< Export transform functor type. | |
| using source_view_type = V; ///< Export source view type. | |
| using source_value_type = std::remove_reference_t<decltype(*source_iterator{})>; | |
| /// Result type of calling the transform on an element of the source view. | |
| using value_type = std::invoke_result_t<transform_type, source_value_type>; | |
| /// This class serves as its own iterator. | |
| using iterator = self_type; | |
| /** Construct a transform view using transform @a xf on source view @a v. | |
| * | |
| * @param xf Transform instance. | |
| * @param v Source view. | |
| */ | |
| TransformView(transform_type &&xf, source_view_type const &v); | |
| /** Construct a transform view using transform @a xf on source view @a v. | |
| * | |
| * @param xf Transform instance. | |
| * @param v Source view. | |
| */ | |
| TransformView(transform_type const &xf, source_view_type const &v); | |
| /// Copy constructor. | |
| TransformView(self_type const &that) = default; | |
| /// Move constructor. | |
| TransformView(self_type &&that) = default; | |
| /// Copy assignment. | |
| self_type &operator=(self_type const &that) = default; | |
| /// Move assignment. | |
| self_type &operator=(self_type &&that) = default; | |
| /// Equality. | |
| bool operator==(self_type const &that) const; | |
| /// Inequality. | |
| bool operator!=(self_type const &that) const; | |
| /// Get the current element. | |
| value_type operator*() const; | |
| /// Move to next element. | |
| self_type &operator++(); | |
| /// Move to next element. | |
| self_type operator++(int); | |
| /// Check if view is empty. | |
| bool empty() const; | |
| /// Check if bool is not empty. | |
| explicit operator bool() const; | |
| /// Iterator to first transformed character. | |
| iterator | |
| begin() const { | |
| return *this; | |
| } | |
| /// Iterator past last transformed character. | |
| iterator | |
| end() const { | |
| return self_type{_xf, _limit}; | |
| } | |
| protected: | |
| transform_type _xf; ///< Per character transform functor. | |
| source_iterator _spot; ///< Current location in the source view. | |
| source_iterator _limit; ///< End of source view. | |
| /// Special constructor for making an empty instance to serve as the @c end iterator. | |
| TransformView(transform_type &&xf, source_iterator &&limit) : _xf(xf), _spot(limit), _limit(limit) {} | |
| }; | |
| template <typename X, typename V> | |
| TransformView<X, V>::TransformView(transform_type &&xf, source_view_type const &v) : _xf(xf), _spot(v.begin()), _limit(v.end()) {} | |
| template <typename X, typename V> | |
| TransformView<X, V>::TransformView(transform_type const &xf, source_view_type const &v) | |
| : _xf(xf), _spot(v.begin()), _limit(v.end()) {} | |
| template <typename X, typename V> | |
| auto | |
| TransformView<X, V>::operator*() const -> value_type { | |
| return _xf(*_spot); | |
| } | |
| template <typename X, typename V> | |
| auto | |
| TransformView<X, V>::operator++() -> self_type & { | |
| ++_spot; | |
| return *this; | |
| } | |
| template <typename X, typename V> | |
| auto | |
| TransformView<X, V>::operator++(int) -> self_type { | |
| self_type zret{*this}; | |
| ++_spot; | |
| return zret; | |
| } | |
| template <typename X, typename V> | |
| bool | |
| TransformView<X, V>::empty() const { | |
| return _spot == _limit; | |
| } | |
| template <typename X, typename V> TransformView<X, V>::operator bool() const { | |
| return _spot != _limit; | |
| } | |
| template <typename X, typename V> | |
| bool | |
| TransformView<X, V>::operator==(self_type const &that) const { | |
| return _spot == that._spot && _limit == that._limit; | |
| } | |
| template <typename X, typename V> | |
| bool | |
| TransformView<X, V>::operator!=(self_type const &that) const { | |
| return _spot != that._spot || _limit != that._limit; | |
| } | |
| /** Create a transformed view of a source. | |
| * | |
| * @tparam X The transform functor type. | |
| * @tparam V The source type. | |
| * @param xf The transform. | |
| * @param src The view source. | |
| * @return A @c TransformView that applies @a xf to @a src. | |
| */ | |
| template <typename X, typename V> | |
| TransformView<X, V> | |
| transform_view_of(X const &xf, V const &src) { | |
| return TransformView<X, V>(xf, src); | |
| } | |
| /** Identity transform view. | |
| * | |
| * @tparam V The source type. | |
| * | |
| * This is a transform that returns the input unmodified. This is convenient when a transform is | |
| * required in general but not in all cases. | |
| */ | |
| template <typename V> class TransformView<void, V> { | |
| using self_type = TransformView; ///< Self reference type. | |
| /// Iterator over source, for internal use. | |
| using source_iterator = decltype(V{}.begin()); | |
| public: | |
| using source_view_type = V; ///< Export source view type. | |
| using source_value_type = std::remove_reference_t<decltype(*source_iterator{})>; | |
| /// Result type of calling the transform on an element of the source view. | |
| using value_type = source_value_type; | |
| /// This class serves as its own iterator. | |
| using iterator = self_type; | |
| /** Construct identity transform view from @a v. | |
| * | |
| * @param v Source view. | |
| */ | |
| TransformView(source_view_type const &v) : _spot(v.begin()), _limit(v.end()) {} | |
| /// Copy constructor. | |
| TransformView(self_type const &that) = default; | |
| /// Move constructor. | |
| TransformView(self_type &&that) = default; | |
| /// Copy assignment. | |
| self_type &operator=(self_type const &that) = default; | |
| /// Move assignment. | |
| self_type &operator=(self_type &&that) = default; | |
| /// Equality. | |
| bool operator==(self_type const &that) const; | |
| /// Inequality. | |
| bool operator!=(self_type const &that) const; | |
| /// Get the current element. | |
| value_type operator*() const; | |
| /// Move to next element. | |
| self_type &operator++(); | |
| /// Move to next element. | |
| self_type operator++(int); | |
| /// Check if view is empty. | |
| bool empty() const; | |
| /// Check if bool is not empty. | |
| explicit operator bool() const; | |
| /// Iterator to first transformed character. | |
| iterator begin() const; | |
| /// Iterator past last transformed character. | |
| iterator end() const; | |
| protected: | |
| source_iterator _spot; ///< Current location. | |
| source_iterator _limit; ///< End marker. | |
| /// Special constructor for making an empty instance to serve as the @c end iterator. | |
| explicit TransformView(source_iterator &&limit) : _spot(limit), _limit(limit) {} | |
| }; | |
| template <typename V> | |
| auto | |
| TransformView<void, V>::operator*() const -> value_type { | |
| return *_spot; | |
| } | |
| template <typename V> | |
| auto | |
| TransformView<void, V>::operator++() -> self_type & { | |
| ++_spot; | |
| return *this; | |
| } | |
| template <typename V> | |
| auto | |
| TransformView<void, V>::operator++(int) -> self_type { | |
| auto zret{*this}; | |
| ++*this; | |
| return zret; | |
| } | |
| template <typename V> | |
| bool | |
| TransformView<void, V>::operator==(const self_type &that) const { | |
| return _spot == that._spot && _limit == that._limit; | |
| } | |
| template <typename V> | |
| bool | |
| TransformView<void, V>::operator!=(const self_type &that) const { | |
| return _spot != that._spot || _limit != that._limit; | |
| } | |
| template <typename V> | |
| bool | |
| TransformView<void, V>::empty() const { | |
| return _spot == _limit; | |
| } | |
| template <typename V> TransformView<void, V>::operator bool() const { | |
| return _spot != _limit; | |
| } | |
| template <typename V> | |
| auto | |
| TransformView<void, V>::begin() const -> self_type { | |
| return *this; | |
| } | |
| template <typename V> | |
| auto | |
| TransformView<void, V>::end() const -> self_type { | |
| return self_type{_limit}; | |
| } | |
| /// @cond INTERNAL_DETAIL | |
| // Capture @c void transforms and make them identity transforms. | |
| template <typename V> | |
| TransformView<void, V> | |
| transform_view_of(V const &v) { | |
| return TransformView<void, V>(v); | |
| } | |
| /// @endcond | |
| /** User literals for TextView. | |
| * | |
| * - _tv : TextView | |
| * - _sv : std::string_view | |
| */ | |
| namespace literals { | |
| /** Literal constructor for @c std::string_view. | |
| * | |
| * @param s The source string. | |
| * @param n Size of the source string. | |
| * @return A @c string_view | |
| * | |
| * @internal This is provided because the STL one does not support @c constexpr which seems | |
| * rather bizarre to me, but there it is. Update: this depends on the version of the compiler, | |
| * so hopefully someday this can be removed. | |
| */ | |
| constexpr std::string_view | |
| operator""_sv(const char *s, size_t n) { | |
| return {s, n}; | |
| } | |
| /** Literal constructor for @c swoc::TextView. | |
| * | |
| * @param s The source string. | |
| * @param n Size of the source string. | |
| * @return A @c string_view | |
| * | |
| * @internal This is provided because the STL one does not support @c constexpr which seems | |
| * rather bizarre to me, but there it is. Update: this depends on the version of the compiler, | |
| * so hopefully someday this can be removed. | |
| */ | |
| constexpr swoc::TextView | |
| operator""_tv(const char *s, size_t n) { | |
| return {s, n}; | |
| } | |
| } // namespace literals | |
| }} // namespace swoc::SWOC_VERSION_NS | |
| namespace std { | |
| /// Write the contents of @a view to the stream @a os. | |
| ostream &operator<<(ostream &os, const swoc::TextView &view); | |
| /// @cond INTERNAL_DETAIL | |
| /* For interaction with specific STL interfaces, primarily std::filesystem. Along with the | |
| * dereference operator, this enables a @c TextView to act as a character iterator to a C string | |
| * even if the internal view is not nul terminated. | |
| * @note Putting these directly in the class doesn't seem to work. | |
| */ | |
| template <> struct iterator_traits<swoc::TextView> { | |
| using value_type = char; | |
| using pointer_type = const char *; | |
| using reference_type = const char &; | |
| using difference_type = ssize_t; | |
| using iterator_category = forward_iterator_tag; | |
| }; | |
| template <typename X, typename V> struct iterator_traits<swoc::TransformView<X, V>> { | |
| using value_type = typename swoc::TransformView<X, V>::value_type; | |
| using pointer_type = const value_type *; | |
| using reference_type = const value_type &; | |
| using difference_type = ssize_t; | |
| using iterator_category = forward_iterator_tag; | |
| }; | |
| template <> struct hash<swoc::TextView> { | |
| static constexpr hash<string_view> super_hash{}; | |
| size_t | |
| operator()(swoc::TextView const &s) const { | |
| return super_hash(s); | |
| } | |
| }; | |
| /// @endcond | |
| } // namespace std | |
| #if __GNUC__ == 11 | |
| #pragma GCC diagnostic pop | |
| #endif |