[Libreoffice-commits] core.git: desktop/source include/tools include/vcl sc/inc sc/source sw/inc sw/source tools/CppunitTest_tools_test.mk tools/Library_tl.mk tools/qa tools/source

Noel Grandin (via logerrit) logerrit at kemper.freedesktop.org
Thu Jun 18 08:54:46 UTC 2020


 desktop/source/lib/init.cxx           |   31 +---
 include/tools/json_writer.hxx         |   86 +++++++++++
 include/vcl/ITiledRenderable.hxx      |    4 
 sc/inc/chgtrack.hxx                   |    3 
 sc/inc/docuno.hxx                     |    2 
 sc/source/core/tool/chgtrack.cxx      |   30 +---
 sc/source/ui/unoobj/docuno.cxx        |    8 -
 sw/inc/unotxdoc.hxx                   |    2 
 sw/source/uibase/uno/unotxdoc.cxx     |   36 +---
 tools/CppunitTest_tools_test.mk       |    1 
 tools/Library_tl.mk                   |    1 
 tools/qa/cppunit/test_json_writer.cxx |   57 +++++++
 tools/source/misc/json_writer.cxx     |  253 ++++++++++++++++++++++++++++++++++
 13 files changed, 442 insertions(+), 72 deletions(-)

New commits:
commit c8bed6445b244a5d9021dbd9a2ff19d80c03917b
Author:     Noel Grandin <noelgrandin at gmail.com>
AuthorDate: Mon Jun 15 20:32:25 2020 +0200
Commit:     Noel Grandin <noel.grandin at collabora.co.uk>
CommitDate: Thu Jun 18 10:54:10 2020 +0200

    new json writer for LOK
    
    we shave about 3 memory copies off in the process, and
    make the class play nicely with our string types.
    
    Change-Id: I1f614fb35b1de499ac99e3b33ac638ad81054bed
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96393
    Tested-by: Noel Grandin <noel.grandin at collabora.co.uk>
    Reviewed-by: Noel Grandin <noel.grandin at collabora.co.uk>

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 8b3ba76bfaf9..2843c0f5c250 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -118,6 +118,7 @@
 #include <vcl/svapp.hxx>
 #include <unotools/resmgr.hxx>
 #include <tools/fract.hxx>
+#include <tools/json_writer.hxx>
 #include <svtools/ctrltool.hxx>
 #include <svtools/langtab.hxx>
 #include <vcl/floatwin.hxx>
@@ -4658,47 +4659,41 @@ static char* getTrackedChanges(LibreOfficeKitDocument* pThis)
     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
 
     uno::Reference<document::XRedlinesSupplier> xRedlinesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
-    std::stringstream aStream;
+    tools::JsonWriter aJson;
     // We want positions of the track changes also which is not possible from
     // UNO. Enable positioning information for text documents only for now, so
     // construct the tracked changes JSON from inside the sw/, not here using UNO
     if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT && xRedlinesSupplier.is())
     {
+        auto redlinesNode = aJson.startNode("redlines");
         uno::Reference<container::XEnumeration> xRedlines = xRedlinesSupplier->getRedlines()->createEnumeration();
-        boost::property_tree::ptree aRedlines;
         for (size_t nIndex = 0; xRedlines->hasMoreElements(); ++nIndex)
         {
             uno::Reference<beans::XPropertySet> xRedline(xRedlines->nextElement(), uno::UNO_QUERY);
-            boost::property_tree::ptree aRedline;
-            aRedline.put("index", nIndex);
+            auto redlineNode = aJson.startNode("");
+            aJson.put("index", nIndex);
 
             OUString sAuthor;
             xRedline->getPropertyValue("RedlineAuthor") >>= sAuthor;
-            aRedline.put("author", sAuthor.toUtf8().getStr());
+            aJson.put("author", sAuthor);
 
             OUString sType;
             xRedline->getPropertyValue("RedlineType") >>= sType;
-            aRedline.put("type", sType.toUtf8().getStr());
+            aJson.put("type", sType);
 
             OUString sComment;
             xRedline->getPropertyValue("RedlineComment") >>= sComment;
-            aRedline.put("comment", sComment.toUtf8().getStr());
+            aJson.put("comment", sComment);
 
             OUString sDescription;
             xRedline->getPropertyValue("RedlineDescription") >>= sDescription;
-            aRedline.put("description", sDescription.toUtf8().getStr());
+            aJson.put("description", sDescription);
 
             util::DateTime aDateTime;
             xRedline->getPropertyValue("RedlineDateTime") >>= aDateTime;
             OUString sDateTime = utl::toISO8601(aDateTime);
-            aRedline.put("dateTime", sDateTime.toUtf8().getStr());
-
-            aRedlines.push_back(std::make_pair("", aRedline));
+            aJson.put("dateTime", sDateTime);
         }
-
-        boost::property_tree::ptree aTree;
-        aTree.add_child("redlines", aRedlines);
-        boost::property_tree::write_json(aStream, aTree);
     }
     else
     {
@@ -4708,12 +4703,10 @@ static char* getTrackedChanges(LibreOfficeKitDocument* pThis)
             SetLastExceptionMsg("Document doesn't support tiled rendering");
             return nullptr;
         }
-        OUString aTrackedChanges = pDoc->getTrackedChanges();
-        aStream << aTrackedChanges.toUtf8();
+        pDoc->getTrackedChanges(aJson);
     }
 
-    char* pJson = strdup(aStream.str().c_str());
-    return pJson;
+    return aJson.extractData();
 }
 
 
diff --git a/include/tools/json_writer.hxx b/include/tools/json_writer.hxx
new file mode 100644
index 000000000000..e811bb90e277
--- /dev/null
+++ b/include/tools/json_writer.hxx
@@ -0,0 +1,86 @@
+/* -*- 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/.
+ */
+#pragma once
+
+#include <tools/toolsdllapi.h>
+#include <rtl/ustring.hxx>
+#include <memory>
+
+/** Simple JSON encoder designed specifically for LibreOfficeKit purposes.
+ *
+ * (1) Minimal allocations/re-allocations/copying
+ * (2) Small/simple JSON documents
+ * (3) ascii property names
+ */
+namespace tools
+{
+class ScopedJsonWriterNode;
+
+class TOOLS_DLLPUBLIC JsonWriter
+{
+    friend class ScopedJsonWriterNode;
+
+    int mSpaceAllocated;
+    std::unique_ptr<char[]> maBuffer;
+    int mStartNodeCount;
+    char* mPos;
+    bool mbFirstFieldInNode;
+
+public:
+    JsonWriter();
+    ~JsonWriter();
+
+    [[nodiscard]] ScopedJsonWriterNode startNode(const char*);
+
+    void put(const char* pPropName, const OUString& rPropValue);
+    void put(const char* pPropName, const OString& rPropValue);
+    void put(const char* pPropName, const char* pPropVal);
+    void put(const char*, int);
+
+    /** Hands ownership of the the underlying storage buffer to the caller,
+     * after this no more document modifications may be written. */
+    char* extractData();
+
+private:
+    void endNode();
+    void addCommaBeforeField();
+
+    inline void ensureSpace(int noMoreBytesRequired)
+    {
+        int currentUsed = mPos - maBuffer.get();
+        if (currentUsed + noMoreBytesRequired >= mSpaceAllocated)
+        {
+            auto newSize = std::max(mSpaceAllocated * 2, (currentUsed + noMoreBytesRequired) * 2);
+            auto pNew = new char[newSize];
+            memcpy(pNew, maBuffer.get(), currentUsed);
+            maBuffer.reset(pNew);
+            mPos = maBuffer.get();
+        }
+    }
+};
+
+/**
+ * Auto-closes the node.
+ */
+class ScopedJsonWriterNode
+{
+    friend class JsonWriter;
+
+    JsonWriter& mrWriter;
+
+    ScopedJsonWriterNode(JsonWriter& rWriter)
+        : mrWriter(rWriter)
+    {
+    }
+
+public:
+    ~ScopedJsonWriterNode() { mrWriter.endNode(); }
+};
+};
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/vcl/ITiledRenderable.hxx b/include/vcl/ITiledRenderable.hxx
index 9e995216e573..e290c4960368 100644
--- a/include/vcl/ITiledRenderable.hxx
+++ b/include/vcl/ITiledRenderable.hxx
@@ -24,6 +24,7 @@ namespace com::sun::star::datatransfer::clipboard { class XClipboard; }
 namespace com::sun::star::uno { template <class interface_type> class Reference; }
 namespace com::sun::star::uno { template <typename > class Sequence; }
 namespace vcl { class Window; }
+namespace tools { class JsonWriter; }
 
 class VirtualDevice;
 
@@ -230,9 +231,8 @@ public:
     /// Implementation for
     /// lok::Document::getCommandValues(".uno:AcceptTrackedChanges") when there
     /// is no matching UNO API.
-    virtual OUString getTrackedChanges()
+    virtual void getTrackedChanges(tools::JsonWriter&)
     {
-        return OUString();
     }
 
     /// Implementation for
diff --git a/sc/inc/chgtrack.hxx b/sc/inc/chgtrack.hxx
index 544e2566fdba..146521538943 100644
--- a/sc/inc/chgtrack.hxx
+++ b/sc/inc/chgtrack.hxx
@@ -43,6 +43,7 @@ class ScFormulaCell;
 class ScChangeAction;
 class ScChangeTrack;
 class ScAppOptions;
+namespace tools { class JsonWriter; }
 
 class ScActionColorChanger
 {
@@ -1146,7 +1147,7 @@ public:
     SC_DLLPUBLIC ScChangeTrack* Clone( ScDocument* pDocument ) const;
     static void MergeActionState( ScChangeAction* pAct, const ScChangeAction* pOtherAct );
     /// Get info about all ScChangeAction elements.
-    OUString GetChangeTrackInfo();
+    void GetChangeTrackInfo(tools::JsonWriter&);
 };
 
 #endif
diff --git a/sc/inc/docuno.hxx b/sc/inc/docuno.hxx
index 7c9c87a4f109..02bd4c04981d 100644
--- a/sc/inc/docuno.hxx
+++ b/sc/inc/docuno.hxx
@@ -375,7 +375,7 @@ public:
     virtual PointerStyle getPointer() override;
 
     /// @see vcl::ITiledRenderable::getTrackedChanges().
-    OUString getTrackedChanges() override;
+    void getTrackedChanges(tools::JsonWriter&) override;
 
     /// @see vcl::ITiledRenderable::setClientVisibleArea().
     virtual void setClientVisibleArea(const tools::Rectangle& rRectangle) override;
diff --git a/sc/source/core/tool/chgtrack.cxx b/sc/source/core/tool/chgtrack.cxx
index 3a45a5f3be83..f734224ce3fa 100644
--- a/sc/source/core/tool/chgtrack.cxx
+++ b/sc/source/core/tool/chgtrack.cxx
@@ -47,6 +47,7 @@
 #include <unotools/useroptions.hxx>
 #include <unotools/datetime.hxx>
 #include <sfx2/sfxsids.hrc>
+#include <tools/json_writer.hxx>
 #include <algorithm>
 #include <memory>
 #include <boost/property_tree/json_parser.hpp>
@@ -4676,34 +4677,31 @@ void ScChangeTrack::MergeActionState( ScChangeAction* pAct, const ScChangeAction
 }
 
 /// Get info about a single ScChangeAction element.
-static void lcl_getTrackedChange(ScDocument* pDoc, int nIndex, const ScChangeAction* pAction, boost::property_tree::ptree& rRedlines)
+static void lcl_getTrackedChange(ScDocument* pDoc, int nIndex, const ScChangeAction* pAction, tools::JsonWriter& rRedlines)
 {
     if (pAction->GetType() == SC_CAT_CONTENT)
     {
-        boost::property_tree::ptree aRedline;
-        aRedline.put("index", nIndex);
+        auto redlinesNode = rRedlines.startNode("");
+        rRedlines.put("index", nIndex);
 
-        const OUString& sAuthor = pAction->GetUser();
-        aRedline.put("author", sAuthor.toUtf8().getStr());
+        rRedlines.put("author", pAction->GetUser());
 
-        aRedline.put("type", "Modify");
+        rRedlines.put("type", "Modify");
 
-        aRedline.put("comment", pAction->GetComment().toUtf8().getStr());
+        rRedlines.put("comment", pAction->GetComment());
 
         OUString aDescription;
         pAction->GetDescription(aDescription, pDoc, true);
-        aRedline.put("description", aDescription);
+        rRedlines.put("description", aDescription);
 
         OUString sDateTime = utl::toISO8601(pAction->GetDateTimeUTC().GetUNODateTime());
-        aRedline.put("dateTime", sDateTime.toUtf8().getStr());
-
-        rRedlines.push_back(std::make_pair("", aRedline));
+        rRedlines.put("dateTime", sDateTime);
     }
 }
 
-OUString ScChangeTrack::GetChangeTrackInfo()
+void ScChangeTrack::GetChangeTrackInfo(tools::JsonWriter& aRedlines)
 {
-    boost::property_tree::ptree aRedlines;
+    auto redlinesNode = aRedlines.startNode("redlines");
 
     ScChangeAction* pAction = GetFirst();
     if (pAction)
@@ -4717,12 +4715,6 @@ OUString ScChangeTrack::GetChangeTrackInfo()
             lcl_getTrackedChange(pDoc, i++, pAction, aRedlines);
         }
     }
-
-    boost::property_tree::ptree aTree;
-    aTree.add_child("redlines", aRedlines);
-    std::stringstream aStream;
-    boost::property_tree::write_json(aStream, aTree);
-    return OUString::fromUtf8(aStream.str().c_str());
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx
index 4592bdb9c04d..7e5dd7fe33dd 100644
--- a/sc/source/ui/unoobj/docuno.cxx
+++ b/sc/source/ui/unoobj/docuno.cxx
@@ -940,17 +940,13 @@ PointerStyle ScModelObj::getPointer()
     return pGridWindow->GetPointer();
 }
 
-OUString ScModelObj::getTrackedChanges()
+void ScModelObj::getTrackedChanges(tools::JsonWriter& rJson)
 {
-    OUString aRet;
-
     if (pDocShell)
     {
         if (ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack())
-            aRet = pChangeTrack->GetChangeTrackInfo();
+            pChangeTrack->GetChangeTrackInfo(rJson);
     }
-
-    return aRet;
 }
 
 void ScModelObj::setClientVisibleArea(const tools::Rectangle& rRectangle)
diff --git a/sw/inc/unotxdoc.hxx b/sw/inc/unotxdoc.hxx
index 8c6a1358520c..16780ce2edeb 100644
--- a/sw/inc/unotxdoc.hxx
+++ b/sw/inc/unotxdoc.hxx
@@ -436,7 +436,7 @@ public:
     /// @see vcl::ITiledRenderable::getPointer().
     virtual PointerStyle getPointer() override;
     /// @see vcl::ITiledRenderable::getTrackedChanges().
-    OUString getTrackedChanges() override;
+    void getTrackedChanges(tools::JsonWriter&) override;
     /// @see vcl::ITiledRenderable::getTrackedChangeAuthors().
     OUString getTrackedChangeAuthors() override;
 
diff --git a/sw/source/uibase/uno/unotxdoc.cxx b/sw/source/uibase/uno/unotxdoc.cxx
index 15be728d74af..0d934c32f39c 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -161,6 +161,7 @@
 #include <redline.hxx>
 #include <DocumentRedlineManager.hxx>
 #include <xmloff/odffields.hxx>
+#include <tools/json_writer.hxx>
 
 #define TWIPS_PER_PIXEL 15
 
@@ -3255,9 +3256,9 @@ PointerStyle SwXTextDocument::getPointer()
     return pWrtShell->GetView().GetEditWin().GetPointer();
 }
 
