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