calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / tools / source / misc / json_writer.cxx
blob865b09d50e6a912c298e0fdedc737437b738a927
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/math.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 : mpBuffer(static_cast<char*>(malloc(DEFAULT_BUFFER_SIZE)))
23 , mPos(mpBuffer)
24 , mSpaceAllocated(DEFAULT_BUFFER_SIZE)
25 , mStartNodeCount(0)
26 , mbFirstFieldInNode(true)
28 *mPos = '{';
29 ++mPos;
30 *mPos = ' ';
31 ++mPos;
33 addValidationMark();
36 JsonWriter::~JsonWriter()
38 assert(!mpBuffer && "forgot to extract data?");
39 free(mpBuffer);
42 ScopedJsonWriterNode JsonWriter::startNode(const char* pNodeName)
44 auto len = strlen(pNodeName);
45 ensureSpace(len + 8);
47 addCommaBeforeField();
49 *mPos = '"';
50 ++mPos;
51 memcpy(mPos, pNodeName, len);
52 mPos += len;
53 memcpy(mPos, "\": { ", 5);
54 mPos += 5;
55 mStartNodeCount++;
56 mbFirstFieldInNode = true;
58 validate();
60 return ScopedJsonWriterNode(*this);
63 void JsonWriter::endNode()
65 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
66 --mStartNodeCount;
67 ensureSpace(1);
68 *mPos = '}';
69 ++mPos;
70 mbFirstFieldInNode = false;
72 validate();
75 ScopedJsonWriterArray JsonWriter::startArray(const char* pNodeName)
77 auto len = strlen(pNodeName);
78 ensureSpace(len + 8);
80 addCommaBeforeField();
82 *mPos = '"';
83 ++mPos;
84 memcpy(mPos, pNodeName, len);
85 mPos += len;
86 memcpy(mPos, "\": [ ", 5);
87 mPos += 5;
88 mStartNodeCount++;
89 mbFirstFieldInNode = true;
91 validate();
93 return ScopedJsonWriterArray(*this);
96 void JsonWriter::endArray()
98 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
99 --mStartNodeCount;
100 ensureSpace(1);
101 *mPos = ']';
102 ++mPos;
103 mbFirstFieldInNode = false;
105 validate();
108 ScopedJsonWriterStruct JsonWriter::startStruct()
110 ensureSpace(6);
112 addCommaBeforeField();
114 *mPos = '{';
115 ++mPos;
116 *mPos = ' ';
117 ++mPos;
118 mStartNodeCount++;
119 mbFirstFieldInNode = true;
121 validate();
123 return ScopedJsonWriterStruct(*this);
126 void JsonWriter::endStruct()
128 assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
129 --mStartNodeCount;
130 ensureSpace(1);
131 *mPos = '}';
132 ++mPos;
133 mbFirstFieldInNode = false;
135 validate();
138 static char getEscapementChar(char ch)
140 switch (ch)
142 case '\b':
143 return 'b';
144 case '\t':
145 return 't';
146 case '\n':
147 return 'n';
148 case '\f':
149 return 'f';
150 case '\r':
151 return 'r';
152 default:
153 return ch;
157 static bool writeEscapedSequence(sal_uInt32 ch, char*& pos)
159 // control characters
160 if (ch <= 0x1f)
162 // clang-format off
163 SAL_WNODEPRECATED_DECLARATIONS_PUSH // sprintf (macOS 13 SDK)
164 int written = sprintf(pos, "\\u%.4x", ch);
165 SAL_WNODEPRECATED_DECLARATIONS_POP
166 // clang-format on
168 if (written > 0)
169 pos += written;
170 return true;
173 switch (ch)
175 case '\b':
176 case '\t':
177 case '\n':
178 case '\f':
179 case '\r':
180 case '"':
181 case '/':
182 case '\\':
183 *pos++ = '\\';
184 *pos++ = getEscapementChar(ch);
185 return true;
186 // Special processing of U+2028 and U+2029, which are valid JSON, but invalid JavaScript
187 // Write them in escaped '\u2028' or '\u2029' form
188 case 0x2028:
189 case 0x2029:
190 *pos++ = '\\';
191 *pos++ = 'u';
192 *pos++ = '2';
193 *pos++ = '0';
194 *pos++ = '2';
195 *pos++ = ch == 0x2028 ? '8' : '9';
196 return true;
197 default:
198 return false;
202 void JsonWriter::writeEscapedOUString(const OUString& rPropVal)
204 // Convert from UTF-16 to UTF-8 and perform escaping
205 sal_Int32 i = 0;
206 while (i < rPropVal.getLength())
208 sal_uInt32 ch = rPropVal.iterateCodePoints(&i);
209 if (writeEscapedSequence(ch, mPos))
210 continue;
211 if (ch <= 0x7F)
213 *mPos = static_cast<char>(ch);
214 ++mPos;
216 else if (ch <= 0x7FF)
218 *mPos = 0xC0 | (ch >> 6); /* 110xxxxx */
219 ++mPos;
220 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
221 ++mPos;
223 else if (ch <= 0xFFFF)
225 *mPos = 0xE0 | (ch >> 12); /* 1110xxxx */
226 ++mPos;
227 *mPos = 0x80 | ((ch >> 6) & 0x3F); /* 10xxxxxx */
228 ++mPos;
229 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
230 ++mPos;
232 else
234 *mPos = 0xF0 | (ch >> 18); /* 11110xxx */
235 ++mPos;
236 *mPos = 0x80 | ((ch >> 12) & 0x3F); /* 10xxxxxx */
237 ++mPos;
238 *mPos = 0x80 | ((ch >> 6) & 0x3F); /* 10xxxxxx */
239 ++mPos;
240 *mPos = 0x80 | (ch & 0x3F); /* 10xxxxxx */
241 ++mPos;
245 validate();
248 void JsonWriter::put(const OUString& pPropName, const OUString& rPropVal)
250 auto nPropNameLength = pPropName.getLength();
251 // But values can be any UTF-8,
252 // if the string only contains of 0x2028, it will be expanded 6 times (see writeEscapedSequence)
253 auto nWorstCasePropValLength = rPropVal.getLength() * 6;
254 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
256 addCommaBeforeField();
258 *mPos = '"';
259 ++mPos;
261 writeEscapedOUString(pPropName);
263 memcpy(mPos, "\": \"", 4);
264 mPos += 4;
266 writeEscapedOUString(rPropVal);
268 *mPos = '"';
269 ++mPos;
271 validate();
274 void JsonWriter::put(const char* pPropName, const OUString& rPropVal)
276 auto nPropNameLength = strlen(pPropName);
277 // But values can be any UTF-8,
278 // if the string only contains of 0x2028, it will be expanded 6 times (see writeEscapedSequence)
279 auto nWorstCasePropValLength = rPropVal.getLength() * 6;
280 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
282 addCommaBeforeField();
284 *mPos = '"';
285 ++mPos;
286 memcpy(mPos, pPropName, nPropNameLength);
287 mPos += nPropNameLength;
288 memcpy(mPos, "\": \"", 4);
289 mPos += 4;
291 writeEscapedOUString(rPropVal);
293 *mPos = '"';
294 ++mPos;
296 validate();
299 void JsonWriter::put(const char* pPropName, std::string_view rPropVal)
301 // we assume property names are ascii
302 auto nPropNameLength = strlen(pPropName);
303 // escaping can double the length
304 auto nWorstCasePropValLength = rPropVal.size() * 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 bool bReachedEnd = false;
318 for (size_t i = 0; i < rPropVal.size() && !bReachedEnd; ++i)
320 char ch = rPropVal[i];
321 switch (ch)
323 case '\b':
324 case '\t':
325 case '\n':
326 case '\f':
327 case '\r':
328 case '"':
329 case '/':
330 case '\\':
331 writeEscapedSequence(ch, mPos);
332 break;
333 case 0:
334 bReachedEnd = true;
335 break;
336 case '\xE2': // Special processing of U+2028 and U+2029
337 if (i + 2 < rPropVal.size() && rPropVal[i + 1] == '\x80'
338 && (rPropVal[i + 2] == '\xA8' || rPropVal[i + 2] == '\xA9'))
340 writeEscapedSequence(rPropVal[i + 2] == '\xA8' ? 0x2028 : 0x2029, mPos);
341 i += 2;
342 break;
344 [[fallthrough]];
345 default:
346 *mPos = ch;
347 ++mPos;
348 break;
352 *mPos = '"';
353 ++mPos;
355 validate();
358 void JsonWriter::put(const char* pPropName, sal_Int64 nPropVal)
360 auto nPropNameLength = strlen(pPropName);
361 auto nWorstCasePropValLength = 32;
362 ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
364 addCommaBeforeField();
366 *mPos = '"';
367 ++mPos;
368 memcpy(mPos, pPropName, nPropNameLength);
369 mPos += nPropNameLength;
370 memcpy(mPos, "\": ", 3);
371 mPos += 3;
373 // clang-format off
374 SAL_WNODEPRECATED_DECLARATIONS_PUSH // sprintf (macOS 13 SDK)
375 mPos += sprintf(mPos, "%" SAL_PRIdINT64, nPropVal);
376 SAL_WNODEPRECATED_DECLARATIONS_POP
377 // clang-format on
379 validate();
382 void JsonWriter::put(const char* pPropName, double fPropVal)
384 OString sPropVal = rtl::math::doubleToString(fPropVal, rtl_math_StringFormat_F, 12, '.');
385 auto nPropNameLength = strlen(pPropName);
386 ensureSpace(nPropNameLength + sPropVal.getLength() + 8);
388 addCommaBeforeField();
390 *mPos = '"';
391 ++mPos;
392 memcpy(mPos, pPropName, nPropNameLength);
393 mPos += nPropNameLength;
394 memcpy(mPos, "\": ", 3);
395 mPos += 3;
397 memcpy(mPos, sPropVal.getStr(), sPropVal.getLength());
398 mPos += sPropVal.getLength();
400 validate();
403 void JsonWriter::put(const char* pPropName, bool nPropVal)
405 auto nPropNameLength = strlen(pPropName);
406 ensureSpace(nPropNameLength + 5 + 8);
408 addCommaBeforeField();
410 *mPos = '"';
411 ++mPos;
412 memcpy(mPos, pPropName, nPropNameLength);
413 mPos += nPropNameLength;
414 memcpy(mPos, "\": ", 3);
415 mPos += 3;
417 const char* pVal;
418 if (nPropVal)
419 pVal = "true";
420 else
421 pVal = "false";
422 memcpy(mPos, pVal, strlen(pVal));
423 mPos += strlen(pVal);
425 validate();
428 void JsonWriter::putSimpleValue(const OUString& rPropVal)
430 auto nWorstCasePropValLength = rPropVal.getLength() * 3;
431 ensureSpace(nWorstCasePropValLength + 4);
433 addCommaBeforeField();
435 *mPos = '"';
436 ++mPos;
438 writeEscapedOUString(rPropVal);
440 *mPos = '"';
441 ++mPos;
443 validate();
446 void JsonWriter::putRaw(std::string_view rRawBuf)
448 ensureSpace(rRawBuf.size() + 2);
450 addCommaBeforeField();
452 memcpy(mPos, rRawBuf.data(), rRawBuf.size());
453 mPos += rRawBuf.size();
455 validate();
458 void JsonWriter::addCommaBeforeField()
460 if (mbFirstFieldInNode)
461 mbFirstFieldInNode = false;
462 else
464 *mPos = ',';
465 ++mPos;
466 *mPos = ' ';
467 ++mPos;
471 void JsonWriter::ensureSpace(int noMoreBytesRequired)
473 assert(mpBuffer && "already extracted data");
474 int currentUsed = mPos - mpBuffer;
475 if (currentUsed + noMoreBytesRequired >= mSpaceAllocated)
477 auto newSize = (currentUsed + noMoreBytesRequired) * 2;
478 mpBuffer = static_cast<char*>(realloc(mpBuffer, newSize));
479 mPos = mpBuffer + currentUsed;
480 mSpaceAllocated = newSize;
482 addValidationMark();
486 /** Hands ownership of the underlying storage buffer to the caller,
487 * after this no more document modifications may be written. */
488 std::pair<char*, int> JsonWriter::extractDataImpl()
490 assert(mStartNodeCount == 0 && "did not close all nodes");
491 assert(mpBuffer && "data already extracted");
492 ensureSpace(2);
493 // add closing brace
494 *mPos = '}';
495 ++mPos;
496 // null-terminate
497 *mPos = 0;
498 const int sz = mPos - mpBuffer;
499 mPos = nullptr;
500 return { std::exchange(mpBuffer, nullptr), sz };
503 OString JsonWriter::extractAsOString()
505 auto[pChar, sz] = extractDataImpl();
506 OString ret(pChar, sz);
507 free(pChar);
508 return ret;
511 std::string JsonWriter::extractAsStdString()
513 auto[pChar, sz] = extractDataImpl();
514 std::string ret(pChar, sz);
515 free(pChar);
516 return ret;
519 bool JsonWriter::isDataEquals(const std::string& s) const
521 return s.length() == static_cast<size_t>(mPos - mpBuffer)
522 && memcmp(s.data(), mpBuffer, s.length()) == 0;
525 } // namespace tools
526 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */