GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
#ifndef ZSERIO_JSON_DECODER_H_INC |
||
2 |
#define ZSERIO_JSON_DECODER_H_INC |
||
3 |
|||
4 |
#include <utility> |
||
5 |
#include <cerrno> |
||
6 |
#include <cmath> |
||
7 |
#include <cstdlib> |
||
8 |
#include <cstring> |
||
9 |
|||
10 |
#include "zserio/AllocatorHolder.h" |
||
11 |
#include "zserio/AnyHolder.h" |
||
12 |
#include "zserio/CppRuntimeException.h" |
||
13 |
#include "zserio/String.h" |
||
14 |
#include "zserio/StringView.h" |
||
15 |
|||
16 |
namespace zserio |
||
17 |
{ |
||
18 |
|||
19 |
/** |
||
20 |
* JSON value decoder. |
||
21 |
*/ |
||
22 |
template <typename ALLOC = std::allocator<uint8_t>> |
||
23 |
89 |
class BasicJsonDecoder : public AllocatorHolder<ALLOC> |
|
24 |
{ |
||
25 |
public: |
||
26 |
using AllocatorHolder<ALLOC>::get_allocator; |
||
27 |
|||
28 |
/** |
||
29 |
* Decoder result value. |
||
30 |
*/ |
||
31 |
49010 |
struct DecoderResult |
|
32 |
{ |
||
33 |
/** |
||
34 |
* Constructor used for decoder failure. |
||
35 |
* |
||
36 |
* \param numRead Number of processed characters. |
||
37 |
* \param allocator Allocator to use. |
||
38 |
*/ |
||
39 |
132 |
DecoderResult(size_t numRead, const ALLOC& allocator) : |
|
40 |
132 |
numReadChars(numRead), value(allocator), integerOverflow(false) |
|
41 |
132 |
{} |
|
42 |
|||
43 |
/** |
||
44 |
* Constructor for decoder success. |
||
45 |
* |
||
46 |
* \param numRead Number of processed characters. |
||
47 |
* \param decodedValue Value decoded from JSON stream. |
||
48 |
* \param allocator Allocator to use. |
||
49 |
*/ |
||
50 |
template <typename T> |
||
51 |
20355 |
DecoderResult(size_t numRead, T&& decodedValue, const ALLOC& allocator) : |
|
52 |
20355 |
numReadChars(numRead), value(std::forward<T>(decodedValue), allocator), integerOverflow(false) |
|
53 |
20355 |
{} |
|
54 |
|||
55 |
/** |
||
56 |
* Constructor used for integer decoder. |
||
57 |
* |
||
58 |
* \param numRead Number of processed characters. |
||
59 |
* \param decodedValue Value decoded from JSON stream. |
||
60 |
* \param overflow True in case of integer overflow. |
||
61 |
* \param allocator Allocator to use. |
||
62 |
*/ |
||
63 |
template <typename T> |
||
64 |
4107 |
DecoderResult(size_t numRead, T&& decodedValue, bool overflow, const ALLOC& allocator) : |
|
65 |
numReadChars(numRead), value(createValue(decodedValue, overflow, allocator)), |
||
66 |
4107 |
integerOverflow(overflow) |
|
67 |
4107 |
{} |
|
68 |
|||
69 |
size_t numReadChars; /**< Number of processed characters. */ |
||
70 |
AnyHolder<ALLOC> value; /**< Decoded value. Empty on failure. */ |
||
71 |
bool integerOverflow; /**< True if decoded value was bigger than UINT64_MAX or was not in interval |
||
72 |
<INT64_MIN, INT64_MAX>. */ |
||
73 |
|||
74 |
private: |
||
75 |
template <typename T> |
||
76 |
4107 |
AnyHolder<ALLOC> createValue(T&& decodedValue, bool overflow, const ALLOC& allocator) |
|
77 |
{ |
||
78 |
return overflow ? AnyHolder<ALLOC>(allocator) : |
||
79 |
✗✗✗✗ ✓✓✗✓ |
4107 |
AnyHolder<ALLOC>(std::forward<T>(decodedValue), allocator); |
80 |
} |
||
81 |
}; |
||
82 |
|||
83 |
/** |
||
84 |
* Empty constructor. |
||
85 |
*/ |
||
86 |
12 |
BasicJsonDecoder() : |
|
87 |
12 |
AllocatorHolder<ALLOC>(ALLOC()) |
|
88 |
12 |
{} |
|
89 |
|||
90 |
/** |
||
91 |
* Constructor from given allocator. |
||
92 |
* |
||
93 |
* \param allocator Allocator to use. |
||
94 |
*/ |
||
95 |
91 |
explicit BasicJsonDecoder(const ALLOC& allocator) : |
|
96 |
91 |
AllocatorHolder<ALLOC>(allocator) |
|
97 |
91 |
{} |
|
98 |
|||
99 |
/** |
||
100 |
* Decodes the JSON value from the input. |
||
101 |
* |
||
102 |
* \param input Input to decode from. |
||
103 |
* |
||
104 |
* \return Decoder result. |
||
105 |
*/ |
||
106 |
24503 |
DecoderResult decodeValue(StringView input) |
|
107 |
{ |
||
108 |
✗✓✓✓ |
24503 |
if (input.empty()) |
109 |
✗✗✓✗ |
1 |
return DecoderResult(0, get_allocator()); |
110 |
|||
111 |
✗✗✗✗ ✗✓✗✗ ✓✓✓✓ ✓✓✓✓ |
24502 |
switch (input[0]) |
112 |
{ |
||
113 |
case 'n': |
||
114 |
✗✗✓✗ |
9 |
return decodeLiteral(input, "null"_sv, nullptr); |
115 |
case 't': |
||
116 |
✗✗✓✗ |
7 |
return decodeLiteral(input, "true"_sv, true); |
117 |
case 'f': |
||
118 |
✗✗✓✗ |
4 |
return decodeLiteral(input, "false"_sv, false); |
119 |
case 'N': |
||
120 |
✗✗✓✗ |
4 |
return decodeLiteral(input, "NaN"_sv, static_cast<double>(NAN)); |
121 |
case 'I': |
||
122 |
✗✗✓✗ |
4 |
return decodeLiteral(input, "Infinity"_sv, static_cast<double>(INFINITY)); |
123 |
case '"': |
||
124 |
16331 |
return decodeString(input); |
|
125 |
case '-': |
||
126 |
✗✗✗✗ ✗✗✓✓ ✓✓✓✓ |
20 |
if (input.size() > 1 && input[1] == 'I') |
127 |
✗✗✓✗ |
5 |
return decodeLiteral(input, "-Infinity"_sv, -static_cast<double>(INFINITY)); |
128 |
15 |
return decodeNumber(input); |
|
129 |
default: |
||
130 |
8123 |
return decodeNumber(input); |
|
131 |
} |
||
132 |
} |
||
133 |
|||
134 |
private: |
||
135 |
template <typename T> |
||
136 |
DecoderResult decodeLiteral(StringView input, StringView literal, T&& value); |
||
137 |
DecoderResult decodeString(StringView input); |
||
138 |
static bool decodeUnicodeEscape(StringView input, StringView::const_iterator& inputIt, |
||
139 |
string<ALLOC>& value); |
||
140 |
static char decodeHex(char ch); |
||
141 |
size_t checkNumber(StringView input, bool& isDouble, bool& isSigned); |
||
142 |
DecoderResult decodeNumber(StringView input); |
||
143 |
DecoderResult decodeSigned(StringView input); |
||
144 |
DecoderResult decodeUnsigned(StringView input); |
||
145 |
DecoderResult decodeDouble(StringView input, size_t numChars); |
||
146 |
}; |
||
147 |
|||
148 |
template <typename ALLOC> |
||
149 |
template <typename T> |
||
150 |
33 |
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeLiteral( |
|
151 |
StringView input, StringView literal, T&& value) |
||
152 |
{ |
||
153 |
33 |
StringView::const_iterator literalIt = literal.begin(); |
|
154 |
33 |
StringView::const_iterator inputIt = input.begin(); |
|
155 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ |
315 |
while (inputIt != input.end() && literalIt != literal.end()) |
156 |
{ |
||
157 |
✗✗✗✗ ✗✗✓✓ ✓✓✓✓ |
148 |
if (*inputIt++ != *literalIt++) |
158 |
{ |
||
159 |
// failure, not decoded |
||
160 |
✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✓✗✓✗ |
7 |
return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator()); |
161 |
} |
||
162 |
} |
||
163 |
|||
164 |
✗✗✗✗ ✗✗✓✓ ✓✓✓✓ |
26 |
if (literalIt != literal.end()) |
165 |
{ |
||
166 |
// short input, not decoded |
||
167 |
✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✓✗✓✗ |
6 |
return DecoderResult(input.size(), get_allocator()); |
168 |
} |
||
169 |
|||
170 |
// success |
||
171 |
✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✓✗✓✗ |
20 |
return DecoderResult(literal.size(), std::forward<T>(value), get_allocator()); |
172 |
} |
||
173 |
|||
174 |
template <typename ALLOC> |
||
175 |
16331 |
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeString(StringView input) |
|
176 |
{ |
||
177 |
16331 |
StringView::const_iterator inputIt = input.begin() + 1; // we know that at the beginning is '"' |
|
178 |
✓✗✗✗ ✓✗✓✗ |
32662 |
string<ALLOC> value(get_allocator()); |
179 |
|||
180 |
✓✗✓✓ |
149493 |
while (inputIt != input.end()) |
181 |
{ |
||
182 |
✗✓✓✓ |
82910 |
if (*inputIt == '\\') |
183 |
{ |
||
184 |
24 |
++inputIt; |
|
185 |
✗✗✓✓ |
24 |
if (inputIt == input.end()) |
186 |
{ |
||
187 |
// wrong escape, not decoded |
||
188 |
✗✗✗✗ ✓✗✓✗ |
1 |
return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator()); |
189 |
} |
||
190 |
|||
191 |
23 |
char nextChar = *inputIt; |
|
192 |
✗✗✗✗ ✗✗✗✗ ✓✓✓✓ ✓✓✓✓ |
23 |
switch (nextChar) |
193 |
{ |
||
194 |
case '\\': |
||
195 |
case '"': |
||
196 |
✗✗✓✗ |
4 |
value.push_back(nextChar); |
197 |
4 |
++inputIt; |
|
198 |
4 |
break; |
|
199 |
case 'b': |
||
200 |
✗✗✓✗ |
1 |
value.push_back('\b'); |
201 |
1 |
++inputIt; |
|
202 |
1 |
break; |
|
203 |
case 'f': |
||
204 |
✗✗✓✗ |
1 |
value.push_back('\f'); |
205 |
1 |
++inputIt; |
|
206 |
1 |
break; |
|
207 |
case 'n': |
||
208 |
✗✗✓✗ |
2 |
value.push_back('\n'); |
209 |
2 |
++inputIt; |
|
210 |
2 |
break; |
|
211 |
case 'r': |
||
212 |
✗✗✓✗ |
1 |
value.push_back('\r'); |
213 |
1 |
++inputIt; |
|
214 |
1 |
break; |
|
215 |
case 't': |
||
216 |
✗✗✓✗ |
2 |
value.push_back('\t'); |
217 |
2 |
++inputIt; |
|
218 |
2 |
break; |
|
219 |
case 'u': // unicode escape |
||
220 |
{ |
||
221 |
11 |
++inputIt; |
|
222 |
✗✗✗✗ ✓✗✓✓ |
11 |
if (!decodeUnicodeEscape(input, inputIt, value)) |
223 |
{ |
||
224 |
// unsupported unicode escape, not decoded |
||
225 |
✗✗✗✗ ✓✗✓✗ |
10 |
return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator()); |
226 |
} |
||
227 |
1 |
break; |
|
228 |
} |
||
229 |
default: |
||
230 |
1 |
++inputIt; |
|
231 |
// unknown escape, not decoded |
||
232 |
✗✗✗✗ ✓✗✓✗ |
1 |
return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator()); |
233 |
} |
||
234 |
} |
||
235 |
✓✓✓✓ |
82886 |
else if (*inputIt == '"') |
236 |
{ |
||
237 |
16317 |
++inputIt; |
|
238 |
// successfully decoded |
||
239 |
32634 |
return DecoderResult(static_cast<size_t>(inputIt - input.begin()), std::move(value), |
|
240 |
✓✗✓✗ ✓✗✓✗ |
48951 |
get_allocator()); |
241 |
} |
||
242 |
else |
||
243 |
{ |
||
244 |
✓✗✓✗ |
66569 |
value.push_back(*inputIt++); |
245 |
} |
||
246 |
} |
||
247 |
|||
248 |
// unterminated string, not decoded |
||
249 |
✗✗✗✗ ✓✗✓✗ |
2 |
return DecoderResult(input.size(), get_allocator()); |
250 |
} |
||
251 |
|||
252 |
template <typename ALLOC> |
||
253 |
11 |
bool BasicJsonDecoder<ALLOC>::decodeUnicodeEscape(StringView input, StringView::const_iterator& inputIt, |
|
254 |
string<ALLOC>& value) |
||
255 |
{ |
||
256 |
// TODO[Mi-L@]: Simplified just to decode what zserio encodes, for complex solution we could use |
||
257 |
// std::wstring_convert but it's deprecated in C++17. |
||
258 |
✗✗✗✗ ✗✗✓✓ ✓✓✓✓ |
11 |
if (inputIt == input.end() || *inputIt++ != '0') |
259 |
2 |
return false; |
|
260 |
✗✗✗✗ ✗✗✓✓ ✓✓✓✓ |
9 |
if (inputIt == input.end() || *inputIt++ != '0') |
261 |
2 |
return false; |
|
262 |
|||
263 |
✗✗✓✓ |
7 |
if (inputIt == input.end()) |
264 |
1 |
return false; |
|
265 |
6 |
const char ch1 = decodeHex(*inputIt++); |
|
266 |
✗✗✓✓ |
6 |
if (ch1 == -1) |
267 |
2 |
return false; |
|
268 |
|||
269 |
✗✗✓✓ |
4 |
if (inputIt == input.end()) |
270 |
1 |
return false; |
|
271 |
3 |
const char ch2 = decodeHex(*inputIt++); |
|
272 |
✗✗✓✓ |
3 |
if (ch2 == -1) |
273 |
2 |
return false; |
|
274 |
|||
275 |
1 |
value.push_back(static_cast<char>((static_cast<uint32_t>(ch1) << 4U) | static_cast<uint32_t>(ch2))); |
|
276 |
1 |
return true; |
|
277 |
} |
||
278 |
|||
279 |
template <typename ALLOC> |
||
280 |
9 |
char BasicJsonDecoder<ALLOC>::decodeHex(char ch) |
|
281 |
{ |
||
282 |
✗✗✗✗ ✓✓✓✓ |
9 |
if (ch >= '0' && ch <= '9') |
283 |
3 |
return static_cast<char>(ch - '0'); |
|
284 |
✗✗✗✗ ✓✓✓✓ |
6 |
else if (ch >= 'a' && ch <= 'f') |
285 |
1 |
return static_cast<char>(ch - 'a' + 10); |
|
286 |
✗✗✗✗ ✓✓✓✓ |
5 |
else if (ch >= 'A' && ch <= 'F') |
287 |
1 |
return static_cast<char>(ch - 'A' + 10); |
|
288 |
|||
289 |
4 |
return -1; |
|
290 |
} |
||
291 |
|||
292 |
template <typename ALLOC> |
||
293 |
8138 |
size_t BasicJsonDecoder<ALLOC>::checkNumber(StringView input, bool& isDouble, bool& isSigned) |
|
294 |
{ |
||
295 |
8138 |
StringView::const_iterator inputIt = input.begin(); |
|
296 |
8138 |
bool acceptExpSign = false; |
|
297 |
8138 |
isDouble = false; |
|
298 |
|||
299 |
✗✗✓✓ |
8138 |
if (*inputIt == '-') // we know that at the beginning is at least one character |
300 |
{ |
||
301 |
15 |
++inputIt; |
|
302 |
15 |
isSigned = true; |
|
303 |
} |
||
304 |
else |
||
305 |
{ |
||
306 |
8123 |
isSigned = false; |
|
307 |
} |
||
308 |
|||
309 |
✗✗✓✓ |
105006 |
while (inputIt != input.end()) |
310 |
{ |
||
311 |
✗✗✓✓ |
56544 |
if (acceptExpSign) |
312 |
{ |
||
313 |
4013 |
acceptExpSign = false; |
|
314 |
✗✗✗✗ ✓✓✓✓ |
4013 |
if (*inputIt == '+' || *inputIt == '-') |
315 |
{ |
||
316 |
11 |
++inputIt; |
|
317 |
11 |
continue; |
|
318 |
} |
||
319 |
} |
||
320 |
✗✗✗✗ ✓✓✓✓ |
56533 |
if (*inputIt >= '0' && *inputIt <= '9') |
321 |
{ |
||
322 |
44399 |
++inputIt; |
|
323 |
44399 |
continue; |
|
324 |
} |
||
325 |
✗✗✗✗ ✗✗✗✗ ✓✓✓✓ ✓✓✓✓ |
12134 |
if (!isDouble && (*inputIt == '.' || *inputIt == 'e' || *inputIt == 'E')) |
326 |
{ |
||
327 |
4024 |
isDouble = true; |
|
328 |
✗✗✗✗ ✓✓✓✓ |
4024 |
if (*inputIt == 'e' || *inputIt == 'E') |
329 |
4015 |
acceptExpSign = true; |
|
330 |
4024 |
++inputIt; |
|
331 |
4024 |
continue; |
|
332 |
} |
||
333 |
|||
334 |
8110 |
break; // end of a number |
|
335 |
} |
||
336 |
|||
337 |
8138 |
const size_t numberLen = static_cast<size_t>(inputIt - input.begin()); |
|
338 |
✗✗✗✗ ✓✓✓✓ |
8138 |
if (isSigned && numberLen == 1) |
339 |
3 |
return 0; // single minus is not a number |
|
340 |
|||
341 |
8135 |
return numberLen; |
|
342 |
} |
||
343 |
|||
344 |
template <typename ALLOC> |
||
345 |
8138 |
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeNumber(StringView input) |
|
346 |
{ |
||
347 |
8138 |
bool isDouble = false; |
|
348 |
8138 |
bool isSigned = false; |
|
349 |
8138 |
const size_t numChars = checkNumber(input, isDouble, isSigned); |
|
350 |
✗✗✓✓ |
8138 |
if (numChars == 0) |
351 |
✗✗✗✗ ✓✗✓✗ |
7 |
return DecoderResult(1, get_allocator()); |
352 |
|||
353 |
// for decodeSigned and decodeUnsigned, we know that all numChars will be processed because checkNumber |
||
354 |
// already checked this |
||
355 |
✗✗✓✓ |
8131 |
if (isDouble) |
356 |
✗✗✓✗ |
4024 |
return decodeDouble(input, numChars); |
357 |
✗✗✓✓ |
4107 |
else if (isSigned) |
358 |
✗✗✓✗ |
7 |
return decodeSigned(input); |
359 |
else |
||
360 |
✗✗✓✗ |
4100 |
return decodeUnsigned(input); |
361 |
} |
||
362 |
|||
363 |
template <typename ALLOC> |
||
364 |
7 |
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeSigned(StringView input) |
|
365 |
{ |
||
366 |
7 |
char* pEnd = nullptr; |
|
367 |
7 |
errno = 0; // no library function sets its value back to zero once changed |
|
368 |
7 |
const int64_t value = std::strtoll(input.begin(), &pEnd, 10); |
|
369 |
|||
370 |
7 |
const bool overflow = (errno == ERANGE); |
|
371 |
|||
372 |
✗✗✗✗ ✓✗✓✗ |
7 |
return DecoderResult(static_cast<size_t>(pEnd - input.begin()), value, overflow, get_allocator()); |
373 |
} |
||
374 |
|||
375 |
template <typename ALLOC> |
||
376 |
4100 |
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeUnsigned(StringView input) |
|
377 |
{ |
||
378 |
4100 |
char* pEnd = nullptr; |
|
379 |
4100 |
errno = 0; // no library function sets its value back to zero once changed |
|
380 |
4100 |
const uint64_t value = std::strtoull(input.begin(), &pEnd, 10); |
|
381 |
|||
382 |
4100 |
const bool overflow = (errno == ERANGE); |
|
383 |
|||
384 |
✗✗✗✗ ✓✗✓✗ |
4100 |
return DecoderResult(static_cast<size_t>(pEnd - input.begin()), value, overflow, get_allocator()); |
385 |
} |
||
386 |
|||
387 |
template <typename ALLOC> |
||
388 |
4024 |
typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeDouble( |
|
389 |
StringView input, size_t numChars) |
||
390 |
{ |
||
391 |
4024 |
char* pEnd = nullptr; |
|
392 |
4024 |
const double value = std::strtod(input.begin(), &pEnd); |
|
393 |
✗✗✓✓ |
4024 |
if (static_cast<size_t>(pEnd - input.begin()) != numChars) |
394 |
✗✗✗✗ ✓✗✓✗ |
6 |
return DecoderResult(numChars, get_allocator()); |
395 |
|||
396 |
✗✗✗✗ ✓✗✓✗ |
4018 |
return DecoderResult(numChars, value, get_allocator()); |
397 |
} |
||
398 |
|||
399 |
} // namespace zserio |
||
400 |
|||
401 |
#endif // ZSERIO_JSON_DECODER_H_INC |
Generated by: GCOVR (Version 4.2) |