Zserio C++17 runtime library 1.3.0
Built for Zserio 2.20.0
Loading...
Searching...
No Matches
JsonDecoder.h
Go to the documentation of this file.
1#ifndef ZSERIO_JSON_DECODER_H_INC
2#define ZSERIO_JSON_DECODER_H_INC
3
4#include <cerrno>
5#include <cmath>
6#include <cstdlib>
7#include <string_view>
8#include <utility>
9
11#include "zserio/Any.h"
12#include "zserio/String.h"
13
14namespace zserio
15{
16
20template <typename ALLOC = std::allocator<uint8_t>>
21class BasicJsonDecoder : public AllocatorHolder<ALLOC>
22{
23public:
25
30 {
42
50 template <typename T>
56
65 template <typename T>
71
72 size_t numReadChars;
77 private:
78 template <typename T>
79 BasicAny<ALLOC> createValue(T&& decodedValue, bool overflow, const ALLOC& allocator)
80 {
82 : BasicAny<ALLOC>(std::forward<T>(decodedValue), allocator);
83 }
84 };
85
92
101
110 {
111 using namespace std::literals::string_view_literals;
112
113 if (input.empty())
114 {
115 return DecoderResult(0, get_allocator());
116 }
117
118 switch (input[0])
119 {
120 case 'n':
121 return decodeLiteral(input, "null"sv, nullptr);
122 case 't':
123 return decodeLiteral(input, "true"sv, true);
124 case 'f':
125 return decodeLiteral(input, "false"sv, false);
126 case 'N':
127 return decodeLiteral(input, "NaN"sv, static_cast<double>(NAN));
128 case 'I':
129 return decodeLiteral(input, "Infinity"sv, static_cast<double>(INFINITY));
130 case '"':
131 return decodeString(input);
132 case '-':
133 if (input.size() > 1 && input[1] == 'I')
134 {
135 return decodeLiteral(input, "-Infinity"sv, -static_cast<double>(INFINITY));
136 }
137 return decodeNumber(input);
138 default:
139 return decodeNumber(input);
140 }
141 }
142
143private:
144 template <typename T>
145 DecoderResult decodeLiteral(std::string_view input, std::string_view literal, T&& value);
146 DecoderResult decodeString(std::string_view input);
147 static bool decodeUnicodeEscape(std::string_view input, std::string_view::const_iterator& inputIt,
149 static int32_t decodeHex(char character);
150 size_t checkNumber(std::string_view input, bool& isDouble, bool& isSigned);
151 DecoderResult decodeNumber(std::string_view input);
152 DecoderResult decodeSigned(std::string_view input);
153 DecoderResult decodeUnsigned(std::string_view input);
154 DecoderResult decodeDouble(std::string_view input, size_t numChars);
155};
156
157template <typename ALLOC>
158template <typename T>
159typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeLiteral(
160 std::string_view input, std::string_view literal, T&& value)
161{
162 std::string_view::const_iterator literalIt = literal.begin();
163 std::string_view::const_iterator inputIt = input.begin();
164 while (inputIt != input.end() && literalIt != literal.end())
165 {
166 if (*inputIt++ != *literalIt++)
167 {
168 // failure, not decoded
169 return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
170 }
171 }
172
173 if (literalIt != literal.end())
174 {
175 // short input, not decoded
176 return DecoderResult(input.size(), get_allocator());
177 }
178
179 // success
180 return DecoderResult(literal.size(), std::forward<T>(value), get_allocator());
181}
182
183template <typename ALLOC>
184typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeString(std::string_view input)
185{
186 std::string_view::const_iterator inputIt = input.begin() + 1; // we know that at the beginning is '"'
187 BasicString<RebindAlloc<ALLOC, char>> value(get_allocator());
188
189 while (inputIt != input.end())
190 {
191 if (*inputIt == '\\')
192 {
193 ++inputIt;
194 if (inputIt == input.end())
195 {
196 // wrong escape, not decoded
197 return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
198 }
199
200 char nextChar = *inputIt;
201 switch (nextChar)
202 {
203 case '\\':
204 case '"':
205 value.push_back(nextChar);
206 ++inputIt;
207 break;
208 case 'b':
209 value.push_back('\b');
210 ++inputIt;
211 break;
212 case 'f':
213 value.push_back('\f');
214 ++inputIt;
215 break;
216 case 'n':
217 value.push_back('\n');
218 ++inputIt;
219 break;
220 case 'r':
221 value.push_back('\r');
222 ++inputIt;
223 break;
224 case 't':
225 value.push_back('\t');
226 ++inputIt;
227 break;
228 case 'u': // unicode escape
229 {
230 ++inputIt;
231 if (!decodeUnicodeEscape(input, inputIt, value))
232 {
233 // unsupported unicode escape, not decoded
234 return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
235 }
236 break;
237 }
238 default:
239 ++inputIt;
240 // unknown escape, not decoded
241 return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
242 }
243 }
244 else if (*inputIt == '"')
245 {
246 ++inputIt;
247 // successfully decoded
248 return DecoderResult(
249 static_cast<size_t>(inputIt - input.begin()), std::move(value), get_allocator());
250 }
251 else
252 {
253 value.push_back(*inputIt++);
254 }
255 }
256
257 // unterminated string, not decoded
258 return DecoderResult(input.size(), get_allocator());
259}
260
261template <typename ALLOC>
262bool BasicJsonDecoder<ALLOC>::decodeUnicodeEscape(std::string_view input,
263 std::string_view::const_iterator& inputIt, BasicString<RebindAlloc<ALLOC, char>>& value)
264{
265 // simplified just to decode what zserio encodes
266 if (inputIt == input.end() || *inputIt++ != '0')
267 {
268 return false;
269 }
270 if (inputIt == input.end() || *inputIt++ != '0')
271 {
272 return false;
273 }
274
275 if (inputIt == input.end())
276 {
277 return false;
278 }
279
280 const int32_t hex1 = decodeHex(*inputIt++);
281 if (hex1 < 0)
282 {
283 return false;
284 }
285
286 if (inputIt == input.end())
287 {
288 return false;
289 }
290
291 const int32_t hex2 = decodeHex(*inputIt++);
292 if (hex2 < 0)
293 {
294 return false;
295 }
296
297 const uint32_t characterInt = (static_cast<uint32_t>(hex1) << 4U) | static_cast<uint32_t>(hex2);
298 using char_traits = std::char_traits<char>;
299 const char character = char_traits::to_char_type(static_cast<char_traits::int_type>(characterInt));
300 value.push_back(character);
301
302 return true;
303}
304
305template <typename ALLOC>
306int32_t BasicJsonDecoder<ALLOC>::decodeHex(char character)
307{
308 if (character >= '0' && character <= '9')
309 {
310 return static_cast<int32_t>(character - '0');
311 }
312 else if (character >= 'a' && character <= 'f')
313 {
314 return static_cast<int32_t>(character - 'a' + 10);
315 }
316 else if (character >= 'A' && character <= 'F')
317 {
318 return static_cast<int32_t>(character - 'A' + 10);
319 }
320
321 return -1;
322}
323
324template <typename ALLOC>
325size_t BasicJsonDecoder<ALLOC>::checkNumber(std::string_view input, bool& isDouble, bool& isSigned)
326{
327 std::string_view::const_iterator inputIt = input.begin();
328 bool acceptExpSign = false;
329 bool isScientificDouble = false;
330 isDouble = false;
331
332 if (*inputIt == '-') // we know that at the beginning is at least one character
333 {
334 ++inputIt;
335 isSigned = true;
336 }
337 else
338 {
339 isSigned = false;
340 }
341
342 while (inputIt != input.end())
343 {
344 if (acceptExpSign)
345 {
346 acceptExpSign = false;
347 if (*inputIt == '+' || *inputIt == '-')
348 {
349 ++inputIt;
350 continue;
351 }
352 }
353
354 if (*inputIt >= '0' && *inputIt <= '9')
355 {
356 ++inputIt;
357 continue;
358 }
359
360 if ((*inputIt == 'e' || *inputIt == 'E') && !isScientificDouble)
361 {
362 isDouble = true;
363 isScientificDouble = true;
364 acceptExpSign = true;
365 ++inputIt;
366 continue;
367 }
368
369 if (*inputIt == '.' && !isDouble)
370 {
371 isDouble = true;
372 ++inputIt;
373 continue;
374 }
375
376 break; // end of a number
377 }
378
379 const size_t numberLen = static_cast<size_t>(inputIt - input.begin());
380 if (isSigned && numberLen == 1)
381 {
382 return 0; // single minus is not a number
383 }
384
385 return numberLen;
386}
387
388template <typename ALLOC>
389typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeNumber(std::string_view input)
390{
391 bool isDouble = false;
392 bool isSigned = false;
393 const size_t numChars = checkNumber(input, isDouble, isSigned);
394 if (numChars == 0)
395 {
396 return DecoderResult(1, get_allocator());
397 }
398
399 // for decodeSigned and decodeUnsigned, we know that all numChars will be processed because checkNumber
400 // already checked this
401 if (isDouble)
402 {
403 return decodeDouble(input, numChars);
404 }
405 else if (isSigned)
406 {
407 return decodeSigned(input);
408 }
409 else
410 {
411 return decodeUnsigned(input);
412 }
413}
414
415template <typename ALLOC>
416typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeSigned(std::string_view input)
417{
418 const char* pBegin = &input.front();
419 char* pEnd = nullptr;
420 errno = 0; // no library function sets its value back to zero once changed
421 const int64_t value = std::strtoll(pBegin, &pEnd, 10);
422
423 const bool overflow = (errno == ERANGE);
424 const size_t numRead = static_cast<size_t>(pEnd - pBegin);
425
426 return DecoderResult(numRead, value, overflow, get_allocator());
427}
428
429template <typename ALLOC>
430typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeUnsigned(std::string_view input)
431{
432 const char* pBegin = &input.front();
433 char* pEnd = nullptr;
434 errno = 0; // no library function sets its value back to zero once changed
435 const uint64_t value = std::strtoull(pBegin, &pEnd, 10);
436
437 const bool overflow = (errno == ERANGE);
438 const size_t numRead = static_cast<size_t>(pEnd - pBegin);
439
440 return DecoderResult(numRead, value, overflow, get_allocator());
441}
442
443template <typename ALLOC>
444typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeDouble(
445 std::string_view input, size_t numChars)
446{
447 const char* pBegin = &input.front();
448 char* pEnd = nullptr;
449 const double value = std::strtod(pBegin, &pEnd);
450 if (static_cast<size_t>(pEnd - pBegin) != numChars)
451 {
452 return DecoderResult(numChars, get_allocator());
453 }
454
455 return DecoderResult(numChars, value, get_allocator());
456}
457
458} // namespace zserio
459
460#endif // ZSERIO_JSON_DECODER_H_INC
allocator_type get_allocator() const
BasicJsonDecoder(const ALLOC &allocator)
Definition JsonDecoder.h:98
DecoderResult decodeValue(std::string_view input)
std::basic_string< char, std::char_traits< char >, ALLOC > BasicString
Definition String.h:17
DecoderResult(size_t numRead, T &&decodedValue, bool overflow, const ALLOC &allocator)
Definition JsonDecoder.h:66
DecoderResult(size_t numRead, const ALLOC &allocator)
Definition JsonDecoder.h:37
DecoderResult(size_t numRead, T &&decodedValue, const ALLOC &allocator)
Definition JsonDecoder.h:51