-OUString SwXTextDocument::getTrackedChanges()
+void SwXTextDocument::getTrackedChanges(tools::JsonWriter& rJson)
 {
-    boost::property_tree::ptree aTrackedChanges;
+    auto redlinesNode = rJson.startNode("redlines");
 
     // Disable since usability is very low beyond some small number of changes.
     static bool bDisableRedlineComments = getenv("DISABLE_REDLINE") != nullptr;
@@ -3267,19 +3268,17 @@ OUString SwXTextDocument::getTrackedChanges()
             = pDocShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
         for (SwRedlineTable::size_type i = 0; i < rRedlineTable.size(); ++i)
         {
-            boost::property_tree::ptree aTrackedChange;
-            aTrackedChange.put("index", rRedlineTable[i]->GetId());
-            aTrackedChange.put("author", rRedlineTable[i]->GetAuthorString(1).toUtf8().getStr());
-            aTrackedChange.put("type", SwRedlineTypeToOUString(
-                                           rRedlineTable[i]->GetRedlineData().GetType())
-                                           .toUtf8()
-                                           .getStr());
-            aTrackedChange.put("comment",
-                               rRedlineTable[i]->GetRedlineData().GetComment().toUtf8().getStr());
-            aTrackedChange.put("description", rRedlineTable[i]->GetDescr().toUtf8().getStr());
+            auto redlineNode = rJson.startNode("");
+            rJson.put("index", rRedlineTable[i]->GetId());
+            rJson.put("author", rRedlineTable[i]->GetAuthorString(1));
+            rJson.put("type", SwRedlineTypeToOUString(
+                                           rRedlineTable[i]->GetRedlineData().GetType()));
+            rJson.put("comment",
+                               rRedlineTable[i]->GetRedlineData().GetComment());
+            rJson.put("description", rRedlineTable[i]->GetDescr());
             OUString sDateTime = utl::toISO8601(
                 rRedlineTable[i]->GetRedlineData().GetTimeStamp().GetUNODateTime());
-            aTrackedChange.put("dateTime", sDateTime.toUtf8().getStr());
+            rJson.put("dateTime", sDateTime);
 
             SwContentNode* pContentNd = rRedlineTable[i]->GetContentNode();
             SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
@@ -3299,19 +3298,10 @@ OUString SwXTextDocument::getTrackedChanges()
                     aRects.push_back(rNextRect.SVRect().toString());
 
                 const OString sRects = comphelper::string::join("; ", aRects);
-                aTrackedChange.put("textRange", sRects.getStr());
+                rJson.put("textRange", sRects);
             }
-
-            aTrackedChanges.push_back(std::make_pair("", aTrackedChange));
         }
     }
-
-    boost::property_tree::ptree aTree;
-    aTree.add_child("redlines", aTrackedChanges);
-    std::stringstream aStream;
-    boost::property_tree::write_json(aStream, aTree);
-
-    return OUString::fromUtf8(aStream.str().c_str());
 }
 
 OUString SwXTextDocument::getTrackedChangeAuthors()
diff --git a/tools/CppunitTest_tools_test.mk b/tools/CppunitTest_tools_test.mk
index 5672be53353f..46a6cf5242cd 100644
--- a/tools/CppunitTest_tools_test.mk
+++ b/tools/CppunitTest_tools_test.mk
@@ -19,6 +19,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,tools_test, \
     tools/qa/cppunit/test_time \
     tools/qa/cppunit/test_fract \
     tools/qa/cppunit/test_inetmime \
+    tools/qa/cppunit/test_json_writer \
     tools/qa/cppunit/test_pathutils \
     tools/qa/cppunit/test_reversemap \
     tools/qa/cppunit/test_stream \
