From 456dc1df0db2d8db6a1d1c42eab2d58177945abb Mon Sep 17 00:00:00 2001 From: Graham Sedman <71765842+grahamsedman@users.noreply.github.com> Date: Wed, 20 May 2026 19:28:40 +0100 Subject: [PATCH 1/2] refactor: reorganise includes and apply formatting to TextView Add missing standard library headers required for compilation, such as , , , , , , and . Reorder existing includes for better organisation. Apply consistent indentation and formatting style (placing opening braces on new lines) to the entire file to improve readability and adherence to style guidelines. Remove redundant TextView constructor overloads taking unsigned and ssize_t to simplify the interface and reduce ambiguity. Update namespace closing comments to reflect the new brace style. --- lib/swoc/include/swoc/TextView.h | 3972 +++++++++++++++--------------- 1 file changed, 2049 insertions(+), 1923 deletions(-) diff --git a/lib/swoc/include/swoc/TextView.h b/lib/swoc/include/swoc/TextView.h index ed017da673b..74c78daa3b5 100644 --- a/lib/swoc/include/swoc/TextView.h +++ b/lib/swoc/include/swoc/TextView.h @@ -12,17 +12,23 @@ */ #pragma once +#include #include +#include #include +#include #include +#include #include +#include #include #include -#include +#include +#include #include -#include "swoc/swoc_version.h" #include "swoc/string_view_util.h" +#include "swoc/swoc_version.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 @@ -32,2082 +38,2201 @@ #pragma GCC diagnostic ignored "-Warray-bounds" #endif -namespace swoc { inline namespace SWOC_VERSION_NS { +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::max() + 1> _chars; + }; -class TextView; + /** 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. + * + * 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 + * (char const *, char const *) then a construct like { "really", "broken" } can + * be interpreted as a @c TextView because the elements implicitly convert to char const + * *. 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 + explicit TextView( + T first, + std::enable_if_t && std::is_pointer_v && std::is_convertible_v, T> last) noexcept + : super_type(first, last - first) + { + } -/** A set of characters. - * - */ -class CharSet { - using self_type = CharSet; + /** 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 ().data()), char const *> && + std::is_convertible_v().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 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 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 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 ().data()), char const *> && + std::is_convertible_v().size()), size_t>, + void>> + constexpr self_type & + assign(C const &c) + { + return this->assign(c.data(), c.size()); + } -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); + /** 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 size_t find_if(F const &pred) const; + /// Get the offset of the last character for which @a pred is @c true. + template 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 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 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 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 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 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 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 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 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 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 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 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 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 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 this->prefix(prefix.size()) == prefix, @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 this->prefix(prefix.size()) == prefix, @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 this->prefix(prefix.size()) == prefix 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 this->prefix(prefix.size()) == prefix, @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 this->suffix(suffix.size()) == suffix, @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 this->suffix(suffix.size()) == suffix, @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 this->suffix(suffix.size()) == suffix 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 this->suffix(suffix.size()) == suffix 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 char const * 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 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); + }; - /** 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; + /// Internal table of digit values for characters. + /// This is -1 for characters that are not valid digits. + extern const int8_t svtoi_convert[256]; - /** 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; + /** Convert the text in @c TextView @a src to a signed numeric value. -protected: - std::bitset::max() + 1> _chars; -}; + 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: -/** 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. + - 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. -public: - /// Default constructor (empty buffer). - constexpr TextView() noexcept = default; + 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); - /// 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; + /** Convert the text in @c TextView @a src to an unsigned numeric value. - /** 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; + 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: - /** Construct from pointer and size. - * - * @param ptr Pointer to first character. - * @param n Number of characters. - */ - constexpr TextView(char const *ptr, unsigned n) noexcept; + - 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. - /** 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; + 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 + 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::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; + } - /** 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; + /// Convenience overload. + /// @see svto_radix(swoc::TextView &src) + template + uintmax_t + svto_radix(TextView &&src) + { + return svto_radix(src); + } - /** Construct from a half open range [first, last). + /** Parse @a text as a floating point number. * - * @param first Start of half open range. - * @param last End of half open range. + * @param text The input text. + * @param parsed Parsed text [out] + * @return The floating point value, or 0.0 if invalid input. * - * The character at @a first will be in the view, but the character at @a last will not. + * 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 @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 - * (char const *, char const *) then a construct like { "really", "broken" } can - * be interpreted as a @c TextView because the elements implicitly convert to char const - * *. 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. + * @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. */ - template - explicit TextView( - T first, - std::enable_if_t && std::is_pointer_v && std::is_convertible_v, T> last) noexcept - : super_type(first, last - first) {} + 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. - /** 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 ().data()), char const *> && - std::is_convertible_v().size()), size_t>, - void>> - constexpr TextView(C const &c); + inline constexpr CharSet::CharSet(TextView const &chars) + { + for (auto c : chars) { + _chars[uint8_t(c)] = true; + } + } - /** Construct from literal string or array. + inline bool + CharSet::operator()(unsigned char c) const + { + return _chars[c]; + } - 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. + inline bool + CharSet::operator()(char c) const + { + return _chars[uint8_t(c)]; + } - @code - TextView a("A literal string"); - @endcode - The last character in @a a will be 'g'. - */ - template constexpr TextView(const char (&s)[N]) noexcept; + // === 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, 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 constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {} + template 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(c)] = true; + } - /** 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) {} + inline auto + TextView::clear() -> self_type & + { + new (this) self_type(); + return *this; + } - /** 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) {} + inline constexpr char + TextView::operator*() const + { + return this->empty() ? char(0) : *(this->data()); + } - /** Construct from nullptr. - This implicitly makes the length 0. - */ - constexpr TextView(std::nullptr_t) noexcept; + inline constexpr bool + TextView::operator!() const noexcept + { + return this->empty(); + } - /// 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; + inline constexpr TextView::operator bool() const noexcept + { + return !this->empty(); + } - /// Assign a super class instance, @c std::string_view to @a this. - self_type &operator=(super_type const &that); + inline auto + TextView::operator++() -> self_type & + { + this->remove_prefix(1); + return *this; + } - /// 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 self_type &operator=(const char (&s)[N]); + inline auto + TextView::operator++(int) -> self_type + { + self_type zret{*this}; + this->remove_prefix(1); + return zret; + } - /// Assign from C-string @a s. - self_type &operator=(char *&s); - /// Assign from C-string @a s. - self_type &operator=(char const *&s); + inline auto + TextView::operator+=(size_t n) -> self_type & + { + this->remove_prefix(n); + return *this; + } - /// Assign from a @c std::string. - self_type &operator=(const std::string &s); + template + inline auto + TextView::operator=(const char (&s)[N]) -> self_type & + { + return *this = self_type{s, s[N - 1] ? N : N - 1}; + } - /** 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); + inline auto + TextView::operator=(super_type const &that) -> self_type & + { + this->super_type::operator=(that); + return *this; + } - /** 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); + inline auto + TextView::operator=(char *&s) -> self_type & + { + this->super_type::operator=(s); + return *this; + } - /** 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); + inline auto + TextView::operator=(char const *&s) -> self_type & + { + this->super_type::operator=(s); + return *this; + } - /** 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); + inline auto + TextView::operator=(const std::string &s) -> self_type & + { + this->super_type::operator=(s); + return *this; + } - /// Explicitly set the view from a @c std::string - self_type &assign(std::string const &s); + inline auto + TextView::assign(char *&c_str) -> self_type & + { + return this->assign(c_str, strlen(c_str)); + } - /** Assign literal string or array. + inline auto + TextView::assign(char const *&c_str) -> self_type & + { + return this->assign(c_str, strlen(c_str)); + } - * 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 self_type &assign(const char (&s)[N]) noexcept; + inline auto + TextView::assign(const std::string &s) -> self_type & + { + *this = super_type(s); + return *this; + } - /** 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 ().data()), char const *> && - std::is_convertible_v().size()), size_t>, - void>> - constexpr self_type & - assign(C const &c) { - return this->assign(c.data(), c.size()); + inline TextView & + TextView::assign(char const *ptr, size_t n) + { + *this = super_type(ptr, n == npos ? (ptr ? ::strlen(ptr) : 0) : n); + return *this; } - /** Dereference operator. + inline TextView & + TextView::assign(char const *b, char const *e) + { + *this = super_type(b, e - b); + return *this; + } - @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. + template + inline auto + TextView::assign(char const (&s)[N]) noexcept -> self_type & + { + return *this = self_type{s, s[N - 1] ? N : N - 1}; + } - @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; + inline constexpr auto + TextView::prefix(size_t n) const noexcept -> self_type + { + return {this->data(), std::min(n, this->size())}; + } - /** Discard the first byte of the view. - * - * @return @a this. - */ - self_type &operator++(); + inline constexpr TextView + TextView::prefix(int n) const noexcept + { + return {this->data(), std::min(n, this->size())}; + } - /** Discard the first byte of the view. - * - * @return The view before discarding the byte. - */ - self_type operator++(int); + 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; + } - /** Discard the first @a n bytes of the view. - * - * Equivalent to @c remove_prefix(n). - * @return @a this - */ - self_type &operator+=(size_t n); + 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; + } - /// Check for empty view. - /// @return @c true if the view has a nullptr @b or zero size. - constexpr bool operator!() const noexcept; + template + 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; + } - /// 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; + inline auto + TextView::remove_prefix(size_t n) -> self_type & + { + this->super_type::remove_prefix(std::min(n, this->size())); + return *this; + } - /// Clear the view (become an empty view). - self_type &clear(); + 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; + } - /// Get the offset of the first character for which @a pred is @c true. - template size_t find_if(F const &pred) const; - /// Get the offset of the last character for which @a pred is @c true. - template size_t rfind_if(F const &pred) const; + 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; + } - /** Remove bytes that match @a c from the start of the view. - * - * @return @a this - */ - self_type <rim(char c); + template + 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; + } - /** Remove bytes from the start of the view that are in @a delimiters. - * - * @return @a this - */ - self_type <rim(CharSet const &delimiters); + 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; + } - /** Remove bytes from the start of the view that are in @a delimiters. - * - * @return @a this - */ - self_type <rim(std::string_view const &delimiters); + inline TextView + TextView::split_prefix(int n) + { + return this->split_prefix(size_t(n)); + } - /** 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); + inline TextView + TextView::split_prefix_at(char c) + { + return this->split_prefix(this->find(c)); + } - /** 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 self_type <rim_if(F const &pred); + inline TextView + TextView::split_prefix_at(std::string_view const &delimiters) + { + return this->split_prefix(this->find_first_of(delimiters)); + } - /** Remove bytes that match @a c from the end of the view. - * - * @return @a this - */ - self_type &rtrim(char c); + template + TextView::self_type + TextView::split_prefix_if(F const &pred) + { + return this->split_prefix(this->find_if(pred)); + } - /** Remove bytes from the end of the view that are in @a delimiters. - * - * @return @a this - */ - self_type &rtrim(CharSet const &delimiters); + 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; + } - /** Remove bytes from the end of the view that are in @a delimiters. - * @return @a this - */ - self_type &rtrim(std::string_view const &delimiters); + inline TextView + TextView::take_prefix_at(char c) + { + return this->take_prefix(this->find(c)); + } - /** 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 self_type &rtrim_if(F const &pred); + inline TextView + TextView::take_prefix_at(std::string_view const &delimiters) + { + return this->take_prefix(this->find_first_of(delimiters)); + } - /** Remove bytes that match @a c from the start and end of this view. - * - * @return @a this - */ - self_type &trim(char c); + template + auto + TextView::take_prefix_if(F const &pred) -> self_type + { + return this->take_prefix(this->find_if(pred)); + } - /** Remove bytes from the start and end of the view that are in @a delimiters. - * @return @a this - */ - self_type &trim(CharSet const &delimiters); + inline constexpr TextView + TextView::suffix(size_t n) const noexcept + { + n = std::min(n, this->size()); + return {this->data_end() - n, n}; + } - /** 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); + inline constexpr TextView + TextView::suffix(int n) const noexcept + { + return this->suffix(size_t(n)); + } - /** 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); + 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; + } - /** 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 self_type &trim_if(F const &pred); + 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; + } - /** 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; + template + 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; + } - /** 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; + inline auto + TextView::remove_suffix(size_t n) -> self_type & + { + this->super_type::remove_suffix(std::min(n, this->size())); + return *this; + } - /** 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; + 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(); + } - /** 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 self_type prefix_if(F const &pred) const; + 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(); + } - /** 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); + template + 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(); + } - /** 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); + 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; + } - /** 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); + inline auto + TextView::split_suffix(int n) -> self_type + { + return this->split_suffix(size_t(n)); + } - /** 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); + 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)); + } - /** 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 self_type &remove_prefix_if(F const &pred); + 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)); + } - /** 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); + template + TextView::self_type + TextView::split_suffix_if(F const &pred) + { + return this->split_suffix(this->rfind_if(pred)); + } - /** 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); + inline TextView + TextView::take_suffix(size_t n) + { + self_type zret{*this}; + *this = zret.split_prefix(n); + return zret; + } - /** 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); + inline TextView + TextView::take_suffix(int n) + { + return this->take_suffix(size_t(n)); + } - /** 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 self_type split_prefix_if(F const &pred); + inline TextView + TextView::take_suffix_at(char c) + { + return this->take_suffix(this->rfind(c)); + } - /** 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); + inline TextView + TextView::take_suffix_at(std::string_view const &delimiters) + { + return this->take_suffix(this->find_last_of(delimiters)); + } - /** 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); + template + TextView::self_type + TextView::take_suffix_if(F const &pred) + { + return this->take_suffix_at(this->rfind_if(pred)); + } - /** 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); + template + 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; + } - /** 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 self_type take_prefix_if(F const &pred); + template + 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; + } - /** 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 self_type clip_prefix_of(F const &pred); + inline TextView & + TextView::ltrim(char c) + { + this->remove_prefix(this->find_first_not_of(c)); + return *this; + } - /** 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; + 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; + } - /** 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; + inline TextView & + TextView::trim(char c) + { + return this->ltrim(c).rtrim(c); + } - /** 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; + inline TextView & + TextView::ltrim(CharSet const &delimiters) + { + const char *spot = this->data(); + const char *limit = this->data_end(); - /** 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 self_type suffix_if(F const &pred) const; + while (spot < limit && delimiters(*spot)) { + ++spot; + } + this->remove_prefix(spot - this->data()); - /** 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); + return *this; + } - /** 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); + inline TextView & + TextView::ltrim(std::string_view const &delimiters) + { + return this->ltrim(CharSet(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 self_type &remove_suffix_if(F const &pred); + inline TextView & + TextView::ltrim(const char *delimiters) + { + return this->ltrim(CharSet(delimiters)); + } - /** 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); + inline TextView & + TextView::rtrim(CharSet const &delimiters) + { + const char *spot = this->data_end(); + const char *limit = this->data(); + while (limit < spot-- && delimiters(*spot)) {} - /** 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); + this->remove_suffix(this->data_end() - (spot + 1)); + return *this; + } - /** 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); + inline TextView & + TextView::rtrim(std::string_view const &delimiters) + { + return this->rtrim(CharSet(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 self_type split_suffix_if(F const &pred); + inline TextView & + TextView::trim(CharSet const &delimiters) + { + const char *spot; + const char *limit; - /** 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); + // 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()); - /** 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); + spot = this->data_end(); + limit = this->data(); + while (limit < spot-- && delimiters(*spot)) {} + this->remove_suffix(this->data_end() - (spot + 1)); - /** 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); + return *this; + } - /** 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 self_type take_suffix_if(F const &pred); + inline TextView & + TextView::trim(std::string_view const &delimiters) + { + return this->trim(CharSet(delimiters)); + } - /** 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 self_type clip_suffix_of(F const &pred); + inline TextView & + TextView::trim(const char *delimiters) + { + return this->trim(CharSet(delimiters)); + } - /** 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; + template + 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; + } - /** Check if the view begins with a specific @a prefix. - * - * @param prefix String to check against @a this. - * @return @c true if this->prefix(prefix.size()) == prefix, @c false otherwise. - * @internal C++20 preview. - */ - bool starts_with(std::string_view const &prefix) const noexcept; + template + 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; + } - /** Check if the view begins with a specific @a prefix. - * - * @param prefix String to check against @a this. - * @return @c true if this->prefix(prefix.size()) == prefix, @c false otherwise. - * @internal C++20 preview. - */ - bool starts_with(char const *prefix) const; + template + TextView::self_type & + TextView::trim_if(F const &pred) + { + return this->ltrim_if(pred).rtrim_if(pred); + } - /** 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; + constexpr inline auto + TextView::data() const noexcept -> value_type const * + { + return super_type::data(); + } - /** Check if the view begins with a specific @a prefix, ignoring case. - * - * @param prefix String to check against @a this. - * @return @c true if this->prefix(prefix.size()) == prefix without regard to case, @c false otherwise. - * @internal C++20 preview. - */ - bool starts_with_nocase(std::string_view const &prefix) const noexcept; + constexpr inline auto + TextView::data_end() const noexcept -> value_type const * + { + return this->data() + this->size(); + } - /** Check if the view begins with a specific @a prefix. - * - * @param prefix String to check against @a this. - * @return @c true if this->prefix(prefix.size()) == prefix, @c false otherwise. - * @internal C++20 preview. - */ - bool starts_with_nocase(char const *prefix) const; + 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}; + } - /** 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; + 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()); + } - /** Check if the view ends with a specific @a suffix. - * - * @param suffix String to check against @a this. - * @return @c true if this->suffix(suffix.size()) == suffix, @c false otherwise. - * @internal C++20 preview. - */ - bool ends_with(std::string_view const &suffix) const noexcept; + 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}); + } - /** Check if the view ends with a specific @a suffix. - * - * @param suffix String to check against @a this. - * @return @c true if this->suffix(suffix.size()) == suffix, @c false otherwise. - * @internal C++20 preview. - */ - bool ends_with(char const *suffix) const; + 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()); + } - /** 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; + 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()); + } - /** Check if the view starts with a specific @a suffix, ignoring case. - * - * @param suffix String to check against @a this. - * @return @c true if this->suffix(suffix.size()) == suffix without regard to case, @c false otherwise. - * @internal C++20 preview. - */ - bool ends_with_nocase(std::string_view const &suffix) const noexcept; + 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()); + } - /** Check if the view starts with a specific @a suffix, ignoring case. - * - * @param suffix String to check against @a this. - * @return @c true if this->suffix(suffix.size()) == suffix without regard to case, @c false otherwise. - * @internal C++20 preview. - */ - bool ends_with_nocase(char const *suffix) const; + 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()); + } - /** 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); - } - }; + inline bool + TextView::ends_with(char const *suffix) const + { + return this->ends_with(super_type(suffix)); + } - /// 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); - } - }; + inline bool + TextView::ends_with_nocase(char const *suffix) const + { + return this->ends_with_nocase(super_type(suffix)); + } - /// 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); + 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 + 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 + 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 + 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 - /** A pointer to the first byte. + // 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. * - * @return Address of the first byte of the view. + * @tparam X Transform functor type. + * @tparam V Source view type. * - * @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 char const * which makes meta-programming - * painful. - */ - constexpr value_type const *data() const noexcept; - - /** A pointer to past the last byte. + * A transform view acts like a view on the original source view @a V with each element transformed by + * @a X. * - * @return Address of the first byte past the end of the view. + * 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 xv(&tolower, source); + * @endcode * - * 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 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 + * 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 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; + /// Result type of calling the transform on an element of the source view. + using value_type = std::invoke_result_t; + /// 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: - /// 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); -}; + protected: + transform_type _xf; ///< Per character transform functor. + source_iterator _spot; ///< Current location in the source view. + source_iterator _limit; ///< End of source view. -/// Internal table of digit values for characters. -/// This is -1 for characters that are not valid digits. -extern const int8_t svtoi_convert[256]; + /// 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) {} + }; -/** Convert the text in @c TextView @a src to a signed numeric value. + template + TransformView::TransformView(transform_type &&xf, source_view_type const &v) : _xf(xf), _spot(v.begin()), _limit(v.end()) + { + } - 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: + template + TransformView::TransformView(transform_type const &xf, source_view_type const &v) + : _xf(xf), _spot(v.begin()), _limit(v.end()) + { + } - - 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. + template + auto + TransformView::operator*() const -> value_type + { + return _xf(*_spot); + } - 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); + template + auto + TransformView::operator++() -> self_type & + { + ++_spot; + return *this; + } -/** Convert the text in @c TextView @a src to an unsigned numeric value. + template + auto + TransformView::operator++(int) -> self_type + { + self_type zret{*this}; + ++_spot; + return zret; + } - 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: + template + bool + TransformView::empty() const + { + return _spot == _limit; + } - - 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. + template TransformView::operator bool() const + { + return _spot != _limit; + } - 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 -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::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. - } + template + bool + TransformView::operator==(self_type const &that) const + { + return _spot == that._spot && _limit == that._limit; } - return zret; -} - -/// Convenience overload. -/// @see svto_radix(swoc::TextView &src) -template -uintmax_t -svto_radix(TextView &&src) { - return svto_radix(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 constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {} -template 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(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 -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 -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(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 -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 -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())); + + template + bool + TransformView::operator!=(self_type const &that) const + { + return _spot != that._spot || _limit != that._limit; } - 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 -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 -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 -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 -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 -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 -TextView::self_type -TextView::take_suffix_if(F const &pred) { - return this->take_suffix_at(this->rfind_if(pred)); -} - -template -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 -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 -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 -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 -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 -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 -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 -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 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 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; - /// Result type of calling the transform on an element of the source view. - using value_type = std::invoke_result_t; - /// This class serves as its own iterator. - using iterator = self_type; - - /** Construct a transform view using transform @a xf on source view @a v. + /** Create a transformed view of a source. * - * @param xf Transform instance. - * @param v Source view. + * @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. */ - TransformView(transform_type &&xf, source_view_type const &v); + template + TransformView + transform_view_of(X const &xf, V const &src) + { + return TransformView(xf, src); + } - /** Construct a transform view using transform @a xf on source view @a v. + /** Identity transform view. + * + * @tparam V The source type. * - * @param xf Transform instance. - * @param v Source view. + * This is a transform that returns the input unmodified. This is convenient when a transform is + * required in general but not in all cases. */ - 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}; - } + template class TransformView + { + using self_type = TransformView; ///< Self reference type. + /// Iterator over source, for internal use. + using source_iterator = decltype(V{}.begin()); -protected: - transform_type _xf; ///< Per character transform functor. - source_iterator _spot; ///< Current location in the source view. - source_iterator _limit; ///< End of source view. + public: + using source_view_type = V; ///< Export source view type. + using source_value_type = std::remove_reference_t; + /// 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; - /// 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) {} -}; + /** Construct identity transform view from @a v. + * + * @param v Source view. + */ + TransformView(source_view_type const &v) : _spot(v.begin()), _limit(v.end()) {} -template -TransformView::TransformView(transform_type &&xf, source_view_type const &v) : _xf(xf), _spot(v.begin()), _limit(v.end()) {} - -template -TransformView::TransformView(transform_type const &xf, source_view_type const &v) - : _xf(xf), _spot(v.begin()), _limit(v.end()) {} - -template -auto -TransformView::operator*() const -> value_type { - return _xf(*_spot); -} - -template -auto -TransformView::operator++() -> self_type & { - ++_spot; - return *this; -} - -template -auto -TransformView::operator++(int) -> self_type { - self_type zret{*this}; - ++_spot; - return zret; -} - -template -bool -TransformView::empty() const { - return _spot == _limit; -} - -template TransformView::operator bool() const { - return _spot != _limit; -} - -template -bool -TransformView::operator==(self_type const &that) const { - return _spot == that._spot && _limit == that._limit; -} - -template -bool -TransformView::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 -TransformView -transform_view_of(X const &xf, V const &src) { - return TransformView(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 class TransformView { - 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; - /// 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 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; - /// 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; - /// Equality. - bool operator==(self_type const &that) const; - /// Inequality. - bool operator!=(self_type const &that) const; + /// Get the current element. + value_type operator*() const; - /// Get the current element. - value_type operator*() const; + /// Move to next element. + self_type &operator++(); - /// Move to next element. - self_type &operator++(); + /// Move to next element. + self_type operator++(int); - /// Move to next element. - self_type operator++(int); + /// Check if view is empty. + bool empty() const; - /// Check if view is empty. - bool empty() const; + /// Check if bool is not empty. + explicit operator bool() 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; - /// 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. -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) {} + }; - /// Special constructor for making an empty instance to serve as the @c end iterator. - explicit TransformView(source_iterator &&limit) : _spot(limit), _limit(limit) {} -}; + template + auto + TransformView::operator*() const -> value_type + { + return *_spot; + } -template -auto -TransformView::operator*() const -> value_type { - return *_spot; -} - -template -auto -TransformView::operator++() -> self_type & { - ++_spot; - return *this; -} - -template -auto -TransformView::operator++(int) -> self_type { - auto zret{*this}; - ++*this; - return zret; -} - -template -bool -TransformView::operator==(const self_type &that) const { - return _spot == that._spot && _limit == that._limit; -} - -template -bool -TransformView::operator!=(const self_type &that) const { - return _spot != that._spot || _limit != that._limit; -} - -template -bool -TransformView::empty() const { - return _spot == _limit; -} - -template TransformView::operator bool() const { - return _spot != _limit; -} - -template -auto -TransformView::begin() const -> self_type { - return *this; -} - -template -auto -TransformView::end() const -> self_type { - return self_type{_limit}; -} + template + auto + TransformView::operator++() -> self_type & + { + ++_spot; + return *this; + } -/// @cond INTERNAL_DETAIL -// Capture @c void transforms and make them identity transforms. -template -TransformView -transform_view_of(V const &v) { - return TransformView(v); -} + template + auto + TransformView::operator++(int) -> self_type + { + auto zret{*this}; + ++*this; + return zret; + } -/// @endcond + template + bool + TransformView::operator==(const self_type &that) const + { + return _spot == that._spot && _limit == that._limit; + } -/** 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 + template + bool + TransformView::operator!=(const self_type &that) const + { + return _spot != that._spot || _limit != that._limit; + } + + template + bool + TransformView::empty() const + { + return _spot == _limit; + } + + template TransformView::operator bool() const + { + return _spot != _limit; + } + + template + auto + TransformView::begin() const -> self_type + { + return *this; + } + + template + auto + TransformView::end() const -> self_type + { + return self_type{_limit}; + } + + /// @cond INTERNAL_DETAIL + // Capture @c void transforms and make them identity transforms. + template + TransformView + transform_view_of(V const &v) + { + return TransformView(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 SWOC_VERSION_NS +} // namespace swoc -namespace std { +namespace std +{ /// Write the contents of @a view to the stream @a os. ostream &operator<<(ostream &os, const swoc::TextView &view); @@ -2136,7 +2261,8 @@ template struct iterator_traits struct hash { static constexpr hash super_hash{}; size_t - operator()(swoc::TextView const &s) const { + operator()(swoc::TextView const &s) const + { return super_hash(s); } }; From e2b7fcb74c62f0ee5adf5ba6e454006721e1e70a Mon Sep 17 00:00:00 2001 From: Graham Sedman <71765842+grahamsedman@users.noreply.github.com> Date: Wed, 20 May 2026 20:01:33 +0100 Subject: [PATCH 2/2] refactor: update code formatting and remove unused includes Update the source code style and remove unnecessary header dependencies. * Adjust indentation for the contents of the swoc and SWOC_VERSION_NS namespaces. * Move opening braces for functions and namespaces to new lines. * Remove unused and includes. --- lib/swoc/src/TextView.cc | 383 ++++++++++++++++++++------------------- 1 file changed, 195 insertions(+), 188 deletions(-) diff --git a/lib/swoc/src/TextView.cc b/lib/swoc/src/TextView.cc index 5e6cd749117..0b79267fc77 100644 --- a/lib/swoc/src/TextView.cc +++ b/lib/swoc/src/TextView.cc @@ -10,233 +10,240 @@ */ #include "swoc/TextView.h" -#include -#include using namespace swoc::literals; -namespace swoc { inline namespace SWOC_VERSION_NS { - -/// @cond INTERNAL_DETAIL -const int8_t svtoi_convert[256] = { - /* [can't do this nicely because clang format won't allow exdented comments] - 0 1 2 3 4 5 6 7 8 9 A B C D E F - */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 30 - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 40 - 25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 50 - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 60 - 25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 70 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // B0 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // D0 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // F0 -}; -/// @endcond - -intmax_t -svtoi(TextView src, TextView *out, int base) { - static constexpr uintmax_t ABS_MAX = std::numeric_limits::max(); - static constexpr uintmax_t ABS_MIN = uintmax_t(std::numeric_limits::min()); - intmax_t zret = 0; - - if (src.ltrim_if(&isspace)) { - TextView parsed; - const char *start = src.data(); - bool neg = false; - if ('-' == *src) { - ++src; - neg = true; - } else if ('+' == *src) { - ++src; - } - auto n = svtou(src, &parsed, base); - if (!parsed.empty()) { - if (out) { - out->assign(start, parsed.data_end()); +namespace swoc +{ +inline namespace SWOC_VERSION_NS +{ + + /// @cond INTERNAL_DETAIL + const int8_t svtoi_convert[256] = { + /* [can't do this nicely because clang format won't allow exdented comments] + 0 1 2 3 4 5 6 7 8 9 A B C D E F + */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 30 + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 40 + 25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 50 + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 60 + 25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 70 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // B0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // D0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // F0 + }; + /// @endcond + + intmax_t + svtoi(TextView src, TextView *out, int base) + { + static constexpr uintmax_t ABS_MAX = std::numeric_limits::max(); + static constexpr uintmax_t ABS_MIN = uintmax_t(std::numeric_limits::min()); + intmax_t zret = 0; + + if (src.ltrim_if(&isspace)) { + TextView parsed; + const char *start = src.data(); + bool neg = false; + if ('-' == *src) { + ++src; + neg = true; + } else if ('+' == *src) { + ++src; } - if (neg) { - uintmax_t temp = std::min(n, ABS_MIN); - if (temp == ABS_MIN) { - zret = std::numeric_limits::min(); + auto n = svtou(src, &parsed, base); + if (!parsed.empty()) { + if (out) { + out->assign(start, parsed.data_end()); + } + if (neg) { + uintmax_t temp = std::min(n, ABS_MIN); + if (temp == ABS_MIN) { + zret = std::numeric_limits::min(); + } else { + zret = -intmax_t(temp); + } } else { - zret = -intmax_t(temp); + zret = std::min(n, ABS_MAX); } - } else { - zret = std::min(n, ABS_MAX); } } + return zret; } - return zret; -} -uintmax_t -svtou(TextView src, TextView *out, int base) { - uintmax_t zret = 0; + uintmax_t + svtou(TextView src, TextView *out, int base) + { + uintmax_t zret = 0; - if (out) { - out->clear(); - } + if (out) { + out->clear(); + } - if (src.ltrim_if(&isspace).size()) { - auto origin = src.data(); // cache to handle prefix skipping. - // If base is 0, it wasn't specified - check for standard base prefixes - if (0 == base) { - base = 10; - if ('0' == *src) { - ++src; - base = 8; - if (src) { - switch (*src) { - case 'x': - case 'X': - ++src; - base = 16; - break; - case 'b': - case 'B': - ++src; - base = 2; - break; + if (src.ltrim_if(&isspace).size()) { + auto origin = src.data(); // cache to handle prefix skipping. + // If base is 0, it wasn't specified - check for standard base prefixes + if (0 == base) { + base = 10; + if ('0' == *src) { + ++src; + base = 8; + if (src) { + switch (*src) { + case 'x': + case 'X': + ++src; + base = 16; + break; + case 'b': + case 'B': + ++src; + base = 2; + break; + } } } } - } - if (!(1 <= base && base <= 36)) { - return 0; - } + if (!(1 <= base && base <= 36)) { + return 0; + } - // For performance in common cases, use the templated conversion. - switch (base) { - case 2: - zret = svto_radix<2>(src); - break; - case 8: - zret = svto_radix<8>(src); - break; - case 10: - zret = svto_radix<10>(src); - break; - case 16: - zret = svto_radix<16>(src); - break; - default: { - static constexpr auto MAX = std::numeric_limits::max(); - const auto OVERFLOW_LIMIT = MAX / base; - intmax_t v = 0; - while (src.size() && (0 <= (v = svtoi_convert[static_cast(*src)])) && v < base) { - ++src; - if (zret <= OVERFLOW_LIMIT && uintmax_t(v) <= (MAX - (zret *= base))) { - zret += v; - } else { - zret = MAX; + // For performance in common cases, use the templated conversion. + switch (base) { + case 2: + zret = svto_radix<2>(src); + break; + case 8: + zret = svto_radix<8>(src); + break; + case 10: + zret = svto_radix<10>(src); + break; + case 16: + zret = svto_radix<16>(src); + break; + default: { + static constexpr auto MAX = std::numeric_limits::max(); + const auto OVERFLOW_LIMIT = MAX / base; + intmax_t v = 0; + while (src.size() && (0 <= (v = svtoi_convert[static_cast(*src)])) && v < base) { + ++src; + if (zret <= OVERFLOW_LIMIT && uintmax_t(v) <= (MAX - (zret *= base))) { + zret += v; + } else { + zret = MAX; + } } + break; } - break; } - } - if (out) { - out->assign(origin, src.data()); + if (out) { + out->assign(origin, src.data()); + } } + return zret; } - return zret; -} -double -svtod(swoc::TextView text, swoc::TextView *parsed) { - // @return 10^e - auto pow10 = [](int e) -> double { - double zret = 1.0; - double scale = 10.0; - if (e < 0) { // flip the scale and make @a e positive. - e = -e; - scale = 0.1; - } + double + svtod(swoc::TextView text, swoc::TextView *parsed) + { + // @return 10^e + auto pow10 = [](int e) -> double { + double zret = 1.0; + double scale = 10.0; + if (e < 0) { // flip the scale and make @a e positive. + e = -e; + scale = 0.1; + } - // Walk the bits in the exponent @a e and multiply the scale for set bits. - while (e) { - if (e & 1) { - zret *= scale; + // Walk the bits in the exponent @a e and multiply the scale for set bits. + while (e) { + if (e & 1) { + zret *= scale; + } + scale *= scale; + e >>= 1; } - scale *= scale; - e >>= 1; - } - return zret; - }; + return zret; + }; - if (text.empty()) { - return 0; - } + if (text.empty()) { + return 0; + } - auto org_text = text; // save this to update @a parsed. - // Check just once and dump to a local copy if needed. - TextView local_parsed; - if (!parsed) { - parsed = &local_parsed; - } + auto org_text = text; // save this to update @a parsed. + // Check just once and dump to a local copy if needed. + TextView local_parsed; + if (!parsed) { + parsed = &local_parsed; + } - // Handle leading sign. - int sign = 1; - if (*text == '-') { - ++text; - sign = -1; - } else if (*text == '+') { - ++text; - } - // Parse the leading whole part as an integer. - intmax_t whole = svto_radix<10>(text); - parsed->assign(org_text.data(), text.data()); + // Handle leading sign. + int sign = 1; + if (*text == '-') { + ++text; + sign = -1; + } else if (*text == '+') { + ++text; + } + // Parse the leading whole part as an integer. + intmax_t whole = svto_radix<10>(text); + parsed->assign(org_text.data(), text.data()); - if (text.empty()) { - return whole; - } + if (text.empty()) { + return whole; + } - double frac = 0.0; - if (*text == '.') { // fractional part. - ++text; - double scale = 0.1; - while (text && isdigit(*text)) { - frac += scale * (*text++ - '0'); - scale /= 10.0; + double frac = 0.0; + if (*text == '.') { // fractional part. + ++text; + double scale = 0.1; + while (text && isdigit(*text)) { + frac += scale * (*text++ - '0'); + scale /= 10.0; + } } - } - double exp = 1.0; - if (text.starts_with_nocase("e")) { - int exp_sign = 1; - ++text; - if (text) { - if (*text == '+') { - ++text; - } else if (*text == '-') { - ++text; - exp_sign = -1; + double exp = 1.0; + if (text.starts_with_nocase("e")) { + int exp_sign = 1; + ++text; + if (text) { + if (*text == '+') { + ++text; + } else if (*text == '-') { + ++text; + exp_sign = -1; + } } + auto exp_part = svto_radix<10>(text); + exp = pow10(exp_part * exp_sign); } - auto exp_part = svto_radix<10>(text); - exp = pow10(exp_part * exp_sign); - } - parsed->assign(org_text.data(), text.data()); - return sign * (whole + frac) * exp; -} + parsed->assign(org_text.data(), text.data()); + return sign * (whole + frac) * exp; + } -// Do the template instantiations. -template std::ostream &TextView::stream_write(std::ostream &, const TextView &) const; + // Do the template instantiations. + template std::ostream &TextView::stream_write(std::ostream &, const TextView &) const; -}} // namespace swoc::SWOC_VERSION_NS +} // namespace SWOC_VERSION_NS +} // namespace swoc -namespace std { +namespace std +{ ostream & -operator<<(ostream &os, const swoc::TextView &b) { +operator<<(ostream &os, const swoc::TextView &b) +{ if (os.good()) { b.stream_write(os, b); os.width(0);