summaryrefslogtreecommitdiff
path: root/tools/source/misc/json_writer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'tools/source/misc/json_writer.cxx')
-rw-r--r--tools/source/misc/json_writer.cxx343
1 files changed, 130 insertions, 213 deletions
diff --git a/tools/source/misc/json_writer.cxx b/tools/source/misc/json_writer.cxx
index d6e34179f930..f5c262573418 100644
--- a/tools/source/misc/json_writer.cxx
+++ b/tools/source/misc/json_writer.cxx
@@ -8,8 +8,9 @@
*/
#include <tools/json_writer.hxx>
+#include <o3tl/string_view.hxx>
#include <stdio.h>
-#include <algorithm>
+#include <cstddef>
#include <cstring>
#include <rtl/math.hxx>
@@ -20,103 +21,77 @@ namespace tools
constexpr int DEFAULT_BUFFER_SIZE = 2048;
JsonWriter::JsonWriter()
- : mpBuffer(static_cast<char*>(malloc(DEFAULT_BUFFER_SIZE)))
- , mPos(mpBuffer)
+ : mpBuffer(rtl_string_alloc(DEFAULT_BUFFER_SIZE))
+ , mPos(mpBuffer->buffer)
, mSpaceAllocated(DEFAULT_BUFFER_SIZE)
, mStartNodeCount(0)
, mbFirstFieldInNode(true)
+ , mbClosed(false)
{
*mPos = '{';
++mPos;
*mPos = ' ';
++mPos;
+
+ addValidationMark();
}
JsonWriter::~JsonWriter()
{
- assert(!mpBuffer && "forgot to extract data?");
- free(mpBuffer);
+ assert(mbClosed && "forgot to extract data?");
+ rtl_string_release(mpBuffer);
}
-ScopedJsonWriterNode JsonWriter::startNode(const char* pNodeName)
+JsonWriter::ScopedJsonWriterNode<'}'> JsonWriter::startNode(std::string_view pNodeName)
{
- auto len = strlen(pNodeName);
- ensureSpace(len + 6);
-
- addCommaBeforeField();
+ putLiteral(pNodeName, "{ ");
- *mPos = '"';
- ++mPos;
- memcpy(mPos, pNodeName, len);
- mPos += len;
- memcpy(mPos, "\": { ", 5);
- mPos += 5;
mStartNodeCount++;
mbFirstFieldInNode = true;
- return ScopedJsonWriterNode(*this);
+
+ return { *this };
}
-void JsonWriter::endNode()
+void JsonWriter::endNode(char closing)
{
assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
--mStartNodeCount;
ensureSpace(1);
- *mPos = '}';
+ *mPos = closing;
++mPos;
mbFirstFieldInNode = false;
+
+ validate();
}
-ScopedJsonWriterArray JsonWriter::startArray(const char* pNodeName)
+JsonWriter::ScopedJsonWriterNode<']'> JsonWriter::startArray(std::string_view pNodeName)
{
- auto len = strlen(pNodeName);
- ensureSpace(len + 6);
+ putLiteral(pNodeName, "[ ");
- addCommaBeforeField();
-
- *mPos = '"';
- ++mPos;
- memcpy(mPos, pNodeName, len);
- mPos += len;
- memcpy(mPos, "\": [ ", 5);
- mPos += 5;
mStartNodeCount++;
mbFirstFieldInNode = true;
- return ScopedJsonWriterArray(*this);
-}
-void JsonWriter::endArray()
-{
- assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
- --mStartNodeCount;
- ensureSpace(1);
- *mPos = ']';
- ++mPos;
- mbFirstFieldInNode = false;
+ return { *this };
}
-ScopedJsonWriterStruct JsonWriter::startStruct()
+JsonWriter::ScopedJsonWriterNode<']'> JsonWriter::startAnonArray()
{
- ensureSpace(6);
-
- addCommaBeforeField();
+ putRaw("[ ");
- *mPos = '{';
- ++mPos;
- *mPos = ' ';
- ++mPos;
mStartNodeCount++;
mbFirstFieldInNode = true;
- return ScopedJsonWriterStruct(*this);
+
+ return { *this };
}
-void JsonWriter::endStruct()
+JsonWriter::ScopedJsonWriterNode<'}'> JsonWriter::startStruct()
{
- assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
- --mStartNodeCount;
- ensureSpace(1);
- *mPos = '}';
- ++mPos;
- mbFirstFieldInNode = false;
+ putRaw("{ ");
+
+ mStartNodeCount++;
+ mbFirstFieldInNode = true;
+
+ return { *this };
}
static char getEscapementChar(char ch)
@@ -153,29 +128,30 @@ static bool writeEscapedSequence(sal_uInt32 ch, char*& pos)
*pos++ = '\\';
*pos++ = getEscapementChar(ch);
return true;
- // Special processing of U+2028 and U+2029, which are valid JSON, but invalid JavaScript
- // Write them in escaped '\u2028' or '\u2029' form
- case 0x2028:
- case 0x2029:
- *pos++ = '\\';
- *pos++ = 'u';
- *pos++ = '2';
- *pos++ = '0';
- *pos++ = '2';
- *pos++ = ch == 0x2028 ? '8' : '9';
- return true;
default:
+ if (ch <= 0x1f || ch == 0x2028 || ch == 0x2029)
+ {
+ // control characters, plus special processing of U+2028 and U+2029, which are valid
+ // JSON, but invalid JavaScript. Write them in escaped '\u2028' or '\u2029' form
+ int written = snprintf(pos, 7, "\\u%.4x", static_cast<unsigned int>(ch));
+ if (written > 0)
+ pos += written;
+ return true;
+ }
return false;
}
}
-void JsonWriter::writeEscapedOUString(const OUString& rPropVal)
+void JsonWriter::writeEscapedOUString(std::u16string_view rPropVal)
{
+ *mPos = '"';
+ ++mPos;
+
// Convert from UTF-16 to UTF-8 and perform escaping
- sal_Int32 i = 0;
- while (i < rPropVal.getLength())
+ std::size_t i = 0;
+ while (i < rPropVal.size())
{
- sal_uInt32 ch = rPropVal.iterateCodePoints(&i);
+ sal_uInt32 ch = o3tl::iterateCodePoints(rPropVal, &i);
if (writeEscapedSequence(ch, mPos))
continue;
if (ch <= 0x7F)
@@ -211,158 +187,91 @@ void JsonWriter::writeEscapedOUString(const OUString& rPropVal)
++mPos;
}
}
+
+ *mPos = '"';
+ ++mPos;
+
+ validate();
}
-void JsonWriter::put(const char* pPropName, const OUString& rPropVal)
+void JsonWriter::put(std::u16string_view pPropName, std::u16string_view rPropVal)
{
- auto nPropNameLength = strlen(pPropName);
+ auto nPropNameLength = pPropName.length();
// But values can be any UTF-8,
// if the string only contains of 0x2028, it will be expanded 6 times (see writeEscapedSequence)
- auto nWorstCasePropValLength = rPropVal.getLength() * 6;
+ auto nWorstCasePropValLength = rPropVal.size() * 6;
ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
addCommaBeforeField();
- *mPos = '"';
- ++mPos;
- memcpy(mPos, pPropName, nPropNameLength);
- mPos += nPropNameLength;
- memcpy(mPos, "\": \"", 4);
- mPos += 4;
+ writeEscapedOUString(pPropName);
+
+ memcpy(mPos, ": ", 2);
+ mPos += 2;
writeEscapedOUString(rPropVal);
- *mPos = '"';
- ++mPos;
+ validate();
}
-void JsonWriter::put(const char* pPropName, std::string_view rPropVal)
+void JsonWriter::put(std::string_view pPropName, const OUString& rPropVal)
{
- // we assume property names are ascii
- auto nPropNameLength = strlen(pPropName);
- // escaping can double the length
- auto nWorstCasePropValLength = rPropVal.size() * 2;
- ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
+ // Values can be any UTF-8,
+ // if the string only contains of 0x2028, it will be expanded 6 times (see writeEscapedSequence)
+ auto nWorstCasePropValLength = rPropVal.getLength() * 6 + 2;
+ ensureSpaceAndWriteNameColon(pPropName, nWorstCasePropValLength);
- addCommaBeforeField();
+ writeEscapedOUString(rPropVal);
+}
+
+void JsonWriter::put(std::string_view pPropName, std::string_view rPropVal)
+{
+ // escaping can double the length, plus quotes
+ auto nWorstCasePropValLength = rPropVal.size() * 2 + 2;
+ ensureSpaceAndWriteNameColon(pPropName, nWorstCasePropValLength);
*mPos = '"';
++mPos;
- memcpy(mPos, pPropName, nPropNameLength);
- mPos += nPropNameLength;
- memcpy(mPos, "\": \"", 4);
- mPos += 4;
// copy and perform escaping
for (size_t i = 0; i < rPropVal.size(); ++i)
{
char ch = rPropVal[i];
- switch (ch)
+ if (ch == 0)
+ break;
+ // Special processing of U+2028 and U+2029
+ if (ch == '\xE2' && i + 2 < rPropVal.size() && rPropVal[i + 1] == '\x80'
+ && (rPropVal[i + 2] == '\xA8' || rPropVal[i + 2] == '\xA9'))
{
- case '\b':
- case '\t':
- case '\n':
- case '\f':
- case '\r':
- case '"':
- case '/':
- case '\\':
- writeEscapedSequence(ch, mPos);
- break;
- case '\xE2': // Special processing of U+2028 and U+2029
- if (i + 2 < rPropVal.size() && rPropVal[i + 1] == '\x80'
- && (rPropVal[i + 2] == '\xA8' || rPropVal[i + 2] == '\xA9'))
- {
- writeEscapedSequence(rPropVal[i + 2] == '\xA8' ? 0x2028 : 0x2029, mPos);
- i += 2;
- break;
- }
- [[fallthrough]];
- default:
- *mPos = ch;
- ++mPos;
- break;
+ writeEscapedSequence(rPropVal[i + 2] == '\xA8' ? 0x2028 : 0x2029, mPos);
+ i += 2;
+ }
+ else if (!writeEscapedSequence(static_cast<sal_uInt32>(ch), mPos))
+ {
+ *mPos = ch;
+ ++mPos;
}
}
*mPos = '"';
++mPos;
-}
-void JsonWriter::put(const char* pPropName, sal_Int64 nPropVal)
-{
- auto nPropNameLength = strlen(pPropName);
- auto nWorstCasePropValLength = 32;
- ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
-
- addCommaBeforeField();
-
- *mPos = '"';
- ++mPos;
- memcpy(mPos, pPropName, nPropNameLength);
- mPos += nPropNameLength;
- memcpy(mPos, "\": ", 3);
- mPos += 3;
-
- mPos += sprintf(mPos, "%" SAL_PRIdINT64, nPropVal);
+ validate();
}
-void JsonWriter::put(const char* pPropName, double fPropVal)
+void JsonWriter::put(std::string_view pPropName, bool nPropVal)
{
- OString sPropVal = rtl::math::doubleToString(fPropVal, rtl_math_StringFormat_F, 12, '.');
- auto nPropNameLength = strlen(pPropName);
- ensureSpace(nPropNameLength + sPropVal.getLength() + 8);
-
- addCommaBeforeField();
-
- *mPos = '"';
- ++mPos;
- memcpy(mPos, pPropName, nPropNameLength);
- mPos += nPropNameLength;
- memcpy(mPos, "\": ", 3);
- mPos += 3;
-
- memcpy(mPos, sPropVal.getStr(), sPropVal.getLength());
- mPos += sPropVal.getLength();
+ putLiteral(pPropName, nPropVal ? std::string_view("true") : std::string_view("false"));
}
-void JsonWriter::put(const char* pPropName, bool nPropVal)
+void JsonWriter::putSimpleValue(std::u16string_view rPropVal)
{
- auto nPropNameLength = strlen(pPropName);
- ensureSpace(nPropNameLength + 5 + 8);
-
- addCommaBeforeField();
-
- *mPos = '"';
- ++mPos;
- memcpy(mPos, pPropName, nPropNameLength);
- mPos += nPropNameLength;
- memcpy(mPos, "\": ", 3);
- mPos += 3;
-
- const char* pVal;
- if (nPropVal)
- pVal = "true";
- else
- pVal = "false";
- memcpy(mPos, pVal, strlen(pVal));
- mPos += strlen(pVal);
-}
-
-void JsonWriter::putSimpleValue(const OUString& rPropVal)
-{
- auto nWorstCasePropValLength = rPropVal.getLength() * 3;
+ auto nWorstCasePropValLength = rPropVal.size() * 6;
ensureSpace(nWorstCasePropValLength + 4);
addCommaBeforeField();
- *mPos = '"';
- ++mPos;
-
writeEscapedOUString(rPropVal);
-
- *mPos = '"';
- ++mPos;
}
void JsonWriter::putRaw(std::string_view rRawBuf)
@@ -373,6 +282,8 @@ void JsonWriter::putRaw(std::string_view rRawBuf)
memcpy(mPos, rRawBuf.data(), rRawBuf.size());
mPos += rRawBuf.size();
+
+ validate();
}
void JsonWriter::addCommaBeforeField()
@@ -390,54 +301,60 @@ void JsonWriter::addCommaBeforeField()
void JsonWriter::ensureSpace(int noMoreBytesRequired)
{
- assert(mpBuffer && "already extracted data");
- int currentUsed = mPos - mpBuffer;
+ assert(!mbClosed && "already extracted data");
+ int currentUsed = mPos - mpBuffer->buffer;
if (currentUsed + noMoreBytesRequired >= mSpaceAllocated)
{
auto newSize = (currentUsed + noMoreBytesRequired) * 2;
- mpBuffer = static_cast<char*>(realloc(mpBuffer, newSize));
- mPos = mpBuffer + currentUsed;
+ rtl_String* pNewBuffer = rtl_string_alloc(newSize);
+ memcpy(pNewBuffer->buffer, mpBuffer->buffer, currentUsed);
+ rtl_string_release(mpBuffer);
+ mpBuffer = pNewBuffer;
+ mPos = mpBuffer->buffer + currentUsed;
mSpaceAllocated = newSize;
+
+ addValidationMark();
}
}
-/** Hands ownership of the underlying storage buffer to the caller,
- * after this no more document modifications may be written. */
-std::pair<char*, int> JsonWriter::extractDataImpl()
+void JsonWriter::ensureSpaceAndWriteNameColon(std::string_view name, int valSize)
{
- assert(mStartNodeCount == 0 && "did not close all nodes");
- assert(mpBuffer && "data already extracted");
- ensureSpace(2);
- // add closing brace
- *mPos = '}';
+ // we assume property names are ascii
+ ensureSpace(name.size() + valSize + 6);
+
+ addCommaBeforeField();
+
+ *mPos = '"';
++mPos;
- // null-terminate
- *mPos = 0;
- const int sz = mPos - mpBuffer;
- mPos = nullptr;
- return { std::exchange(mpBuffer, nullptr), sz };
+ memcpy(mPos, name.data(), name.size());
+ mPos += name.size();
+ memcpy(mPos, "\": ", 3);
+ mPos += 3;
}
-OString JsonWriter::extractAsOString()
+void JsonWriter::putLiteral(std::string_view propName, std::string_view propValue)
{
- auto[pChar, sz] = extractDataImpl();
- OString ret(pChar, sz);
- free(pChar);
- return ret;
-}
+ ensureSpaceAndWriteNameColon(propName, propValue.size());
+ memcpy(mPos, propValue.data(), propValue.size());
+ mPos += propValue.size();
-std::string JsonWriter::extractAsStdString()
-{
- auto[pChar, sz] = extractDataImpl();
- std::string ret(pChar, sz);
- free(pChar);
- return ret;
+ validate();
}
-bool JsonWriter::isDataEquals(const std::string& s) const
+OString JsonWriter::finishAndGetAsOString()
{
- return s.length() == static_cast<size_t>(mPos - mpBuffer)
- && memcmp(s.data(), mpBuffer, s.length()) == 0;
+ assert(mStartNodeCount == 0 && "did not close all nodes");
+ assert(!mbClosed && "data already extracted");
+ ensureSpace(2);
+ // add closing brace
+ *mPos = '}';
+ ++mPos;
+ // null-terminate
+ *mPos = 0;
+ mbClosed = true;
+
+ mpBuffer->length = mPos - mpBuffer->buffer;
+ return mpBuffer;
}
} // namespace tools