Bump version to 21.06.18.1
[LibreOffice.git] / tools / source / misc / json_writer.cxx
bloba262c22dd4ef9afb15d23b1846ce1dc3713815e1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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/.
8 */
10 #include <tools/json_writer.hxx>
11 #include <stdio.h>
12 #include <algorithm>
13 #include <cstring>
14 #include <rtl/strbuf.hxx>
15 #include <rtl/math.hxx>
17 namespace tools
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)))
26 , mStartNodeCount(0)
27 , mPos(mpBuffer)
28 , mbFirstFieldInNode(true)
30 *mPos = '{';
31 ++mPos;
32 *mPos = ' ';
33 ++mPos;
35 addValidationMark();
38 JsonWriter::~JsonWriter()
40 assert(!mpBuffer && "forgot to extract data?");
41 free(mpBuffer);
44 ScopedJsonWriterNode JsonWriter::startNode(const char* pNodeName)
46 auto len = strlen(pNodeName);
47 ensureSpace(len + 8);
49 addCommaBeforeField();
51 *mPos = '"';
52 ++mPos;
53 memcpy(mPos, pNodeName, len);
54 mPos += len;
55 memcpy(mPos, "\": { ", 5);
56 mPos += 5;
57 mStartNodeCount++;
58 mbFirstFieldInNode = true;
60 validate();
62 return ScopedJsonWriterNode(*this);
65 void JsonWriter::endNode()
67 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
68 --mStartNodeCount;
69 ensureSpace(1);
70 *mPos = '}';
71 ++mPos;
72 mbFirstFieldInNode = false;
74 validate();
77 ScopedJsonWriterArray JsonWriter::startArray(const char* pNodeName)
79 auto len = strlen(pNodeName);
80 ensureSpace(len + 8);
82 addCommaBeforeField();
84 *mPos = '"';
85 ++mPos;
86 memcpy(mPos, pNodeName, len);
87 mPos += len;
88 memcpy(mPos, "\": [ ", 5);
89 mPos += 5;
90 mStartNodeCount++;
91 mbFirstFieldInNode = true;
93 validate();
95 return ScopedJsonWriterArray(*this);
98 void JsonWriter::endArray()
100 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
101 --mStartNodeCount;
102 ensureSpace(1);
103 *mPos = ']';
104 ++mPos;
105 mbFirstFieldInNode = false;
107 validate();
110 ScopedJsonWriterStruct JsonWriter::startStruct()
112 ensureSpace(6);
114 addCommaBeforeField();
116 *mPos = '{';
117 ++mPos;
118 *mPos = ' ';
119 ++mPos;
120 mStartNodeCount++;
121 mbFirstFieldInNode = true;
123 validate();
125 return ScopedJsonWriterStruct(*this);
128 void JsonWriter::endStruct()
130 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
131 --mStartNodeCount;
132 ensureSpace(1);
133 *mPos = '}';
134 ++mPos;
135 mbFirstFieldInNode = false;
137 validate();
140 void JsonWriter::writeEscapedOUString(const OUString& rPropVal)
142 // Convert from UTF-16 to UTF-8 and perform escaping
143 sal_Int32 i = 0;
144 while (i < rPropVal.getLength())
146 sal_uInt32 ch = rPropVal.iterateCodePoints(&i);
147 if (ch == '\\')
149 *mPos = static_cast<char>(ch);
150 ++mPos;
151 *mPos = static_cast<char>(ch);
152 ++mPos;
154 else if (ch == '"')
156 *mPos = '\\';
157 ++mPos;
158 *mPos = static_cast<char>(ch);
159 ++mPos;
161 else if (ch == '\n')
163 *mPos = '\\';
164 ++mPos;
165 *mPos = 'n';
166 ++mPos;
168 else if (ch == '\t')
170 *mPos = '\\';
171 ++mPos;
172 *mPos = 't';
173 ++mPos;
175 else if (ch == '\r')
177 *mPos = '\\';
178 ++mPos;
179 *mPos = 'r';
180 ++mPos;
182 else if (ch == '\f')
184 *mPos = '\\';
185 ++mPos;
186 *mPos = 'f';
187 ++mPos;
189 else if (ch <= 0x7F)
191 *mPos = static_cast<char>(ch);
192 ++mPos;
194 else if (ch <= 0x7FF)
196 *mPos = 0xC0 | (ch >> 6); /* 110xxxxx */
197 ++mPos;
198 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
199 ++mPos;
201 else if (ch <= 0xFFFF)
203 *mPos = 0xE0 | (ch >> 12); /* 1110xxxx */
204 ++mPos;
205 *mPos = 0x80 | ((ch >> 6) & 0x3F); /* 10xxxxxx */
206 ++mPos;
207 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
208 ++mPos;
210 else
212 *mPos = 0xF0 | (ch >> 18); /* 11110xxx */
213 ++mPos;
214 *mPos = 0x80 | ((ch >> 12) & 0x3F); /* 10xxxxxx */
215 ++mPos;
216 *mPos = 0x80 | ((ch >> 6) & 0x3F); /* 10xxxxxx */
217 ++mPos;
218 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
219 ++mPos;
223 validate();
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
231 // is the worst case
232 auto nWorstCasePropValLength = rPropVal.getLength() * 3;
233 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
235 addCommaBeforeField();
237 *mPos = '"';
238 ++mPos;
239 memcpy(mPos, pPropName, nPropNameLength);
240 mPos += nPropNameLength;
241 memcpy(mPos, "\": \"", 4);
242 mPos += 4;
244 writeEscapedOUString(rPropVal);
246 *mPos = '"';
247 ++mPos;
249 validate();
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();
262 *mPos = '"';
263 ++mPos;
264 memcpy(mPos, pPropName, nPropNameLength);
265 mPos += nPropNameLength;
266 memcpy(mPos, "\": \"", 4);
267 mPos += 4;
269 // copy and perform escaping
270 for (int i = 0; i < rPropVal.getLength(); ++i)
272 char ch = rPropVal[i];
273 if (ch == '\\')
275 *mPos = ch;
276 ++mPos;
277 *mPos = ch;
278 ++mPos;
280 else if (ch == '"')
282 *mPos = '\\';
283 ++mPos;
284 *mPos = ch;
285 ++mPos;
287 else
289 *mPos = ch;
290 ++mPos;
294 *mPos = '"';
295 ++mPos;
297 validate();
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();
309 *mPos = '"';
310 ++mPos;
311 memcpy(mPos, pPropName, nPropNameLength);
312 mPos += nPropNameLength;
313 memcpy(mPos, "\": \"", 4);
314 mPos += 4;
316 // copy and perform escaping
317 for (;;)
319 char ch = *pPropVal;
320 if (!ch)
321 break;
322 ++pPropVal;
323 if (ch == '\\')
325 *mPos = ch;
326 ++mPos;
327 *mPos = ch;
328 ++mPos;
330 else if (ch == '"')
332 *mPos = '\\';
333 ++mPos;
334 *mPos = ch;
335 ++mPos;
337 else
339 *mPos = ch;
340 ++mPos;
344 *mPos = '"';
345 ++mPos;
347 validate();
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();
358 *mPos = '"';
359 ++mPos;
360 memcpy(mPos, pPropName, nPropNameLength);
361 mPos += nPropNameLength;
362 memcpy(mPos, "\": ", 3);
363 mPos += 3;
365 mPos += sprintf(mPos, "%" SAL_PRIdINT64, nPropVal);
367 validate();
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();
378 *mPos = '"';
379 ++mPos;
380 memcpy(mPos, pPropName, nPropNameLength);
381 mPos += nPropNameLength;
382 memcpy(mPos, "\": ", 3);
383 mPos += 3;
385 memcpy(mPos, sPropVal.getStr(), sPropVal.getLength());
386 mPos += sPropVal.getLength();
388 validate();
391 void JsonWriter::put(const char* pPropName, bool nPropVal)
393 auto nPropNameLength = strlen(pPropName);
394 ensureSpace(nPropNameLength + 5 + 8);
396 addCommaBeforeField();
398 *mPos = '"';
399 ++mPos;
400 memcpy(mPos, pPropName, nPropNameLength);
401 mPos += nPropNameLength;
402 memcpy(mPos, "\": ", 3);
403 mPos += 3;
405 const char* pVal;
406 if (nPropVal)
407 pVal = "true";
408 else
409 pVal = "false";
410 memcpy(mPos, pVal, strlen(pVal));
411 mPos += strlen(pVal);
413 validate();
416 void JsonWriter::putSimpleValue(const OUString& rPropVal)
418 auto nWorstCasePropValLength = rPropVal.getLength() * 3;
419 ensureSpace(nWorstCasePropValLength + 4);
421 addCommaBeforeField();
423 *mPos = '"';
424 ++mPos;
426 writeEscapedOUString(rPropVal);
428 *mPos = '"';
429 ++mPos;
431 validate();
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();
443 validate();
446 void JsonWriter::addCommaBeforeField()
448 if (mbFirstFieldInNode)
449 mbFirstFieldInNode = false;
450 else
452 *mPos = ',';
453 ++mPos;
454 *mPos = ' ';
455 ++mPos;
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);
465 free(mpBuffer);
466 mpBuffer = pNew;
467 mPos = mpBuffer + currentUsed;
468 mSpaceAllocated = newSize;
470 addValidationMark();
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");
479 ensureSpace(2);
480 // add closing brace
481 *mPos = '}';
482 ++mPos;
483 // null-terminate
484 *mPos = 0;
485 mPos = nullptr;
486 char* pRet = nullptr;
487 std::swap(pRet, mpBuffer);
488 return pRet;
491 OString JsonWriter::extractAsOString()
493 char* pChar = extractData();
494 OString ret(pChar);
495 free(pChar);
496 return ret;
499 std::string JsonWriter::extractAsStdString()
501 char* pChar = extractData();
502 std::string ret(pChar);
503 free(pChar);
504 return ret;
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;
513 } // namespace tools
514 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */