[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.4' - 2 commits - 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
Fri Jun 19 14:10:17 UTC 2020


 desktop/source/lib/init.cxx           |   31 +---
 include/tools/json_writer.hxx         |   88 +++++++++++
 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 |   65 ++++++++
 tools/source/misc/json_writer.cxx     |  259 ++++++++++++++++++++++++++++++++++
 13 files changed, 458 insertions(+), 72 deletions(-)

New commits:
commit 37db9d6876e6f64153d37d53bc0ab7022d8f2b68
Author:     Noel Grandin <noel.grandin at collabora.co.uk>
AuthorDate: Fri Jun 19 09:57:58 2020 +0200
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Fri Jun 19 14:53:53 2020 +0100

    asan: need to use malloc instead of new for tools::JsonWriter
    
    because the LOK API expects memory returned from those calls to be
    malloc'ed
    
    Change-Id: If6fbfb60c433bd6e58bc90a9a90a90663e2e1c60
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96676
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <sbergman at redhat.com>

diff --git a/include/tools/json_writer.hxx b/include/tools/json_writer.hxx
index e811bb90e277..34930584730d 100644
--- a/include/tools/json_writer.hxx
+++ b/include/tools/json_writer.hxx
@@ -11,6 +11,7 @@
 #include <tools/toolsdllapi.h>
 #include <rtl/ustring.hxx>
 #include <memory>
+#include <algorithm>
 
 /** Simple JSON encoder designed specifically for LibreOfficeKit purposes.
  *
@@ -27,7 +28,7 @@ class TOOLS_DLLPUBLIC JsonWriter
     friend class ScopedJsonWriterNode;
 
     int mSpaceAllocated;
-    std::unique_ptr<char[]> maBuffer;
+    char* mpBuffer;
     int mStartNodeCount;
     char* mPos;
     bool mbFirstFieldInNode;
@@ -53,14 +54,15 @@ private:
 
     inline void ensureSpace(int noMoreBytesRequired)
     {
-        int currentUsed = mPos - maBuffer.get();
+        int currentUsed = mPos - mpBuffer;
         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();
+            char* pNew = static_cast<char*>(malloc(newSize));
+            memcpy(pNew, mpBuffer, currentUsed);
+            free(mpBuffer);
+            mpBuffer = pNew;
+            mPos = mpBuffer;
         }
     }
 };
diff --git a/tools/qa/cppunit/test_json_writer.cxx b/tools/qa/cppunit/test_json_writer.cxx
index c4e9331c8d17..e27afc95f712 100644
--- a/tools/qa/cppunit/test_json_writer.cxx
+++ b/tools/qa/cppunit/test_json_writer.cxx
@@ -7,6 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include <sal/config.h>
+
+#include <cstdlib>
+
 #include <cppunit/extensions/HelperMacros.h>
 #include <test/bootstrapfixture.hxx>
 #include <rtl/ustring.hxx>
@@ -44,7 +48,11 @@ void JsonWriterTest::test1()
         aJson.put("int", 12);
     }
 
-    std::unique_ptr<char[]> result(aJson.extractData());
+    struct Free
+    {
+        void operator()(void* p) const { std::free(p); }
+    };
+    std::unique_ptr<char, Free> result(aJson.extractData());
 
     CPPUNIT_ASSERT_EQUAL(std::string("{ \"node\": { \"oustring\": \"val1\", \"ostring\": \"val2\", "
                                      "\"charptr\": \"val3\", \"int\": 12}}"),
diff --git a/tools/source/misc/json_writer.cxx b/tools/source/misc/json_writer.cxx
index a233381038c4..5bba05a4c21d 100644
--- a/tools/source/misc/json_writer.cxx
+++ b/tools/source/misc/json_writer.cxx
@@ -19,9 +19,9 @@ constexpr int DEFAULT_BUFFER_SIZE = 2048;
 
 JsonWriter::JsonWriter()
     : mSpaceAllocated(DEFAULT_BUFFER_SIZE)
-    , maBuffer(new char[mSpaceAllocated])
+    , mpBuffer(static_cast<char*>(malloc(mSpaceAllocated)))
     , mStartNodeCount(0)
-    , mPos(maBuffer.get())
+    , mPos(mpBuffer)
 {
     *mPos = '{';
     ++mPos;
@@ -29,7 +29,11 @@ JsonWriter::JsonWriter()
     ++mPos;
 }
 
-JsonWriter::~JsonWriter() { assert(!maBuffer && "forgot to extract data?"); }
+JsonWriter::~JsonWriter()
+{
+    assert(!mpBuffer && "forgot to extract data?");
+    free(mpBuffer);
+}
 
 ScopedJsonWriterNode JsonWriter::startNode(const char* pNodeName)
 {
@@ -239,14 +243,16 @@ void JsonWriter::addCommaBeforeField()
 char* JsonWriter::extractData()
 {
     assert(mStartNodeCount == 0 && "did not close all nodes");
-    assert(maBuffer && "data already extracted");
+    assert(mpBuffer && "data already extracted");
     // add closing brace
     *mPos = '}';
     ++mPos;
     // null-terminate
     *mPos = 0;
     mPos = nullptr;
-    return maBuffer.release();
+    char* pRet = nullptr;
+    std::swap(pRet, mpBuffer);
+    return pRet;
 }
 
 } // namespace tools
commit aac0764cae772d4ace7a279ae9800319dc61f46c
Author:     Noel Grandin <noelgrandin at gmail.com>
AuthorDate: Mon Jun 15 20:32:25 2020 +0200
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Fri Jun 19 14:46:16 2020 +0100

    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 060a36581223..67ce622436ee 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -124,6 +124,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>
@@ -4751,47 +4752,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
     {
@@ -4801,12 +4796,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 833f76c0dcde..4fcf0c9f05e1 100644
--- a/include/vcl/ITiledRenderable.hxx
+++ b/include/vcl/ITiledRenderable.hxx
@@ -24,6 +24,7 @@ namespace com::sun::star::datatransfer { namespace 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 fcaef910b69c..baa51b30419e 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 0f74bd9451b5..78416db9e4b5 100644
--- a/sc/inc/docuno.hxx
+++ b/sc/inc/docuno.hxx
@@ -373,7 +373,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 db490b114f3b..3deab497102c 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>
@@ -4665,34 +4666,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)
@@ -4706,12 +4704,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 a89b32bb92ed..2671b02c2edc 100644
--- a/sc/source/ui/unoobj/docuno.cxx
+++ b/sc/source/ui/unoobj/docuno.cxx
@@ -939,17 +939,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 fb9c2fcc926e..ffb4c790bd96 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 9f0b2e20d72f..c7f66b67640e 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -182,6 +182,7 @@
 #include <redline.hxx>
 #include <DocumentRedlineManager.hxx>
 #include <xmloff/odffields.hxx>
+#include <tools/json_writer.hxx>
 
 #define TWIPS_PER_PIXEL 15
 
@@ -3272,9 +3273,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;
@@ -3284,19 +3285,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());
@@ -3316,19 +3315,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