From 8add8d8956e9e27ecac8465f38f485da135f5eed Mon Sep 17 00:00:00 2001 From: Sergei Date: Wed, 23 Oct 2024 17:09:55 +0300 Subject: [PATCH] Issue/string view 2 (#145) Co-authored-by: Scott Dixon --- .../suites/unittest/test_pf17_string_view.cpp | 507 ++++++++++++++++++ include/cetl/pf17/cetlpf.hpp | 10 + include/cetl/pf17/string_view.hpp | 404 ++++++++++++++ 3 files changed, 921 insertions(+) create mode 100644 cetlvast/suites/unittest/test_pf17_string_view.cpp create mode 100644 include/cetl/pf17/string_view.hpp diff --git a/cetlvast/suites/unittest/test_pf17_string_view.cpp b/cetlvast/suites/unittest/test_pf17_string_view.cpp new file mode 100644 index 0000000..afd4cab --- /dev/null +++ b/cetlvast/suites/unittest/test_pf17_string_view.cpp @@ -0,0 +1,507 @@ +/// @file +/// Unit tests for string_view.hpp +/// +/// @copyright +/// Copyright (C) OpenCyphal Development Team +/// Copyright Amazon.com Inc. or its affiliates. +/// SPDX-License-Identifier: MIT +/// + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if (__cplusplus >= CETL_CPP_STANDARD_17) +# include +#endif + +using testing::Gt; +using testing::Lt; +using testing::IsNull; + +template +struct TestSpec +{ + using sv_type = StringViewT; + using char_type = CharT; +}; +// +#if (__cplusplus >= CETL_CPP_STANDARD_17) +using TestTypes = testing::Types< // + TestSpec, + TestSpec, wchar_t>, + TestSpec, + TestSpec, wchar_t>>; +#else +using TestTypes = testing::Types< // + TestSpec, + TestSpec, wchar_t>>; +#endif + +template +class TestStringView; +// +template +class TestStringView> : public testing::Test +{ +public: + static char toChar(const char ch) + { + return ch; + } + static std::basic_string toStr(const char* const cstr) + { + return {cstr}; + } +}; +// +template +class TestStringView> : public testing::Test +{ +public: + static wchar_t toChar(const char ch) + { + return ch; + } + static std::basic_string toStr(const char* const cstr) + { + std::array wstr{}; + const auto result = std::mbstowcs(wstr.data(), cstr, wstr.size()); + assert(result == std::strlen(cstr)); + (void) result; + return {wstr.data()}; + } + +}; // TestStringView + +TYPED_TEST_SUITE(TestStringView, TestTypes, ); + +/// TESTS ----------------------------------------------------------------------------------------------------------- + +TYPED_TEST(TestStringView, DefaultConstructor) +{ + using basic_string_view = typename TypeParam::sv_type; + + const basic_string_view sv; + EXPECT_THAT(sv.data(), IsNull()); + EXPECT_THAT(sv.size(), 0u); + EXPECT_TRUE(sv.empty()); +} + +TYPED_TEST(TestStringView, ConstructFromCString) +{ + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello, World!"); + + const char_type* const cstr = test_str.c_str(); + const basic_string_view sv{cstr}; + EXPECT_THAT(sv.data(), cstr); + EXPECT_THAT(sv.size(), test_str.size()); + EXPECT_THAT(sv, TestFixture::toStr("Hello, World!")); +} + +TYPED_TEST(TestStringView, ConstructFromCStringWithLength) +{ + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello, World!"); + + const char_type* cstr = test_str.c_str(); + const basic_string_view sv{cstr, 5}; + EXPECT_THAT(sv.size(), 5u); + EXPECT_THAT(sv, TestFixture::toStr("Hello")); +} + +TYPED_TEST(TestStringView, ConstructFromStdString) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello, World!"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv.data(), test_str.data()); + EXPECT_THAT(sv.size(), test_str.size()); + EXPECT_THAT(sv, test_str); +} + +TYPED_TEST(TestStringView, SizeAndLength) +{ + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Test string"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv.size(), 11u); + EXPECT_THAT(sv.length(), 11u); + EXPECT_THAT(sv.max_size(), + (basic_string_view::npos - sizeof(typename basic_string_view::size_type) - sizeof(void*)) / + sizeof(char_type) / 4); +} + +TYPED_TEST(TestStringView, Empty) +{ + using basic_string_view = typename TypeParam::sv_type; + + const basic_string_view sv1; + EXPECT_TRUE(sv1.empty()); + + const auto test_str2 = TestFixture::toStr(""); + const basic_string_view sv2{test_str2}; + EXPECT_TRUE(sv2.empty()); + + const auto test_str3 = TestFixture::toStr("Non-empty"); + const basic_string_view sv3{test_str3}; + EXPECT_FALSE(sv3.empty()); +} + +TYPED_TEST(TestStringView, ElementAccessOperator) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("abcdef"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv[0], TestFixture::toChar('a')); + EXPECT_THAT(sv[1], TestFixture::toChar('b')); + EXPECT_THAT(sv[5], TestFixture::toChar('f')); +} + +TYPED_TEST(TestStringView, ElementAccessAt) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("abcdef"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv.at(0), TestFixture::toChar('a')); + EXPECT_THAT(sv.at(5), TestFixture::toChar('f')); +#if defined(__cpp_exceptions) + EXPECT_THROW((void) sv.at(6), std::out_of_range); +#endif +} + +TYPED_TEST(TestStringView, FrontBack) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("abcdef"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv.front(), TestFixture::toChar('a')); + EXPECT_THAT(sv.back(), TestFixture::toChar('f')); +} + +TYPED_TEST(TestStringView, Data) +{ + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello, World!"); + + const char_type* const cstr = test_str.c_str(); + const basic_string_view sv{cstr}; + EXPECT_THAT(sv.data(), cstr); +} + +TYPED_TEST(TestStringView, Iterators) +{ + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("abcdef"); + + const basic_string_view sv{test_str}; + std::basic_string str; + for (auto it = sv.begin(); it != sv.end(); ++it) // NOLINT + { + str += *it; + } + EXPECT_THAT(str, TestFixture::toStr("abcdef")); +} + +TYPED_TEST(TestStringView, ConstIterators) +{ + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("abcdef"); + + const basic_string_view sv{test_str}; + std::basic_string str; + for (auto it = sv.cbegin(); it != sv.cend(); ++it) // NOLINT + { + str += *it; + } + EXPECT_THAT(str, TestFixture::toStr("abcdef")); +} + +TYPED_TEST(TestStringView, RemovePrefix) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("abcdef"); + + basic_string_view sv{test_str}; + sv.remove_prefix(2); + EXPECT_THAT(sv, TestFixture::toStr("cdef")); +} + +TYPED_TEST(TestStringView, RemoveSuffix) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("abcdef"); + + basic_string_view sv{test_str}; + sv.remove_suffix(2); + EXPECT_THAT(sv, TestFixture::toStr("abcd")); +} + +TYPED_TEST(TestStringView, Swap) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str1 = TestFixture::toStr("Hello"); + const auto test_str2 = TestFixture::toStr("World"); + + basic_string_view sv1{test_str1}; + basic_string_view sv2{test_str2}; + sv1.swap(sv2); + EXPECT_THAT(sv1, TestFixture::toStr("World")); + EXPECT_THAT(sv2, TestFixture::toStr("Hello")); +} + +TYPED_TEST(TestStringView, SwapNonMember) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str1 = TestFixture::toStr("Hello"); + const auto test_str2 = TestFixture::toStr("World"); + + basic_string_view sv1{test_str1}; + basic_string_view sv2{test_str2}; + swap(sv1, sv2); + EXPECT_THAT(sv1, TestFixture::toStr("World")); + EXPECT_THAT(sv2, TestFixture::toStr("Hello")); +} + +TYPED_TEST(TestStringView, Copy) +{ + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello, World!"); + + const basic_string_view sv{test_str}; + char_type buffer[20] = {}; + const size_t copied = sv.copy(buffer, 5); + EXPECT_THAT(copied, 5u); + EXPECT_THAT(std::basic_string(buffer, copied), TestFixture::toStr("Hello")); +} + +TYPED_TEST(TestStringView, Substr) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello, World!"); + + const basic_string_view sv{test_str}; + const basic_string_view sub = sv.substr(7, 5); + EXPECT_THAT(sub, TestFixture::toStr("World")); +#if defined(__cpp_exceptions) + EXPECT_THROW((void) sv.substr(20), std::out_of_range); +#endif +} + +TYPED_TEST(TestStringView, Compare) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str1 = TestFixture::toStr("abc"); + const auto test_str2 = TestFixture::toStr("abc"); + const auto test_str3 = TestFixture::toStr("abd"); + const auto test_str4 = TestFixture::toStr("abcd"); + + const basic_string_view sv1{test_str1}; + const basic_string_view sv2{test_str2}; + const basic_string_view sv3{test_str3}; + const basic_string_view sv4{test_str4}; + + EXPECT_THAT(sv1.compare(sv2), 0); + EXPECT_THAT(sv1.compare(sv3), Lt(0)); + EXPECT_THAT(sv3.compare(sv1), Gt(0)); + EXPECT_THAT(sv1.compare(sv4), Lt(0)); + EXPECT_THAT(sv4.compare(sv1), Gt(0)); +} + +TYPED_TEST(TestStringView, FindChar) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello, World!"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv.find(TestFixture::toChar('W')), 7u); + EXPECT_THAT(sv.find(TestFixture::toChar('z')), basic_string_view::npos); +} + +TYPED_TEST(TestStringView, FindStringView) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str1 = TestFixture::toStr("Hello, World!"); + const auto test_str2 = TestFixture::toStr("World"); + + const basic_string_view sv{test_str1}; + const basic_string_view to_find{test_str2}; + EXPECT_THAT(sv.find(to_find), 7u); + EXPECT_THAT(sv.find(TestFixture::toStr("")), 0u); + EXPECT_THAT(sv.find(TestFixture::toStr("Earth")), basic_string_view::npos); + EXPECT_THAT(sv.find(TestFixture::toStr("too long too long too long")), basic_string_view::npos); +} + +TYPED_TEST(TestStringView, RelationalOperators) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str1 = TestFixture::toStr("abc"); + const auto test_str2 = TestFixture::toStr("abc"); + const auto test_str3 = TestFixture::toStr("abd"); + + const basic_string_view sv1{test_str1}; + const basic_string_view sv2{test_str2}; + const basic_string_view sv3{test_str3}; + + EXPECT_TRUE(sv1 == sv2); + EXPECT_FALSE(sv1 != sv2); + EXPECT_TRUE(sv1 < sv3); + EXPECT_TRUE(sv3 > sv1); + EXPECT_TRUE(sv1 <= sv2); + EXPECT_TRUE(sv1 >= sv2); +} + +TYPED_TEST(TestStringView, FindOutOfBounds) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv.find(TestFixture::toChar('H'), 10), basic_string_view::npos); + EXPECT_THAT(sv.find(TestFixture::toStr("He"), 10), basic_string_view::npos); +} + +TYPED_TEST(TestStringView, SubstrWithNpos) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello, World!"); + + const basic_string_view sv{test_str}; + const basic_string_view sub = sv.substr(7); + EXPECT_THAT(sub, TestFixture::toStr("World!")); +} + +TYPED_TEST(TestStringView, EmptyStringViewOperations) +{ + using basic_string_view = typename TypeParam::sv_type; + + const basic_string_view sv; + EXPECT_THAT(sv.size(), 0u); + EXPECT_TRUE(sv.empty()); + EXPECT_THAT(sv.data(), IsNull()); + EXPECT_THAT(sv.begin(), sv.end()); +} + +TYPED_TEST(TestStringView, ComparisonWithString) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv, test_str); + EXPECT_THAT(test_str, sv); +} + +TYPED_TEST(TestStringView, ComparisonWithCString) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv, test_str.c_str()); + EXPECT_THAT(test_str.c_str(), sv); +} + +TYPED_TEST(TestStringView, FindPartial) +{ + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("ababab"); + + const basic_string_view sv{test_str}; + EXPECT_THAT(sv.find(TestFixture::toStr("aba")), 0u); + EXPECT_THAT(sv.find(TestFixture::toStr("aba"), 1), 2u); +} + +TYPED_TEST(TestStringView, CopyOutOfBounds) +{ +#if defined(__cpp_exceptions) + + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Hello"); + + const basic_string_view sv{test_str}; + char_type buffer[10]; + EXPECT_THROW(sv.copy(buffer, 5, 6), std::out_of_range); + +#else + GTEST_SKIP() << "Not applicable when exceptions are disabled."; +#endif +} + +TYPED_TEST(TestStringView, stream_operator) +{ + using char_type = typename TypeParam::char_type; + using basic_string_view = typename TypeParam::sv_type; + + const auto test_str = TestFixture::toStr("Test"); + { + std::basic_stringstream ss; + ss << basic_string_view{test_str} << basic_string_view{test_str}; + EXPECT_THAT(ss.str(), TestFixture::toStr("TestTest")); + } + { + std::basic_stringstream ss; + ss << std::setw(9) << std::setfill(TestFixture::toChar('-')) << std::left << basic_string_view{test_str}; + EXPECT_THAT(ss.str(), TestFixture::toStr("Test-----")); + } + { + std::basic_stringstream ss; + ss << std::setw(9) << std::setfill(TestFixture::toChar('-')) << std::right << basic_string_view{test_str}; + EXPECT_THAT(ss.str(), TestFixture::toStr("-----Test")); + } + { + std::basic_stringstream ss; + ss << std::setw(2) << basic_string_view{test_str}; + EXPECT_THAT(ss.str(), test_str); + } +} diff --git a/include/cetl/pf17/cetlpf.hpp b/include/cetl/pf17/cetlpf.hpp index b52fee8..22a1ffd 100644 --- a/include/cetl/pf17/cetlpf.hpp +++ b/include/cetl/pf17/cetlpf.hpp @@ -42,6 +42,7 @@ # include # include # include +# include namespace cetl { @@ -113,6 +114,10 @@ using std::get_if; using std::visit; using std::holds_alternative; +// string_view +using std::basic_string_view; +using std::string_view; + } // namespace cetl #else @@ -120,6 +125,7 @@ using std::holds_alternative; # include "cetl/pf17/utility.hpp" # include "cetl/pf17/optional.hpp" # include "cetl/pf17/variant.hpp" +# include "cetl/pf17/string_view.hpp" namespace cetl { @@ -196,6 +202,10 @@ using cetl::pf17::holds_alternative; using cetl::pf17::bad_variant_access; # endif +// string_view +using cetl::pf17::basic_string_view; +using cetl::pf17::string_view; + } // namespace cetl #endif diff --git a/include/cetl/pf17/string_view.hpp b/include/cetl/pf17/string_view.hpp new file mode 100644 index 0000000..9c4ca08 --- /dev/null +++ b/include/cetl/pf17/string_view.hpp @@ -0,0 +1,404 @@ +/// @file +/// Defines the C++17 std::variant type and several related entities. +/// @copyright +/// Copyright (C) OpenCyphal Development Team +/// Copyright Amazon.com Inc. or its affiliates. +/// SPDX-License-Identifier: MIT + +#ifndef CETL_PF17_STRING_VIEW_HPP_INCLUDED +#define CETL_PF17_STRING_VIEW_HPP_INCLUDED + +#include +#include +#include +#include // We need this even if exceptions are disabled for std::terminate. +#include +#include +#include + +namespace cetl +{ +namespace pf17 +{ + +/// The class template basic_string_view describes an object that can refer to a constant contiguous sequence of CharT +/// with the first element of the sequence at position zero. +/// +template > +class basic_string_view +{ +public: + // types + using traits_type = Traits; + using value_type = CharT; + using pointer = CharT*; + using const_pointer = const CharT*; + using reference = CharT&; + using const_reference = const CharT&; + using const_iterator = const CharT*; + using iterator = const_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + enum : size_type + { + npos = static_cast(-1) + }; + + /// Default constructor. Constructs an empty basic_string_view. After construction, data() is equal to nullptr, + /// and size() is equal to 0. + constexpr basic_string_view() noexcept + : data_{nullptr} + , size_{0} + { + } + + /// Constructs a view of the null-terminated character string pointed to by s, not including the terminating null + /// character. + /// + /// No lint and Sonar cpp:S1709 b/c this is an intentional implicit conversion. + /// + /// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) + basic_string_view(const CharT* const str) // NOSONAR cpp:S1709 + : data_{str} + , size_{traits_type::length(str)} + { + } + + /// No lint and Sonar cpp:S1709 b/c this is an intentional implicit conversion. + /// + /// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) + template + basic_string_view(const std::basic_string& str) // NOSONAR cpp:S1709 + : data_{str.data()} + , size_{str.size()} + { + } + + /// Constructs a view of the first count characters of the character array starting with the element pointed by + /// str. str can contain null characters. + /// + constexpr basic_string_view(const CharT* const str, const size_type size) + : data_{str} + , size_{size} + { + } + + /// basic_string_view cannot be constructed from nullptr. + /// + constexpr basic_string_view(std::nullptr_t) = delete; + + /// Returns the number of CharT elements in the view, i.e. `std::distance(begin(), end())`. + /// + constexpr size_type size() const noexcept + { + return size_; + } + + /// Returns the number of CharT elements in the view, i.e. `std::distance(begin(), end())`. + /// + constexpr size_type length() const noexcept + { + return size_; + } + + /// The largest possible number of char-like objects that can be referred to by a basic_string_view. + /// + constexpr size_type max_size() const noexcept + { + return (npos - sizeof(size_type) - sizeof(void*)) / sizeof(value_type) / 4; + } + + /// Checks if the view has no characters, i.e. whether `size() == 0`. + /// + constexpr bool empty() const noexcept + { + return size_ == 0; + } + + /// Returns a const reference to the character at specified location `pos`. + /// + constexpr const_reference operator[](const size_type pos) const + { + return data_[pos]; + } + + /// Returns a const reference to the character at specified location `pos`. + /// + /// Bounds checking is performed, exception of type `std::out_of_range` will be thrown on invalid access. + /// + constexpr const_reference at(const size_type pos) const + { + if (pos >= size_) + { +#if defined(__cpp_exceptions) + throw std::out_of_range("basic_string_view::at"); +#else + std::terminate(); +#endif + } + return data_[pos]; + } + + /// Returns reference to the first character in the view. The behavior is undefined if `empty() == true`. + /// + constexpr const_reference front() const + { + return data_[0]; + } + + /// Returns reference to the last character in the view. The behavior is undefined if `empty() == true`. + /// + constexpr const_reference back() const + { + return data_[size_ - 1]; + } + + /// Returns a pointer to the underlying character array. + /// + /// The pointer is such that the range `[data(), data() + size())` is valid and the values in it correspond to the + /// values of the view. + /// + constexpr const_pointer data() const noexcept + { + return data_; + } + + /// Returns an iterator to the first character of the view. + /// + constexpr const_iterator begin() const noexcept + { + return data_; + } + + /// Returns an iterator to the character following the last character of the view. + /// + /// This character acts as a placeholder, attempting to access it results in undefined behavior. + /// + constexpr const_iterator end() const noexcept + { + return data_ + size_; + } + + /// Returns a constant iterator to the first character of the view. + /// + constexpr const_iterator cbegin() const noexcept + { + return data_; + } + + /// Returns a constant iterator to the character following the last character of the view. + /// + /// This character acts as a placeholder, attempting to access it results in undefined behavior. + /// + constexpr const_iterator cend() const noexcept + { + return data_ + size_; + } + + /// Moves the start of the view forward by n characters. + /// + /// The behavior is undefined if `n > size()`. + /// + constexpr void remove_prefix(const size_type n) + { + const size_type to_remove = std::min(n, size_); + data_ += to_remove; + size_ -= to_remove; + } + + /// Moves the end of the view back by n characters. + /// + /// The behavior is undefined if `n > size()`. + /// + constexpr void remove_suffix(const size_type n) + { + const size_type to_remove = std::min(n, size_); + size_ -= to_remove; + } + + /// Exchanges the view with that of `sv`. + /// + constexpr void swap(basic_string_view& sv) noexcept + { + const CharT* const tmp_data = data_; + const size_type tmp_size = size_; + data_ = sv.data_; + size_ = sv.size_; + sv.data_ = tmp_data; + sv.size_ = tmp_size; + } + + /// Copies the substring [pos, pos + rcount) to the character array pointed to by dest, where rcount is the smaller + /// of count and size() - pos. + /// + /// Equivalent to `Traits::copy(dest, data() + pos, rcount)`. + /// + size_type copy(CharT* const dest, const size_type count, const size_type pos = 0) const + { + if (pos > size_) + { +#if defined(__cpp_exceptions) + throw std::out_of_range("basic_string_view::copy"); +#else + std::terminate(); +#endif + } + const size_type rcount = std::min(count, size_ - pos); + std::copy_n(data_ + pos, rcount, dest); + return rcount; + } + + /// Returns a view of the substring `[pos, pos + rlen)`, where `rlen` is the smaller of `count` and `size() - pos`. + /// + constexpr basic_string_view substr(const size_type pos = 0, const size_type count = npos) const + { + if (pos > size_) + { +#if defined(__cpp_exceptions) + throw std::out_of_range("basic_string_view::substr"); +#else + std::terminate(); +#endif + } + const size_type rcount = std::min(count, size_ - pos); + return basic_string_view(data_ + pos, rcount); + } + + /// Compares two character sequences. + /// + /// @return Negative value if this view is less than the other character sequence, zero if the both character + /// sequences are equal, positive value if this view is greater than the other character sequence. + /// + int compare(const basic_string_view sv) const noexcept + { + const size_type rlen = std::min(size_, sv.size_); + const int cmp = traits_type::compare(data_, sv.data_, rlen); + if (cmp != 0) + { + return cmp; + } + if (size_ == sv.size_) + { + return 0; + } + return size_ < sv.size_ ? -1 : 1; + } + + /// Compares two views. + /// + friend bool operator==(const basic_string_view lhs, const basic_string_view rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(const basic_string_view lhs, const basic_string_view rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + friend bool operator<(const basic_string_view lhs, const basic_string_view rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + friend bool operator>(const basic_string_view lhs, const basic_string_view rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + friend bool operator<=(const basic_string_view lhs, const basic_string_view rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + friend bool operator>=(const basic_string_view lhs, const basic_string_view rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + + /// Finds the first substring equal to the given character. + /// + constexpr size_type find(const CharT ch, const size_type pos = 0) const noexcept + { + if (pos >= size_) + { + return npos; + } + const const_iterator result = std::find(data_ + pos, data_ + size_, ch); + return result != data_ + size_ ? static_cast(result - data_) : npos; + } + + /// Finds the first substring equal to the given character sequence. + /// + constexpr size_type find(const basic_string_view sv, const size_type pos = 0) const noexcept + { + if (pos > size_ || sv.size_ > size_ - pos) + { + return npos; + } + const const_iterator result = std::search(data_ + pos, data_ + size_, sv.data_, sv.data_ + sv.size_); + return result != data_ + size_ ? static_cast(result - data_) : npos; + } + + /// Performs stream output on string view. + /// + friend std::basic_ostream& operator<<(std::basic_ostream& os, + const basic_string_view& sv) + { + const auto sv_length = static_cast(sv.length()); + + typename std::basic_ostream::sentry s{os}; + if (s) + { + const std::streamsize width = os.width(); + + if (sv_length < width) + { + auto pad = [&os, fill = os.fill()](std::streamsize padding_width) { + while (padding_width--) + { + os.put(fill); + } + }; + + const std::streamsize padding_len = width - sv_length; + const bool left = os.flags() & std::basic_ios::left; + const bool right = os.flags() & std::basic_ios::right; + + if (right) + { + pad(padding_len); + } + os.write(sv.data(), sv_length); + if (left) + { + pad(padding_len); + } + } + else + { + os.write(sv.data(), sv_length); + } + } + os.width(0); + return os; + } + +private: + const CharT* data_; + size_type size_; + +}; // basic_string_view + +// non-member functions + +/// Exchanges the given string views. +/// +template +void swap(basic_string_view& lhs, basic_string_view& rhs) noexcept +{ + lhs.swap(rhs); +} + +using string_view = basic_string_view; + +} // namespace pf17 +} // namespace cetl + +#endif // CETL_PF17_STRING_VIEW_HPP_INCLUDED