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.
4 *
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 */
9
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>
16
17 namespace tools
18 {
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;
22
JsonWriter()23 JsonWriter::JsonWriter()
24 : mSpaceAllocated(DEFAULT_BUFFER_SIZE)
25 , mpBuffer(static_cast<char*>(malloc(mSpaceAllocated)))
26 , mStartNodeCount(0)
27 , mPos(mpBuffer)
28 , mbFirstFieldInNode(true)
29 {
30 *mPos = '{';
31 ++mPos;
32 *mPos = ' ';
33 ++mPos;
34 }
35
~JsonWriter()36 JsonWriter::~JsonWriter()
37 {
38 assert(!mpBuffer && "forgot to extract data?");
39 free(mpBuffer);
40 }
41
startNode(const char * pNodeName)42 ScopedJsonWriterNode JsonWriter::startNode(const char* pNodeName)
43 {
44 auto len = strlen(pNodeName);
45 ensureSpace(len + 6);
46
47 addCommaBeforeField();
48
49 *mPos = '"';
50 ++mPos;
51 memcpy(mPos, pNodeName, len);
52 mPos += len;
53 memcpy(mPos, "\": { ", 5);
54 mPos += 5;
55 mStartNodeCount++;
56 mbFirstFieldInNode = true;
57 return ScopedJsonWriterNode(*this);
58 }
59
endNode()60 void JsonWriter::endNode()
61 {
62 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
63 --mStartNodeCount;
64 ensureSpace(1);
65 *mPos = '}';
66 ++mPos;
67 mbFirstFieldInNode = false;
68 }
69
startArray(const char * pNodeName)70 ScopedJsonWriterArray JsonWriter::startArray(const char* pNodeName)
71 {
72 auto len = strlen(pNodeName);
73 ensureSpace(len + 6);
74
75 addCommaBeforeField();
76
77 *mPos = '"';
78 ++mPos;
79 memcpy(mPos, pNodeName, len);
80 mPos += len;
81 memcpy(mPos, "\": [ ", 5);
82 mPos += 5;
83 mStartNodeCount++;
84 mbFirstFieldInNode = true;
85 return ScopedJsonWriterArray(*this);
86 }
87
endArray()88 void JsonWriter::endArray()
89 {
90 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
91 --mStartNodeCount;
92 ensureSpace(1);
93 *mPos = ']';
94 ++mPos;
95 mbFirstFieldInNode = false;
96 }
97
startStruct()98 ScopedJsonWriterStruct JsonWriter::startStruct()
99 {
100 ensureSpace(6);
101
102 addCommaBeforeField();
103
104 *mPos = '{';
105 ++mPos;
106 *mPos = ' ';
107 ++mPos;
108 mStartNodeCount++;
109 mbFirstFieldInNode = true;
110 return ScopedJsonWriterStruct(*this);
111 }
112
endStruct()113 void JsonWriter::endStruct()
114 {
115 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
116 --mStartNodeCount;
117 ensureSpace(1);
118 *mPos = '}';
119 ++mPos;
120 mbFirstFieldInNode = false;
121 }
122
writeEscapedOUString(const OUString & rPropVal)123 void JsonWriter::writeEscapedOUString(const OUString& rPropVal)
124 {
125 // Convert from UTF-16 to UTF-8 and perform escaping
126 sal_Int32 i = 0;
127 while (i < rPropVal.getLength())
128 {
129 sal_uInt32 ch = rPropVal.iterateCodePoints(&i);
130 if (ch == '\\')
131 {
132 *mPos = static_cast<char>(ch);
133 ++mPos;
134 *mPos = static_cast<char>(ch);
135 ++mPos;
136 }
137 else if (ch == '"')
138 {
139 *mPos = '\\';
140 ++mPos;
141 *mPos = static_cast<char>(ch);
142 ++mPos;
143 }
144 else if (ch == '\n')
145 {
146 *mPos = '\\';
147 ++mPos;
148 *mPos = 'n';
149 ++mPos;
150 }
151 else if (ch == '\r')
152 {
153 *mPos = '\\';
154 ++mPos;
155 *mPos = 'r';
156 ++mPos;
157 }
158 else if (ch == '\f')
159 {
160 *mPos = '\\';
161 ++mPos;
162 *mPos = 'f';
163 ++mPos;
164 }
165 else if (ch <= 0x7F)
166 {
167 *mPos = static_cast<char>(ch);
168 ++mPos;
169 }
170 else if (ch <= 0x7FF)
171 {
172 *mPos = 0xC0 | (ch >> 6); /* 110xxxxx */
173 ++mPos;
174 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
175 ++mPos;
176 }
177 else if (ch <= 0xFFFF)
178 {
179 *mPos = 0xE0 | (ch >> 12); /* 1110xxxx */
180 ++mPos;
181 *mPos = 0x80 | ((ch >> 6) & 0x3F); /* 10xxxxxx */
182 ++mPos;
183 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
184 ++mPos;
185 }
186 else
187 {
188 *mPos = 0xF0 | (ch >> 18); /* 11110xxx */
189 ++mPos;
190 *mPos = 0x80 | ((ch >> 12) & 0x3F); /* 10xxxxxx */
191 ++mPos;
192 *mPos = 0x80 | ((ch >> 6) & 0x3F); /* 10xxxxxx */
193 ++mPos;
194 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
195 ++mPos;
196 }
197 }
198 }
199
put(const char * pPropName,const OUString & rPropVal)200 void JsonWriter::put(const char* pPropName, const OUString& rPropVal)
201 {
202 auto nPropNameLength = strlen(pPropName);
203 auto nWorstCasePropValLength = rPropVal.getLength() * 2;
204 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
205
206 addCommaBeforeField();
207
208 *mPos = '"';
209 ++mPos;
210 memcpy(mPos, pPropName, nPropNameLength);
211 mPos += nPropNameLength;
212 memcpy(mPos, "\": \"", 4);
213 mPos += 4;
214
215 writeEscapedOUString(rPropVal);
216
217 *mPos = '"';
218 ++mPos;
219 }
220
put(const char * pPropName,const OString & rPropVal)221 void JsonWriter::put(const char* pPropName, const OString& rPropVal)
222 {
223 auto nPropNameLength = strlen(pPropName);
224 auto nWorstCasePropValLength = rPropVal.getLength();
225 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
226
227 addCommaBeforeField();
228
229 *mPos = '"';
230 ++mPos;
231 memcpy(mPos, pPropName, nPropNameLength);
232 mPos += nPropNameLength;
233 memcpy(mPos, "\": \"", 4);
234 mPos += 4;
235
236 // copy and perform escaping
237 for (int i = 0; i < rPropVal.getLength(); ++i)
238 {
239 char ch = rPropVal[i];
240 if (ch == '\\')
241 {
242 *mPos = ch;
243 ++mPos;
244 *mPos = ch;
245 ++mPos;
246 }
247 else if (ch == '"')
248 {
249 *mPos = '\\';
250 ++mPos;
251 *mPos = ch;
252 ++mPos;
253 }
254 else
255 {
256 *mPos = ch;
257 ++mPos;
258 }
259 }
260
261 *mPos = '"';
262 ++mPos;
263 }
264
put(const char * pPropName,const char * pPropVal)265 void JsonWriter::put(const char* pPropName, const char* pPropVal)
266 {
267 auto nPropNameLength = strlen(pPropName);
268 auto nPropValLength = strlen(pPropVal);
269 auto nWorstCasePropValLength = nPropValLength * 2;
270 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
271
272 addCommaBeforeField();
273
274 *mPos = '"';
275 ++mPos;
276 memcpy(mPos, pPropName, nPropNameLength);
277 mPos += nPropNameLength;
278 memcpy(mPos, "\": \"", 4);
279 mPos += 4;
280
281 // copy and perform escaping
282 for (;;)
283 {
284 char ch = *pPropVal;
285 if (!ch)
286 break;
287 ++pPropVal;
288 if (ch == '\\')
289 {
290 *mPos = ch;
291 ++mPos;
292 *mPos = ch;
293 ++mPos;
294 }
295 else if (ch == '"')
296 {
297 *mPos = '\\';
298 ++mPos;
299 *mPos = ch;
300 ++mPos;
301 }
302 else
303 {
304 *mPos = ch;
305 ++mPos;
306 }
307 }
308
309 *mPos = '"';
310 ++mPos;
311 }
312
put(const char * pPropName,sal_Int64 nPropVal)313 void JsonWriter::put(const char* pPropName, sal_Int64 nPropVal)
314 {
315 auto nPropNameLength = strlen(pPropName);
316 auto nWorstCasePropValLength = 32;
317 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
318
319 addCommaBeforeField();
320
321 *mPos = '"';
322 ++mPos;
323 memcpy(mPos, pPropName, nPropNameLength);
324 mPos += nPropNameLength;
325 memcpy(mPos, "\": ", 3);
326 mPos += 3;
327
328 mPos += sprintf(mPos, "%" SAL_PRIdINT64, nPropVal);
329 }
330
put(const char * pPropName,double fPropVal)331 void JsonWriter::put(const char* pPropName, double fPropVal)
332 {
333 OString sPropVal = rtl::math::doubleToString(fPropVal, rtl_math_StringFormat_F, 12, '.');
334 auto nPropNameLength = strlen(pPropName);
335 ensureSpace(nPropNameLength + sPropVal.getLength() + 8);
336
337 addCommaBeforeField();
338
339 *mPos = '"';
340 ++mPos;
341 memcpy(mPos, pPropName, nPropNameLength);
342 mPos += nPropNameLength;
343 memcpy(mPos, "\": ", 3);
344 mPos += 3;
345
346 memcpy(mPos, sPropVal.getStr(), sPropVal.getLength());
347 mPos += sPropVal.getLength();
348 }
349
put(const char * pPropName,bool nPropVal)350 void JsonWriter::put(const char* pPropName, bool nPropVal)
351 {
352 auto nPropNameLength = strlen(pPropName);
353 ensureSpace(nPropNameLength + 5 + 8);
354
355 addCommaBeforeField();
356
357 *mPos = '"';
358 ++mPos;
359 memcpy(mPos, pPropName, nPropNameLength);
360 mPos += nPropNameLength;
361 memcpy(mPos, "\": ", 3);
362 mPos += 3;
363
364 const char* pVal;
365 if (nPropVal)
366 pVal = "true";
367 else
368 pVal = "false";
369 memcpy(mPos, pVal, strlen(pVal));
370 mPos += strlen(pVal);
371 }
372
putSimpleValue(const OUString & rPropVal)373 void JsonWriter::putSimpleValue(const OUString& rPropVal)
374 {
375 auto nWorstCasePropValLength = rPropVal.getLength() * 2;
376 ensureSpace(nWorstCasePropValLength + 4);
377
378 addCommaBeforeField();
379
380 *mPos = '"';
381 ++mPos;
382
383 writeEscapedOUString(rPropVal);
384
385 *mPos = '"';
386 ++mPos;
387 }
388
putRaw(const rtl::OStringBuffer & rRawBuf)389 void JsonWriter::putRaw(const rtl::OStringBuffer& rRawBuf)
390 {
391 ensureSpace(rRawBuf.getLength() + 2);
392
393 addCommaBeforeField();
394
395 memcpy(mPos, rRawBuf.getStr(), rRawBuf.getLength());
396 mPos += rRawBuf.getLength();
397 }
398
addCommaBeforeField()399 void JsonWriter::addCommaBeforeField()
400 {
401 if (mbFirstFieldInNode)
402 mbFirstFieldInNode = false;
403 else
404 {
405 *mPos = ',';
406 ++mPos;
407 *mPos = ' ';
408 ++mPos;
409 }
410 }
411
reallocBuffer(int noMoreBytesRequired)412 void JsonWriter::reallocBuffer(int noMoreBytesRequired)
413 {
414 int currentUsed = mPos - mpBuffer;
415 auto newSize = std::max<int>(mSpaceAllocated * 2, (currentUsed + noMoreBytesRequired) * 2);
416 char* pNew = static_cast<char*>(malloc(newSize));
417 memcpy(pNew, mpBuffer, currentUsed);
418 free(mpBuffer);
419 mpBuffer = pNew;
420 mPos = mpBuffer + currentUsed;
421 mSpaceAllocated = newSize;
422 }
423
424 /** Hands ownership of the underlying storage buffer to the caller,
425 * after this no more document modifications may be written. */
extractData()426 char* JsonWriter::extractData()
427 {
428 assert(mStartNodeCount == 0 && "did not close all nodes");
429 assert(mpBuffer && "data already extracted");
430 ensureSpace(2);
431 // add closing brace
432 *mPos = '}';
433 ++mPos;
434 // null-terminate
435 *mPos = 0;
436 mPos = nullptr;
437 char* pRet = nullptr;
438 std::swap(pRet, mpBuffer);
439 return pRet;
440 }
441
extractAsOString()442 OString JsonWriter::extractAsOString()
443 {
444 char* pChar = extractData();
445 OString ret(pChar);
446 free(pChar);
447 return ret;
448 }
449
extractAsStdString()450 std::string JsonWriter::extractAsStdString()
451 {
452 char* pChar = extractData();
453 std::string ret(pChar);
454 free(pChar);
455 return ret;
456 }
457
isDataEquals(const std::string & s) const458 bool JsonWriter::isDataEquals(const std::string& s) const
459 {
460 return s.length() == static_cast<size_t>(mPos - mpBuffer)
461 && memcmp(s.data(), mpBuffer, s.length()) == 0;
462 }
463
464 } // namespace tools
465 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
466