Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: JSon streaming writer
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : /*! @cond Doxygen_Suppress */
14 :
15 : #include <cmath>
16 : #include <vector>
17 : #include <string>
18 :
19 : #include "cpl_conv.h"
20 : #include "cpl_float.h"
21 : #include "cpl_string.h"
22 : #include "cpl_json_streaming_writer.h"
23 :
24 331 : CPLJSonStreamingWriter::CPLJSonStreamingWriter(
25 331 : SerializationFuncType pfnSerializationFunc, void *pUserData)
26 331 : : m_pfnSerializationFunc(pfnSerializationFunc), m_pUserData(pUserData)
27 : {
28 331 : }
29 :
30 331 : CPLJSonStreamingWriter::~CPLJSonStreamingWriter()
31 : {
32 331 : CPLAssert(m_nLevel == 0);
33 331 : CPLAssert(m_states.empty());
34 331 : }
35 :
36 200 : void CPLJSonStreamingWriter::clear()
37 : {
38 200 : m_nLevel = 0;
39 200 : m_osStr.clear();
40 200 : m_osIndentAcc.clear();
41 200 : m_states.clear();
42 200 : m_bWaitForValue = false;
43 200 : }
44 :
45 23863 : void CPLJSonStreamingWriter::Serialize(const std::string_view &str)
46 : {
47 23863 : if (m_pfnSerializationFunc)
48 : {
49 6083 : m_osTmpForSerialize = str;
50 6083 : m_pfnSerializationFunc(m_osTmpForSerialize.c_str(), m_pUserData);
51 : }
52 : else
53 : {
54 17780 : m_osStr.append(str);
55 : }
56 23863 : }
57 :
58 17996 : void CPLJSonStreamingWriter::Serialize(const char *pszStr, size_t nLength)
59 : {
60 17996 : Serialize(std::string_view(pszStr, nLength));
61 17996 : }
62 :
63 0 : void CPLJSonStreamingWriter::SetIndentationSize(int nSpaces)
64 : {
65 0 : CPLAssert(m_nLevel == 0);
66 0 : m_osIndent.clear();
67 0 : m_osIndent.resize(nSpaces, ' ');
68 0 : }
69 :
70 1304 : void CPLJSonStreamingWriter::IncIndent()
71 : {
72 1304 : m_nLevel++;
73 1304 : if (m_bPretty)
74 1165 : m_osIndentAcc += m_osIndent;
75 1304 : }
76 :
77 1294 : void CPLJSonStreamingWriter::DecIndent()
78 : {
79 1294 : CPLAssert(m_nLevel > 0);
80 1294 : m_nLevel--;
81 1294 : if (m_bPretty)
82 1155 : m_osIndentAcc.resize(m_osIndentAcc.size() - m_osIndent.size());
83 1294 : }
84 :
85 : const std::string &
86 4274 : CPLJSonStreamingWriter::FormatString(const std::string_view &str)
87 : {
88 4274 : m_osTmpForFormatString.clear();
89 4274 : m_osTmpForFormatString += '"';
90 64185 : for (char ch : str)
91 : {
92 59911 : switch (ch)
93 : {
94 1070 : case '"':
95 1070 : m_osTmpForFormatString += "\\\"";
96 1070 : break;
97 2 : case '\\':
98 2 : m_osTmpForFormatString += "\\\\";
99 2 : break;
100 2 : case '\b':
101 2 : m_osTmpForFormatString += "\\b";
102 2 : break;
103 2 : case '\f':
104 2 : m_osTmpForFormatString += "\\f";
105 2 : break;
106 166 : case '\n':
107 166 : m_osTmpForFormatString += "\\n";
108 166 : break;
109 2 : case '\r':
110 2 : m_osTmpForFormatString += "\\r";
111 2 : break;
112 2 : case '\t':
113 2 : m_osTmpForFormatString += "\\t";
114 2 : break;
115 58665 : default:
116 58665 : if (static_cast<unsigned char>(ch) < ' ')
117 2 : m_osTmpForFormatString += CPLSPrintf("\\u%04X", ch);
118 : else
119 58663 : m_osTmpForFormatString += ch;
120 58665 : break;
121 : }
122 : }
123 4274 : m_osTmpForFormatString += '"';
124 4274 : return m_osTmpForFormatString;
125 : }
126 :
127 7252 : void CPLJSonStreamingWriter::EmitCommaIfNeeded()
128 : {
129 7252 : if (m_bWaitForValue)
130 : {
131 2499 : m_bWaitForValue = false;
132 : }
133 4753 : else if (!m_states.empty())
134 : {
135 4512 : if (!m_states.back().bFirstChild)
136 : {
137 3253 : Serialize(",", 1);
138 3253 : if (m_bPretty && !m_bNewLineEnabled)
139 893 : Serialize(" ", 1);
140 : }
141 4512 : if (m_bPretty && m_bNewLineEnabled)
142 : {
143 3306 : Serialize("\n", 1);
144 3306 : Serialize(m_osIndentAcc.c_str(), m_osIndentAcc.size());
145 : }
146 4512 : m_states.back().bFirstChild = false;
147 : }
148 7252 : }
149 :
150 813 : void CPLJSonStreamingWriter::StartObj()
151 : {
152 813 : EmitCommaIfNeeded();
153 813 : Serialize("{", 1);
154 813 : IncIndent();
155 813 : m_states.emplace_back(State(true));
156 813 : }
157 :
158 803 : void CPLJSonStreamingWriter::EndObj()
159 : {
160 803 : CPLAssert(!m_bWaitForValue);
161 803 : CPLAssert(!m_states.empty());
162 803 : CPLAssert(m_states.back().bIsObj);
163 803 : DecIndent();
164 803 : if (!m_states.back().bFirstChild)
165 : {
166 775 : if (m_bPretty && m_bNewLineEnabled)
167 : {
168 678 : Serialize("\n", 1);
169 678 : Serialize(m_osIndentAcc.c_str(), m_osIndentAcc.size());
170 : }
171 : }
172 803 : m_states.pop_back();
173 803 : Serialize("}", 1);
174 803 : }
175 :
176 491 : void CPLJSonStreamingWriter::StartArray()
177 : {
178 491 : EmitCommaIfNeeded();
179 491 : Serialize("[", 1);
180 491 : IncIndent();
181 491 : m_states.emplace_back(State(false));
182 491 : }
183 :
184 491 : void CPLJSonStreamingWriter::EndArray()
185 : {
186 491 : CPLAssert(!m_states.empty());
187 491 : CPLAssert(!m_states.back().bIsObj);
188 491 : DecIndent();
189 491 : if (!m_states.back().bFirstChild)
190 : {
191 474 : if (m_bPretty && m_bNewLineEnabled)
192 : {
193 352 : Serialize("\n", 1);
194 352 : Serialize(m_osIndentAcc.c_str(), m_osIndentAcc.size());
195 : }
196 : }
197 491 : m_states.pop_back();
198 491 : Serialize("]", 1);
199 491 : }
200 :
201 2499 : void CPLJSonStreamingWriter::AddObjKey(const std::string_view &key)
202 : {
203 2499 : CPLAssert(!m_states.empty());
204 2499 : CPLAssert(m_states.back().bIsObj);
205 2499 : CPLAssert(!m_bWaitForValue);
206 2499 : EmitCommaIfNeeded();
207 2499 : Serialize(FormatString(key));
208 2499 : if (m_bPretty)
209 2334 : Serialize(": ", 2);
210 : else
211 165 : Serialize(":", 1);
212 2499 : m_bWaitForValue = true;
213 2499 : }
214 :
215 7 : void CPLJSonStreamingWriter::Add(bool bVal)
216 : {
217 7 : EmitCommaIfNeeded();
218 7 : Serialize(bVal ? "true" : "false", bVal ? 4 : 5);
219 7 : }
220 :
221 569 : void CPLJSonStreamingWriter::Add(const char *pszStr)
222 : {
223 569 : EmitCommaIfNeeded();
224 569 : Serialize(FormatString(std::string_view(pszStr)));
225 569 : }
226 :
227 25 : void CPLJSonStreamingWriter::Add(const std::string_view &str)
228 : {
229 25 : EmitCommaIfNeeded();
230 25 : Serialize(FormatString(str));
231 25 : }
232 :
233 1181 : void CPLJSonStreamingWriter::Add(const std::string &str)
234 : {
235 1181 : EmitCommaIfNeeded();
236 1181 : Serialize(FormatString(str));
237 1181 : }
238 :
239 135 : void CPLJSonStreamingWriter::AddSerializedValue(const std::string_view &str)
240 : {
241 135 : EmitCommaIfNeeded();
242 135 : Serialize(str);
243 135 : }
244 :
245 1017 : void CPLJSonStreamingWriter::Add(std::int64_t nVal)
246 : {
247 1017 : EmitCommaIfNeeded();
248 1017 : Serialize(CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nVal)));
249 1017 : }
250 :
251 378 : void CPLJSonStreamingWriter::Add(std::uint64_t nVal)
252 : {
253 378 : EmitCommaIfNeeded();
254 378 : Serialize(CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nVal)));
255 378 : }
256 :
257 0 : void CPLJSonStreamingWriter::Add(GFloat16 hfVal, int nPrecision)
258 : {
259 0 : EmitCommaIfNeeded();
260 0 : if (CPLIsNan(hfVal))
261 : {
262 0 : Serialize("\"NaN\"", 5);
263 : }
264 0 : else if (CPLIsInf(hfVal))
265 : {
266 0 : Serialize(hfVal > 0 ? "\"Infinity\"" : "\"-Infinity\"",
267 0 : hfVal > 0 ? 10 : 11);
268 : }
269 : else
270 : {
271 : char szFormatting[10];
272 0 : snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
273 0 : Serialize(CPLSPrintf(szFormatting, double(hfVal)));
274 : }
275 0 : }
276 :
277 28 : void CPLJSonStreamingWriter::Add(float fVal, int nPrecision)
278 : {
279 28 : EmitCommaIfNeeded();
280 28 : if (std::isnan(fVal))
281 : {
282 10 : Serialize("\"NaN\"", 5);
283 : }
284 18 : else if (std::isinf(fVal))
285 : {
286 2 : Serialize(fVal > 0 ? "\"Infinity\"" : "\"-Infinity\"",
287 : fVal > 0 ? 10 : 11);
288 : }
289 : else
290 : {
291 : char szFormatting[10];
292 16 : snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
293 16 : Serialize(CPLSPrintf(szFormatting, static_cast<double>(fVal)));
294 : }
295 28 : }
296 :
297 82 : void CPLJSonStreamingWriter::Add(double dfVal, int nPrecision)
298 : {
299 82 : EmitCommaIfNeeded();
300 82 : if (std::isnan(dfVal))
301 : {
302 35 : Serialize("\"NaN\"", 5);
303 : }
304 47 : else if (std::isinf(dfVal))
305 : {
306 2 : Serialize(dfVal > 0 ? "\"Infinity\"" : "\"-Infinity\"");
307 : }
308 : else
309 : {
310 : char szFormatting[10];
311 45 : snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
312 45 : Serialize(CPLSPrintf(szFormatting, dfVal));
313 : }
314 82 : }
315 :
316 27 : void CPLJSonStreamingWriter::AddNull()
317 : {
318 27 : EmitCommaIfNeeded();
319 27 : Serialize("null", 4);
320 27 : }
321 :
322 : /*! @endcond */
|