You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2017/10/08 20:46:41 UTC

[trafficserver] branch master updated: TextView: First pass on converting StringView to TextView and basing it on ts::string_view.

This is an automated email from the ASF dual-hosted git repository.

amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 0ba2353  TextView: First pass on converting StringView to TextView and basing it on ts::string_view.
0ba2353 is described below

commit 0ba2353890a9e4fb1af53b7b46b4ea279860a13a
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Sun Sep 24 15:20:43 2017 -0500

    TextView: First pass on converting StringView to TextView and basing it on ts::string_view.
---
 iocore/net/P_SSLNetVConnection.h              |   13 +-
 lib/records/RecHttp.cc                        |    2 +-
 lib/records/RecRawStats.cc                    |    2 +-
 lib/ts/Makefile.am                            |   13 +-
 lib/ts/{MemView.cc => TextView.cc}            |   41 +-
 lib/ts/{MemView.h => TextView.h}              | 1904 ++++++++++++-------------
 lib/ts/ink_inet.cc                            |  136 +-
 lib/ts/ink_inet.h                             |    4 +-
 lib/ts/test_MemView.cc                        |   96 --
 lib/ts/unit-tests/test_TextView.cc            |  147 ++
 lib/ts/unit-tests/test_ink_inet.cc            |   82 ++
 lib/ts/unit-tests/test_string_view.cc         |    5 +
 mgmt/LocalManager.cc                          |    8 +-
 proxy/ProxyClientSession.h                    |    5 +-
 proxy/ProxyClientTransaction.h                |    2 +-
 proxy/http/ForwardedConfig.cc                 |   24 +-
 proxy/http/HttpSM.h                           |    2 +-
 proxy/http/HttpTransactHeaders.cc             |    2 +-
 proxy/http/Makefile.am                        |    2 +-
 proxy/http/unit-tests/sym-links/MemView.cc    |    1 -
 proxy/http/unit-tests/test_ForwardedConfig.cc |    4 +-
 proxy/http2/Http2ClientSession.h              |    2 +-
 22 files changed, 1257 insertions(+), 1240 deletions(-)

diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index 125d3c8..f885184 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -32,16 +32,17 @@
 #if !defined(_SSLNetVConnection_h_)
 #define _SSLNetVConnection_h_
 
-#include "ts/ink_platform.h"
-#include "P_EventSystem.h"
-#include "P_UnixNetVConnection.h"
-#include "P_UnixNet.h"
-#include "ts/apidefs.h"
-#include <ts/MemView.h>
+#include <ts/ink_platform.h>
+#include <ts/apidefs.h>
+#include <ts/string_view.h>
 
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 
+#include "P_EventSystem.h"
+#include "P_UnixNetVConnection.h"
+#include "P_UnixNet.h"
+
 // These are included here because older OpenSSL libraries don't have them.
 // Don't copy these defines, or use their values directly, they are merely
 // here to avoid compiler errors.
diff --git a/lib/records/RecHttp.cc b/lib/records/RecHttp.cc
index 61b7541..e480c51 100644
--- a/lib/records/RecHttp.cc
+++ b/lib/records/RecHttp.cc
@@ -26,9 +26,9 @@
 #include <ts/ink_defs.h>
 #include <ts/ink_hash_table.h>
 #include <ts/Tokenizer.h>
-#include <ts/MemView.h>
 #include <strings.h>
 #include <ts/ink_inet.h>
+#include <ts/string_view.h>
 
 SessionProtocolNameRegistry globalSessionProtocolNameRegistry;
 
diff --git a/lib/records/RecRawStats.cc b/lib/records/RecRawStats.cc
index 8a57019..0dd7168 100644
--- a/lib/records/RecRawStats.cc
+++ b/lib/records/RecRawStats.cc
@@ -23,7 +23,7 @@
 
 #include "P_RecCore.h"
 #include "P_RecProcess.h"
-#include <ts/MemView.h>
+#include <ts/string_view.h>
 
 //-------------------------------------------------------------------------
 // raw_stat_get_total
diff --git a/lib/ts/Makefile.am b/lib/ts/Makefile.am
index 5880f5c..0e8d1ab 100644
--- a/lib/ts/Makefile.am
+++ b/lib/ts/Makefile.am
@@ -23,7 +23,7 @@ library_includedir=$(includedir)/ts
 library_include_HEADERS = apidefs.h
 
 noinst_PROGRAMS = mkdfa CompileParseRules
-check_PROGRAMS = test_tsutil test_arena test_atomic test_freelist test_geometry test_List test_Map test_Vec test_X509HostnameValidator test_MemView test_Scalar test_tslib
+check_PROGRAMS = test_tsutil test_arena test_atomic test_freelist test_geometry test_List test_Map test_Vec test_X509HostnameValidator test_Scalar test_tslib
 
 TESTS_ENVIRONMENT = LSAN_OPTIONS=suppressions=suppression.txt
 
@@ -31,7 +31,7 @@ TESTS = $(check_PROGRAMS)
 
 lib_LTLIBRARIES = libtsutil.la
 
-AM_CPPFLAGS += -I$(abs_top_srcdir)/lib 
+AM_CPPFLAGS += -I$(abs_top_srcdir)/lib
 
 libtsutil_la_LDFLAGS = -no-undefined -version-info @TS_LIBTOOL_VERSION@
 libtsutil_la_LIBADD = \
@@ -162,7 +162,6 @@ libtsutil_la_SOURCES = \
   Map.h \
   MatcherUtils.cc \
   MatcherUtils.h \
-  MemView.h MemView.cc \
   MMH.cc \
   MMH.h \
   ParseRules.cc \
@@ -190,6 +189,7 @@ libtsutil_la_SOURCES = \
   TestBox.h \
   TextBuffer.cc \
   TextBuffer.h \
+  TextView.h TextView.cc \
   Tokenizer.cc \
   Tokenizer.h \
   Trie.h \
@@ -244,9 +244,6 @@ test_tsutil_SOURCES = \
 	test_Regex.cc \
 	tests.cc
 
-test_MemView_SOURCES = test_MemView.cc
-test_MemView_LDADD = libtsutil.la
-
 test_Scalar_SOURCES = test_Scalar.cc Scalar.h
 
 test_tslib_CPPFLAGS = $(AM_CPPFLAGS)\
@@ -257,9 +254,11 @@ test_tslib_LDADD = libtsutil.la
 test_tslib_SOURCES = \
 	unit-tests/unit_test_main.cc \
 	unit-tests/test_BufferWriter.cc \
+	unit-tests/test_ink_inet.cc \
 	unit-tests/test_IpMap.cc \
 	unit-tests/test_layout.cc \
-	unit-tests/test_string_view.cc
+	unit-tests/test_string_view.cc \
+	unit-tests/test_TextView.cc
 
 CompileParseRules_SOURCES = CompileParseRules.cc
 
diff --git a/lib/ts/MemView.cc b/lib/ts/TextView.cc
similarity index 81%
rename from lib/ts/MemView.cc
rename to lib/ts/TextView.cc
index 9699bc1..28d8207 100644
--- a/lib/ts/MemView.cc
+++ b/lib/ts/TextView.cc
@@ -23,19 +23,13 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 */
-#include <ts/MemView.h>
+#include <ts/TextView.h>
 #include <sstream>
 #include <cctype>
 #include <ts/ink_platform.h>
 
-namespace ts
-{
-StringView::StringView(const char *s) : _ptr(s), _size(s ? strlen(s) : 0)
-{
-}
-
 int
-memcmp(MemView const &lhs, MemView const &rhs)
+ts::memcmp(TextView const &lhs, TextView const &rhs)
 {
   int zret;
   size_t n;
@@ -48,7 +42,7 @@ memcmp(MemView const &lhs, MemView const &rhs)
     zret = rhs.size() < lhs.size() ? -1 : 0;
   }
 
-  int r = ::memcmp(lhs.ptr(), rhs.ptr(), n);
+  int r = ::memcmp(lhs.data(), rhs.data(), n);
   if (0 != r) { // If we got a not-equal, override the size based result.
     zret = r;
   }
@@ -57,7 +51,7 @@ memcmp(MemView const &lhs, MemView const &rhs)
 }
 
 int
-strcasecmp(StringView lhs, StringView rhs)
+ts::strcasecmp(TextView lhs, TextView rhs)
 {
   while (lhs && rhs) {
     char l = tolower(*lhs);
@@ -73,7 +67,7 @@ strcasecmp(StringView lhs, StringView rhs)
 }
 
 intmax_t
-svtoi(StringView src, StringView *out, int base)
+ts::svtoi(TextView src, TextView *out, int base)
 {
   static const int8_t convert[256] = {
     /* [can't do this nicely because clang format won't allow exdented comments]
@@ -105,8 +99,8 @@ svtoi(StringView src, StringView *out, int base)
   if (!(1 < base && base <= 36)) {
     return 0;
   }
-  if (src.ltrim(&isspace)) {
-    const char *start = src.ptr();
+  if (src.ltrim_if(&isspace)) {
+    const char *start = src.data();
     int8_t v;
     bool neg = false;
     if ('-' == *src) {
@@ -117,8 +111,8 @@ svtoi(StringView src, StringView *out, int base)
       zret = zret * base + v;
       ++src;
     }
-    if (out && (src.ptr() > (neg ? start + 1 : start))) {
-      out->setView(start, src.ptr());
+    if (out && (src.data() > (neg ? start + 1 : start))) {
+      out->set_view(start, src.data());
     }
 
     if (neg) {
@@ -129,25 +123,12 @@ svtoi(StringView src, StringView *out, int base)
 }
 
 // Do the template instantions.
-template void detail::stream_fill(std::ostream &, std::size_t);
-template std::ostream &StringView::stream_write(std::ostream &, const StringView &) const;
-}
+template std::ostream &ts::TextView::stream_write(std::ostream &, const ts::TextView &) const;
 
 namespace std
 {
 ostream &
-operator<<(ostream &os, const ts::MemView &b)
-{
-  if (os.good()) {
-    ostringstream out;
-    out << b.size() << '@' << hex << b.ptr();
-    os << out.str();
-  }
-  return os;
-}
-
-ostream &
-operator<<(ostream &os, const ts::StringView &b)
+operator<<(ostream &os, const ts::TextView &b)
 {
   if (os.good()) {
     b.stream_write<ostream>(os, b);
diff --git a/lib/ts/MemView.h b/lib/ts/TextView.h
similarity index 50%
rename from lib/ts/MemView.h
rename to lib/ts/TextView.h
index 1e31968..398ce82 100644
--- a/lib/ts/MemView.h
+++ b/lib/ts/TextView.h
@@ -1,32 +1,33 @@
-#if !defined TS_MEM_VIEW
-#define TS_MEM_VIEW
-
 /** @file
 
-   Class for handling "views" of a buffer. Views presume the memory for the buffer is managed
+   Class for handling "views" of text. Views presume the memory for the buffer is managed
    elsewhere and allow efficient access to segments of the buffer without copies. Views are read
    only as the view doesn't own the memory. Along with generic buffer methods are specialized
    methods to support better string parsing, particularly token based parsing.
 
-    @section license License
+   This class is based on @c ts::string_view and is easily and cheaply converted to and from that class.
+
 
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
+   @section license License
 
-    http://www.apache.org/licenses/LICENSE-2.0
+   Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you under the Apache License, Version 2.0 (the
+   "License"); you may not use this file except in compliance
+   with the License.  You may obtain a copy of the License at
 
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
  */
 
+#pragma once
 #include <bitset>
 #include <functional>
 #include <iosfwd>
@@ -38,9 +39,7 @@
 /// Apache Traffic Server commons.
 namespace ts
 {
-class MemView;
-class StringView;
-
+class TextView;
 /// Compare the memory in two views.
 /// Return based on the first different byte. If one argument is a prefix of the other, the prefix
 /// is considered the "smaller" value.
@@ -48,7 +47,7 @@ class StringView;
 /// - -1 if @a lhs byte is less than @a rhs byte.
 /// -  1 if @a lhs byte is greater than @a rhs byte.
 /// -  0 if the views contain identical memory.
-int memcmp(MemView const &lhs, MemView const &rhs);
+int memcmp(TextView const &lhs, TextView const &rhs);
 using ::memcmp; // Make this an overload, not an override.
 /// Compare the strings in two views.
 /// Return based on the first different character. If one argument is a prefix of the other, the prefix
@@ -57,7 +56,7 @@ using ::memcmp; // Make this an overload, not an override.
 /// - -1 if @a lhs char is less than @a rhs char.
 /// -  1 if @a lhs char is greater than @a rhs char.
 /// -  0 if the views contain identical strings.
-int strcmp(StringView const &lhs, StringView const &rhs);
+int strcmp(TextView const &lhs, TextView const &rhs);
 using ::strcmp; // Make this an overload, not an override.
 /// Compare the strings in two views.
 /// Return based on the first different character. If one argument is a prefix of the other, the prefix
@@ -69,10 +68,10 @@ using ::strcmp; // Make this an overload, not an override.
 ///
 /// @internal Why not <const&>? Because the implementation would make copies anyway, might as well save
 /// the cost of passing the pointers.
-int strcasecmp(StringView lhs, StringView rhs);
+int strcasecmp(TextView lhs, TextView rhs);
 using ::strcasecmp; // Make this an overload, not an override.
 
-/** Convert the text in @c StringView @a src to a numeric value.
+/** Convert the text in @c TextView @a src to a numeric value.
 
     If @a parsed is non-null then the part of the string actually parsed is placed there.
     @a base sets the conversion base. This defaults to 10 with two special cases:
@@ -80,91 +79,105 @@ using ::strcasecmp; // Make this an overload, not an override.
     - 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.
 */
-intmax_t svtoi(StringView src, StringView *parsed = nullptr, int base = 10);
+intmax_t svtoi(TextView src, TextView *parsed = nullptr, int base = 10);
 
 /** A read only view of contiguous piece of memory.
 
-    A @c MemView does not own the memory to which it refers, it is simply a view of part of some
+    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. This can avoid copying or allocation by allocating all needed memory at once
-    and then working with it via instances of this class.
+    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.
 
-    MemView is based on an earlier class ConstBuffer and influenced by Boost.string_ref. Neither
-    of these were adequate for how use of @c ConstBuffer evolved and so @c MemView is @c
-    ConstBuffer with some additional stylistic changes based on Boost.string_ref.
+    @c TextView is based on an earlier classes @c ConstBuffer, @c StringView and influenced by @c
+    Boost.string_ref and @c std::string_view. None of these were adequate for how use of @c
+    ConstBuffer evolved with regard to text based manipulations. @c TextView is a super set of @c
+    std::string_view (and therefore our local implementation, @ts::string_view). It is designed to
+    be a drop in replacement.
 
-    This class is closely integrated with @c StringView. These classes have the same underlying
-    implementation and are differentiated only because of the return types and a few string oriented
-    methods.
+    @note To simplify the interface there is no constructor just 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.
  */
-class MemView
+class TextView : public string_view
 {
-  typedef MemView self; ///< Self reference type.
-
-protected:
-  const void *_ptr = nullptr; ///< Pointer to base of memory chunk.
-  size_t _size     = 0;       ///< Size of memory chunk.
+  using self_type  = TextView;    ///< Self reference type.
+  using super_type = string_view; ///< Parent type.
 
 public:
   /// Default constructor (empty buffer).
-  constexpr MemView();
+  constexpr TextView();
 
   /** Construct explicitly with a pointer and size.
    */
-  constexpr MemView(const void *ptr, ///< Pointer to buffer.
-                    size_t n         ///< Size of buffer.
-                    );
+  constexpr TextView(const char *ptr, ///< Pointer to buffer.
+                     size_t n         ///< Size of buffer.
+                     );
 
-  /** Construct from a half open range of two pointers.
-      @note The instance at @start is in the view but the instance at @a end is not.
-  */
-  template <typename T>
-  constexpr MemView(T const *start, ///< First byte in the view.
-                    T const *end    ///< First byte not in the view.
-                    );
+  /** Construct explicitly with a pointer and size.
+      If @a n is negative it is treated as 0.
+      @internal Overload for convience, otherwise get "narrow conversion" errors.
+   */
+  constexpr TextView(const char *ptr, ///< Pointer to buffer.
+                     int n            ///< Size of buffer.
+                     );
 
   /** Construct from a half open range of two pointers.
-      @note The instance at @start is in the view but the instance at @a end is not.
+      @note The byte at @start is in the view but the byte at @a end is not.
   */
-  MemView(void const *start, ///< First byte in the view.
-          void const *end    ///< First byte not in the view.
-          );
+  constexpr TextView(const char *start, ///< First byte in the view.
+                     const char *end    ///< First byte not in the view.
+                     );
+
+  /** Constructor from constant string.
+
+      Construct directly from an array of characters. 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 inapropriate then a constructor with an explicit size should be used.
+
+      @code
+        TextView a("A literal string");
+      @endcode
+   */
+  template <size_t N> constexpr TextView(const char (&s)[N]);
 
   /** Construct from nullptr.
       This implicitly makes the length 0.
   */
-  constexpr MemView(std::nullptr_t);
+  constexpr TextView(std::nullptr_t);
 
-  /// Convert from StringView.
-  constexpr MemView(StringView const &that);
+  /// Construct from a @c ts::string_view.
+  constexpr TextView(super_type const &that);
 
-  /** Equality.
+  /// Construct from @c std::string, referencing the entire string contents.
+  /// @internal Not all compilers make @c std::string methods called @c constexpr
+  constexpr TextView(std::string const &str);
 
-      This is effectively a pointer comparison, buffer contents are not compared.
+  /// Pointer to byte past the last byte in the view.
+  const char *data_end() const;
 
-      @return @c true if @a that refers to the same view as @a this,
-      @c false otherwise.
-   */
-  bool operator==(self const &that) const;
+  /// Assignment.
+  self_type &operator                    =(super_type const &that);
+  template <size_t N> self_type &operator=(const char (&s)[N]);
 
-  /** Inequality.
-      @return @c true if @a that does not refer to the same view as @a this,
-      @c false otherwise.
-   */
-  bool operator!=(self const &that) const;
+  /// Explicitly set the view.
+  self_type &set_view(char const *ptr, size_t n);
 
-  /// Assignment - the view is copied, not the content.
-  self &operator=(self const &that);
+  /// Explicitly set the view to the range [ @a b , @a e )
+  self_type &set_view(char const *b, char const *e);
+
+  /// @return The first byte in the view.
+  char operator*() const;
 
   /** Shift the view to discard the first byte.
       @return @a this.
   */
-  self &operator++();
+  self_type &operator++();
 
   /** Shift the view to discard the leading @a n bytes.
+      Equivalent to @c ts::string_view::remove_prefix
       @return @a this
   */
-  self &operator+=(size_t n);
+  self_type &operator+=(size_t n);
 
   /// Check for empty view.
   /// @return @c true if the view has a zero pointer @b or size.
@@ -174,490 +187,211 @@ public:
   /// @return @c true if the view refers to a non-empty range of bytes.
   explicit operator bool() const;
 
-  /// Check for empty view (no content).
-  /// @see operator bool
-  bool isEmpty() const;
-
-  /// @name Accessors.
-  //@{
-  /// Pointer to the first byte in the view.
-  const void *begin() const;
-  /// Pointer to first byte not in the view.
-  const void *end() const;
-  /// Number of bytes in the view.
-  constexpr size_t size() const;
-  /// Memory pointer.
-  /// @note This is equivalent to @c begin currently but it's probably good to have separation.
-  constexpr const void *ptr() const;
-  /// @return the @a V value at index @a n.
-  template <typename V> V at(ssize_t n) const;
-  /// @return a pointer to the @a V value at index @a n.
-  template <typename V> V const *at_ptr(ssize_t n) const;
-  //@}
-
-  /// Set the view.
-  /// This is faster but equivalent to constructing a new view with the same
-  /// arguments and assigning it.
-  /// @return @c this.
-  self &setView(const void *ptr, ///< Buffer address.
-                size_t n = 0     ///< Buffer size.
-                );
-
-  /// Set the view.
-  /// This is faster but equivalent to constructing a new view with the same
-  /// arguments and assigning it.
-  /// @return @c this.
-  self &setView(const void *start, ///< First valid character.
-                const void *end    ///< First invalid character.
-                );
-
   /// Clear the view (become an empty view).
-  self &clear();
+  self_type &clear();
 
-  /// @return @c true if the byte at @a *p is in the view.
-  bool contains(const void *p) const;
-
-  /** Find a value.
-      The memory is searched as if it were an array of the value type @a T.
+  /// Get the offset of the first character for which @a pred is @c true.
+  template <typename F> size_t find_if(F const &pred) const;
+  /// Get the offset of the last character for which @a pred is @c true.
+  template <typename F> size_t rfind_if(F const &pred) const;
 
-      @return A pointer to the first occurrence of @a v in @a this
-      or @c nullptr if @a v is not found.
+  /** Remove bytes that match @a c from the start of the view.
   */
-  template <typename V> const V *find(V v) const;
-
-  /** Find a value.
-      The memory is searched as if it were an array of the value type @a V.
+  self_type &ltrim(char c);
 
-      @return A pointer to the first value for which @a pred is @c true otherwise
-      @c nullptr.
+  /** Remove bytes from the start of the view that are in @a delimiters.
   */
-  template <typename V> const V *find(std::function<bool(V)> const &pred);
-
-  /** Get the initial segment of the view before @a p.
-
-      The byte at @a p is not included. If @a p is not in the view an empty view
-      is returned.
+  self_type &ltrim(super_type const &delimiters);
 
-      @return A buffer that contains all data before @a p.
+  /** 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 prefix(const void *p) const;
-
-  /** Split the view at @a p.
-
-      The view is split in to two parts at @a p and the prefix is returned. The view is updated to
-     contain the bytes not returned in the prefix. The prefix will not contain @a p.
-
-      @note If @a *p refers to a byte that is not in @a this then @a this is not changed and an empty
-      buffer is returned. Therefore this method can be safely called with the return value of
-      calling @c find.
-
-      @return A buffer containing data up to but not including @a p.
+  self_type &ltrim(const char *delimiters);
 
-      @see extractPrefix
+  /** 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
   */
-  self splitPrefix(const void *p);
-
-  /** Extract a prefix delimited by @a p.
-
-      A prefix of @a this is removed from the view and returned. If @a p is not in the view then the
-      entire view is extracted and returned.
-
-      If @a p points at a byte in the view this is identical to @c splitPrefix.  If not then the
-      entire view in @a this will be returned and @a this will become an empty view.
-
-      @return The prefix bounded at @a p or the entire view if @a p is not a byte in the view.
+  template <typename F> self_type &ltrim_if(F const &pred);
 
-      @see splitPrefix
+  /** Remove bytes that match @a c from the end of the view.
   */
-  self extractPrefix(const void *p);
+  self_type &rtrim(char c);
 
-  /** Get the trailing segment of the view after @a p.
+  /** Remove bytes from the end of the view that are in @a delimiters.
+  */
+  self_type &rtrim(super_type const &delimiters);
 
-      The byte at @a p is not included. If @a p is not in the view an empty view is returned.
+  /** Remove bytes from the 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 &rtrim(const char *delimiters);
 
-      @return A buffer that contains all data after @a p.
+  /** 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
   */
-  self suffix(const void *p) const;
+  template <typename F> self_type &rtrim_if(F const &pred);
 
-  /** Split the view at @a p.
+  /** Remove bytes that match @a c from the end of the view.
+  */
+  self_type &trim(char c);
 
-      The view is split in to two parts and the suffix is returned. The view is updated to contain
-      the bytes not returned in the suffix. The suffix will not contain @a p.
+  /** Remove bytes from the start and end of the view that are in @a delimiters.
+  */
+  self_type &trim(super_type const &delimiters);
 
-      @note If @a p does not refer to a byte in the view, an empty view is returned and @a this is
-      unchanged.
+  /** 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);
 
-      @return @a this.
+  /** 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
   */
-  self splitSuffix(const void *p);
-};
+  template <typename F> self_type &trim_if(F const &pred);
 
-/** A read only view of contiguous piece of memory.
+  /** Get the initial segment of the view before @a p.
 
-    A @c StringView 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 StringView supports this style.
+      The byte at @a p is not included. If @a p is not in the view an empty view
+      is returned.
 
-    MemView is based on an earlier class ConstBuffer and influenced by Boost.string_ref. Neither
-    of these were adequate for how use of @c ConstBuffer evolved and so @c MemView is @c
-    ConstBuffer with some additional stylistic changes based on Boost.string_ref.
+      @return A buffer that contains all data before @a p.
+  */
+  //  self_type prefix(const char *p) const;
 
-    In particular @c MemView is designed both to support passing via API (to replace the need to
-    pass two parameters for one real argument) and to aid in parsing input without copying.
+  /// Get the prefix of size @a n.
+  self_type prefix(size_t n) const;
+  /// Convenience overload to avoid ambiguity for literal numbers.
+  self_type prefix(int n) const;
+  /// Get the prefix delimited by the character 'c'.
+  self_type prefix(char c) const;
+  /// Get the prefix delimited by any character in @a delimiters .
+  self_type prefix(super_type const &delimiters) const;
+  /// Get the prefix delimited by the first character for which @a pred is @c true.
+  template <typename F> self_type prefix_if(F const &pred) const;
 
- */
-class StringView
-{
-  typedef StringView self; ///< Self reference type.
+  /** Split a prefix from the view on the character at offset @a n.
 
-protected:
-  const char *_ptr = nullptr; ///< Pointer to base of memory chunk.
-  size_t _size     = 0;       ///< Size of memory chunk.
+      The view is split in to two parts and the byte at offset @a n is discarded. @a this retains
+      all data @b after offset @a n (equivalent to <tt>TextView::substr(n+1)</tt>). A new view
+      containing the initial bytes up to but not including the byte at offset @a n is returned,
+      (equivalent to <tt>TextView(0, n)</tt>).
 
-  struct literal_t {
-  };
-  struct array_t {
-  };
+      This is convenient when tokenizing.
 
-public:
-  /// Default constructor (empty buffer).
-  constexpr StringView();
+      @note If @a n is larger than the size of the view no change is made and an empty buffer is
+      returned. Therefore this method can be safely called with the return value of calling @c find.
 
-  /** Construct explicitly with a pointer and size.
-   */
-  constexpr StringView(const char *ptr, ///< Pointer to buffer.
-                       size_t n         ///< Size of buffer.
-                       );
+      @code
+        void f(TextView& text) {
+          TextView token = text.get_prefix_at(text.find(delimiter));
+          if (token) { // ... process token }
+      @endcode
 
-  /** Construct explicitly with a pointer and size.
-      If @a n is negative it is treated as 0.
-      @internal Overload for convience, otherwise get "narrow conversion" errors.
-   */
-  constexpr StringView(const char *ptr, ///< Pointer to buffer.
-                       int n            ///< Size of buffer.
-                       );
+      @return A buffer containing data up to but not including the byte at offset @a n.
 
-  /** Construct from a half open range of two pointers.
-      @note The byte at @start is in the view but the byte at @a end is not.
+      @see take_prefix_at
   */
-  constexpr StringView(const char *start, ///< First byte in the view.
-                       const char *end    ///< First byte not in the view.
-                       );
+  self_type split_prefix_at(size_t n);
 
-  /** Construct from standard string view.
-   */
-  constexpr StringView(ts::string_view const &that);
+  /// Convenience overload for literal numbers.
+  self_type split_prefix_at(int n);
+  /// Convenience overload, split on character.
+  self_type split_prefix_at(char c);
+  /// Convenience overload, split on delimiter set.
+  self_type split_prefix_at(super_type const &delimiters);
+  /// Convenience overload, split on delimiter set.
+  //  self_type split_prefix_at(const char *delimiters);
+  /// Convenience overload, split on predicate.
+  template <typename F> self_type split_prefix_if(F const &pred);
 
-  /** Constructor from literal string.
+  /** Always take the prefix of the view on the character at offset @a n.
 
-      Construct directly from a literal string. This avoids a call to :c strlen and therefore is
-      faster and can be @c constexpr. The terminal nul character is excluded. Internal nul
-      characters are included.
+      A prefix of @a this is removed from the view and returned. If @a n is larger than the view
+      size then the entire view is removed and returned, leaving an empty view.
 
       @code
-        StringView a("literal", StringView::literal);
+      TextView text;
+      while (text) {
+        TextView token = text.take_prefix_at(text.find(delimiter));
+        // .. process token which will always be non-empty because text was not empty.
+      }
       @endcode
-   */
-  template <size_t N> constexpr StringView(const char (&s)[N], literal_t);
 
-  /** Constructor from character array.
+      @return The prefix bounded at offset @a n or the entire view if @a n is more than the view
+      size.
 
-      Construct directly from an array of characters. All elements of the array are
-      included in the view.
+      @see split_prefix_at
+  */
+  self_type take_prefix_at(size_t n);
 
-      @code
-        char buff[SIZE];
-        StringView a(buff, StringView::array);
-      @endcode
+  /// Convenience overload, extract on delimiter set.
+  self_type take_prefix_at(char c);
+  /// Convenience overload, extract on delimiter set.
+  self_type take_prefix_at(super_type const &delimiters);
+  /// Convenience overload, extract on predicate.
+  template <typename F> self_type take_prefix_if(F const &pred);
 
-      @note If this is used on a literal string, the terminal nul character is included.
-   */
-  template <size_t N> constexpr StringView(const char (&s)[N], array_t);
+  /** Get the last @a n characters of the view.
 
-  /** Construct from nullptr.
-      This implicitly makes the length 0.
+      @return A buffer that contains @a n characters at the end of the view.
   */
-  constexpr StringView(std::nullptr_t);
+  self_type suffix(size_t n) const;
 
-  /// Construct from @c MemView to reference the same view.
-  /// @internal Can't be @c constexpr because @c static_cast of @c <void*> is not permitted.
-  StringView(MemView const &that);
+  /// Convenience overload to avoid ambiguity for literal numbers.
+  self_type suffix(int n) const;
+  /// Convenience overload for character.
+  self_type suffix(char c) const;
+  /// Convenience overload for delimiter set.
+  self_type suffix(super_type const &delimiters) const;
+  /// Convenience overload for delimiter set.
+  self_type suffix(const char *delimiters) const;
+  /// Get the prefix delimited by the first character for which @a pred is @c true.
+  template <typename F> self_type suffix_if(F const &pred) const;
 
-  /** Construct from null terminated string.
-      @note The terminating null is not included. @c strlen is used to determine the length.
-  */
-  explicit StringView(const char *s);
+  /** Split the view on the character at offset @a n.
 
-  /// Construct from @c std::string, referencing the entire string contents.
-  /// @internal Not all compilers make @c std::string methods called @c constexpr
-  StringView(std::string const &str);
+      The view is split in to two parts and the byte at offset @a n is discarded. @a this retains
+      all data @b before offset @a n (equivalent to <tt>TextView::prefix(this->size()-n-1)</tt>). A
+      new view containing the trailing bytes after offset @a n is returned, (equivalent to
+      <tt>TextView::suffix(n))</tt>).
 
-  /// Convert to standard string view.
-  operator ts::string_view() const;
+      @note If @a p does not refer to a byte in the view, an empty view is returned and @a this is
+      unchanged.
 
-  /** Equality.
+      @return @a this.
+  */
+  self_type split_suffix_at(size_t n);
 
-      This is effectively a pointer comparison, buffer contents are not compared.
+  /// Convenience overload for literal integers.
+  self_type split_suffix_at(int n);
+  /// Convenience overload for character.
+  self_type split_suffix_at(char c);
+  /// Convenience overload for delimiter set.
+  self_type split_suffix_at(super_type const &delimiters);
+  /// Split the view on the last character for which @a pred is @c true.
+  template <typename F> self_type split_suffix_if(F const &pred);
 
-      @return @c true if @a that refers to the same view as @a this,
-      @c false otherwise.
-   */
-  bool operator==(self const &that) const;
-
-  /** Inequality.
-      @return @c true if @a that does not refer to the same view as @a this,
-      @c false otherwise.
-   */
-  bool operator!=(self const &that) const;
-
-  /** Prefix check.
-      @return @c true if @a this is a prefix of @a that.
-  */
-  bool isPrefixOf(self const &that) const;
+  /** Prefix check.
+      @return @c true if @a this is a prefix of @a that.
+  */
+  bool isPrefixOf(super_type const &that) const;
 
   /** Case ignoring prefix check.
       @return @c true if @a this is a prefix of @a that, ignoring case.
   */
-  bool isNoCasePrefixOf(self const &that) const;
-
-  /// Assignment - the view is copied, not the content.
-  self &operator=(self const &that);
-
-  /// @return The first byte in the view.
-  char operator*() const;
-
-  /// @return the byte at offset @a n.
-  char operator[](size_t n) const;
-
-  /// @return the byte at offset @a n.
-  char operator[](int n) const;
-
-  /** Shift the view to discard the first byte.
-      @return @a this.
-  */
-  self &operator++();
-
-  /** Shift the view to discard the leading @a n bytes.
-      @return @a this
-  */
-  self &operator+=(size_t n);
-
-  /// Check for empty view.
-  /// @return @c true if the view has a zero pointer @b or size.
-  bool operator!() const;
-
-  /// Check for non-empty view.
-  /// @return @c true if the view refers to a non-empty range of bytes.
-  explicit operator bool() const;
-
-  /// Check for empty view (no content).
-  /// @see operator bool
-  bool isEmpty() const;
-
-  /// @name Accessors.
-  //@{
-  /// Pointer to the first byte in the view.
-  const char *begin() const;
-  /// Pointer to first byte not in the view.
-  const char *end() const;
-  /// Number of bytes in the view.
-  constexpr size_t size() const;
-  /// Memory pointer.
-  /// @note This is equivalent to @c begin currently but it's probably good to have separation.
-  constexpr const char *ptr() const;
-  //@}
-
-  /// Set the view.
-  /// This is faster but equivalent to constructing a new view with the same
-  /// arguments and assigning it.
-  /// @return @c this.
-  self &setView(const char *ptr, ///< Buffer address.
-                size_t n = 0     ///< Buffer size.
-                );
-
-  /// Set the view.
-  /// This is faster but equivalent to constructing a new view with the same
-  /// arguments and assigning it.
-  /// @return @c this.
-  self &setView(const char *start, ///< First valid character.
-                const char *end    ///< First invalid character.
-                );
-
-  /// Clear the view (become an empty view).
-  self &clear();
-
-  /// @return @c true if the byte at @a *p is in the view.
-  bool contains(const char *p) const;
-
-  /** Find a byte.
-      @return A pointer to the first occurrence of @a c in @a this
-      or @c nullptr if @a c is not found.
-  */
-  const char *find(char c) const;
-
-  /** Find a byte.
-      @return A pointer to the first occurence of any of @a delimiters in @a
-      this or @c nullptr if not found.
-  */
-  const char *find(self delimiters) const;
-
-  /** Find a byte.
-      @return A pointer to the first byte for which @a pred is @c true otherwise
-      @c nullptr.
-  */
-  const char *find(std::function<bool(char)> const &pred) const;
-
-  /** Remove bytes that match @a c from the start of the view.
-  */
-  self &ltrim(char c);
-  /** Remove bytes from the start of the view that are in @a delimiters.
-  */
-  self &ltrim(self delimiters);
-  /** Remove bytes from the start of the view for which @a pred is @c true.
-  */
-  self &ltrim(std::function<bool(char)> const &pred);
-
-  /** Remove bytes that match @a c from the end of the view.
-  */
-  self &rtrim(char c);
-  /** Remove bytes from the end of the view that are in @a delimiters.
-  */
-  self &rtrim(self delimiters);
-  /** Remove bytes from the start and end of the view for which @a pred is @c true.
-  */
-  self &rtrim(std::function<bool(char)> const &pred);
-
-  /** Remove bytes that match @a c from the end of the view.
-  */
-  self &trim(char c);
-  /** Remove bytes from the start and end of the view that are in @a delimiters.
-  */
-  self &trim(self delimiters);
-  /** Remove bytes from the start and end of the view for which @a pred is @c true.
-  */
-  self &trim(std::function<bool(char)> const &pred);
-
-  /** Get the initial segment of the view before @a p.
-
-      The byte at @a p is not included. If @a p is not in the view an empty view
-      is returned.
-
-      @return A buffer that contains all data before @a p.
-  */
-  self prefix(const char *p) const;
-
-  /// Convenience overload for character.
-  self prefix(char c);
-  /// Convenience overload, split on delimiter set.
-  self prefix(self delimiters) const;
-  /// Convenience overload, split on predicate.
-  self prefix(std::function<bool(char)> const &pred) const;
-
-  /** Split the view on the character at @a p.
-
-      The view is split in to two parts and the byte at @a p is discarded. @a this retains all data
-      @b after @a p (equivalent to <tt>MemView(p+1, this->end()</tt>). A new view containing the
-      initial bytes up to but not including @a p is returned, (equivalent to
-      <tt>MemView(this->begin(), p)</tt>).
-
-      This is convenient when tokenizing and @a p points at a delimiter.
-
-      @note If @a *p refers toa byte that is not in @a this then @a this is not changed and an empty
-      buffer is returned. Therefore this method can be safely called with the return value of
-      calling @c find.
-
-      @code
-        void f(MemView& text) {
-          MemView token = text.splitPrefix(text.find(delimiter));
-          if (token) { // ... process token }
-      @endcode
-
-      @return A buffer containing data up to but not including @a p.
-
-      @see extractPrefix
-  */
-  self splitPrefix(const char *p);
-
-  /// Convenience overload, split on character.
-  self splitPrefix(char c);
-  /// Convenience overload, split on delimiter set.
-  self splitPrefix(self delimiters);
-  /// Convenience overload, split on predicate.
-  self splitPrefix(std::function<bool(char)> const &pred);
-
-  /** Extract a prefix delimited by @a p.
-
-      A prefix of @a this is removed from the view and returned. If @a p is not in the view then the
-      entire view is extracted and returned.
-
-      If @a p points at a byte in the view this is identical to @c splitPrefix.  If not then the
-      entire view in @a this will be returned and @a this will become an empty view. This is easier
-      to use when repeated extracting tokens. The source view will become empty after extracting the
-      last token.
-
-      @code
-      MemView text;
-      while (text) {
-        MemView token = text.extractPrefix(text.find(delimiter));
-        // .. process token which will always be non-empty because text was not empty.
-      }
-      @endcode
-
-      @return The prefix bounded at @a p or the entire view if @a p is not a byte in the view.
-
-      @see splitPrefix
-  */
-  self extractPrefix(const char *p);
-
-  /// Convenience overload, extract on delimiter set.
-  self extractPrefix(char c);
-  /// Convenience overload, extract on delimiter set.
-  self extractPrefix(self delimiters);
-  /// Convenience overload, extract on predicate.
-  self extractPrefix(std::function<bool(char)> const &pred);
-
-  /** Get the trailing segment of the view after @a p.
-
-      The byte at @a p is not included. If @a p is not in the view an empty view is returned.
-
-      @return A buffer that contains all data after @a p.
-  */
-  self suffix(const char *p) const;
-
-  /// Convenience overload for character.
-  self suffix(char c);
-  /// Convenience overload for delimiter set.
-  self suffix(self delimiters);
-  /// Convenience overload for predicate.
-  self suffix(std::function<bool(char)> const &pred);
-
-  /** Split the view on the character at @a p.
-
-      The view is split in to two parts and the byte at @a p is discarded. @a this retains all data
-      @b before @a p (equivalent to <tt>MemView(this->begin(), p)</tt>). A new view containing
-      the trailing bytes after @a p is returned, (equivalent to <tt>MemView(p+1,
-      this->end())</tt>).
-
-      @note If @a p does not refer to a byte in the view, an empty view is returned and @a this is
-      unchanged.
-
-      @return @a this.
-  */
-  self splitSuffix(const char *p);
-
-  /// Convenience overload for character.
-  self splitSuffix(char c);
-  /// Convenience overload for delimiter set.
-  self splitSuffix(self delimiters);
-  /// Convenience overload for predicate.
-  self splitSuffix(std::function<bool(char)> const &pred);
+  bool isNoCasePrefixOf(super_type const &that) const;
 
   // Functors for using this class in STL containers.
   /// Ordering functor, lexicographic comparison.
   struct LessThan {
     bool
-    operator()(MemView const &lhs, MemView const &rhs)
+    operator()(self_type const &lhs, self_type const &rhs)
     {
       return -1 == strcmp(lhs, rhs);
     }
@@ -665,7 +399,7 @@ public:
   /// Ordering functor, case ignoring lexicographic comparison.
   struct LessThanNoCase {
     bool
-    operator()(MemView const &lhs, MemView const &rhs)
+    operator()(self_type const &lhs, self_type const &rhs)
     {
       return -1 == strcasecmp(lhs, rhs);
     }
@@ -676,309 +410,758 @@ public:
   /// @return The stream @a os.
   /// @internal Needed because @c std::ostream::write must be used and
   /// so alignment / fill have to be explicitly handled.
-  template <typename Stream> Stream &stream_write(Stream &os, const StringView &b) const;
-
-  static constexpr literal_t literal{};
-  static constexpr array_t array{};
+  template <typename Stream> Stream &stream_write(Stream &os, const TextView &b) const;
 
 protected:
+  /// Faster find on a delimiter set, taking advantage of supporting only ASCII.
+  size_t search(super_type const &delimiters) const;
+  /// Faster reverse find on a delimiter set, taking advantage of supporting only ASCII.
+  size_t rsearch(super_type const &delimiters) const;
+
   /// Initialize a bit mask to mark which characters are in this view.
-  void initDelimiterSet(std::bitset<256> &set);
+  static void init_delimiter_set(super_type const &delimiters, std::bitset<256> &set);
 };
 // ----------------------------------------------------------
 // Inline implementations.
 
-inline constexpr MemView::MemView()
+// === TextView Implementation ===
+inline constexpr TextView::TextView()
 {
 }
-inline constexpr MemView::MemView(void const *ptr, size_t n) : _ptr(ptr), _size(n)
+inline constexpr TextView::TextView(const char *ptr, size_t n) : super_type(ptr, n)
 {
 }
-template <typename T> constexpr MemView::MemView(const T *start, const T *end) : _ptr(start), _size((end - start) * sizeof(T))
+inline constexpr TextView::TextView(const char *ptr, int n) : super_type(ptr, n < 0 ? 0 : n)
 {
 }
-// <void*> is magic, handle that specially.
-// No constexpr because the spec specifically forbids casting from <void*> to a typed pointer.
-inline MemView::MemView(void const *start, void const *end)
-  : _ptr(start), _size(static_cast<const char *>(end) - static_cast<char const *>(start))
+inline constexpr TextView::TextView(const char *start, const char *end) : super_type(start, end - start)
 {
 }
-inline constexpr MemView::MemView(std::nullptr_t) : _ptr(nullptr), _size(0)
+inline constexpr TextView::TextView(std::nullptr_t) : super_type(nullptr, 0)
 {
 }
-inline constexpr MemView::MemView(StringView const &that) : _ptr(that.ptr()), _size(that.size())
+inline constexpr TextView::TextView(std::string const &str) : super_type(str)
+{
+}
+inline constexpr TextView::TextView(super_type const &that) : super_type(that)
+{
+}
+template <size_t N> constexpr TextView::TextView(const char (&s)[N]) : super_type(s, s[N - 1] ? N : N - 1)
 {
 }
 
-inline MemView &
-MemView::setView(const void *ptr, size_t n)
+inline void
+TextView::init_delimiter_set(super_type const &delimiters, std::bitset<256> &set)
 {
-  _ptr  = ptr;
-  _size = n;
-  return *this;
+  set.reset();
+  for (char c : delimiters)
+    set[static_cast<uint8_t>(c)] = true;
 }
 
-inline MemView &
-MemView::setView(const void *ptr, const void *limit)
+inline const char *
+TextView::data_end() const
 {
-  _ptr  = ptr;
-  _size = static_cast<const char *>(limit) - static_cast<const char *>(ptr);
-  return *this;
+  return this->data() + this->size();
 }
 
-inline MemView &
-MemView::clear()
+inline TextView &
+TextView::clear()
 {
-  _ptr  = 0;
-  _size = 0;
+  new (this) self_type();
   return *this;
 }
 
-inline bool
-MemView::operator==(self const &that) const
+inline char TextView::operator*() const
 {
-  return _size == that._size && _ptr == that._ptr;
+  return *(this->data());
 }
 
-inline bool
-MemView::operator!=(self const &that) const
+inline bool TextView::operator!() const
 {
-  return !(*this == that);
+  return !this->empty();
 }
 
-inline bool MemView::operator!() const
+inline TextView::operator bool() const
 {
-  return !(_ptr && _size);
+  return !this->empty();
 }
 
-inline MemView::operator bool() const
+inline TextView &TextView::operator++()
 {
-  return _ptr && _size;
+  this->remove_prefix(1);
+  return *this;
 }
 
-inline bool
-MemView::isEmpty() const
+inline TextView &
+TextView::operator+=(size_t n)
 {
-  return !(_ptr && _size);
+  this->remove_prefix(n);
+  return *this;
 }
 
-inline MemView &MemView::operator++()
+template <size_t N>
+inline TextView &
+TextView::operator=(const char (&s)[N])
 {
-  _ptr = static_cast<const char *>(_ptr) + 1;
-  --_size;
-  return *this;
+  return *this = self_type{s, s[N - 1] ? N : N - 1};
 }
 
-inline MemView &
-MemView::operator+=(size_t n)
+inline TextView &
+TextView::operator=(super_type const &that)
 {
-  if (n > _size) {
-    _ptr  = nullptr;
-    _size = 0;
-  } else {
-    _ptr = static_cast<const char *>(_ptr) + n;
-    _size -= n;
-  }
+  this->super_type::operator=(that);
   return *this;
 }
 
-inline const void *
-MemView::begin() const
+inline TextView &
+TextView::set_view(char const *ptr, size_t n)
 {
-  return _ptr;
+  *this = super_type(ptr, n);
+  return *this;
 }
-inline constexpr const void *
-MemView::ptr() const
+
+inline TextView &
+TextView::set_view(char const *b, char const *e)
 {
-  return _ptr;
+  *this = super_type(b, e - b);
+  return *this;
 }
 
-inline const void *
-MemView::end() const
+inline size_t
+TextView::search(super_type const &delimiters) const
 {
-  return static_cast<const char *>(_ptr) + _size;
+  std::bitset<256> valid;
+  this->init_delimiter_set(delimiters, valid);
+
+  for (const char *spot = this->data(), *limit = this->data_end(); spot < limit; ++spot)
+    if (valid[static_cast<uint8_t>(*spot)])
+      return spot - this->data();
+  return npos;
 }
 
-inline constexpr size_t
-MemView::size() const
+inline size_t
+TextView::rsearch(super_type const &delimiters) const
 {
-  return _size;
+  std::bitset<256> valid;
+  this->init_delimiter_set(delimiters, valid);
+
+  for (const char *spot = this->data_end(), *limit = this->data(); spot > limit;)
+    if (valid[static_cast<uint8_t>(*--spot)])
+      return spot - this->data();
+  return npos;
 }
 
-inline MemView &
-MemView::operator=(MemView const &that)
+inline TextView
+TextView::prefix(size_t n) const
 {
-  _ptr  = that._ptr;
-  _size = that._size;
-  return *this;
+  return self_type(this->data(), std::min(n, this->size()));
 }
 
-inline bool
-MemView::contains(const void *p) const
+inline TextView
+TextView::prefix(int n) const
 {
-  return _ptr <= this->begin() && p < this->end();
+  return this->prefix(static_cast<size_t>(n));
 }
 
-inline MemView
-MemView::prefix(const void *p) const
+inline TextView
+TextView::prefix(char c) const
 {
-  self zret;
-  if (this->contains(p))
-    zret.setView(_ptr, p);
-  return zret;
+  return this->prefix(this->find(c));
 }
 
-inline MemView
-MemView::splitPrefix(const void *p)
+inline TextView
+TextView::prefix(super_type const &delimiters) const
 {
-  self zret; // default to empty return.
-  if (this->contains(p)) {
-    zret.setView(_ptr, p);
-    this->setView(p, this->end());
+  return this->prefix(this->search(delimiters));
+}
+
+template <typename F>
+inline TextView
+TextView::prefix_if(F const &pred) const
+{
+  return this->prefix(this->find(pred));
+}
+
+inline TextView
+TextView::split_prefix_at(size_t n)
+{
+  self_type zret; // default to empty return.
+  if (n < this->size()) {
+    zret = this->prefix(n);
+    this->remove_prefix(n + 1);
   }
   return zret;
 }
 
-inline MemView
-MemView::extractPrefix(const void *p)
+inline TextView
+TextView::split_prefix_at(int n)
 {
-  self zret{this->splitPrefix(p)};
+  return this->split_prefix_at(static_cast<size_t>(n));
+}
 
-  // For extraction if zret is empty, use up all of @a this
-  if (!zret) {
-    zret = *this;
-    this->clear();
-  }
+inline TextView
+TextView::split_prefix_at(char c)
+{
+  return this->split_prefix_at(this->find(c));
+}
 
-  return zret;
+inline TextView
+TextView::split_prefix_at(super_type const &delimiters)
+{
+  return this->split_prefix_at(this->search(delimiters));
 }
 
-inline MemView
-MemView::suffix(const void *p) const
+template <typename F>
+inline TextView
+TextView::split_prefix_if(F const &pred)
 {
-  self zret;
-  if (this->contains(p))
-    zret.setView(p, this->end());
+  return this->split_prefix_at(this->find_if(pred));
+}
+
+inline TextView
+TextView::take_prefix_at(size_t n)
+{
+  n              = std::min(n, this->size());
+  self_type zret = this->prefix(n);
+  this->remove_prefix(n + 1);
   return zret;
 }
 
-inline MemView
-MemView::splitSuffix(const void *p)
+inline TextView
+TextView::take_prefix_at(char c)
 {
-  self zret;
-  if (this->contains(p)) {
-    zret.setView(p, this->end());
-    this->setView(_ptr, p);
+  return this->take_prefix_at(this->find(c));
+}
+
+inline TextView
+TextView::take_prefix_at(super_type const &delimiters)
+{
+  return this->take_prefix_at(this->search(delimiters));
+}
+
+template <typename F>
+inline TextView
+TextView::take_prefix_if(F const &pred)
+{
+  return this->take_prefix_at(this->find_if(pred));
+}
+
+inline TextView
+TextView::suffix(size_t n) const
+{
+  n = std::min(n, this->size());
+  return self_type(this->data_end() - n, n);
+}
+
+inline TextView
+TextView::suffix(int n) const
+{
+  return this->suffix(static_cast<size_t>(n));
+}
+
+inline TextView
+TextView::suffix(char c) const
+{
+  return this->suffix((this->size() - std::min(this->size(), this->rfind(c))) - 1);
+}
+
+inline TextView
+TextView::suffix(super_type const &delimiters) const
+{
+  return this->suffix((this->size() - std::min(this->size(), this->rsearch(delimiters))) - 1);
+}
+
+template <typename F>
+inline TextView
+TextView::suffix_if(F const &pred) const
+{
+  return this->suffix((this->size() - std::min(this->size(), this->rfind_if(pred))) - 1);
+}
+
+inline TextView
+TextView::split_suffix_at(size_t n)
+{
+  self_type zret;
+  if (n < this->size()) {
+    zret = this->suffix(n);
+    this->remove_suffix(n + 1);
   }
   return zret;
 }
 
-template <typename V>
-inline V
-MemView::at(ssize_t n) const
+inline TextView
+TextView::split_suffix_at(int n)
 {
-  return static_cast<V const *>(_ptr)[n];
+  return this->split_suffix_at(static_cast<size_t>(n));
 }
 
-template <typename V>
-inline V const *
-MemView::at_ptr(ssize_t n) const
+inline TextView
+TextView::split_suffix_at(char c)
 {
-  return static_cast<V const *>(_ptr) + n;
+  return this->split_suffix_at(this->find(c));
 }
 
-template <typename V>
-inline const V *
-MemView::find(V v) const
+inline TextView
+TextView::split_suffix_at(super_type const &delimiters)
 {
-  for (const V *spot = static_cast<const V *>(_ptr), limit = spot + (_size / sizeof(V)); spot < limit; ++spot)
-    if (v == *spot)
-      return spot;
-  return nullptr;
+  return this->split_suffix_at(this->rsearch(delimiters));
+}
+
+template <typename F>
+inline TextView
+TextView::split_suffix_if(F const &pred)
+{
+  return this->split_suffix_at(this->rfind_if(pred));
+}
+
+template <typename F>
+inline size_t
+TextView::find_if(F const &pred) const
+{
+  for (const char *spot = this->data(), *limit = this->data_end(); spot < limit; ++spot)
+    if (pred(*spot))
+      return spot - this->data();
+  return npos;
+}
+
+template <typename F>
+inline size_t
+TextView::rfind_if(F const &pred) const
+{
+  for (const char *spot = this->data_end(), *limit = this->data(); spot > limit;)
+    if (pred(*--spot))
+      return spot - this->data();
+  return npos;
+}
+
+inline TextView &
+TextView::ltrim(char c)
+{
+  this->remove_prefix(this->find_first_not_of(c));
+  return *this;
+}
+
+inline TextView &
+TextView::rtrim(char c)
+{
+  auto n = this->find_last_not_of(c);
+  this->remove_suffix(this->size() - (n == npos ? 0 : n + 1));
+  return *this;
+}
+
+inline TextView &
+TextView::trim(char c)
+{
+  return this->ltrim(c).rtrim(c);
+}
+
+inline TextView &
+TextView::ltrim(super_type const &delimiters)
+{
+  std::bitset<256> valid;
+  this->init_delimiter_set(delimiters, valid);
+  const char *spot;
+  const char *limit;
+
+  for (spot = this->data(), limit = this->data_end(); spot < limit && valid[static_cast<uint8_t>(*spot)]; ++spot)
+    ;
+  this->remove_prefix(spot - this->data());
+
+  return *this;
+}
+
+inline TextView &
+TextView::ltrim(const char *delimiters)
+{
+  return this->ltrim(ts::string_view(delimiters));
+}
+
+inline TextView &
+TextView::rtrim(super_type const &delimiters)
+{
+  std::bitset<256> valid;
+  this->init_delimiter_set(delimiters, valid);
+  const char *spot  = this->data_end();
+  const char *limit = this->data();
+
+  while (limit < spot && valid[static_cast<uint8_t>(*--spot)])
+    ;
+
+  this->remove_suffix(this->data_end() - (spot + 1));
+  return *this;
+}
+
+inline TextView &
+TextView::trim(super_type const &delimiters)
+{
+  std::bitset<256> valid;
+  this->init_delimiter_set(delimiters, valid);
+  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 && valid[static_cast<uint8_t>(*spot)]; ++spot)
+    ;
+  this->remove_prefix(spot - this->data());
+
+  for (spot = this->data_end(), limit = this->data(); limit < spot && valid[static_cast<uint8_t>(*--spot)];)
+    ;
+  this->remove_suffix(this->data_end() - (spot + 1));
+
+  return *this;
+}
+
+inline TextView &
+TextView::trim(const char *delimiters)
+{
+  return this->trim(ts::string_view(delimiters));
+}
+
+template <typename F>
+inline TextView &
+TextView::ltrim_if(F const &pred)
+{
+  const char *spot;
+  const char *limit;
+  for (spot = this->data(), limit = this->data_end(); spot < limit && pred(*spot); ++spot)
+    ;
+  this->remove_prefix(spot - this->data());
+  return *this;
+}
+
+template <typename F>
+inline TextView &
+TextView::rtrim_if(F const &pred)
+{
+  const char *spot;
+  const char *limit;
+  for (spot = this->data_end(), limit = this->data(); limit < spot && pred(*--spot);)
+    ;
+  this->remove_suffix(this->data_end() - (spot + 1));
+  return *this;
+}
+
+template <typename F>
+inline TextView &
+TextView::trim_if(F const &pred)
+{
+  return this->ltrim_if(pred).rtrim_if(pred);
+}
+
+inline bool
+TextView::isPrefixOf(super_type const &that) const
+{
+  return this->size() <= that.size() && 0 == memcmp(this->data(), that.data(), this->size());
 }
 
-// Specialize char for performance.
-template <>
-inline const char *
-MemView::find(char v) const
-{
-  return static_cast<const char *>(memchr(_ptr, v, _size));
-}
+inline bool
+TextView::isNoCasePrefixOf(super_type const &that) const
+{
+  return this->size() <= that.size() && 0 == strncasecmp(this->data(), that.data(), this->size());
+}
+
+inline int
+strcmp(TextView const &lhs, TextView const &rhs)
+{
+  return ts::memcmp(lhs, rhs);
+}
+
+template <typename Stream>
+Stream &
+TextView::stream_write(Stream &os, const TextView &b) const
+{
+  // Local function, avoids extra template work.
+  static const auto stream_fill = [](Stream &os, size_t n) -> Stream & {
+    static constexpr size_t pad_size = 8;
+    typename Stream::char_type padding[pad_size];
+
+    std::fill_n(padding, pad_size, os.fill());
+    for (; n >= pad_size && os.good(); n -= pad_size)
+      os.write(padding, pad_size);
+    if (n > 0 && os.good())
+      os.write(padding, n);
+    return os;
+  };
+
+  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;
+}
+
+// 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;
+
+} // end namespace ApacheTrafficServer
+
+namespace std
+{
+ostream &operator<<(ostream &os, const ts::TextView &b);
+ostream &operator<<(ostream &os, const ts::TextView &b);
+}
+
+#if 0
+// Preserved for now, I may want this back later.
+/** A read only view of contiguous piece of memory.
+
+    A @c MemView 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. This can avoid copying or allocation by allocating all needed memory at once
+    and then working with it via instances of this class.
+
+    MemView is based on an earlier class ConstBuffer and influenced by Boost.string_ref. Neither
+    of these were adequate for how use of @c ConstBuffer evolved and so @c MemView is @c
+    ConstBuffer with some additional stylistic changes based on Boost.string_ref.
+
+    This class is closely integrated with @c StringView. These classes have the same underlying
+    implementation and are differentiated only because of the return types and a few string oriented
+    methods.
+ */
+class MemView
+{
+  typedef MemView self; ///< Self reference type.
+
+protected:
+  const void *_ptr = nullptr; ///< Pointer to base of memory chunk.
+  size_t _size     = 0;       ///< Size of memory chunk.
+
+public:
+  /// Default constructor (empty buffer).
+  constexpr MemView();
+
+  /** Construct explicitly with a pointer and size.
+   */
+  constexpr MemView(const void *ptr, ///< Pointer to buffer.
+                    size_t n         ///< Size of buffer.
+                    );
+
+  /** Construct from a half open range of two pointers.
+      @note The instance at @start is in the view but the instance at @a end is not.
+  */
+  template <typename T>
+  constexpr MemView(T const *start, ///< First byte in the view.
+                    T const *end    ///< First byte not in the view.
+                    );
+
+  /** Construct from a half open range of two pointers.
+      @note The instance at @start is in the view but the instance at @a end is not.
+  */
+  MemView(void const *start, ///< First byte in the view.
+          void const *end    ///< First byte not in the view.
+          );
+
+  /** Construct from nullptr.
+      This implicitly makes the length 0.
+  */
+  constexpr MemView(std::nullptr_t);
+
+  /// Convert from StringView.
+  constexpr MemView(StringView const &that);
+
+  /** Equality.
+
+      This is effectively a pointer comparison, buffer contents are not compared.
+
+      @return @c true if @a that refers to the same view as @a this,
+      @c false otherwise.
+   */
+  bool operator==(self const &that) const;
+
+  /** Inequality.
+      @return @c true if @a that does not refer to the same view as @a this,
+      @c false otherwise.
+   */
+  bool operator!=(self const &that) const;
+
+  /// Assignment - the view is copied, not the content.
+  self &operator=(self const &that);
+
+  /** Shift the view to discard the first byte.
+      @return @a this.
+  */
+  self &operator++();
+
+  /** Shift the view to discard the leading @a n bytes.
+      @return @a this
+  */
+  self &operator+=(size_t n);
+
+  /// Check for empty view.
+  /// @return @c true if the view has a zero pointer @b or size.
+  bool operator!() const;
+
+  /// Check for non-empty view.
+  /// @return @c true if the view refers to a non-empty range of bytes.
+  explicit operator bool() const;
+
+  /// Check for empty view (no content).
+  /// @see operator bool
+  bool isEmpty() const;
+
+  /// @name Accessors.
+  //@{
+  /// Pointer to the first byte in the view.
+  const void *begin() const;
+  /// Pointer to first byte not in the view.
+  const void *end() const;
+  /// Number of bytes in the view.
+  constexpr size_t size() const;
+  /// Memory pointer.
+  /// @note This is equivalent to @c begin currently but it's probably good to have separation.
+  constexpr const void *ptr() const;
+  /// @return the @a V value at index @a n.
+  template <typename V> V at(ssize_t n) const;
+  /// @return a pointer to the @a V value at index @a n.
+  template <typename V> V const *at_ptr(ssize_t n) const;
+  //@}
+
+  /// Set the view.
+  /// This is faster but equivalent to constructing a new view with the same
+  /// arguments and assigning it.
+  /// @return @c this.
+  self &setView(const void *ptr, ///< Buffer address.
+                size_t n = 0     ///< Buffer size.
+                );
+
+  /// Set the view.
+  /// This is faster but equivalent to constructing a new view with the same
+  /// arguments and assigning it.
+  /// @return @c this.
+  self &setView(const void *start, ///< First valid character.
+                const void *end    ///< First invalid character.
+                );
+
+  /// Clear the view (become an empty view).
+  self &clear();
+
+  /// @return @c true if the byte at @a *p is in the view.
+  bool contains(const void *p) const;
+
+  /** Find a value.
+      The memory is searched as if it were an array of the value type @a T.
+
+      @return A pointer to the first occurrence of @a v in @a this
+      or @c nullptr if @a v is not found.
+  */
+  template <typename V> const V *find(V v) const;
+
+  /** Find a value.
+      The memory is searched as if it were an array of the value type @a V.
+
+      @return A pointer to the first value for which @a pred is @c true otherwise
+      @c nullptr.
+  */
+  template <typename V> const V *find(std::function<bool(V)> const &pred);
+
+  /** Get the initial segment of the view before @a p.
+
+      The byte at @a p is not included. If @a p is not in the view an empty view
+      is returned.
+
+      @return A buffer that contains all data before @a p.
+  */
+  self prefix(const void *p) const;
+
+  /** Split the view at @a p.
+
+      The view is split in to two parts at @a p and the prefix is returned. The view is updated to
+     contain the bytes not returned in the prefix. The prefix will not contain @a p.
+
+      @note If @a *p refers to a byte that is not in @a this then @a this is not changed and an empty
+      buffer is returned. Therefore this method can be safely called with the return value of
+      calling @c find.
+
+      @return A buffer containing data up to but not including @a p.
+
+      @see extractPrefix
+  */
+  self splitPrefix(const void *p);
+
+  /** Extract a prefix delimited by @a p.
+
+      A prefix of @a this is removed from the view and returned. If @a p is not in the view then the
+      entire view is extracted and returned.
+
+      If @a p points at a byte in the view this is identical to @c splitPrefix.  If not then the
+      entire view in @a this will be returned and @a this will become an empty view.
+
+      @return The prefix bounded at @a p or the entire view if @a p is not a byte in the view.
+
+      @see splitPrefix
+  */
+  self extractPrefix(const void *p);
+
+  /** Get the trailing segment of the view after @a p.
+
+      The byte at @a p is not included. If @a p is not in the view an empty view is returned.
+
+      @return A buffer that contains all data after @a p.
+  */
+  self suffix(const void *p) const;
+
+  /** Split the view at @a p.
+
+      The view is split in to two parts and the suffix is returned. The view is updated to contain
+      the bytes not returned in the suffix. The suffix will not contain @a p.
+
+      @note If @a p does not refer to a byte in the view, an empty view is returned and @a this is
+      unchanged.
 
-template <typename V>
-inline const V *
-MemView::find(std::function<bool(V)> const &pred)
-{
-  for (const V *p = static_cast<const V *>(_ptr), *limit = p + (_size / sizeof(V)); p < limit; ++p)
-    if (pred(*p))
-      return p;
-  return nullptr;
-}
+      @return @a this.
+  */
+  self splitSuffix(const void *p);
+};
 
-// === StringView Implementation ===
-inline constexpr StringView::StringView()
-{
-}
-inline constexpr StringView::StringView(const char *ptr, size_t n) : _ptr(ptr), _size(n)
-{
-}
-inline constexpr StringView::StringView(const char *ptr, int n) : _ptr(ptr), _size(n < 0 ? 0 : n)
-{
-}
-inline constexpr StringView::StringView(const char *start, const char *end) : _ptr(start), _size(end - start)
-{
-}
-inline constexpr StringView::StringView(std::nullptr_t) : _ptr(nullptr), _size(0)
-{
-}
-inline StringView::StringView(MemView const &that) : _ptr(static_cast<const char *>(that.ptr())), _size(that.size())
-{
-}
-inline StringView::StringView(std::string const &str) : _ptr(str.data()), _size(str.size())
+inline constexpr MemView::MemView()
 {
 }
-template <size_t N> constexpr StringView::StringView(const char (&s)[N], literal_t) : _ptr(s), _size(N - 1)
+inline constexpr MemView::MemView(void const *ptr, size_t n) : _ptr(ptr), _size(n)
 {
 }
-
-template <size_t N> constexpr StringView::StringView(const char (&s)[N], array_t) : _ptr(s), _size(N)
+template <typename T> constexpr MemView::MemView(const T *start, const T *end) : _ptr(start), _size((end - start) * sizeof(T))
 {
 }
-
-inline constexpr StringView::StringView(ts::string_view const &that) : _ptr(that.data()), _size(that.size())
+// <void*> is magic, handle that specially.
+// No constexpr because the spec specifically forbids casting from <void*> to a typed pointer.
+inline MemView::MemView(void const *start, void const *end)
+  : _ptr(start), _size(static_cast<const char *>(end) - static_cast<char const *>(start))
 {
 }
-
-inline StringView::operator ts::string_view() const
+inline constexpr MemView::MemView(std::nullptr_t) : _ptr(nullptr), _size(0)
 {
-  return {_ptr, _size};
 }
-
-inline void StringView::initDelimiterSet(std::bitset<256> &set)
+inline constexpr MemView::MemView(StringView const &that) : _ptr(that.ptr()), _size(that.size())
 {
-  set.reset();
-  for (char c : *this)
-    set[static_cast<uint8_t>(c)] = true;
 }
 
-inline StringView &
-StringView::setView(const char *ptr, size_t n)
+inline MemView &
+MemView::setView(const void *ptr, size_t n)
 {
   _ptr  = ptr;
   _size = n;
   return *this;
 }
 
-inline StringView &
-StringView::setView(const char *ptr, const char *limit)
+inline MemView &
+MemView::setView(const void *ptr, const void *limit)
 {
   _ptr  = ptr;
-  _size = limit - ptr;
+  _size = static_cast<const char *>(limit) - static_cast<const char *>(ptr);
   return *this;
 }
 
-inline StringView &
-StringView::clear()
+inline MemView &
+MemView::clear()
 {
   _ptr  = 0;
   _size = 0;
@@ -986,107 +1169,92 @@ StringView::clear()
 }
 
 inline bool
-StringView::operator==(self const &that) const
+MemView::operator==(self const &that) const
 {
   return _size == that._size && _ptr == that._ptr;
 }
 
 inline bool
-StringView::operator!=(self const &that) const
+MemView::operator!=(self const &that) const
 {
   return !(*this == that);
 }
 
-inline bool StringView::operator!() const
+inline bool MemView::operator!() const
 {
   return !(_ptr && _size);
 }
 
-inline StringView::operator bool() const
+inline MemView::operator bool() const
 {
   return _ptr && _size;
 }
 
 inline bool
-StringView::isEmpty() const
+MemView::isEmpty() const
 {
   return !(_ptr && _size);
 }
 
-inline char StringView::operator*() const
-{
-  return *_ptr;
-}
-
-inline StringView &StringView::operator++()
+inline MemView &MemView::operator++()
 {
-  ++_ptr;
+  _ptr = static_cast<const char *>(_ptr) + 1;
   --_size;
   return *this;
 }
 
-inline StringView &
-StringView::operator+=(size_t n)
+inline MemView &
+MemView::operator+=(size_t n)
 {
   if (n > _size) {
     _ptr  = nullptr;
     _size = 0;
   } else {
-    _ptr += n;
+    _ptr = static_cast<const char *>(_ptr) + n;
     _size -= n;
   }
   return *this;
 }
 
-inline const char *
-StringView::begin() const
+inline const void *
+MemView::begin() const
 {
   return _ptr;
 }
-inline constexpr const char *
-StringView::ptr() const
+inline constexpr const void *
+MemView::ptr() const
 {
   return _ptr;
 }
 
-inline const char *
-StringView::end() const
+inline const void *
+MemView::end() const
 {
-  return _ptr + _size;
+  return static_cast<const char *>(_ptr) + _size;
 }
 
 inline constexpr size_t
-StringView::size() const
+MemView::size() const
 {
   return _size;
 }
 
-inline StringView &
-StringView::operator=(StringView const &that)
+inline MemView &
+MemView::operator=(MemView const &that)
 {
   _ptr  = that._ptr;
   _size = that._size;
   return *this;
 }
 
-inline char StringView::operator[](size_t n) const
-{
-  return _ptr[n];
-}
-
-inline char StringView::operator[](int n) const
-{
-  return _ptr[n];
-}
-
 inline bool
-StringView::contains(const char *p) const
+MemView::contains(const void *p) const
 {
-  return _ptr <= p && p < _ptr + _size;
+  return _ptr <= this->begin() && p < this->end();
 }
 
-inline StringView
-StringView::prefix(const char *p) const
+inline MemView
+MemView::prefix(const void *p) const
 {
   self zret;
   if (this->contains(p))
@@ -1094,55 +1262,19 @@ StringView::prefix(const char *p) const
   return zret;
 }
 
-inline StringView
-StringView::prefix(char c)
-{
-  return this->prefix(this->find(c));
-}
-
-inline StringView
-StringView::prefix(self delimiters) const
-{
-  return this->prefix(this->find(delimiters));
-}
-
-inline StringView
-StringView::prefix(std::function<bool(char)> const &pred) const
-{
-  return this->prefix(this->find(pred));
-}
-
-inline StringView
-StringView::splitPrefix(const char *p)
+inline MemView
+MemView::splitPrefix(const void *p)
 {
   self zret; // default to empty return.
   if (this->contains(p)) {
     zret.setView(_ptr, p);
-    this->setView(p + 1, this->end());
+    this->setView(p, this->end());
   }
   return zret;
 }
 
-inline StringView
-StringView::splitPrefix(char c)
-{
-  return this->splitPrefix(this->find(c));
-}
-
-inline StringView
-StringView::splitPrefix(self delimiters)
-{
-  return this->splitPrefix(this->find(delimiters));
-}
-
-inline StringView
-StringView::splitPrefix(std::function<bool(char)> const &pred)
-{
-  return this->splitPrefix(this->find(pred));
-}
-
-inline StringView
-StringView::extractPrefix(const char *p)
+inline MemView
+MemView::extractPrefix(const void *p)
 {
   self zret{this->splitPrefix(p)};
 
@@ -1155,255 +1287,101 @@ StringView::extractPrefix(const char *p)
   return zret;
 }
 
-inline StringView
-StringView::extractPrefix(char c)
-{
-  return this->extractPrefix(this->find(c));
-}
-
-inline StringView
-StringView::extractPrefix(self delimiters)
-{
-  return this->extractPrefix(this->find(delimiters));
-}
-
-inline StringView
-StringView::extractPrefix(std::function<bool(char)> const &pred)
-{
-  return this->extractPrefix(this->find(pred));
-}
-
-inline StringView
-StringView::suffix(const char *p) const
+inline MemView
+MemView::suffix(const void *p) const
 {
   self zret;
   if (this->contains(p))
-    zret.setView(p + 1, _ptr + _size);
+    zret.setView(p, this->end());
   return zret;
 }
 
-inline StringView
-StringView::suffix(char c)
-{
-  return this->suffix(this->find(c));
-}
-
-inline StringView
-StringView::suffix(self delimiters)
-{
-  return this->suffix(this->find(delimiters));
-}
-
-inline StringView
-StringView::suffix(std::function<bool(char)> const &pred)
-{
-  return this->suffix(this->find(pred));
-}
-
-inline StringView
-StringView::splitSuffix(const char *p)
+inline MemView
+MemView::splitSuffix(const void *p)
 {
   self zret;
   if (this->contains(p)) {
-    zret.setView(p + 1, this->end());
+    zret.setView(p, this->end());
     this->setView(_ptr, p);
   }
   return zret;
 }
 
-inline StringView
-StringView::splitSuffix(char c)
+template <typename V>
+inline V
+MemView::at(ssize_t n) const
 {
-  return this->splitSuffix(this->find(c));
+  return static_cast<V const *>(_ptr)[n];
 }
 
-inline StringView
-StringView::splitSuffix(self delimiters)
+template <typename V>
+inline V const *
+MemView::at_ptr(ssize_t n) const
 {
-  return this->splitSuffix(this->find(delimiters));
+  return static_cast<V const *>(_ptr) + n;
 }
 
-inline StringView
-StringView::splitSuffix(std::function<bool(char)> const &pred)
+template <typename V>
+inline const V *
+MemView::find(V v) const
 {
-  return this->splitSuffix(this->find(pred));
+  for (const V *spot = static_cast<const V *>(_ptr), limit = spot + (_size / sizeof(V)); spot < limit; ++spot)
+    if (v == *spot)
+      return spot;
+  return nullptr;
 }
 
+// Specialize char for performance.
+template <>
 inline const char *
-StringView::find(char c) const
+MemView::find(char v) const
 {
-  return static_cast<const char *>(memchr(_ptr, c, _size));
+  return static_cast<const char *>(memchr(_ptr, v, _size));
 }
 
-inline const char *
-StringView::find(self delimiters) const
+template <typename V>
+inline const V *
+MemView::find(std::function<bool(V)> const &pred)
 {
-  std::bitset<256> valid;
-  delimiters.initDelimiterSet(valid);
-
-  for (const char *p = this->begin(), *limit = this->end(); p < limit; ++p)
-    if (valid[static_cast<uint8_t>(*p)])
+  for (const V *p = static_cast<const V *>(_ptr), *limit = p + (_size / sizeof(V)); p < limit; ++p)
+    if (pred(*p))
       return p;
-
   return nullptr;
 }
 
-inline const char *
-StringView::find(std::function<bool(char)> const &pred) const
-{
-  const char *p = std::find_if(this->begin(), this->end(), pred);
-  return p == this->end() ? nullptr : p;
-}
-
-inline StringView &
-StringView::ltrim(char c)
-{
-  while (_size && *_ptr == c)
-    ++*this;
-  return *this;
-}
-
-inline StringView &
-StringView::rtrim(char c)
-{
-  while (_size && _ptr[_size - 1] == c)
-    --_size;
-  return *this;
-}
-inline StringView &
-StringView::trim(char c)
-{
-  this->ltrim(c);
-  return this->rtrim(c);
-}
-
-inline StringView &
-StringView::ltrim(self delimiters)
-{
-  std::bitset<256> valid;
-  delimiters.initDelimiterSet(valid);
-
-  while (_size && valid[static_cast<uint8_t>(*_ptr)])
-    ++*this;
-
-  return *this;
-}
-
-inline StringView &
-StringView::rtrim(self delimiters)
-{
-  std::bitset<256> valid;
-  delimiters.initDelimiterSet(valid);
-
-  while (_size && valid[static_cast<uint8_t>(_ptr[_size - 1])])
-    --_size;
-
-  return *this;
-}
-
-inline StringView &
-StringView::trim(self delimiters)
-{
-  std::bitset<256> valid;
-  delimiters.initDelimiterSet(valid);
-  // Do this explicitly, so we don't have to initialize the character set twice.
-  while (_size && valid[static_cast<uint8_t>(_ptr[_size - 1])])
-    --_size;
-  while (_size && valid[static_cast<uint8_t>(_ptr[0])])
-    ++*this;
-  return *this;
-}
-
-inline StringView &
-StringView::ltrim(std::function<bool(char)> const &pred)
-{
-  while (_size && pred(_ptr[0]))
-    ++*this;
-  return *this;
-}
-
-inline StringView &
-StringView::rtrim(std::function<bool(char)> const &pred)
-{
-  while (_size && pred(_ptr[_size - 1]))
-    --_size;
-  return *this;
-}
-
-inline StringView &
-StringView::trim(std::function<bool(char)> const &pred)
-{
-  this->ltrim(pred);
-  return this->rtrim(pred);
-}
-
-inline bool
-StringView::isPrefixOf(self const &that) const
-{
-  return _size <= that._size && 0 == memcmp(_ptr, that._ptr, _size);
-}
-
-inline bool
-StringView::isNoCasePrefixOf(self const &that) const
-{
-  return _size <= that._size && 0 == strncasecmp(_ptr, that._ptr, _size);
-}
-
 inline int
-strcmp(StringView const &lhs, StringView const &rhs)
+memcmp(MemView const &lhs, MemView const &rhs)
 {
-  return ts::memcmp(lhs, rhs);
-}
+  int zret;
+  size_t n;
 
-namespace detail
-{
-  /// Write padding to the stream, using the current stream fill character.
-  template <typename Stream>
-  void
-  stream_fill(Stream &os, std::size_t n)
-  {
-    static constexpr size_t pad_size = 8;
-    typename Stream::char_type padding[pad_size];
+  // Seems a bit ugly but size comparisons must be done anyway to get the memcmp args.
+  if (lhs.size() < rhs.size()) {
+    zret = 1, n = lhs.size();
+  } else {
+    n    = rhs.size();
+    zret = rhs.size() < lhs.size() ? -1 : 0;
+  }
 
-    std::fill_n(padding, pad_size, os.fill());
-    for (; n >= pad_size && os.good(); n -= pad_size)
-      os.write(padding, pad_size);
-    if (n > 0 && os.good())
-      os.write(padding, n);
+  int r = ::memcmp(lhs.ptr(), rhs.ptr(), n);
+  if (0 != r) { // If we got a not-equal, override the size based result.
+    zret = r;
   }
 
-  extern template void stream_fill(std::ostream &, std::size_t);
-} // detail
+  return zret;
+}
 
-template <typename Stream>
-Stream &
-StringView::stream_write(Stream &os, const StringView &b) const
+namespace std
 {
-  const std::size_t w = os.width();
-  if (w <= b.size()) {
-    os.write(b.ptr(), 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())
-      detail::stream_fill(os, pad_size);
-    if (os.good())
-      os.write(b.ptr(), b.size());
-    if (align_left && os.good())
-      detail::stream_fill(os, pad_size);
+ostream &
+operator<<(ostream &os, const ts::MemView &b)
+{
+  if (os.good()) {
+    ostringstream out;
+    out << b.size() << '@' << hex << b.ptr();
+    os << out.str();
   }
   return os;
 }
-
-// Provide an instantiation for @c std::ostream as it's likely this is the only one ever used.
-extern template std::ostream &StringView::stream_write(std::ostream &, const StringView &) const;
-
-} // end namespace ApacheTrafficServer
-
-namespace std
-{
-ostream &operator<<(ostream &os, const ts::MemView &b);
-ostream &operator<<(ostream &os, const ts::StringView &b);
 }
-
-#endif // TS_BUFFER_HEADER
+#endif
diff --git a/lib/ts/ink_inet.cc b/lib/ts/ink_inet.cc
index e2f033f..0fa4251 100644
--- a/lib/ts/ink_inet.cc
+++ b/lib/ts/ink_inet.cc
@@ -27,9 +27,8 @@
 #include "ts/ParseRules.h"
 #include "ts/ink_code.h"
 #include "ts/ink_assert.h"
-#include "ts/TestBox.h"
 #include <fstream>
-#include <ts/MemView.h>
+#include <ts/TextView.h>
 
 IpAddr const IpAddr::INVALID;
 
@@ -156,25 +155,28 @@ ats_ip_nptop(sockaddr const *addr, char *dst, size_t size)
 int
 ats_ip_parse(ts::string_view str, ts::string_view *addr, ts::string_view *port, ts::string_view *rest)
 {
-  // In case the incoming arguments are null.
-  ts::string_view localAddr, localPort;
-  ts::StringView src(str);
+  ts::TextView src(str); /// Easier to work with for parsing.
+  // In case the incoming arguments are null, set them here and only check for null once.
+  // it doesn't matter if it's all the same, the results will be thrown away.
+  ts::string_view local;
   if (!addr) {
-    addr = &localAddr;
+    addr = &local;
   }
   if (!port) {
-    port = &localPort;
+    port = &local;
   }
+  if (!rest) {
+    rest = &local;
+  }
+
   ink_zero(*addr);
   ink_zero(*port);
-  if (rest) {
-    ink_zero(*rest);
-  }
+  ink_zero(*rest);
 
   // Let's see if we can find out what's in the address string.
   if (src) {
     bool colon_p = false;
-    src.ltrim(&ParseRules::is_ws);
+    src.ltrim_if(&ParseRules::is_ws);
     // Check for brackets.
     if ('[' == *src) {
       /* Ugly. In a number of places we must use bracket notation
@@ -192,15 +194,16 @@ ats_ip_parse(ts::string_view str, ts::string_view *addr, ts::string_view *port,
          So we can't depend on that sizing.
       */
       ++src; // skip bracket.
-      *addr = src.extractPrefix(']');
+      *addr = src.take_prefix_at(']');
       if (':' == *src) {
         colon_p = true;
         ++src;
       }
     } else {
-      ts::StringView post = src.suffix(':');
-      if (post.ptr() && !post.find(':')) {
-        *addr   = src.extractPrefix(post.ptr() - 1);
+      ts::TextView::size_type last = src.rfind(':');
+      if (last != ts::TextView::npos && last == src.find(':')) {
+        // Exactly one colon - leave post colon stuff in @a src.
+        *addr   = src.take_prefix_at(last);
         colon_p = true;
       } else { // presume no port, use everything.
         *addr = src;
@@ -208,18 +211,16 @@ ats_ip_parse(ts::string_view str, ts::string_view *addr, ts::string_view *port,
       }
     }
     if (colon_p) {
-      ts::StringView tmp(src);
-      src.ltrim(&ParseRules::is_digit);
+      ts::TextView tmp{src};
+      src.ltrim_if(&ParseRules::is_digit);
 
-      if (tmp.ptr() == src.ptr()) {                 // no digits at all
-        src.setView(tmp.ptr() - 1, tmp.size() + 1); // back up to include colon
+      if (tmp.data() == src.data()) {                 // no digits at all
+        src.set_view(tmp.data() - 1, tmp.size() + 1); // back up to include colon
       } else {
-        *port = ts::string_view(tmp.ptr(), src.ptr() - tmp.ptr());
+        *port = ts::string_view(tmp.data(), src.data() - tmp.data());
       }
     }
-    if (rest) {
-      *rest = src;
-    }
+    *rest = src;
   }
   return addr->empty() ? -1 : 0; // true if we found an address.
 }
@@ -547,12 +548,12 @@ ats_ip_check_characters(ts::string_view text)
 {
   bool found_colon = false;
   bool found_hex   = false;
-  for (const char *p = text.data(), *limit = p + text.size(); p < limit; ++p) {
-    if (':' == *p) {
+  for (char c : text) {
+    if (':' == c) {
       found_colon = true;
-    } else if ('.' == *p || isdigit(*p)) { /* empty */
+    } else if ('.' == c || isdigit(c)) { /* empty */
       ;
-    } else if (isxdigit(*p)) {
+    } else if (isxdigit(c)) {
       found_hex = true;
     } else {
       return AF_UNSPEC;
@@ -562,87 +563,6 @@ ats_ip_check_characters(ts::string_view text)
   return found_hex && !found_colon ? AF_UNSPEC : found_colon ? AF_INET6 : AF_INET;
 }
 
-// Need to declare this type globally so gcc 4.4 can use it in the countof() template ...
-struct ip_parse_spec {
-  const char *hostspec;
-  const char *host;
-  const char *port;
-  const char *rest;
-};
-
-REGRESSION_TEST(Ink_Inet)(RegressionTest *t, int /* atype */, int *pstatus)
-{
-  TestBox box(t, pstatus);
-  IpEndpoint ep;
-  IpAddr addr;
-
-  box = REGRESSION_TEST_PASSED;
-
-  // Test ats_ip_parse() ...
-  {
-    struct ip_parse_spec names[] = {
-      {"::", "::", nullptr, nullptr},
-      {"[::1]:99", "::1", "99", nullptr},
-      {"127.0.0.1:8080", "127.0.0.1", "8080", nullptr},
-      {"127.0.0.1:8080-Bob", "127.0.0.1", "8080", "-Bob"},
-      {"127.0.0.1:", "127.0.0.1", nullptr, ":"},
-      {"foo.example.com", "foo.example.com", nullptr, nullptr},
-      {"foo.example.com:99", "foo.example.com", "99", nullptr},
-      {"ffee::24c3:3349:3cee:0143", "ffee::24c3:3349:3cee:0143", nullptr, nullptr},
-      {"fe80:88b5:4a:20c:29ff:feae:1c33:8080", "fe80:88b5:4a:20c:29ff:feae:1c33:8080", nullptr, nullptr},
-      {"[ffee::24c3:3349:3cee:0143]", "ffee::24c3:3349:3cee:0143", nullptr, nullptr},
-      {"[ffee::24c3:3349:3cee:0143]:80", "ffee::24c3:3349:3cee:0143", "80", nullptr},
-      {"[ffee::24c3:3349:3cee:0143]:8080x", "ffee::24c3:3349:3cee:0143", "8080", "x"},
-    };
-
-    for (unsigned i = 0; i < countof(names); ++i) {
-      ip_parse_spec const &s = names[i];
-      ts::string_view host, port, rest;
-      size_t len;
-
-      box.check(ats_ip_parse(ts::string_view(s.hostspec, strlen(s.hostspec)), &host, &port, &rest) == 0, "ats_ip_parse(%s)",
-                s.hostspec);
-      len = strlen(s.host);
-      box.check(len == host.size() && strncmp(host.data(), s.host, host.size()) == 0, "ats_ip_parse(%s) gave addr '%.*s'",
-                s.hostspec, static_cast<int>(host.size()), host.data());
-      if (s.port) {
-        len = strlen(s.port);
-        box.check(len == port.size() && strncmp(port.data(), s.port, port.size()) == 0, "ats_ip_parse(%s) gave port '%.*s'",
-                  s.hostspec, static_cast<int>(port.size()), port.data());
-      } else {
-        box.check(port.size() == 0, "ats_ip_parse(%s) gave port '%.*s' instead of empty", s.hostspec, static_cast<int>(port.size()),
-                  port.data());
-      }
-
-      if (s.rest) {
-        len = strlen(s.rest);
-        box.check(len == rest.size() && strncmp(rest.data(), s.rest, len) == 0, "ats_ip_parse(%s) gave rest '%.*s' instead of '%s'",
-                  s.hostspec, static_cast<int>(rest.size()), rest.data(), s.rest);
-      } else {
-        box.check(rest.size() == 0, "ats_ip_parse(%s) gave rest '%.*s' instead of empty", s.hostspec, static_cast<int>(rest.size()),
-                  rest.data());
-      }
-    }
-  }
-
-  // Test ats_ip_pton() ...
-  {
-    box.check(ats_ip_pton("76.14.64.156", &ep.sa) == 0, "ats_ip_pton()");
-    box.check(addr.load("76.14.64.156") == 0, "IpAddr::load()");
-    box.check(addr.family() == ep.family(), "mismatched address family");
-
-    switch (addr.family()) {
-    case AF_INET:
-      box.check(ep.sin.sin_addr.s_addr == addr._addr._ip4, "IPv4 address mismatch");
-      break;
-    case AF_INET6:
-      box.check(memcmp(&ep.sin6.sin6_addr, &addr._addr._ip6, sizeof(in6_addr)) == 0, "IPv6 address mismatch");
-      break;
-    default:;
-    }
-  }
-}
-
 int
 ats_tcp_somaxconn()
 {
diff --git a/lib/ts/ink_inet.h b/lib/ts/ink_inet.h
index 4f52bec..99c99c6 100644
--- a/lib/ts/ink_inet.h
+++ b/lib/ts/ink_inet.h
@@ -28,8 +28,8 @@
 #include <netinet/in.h>
 #include <netdb.h>
 #include <sys/socket.h>
-#include "ts/ink_memory.h"
-#include "ts/ink_apidefs.h"
+#include <ts/ink_memory.h>
+#include <ts/ink_apidefs.h>
 #include <ts/string_view.h>
 
 #if !TS_HAS_IN6_IS_ADDR_UNSPECIFIED
diff --git a/lib/ts/test_MemView.cc b/lib/ts/test_MemView.cc
deleted file mode 100644
index efe7b55..0000000
--- a/lib/ts/test_MemView.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-/** @file
-
-   MemView testing.
-
-    @section license License
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
- */
-
-#include <ts/MemView.h>
-#include <algorithm>
-#include <iomanip>
-#include <ios>
-#include <iostream>
-#include <string>
-
-using namespace ts;
-
-template <typename T, typename S>
-bool
-CheckEqual(T const &lhs, S const &rhs, std::string const &prefix)
-{
-  bool zret = lhs == rhs;
-  if (!zret) {
-    std::cout << "FAIL: " << prefix << ": Expected " << lhs << " to be " << rhs << std::endl;
-  }
-  return zret;
-}
-
-bool
-Test_1()
-{
-  std::string text = "01234567";
-  StringView a(text);
-
-  std::cout << "Text = |" << a << '|' << std::endl;
-  std::cout << "     = |" << std::setw(5) << a << '|' << std::endl;
-  std::cout << "     = |" << std::setw(12) << a << '|' << std::endl;
-  std::cout << "     = |" << std::setw(12) << std::right << a << '|' << std::endl;
-  std::cout << "     = |" << std::setw(12) << std::left << a << '|' << std::endl;
-  std::cout << "     = |" << std::setw(12) << std::right << std::setfill('_') << a << '|' << std::endl;
-  std::cout << "     = |" << std::setw(12) << std::left << std::setfill('_') << a << '|' << std::endl;
-  return true;
-}
-
-bool
-Test_2()
-{
-  bool zret = true;
-  StringView sva("litt\0ral");
-  StringView svb("litt\0ral", StringView::literal);
-  StringView svc("litt\0ral", StringView::array);
-
-  zret = zret && CheckEqual(sva.size(), 4U, "strlen constructor");
-  zret = zret && CheckEqual(svb.size(), 8U, "literal constructor");
-  zret = zret && CheckEqual(svc.size(), 9U, "array constructor");
-
-  return zret;
-}
-
-// These tests are purely compile time.
-void
-Test_Compile()
-{
-  int i[12];
-  char c[29];
-  void *x = i, *y = i + 12;
-  MemView mvi(i, i + 12);
-  MemView mci(c, c + 29);
-  MemView mcv(x, y);
-}
-
-int
-main(int, char *argv[])
-{
-  bool zret = true;
-
-  zret = zret && Test_1();
-  zret = zret && Test_2();
-
-  return zret ? 0 : 1;
-}
diff --git a/lib/ts/unit-tests/test_TextView.cc b/lib/ts/unit-tests/test_TextView.cc
new file mode 100644
index 0000000..c8e51c3
--- /dev/null
+++ b/lib/ts/unit-tests/test_TextView.cc
@@ -0,0 +1,147 @@
+/** @file
+
+    TextView unit tests.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#include <ts/TextView.h>
+#include <catch.hpp>
+#include <iostream>
+
+TEST_CASE("TextView Constructor", "[libts][TextView]")
+{
+  static std::string base = "Evil Dave Rulez!";
+  ts::TextView tv(base);
+  ts::TextView a{"Evil Dave Rulez"};
+  ts::TextView b{base.data(), base.size()};
+  ts::TextView c{ts::string_view(base)};
+  constexpr ts::TextView d{"Grigor!"_sv};
+}
+
+TEST_CASE("TextView Operations", "[libts][TextView]")
+{
+  ts::TextView tv{"Evil Dave Rulez"};
+  size_t off;
+
+  REQUIRE(tv.find('l') == 3);
+  off = tv.find_if([](char c) { return c == 'D'; });
+  REQUIRE(off == tv.find('D'));
+}
+
+TEST_CASE("TextView Trimming", "[libts][TextView]")
+{
+  ts::TextView tv("  Evil Dave Rulz   ...");
+  ts::TextView tv2{"More Text1234567890"};
+  REQUIRE("Evil Dave Rulz   ..." == ts::TextView(tv).ltrim_if(&isspace));
+  REQUIRE(tv2 == ts::TextView{tv2}.ltrim_if(&isspace));
+  REQUIRE("More Text" == ts::TextView{tv2}.rtrim_if(&isdigit));
+  REQUIRE("  Evil Dave Rulz   " == ts::TextView(tv).rtrim('.'));
+  REQUIRE("Evil Dave Rulz" == ts::TextView(tv).trim(" ."));
+}
+
+TEST_CASE("TextView Affixes", "[libts][TextView]")
+{
+  ts::TextView tv1("0123456789;01234567890");
+  ts::TextView prefix{tv1.prefix(10)};
+
+  REQUIRE("0123456789" == prefix);
+  REQUIRE("67890" == tv1.suffix(5));
+
+  ts::TextView tv2 = tv1.prefix(';');
+  REQUIRE(tv2 == "0123456789");
+
+  ts::TextView right{tv1};
+  ts::TextView left{right.split_prefix_at(';')};
+  REQUIRE(right.size() == 11);
+  REQUIRE(left.size() == 10);
+
+  ts::TextView tv3 = "abcdefg:gfedcba";
+  left             = tv3;
+  right            = left.split_suffix_at(";:,");
+  ts::TextView pre{tv3}, post{pre.split_suffix_at(7)};
+  REQUIRE(right.size() == 7);
+  REQUIRE(left.size() == 7);
+  REQUIRE(left == "abcdefg");
+  REQUIRE(right == "gfedcba");
+
+  ts::TextView addr1{"[fe80::fc54:ff:fe60:d886]"};
+  ts::TextView addr2{"[fe80::fc54:ff:fe60:d886]:956"};
+  ts::TextView addr3{"192.168.1.1:5050"};
+
+  ts::TextView t = addr1;
+  ++t;
+  REQUIRE("fe80::fc54:ff:fe60:d886]" == t);
+  ts::TextView a = t.take_prefix_at(']');
+  REQUIRE("fe80::fc54:ff:fe60:d886" == a);
+  REQUIRE(t.empty());
+
+  t = addr2;
+  ++t;
+  a = t.take_prefix_at(']');
+  REQUIRE("fe80::fc54:ff:fe60:d886" == a);
+  REQUIRE(':' == *t);
+  ++t;
+  REQUIRE("956" == t);
+
+  t = addr3;
+  ts::TextView s{t.suffix(':')};
+  REQUIRE("5050" == s);
+  REQUIRE(t == addr3);
+}
+
+TEST_CASE("TextView Formatting", "[libts][TextView]")
+{
+  ts::TextView a("01234567");
+  {
+    std::ostringstream buff;
+    buff << '|' << a << '|';
+    REQUIRE(buff.str() == "|01234567|");
+  }
+  {
+    std::ostringstream buff;
+    buff << '|' << std::setw(5) << a << '|';
+    REQUIRE(buff.str() == "|01234567|");
+  }
+  {
+    std::ostringstream buff;
+    buff << '|' << std::setw(12) << a << '|';
+    REQUIRE(buff.str() == "|    01234567|");
+  }
+  {
+    std::ostringstream buff;
+    buff << '|' << std::setw(12) << std::right << a << '|';
+    REQUIRE(buff.str() == "|    01234567|");
+  }
+  {
+    std::ostringstream buff;
+    buff << '|' << std::setw(12) << std::left << a << '|';
+    REQUIRE(buff.str() == "|01234567    |");
+  }
+  {
+    std::ostringstream buff;
+    buff << '|' << std::setw(12) << std::right << std::setfill('_') << a << '|';
+    REQUIRE(buff.str() == "|____01234567|");
+  }
+  {
+    std::ostringstream buff;
+    buff << '|' << std::setw(12) << std::left << std::setfill('_') << a << '|';
+    REQUIRE(buff.str() == "|01234567____|");
+  }
+}
diff --git a/lib/ts/unit-tests/test_ink_inet.cc b/lib/ts/unit-tests/test_ink_inet.cc
new file mode 100644
index 0000000..e7461d6
--- /dev/null
+++ b/lib/ts/unit-tests/test_ink_inet.cc
@@ -0,0 +1,82 @@
+/** @file
+
+    ink_inet unit tests.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#include <ts/TextView.h>
+#include <ts/ink_inet.h>
+#include <catch.hpp>
+#include <iostream>
+
+TEST_CASE("ink_inet", "[libts][inet][ink_inet]")
+{
+  // Use TextView because string_view(nullptr) fails. Gah.
+  struct ip_parse_spec {
+    ts::TextView hostspec;
+    ts::TextView host;
+    ts::TextView port;
+    ts::TextView rest;
+  };
+
+  constexpr ip_parse_spec names[] = {
+    {{"::"}, {"::"}, {nullptr}, {nullptr}},
+    {{"[::1]:99"}, {"::1"}, {"99"}, {nullptr}},
+    {{"127.0.0.1:8080"}, {"127.0.0.1"}, {"8080"}, {nullptr}},
+    {{"127.0.0.1:8080-Bob"}, {"127.0.0.1"}, {"8080"}, {"-Bob"}},
+    {{"127.0.0.1:"}, {"127.0.0.1"}, {nullptr}, {":"}},
+    {{"foo.example.com"}, {"foo.example.com"}, {nullptr}, {nullptr}},
+    {{"foo.example.com:99"}, {"foo.example.com"}, {"99"}, {nullptr}},
+    {{"ffee::24c3:3349:3cee:0143"}, {"ffee::24c3:3349:3cee:0143"}, {nullptr}, {nullptr}},
+    {{"fe80:88b5:4a:20c:29ff:feae:1c33:8080"}, {"fe80:88b5:4a:20c:29ff:feae:1c33:8080"}, {nullptr}, {nullptr}},
+    {{"[ffee::24c3:3349:3cee:0143]"}, {"ffee::24c3:3349:3cee:0143"}, {nullptr}, {nullptr}},
+    {{"[ffee::24c3:3349:3cee:0143]:80"}, {"ffee::24c3:3349:3cee:0143"}, {"80"}, {nullptr}},
+    {{"[ffee::24c3:3349:3cee:0143]:8080x"}, {"ffee::24c3:3349:3cee:0143"}, {"8080"}, {"x"}},
+  };
+
+  for (auto const &s : names) {
+    ts::string_view host, port, rest;
+
+    REQUIRE(0 == ats_ip_parse(s.hostspec, &host, &port, &rest));
+    REQUIRE(s.host == host);
+    REQUIRE(s.port == port);
+    REQUIRE(s.rest == rest);
+  }
+}
+
+TEST_CASE("ats_ip_pton", "[libts][inet][ink_inet]")
+{
+  IpEndpoint ep;
+  IpAddr addr;
+
+  REQUIRE(0 == ats_ip_pton("76.14.64.156", &ep.sa));
+  REQUIRE(0 == addr.load("76.14.64.156"));
+  REQUIRE(addr.family() == ep.family());
+
+  switch (addr.family()) {
+  case AF_INET:
+    REQUIRE(ep.sin.sin_addr.s_addr == addr._addr._ip4);
+    break;
+  case AF_INET6:
+    REQUIRE(0 == memcmp(&ep.sin6.sin6_addr, &addr._addr._ip6, sizeof(in6_addr)));
+    break;
+  default:;
+  }
+}
diff --git a/lib/ts/unit-tests/test_string_view.cc b/lib/ts/unit-tests/test_string_view.cc
index 2bac5b8..42f4cb9 100644
--- a/lib/ts/unit-tests/test_string_view.cc
+++ b/lib/ts/unit-tests/test_string_view.cc
@@ -91,6 +91,10 @@ TEST_CASE("constructor calls", "[string_view] [constructor]")
     REQUIRE(sv.length() == 5);
     REQUIRE(sv.empty() == false);
     REQUIRE(sv == "hello");
+
+    ts::string_view sv2("0123456789", 6);
+    REQUIRE(sv2.size() == 6);
+    REQUIRE(sv2 == "012345");
   }
 
   SECTION("Literal length equal 0")
@@ -528,6 +532,7 @@ TEST_CASE("Find", "[string_view] [find]")
     REQUIRE(sv.find_last_not_of(svtest) == 6);
     REQUIRE(sv.find_last_not_of("abcdxyz") == 6);
     REQUIRE(sv.find_last_not_of("abcdefg") == npos);
+    REQUIRE("abcdexyzqqq"_sv.find_last_not_of('q') == 7);
 
     REQUIRE(sv.find_last_not_of("abcdxyz", 1, 0) == 1);
     REQUIRE(sv.find_last_not_of("abcdxyz", 1, 5) == npos);
diff --git a/mgmt/LocalManager.cc b/mgmt/LocalManager.cc
index 4b778ab..687c965 100644
--- a/mgmt/LocalManager.cc
+++ b/mgmt/LocalManager.cc
@@ -31,7 +31,7 @@
 #include "MgmtSocket.h"
 #include "ts/ink_cap.h"
 #include "FileManager.h"
-#include <ts/MemView.h>
+#include <ts/string_view.h>
 
 #if TS_USE_POSIX_CAP
 #include <sys/capability.h>
@@ -911,14 +911,14 @@ LocalManager::listenForProxy()
 
     // read backlong configuration value and overwrite the default value if found
     bool found;
-    ts::StringView fam{ats_ip_family_name(p.m_family)};
+    ts::string_view fam{ats_ip_family_name(p.m_family)};
     RecInt backlog = REC_readInteger("proxy.config.net.listen_backlog", &found);
     backlog        = (found && backlog >= 0) ? backlog : ats_tcp_somaxconn();
 
     if ((listen(p.m_fd, backlog)) < 0) {
-      mgmt_fatal(errno, "[LocalManager::listenForProxy] Unable to listen on port: %d (%.*s)\n", p.m_port, fam.size(), fam.ptr());
+      mgmt_fatal(errno, "[LocalManager::listenForProxy] Unable to listen on port: %d (%.*s)\n", p.m_port, fam.size(), fam.data());
     }
-    mgmt_log("[LocalManager::listenForProxy] Listening on port: %d (%.*s)\n", p.m_port, fam.size(), fam.ptr());
+    mgmt_log("[LocalManager::listenForProxy] Listening on port: %d (%.*s)\n", p.m_port, fam.size(), fam.data());
   }
   return;
 }
diff --git a/proxy/ProxyClientSession.h b/proxy/ProxyClientSession.h
index 9a64fb8..83cc7de 100644
--- a/proxy/ProxyClientSession.h
+++ b/proxy/ProxyClientSession.h
@@ -24,8 +24,9 @@
 #ifndef __PROXY_CLIENT_SESSION_H__
 #define __PROXY_CLIENT_SESSION_H__
 
-#include "ts/ink_platform.h"
-#include "ts/ink_resolver.h"
+#include <ts/ink_platform.h>
+#include <ts/ink_resolver.h>
+#include <ts/string_view.h>
 #include "P_Net.h"
 #include "InkAPIInternal.h"
 #include "http/HttpServerSession.h"
diff --git a/proxy/ProxyClientTransaction.h b/proxy/ProxyClientTransaction.h
index a7ea8a5..458e790 100644
--- a/proxy/ProxyClientTransaction.h
+++ b/proxy/ProxyClientTransaction.h
@@ -25,7 +25,7 @@
 #define __PROXY_CLIENT_TRANSACTION_H__
 
 #include "ProxyClientSession.h"
-#include <ts/MemView.h>
+#include <ts/string_view.h>
 
 class HttpSM;
 class HttpServerSession;
diff --git a/proxy/http/ForwardedConfig.cc b/proxy/http/ForwardedConfig.cc
index 27ccb2f..6663d1d 100644
--- a/proxy/http/ForwardedConfig.cc
+++ b/proxy/http/ForwardedConfig.cc
@@ -26,7 +26,7 @@
 #include <cctype>
 
 #include <ts/string_view.h>
-#include <ts/MemView.h>
+#include <ts/TextView.h>
 
 #include <HttpConfig.h>
 
@@ -42,7 +42,7 @@ public:
   // Add a bad option.
   //
   void
-  add(ts::StringView badOpt)
+  add(ts::TextView badOpt)
   {
     if (_count == 0) {
       _err << "\"Forwarded\" configuration: ";
@@ -81,24 +81,24 @@ public:
 
 private:
   void
-  _addQuoted(ts::StringView sv)
+  _addQuoted(ts::TextView sv)
   {
-    _err << '\"' << ts::string_view(sv.begin(), sv.size()) << '\"';
+    _err << '\"' << sv << '\"';
   }
 
   ts::FixedBufferWriter &_err;
 
-  ts::StringView _saveLast;
+  ts::TextView _saveLast;
 
   int _count;
 };
 
-// Compare a StringView to a nul-termimated string, converting the StringView to lower case and ignoring whitespace in it.
+// Compare a TextView to a nul-termimated string, converting the TextView to lower case and ignoring whitespace in it.
 //
 bool
-eqIgnoreCaseWs(ts::StringView sv, const char *target)
+eqIgnoreCaseWs(ts::TextView sv, const char *target)
 {
-  const char *s = sv.begin();
+  const char *s = sv.data();
 
   std::size_t skip = 0;
   std::size_t i    = 0;
@@ -123,13 +123,13 @@ namespace HttpForwarded
 OptionBitSet
 optStrToBitset(ts::string_view optConfigStr, ts::FixedBufferWriter &error)
 {
-  const ts::StringView Delimiters(":|");
+  const ts::TextView Delimiters(":|");
 
   OptionBitSet optBS;
 
-  // Convert to TS StringView to be able to use parsing members.
+  // Convert to TS TextView to be able to use parsing members.
   //
-  ts::StringView oCS(optConfigStr.data(), optConfigStr.size());
+  ts::TextView oCS{optConfigStr};
 
   if (eqIgnoreCaseWs(oCS, "none")) {
     return OptionBitSet();
@@ -138,7 +138,7 @@ optStrToBitset(ts::string_view optConfigStr, ts::FixedBufferWriter &error)
   BadOptionsErrMsg em(error);
 
   do {
-    ts::StringView optStr = oCS.extractPrefix(Delimiters);
+    ts::TextView optStr = oCS.take_prefix_at(Delimiters);
 
     if (eqIgnoreCaseWs(optStr, "for")) {
       optBS.set(FOR);
diff --git a/proxy/http/HttpSM.h b/proxy/http/HttpSM.h
index 0b68390..d4c6839 100644
--- a/proxy/http/HttpSM.h
+++ b/proxy/http/HttpSM.h
@@ -41,7 +41,7 @@
 #include "InkAPIInternal.h"
 #include "../ProxyClientTransaction.h"
 #include "HdrUtils.h"
-#include <ts/MemView.h>
+#include <ts/string_view.h>
 #include <ts/History.h>
 //#include "AuthHttpAdapter.h"
 
diff --git a/proxy/http/HttpTransactHeaders.cc b/proxy/http/HttpTransactHeaders.cc
index fa704a4..5844cc6 100644
--- a/proxy/http/HttpTransactHeaders.cc
+++ b/proxy/http/HttpTransactHeaders.cc
@@ -26,6 +26,7 @@
 
 #include <ts/ink_platform.h>
 #include <ts/BufferWriter.h>
+#include <ts/string_view.h>
 
 #include "HttpTransact.h"
 #include "HttpTransactHeaders.h"
@@ -37,7 +38,6 @@
 #include "I_Machine.h"
 
 #include <array>
-#include <ts/MemView.h>
 
 bool
 HttpTransactHeaders::is_method_cacheable(const HttpConfigParams *http_config_param, const int method)
diff --git a/proxy/http/Makefile.am b/proxy/http/Makefile.am
index a08737e..0ea1639 100644
--- a/proxy/http/Makefile.am
+++ b/proxy/http/Makefile.am
@@ -92,7 +92,7 @@ test_ForwardedConfig_SOURCES = \
   unit-tests/test_ForwardedConfig.cc \
   ForwardedConfig.cc \
   unit-tests/test_ForwardedConfig_mocks.cc \
-  unit-tests/sym-links/MemView.cc
+  ../../lib/ts/TextView.cc
 
 tidy-local: $(libhttp_a_SOURCES) $(noinst_HEADERS)
 	$(CXX_Clang_Tidy)
diff --git a/proxy/http/unit-tests/sym-links/MemView.cc b/proxy/http/unit-tests/sym-links/MemView.cc
deleted file mode 120000
index 51e80fb..0000000
--- a/proxy/http/unit-tests/sym-links/MemView.cc
+++ /dev/null
@@ -1 +0,0 @@
-../../../../lib/ts/MemView.cc
\ No newline at end of file
diff --git a/proxy/http/unit-tests/test_ForwardedConfig.cc b/proxy/http/unit-tests/test_ForwardedConfig.cc
index 712eb9b..338746e 100644
--- a/proxy/http/unit-tests/test_ForwardedConfig.cc
+++ b/proxy/http/unit-tests/test_ForwardedConfig.cc
@@ -115,9 +115,9 @@ TEST_CASE("Forwarded", "[FWD]")
 
   test("\t", "\"Forwarded\" configuration: \"\t   \" is a bad option.", OptionBitSet());
 
-  test(":", "\"Forwarded\" configuration: \"   \" is a bad option.", OptionBitSet());
+  test(":", "\"Forwarded\" configuration: \"\" and \"   \" are bad options.", OptionBitSet());
 
-  test("|", "\"Forwarded\" configuration: \"   \" is a bad option.", OptionBitSet());
+  test("|", "\"Forwarded\" configuration: \"\" and \"   \" are bad options.", OptionBitSet());
 
   test("by=ip", "", OptionBitSetListInit{BY_IP});
 
diff --git a/proxy/http2/Http2ClientSession.h b/proxy/http2/Http2ClientSession.h
index 427a760..42f0afb 100644
--- a/proxy/http2/Http2ClientSession.h
+++ b/proxy/http2/Http2ClientSession.h
@@ -28,7 +28,7 @@
 #include "Plugin.h"
 #include "ProxyClientSession.h"
 #include "Http2ConnectionState.h"
-#include <ts/MemView.h>
+#include <ts/string_view.h>
 #include <ts/ink_inet.h>
 
 // Name                       Edata                 Description

-- 
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].