Coverage Report

Created: 2025-06-13 06:18

/src/proj/src/proj_json_streaming_writer.cpp
Line
Count
Source (jump to first uncovered line)
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
 * Permission is hereby granted, free of charge, to any person obtaining a
11
 * copy of this software and associated documentation files (the "Software"),
12
 * to deal in the Software without restriction, including without limitation
13
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
 * and/or sell copies of the Software, and to permit persons to whom the
15
 * Software is furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included
18
 * in all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
 * DEALINGS IN THE SOFTWARE.
27
 ****************************************************************************/
28
29
/*! @cond Doxygen_Suppress */
30
31
#include <limits>
32
#include <string>
33
#include <vector>
34
35
#include "proj_json_streaming_writer.hpp"
36
37
#include <cmath>
38
#include <sqlite3.h>
39
#include <stdarg.h>
40
#include <string.h>
41
#define CPLAssert(x)                                                           \
42
0
    do {                                                                       \
43
0
    } while (0)
44
0
#define CPLIsNan std::isnan
45
0
#define CPLIsInf std::isinf
46
0
#define CPL_FRMT_GIB "%lld"
47
0
#define CPL_FRMT_GUIB "%llu"
48
typedef std::uint64_t GUIntBig;
49
50
0
static std::string CPLSPrintf(const char *fmt, ...) {
51
0
    std::string res;
52
0
    res.resize(256);
53
0
    va_list list;
54
0
    va_start(list, fmt);
55
0
    sqlite3_vsnprintf(256, &res[0], fmt, list);
56
0
    va_end(list);
57
0
    res.resize(strlen(&res[0]));
58
0
    return res;
59
0
}
60
61
NS_PROJ_START
62
63
CPLJSonStreamingWriter::CPLJSonStreamingWriter(
64
    SerializationFuncType pfnSerializationFunc, void *pUserData)
65
0
    : m_pfnSerializationFunc(pfnSerializationFunc), m_pUserData(pUserData) {}
66
67
0
CPLJSonStreamingWriter::~CPLJSonStreamingWriter() {
68
0
    CPLAssert(m_nLevel == 0);
69
0
    CPLAssert(m_states.empty());
70
0
}
71
72
0
void CPLJSonStreamingWriter::Print(const std::string &text) {
73
0
    if (m_pfnSerializationFunc) {
74
0
        m_pfnSerializationFunc(text.c_str(), m_pUserData);
75
0
    } else {
76
0
        m_osStr += text;
77
0
    }
78
0
}
79
80
0
void CPLJSonStreamingWriter::SetIndentationSize(int nSpaces) {
81
0
    CPLAssert(m_nLevel == 0);
82
0
    m_osIndent.clear();
83
0
    m_osIndent.resize(nSpaces, ' ');
84
0
}
85
86
0
void CPLJSonStreamingWriter::IncIndent() {
87
0
    m_nLevel++;
88
0
    if (m_bPretty)
89
0
        m_osIndentAcc += m_osIndent;
90
0
}
91
92
0
void CPLJSonStreamingWriter::DecIndent() {
93
0
    CPLAssert(m_nLevel > 0);
94
0
    m_nLevel--;
95
0
    if (m_bPretty)
96
0
        m_osIndentAcc.resize(m_osIndentAcc.size() - m_osIndent.size());
97
0
}
98
99
0
std::string CPLJSonStreamingWriter::FormatString(const std::string &str) {
100
0
    std::string ret;
101
0
    ret += '"';
102
0
    for (char ch : str) {
103
0
        switch (ch) {
104
0
        case '"':
105
0
            ret += "\\\"";
106
0
            break;
107
0
        case '\\':
108
0
            ret += "\\\\";
109
0
            break;
110
0
        case '\b':
111
0
            ret += "\\b";
112
0
            break;
113
0
        case '\f':
114
0
            ret += "\\f";
115
0
            break;
116
0
        case '\n':
117
0
            ret += "\\n";
118
0
            break;
119
0
        case '\r':
120
0
            ret += "\\r";
121
0
            break;
122
0
        case '\t':
123
0
            ret += "\\t";
124
0
            break;
125
0
        default:
126
0
            if (static_cast<unsigned char>(ch) < ' ')
127
0
                ret += CPLSPrintf("\\u%04X", ch);
128
0
            else
129
0
                ret += ch;
130
0
            break;
131
0
        }
132
0
    }
133
0
    ret += '"';
134
0
    return ret;
135
0
}
136
137
0
void CPLJSonStreamingWriter::EmitCommaIfNeeded() {
138
0
    if (m_bWaitForValue) {
139
0
        m_bWaitForValue = false;
140
0
    } else if (!m_states.empty()) {
141
0
        if (!m_states.back().bFirstChild) {
142
0
            Print(",");
143
0
            if (m_bPretty && !m_bNewLineEnabled)
144
0
                Print(" ");
145
0
        }
146
0
        if (m_bPretty && m_bNewLineEnabled) {
147
0
            Print("\n");
148
0
            Print(m_osIndentAcc);
149
0
        }
150
0
        m_states.back().bFirstChild = false;
151
0
    }
152
0
}
153
154
0
void CPLJSonStreamingWriter::StartObj() {
155
0
    EmitCommaIfNeeded();
156
0
    Print("{");
157
0
    IncIndent();
158
0
    m_states.emplace_back(State(true));
159
0
}
160
161
0
void CPLJSonStreamingWriter::EndObj() {
162
0
    CPLAssert(!m_bWaitForValue);
163
0
    CPLAssert(!m_states.empty());
164
0
    CPLAssert(m_states.back().bIsObj);
165
0
    DecIndent();
166
0
    if (!m_states.back().bFirstChild) {
167
0
        if (m_bPretty && m_bNewLineEnabled) {
168
0
            Print("\n");
169
0
            Print(m_osIndentAcc);
170
0
        }
171
0
    }
172
0
    m_states.pop_back();
173
0
    Print("}");
174
0
}
175
176
0
void CPLJSonStreamingWriter::StartArray() {
177
0
    EmitCommaIfNeeded();
178
0
    Print("[");
179
0
    IncIndent();
180
0
    m_states.emplace_back(State(false));
181
0
}
182
183
0
void CPLJSonStreamingWriter::EndArray() {
184
0
    CPLAssert(!m_states.empty());
185
0
    CPLAssert(!m_states.back().bIsObj);
186
0
    DecIndent();
187
0
    if (!m_states.back().bFirstChild) {
188
0
        if (m_bPretty && m_bNewLineEnabled) {
189
0
            Print("\n");
190
0
            Print(m_osIndentAcc);
191
0
        }
192
0
    }
193
0
    m_states.pop_back();
194
0
    Print("]");
195
0
}
196
197
0
void CPLJSonStreamingWriter::AddObjKey(const std::string &key) {
198
0
    CPLAssert(!m_states.empty());
199
0
    CPLAssert(m_states.back().bIsObj);
200
0
    CPLAssert(!m_bWaitForValue);
201
0
    EmitCommaIfNeeded();
202
0
    Print(FormatString(key));
203
0
    Print(m_bPretty ? ": " : ":");
204
0
    m_bWaitForValue = true;
205
0
}
206
207
0
void CPLJSonStreamingWriter::Add(bool bVal) {
208
0
    EmitCommaIfNeeded();
209
0
    Print(bVal ? "true" : "false");
210
0
}
211
212
0
void CPLJSonStreamingWriter::Add(const std::string &str) {
213
0
    EmitCommaIfNeeded();
214
0
    Print(FormatString(str));
215
0
}
216
217
0
void CPLJSonStreamingWriter::Add(const char *pszStr) {
218
0
    EmitCommaIfNeeded();
219
0
    Print(FormatString(pszStr));
220
0
}
221
222
0
void CPLJSonStreamingWriter::AddUnquoted(const char *pszStr) {
223
0
    EmitCommaIfNeeded();
224
0
    Print(pszStr);
225
0
}
226
227
0
void CPLJSonStreamingWriter::Add(GIntBig nVal) {
228
0
    EmitCommaIfNeeded();
229
0
    Print(CPLSPrintf(CPL_FRMT_GIB, nVal));
230
0
}
231
232
0
void CPLJSonStreamingWriter::Add(GUInt64 nVal) {
233
0
    EmitCommaIfNeeded();
234
0
    Print(CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nVal)));
235
0
}
236
237
0
void CPLJSonStreamingWriter::Add(float fVal, int nPrecision) {
238
0
    EmitCommaIfNeeded();
239
0
    if (CPLIsNan(fVal)) {
240
0
        Print("\"NaN\"");
241
0
    } else if (CPLIsInf(fVal)) {
242
0
        Print(fVal > 0 ? "\"Infinity\"" : "\"-Infinity\"");
243
0
    } else {
244
0
        char szFormatting[10];
245
0
        snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
246
0
        Print(CPLSPrintf(szFormatting, fVal));
247
0
    }
248
0
}
249
250
0
void CPLJSonStreamingWriter::Add(double dfVal, int nPrecision) {
251
0
    EmitCommaIfNeeded();
252
0
    if (CPLIsNan(dfVal)) {
253
0
        Print("\"NaN\"");
254
0
    } else if (CPLIsInf(dfVal)) {
255
0
        Print(dfVal > 0 ? "\"Infinity\"" : "\"-Infinity\"");
256
0
    } else if (dfVal >= std::numeric_limits<int>::min() &&
257
0
               dfVal <= std::numeric_limits<int>::max() &&
258
0
               static_cast<int>(dfVal) == dfVal) {
259
        // Avoid rounding issues on some platforms like armel, with numbers
260
        // like 2005. See https://github.com/OSGeo/PROJ/issues/3297
261
0
        Print(CPLSPrintf("%d", static_cast<int>(dfVal)));
262
0
    } else {
263
0
        char szFormatting[10];
264
0
        snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
265
0
        Print(CPLSPrintf(szFormatting, dfVal));
266
0
    }
267
0
}
268
269
0
void CPLJSonStreamingWriter::AddNull() {
270
0
    EmitCommaIfNeeded();
271
0
    Print("null");
272
0
}
273
274
NS_PROJ_END
275
276
/*! @endcond */