Coverage Report

Created: 2026-02-14 06:52

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