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>
14 #include <rtl/strbuf.hxx>
15 #include <rtl/math.hxx>
19 /** These buffers are short-lived, so rather waste some space and avoid the cost of
20 * repeated calls into the allocator */
21 constexpr int DEFAULT_BUFFER_SIZE
= 2048;
23 JsonWriter::JsonWriter()
24 : mSpaceAllocated(DEFAULT_BUFFER_SIZE
)
25 , mpBuffer(static_cast<char*>(malloc(mSpaceAllocated
)))
28 , mbFirstFieldInNode(true)
38 JsonWriter::~JsonWriter()
40 assert(!mpBuffer
&& "forgot to extract data?");
44 ScopedJsonWriterNode
JsonWriter::startNode(const char* pNodeName
)
46 auto len
= strlen(pNodeName
);
49 addCommaBeforeField();
53 memcpy(mPos
, pNodeName
, len
);
55 memcpy(mPos
, "\": { ", 5);
58 mbFirstFieldInNode
= true;
62 return ScopedJsonWriterNode(*this);
65 void JsonWriter::endNode()
67 assert(mStartNodeCount
&& "mismatched StartNode/EndNode somewhere");
72 mbFirstFieldInNode
= false;
77 ScopedJsonWriterArray
JsonWriter::startArray(const char* pNodeName
)
79 auto len
= strlen(pNodeName
);
82 addCommaBeforeField();
86 memcpy(mPos
, pNodeName
, len
);
88 memcpy(mPos
, "\": [ ", 5);
91 mbFirstFieldInNode
= true;
95 return ScopedJsonWriterArray(*this);
98 void JsonWriter::endArray()
100 assert(mStartNodeCount
&& "mismatched StartNode/EndNode somewhere");
105 mbFirstFieldInNode
= false;
110 ScopedJsonWriterStruct
JsonWriter::startStruct()
114 addCommaBeforeField();
121 mbFirstFieldInNode
= true;
125 return ScopedJsonWriterStruct(*this);
128 void JsonWriter::endStruct()
130 assert(mStartNodeCount
&& "mismatched StartNode/EndNode somewhere");
135 mbFirstFieldInNode
= false;
140 void JsonWriter::writeEscapedOUString(const OUString
& rPropVal
)
142 // Convert from UTF-16 to UTF-8 and perform escaping
144 while (i
< rPropVal
.getLength())
146 sal_uInt32 ch
= rPropVal
.iterateCodePoints(&i
);
149 *mPos
= static_cast<char>(ch
);
151 *mPos
= static_cast<char>(ch
);
158 *mPos
= static_cast<char>(ch
);
191 *mPos
= static_cast<char>(ch
);
194 else if (ch
<= 0x7FF)
196 *mPos
= 0xC0 | (ch
>> 6); /* 110xxxxx */
198 *mPos
= 0x80 | (ch
& 0x3F); /* 10xxxxxx */
201 else if (ch
<= 0xFFFF)
203 *mPos
= 0xE0 | (ch
>> 12); /* 1110xxxx */
205 *mPos
= 0x80 | ((ch
>> 6) & 0x3F); /* 10xxxxxx */
207 *mPos
= 0x80 | (ch
& 0x3F); /* 10xxxxxx */
212 *mPos
= 0xF0 | (ch
>> 18); /* 11110xxx */
214 *mPos
= 0x80 | ((ch
>> 12) & 0x3F); /* 10xxxxxx */
216 *mPos
= 0x80 | ((ch
>> 6) & 0x3F); /* 10xxxxxx */
218 *mPos
= 0x80 | (ch
& 0x3F); /* 10xxxxxx */
226 void JsonWriter::put(const char* pPropName
, const OUString
& rPropVal
)
228 auto nPropNameLength
= strlen(pPropName
);
229 // But values can be any UTF-8,
230 // see rtl_ImplGetFastUTF8ByteLen in sal/rtl/string.cxx for why a factor 3
232 auto nWorstCasePropValLength
= rPropVal
.getLength() * 3;
233 ensureSpace(nPropNameLength
+ nWorstCasePropValLength
+ 8);
235 addCommaBeforeField();
239 memcpy(mPos
, pPropName
, nPropNameLength
);
240 mPos
+= nPropNameLength
;
241 memcpy(mPos
, "\": \"", 4);
244 writeEscapedOUString(rPropVal
);
252 void JsonWriter::put(const char* pPropName
, const OString
& rPropVal
)
254 // we assume property names are ascii
255 auto nPropNameLength
= strlen(pPropName
);
256 // escaping can double the length
257 auto nWorstCasePropValLength
= rPropVal
.getLength() * 2;
258 ensureSpace(nPropNameLength
+ nWorstCasePropValLength
+ 8);
260 addCommaBeforeField();
264 memcpy(mPos
, pPropName
, nPropNameLength
);
265 mPos
+= nPropNameLength
;
266 memcpy(mPos
, "\": \"", 4);
269 // copy and perform escaping
270 for (int i
= 0; i
< rPropVal
.getLength(); ++i
)
272 char ch
= rPropVal
[i
];
300 void JsonWriter::put(const char* pPropName
, const char* pPropVal
)
302 auto nPropNameLength
= strlen(pPropName
);
303 auto nPropValLength
= strlen(pPropVal
);
304 auto nWorstCasePropValLength
= nPropValLength
* 2;
305 ensureSpace(nPropNameLength
+ nWorstCasePropValLength
+ 8);
307 addCommaBeforeField();
311 memcpy(mPos
, pPropName
, nPropNameLength
);
312 mPos
+= nPropNameLength
;
313 memcpy(mPos
, "\": \"", 4);
316 // copy and perform escaping
350 void JsonWriter::put(const char* pPropName
, sal_Int64 nPropVal
)
352 auto nPropNameLength
= strlen(pPropName
);
353 auto nWorstCasePropValLength
= 32;
354 ensureSpace(nPropNameLength
+ nWorstCasePropValLength
+ 8);
356 addCommaBeforeField();
360 memcpy(mPos
, pPropName
, nPropNameLength
);
361 mPos
+= nPropNameLength
;
362 memcpy(mPos
, "\": ", 3);
365 mPos
+= sprintf(mPos
, "%" SAL_PRIdINT64
, nPropVal
);
370 void JsonWriter::put(const char* pPropName
, double fPropVal
)
372 OString sPropVal
= rtl::math::doubleToString(fPropVal
, rtl_math_StringFormat_F
, 12, '.');
373 auto nPropNameLength
= strlen(pPropName
);
374 ensureSpace(nPropNameLength
+ sPropVal
.getLength() + 8);
376 addCommaBeforeField();
380 memcpy(mPos
, pPropName
, nPropNameLength
);
381 mPos
+= nPropNameLength
;
382 memcpy(mPos
, "\": ", 3);
385 memcpy(mPos
, sPropVal
.getStr(), sPropVal
.getLength());
386 mPos
+= sPropVal
.getLength();
391 void JsonWriter::put(const char* pPropName
, bool nPropVal
)
393 auto nPropNameLength
= strlen(pPropName
);
394 ensureSpace(nPropNameLength
+ 5 + 8);
396 addCommaBeforeField();
400 memcpy(mPos
, pPropName
, nPropNameLength
);
401 mPos
+= nPropNameLength
;
402 memcpy(mPos
, "\": ", 3);
410 memcpy(mPos
, pVal
, strlen(pVal
));
411 mPos
+= strlen(pVal
);
416 void JsonWriter::putSimpleValue(const OUString
& rPropVal
)
418 auto nWorstCasePropValLength
= rPropVal
.getLength() * 3;
419 ensureSpace(nWorstCasePropValLength
+ 4);
421 addCommaBeforeField();
426 writeEscapedOUString(rPropVal
);
434 void JsonWriter::putRaw(const rtl::OStringBuffer
& rRawBuf
)
436 ensureSpace(rRawBuf
.getLength() + 2);
438 addCommaBeforeField();
440 memcpy(mPos
, rRawBuf
.getStr(), rRawBuf
.getLength());
441 mPos
+= rRawBuf
.getLength();
446 void JsonWriter::addCommaBeforeField()
448 if (mbFirstFieldInNode
)
449 mbFirstFieldInNode
= false;
459 void JsonWriter::reallocBuffer(int noMoreBytesRequired
)
461 int currentUsed
= mPos
- mpBuffer
;
462 auto newSize
= std::max
<int>(mSpaceAllocated
* 2, (currentUsed
+ noMoreBytesRequired
) * 2);
463 char* pNew
= static_cast<char*>(malloc(newSize
));
464 memcpy(pNew
, mpBuffer
, currentUsed
);
467 mPos
= mpBuffer
+ currentUsed
;
468 mSpaceAllocated
= newSize
;
473 /** Hands ownership of the underlying storage buffer to the caller,
474 * after this no more document modifications may be written. */
475 char* JsonWriter::extractData()
477 assert(mStartNodeCount
== 0 && "did not close all nodes");
478 assert(mpBuffer
&& "data already extracted");
486 char* pRet
= nullptr;
487 std::swap(pRet
, mpBuffer
);
491 OString
JsonWriter::extractAsOString()
493 char* pChar
= extractData();
499 std::string
JsonWriter::extractAsStdString()
501 char* pChar
= extractData();
502 std::string
ret(pChar
);
507 bool JsonWriter::isDataEquals(const std::string
& s
) const
509 return s
.length() == static_cast<size_t>(mPos
- mpBuffer
)
510 && memcmp(s
.data(), mpBuffer
, s
.length()) == 0;
514 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */