diff options
author | Stephan Bergmann <sbergman@redhat.com> | 2017-04-07 15:59:12 +0200 |
---|---|---|
committer | Michael Stahl <michael.stahl@allotropia.de> | 2022-03-10 13:50:34 +0100 |
commit | f1843592b03b111daeb446e410b606e170dfa67e (patch) | |
tree | a99389fcfde1a057ccc40862c764e0662339eaa6 | |
parent | 06e4c6fd51f60f1f7d474ede321fe031cde0b3ca (diff) |
Introduce o3tl::string_view.hxx approximation of C++17 <string_view>
...and use it in configmgr/source/writemodfile.hxx
Change-Id: Ie683dc21010ed45cc454ff89bea0376994b351f2
Reviewed-on: https://gerrit.libreoffice.org/36270
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
(cherry picked from commit 50057a37a877213d935958d5c643fde1434d680c)
-rw-r--r-- | config_host/config_global.h.in | 1 | ||||
-rw-r--r-- | include/o3tl/string_view.hxx | 867 | ||||
-rw-r--r-- | o3tl/CppunitTest_o3tl_tests.mk | 2 | ||||
-rw-r--r-- | o3tl/qa/test-string_view.cxx | 212 | ||||
-rw-r--r-- | solenv/clang-format/blacklist | 2 |
5 files changed, 1083 insertions, 1 deletions
diff --git a/config_host/config_global.h.in b/config_host/config_global.h.in index 5b04594c12f5..4a4998e77aa5 100644 --- a/config_host/config_global.h.in +++ b/config_host/config_global.h.in @@ -12,6 +12,7 @@ Any change in this header will cause a rebuild of almost everything. #ifndef CONFIG_GLOBAL_H #define CONFIG_GLOBAL_H +#define HAVE_CXX14_CONSTEXPR 0 #define HAVE_GCC_BUILTIN_ATOMIC 0 #define HAVE_GCC_BUILTIN_FFS 0 /* _Pragma */ diff --git a/include/o3tl/string_view.hxx b/include/o3tl/string_view.hxx new file mode 100644 index 000000000000..9230909b7edf --- /dev/null +++ b/include/o3tl/string_view.hxx @@ -0,0 +1,867 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_O3TL_STRING_VIEW_HXX +#define INCLUDED_O3TL_STRING_VIEW_HXX + +#include <sal/config.h> + +#include <algorithm> +#include <cstddef> +#include <ios> +#include <iterator> +#include <ostream> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> + +#include <config_global.h> +#include <rtl/string.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +// An approximation of C++17 <string_view>, including implicit conversion +// from rtl::OString and rtl::OUString. + +namespace o3tl { + +namespace detail { + +template<typename T> struct CharPtrDetector { + static constexpr bool ok = false; +}; + +template<> struct CharPtrDetector<char *> { + static constexpr bool ok = true; +}; + +template<> struct CharPtrDetector<char const *> { + static constexpr bool ok = true; +}; + +template<typename T> struct NonConstCharArrayDetector { + static constexpr bool ok = false; +}; + +template<std::size_t N> struct NonConstCharArrayDetector<char [N]> { + static constexpr bool ok = true; +}; + +template<typename T> struct ConstCharArrayDetector { + static constexpr bool ok = false; +}; + +template<std::size_t N> struct ConstCharArrayDetector<char const[N]> { + static constexpr bool ok = true; + static constexpr std::size_t length = N - 1; +}; + +template<typename T> struct Char16PtrDetector { + static constexpr bool ok = false; +}; + +template<> struct Char16PtrDetector<char16_t *> { + static constexpr bool ok = true; +}; + +template<> struct Char16PtrDetector<char16_t const *> { + static constexpr bool ok = true; +}; + +template<typename T> struct NonConstChar16ArrayDetector { + static constexpr bool ok = false; +}; + +template<std::size_t N> struct NonConstChar16ArrayDetector<char16_t [N]> { + static constexpr bool ok = true; +}; + +template<typename T> struct ConstChar16ArrayDetector { + static constexpr bool ok = false; +}; + +template<std::size_t N> struct ConstChar16ArrayDetector<char16_t const[N]> { + static constexpr bool ok = true; + static constexpr std::size_t length = N - 1; +}; + +template<typename T> struct Char32PtrDetector { + static constexpr bool ok = false; +}; + +template<> struct Char32PtrDetector<char32_t *> { + static constexpr bool ok = true; +}; + +template<> struct Char32PtrDetector<char32_t const *> { + static constexpr bool ok = true; +}; + +template<typename T> struct NonConstChar32ArrayDetector { + static constexpr bool ok = false; +}; + +template<std::size_t N> struct NonConstChar32ArrayDetector<char32_t [N]> { + static constexpr bool ok = true; +}; + +template<typename T> struct ConstChar32ArrayDetector { + static constexpr bool ok = false; +}; + +template<std::size_t N> struct ConstChar32ArrayDetector<char32_t const[N]> { + static constexpr bool ok = true; + static constexpr std::size_t length = N - 1; +}; + +template<typename T> struct WcharPtrDetector { + static constexpr bool ok = false; +}; + +template<> struct WcharPtrDetector<wchar_t *> { + static constexpr bool ok = true; +}; + +template<> struct WcharPtrDetector<wchar_t const *> { + static constexpr bool ok = true; +}; + +template<typename T> struct NonConstWcharArrayDetector { + static constexpr bool ok = false; +}; + +template<std::size_t N> struct NonConstWcharArrayDetector<wchar_t [N]> { + static constexpr bool ok = true; +}; + +template<typename T> struct ConstWcharArrayDetector { + static constexpr bool ok = false; +}; + +template<std::size_t N> struct ConstWcharArrayDetector<wchar_t const[N]> { + static constexpr bool ok = true; + static constexpr std::size_t length = N - 1; +}; + +} + +#if defined _MSC_VER +#pragma warning(push, 1) +#pragma warning(disable: 4814) // in C++14 'constexpr' will not imply 'const' +#endif + +template<typename charT, typename traits = std::char_traits<charT>> +class basic_string_view { +public: + using traits_type = traits; + using value_type = charT; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; + using const_iterator = const_pointer; + using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using reverse_iterator = const_reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + static constexpr size_type npos = size_type(-1); + + constexpr basic_string_view() noexcept: data_(nullptr), size_(0) {} + + constexpr basic_string_view(basic_string_view const &) noexcept = default; + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + basic_string_view & operator =(basic_string_view const & other) noexcept +#if defined _MSC_VER && _MSC_VER <= 1900 && !defined __clang__ + { + data_ = other.data_; + size_ = other.size_; + return *this; + } +#else + = default; +#endif + + // The various character types are handled below in the "LO specifics, to + // make up for traits::length not necessarily being constexpr yet for + // literal arguments" section: + template<typename T = charT> constexpr basic_string_view( + charT const * str, + typename std::enable_if< + !(std::is_same<T, char>::value || std::is_same<T, char16_t>::value + || std::is_same<T, char32_t>::value + || std::is_same<T, wchar_t>::value), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(str), size_(traits::length(str)) {} + + constexpr basic_string_view(charT const * str, size_type len): + data_(str), size_(len) {} + + constexpr const_iterator begin() const noexcept { return data_; } + + constexpr const_iterator end() const noexcept { return begin() + size(); } + + constexpr const_iterator cbegin() const noexcept { return begin(); } + + constexpr const_iterator cend() const noexcept { return end(); } + + constexpr const_reverse_iterator rbegin() const noexcept + { return const_reverse_iterator(end()); } + + constexpr const_reverse_iterator rend() const noexcept + { return const_reverse_iterator(begin()); } + + constexpr const_reverse_iterator crbegin() const noexcept + { return rbegin(); } + + constexpr const_reverse_iterator crend() const noexcept { return rend(); } + + constexpr size_type size() const noexcept { return size_; } + + constexpr size_type length() const noexcept { return size(); } + +#if !defined __clang__ || HAVE_CXX14_CONSTEXPR + constexpr +#endif + size_type max_size() const noexcept { +#if defined __clang__ // avoid constexpr issues with other, older compilers + (void) this; // loplugin:staticmethods +#endif + return npos - 1; + } + + constexpr bool empty() const noexcept { return size_ == 0; } + + constexpr const_reference operator [](size_type pos) const { +#if HAVE_CXX14_CONSTEXPR + assert(pos < size()); +#endif + return data_[pos]; + } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + const_reference at(size_type pos) const { + if (pos >= size()) { + throw std::out_of_range("o3tl::basic_string_view::at"); + } + return operator [](pos); + } + + constexpr const_reference front() const { +#if HAVE_CXX14_CONSTEXPR + assert(!empty()); +#endif + return operator [](0); + } + + constexpr const_reference back() const { +#if HAVE_CXX14_CONSTEXPR + assert(!empty()); +#endif + return operator [](size() - 1); + } + + constexpr const_pointer data() const noexcept { return data_; } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + void remove_prefix(size_type n) { + assert(n <= size()); + data_ += n; + size_ -= n; + } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + void remove_suffix(size_type n) { + assert(n <= size()); + size_ -= n; + } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + void swap(basic_string_view & s) noexcept { + std::swap(data_, s.data_); + std::swap(size_, s.size_); + } + + size_type copy(charT * s, size_type n, size_type pos = 0) const { + if (pos > size()) { + throw std::out_of_range("o3tl::basic_string_view::copy"); + } + auto rlen = std::min(n, size_type(size() - pos)); + traits::copy(s, data() + pos, rlen); + return rlen; + } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + basic_string_view substr(size_type pos = 0, size_type n = npos) const { + if (pos > size()) { + throw std::out_of_range("o3tl::basic_string_view::copy"); + } + return basic_string_view( + data() + pos, std::min(n, size_type(size() - pos))); + } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + int compare(basic_string_view s) const noexcept { + auto n = traits::compare(data(), s.data(), std::min(size(), s.size())); + return n == 0 + ? (size() < s.size() ? -1 : size() == s.size() ? 0 : 1) : n; + } + + constexpr int compare(size_type pos1, size_type n1, basic_string_view s) + const + { return substr(pos1, n1).compare(s); } + + constexpr int compare( + size_type pos1, size_type n1, basic_string_view s, size_type pos2, + size_type n2) const + { return substr(pos1, n1).compare(s.substr(pos2, n2)); } + + constexpr int compare(charT const * s) const + { return compare(basic_string_view(s)); } + + constexpr int compare(size_type pos1, size_type n1, charT const * s) const + { return substr(pos1, n1).compare(s); } + + constexpr int compare( + size_type pos1, size_type n1, charT const * s, size_type n2) const + { return substr(pos1, n1).compare(basic_string_view(s, n2)); } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + size_type find(basic_string_view s, size_type pos = 0) const noexcept { + if (s.size() <= size()) { + for (auto xpos = pos; xpos <= size() - s.size(); ++xpos) { + bool match = true; + for (size_type i = 0; i != s.size(); ++i) { + if (!traits::eq(data_[xpos + i], s.data_[i])) { + match = false; + break; + } + } + if (match) { + return xpos; + } + } + } + return npos; + } + + constexpr size_type find(charT c, size_type pos = 0) const noexcept + { return find(basic_string_view(&c, 1), pos); } + + constexpr size_type find(charT const * s, size_type pos, size_type n) const + { return find(basic_string_view(s, n), pos); } + + constexpr size_type find(charT const * s, size_type pos = 0) const + { return find(basic_string_view(s), pos); } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + size_type rfind(basic_string_view s, size_type pos = npos) const noexcept { + if (s.size() <= size()) { + for (auto xpos = std::min<size_type>(size() - s.size(), pos);; + --xpos) + { + bool match = true; + for (size_type i = 0; i != s.size(); ++i) { + if (!traits::eq(data_[xpos + i], s.data_[i])) { + match = false; + break; + } + } + if (match) { + return xpos; + } + if (xpos == 0) { + break; + } + } + } + return npos; + } + + constexpr size_type rfind(charT c, size_type pos = npos) const noexcept + { return rfind(basic_string_view(&c, 1), pos); } + + constexpr size_type rfind(charT const * s, size_type pos, size_type n) const + { return rfind(basic_string_view(s, n), pos); } + + constexpr size_type rfind(charT const * s, size_type pos = npos) const + { return rfind(basic_string_view(s), pos); } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + size_type find_first_of(basic_string_view s, size_type pos = 0) const + noexcept + { + for (auto xpos = pos; xpos < size(); ++xpos) { + for (size_type i = 0; i != s.size(); ++i) { + if (traits::eq(data_[xpos], s.data_[i])) { + return xpos; + } + } + } + return npos; + } + + constexpr size_type find_first_of(charT c, size_type pos = 0) const noexcept + { return find_first_of(basic_string_view(&c, 1), pos); } + + constexpr size_type find_first_of( + charT const * s, size_type pos, size_type n) const + { return find_first_of(basic_string_view(s, n), pos); } + + constexpr size_type find_first_of(charT const * s, size_type pos = 0) const + { return find_first_of(basic_string_view(s), pos); } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + size_type find_last_of(basic_string_view s, size_type pos = npos) const + noexcept + { + if (!empty()) { + for (auto xpos = std::min<size_type>(size() - 1, pos);; --xpos) { + for (size_type i = 0; i != s.size(); ++i) { + if (traits::eq(data_[xpos], s.data_[i])) { + return xpos; + } + } + if (xpos == 0) { + break; + } + } + } + return npos; + } + + constexpr size_type find_last_of(charT c, size_type pos = npos) const + noexcept + { return find_last_of(basic_string_view(&c, 1), pos); } + + constexpr size_type find_last_of( + charT const * s, size_type pos, size_type n) const + { return find_last_of(basic_string_view(s, n), pos); } + + constexpr size_type find_last_of(charT const * s, size_type pos = npos) + const + { return find_last_of(basic_string_view(s), pos); } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + size_type find_first_not_of(basic_string_view s, size_type pos = 0) const + noexcept + { + for (auto xpos = pos; xpos < size(); ++xpos) { + bool match = true; + for (size_type i = 0; i != s.size(); ++i) { + if (traits::eq(data_[xpos], s.data_[i])) { + match = false; + break; + } + } + if (match) { + return xpos; + } + } + return npos; + } + + constexpr size_type find_first_not_of(charT c, size_type pos = 0) const + noexcept + { return find_first_not_of(basic_string_view(&c, 1), pos); } + + constexpr size_type find_first_not_of( + charT const * s, size_type pos, size_type n) const + { return find_first_not_of(basic_string_view(s, n), pos); } + + constexpr size_type find_first_not_of(charT const * s, size_type pos = 0) + const + { return find_first_not_of(basic_string_view(s), pos); } + +#if HAVE_CXX14_CONSTEXPR + constexpr +#endif + size_type find_last_not_of(basic_string_view s, size_type pos = npos) const + noexcept + { + if (!empty()) { + for (auto xpos = std::min<size_type>(size() - 1, pos);; --xpos) { + bool match = true; + for (size_type i = 0; i != s.size(); ++i) { + if (traits::eq(data_[xpos], s.data_[i])) { + match = false; + break; + } + } + if (match) { + return xpos; + } + if (xpos == 0) { + break; + } + } + } + return npos; + } + + constexpr size_type find_last_not_of(charT c, size_type pos = npos) const + noexcept + { return find_last_not_of(basic_string_view(&c, 1), pos); } + + constexpr size_type find_last_not_of( + charT const * s, size_type pos, size_type n) const + { return find_last_not_of(basic_string_view(s, n), pos); } + + constexpr size_type find_last_not_of(charT const * s, size_type pos = npos) + const + { return find_last_not_of(basic_string_view(s), pos); } + + // LO specifics: + + // For std::basic_string_view, this is provided via a non-explicit + // conversion operator from std::basic_string: + constexpr basic_string_view(std::basic_string<charT, traits> const & s): + data_(s.data()), size_(s.size()) {} + + // For std::string_view, this will be provided by a (LIBO_INTERNAL_ONLY) + // non-explicit conversion operator from rtl::OString: + template<typename T = charT> basic_string_view( + OString const & s, + typename std::enable_if< + std::is_same<T, char>::value, + rtl::libreoffice_internal::Dummy>::type = {}): + data_(s.getStr()), size_(s.getLength()) {} + + // For std::u16string_view, this will be provided by a (LIBO_INTERNAL_ONLY) + // non-explicit conversion operator from rtl::OUString: + template<typename T = charT> basic_string_view( + OUString const & s, + typename std::enable_if< + std::is_same<T, sal_Unicode>::value, + rtl::libreoffice_internal::Dummy>::type = {}): + data_(s.getStr()), size_(s.getLength()) {} + + // For std::u16string_view, this would either be provided by a + // (LIBO_INTERNAL_ONLY) non-explicit conversion operator from + // rtl::OUStringLiteral, or rtl::OUStringLiteral would be given up in favor + // of std::u16string_view anyway (but this constructor also serves to reject + // as ambiguous construction of a o3tl::u16string_view from a narrow string + // literal, which would otherwise go via the above rtl::OUString + // constructor): + template<typename T = charT> constexpr basic_string_view( + OUStringLiteral literal, + typename std::enable_if< + std::is_same<T, sal_Unicode>::value, + rtl::libreoffice_internal::Dummy>::type = {}): + data_(literal.data), size_(literal.size) {} + + // LO specifics, to make up for traits::length not necessarily being + // constexpr yet for literal arguments: + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 const & value, + typename std::enable_if< + std::is_same<T2, char>::value && detail::CharPtrDetector<T1>::ok, + rtl::libreoffice_internal::Dummy>::type = {}): + data_(value), size_(traits::length(value)) {} + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 & value, + typename std::enable_if< + (std::is_same<T2, char>::value + && detail::NonConstCharArrayDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(value), size_(traits::length(value)) {} + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 & literal, + typename std::enable_if< + (std::is_same<T2, char>::value + && detail::ConstCharArrayDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(literal), size_(detail::ConstCharArrayDetector<T1>::length) + { /*assert(size_ == traits::length(literal);*/ } + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 const & value, + typename std::enable_if< + (std::is_same<T2, char16_t>::value + && detail::Char16PtrDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(value), size_(traits::length(value)) {} + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 & value, + typename std::enable_if< + (std::is_same<T2, char16_t>::value + && detail::NonConstChar16ArrayDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(value), size_(traits::length(value)) {} + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 & literal, + typename std::enable_if< + (std::is_same<T2, char16_t>::value + && detail::ConstChar16ArrayDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(literal), size_(detail::ConstChar16ArrayDetector<T1>::length) + { /*assert(size_ == traits::length(literal);*/ } + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 const & value, + typename std::enable_if< + (std::is_same<T2, char32_t>::value + && detail::Char32PtrDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(value), size_(traits::length(value)) {} + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 & value, + typename std::enable_if< + (std::is_same<T2, char32_t>::value + && detail::NonConstChar32ArrayDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(value), size_(traits::length(value)) {} + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 & literal, + typename std::enable_if< + (std::is_same<T2, char32_t>::value + && detail::ConstChar32ArrayDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(literal), size_(detail::ConstChar32ArrayDetector<T1>::length) + { /*assert(size_ == traits::length(literal);*/ } + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 const & value, + typename std::enable_if< + (std::is_same<T2, wchar_t>::value + && detail::WcharPtrDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(value), size_(traits::length(value)) {} + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 & value, + typename std::enable_if< + (std::is_same<T2, wchar_t>::value + && detail::NonConstWcharArrayDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(value), size_(traits::length(value)) {} + + template<typename T1, typename T2 = charT> constexpr basic_string_view( + T1 & literal, + typename std::enable_if< + (std::is_same<T2, wchar_t>::value + && detail::ConstWcharArrayDetector<T1>::ok), + rtl::libreoffice_internal::Dummy>::type = {}): + data_(literal), size_(detail::ConstWcharArrayDetector<T1>::length) + { /*assert(size_ == traits::length(literal);*/ } + +private: + const_pointer data_; + size_type size_; +}; + +template<class charT, class traits> constexpr bool operator ==( + basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) == 0; } + +template<class charT, class traits> constexpr bool operator ==( + basic_string_view<charT, traits> x, + typename std::decay<basic_string_view<charT, traits>>::type y) + noexcept +{ return x.compare(y) == 0; } + +template<class charT, class traits> constexpr bool operator ==( + typename std::decay<basic_string_view<charT, traits>>::type x, + basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) == 0; } + +template<class charT, class traits> constexpr bool operator !=( + basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) != 0; } + +template<class charT, class traits> constexpr bool operator !=( + basic_string_view<charT, traits> x, + typename std::decay<basic_string_view<charT, traits>>::type y) + noexcept +{ return x.compare(y) != 0; } + +template<class charT, class traits> constexpr bool operator !=( + typename std::decay<basic_string_view<charT, traits>>::type x, + basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) != 0; } + +template<class charT, class traits> constexpr bool operator <( + basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) < 0; } + +template<class charT, class traits> constexpr bool operator <( + basic_string_view<charT, traits> x, + typename std::decay<basic_string_view<charT, traits>>::type y) + noexcept +{ return x.compare(y) < 0; } + +template<class charT, class traits> constexpr bool operator <( + typename std::decay<basic_string_view<charT, traits>>::type x, + basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) < 0; } + +template<class charT, class traits> constexpr bool operator >( + basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) > 0; } + +template<class charT, class traits> constexpr bool operator >( + basic_string_view<charT, traits> x, + typename std::decay<basic_string_view<charT, traits>>::type y) + noexcept +{ return x.compare(y) > 0; } + +template<class charT, class traits> constexpr bool operator >( + typename std::decay<basic_string_view<charT, traits>>::type x, + basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) > 0; } + +template<class charT, class traits> constexpr bool operator <=( + basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) <= 0; } + +template<class charT, class traits> constexpr bool operator <=( + basic_string_view<charT, traits> x, + typename std::decay<basic_string_view<charT, traits>>::type y) + noexcept +{ return x.compare(y) <= 0; } + +template<class charT, class traits> constexpr bool operator <=( + typename std::decay<basic_string_view<charT, traits>>::type x, + basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) <= 0; } + +template<class charT, class traits> constexpr bool operator >=( + basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) >= 0; } + +template<class charT, class traits> constexpr bool operator >=( + basic_string_view<charT, traits> x, + typename std::decay<basic_string_view<charT, traits>>::type y) + noexcept +{ return x.compare(y) >= 0; } + +template<class charT, class traits> constexpr bool operator >=( + typename std::decay<basic_string_view<charT, traits>>::type x, + basic_string_view<charT, traits> y) + noexcept +{ return x.compare(y) >= 0; } + +template<class charT, class traits> std::basic_ostream<charT, traits> & +operator <<( + std::basic_ostream<charT, traits> & os, + basic_string_view<charT, traits> str) +{ + typename std::basic_ostream<charT, traits>::sentry sentry; + if (sentry) { + auto const w = os.width(); + auto const pad + = std::max<std::make_unsigned<decltype(w + str.size())>::type>( + w < 0 ? 0 : w, str.size()); + auto const after = (os.flags() & std::ios_base::adjustfield) + == std::ios_base::left; + if (pad != 0 && !after) { + auto const c = os.fill(); + for (; pad != 0; --pad) { + os.rdbuf()->sputc(c); + } + } + os.rdbuf()->sputn(str.data(), str.size()); + if (pad != 0 && after) { + auto const c = os.fill(); + for (; pad != 0; --pad) { + os.rdbuf()->sputc(c); + } + } + os.width(0); + } else { + os.setstate(std::ios_base::failbit); + } + return os; +} + +#if defined _MSC_VER +#pragma warning(pop) +#endif + +using string_view = basic_string_view<char>; +using u16string_view = basic_string_view<char16_t>; +using u32string_view = basic_string_view<char32_t>; +using wstring_view = basic_string_view<wchar_t>; + +// no literals::string_view_literals::operator "" sv + +} + +namespace std { + +template<> struct hash<o3tl::string_view> { + std::size_t operator ()(o3tl::string_view s) + { return hash<string>()(string(s.data(), s.size())); } +}; + +template<> struct hash<o3tl::u16string_view> { + std::size_t operator ()(o3tl::u16string_view s) + { return hash<u16string>()(u16string(s.data(), s.size())); } +}; + +template<> struct hash<o3tl::u32string_view> { + std::size_t operator ()(o3tl::u32string_view s) + { return hash<u32string>()(u32string(s.data(), s.size())); } +}; + +template<> struct hash<o3tl::wstring_view> { + std::size_t operator ()(o3tl::wstring_view s) + { return hash<wstring>()(wstring(s.data(), s.size())); } +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/o3tl/CppunitTest_o3tl_tests.mk b/o3tl/CppunitTest_o3tl_tests.mk index 152fa48c72ca..7dd43402c7ab 100644 --- a/o3tl/CppunitTest_o3tl_tests.mk +++ b/o3tl/CppunitTest_o3tl_tests.mk @@ -32,7 +32,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,o3tl_tests,\ o3tl/qa/test-lru_map \ o3tl/qa/test-safeint \ o3tl/qa/test-sorted_vector \ - o3tl/qa/test-span \ + o3tl/qa/test-string_view \ o3tl/qa/test-typed_flags \ o3tl/qa/test-vector_pool \ )) diff --git a/o3tl/qa/test-string_view.cxx b/o3tl/qa/test-string_view.cxx new file mode 100644 index 000000000000..977cfebc460a --- /dev/null +++ b/o3tl/qa/test-string_view.cxx @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <stdexcept> + +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <o3tl/string_view.hxx> + +namespace { + +class Test: public CppUnit::TestFixture { +private: + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(testCharLiteral); + CPPUNIT_TEST(testChar16Literal); + CPPUNIT_TEST(testChar32Literal); + CPPUNIT_TEST(testWcharLiteral); + CPPUNIT_TEST(testOperations); + CPPUNIT_TEST_SUITE_END(); + + void testCharLiteral() { + char * const s1 = const_cast<char *>("foo"); + o3tl::string_view v1(s1); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(3), v1.size()); + char const * const s2 = "foo"; + o3tl::string_view v2(s2); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(3), v2.size()); + char s3[] = "foo"; + o3tl::string_view v3(s3); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(3), v3.size()); + o3tl::string_view v4("foo"); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(3), v4.size()); + } + + void testChar16Literal() { + char16_t * const s1 = const_cast<char16_t *>(u"foo"); + o3tl::u16string_view v1(s1); + CPPUNIT_ASSERT_EQUAL(o3tl::u16string_view::size_type(3), v1.size()); + char16_t const * const s2 = u"foo"; + o3tl::u16string_view v2(s2); + CPPUNIT_ASSERT_EQUAL(o3tl::u16string_view::size_type(3), v2.size()); + char16_t s3[] = u"foo"; + o3tl::u16string_view v3(s3); + CPPUNIT_ASSERT_EQUAL(o3tl::u16string_view::size_type(3), v3.size()); + o3tl::u16string_view v4(u"foo"); + CPPUNIT_ASSERT_EQUAL(o3tl::u16string_view::size_type(3), v4.size()); + } + + void testChar32Literal() { + char32_t * const s1 = const_cast<char32_t *>(U"foo"); + o3tl::u32string_view v1(s1); + CPPUNIT_ASSERT_EQUAL(o3tl::u32string_view::size_type(3), v1.size()); + char32_t const * const s2 = U"foo"; + o3tl::u32string_view v2(s2); + CPPUNIT_ASSERT_EQUAL(o3tl::u32string_view::size_type(3), v2.size()); + char32_t s3[] = U"foo"; + o3tl::u32string_view v3(s3); + CPPUNIT_ASSERT_EQUAL(o3tl::u32string_view::size_type(3), v3.size()); + o3tl::u32string_view v4(U"foo"); + CPPUNIT_ASSERT_EQUAL(o3tl::u32string_view::size_type(3), v4.size()); + } + + void testWcharLiteral() { + wchar_t * const s1 = const_cast<wchar_t *>(L"foo"); + o3tl::wstring_view v1(s1); + CPPUNIT_ASSERT_EQUAL(o3tl::wstring_view::size_type(3), v1.size()); + wchar_t const * const s2 = L"foo"; + o3tl::wstring_view v2(s2); + CPPUNIT_ASSERT_EQUAL(o3tl::wstring_view::size_type(3), v2.size()); + wchar_t s3[] = L"foo"; + o3tl::wstring_view v3(s3); + CPPUNIT_ASSERT_EQUAL(o3tl::wstring_view::size_type(3), v3.size()); + o3tl::wstring_view v4(L"foo"); + CPPUNIT_ASSERT_EQUAL(o3tl::wstring_view::size_type(3), v4.size()); + } + + void testOperations() { + o3tl::string_view const v("fox"); + auto npos = o3tl::string_view::npos; + // o3tl::basic_string_view::npos will be (implicitly) inline with + // C++17, but for now can't be passed as 'const T& expected' + // argument into CppUnit::assertEquals, so take this detour + CPPUNIT_ASSERT_EQUAL('f', *v.begin()); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::difference_type(3), v.end() - v.begin()); + CPPUNIT_ASSERT_EQUAL('f', *v.cbegin()); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::difference_type(3), v.cend() - v.cbegin()); + CPPUNIT_ASSERT_EQUAL('x', *v.rbegin()); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::difference_type(3), v.rend() - v.rbegin()); + CPPUNIT_ASSERT_EQUAL('x', *v.crbegin()); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::difference_type(3), v.crend() - v.crbegin()); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(3), v.size()); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(3), v.length()); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::npos - 1, v.max_size()); + CPPUNIT_ASSERT(!v.empty()); + CPPUNIT_ASSERT_EQUAL('o', v[1]); + try { + v.at(o3tl::string_view::npos); + CPPUNIT_FAIL("missing exception"); + } catch (std::out_of_range &) {} + CPPUNIT_ASSERT_EQUAL('f', v.at(0)); + CPPUNIT_ASSERT_EQUAL('x', v.at(2)); + try { + v.at(3); + CPPUNIT_FAIL("missing exception"); + } catch (std::out_of_range &) {} + CPPUNIT_ASSERT_EQUAL('f', v.front()); + CPPUNIT_ASSERT_EQUAL('x', v.back()); + CPPUNIT_ASSERT_EQUAL('f', *v.data()); + { + o3tl::string_view v1("fox"); + v1.remove_prefix(2); + CPPUNIT_ASSERT_EQUAL('x', v1.front()); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(1), v1.size()); + } + { + o3tl::string_view v1("fox"); + v1.remove_suffix(2); + CPPUNIT_ASSERT_EQUAL('f', v1.front()); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(1), v1.size()); + } + { + o3tl::string_view v1("fox"); + o3tl::string_view v2("giraffe"); + v1.swap(v2); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(7), v1.size()); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(3), v2.size()); + } + { + char a[2]; + auto n = v.copy(a, 10, 1); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(2), n); + CPPUNIT_ASSERT_EQUAL('o', a[0]); + CPPUNIT_ASSERT_EQUAL('x', a[1]); + } + { + auto v1 = v.substr(1); + CPPUNIT_ASSERT_EQUAL('o', v1.front()); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(2), v1.size()); + } + CPPUNIT_ASSERT(v.compare(o3tl::string_view("foo")) > 0); + CPPUNIT_ASSERT(v.compare(0, 2, o3tl::string_view("foo")) < 0); + CPPUNIT_ASSERT_EQUAL( + 0, v.compare(0, 2, o3tl::string_view("foo"), 0, 2)); + CPPUNIT_ASSERT_EQUAL(0, v.compare("fox")); + CPPUNIT_ASSERT(v.compare(1, 2, "abc") > 0); + CPPUNIT_ASSERT_EQUAL(0, v.compare(1, 2, "oxx", 2)); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(1), v.find("ox")); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(1), v.find('o')); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find("oxx", 0, 2)); + CPPUNIT_ASSERT_EQUAL(npos, v.find("oxx")); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(1), v.rfind("ox")); + CPPUNIT_ASSERT_EQUAL(o3tl::string_view::size_type(1), v.rfind('o')); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), + v.rfind("oxx", o3tl::string_view::npos, 2)); + CPPUNIT_ASSERT_EQUAL(npos, v.rfind("oxx")); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_first_of("nop")); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_first_of('o')); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_first_of("nofx", 0, 2)); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(0), v.find_first_of("nofx")); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_last_of("nop")); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_last_of('o')); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), + v.find_last_of("nofx", o3tl::string_view::npos, 2)); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(2), v.find_last_of("nofx")); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_first_not_of("fx")); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_first_not_of('f')); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_first_not_of("fxo", 0, 2)); + CPPUNIT_ASSERT_EQUAL(npos, v.find_first_not_of("fxo")); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_last_not_of("fx")); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), v.find_last_not_of('x')); + CPPUNIT_ASSERT_EQUAL( + o3tl::string_view::size_type(1), + v.find_last_not_of("fxo", o3tl::string_view::npos, 2)); + CPPUNIT_ASSERT_EQUAL(npos, v.find_last_not_of("fxo")); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/solenv/clang-format/blacklist b/solenv/clang-format/blacklist index 61fbcaeca801..3595505e018d 100644 --- a/solenv/clang-format/blacklist +++ b/solenv/clang-format/blacklist @@ -6387,6 +6387,7 @@ include/o3tl/runtimetooustring.hxx include/o3tl/safeint.hxx include/o3tl/sorted_vector.hxx include/o3tl/span.hxx +include/o3tl/string_view.hxx include/o3tl/strong_int.hxx include/o3tl/typed_flags_set.hxx include/o3tl/vector_pool.hxx @@ -8615,6 +8616,7 @@ o3tl/qa/test-lru_map.cxx o3tl/qa/test-safeint.cxx o3tl/qa/test-sorted_vector.cxx o3tl/qa/test-span.cxx +o3tl/qa/test-string_view.cxx o3tl/qa/test-typed_flags.cxx o3tl/qa/test-vector_pool.cxx odk/examples/DevelopersGuide/Components/Addons/ProtocolHandlerAddon_cpp/addon.cxx |