diff options
Diffstat (limited to 'tools/source/misc/json_writer.cxx')
-rw-r--r-- | tools/source/misc/json_writer.cxx | 343 |
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 |