diff --git a/tools/Library_tl.mk b/tools/Library_tl.mk
index 233e75db0050..ecad06913ed2 100644
--- a/tools/Library_tl.mk
+++ b/tools/Library_tl.mk
@@ -69,6 +69,7 @@ $(eval $(call gb_Library_add_exception_objects,tl,\
     tools/source/memtools/multisel \
     tools/source/misc/cpuid \
     tools/source/misc/extendapplicationenvironment \
+    tools/source/misc/json_writer \
     tools/source/ref/globname \
     tools/source/ref/ref \
     tools/source/stream/stream \
diff --git a/tools/qa/cppunit/test_json_writer.cxx b/tools/qa/cppunit/test_json_writer.cxx
new file mode 100644
index 000000000000..c4e9331c8d17
--- /dev/null
+++ b/tools/qa/cppunit/test_json_writer.cxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <cppunit/extensions/HelperMacros.h>
+#include <test/bootstrapfixture.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/stream.hxx>
+#include <tools/json_writer.hxx>
+
+namespace
+{
+class JsonWriterTest : public test::BootstrapFixture
+{
+public:
+    JsonWriterTest()
+        : BootstrapFixture(true, false)
+    {
+    }
+
+    virtual void setUp() override {}
+
+    void test1();
+
+    CPPUNIT_TEST_SUITE(JsonWriterTest);
+    CPPUNIT_TEST(test1);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+void JsonWriterTest::test1()
+{
+    tools::JsonWriter aJson;
+
+    {
+        auto testNode = aJson.startNode("node");
+        aJson.put("oustring", OUString("val1"));
+        aJson.put("ostring", OString("val2"));
+        aJson.put("charptr", "val3");
+        aJson.put("int", 12);
+    }
+
+    std::unique_ptr<char[]> result(aJson.extractData());
+
+    CPPUNIT_ASSERT_EQUAL(std::string("{ \"node\": { \"oustring\": \"val1\", \"ostring\": \"val2\", "
+                                     "\"charptr\": \"val3\", \"int\": 12}}"),
+                         std::string(result.get()));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(JsonWriterTest);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/tools/source/misc/json_writer.cxx b/tools/source/misc/json_writer.cxx
new file mode 100644
index 000000000000..a233381038c4
--- /dev/null
+++ b/tools/source/misc/json_writer.cxx
@@ -0,0 +1,253 @@
+/* -*- 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 <tools/json_writer.hxx>
+#include <stdio.h>
+#include <cstring>
+
+namespace tools
+{
+/** These buffers are short-lived, so rather waste some space and avoid the cost of
+ * repeated calls into the allocator */
+constexpr int DEFAULT_BUFFER_SIZE = 2048;
+
+JsonWriter::JsonWriter()
+    : mSpaceAllocated(DEFAULT_BUFFER_SIZE)
+    , maBuffer(new char[mSpaceAllocated])
+    , mStartNodeCount(0)
+    , mPos(maBuffer.get())
+{
+    *mPos = '{';
+    ++mPos;
+    *mPos = ' ';
+    ++mPos;
+}
+
+JsonWriter::~JsonWriter() { assert(!maBuffer && "forgot to extract data?"); }
+
+ScopedJsonWriterNode JsonWriter::startNode(const char* pNodeName)
+{
+    auto len = strlen(pNodeName);
+    ensureSpace(len + 4);
+    *mPos = '"';
+    ++mPos;
+    memcpy(mPos, pNodeName, len);
+    mPos += len;
+    strncpy(mPos, "\": { ", 5);
+    mPos += 5;
+    mStartNodeCount++;
+    mbFirstFieldInNode = true;
+    return ScopedJsonWriterNode(*this);
+}
+
+void JsonWriter::endNode()
+{
+    assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
+    --mStartNodeCount;
+    ensureSpace(1);
+    *mPos = '}';
+    ++mPos;
+}
+
+void JsonWriter::put(const char* pPropName, const OUString& rPropVal)
+{
+    addCommaBeforeField();
+
+    auto nPropNameLength = strlen(pPropName);
+    auto nWorstCasePropValLength = rPropVal.getLength() * 2;
+    ensureSpace(nPropNameLength + nWorstCasePropValLength + 6);
+    *mPos = '"';
+    ++mPos;
+    memcpy(mPos, pPropName, nPropNameLength);
+    mPos += nPropNameLength;
+    strncpy(mPos, "\": \"", 4);
+    mPos += 4;
+
+    // Convert from UTF-16 to UTF-8 and perform escaping
+    for (int i = 0; i < rPropVal.getLength(); ++i)
+    {
+        sal_Unicode ch = rPropVal[i];
+        if (ch == '\\')
+        {
+            *mPos = static_cast<char>(ch);
+            ++mPos;
+            *mPos = static_cast<char>(ch);
+            ++mPos;
+        }
+        else if (ch == '"')
+        {
+            *mPos = '\\';
+            ++mPos;
+            *mPos = static_cast<char>(ch);
+            ++mPos;
+        }
+        else if (ch <= 0x7F)
+        {
+            *mPos = static_cast<char>(ch);
+            ++mPos;
+        }
+        else if (ch <= 0x7FF)
+        {
+            *mPos = 0xC0 | (ch >> 6); /* 110xxxxx */
+            ++mPos;
+            *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
+            ++mPos;
+        }
+        else
+        {
+            *mPos = 0xE0 | (ch >> 12); /* 1110xxxx */
+            ++mPos;
+            *mPos = 0x80 | ((ch >> 6) & 0x3F); /* 10xxxxxx */
+            ++mPos;
+            *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
+            ++mPos;
+        }
+    }
+
+    *mPos = '"';
+    ++mPos;
+}
+
+void JsonWriter::put(const char* pPropName, const OString& rPropVal)
+{
+    addCommaBeforeField();
+
+    auto nPropNameLength = strlen(pPropName);
+    auto nWorstCasePropValLength = rPropVal.getLength();
+    ensureSpace(nPropNameLength + nWorstCasePropValLength + 6);
+    *mPos = '"';
+    ++mPos;
+    memcpy(mPos, pPropName, nPropNameLength);
+    mPos += nPropNameLength;
+    strncpy(mPos, "\": \"", 4);
+    mPos += 4;
+
+    // copy and perform escaping
+    for (int i = 0; i < rPropVal.getLength(); ++i)
+    {
+        char ch = rPropVal[i];
+        if (ch == '\\')
+        {
+            *mPos = ch;
+            ++mPos;
+            *mPos = ch;
+            ++mPos;
+        }
+        else if (ch == '"')
+        {
+            *mPos = '\\';
+            ++mPos;
+            *mPos = ch;
+            ++mPos;
+        }
+        else
+        {
+            *mPos = ch;
+            ++mPos;
+        }
+    }
+
+    *mPos = '"';
+    ++mPos;
+}
+
+void JsonWriter::put(const char* pPropName, const char* pPropVal)
+{
+    addCommaBeforeField();
+
+    auto nPropNameLength = strlen(pPropName);
+    auto nPropValLength = strlen(pPropVal);
+    auto nWorstCasePropValLength = nPropValLength * 2;
+    ensureSpace(nPropNameLength + nWorstCasePropValLength + 6);
+    *mPos = '"';
+    ++mPos;
+    memcpy(mPos, pPropName, nPropNameLength);
+    mPos += nPropNameLength;
+    strncpy(mPos, "\": \"", 4);
+    mPos += 4;
+
+    // copy and perform escaping
+    for (;;)
+    {
+        char ch = *pPropVal;
+        if (!ch)
+            break;
+        ++pPropVal;
+        if (ch == '\\')
+        {
+            *mPos = ch;
+            ++mPos;
+            *mPos = ch;
+            ++mPos;
+        }
+        else if (ch == '"')
+        {
+            *mPos = '\\';
+            ++mPos;
+            *mPos = ch;
+            ++mPos;
+        }
+        else
+        {
+            *mPos = ch;
+            ++mPos;
+        }
+    }
+
+    *mPos = '"';
+    ++mPos;
+}
+
+void JsonWriter::put(const char* pPropName, int nPropVal)
+{
+    addCommaBeforeField();
+
+    auto nPropNameLength = strlen(pPropName);
+    auto nWorstCasePropValLength = 32;
+    ensureSpace(nPropNameLength + nWorstCasePropValLength + 6);
+    *mPos = '"';
+    ++mPos;
+    memcpy(mPos, pPropName, nPropNameLength);
+    mPos += nPropNameLength;
+    strncpy(mPos, "\": ", 3);
+    mPos += 3;
+
+    mPos += sprintf(mPos, "%d", nPropVal);
+}
+
+void JsonWriter::addCommaBeforeField()
+{
+    if (mbFirstFieldInNode)
+        mbFirstFieldInNode = false;
+    else
+    {
+        *mPos = ',';
+        ++mPos;
+        *mPos = ' ';
+        ++mPos;
+    }
+}
+
+/** Hands ownership of the the underlying storage buffer to the caller,
+  * after this no more document modifications may be written. */
+char* JsonWriter::extractData()
+{
+    assert(mStartNodeCount == 0 && "did not close all nodes");
+    assert(maBuffer && "data already extracted");
+    // add closing brace
+    *mPos = '}';
+    ++mPos;
+    // null-terminate
+    *mPos = 0;
+    mPos = nullptr;
+    return maBuffer.release();
+}
+
+} // namespace tools
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */


More information about the Libreoffice-commits mailing list