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