[Libreoffice-commits] core.git: configmgr/source include/o3tl o3tl/CppunitTest_o3tl_tests.mk o3tl/qa

Stephan Bergmann sbergman at redhat.com
Fri Apr 7 20:12:59 UTC 2017


 configmgr/source/winreg.cxx       |   42 -
 configmgr/source/writemodfile.cxx |  235 ++++------
 configmgr/source/writemodfile.hxx |    9 
 include/o3tl/string_view.hxx      |  867 ++++++++++++++++++++++++++++++++++++++
 o3tl/CppunitTest_o3tl_tests.mk    |    1 
 o3tl/qa/test-string_view.cxx      |  212 +++++++++
 6 files changed, 1212 insertions(+), 154 deletions(-)

New commits:
commit 50057a37a877213d935958d5c643fde1434d680c
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Fri Apr 7 15:59:12 2017 +0200

    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 at libreoffice.org>
    Reviewed-by: Stephan Bergmann <sbergman at redhat.com>

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("&#xA;"));
+            handle.writeString(convertToUtf8(value.substr(i, j - i)));
+            handle.writeString("&#xA;");
             i = j + 1;
             break;
         case '\x0D':
-            writeData(handle, convertToUtf8(value, i, j - i));
-            writeData_(handle, RTL_CONSTASCII_STRINGPARAM("&#xD;"));
+            handle.writeString(convertToUtf8(value.substr(i, j - i)));
+            handle.writeString("&#xD;");
             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("&#xD;"));
+            handle.writeString(convertToUtf8(value.substr(i, j - i)));
+            handle.writeString("&#xD;");
             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: */


More information about the Libreoffice-commits mailing list