diff options
-rw-r--r-- | configmgr/source/winreg.cxx | 42 | ||||
-rw-r--r-- | configmgr/source/writemodfile.cxx | 235 | ||||
-rw-r--r-- | configmgr/source/writemodfile.hxx | 9 | ||||
-rw-r--r-- | include/o3tl/string_view.hxx | 867 | ||||
-rw-r--r-- | o3tl/CppunitTest_o3tl_tests.mk | 1 | ||||
-rw-r--r-- | o3tl/qa/test-string_view.cxx | 212 |
6 files changed, 1212 insertions, 154 deletions
diff --git a/configmgr/source/winreg.cxx b/configmgr/source/winreg.cxx index c48a85e70e26..76d7abe15462 100644 --- a/configmgr/source/winreg.cxx +++ b/configmgr/source/winreg.cxx @@ -149,7 +149,7 @@ void dumpWindowsRegistryKey(HKEY hKey, OUString const & aKeyName, TempFile &aFil bool bHasNode = false; sal_Int32 nCloseNode = 0; - writeData(aFileHandle, "<item oor:path=\""); + aFileHandle.writeString("<item oor:path=\""); for(sal_Int32 nIndex = 0;; ++nIndex) { OUString aNextPathPart = aPathAndNodes.getToken(nIndex, '\\'); @@ -160,13 +160,13 @@ void dumpWindowsRegistryKey(HKEY hKey, OUString const & aKeyName, TempFile &aFil { bHasNode = true; nCloseNode++; - writeData(aFileHandle, "\"><node oor:name=\""); + aFileHandle.writeString("\"><node oor:name=\""); sal_Int32 nCommandSeparator = aNextPathPart.lastIndexOf('#'); if(nCommandSeparator != -1) { OUString aNodeOp = aNextPathPart.copy(nCommandSeparator + 1); writeAttributeValue(aFileHandle, aNextPathPart.copy(0, nCommandSeparator - 1)); - writeData(aFileHandle, "\" oor:op=\""); + aFileHandle.writeString("\" oor:op=\""); writeAttributeValue(aFileHandle, aNodeOp); } else @@ -176,33 +176,34 @@ void dumpWindowsRegistryKey(HKEY hKey, OUString const & aKeyName, TempFile &aFil } else { - writeAttributeValue(aFileHandle, "/" + aNextPathPart); + writeAttributeValue( + aFileHandle, OUString("/" + aNextPathPart)); } } else { - writeData(aFileHandle, "\">"); + aFileHandle.writeString("\">"); break; } } - writeData(aFileHandle, "<prop oor:name=\""); + aFileHandle.writeString("<prop oor:name=\""); writeAttributeValue(aFileHandle, aProp); - writeData(aFileHandle, "\""); + aFileHandle.writeString("\""); if(!aType.isEmpty()) { - writeData(aFileHandle, " oor:type=\""); + aFileHandle.writeString(" oor:type=\""); writeAttributeValue(aFileHandle, aType); - writeData(aFileHandle, "\""); + aFileHandle.writeString("\""); } if(bFinal) - writeData(aFileHandle, " oor:finalized=\"true\""); - writeData(aFileHandle, "><value>"); + aFileHandle.writeString(" oor:finalized=\"true\""); + aFileHandle.writeString("><value>"); writeValueContent(aFileHandle, aValue); - writeData(aFileHandle, "</value></prop>"); + aFileHandle.writeString("</value></prop>"); for(; nCloseNode > 0; nCloseNode--) - writeData(aFileHandle, "</node>"); - writeData(aFileHandle, "</item>\n"); + aFileHandle.writeString("</node>"); + aFileHandle.writeString("</item>\n"); } RegCloseKey(hCurKey); } @@ -235,14 +236,13 @@ bool dumpWindowsRegistry(OUString* pFileURL, WinRegType eType) "cannot create temporary file"); } aFileHandle.url = *pFileURL; - writeData( - aFileHandle, - "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items" - " xmlns:oor=\"http://openoffice.org/2001/registry\"" - " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" - " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"); + aFileHandle.writeString( + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items" + " xmlns:oor=\"http://openoffice.org/2001/registry\"" + " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"); dumpWindowsRegistryKey(hKey, "", aFileHandle); - writeData(aFileHandle, "</oor:items>"); + aFileHandle.writeString("</oor:items>"); oslFileError e = aFileHandle.closeWithoutUnlink(); if (e != osl_File_E_None) SAL_WARN("configmgr", "osl_closeFile failed with " << +e); diff --git a/configmgr/source/writemodfile.cxx b/configmgr/source/writemodfile.cxx index a1e545e5b5a3..e34b604dafd5 100644 --- a/configmgr/source/writemodfile.cxx +++ b/configmgr/source/writemodfile.cxx @@ -20,12 +20,15 @@ #include <sal/config.h> #include <cassert> +#include <cstddef> +#include <limits> #include <com/sun/star/uno/Any.hxx> #include <com/sun/star/uno/Reference.hxx> #include <com/sun/star/uno/RuntimeException.hpp> #include <com/sun/star/uno/Sequence.hxx> #include <com/sun/star/uno/XInterface.hpp> +#include <o3tl/string_view.hxx> #include <osl/file.h> #include <osl/file.hxx> #include <rtl/string.h> @@ -57,13 +60,11 @@ class Components; namespace { -OString convertToUtf8( - OUString const & text, sal_Int32 offset, sal_Int32 length) -{ - assert(offset <= text.getLength() && text.getLength() - offset >= length); +OString convertToUtf8(o3tl::u16string_view text) { OString s; + assert(text.size() <= sal_uInt32(std::numeric_limits<sal_Int32>::max())); if (!rtl_convertUStringToString( - &s.pData, text.pData->buffer + offset, length, + &s.pData, text.data(), text.size(), RTL_TEXTENCODING_UTF8, (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) @@ -139,43 +140,38 @@ oslFileError TempFile::flush() { return e; } -void TempFile::writeString(char const *begin, sal_Int32 length) { - buffer.append(begin, length); +void TempFile::writeString(o3tl::string_view text) { + buffer.append(text.data(), text.size()); if (buffer.getLength() > 0x10000) flush(); } namespace { -void writeData_(TempFile &handle, char const * begin, sal_Int32 length) { - assert(length >= 0); - handle.writeString(begin, length); -} - void writeValueContent_(TempFile &, bool) = delete; // silence loplugin:salbool void writeValueContent_(TempFile &handle, sal_Bool value) { if (value) { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("true")); + handle.writeString("true"); } else { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("false")); + handle.writeString("false"); } } void writeValueContent_(TempFile &handle, sal_Int16 value) { - writeData(handle, OString::number(value)); + handle.writeString(OString::number(value)); } void writeValueContent_(TempFile &handle, sal_Int32 value) { - writeData(handle, OString::number(value)); + handle.writeString(OString::number(value)); } void writeValueContent_(TempFile &handle, sal_Int64 value) { - writeData(handle, OString::number(value)); + handle.writeString(OString::number(value)); } void writeValueContent_(TempFile &handle, double value) { - writeData(handle, OString::number(value)); + handle.writeString(OString::number(value)); } void writeValueContent_(TempFile &handle, const OUString& value) { @@ -189,48 +185,49 @@ void writeValueContent_( static char const hexDigit[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - writeData_(handle, hexDigit + ((value[i] >> 4) & 0xF), 1); - writeData_(handle, hexDigit + (value[i] & 0xF), 1); + handle.writeString( + o3tl::string_view(hexDigit + ((value[i] >> 4) & 0xF), 1)); + handle.writeString(o3tl::string_view(hexDigit + (value[i] & 0xF), 1)); } } template< typename T > void writeSingleValue( TempFile &handle, css::uno::Any const & value) { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM(">")); + handle.writeString(">"); T val = T(); value >>= val; writeValueContent_(handle, val); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("</value>")); + handle.writeString("</value>"); } template< typename T > void writeListValue( TempFile &handle, css::uno::Any const & value) { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM(">")); + handle.writeString(">"); css::uno::Sequence< T > val; value >>= val; for (sal_Int32 i = 0; i < val.getLength(); ++i) { if (i != 0) { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM(" ")); + handle.writeString(" "); } writeValueContent_(handle, val[i]); } - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("</value>")); + handle.writeString("</value>"); } template< typename T > void writeItemListValue( TempFile &handle, css::uno::Any const & value) { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM(">")); + handle.writeString(">"); css::uno::Sequence< T > val; value >>= val; for (sal_Int32 i = 0; i < val.getLength(); ++i) { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<it>")); + handle.writeString("<it>"); writeValueContent_(handle, val[i]); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("</it>")); + handle.writeString("</it>"); } - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("</value>")); + handle.writeString("</value>"); } void writeValue(TempFile &handle, Type type, css::uno::Any const & value) { @@ -284,7 +281,7 @@ void writeValue(TempFile &handle, Type type, css::uno::Any const & value) { void writeNode( Components & components, TempFile &handle, - rtl::Reference< Node > const & parent, OUString const & name, + rtl::Reference< Node > const & parent, o3tl::u16string_view name, rtl::Reference< Node > const & node) { static xmlreader::Span const typeNames[] = { @@ -308,51 +305,49 @@ void writeNode( case Node::KIND_PROPERTY: { PropertyNode * prop = static_cast< PropertyNode * >(node.get()); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\"")); + handle.writeString("<prop oor:name=\""); writeAttributeValue(handle, name); - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"fuse\"")); + handle.writeString("\" oor:op=\"fuse\""); Type type = prop->getStaticType(); Type dynType = getDynamicType(prop->getValue(components)); assert(dynType != TYPE_ERROR); if (type == TYPE_ANY) { type = dynType; if (type != TYPE_NIL) { - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM(" oor:type=\"")); - writeData_( - handle, typeNames[type].begin, typeNames[type].length); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("\"")); + handle.writeString(" oor:type=\""); + handle.writeString( + o3tl::string_view( + typeNames[type].begin, typeNames[type].length)); + handle.writeString("\""); } } - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("><value")); + handle.writeString("><value"); if (dynType == TYPE_NIL) { - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM(" xsi:nil=\"true\"/>")); + handle.writeString(" xsi:nil=\"true\"/>"); } else { writeValue(handle, type, prop->getValue(components)); } - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("</prop>")); + handle.writeString("</prop>"); } break; case Node::KIND_LOCALIZED_PROPERTY: - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\"")); + handle.writeString("<prop oor:name=\""); writeAttributeValue(handle, name); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"fuse\">")); + handle.writeString("\" oor:op=\"fuse\">"); for (NodeMap::const_iterator i(node->getMembers().begin()); i != node->getMembers().end(); ++i) { writeNode(components, handle, node, i->first, i->second); } - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("</prop>")); + handle.writeString("</prop>"); break; case Node::KIND_LOCALIZED_VALUE: { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<value")); - if (!name.isEmpty()) { - writeData_(handle, RTL_CONSTASCII_STRINGPARAM(" xml:lang=\"")); + handle.writeString("<value"); + if (!name.empty()) { + handle.writeString(" xml:lang=\""); writeAttributeValue(handle, name); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("\"")); + handle.writeString("\""); } Type type = static_cast< LocalizedPropertyNode * >(parent.get())-> getStaticType(); @@ -363,16 +358,15 @@ void writeNode( if (type == TYPE_ANY) { type = dynType; if (type != TYPE_NIL) { - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM(" oor:type=\"")); - writeData_( - handle, typeNames[type].begin, typeNames[type].length); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("\"")); + handle.writeString(" oor:type=\""); + handle.writeString( + o3tl::string_view( + typeNames[type].begin, typeNames[type].length)); + handle.writeString("\""); } } if (dynType == TYPE_NIL) { - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM(" xsi:nil=\"true\"/>")); + handle.writeString(" xsi:nil=\"true\"/>"); } else { writeValue(handle, type, value); } @@ -380,19 +374,18 @@ void writeNode( break; case Node::KIND_GROUP: case Node::KIND_SET: - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<node oor:name=\"")); + handle.writeString("<node oor:name=\""); writeAttributeValue(handle, name); if (!node->getTemplateName().isEmpty()) { // set member - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"replace")); + handle.writeString("\" oor:op=\"replace"); } - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("\">")); + handle.writeString("\">"); for (NodeMap::const_iterator i(node->getMembers().begin()); i != node->getMembers().end(); ++i) { writeNode(components, handle, node, i->first, i->second); } - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("</node>")); + handle.writeString("</node>"); break; case Node::KIND_ROOT: assert(false); // this cannot happen @@ -422,48 +415,40 @@ void writeModifications( if (modifications.children.empty()) { assert(parent.is()); // components themselves have no parent but must have children - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<item oor:path=\"")); + handle.writeString("<item oor:path=\""); writeAttributeValue(handle, parentPathRepresentation); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("\">")); + handle.writeString("\">"); if (node.is()) { writeNode(components, handle, parent, nodeName, node); } else { switch (parent->kind()) { case Node::KIND_LOCALIZED_PROPERTY: - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<value")); + handle.writeString("<value"); if (!nodeName.isEmpty()) { - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM(" xml:lang=\"")); + handle.writeString(" xml:lang=\""); writeAttributeValue(handle, nodeName); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("\"")); + handle.writeString("\""); } - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM(" oor:op=\"remove\"/>")); + handle.writeString(" oor:op=\"remove\"/>"); break; case Node::KIND_GROUP: assert( static_cast< GroupNode * >(parent.get())->isExtensible()); - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\"")); + handle.writeString("<prop oor:name=\""); writeAttributeValue(handle, nodeName); - writeData_( - handle, - RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"remove\"/>")); + handle.writeString("\" oor:op=\"remove\"/>"); break; case Node::KIND_SET: - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM("<node oor:name=\"")); + handle.writeString("<node oor:name=\""); writeAttributeValue(handle, nodeName); - writeData_( - handle, - RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"remove\"/>")); + handle.writeString("\" oor:op=\"remove\"/>"); break; default: assert(false); // this cannot happen break; } } - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("</item>\n")); + handle.writeString("</item>\n"); } else { assert(node.is()); OUString pathRep( @@ -496,91 +481,85 @@ void writeModifications( } -void writeData(TempFile &handle, OString const & text) { - writeData_(handle, text.getStr(), text.getLength()); -} - -void writeAttributeValue(TempFile &handle, OUString const & value) { - sal_Int32 i = 0; - sal_Int32 j = i; - for (; j < value.getLength(); ++j) { +void writeAttributeValue(TempFile &handle, o3tl::u16string_view value) { + std::size_t i = 0; + std::size_t j = i; + for (; j != value.size(); ++j) { assert( value[j] == 0x0009 || value[j] == 0x000A || value[j] == 0x000D || (value[j] >= 0x0020 && value[j] != 0xFFFE && value[j] != 0xFFFF)); switch(value[j]) { case '\x09': - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("	")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("	"); i = j + 1; break; case '\x0A': - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("
")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("
"); i = j + 1; break; case '\x0D': - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("
")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("
"); i = j + 1; break; case '"': - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM(""")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("""); i = j + 1; break; case '&': - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("&")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("&"); i = j + 1; break; case '<': - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("<"); i = j + 1; break; default: break; } } - writeData(handle, convertToUtf8(value, i, j - i)); + handle.writeString(convertToUtf8(value.substr(i, j - i))); } -void writeValueContent(TempFile &handle, OUString const & value) { - sal_Int32 i = 0; - sal_Int32 j = i; - for (; j < value.getLength(); ++j) { - sal_Unicode c = value[j]; +void writeValueContent(TempFile &handle, o3tl::u16string_view value) { + std::size_t i = 0; + std::size_t j = i; + for (; j != value.size(); ++j) { + char16_t c = value[j]; if ((c < 0x0020 && c != 0x0009 && c != 0x000A && c != 0x000D) || c == 0xFFFE || c == 0xFFFF) { - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_( - handle, RTL_CONSTASCII_STRINGPARAM("<unicode oor:scalar=\"")); - writeData( - handle, OString::number(c)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("\"/>")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("<unicode oor:scalar=\""); + handle.writeString(OString::number(c)); + handle.writeString("\"/>"); i = j + 1; } else if (c == '\x0D') { - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("
")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("
"); i = j + 1; } else if (c == '&') { - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("&")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("&"); i = j + 1; } else if (c == '<') { - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM("<")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString("<"); i = j + 1; } else if (c == '>') { // "MUST, for compatibility, be escaped [...] when it appears in the // string ']]>'": - writeData(handle, convertToUtf8(value, i, j - i)); - writeData_(handle, RTL_CONSTASCII_STRINGPARAM(">")); + handle.writeString(convertToUtf8(value.substr(i, j - i))); + handle.writeString(">"); i = j + 1; } } - writeData(handle, convertToUtf8(value, i, j - i)); + handle.writeString(convertToUtf8(value.substr(i, j - i))); } void writeModFile( @@ -617,13 +596,11 @@ void writeModFile( throw css::uno::RuntimeException( "cannot create temporary file in " + dir); } - writeData_( - tmp, - RTL_CONSTASCII_STRINGPARAM( - "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items" - " xmlns:oor=\"http://openoffice.org/2001/registry\"" - " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" - " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n")); + tmp.writeString( + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items" + " xmlns:oor=\"http://openoffice.org/2001/registry\"" + " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"); //TODO: Do not write back information about those removed items that did not // come from the .xcs/.xcu files, anyway (but had been added dynamically // instead): @@ -658,7 +635,7 @@ void writeModFile( data.getComponents().findNode(Data::NO_LAYER, j->first), j->second); } - writeData_(tmp, RTL_CONSTASCII_STRINGPARAM("</oor:items>\n")); + tmp.writeString("</oor:items>\n"); tmp.closeAndRename(url); } diff --git a/configmgr/source/writemodfile.hxx b/configmgr/source/writemodfile.hxx index d1a9348a0490..a8925ab8e758 100644 --- a/configmgr/source/writemodfile.hxx +++ b/configmgr/source/writemodfile.hxx @@ -21,6 +21,8 @@ #define INCLUDED_CONFIGMGR_SOURCE_WRITEMODFILE_HXX #include <sal/config.h> + +#include <o3tl/string_view.hxx> #include <rtl/strbuf.hxx> namespace configmgr { @@ -41,16 +43,15 @@ struct TempFile { #ifdef _WIN32 oslFileError closeWithoutUnlink(); #endif - void writeString(char const *begin, sal_Int32 length); + void writeString(o3tl::string_view text); private: TempFile(const TempFile&) = delete; TempFile& operator=(const TempFile&) = delete; }; -void writeData(TempFile &handle, OString const & text); -void writeAttributeValue(TempFile &handle, OUString const & value); -void writeValueContent(TempFile &handle, OUString const & value); +void writeAttributeValue(TempFile &handle, o3tl::u16string_view value); +void writeValueContent(TempFile &handle, o3tl::u16string_view value); void writeModFile( Components & components, OUString const & url, Data const & data); 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 6d7a1a811adb..62a0fc985be2 100644 --- a/o3tl/CppunitTest_o3tl_tests.mk +++ b/o3tl/CppunitTest_o3tl_tests.mk @@ -31,6 +31,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,o3tl_tests,\ o3tl/qa/test-cow_wrapper \ o3tl/qa/test-lru_map \ o3tl/qa/test-sorted_vector \ + 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: */ |