1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <tools/json_writer.hxx>
13 #include <rtl/math.hxx>
17 /** These buffers are short-lived, so rather waste some space and avoid the cost of
18 * repeated calls into the allocator */
19 constexpr int DEFAULT_BUFFER_SIZE
= 2048;
21 JsonWriter::JsonWriter()
22 : mpBuffer(static_cast<char*>(malloc(DEFAULT_BUFFER_SIZE
)))
24 , mSpaceAllocated(DEFAULT_BUFFER_SIZE
)
26 , mbFirstFieldInNode(true)
37 JsonWriter::~JsonWriter()
39 assert(mbClosed
&& "forgot to extract data?");
43 ScopedJsonWriterNode
JsonWriter::startNode(std::string_view pNodeName
)
45 putLiteral(pNodeName
, "{ ");
48 mbFirstFieldInNode
= true;
50 return ScopedJsonWriterNode(*this);
53 void JsonWriter::endNode()
55 assert(mStartNodeCount
&& "mismatched StartNode/EndNode somewhere");
60 mbFirstFieldInNode
= false;
65 ScopedJsonWriterArray
JsonWriter::startArray(std::string_view pNodeName
)
67 putLiteral(pNodeName
, "[ ");
70 mbFirstFieldInNode
= true;
72 return ScopedJsonWriterArray(*this);
75 void JsonWriter::endArray()
77 assert(mStartNodeCount
&& "mismatched StartNode/EndNode somewhere");
82 mbFirstFieldInNode
= false;
87 ScopedJsonWriterStruct
JsonWriter::startStruct()
91 addCommaBeforeField();
98 mbFirstFieldInNode
= true;
102 return ScopedJsonWriterStruct(*this);
105 void JsonWriter::endStruct()
107 assert(mStartNodeCount
&& "mismatched StartNode/EndNode somewhere");
112 mbFirstFieldInNode
= false;
117 static char getEscapementChar(char ch
)
136 static bool writeEscapedSequence(sal_uInt32 ch
, char*& pos
)
149 *pos
++ = getEscapementChar(ch
);
151 // Special processing of U+2028 and U+2029, which are valid JSON, but invalid JavaScript
152 // Write them in escaped '\u2028' or '\u2029' form
160 *pos
++ = ch
== 0x2028 ? '8' : '9';
167 void JsonWriter::writeEscapedOUString(const OUString
& rPropVal
)
172 // Convert from UTF-16 to UTF-8 and perform escaping
174 while (i
< rPropVal
.getLength())
176 sal_uInt32 ch
= rPropVal
.iterateCodePoints(&i
);
177 if (writeEscapedSequence(ch
, mPos
))
181 *mPos
= static_cast<char>(ch
);
184 else if (ch
<= 0x7FF)
186 *mPos
= 0xC0 | (ch
>> 6); /* 110xxxxx */
188 *mPos
= 0x80 | (ch
& 0x3F); /* 10xxxxxx */
191 else if (ch
<= 0xFFFF)
193 *mPos
= 0xE0 | (ch
>> 12); /* 1110xxxx */
195 *mPos
= 0x80 | ((ch
>> 6) & 0x3F); /* 10xxxxxx */
197 *mPos
= 0x80 | (ch
& 0x3F); /* 10xxxxxx */
202 *mPos
= 0xF0 | (ch
>> 18); /* 11110xxx */
204 *mPos
= 0x80 | ((ch
>> 12) & 0x3F); /* 10xxxxxx */
206 *mPos
= 0x80 | ((ch
>> 6) & 0x3F); /* 10xxxxxx */
208 *mPos
= 0x80 | (ch
& 0x3F); /* 10xxxxxx */
219 void JsonWriter::put(std::string_view pPropName
, const OUString
& rPropVal
)
221 // Values can be any UTF-8,
222 // if the string only contains of 0x2028, it will be expanded 6 times (see writeEscapedSequence)
223 auto nWorstCasePropValLength
= rPropVal
.getLength() * 6 + 2;
224 ensureSpaceAndWriteNameColon(pPropName
, nWorstCasePropValLength
);
226 writeEscapedOUString(rPropVal
);
229 void JsonWriter::put(std::string_view pPropName
, std::string_view rPropVal
)
231 // escaping can double the length, plus quotes
232 auto nWorstCasePropValLength
= rPropVal
.size() * 2 + 2;
233 ensureSpaceAndWriteNameColon(pPropName
, nWorstCasePropValLength
);
238 // copy and perform escaping
239 bool bReachedEnd
= false;
240 for (size_t i
= 0; i
< rPropVal
.size() && !bReachedEnd
; ++i
)
242 char ch
= rPropVal
[i
];
253 writeEscapedSequence(ch
, mPos
);
258 case '\xE2': // Special processing of U+2028 and U+2029
259 if (i
+ 2 < rPropVal
.size() && rPropVal
[i
+ 1] == '\x80'
260 && (rPropVal
[i
+ 2] == '\xA8' || rPropVal
[i
+ 2] == '\xA9'))
262 writeEscapedSequence(rPropVal
[i
+ 2] == '\xA8' ? 0x2028 : 0x2029, mPos
);
280 void JsonWriter::put(std::string_view pPropName
, bool nPropVal
)
282 putLiteral(pPropName
, nPropVal
? std::string_view("true") : std::string_view("false"));
285 void JsonWriter::putSimpleValue(const OUString
& rPropVal
)
287 auto nWorstCasePropValLength
= rPropVal
.getLength() * 6;
288 ensureSpace(nWorstCasePropValLength
+ 4);
290 addCommaBeforeField();
292 writeEscapedOUString(rPropVal
);
295 void JsonWriter::putRaw(std::string_view rRawBuf
)
297 ensureSpace(rRawBuf
.size() + 2);
299 addCommaBeforeField();
301 memcpy(mPos
, rRawBuf
.data(), rRawBuf
.size());
302 mPos
+= rRawBuf
.size();
307 void JsonWriter::addCommaBeforeField()
309 if (mbFirstFieldInNode
)
310 mbFirstFieldInNode
= false;
320 void JsonWriter::ensureSpace(int noMoreBytesRequired
)
322 assert(!mbClosed
&& "already extracted data");
323 int currentUsed
= mPos
- mpBuffer
;
324 if (currentUsed
+ noMoreBytesRequired
>= mSpaceAllocated
)
326 auto newSize
= (currentUsed
+ noMoreBytesRequired
) * 2;
327 mpBuffer
= static_cast<char*>(realloc(mpBuffer
, newSize
));
328 mPos
= mpBuffer
+ currentUsed
;
329 mSpaceAllocated
= newSize
;
335 void JsonWriter::ensureSpaceAndWriteNameColon(std::string_view name
, int valSize
)
337 // we assume property names are ascii
338 ensureSpace(name
.size() + valSize
+ 6);
340 addCommaBeforeField();
344 memcpy(mPos
, name
.data(), name
.size());
346 memcpy(mPos
, "\": ", 3);
350 void JsonWriter::putLiteral(std::string_view propName
, std::string_view propValue
)
352 ensureSpaceAndWriteNameColon(propName
, propValue
.size());
353 memcpy(mPos
, propValue
.data(), propValue
.size());
354 mPos
+= propValue
.size();
359 OString
JsonWriter::finishAndGetAsOString()
361 assert(mStartNodeCount
== 0 && "did not close all nodes");
362 assert(!mbClosed
&& "data already extracted");
371 OString
ret(mpBuffer
, mPos
- mpBuffer
);
375 bool JsonWriter::isDataEquals(std::string_view s
) const
377 return std::string_view(mpBuffer
, static_cast<size_t>(mPos
- mpBuffer
)) == s
;
381 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */