You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2017/06/15 19:39:27 UTC
[trafficserver] 01/03: Via 7.1: Add MemView.
This is an automated email from the ASF dual-hosted git repository.
zwoop pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 235c9cf8a16bf07a5fc32cab98d588526c202ce7
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Tue Jun 13 10:28:40 2017 -0500
Via 7.1: Add MemView.
---
lib/ts/Makefile.am | 7 +-
lib/ts/MemView.cc | 158 ++++++
lib/ts/MemView.h | 1392 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/ts/test_MemView.cc | 96 ++++
4 files changed, 1651 insertions(+), 2 deletions(-)
diff --git a/lib/ts/Makefile.am b/lib/ts/Makefile.am
index 039db6e..8b3c968 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
+check_PROGRAMS = test_tsutil test_arena test_atomic test_freelist test_geometry test_List test_Map test_Vec test_X509HostnameValidator test_MemView
TESTS_ENVIRONMENT = LSAN_OPTIONS=suppressions=suppression.txt
@@ -85,6 +85,7 @@ libtsutil_la_SOURCES = \
IpMapConf.h \
Layout.cc \
List.h \
+ MemView.h MemView.cc \
MMH.cc \
MMH.h \
Map.h \
@@ -235,6 +236,9 @@ test_X509HostnameValidator_SOURCES = test_X509HostnameValidator.cc
test_X509HostnameValidator_LDADD = libtsutil.la @LIBTCL@ @LIBPCRE@ @OPENSSL_LIBS@
test_X509HostnameValidator_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@
+test_MemView_SOURCES = test_MemView.cc
+test_MemView_LDADD = libtsutil.la
+
test_tsutil_LDADD = libtsutil.la @LIBTCL@ @LIBPCRE@
test_tsutil_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@
test_tsutil_SOURCES = \
@@ -250,4 +254,3 @@ clean-local:
tidy-local: $(DIST_SOURCES)
$(CXX_Clang_Tidy)
-
diff --git a/lib/ts/MemView.cc b/lib/ts/MemView.cc
new file mode 100644
index 0000000..3ae7749
--- /dev/null
+++ b/lib/ts/MemView.cc
@@ -0,0 +1,158 @@
+/** @file
+
+ Class for handling "views" of a buffer. 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
+
+ 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 <sstream>
+#include <cctype>
+#include <ts/ink_platform.h>
+
+namespace ts
+{
+StringView::StringView(const char *s) : _ptr(s), _size(strlen(s))
+{
+}
+
+int
+memcmp(MemView const &lhs, MemView const &rhs)
+{
+ int zret;
+ size_t n;
+
+ // 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;
+ }
+
+ int r = ::memcmp(lhs.ptr(), rhs.ptr(), n);
+ if (0 != r) { // If we got a not-equal, override the size based result.
+ zret = r;
+ }
+
+ return zret;
+}
+
+int
+strcasecmp(StringView lhs, StringView rhs)
+{
+ while (lhs && rhs) {
+ char l = tolower(*lhs);
+ char r = tolower(*rhs);
+ if (l < r) {
+ return -1;
+ } else if (r < l) {
+ return 1;
+ }
+ ++lhs, ++rhs;
+ }
+ return lhs ? 1 : rhs ? -1 : 0;
+}
+
+intmax_t
+svtoi(StringView src, StringView *out, int base)
+{
+ static const int8_t convert[256] = {
+ /* [can't do this nicely because clang format won't allow exdented comments]
+ 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 30
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 40
+ 25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 50
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 60
+ 25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 70
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // B0
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // D0
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // F0
+ };
+
+ intmax_t zret = 0;
+
+ if (out) {
+ out->clear();
+ }
+ if (!(1 < base && base <= 36)) {
+ return 0;
+ }
+ if (src.ltrim(&isspace)) {
+ const char *start = src.ptr();
+ int8_t v;
+ bool neg = false;
+ if ('-' == *src) {
+ ++src;
+ neg = true;
+ }
+ while (src.size() && (-1 != (v = convert[static_cast<unsigned char>(*src)]))) {
+ zret = zret * base + v;
+ ++src;
+ }
+ if (out && (src.ptr() > (neg ? start + 1 : start))) {
+ out->setView(start, src.ptr());
+ }
+
+ if (neg) {
+ zret = -zret;
+ }
+ }
+ return zret;
+}
+
+// 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;
+}
+
+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)
+{
+ if (os.good()) {
+ b.stream_write<ostream>(os, b);
+ os.width(0);
+ }
+ return os;
+}
+}
diff --git a/lib/ts/MemView.h b/lib/ts/MemView.h
new file mode 100644
index 0000000..1925bc7
--- /dev/null
+++ b/lib/ts/MemView.h
@@ -0,0 +1,1392 @@
+#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
+ 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
+
+ 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 <bitset>
+#include <functional>
+#include <iosfwd>
+#include <memory.h>
+#include <algorithm>
+#include <string>
+
+/// Apache Traffic Server commons.
+namespace ts
+{
+class MemView;
+class StringView;
+
+/// 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.
+/// @return
+/// - -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);
+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
+/// is considered the "smaller" value.
+/// @return
+/// - -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);
+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
+/// is considered the "smaller" value. The values are compared ignoring case.
+/// @return
+/// - -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.
+///
+/// @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);
+using ::strcasecmp; // Make this an overload, not an override.
+
+/** Convert the text in @c StringView @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:
+
+ - 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);
+
+/** 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.
+
+ @return @a this.
+ */
+ self splitSuffix(const void *p);
+};
+
+/** A read only view of contiguous piece of memory.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ */
+class StringView
+{
+ typedef StringView self; ///< Self reference type.
+
+protected:
+ const char *_ptr = nullptr; ///< Pointer to base of memory chunk.
+ size_t _size = 0; ///< Size of memory chunk.
+
+ struct literal_t {
+ };
+ struct array_t {
+ };
+
+public:
+ /// Default constructor (empty buffer).
+ constexpr StringView();
+
+ /** Construct explicitly with a pointer and size.
+ */
+ constexpr StringView(const char *ptr, ///< Pointer to buffer.
+ size_t n ///< Size of buffer.
+ );
+
+ /** 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.
+ );
+
+ /** 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.
+ */
+ constexpr StringView(const char *start, ///< First byte in the view.
+ const char *end ///< First byte not in the view.
+ );
+
+ /** Constructor from literal string.
+
+ 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.
+
+ @code
+ StringView a("literal", StringView::literal);
+ @endcode
+ */
+ template <size_t N> constexpr StringView(const char (&s)[N], literal_t);
+
+ /** Constructor from character array.
+
+ Construct directly from an array of characters. All elements of the array are
+ included in the view.
+
+ @code
+ char buff[SIZE];
+ StringView a(buff, StringView::array);
+ @endcode
+
+ @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);
+
+ /** Construct from nullptr.
+ This implicitly makes the length 0.
+ */
+ constexpr StringView(std::nullptr_t);
+
+ /// 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);
+
+ /** 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);
+
+ /// 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);
+
+ /** 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;
+
+ /** Prefix check.
+ @return @c true if @a this is a prefix of @a that.
+ */
+ bool isPrefixOf(self 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 <rim(char c);
+ /** Remove bytes from the start of the view that are in @a delimiters.
+ */
+ self <rim(self delimiters);
+ /** Remove bytes from the start of the view for which @a pred is @c true.
+ */
+ self <rim(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);
+
+ // Functors for using this class in STL containers.
+ /// Ordering functor, lexicographic comparison.
+ struct LessThan {
+ bool
+ operator()(MemView const &lhs, MemView const &rhs)
+ {
+ return -1 == strcmp(lhs, rhs);
+ }
+ };
+ /// Ordering functor, case ignoring lexicographic comparison.
+ struct LessThanNoCase {
+ bool
+ operator()(MemView const &lhs, MemView const &rhs)
+ {
+ return -1 == strcasecmp(lhs, rhs);
+ }
+ };
+
+ /// Specialized stream operator implementation.
+ /// @note Use the standard stream operator unless there is a specific need for this, which is unlikely.
+ /// @return The stream @a os.
+ /// @internal Needed because @c std::ostream::write must be used and
+ /// so alignment / fill have to be explicitly handled.
+ template <typename Stream> Stream &stream_write(Stream &os, const StringView &b) const;
+
+ static constexpr literal_t literal{};
+ static constexpr array_t array{};
+
+protected:
+ /// Initialize a bit mask to mark which characters are in this view.
+ void initDelimiterSet(std::bitset<256> &set);
+};
+// ----------------------------------------------------------
+// Inline implementations.
+
+inline constexpr MemView::MemView()
+{
+}
+inline constexpr MemView::MemView(void const *ptr, size_t n) : _ptr(ptr), _size(n)
+{
+}
+template <typename T> constexpr MemView::MemView(const T *start, const T *end) : _ptr(start), _size((end - start) * sizeof(T))
+{
+}
+// <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 MemView::MemView(std::nullptr_t) : _ptr(nullptr), _size(0)
+{
+}
+inline constexpr MemView::MemView(StringView const &that) : _ptr(that.ptr()), _size(that.size())
+{
+}
+
+inline MemView &
+MemView::setView(const void *ptr, size_t n)
+{
+ _ptr = ptr;
+ _size = n;
+ return *this;
+}
+
+inline MemView &
+MemView::setView(const void *ptr, const void *limit)
+{
+ _ptr = ptr;
+ _size = static_cast<const char *>(limit) - static_cast<const char *>(ptr);
+ return *this;
+}
+
+inline MemView &
+MemView::clear()
+{
+ _ptr = 0;
+ _size = 0;
+ return *this;
+}
+
+inline bool
+MemView::operator==(self const &that) const
+{
+ return _size == that._size && _ptr == that._ptr;
+}
+
+inline bool
+MemView::operator!=(self const &that) const
+{
+ return !(*this == that);
+}
+
+inline bool MemView::operator!() const
+{
+ return !(_ptr && _size);
+}
+
+inline MemView::operator bool() const
+{
+ return _ptr && _size;
+}
+
+inline bool
+MemView::isEmpty() const
+{
+ return !(_ptr && _size);
+}
+
+inline MemView &MemView::operator++()
+{
+ _ptr = static_cast<const char *>(_ptr) + 1;
+ --_size;
+ return *this;
+}
+
+inline MemView &
+MemView::operator+=(size_t n)
+{
+ if (n > _size) {
+ _ptr = nullptr;
+ _size = 0;
+ } else {
+ _ptr = static_cast<const char *>(_ptr) + n;
+ _size -= n;
+ }
+ return *this;
+}
+
+inline const void *
+MemView::begin() const
+{
+ return _ptr;
+}
+inline constexpr const void *
+MemView::ptr() const
+{
+ return _ptr;
+}
+
+inline const void *
+MemView::end() const
+{
+ return static_cast<const char *>(_ptr) + _size;
+}
+
+inline constexpr size_t
+MemView::size() const
+{
+ return _size;
+}
+
+inline MemView &
+MemView::operator=(MemView const &that)
+{
+ _ptr = that._ptr;
+ _size = that._size;
+ return *this;
+}
+
+inline bool
+MemView::contains(const void *p) const
+{
+ return _ptr <= this->begin() && p < this->end();
+}
+
+inline MemView
+MemView::prefix(const void *p) const
+{
+ self zret;
+ if (this->contains(p))
+ zret.setView(_ptr, p);
+ return zret;
+}
+
+inline MemView
+MemView::splitPrefix(const void *p)
+{
+ self zret; // default to empty return.
+ if (this->contains(p)) {
+ zret.setView(_ptr, p);
+ this->setView(p, this->end());
+ }
+ return zret;
+}
+
+inline MemView
+MemView::extractPrefix(const void *p)
+{
+ self zret{this->splitPrefix(p)};
+
+ // For extraction if zret is empty, use up all of @a this
+ if (!zret) {
+ zret = *this;
+ this->clear();
+ }
+
+ return zret;
+}
+
+inline MemView
+MemView::suffix(const void *p) const
+{
+ self zret;
+ if (this->contains(p))
+ zret.setView(p, this->end());
+ return zret;
+}
+
+inline MemView
+MemView::splitSuffix(const void *p)
+{
+ self zret;
+ if (this->contains(p)) {
+ zret.setView(p, this->end());
+ this->setView(_ptr, p);
+ }
+ return zret;
+}
+
+template <typename V>
+inline V
+MemView::at(ssize_t n) const
+{
+ return static_cast<V const *>(_ptr)[n];
+}
+
+template <typename V>
+inline V const *
+MemView::at_ptr(ssize_t n) const
+{
+ return static_cast<V const *>(_ptr) + n;
+}
+
+template <typename V>
+inline const V *
+MemView::find(V v) const
+{
+ 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 *
+MemView::find(char v) const
+{
+ return static_cast<const char *>(memchr(_ptr, v, _size));
+}
+
+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;
+}
+
+// === 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())
+{
+}
+template <size_t N> constexpr StringView::StringView(const char (&s)[N], literal_t) : _ptr(s), _size(N - 1)
+{
+}
+
+template <size_t N> constexpr StringView::StringView(const char (&s)[N], array_t) : _ptr(s), _size(N)
+{
+}
+
+inline void StringView::initDelimiterSet(std::bitset<256> &set)
+{
+ set.reset();
+ for (char c : *this)
+ set[static_cast<uint8_t>(c)] = true;
+}
+
+inline StringView &
+StringView::setView(const char *ptr, size_t n)
+{
+ _ptr = ptr;
+ _size = n;
+ return *this;
+}
+
+inline StringView &
+StringView::setView(const char *ptr, const char *limit)
+{
+ _ptr = ptr;
+ _size = limit - ptr;
+ return *this;
+}
+
+inline StringView &
+StringView::clear()
+{
+ _ptr = 0;
+ _size = 0;
+ return *this;
+}
+
+inline bool
+StringView::operator==(self const &that) const
+{
+ return _size == that._size && _ptr == that._ptr;
+}
+
+inline bool
+StringView::operator!=(self const &that) const
+{
+ return !(*this == that);
+}
+
+inline bool StringView::operator!() const
+{
+ return !(_ptr && _size);
+}
+
+inline StringView::operator bool() const
+{
+ return _ptr && _size;
+}
+
+inline bool
+StringView::isEmpty() const
+{
+ return !(_ptr && _size);
+}
+
+inline char StringView::operator*() const
+{
+ return *_ptr;
+}
+
+inline StringView &StringView::operator++()
+{
+ ++_ptr;
+ --_size;
+ return *this;
+}
+
+inline StringView &
+StringView::operator+=(size_t n)
+{
+ if (n > _size) {
+ _ptr = nullptr;
+ _size = 0;
+ } else {
+ _ptr += n;
+ _size -= n;
+ }
+ return *this;
+}
+
+inline const char *
+StringView::begin() const
+{
+ return _ptr;
+}
+inline constexpr const char *
+StringView::ptr() const
+{
+ return _ptr;
+}
+
+inline const char *
+StringView::end() const
+{
+ return _ptr + _size;
+}
+
+inline constexpr size_t
+StringView::size() const
+{
+ return _size;
+}
+
+inline StringView &
+StringView::operator=(StringView 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
+{
+ return _ptr <= p && p < _ptr + _size;
+}
+
+inline StringView
+StringView::prefix(const char *p) const
+{
+ self zret;
+ if (this->contains(p))
+ zret.setView(_ptr, p);
+ 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)
+{
+ self zret; // default to empty return.
+ if (this->contains(p)) {
+ zret.setView(_ptr, p);
+ this->setView(p + 1, 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)
+{
+ self zret{this->splitPrefix(p)};
+
+ // For extraction if zret is empty, use up all of @a this
+ if (!zret) {
+ zret = *this;
+ this->clear();
+ }
+
+ 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
+{
+ self zret;
+ if (this->contains(p))
+ zret.setView(p + 1, _ptr + _size);
+ 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)
+{
+ self zret;
+ if (this->contains(p)) {
+ zret.setView(p + 1, this->end());
+ this->setView(_ptr, p);
+ }
+ return zret;
+}
+
+inline StringView
+StringView::splitSuffix(char c)
+{
+ return this->splitSuffix(this->find(c));
+}
+
+inline StringView
+StringView::splitSuffix(self delimiters)
+{
+ return this->splitSuffix(this->find(delimiters));
+}
+
+inline StringView
+StringView::splitSuffix(std::function<bool(char)> const &pred)
+{
+ return this->splitSuffix(this->find(pred));
+}
+
+inline const char *
+StringView::find(char c) const
+{
+ return static_cast<const char *>(memchr(_ptr, c, _size));
+}
+
+inline const char *
+StringView::find(self delimiters) const
+{
+ 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)])
+ 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)
+{
+ return ts::memcmp(lhs, rhs);
+}
+
+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];
+
+ 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);
+ }
+
+ extern template void stream_fill(std::ostream &, std::size_t);
+} // detail
+
+template <typename Stream>
+Stream &
+StringView::stream_write(Stream &os, const StringView &b) const
+{
+ 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);
+ }
+ 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
diff --git a/lib/ts/test_MemView.cc b/lib/ts/test_MemView.cc
new file mode 100644
index 0000000..efe7b55
--- /dev/null
+++ b/lib/ts/test_MemView.cc
@@ -0,0 +1,96 @@
+/** @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;
+}
--
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.