Bump version to 6.4-15
[LibreOffice.git] / tools / source / misc / json_writer.cxx
blobbe891ef18423c0f36aaee2e1c4166de2ce8aad5b
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 <cstring>
13 #include <rtl/strbuf.hxx>
15 namespace tools
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 : mSpaceAllocated(DEFAULT_BUFFER_SIZE)
23 , mpBuffer(static_cast<char*>(malloc(mSpaceAllocated)))
24 , mStartNodeCount(0)
25 , mPos(mpBuffer)
26 , mbFirstFieldInNode(true)
28 *mPos = '{';
29 ++mPos;
30 *mPos = ' ';
31 ++mPos;
34 JsonWriter::~JsonWriter()
36 assert(!mpBuffer && "forgot to extract data?");
37 free(mpBuffer);
40 ScopedJsonWriterNode JsonWriter::startNode(const char* pNodeName)
42 auto len = strlen(pNodeName);
43 ensureSpace(len + 6);
45 addCommaBeforeField();
47 *mPos = '"';
48 ++mPos;
49 memcpy(mPos, pNodeName, len);
50 mPos += len;
51 memcpy(mPos, "\": { ", 5);
52 mPos += 5;
53 mStartNodeCount++;
54 mbFirstFieldInNode = true;
55 return ScopedJsonWriterNode(*this);
58 void JsonWriter::endNode()
60 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
61 --mStartNodeCount;
62 ensureSpace(1);
63 *mPos = '}';
64 ++mPos;
65 mbFirstFieldInNode = false;
68 ScopedJsonWriterArray JsonWriter::startArray(const char* pNodeName)
70 auto len = strlen(pNodeName);
71 ensureSpace(len + 6);
73 addCommaBeforeField();
75 *mPos = '"';
76 ++mPos;
77 memcpy(mPos, pNodeName, len);
78 mPos += len;
79 memcpy(mPos, "\": [ ", 5);
80 mPos += 5;
81 mStartNodeCount++;
82 mbFirstFieldInNode = true;
83 return ScopedJsonWriterArray(*this);
86 void JsonWriter::endArray()
88 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
89 --mStartNodeCount;
90 ensureSpace(1);
91 *mPos = ']';
92 ++mPos;
93 mbFirstFieldInNode = false;
96 ScopedJsonWriterStruct JsonWriter::startStruct()
98 ensureSpace(6);
100 addCommaBeforeField();
102 *mPos = '{';
103 ++mPos;
104 *mPos = ' ';
105 ++mPos;
106 mStartNodeCount++;
107 mbFirstFieldInNode = true;
108 return ScopedJsonWriterStruct(*this);
111 void JsonWriter::endStruct()
113 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
114 --mStartNodeCount;
115 ensureSpace(1);
116 *mPos = '}';
117 ++mPos;
118 mbFirstFieldInNode = false;
121 void JsonWriter::put(const char* pPropName, const OUString& rPropVal)
123 auto nPropNameLength = strlen(pPropName);
124 auto nWorstCasePropValLength = rPropVal.getLength() * 2;
125 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
127 addCommaBeforeField();
129 *mPos = '"';
130 ++mPos;
131 memcpy(mPos, pPropName, nPropNameLength);
132 mPos += nPropNameLength;
133 memcpy(mPos, "\": \"", 4);
134 mPos += 4;
136 // Convert from UTF-16 to UTF-8 and perform escaping
137 for (int i = 0; i < rPropVal.getLength(); ++i)
139 sal_Unicode ch = rPropVal[i];
140 if (ch == '\\')
142 *mPos = static_cast<char>(ch);
143 ++mPos;
144 *mPos = static_cast<char>(ch);
145 ++mPos;
147 else if (ch == '"')
149 *mPos = '\\';
150 ++mPos;
151 *mPos = static_cast<char>(ch);
152 ++mPos;
154 else if (ch <= 0x7F)
156 *mPos = static_cast<char>(ch);
157 ++mPos;
159 else if (ch <= 0x7FF)
161 *mPos = 0xC0 | (ch >> 6); /* 110xxxxx */
162 ++mPos;
163 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
164 ++mPos;
166 else
168 *mPos = 0xE0 | (ch >> 12); /* 1110xxxx */
169 ++mPos;
170 *mPos = 0x80 | ((ch >> 6) & 0x3F); /* 10xxxxxx */
171 ++mPos;
172 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
173 ++mPos;
177 *mPos = '"';
178 ++mPos;
181 void JsonWriter::put(const char* pPropName, const OString& rPropVal)
183 auto nPropNameLength = strlen(pPropName);
184 auto nWorstCasePropValLength = rPropVal.getLength();
185 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
187 addCommaBeforeField();
189 *mPos = '"';
190 ++mPos;
191 memcpy(mPos, pPropName, nPropNameLength);
192 mPos += nPropNameLength;
193 memcpy(mPos, "\": \"", 4);
194 mPos += 4;
196 // copy and perform escaping
197 for (int i = 0; i < rPropVal.getLength(); ++i)
199 char ch = rPropVal[i];
200 if (ch == '\\')
202 *mPos = ch;
203 ++mPos;
204 *mPos = ch;
205 ++mPos;
207 else if (ch == '"')
209 *mPos = '\\';
210 ++mPos;
211 *mPos = ch;
212 ++mPos;
214 else
216 *mPos = ch;
217 ++mPos;
221 *mPos = '"';
222 ++mPos;
225 void JsonWriter::put(const char* pPropName, const char* pPropVal)
227 auto nPropNameLength = strlen(pPropName);
228 auto nPropValLength = strlen(pPropVal);
229 auto nWorstCasePropValLength = nPropValLength * 2;
230 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
232 addCommaBeforeField();
234 *mPos = '"';
235 ++mPos;
236 memcpy(mPos, pPropName, nPropNameLength);
237 mPos += nPropNameLength;
238 memcpy(mPos, "\": \"", 4);
239 mPos += 4;
241 // copy and perform escaping
242 for (;;)
244 char ch = *pPropVal;
245 if (!ch)
246 break;
247 ++pPropVal;
248 if (ch == '\\')
250 *mPos = ch;
251 ++mPos;
252 *mPos = ch;
253 ++mPos;
255 else if (ch == '"')
257 *mPos = '\\';
258 ++mPos;
259 *mPos = ch;
260 ++mPos;
262 else
264 *mPos = ch;
265 ++mPos;
269 *mPos = '"';
270 ++mPos;
273 void JsonWriter::put(const char* pPropName, int nPropVal)
275 auto nPropNameLength = strlen(pPropName);
276 auto nWorstCasePropValLength = 32;
277 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
279 addCommaBeforeField();
281 *mPos = '"';
282 ++mPos;
283 memcpy(mPos, pPropName, nPropNameLength);
284 mPos += nPropNameLength;
285 memcpy(mPos, "\": ", 3);
286 mPos += 3;
288 mPos += sprintf(mPos, "%d", nPropVal);
291 void JsonWriter::putRaw(const rtl::OStringBuffer& rRawBuf)
293 ensureSpace(rRawBuf.getLength() + 2);
295 addCommaBeforeField();
297 memcpy(mPos, rRawBuf.getStr(), rRawBuf.getLength());
298 mPos += rRawBuf.getLength();
301 void JsonWriter::addCommaBeforeField()
303 if (mbFirstFieldInNode)
304 mbFirstFieldInNode = false;
305 else
307 *mPos = ',';
308 ++mPos;
309 *mPos = ' ';
310 ++mPos;
314 void JsonWriter::reallocBuffer(int noMoreBytesRequired)
316 int currentUsed = mPos - mpBuffer;
317 auto newSize = std::max<int>(mSpaceAllocated * 2, (currentUsed + noMoreBytesRequired) * 2);
318 char* pNew = static_cast<char*>(malloc(newSize));
319 memcpy(pNew, mpBuffer, currentUsed);
320 free(mpBuffer);
321 mpBuffer = pNew;
322 mPos = mpBuffer + currentUsed;
323 mSpaceAllocated = newSize;
326 /** Hands ownership of the underlying storage buffer to the caller,
327 * after this no more document modifications may be written. */
328 char* JsonWriter::extractData()
330 assert(mStartNodeCount == 0 && "did not close all nodes");
331 assert(mpBuffer && "data already extracted");
332 ensureSpace(2);
333 // add closing brace
334 *mPos = '}';
335 ++mPos;
336 // null-terminate
337 *mPos = 0;
338 mPos = nullptr;
339 char* pRet = nullptr;
340 std::swap(pRet, mpBuffer);
341 return pRet;
344 OString JsonWriter::extractAsOString()
346 char* pChar = extractData();
347 OString ret(pChar);
348 free(pChar);
349 return ret;
352 } // namespace tools
353 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */