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