Zserio C++ runtime library  1.0.2
Built for Zserio 2.14.1
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 <utility>
9 
10 #include "zserio/AllocatorHolder.h"
11 #include "zserio/AnyHolder.h"
13 #include "zserio/String.h"
14 #include "zserio/StringView.h"
15 
16 namespace zserio
17 {
18 
22 template <typename ALLOC = std::allocator<uint8_t>>
23 class BasicJsonDecoder : public AllocatorHolder<ALLOC>
24 {
25 public:
27 
32  {
39  DecoderResult(size_t numRead, const ALLOC& allocator) :
40  numReadChars(numRead),
41  value(allocator),
42  integerOverflow(false)
43  {}
44 
52  template <typename T>
53  DecoderResult(size_t numRead, T&& decodedValue, const ALLOC& allocator) :
54  numReadChars(numRead),
55  value(std::forward<T>(decodedValue), allocator),
56  integerOverflow(false)
57  {}
58 
67  template <typename T>
68  DecoderResult(size_t numRead, T&& decodedValue, bool overflow, const ALLOC& allocator) :
69  numReadChars(numRead),
70  value(createValue(decodedValue, overflow, allocator)),
71  integerOverflow(overflow)
72  {}
73 
74  size_t numReadChars;
79  private:
80  template <typename T>
81  AnyHolder<ALLOC> createValue(T&& decodedValue, bool overflow, const ALLOC& allocator)
82  {
83  return overflow ? AnyHolder<ALLOC>(allocator)
84  : AnyHolder<ALLOC>(std::forward<T>(decodedValue), allocator);
85  }
86  };
87 
92  AllocatorHolder<ALLOC>(ALLOC())
93  {}
94 
100  explicit BasicJsonDecoder(const ALLOC& allocator) :
101  AllocatorHolder<ALLOC>(allocator)
102  {}
103 
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 
143 private:
144  template <typename T>
145  DecoderResult decodeLiteral(StringView input, StringView literal, T&& value);
146  DecoderResult decodeString(StringView input);
147  static bool decodeUnicodeEscape(
148  StringView input, StringView::const_iterator& inputIt, string<ALLOC>& value);
149  static char decodeHex(char character);
150  size_t checkNumber(StringView input, bool& isDouble, bool& isSigned);
151  DecoderResult decodeNumber(StringView input);
152  DecoderResult decodeSigned(StringView input);
153  DecoderResult decodeUnsigned(StringView input);
154  DecoderResult decodeDouble(StringView input, size_t numChars);
155 };
156 
157 template <typename ALLOC>
158 template <typename T>
159 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeLiteral(
160  StringView input, StringView literal, T&& value)
161 {
162  StringView::const_iterator literalIt = literal.begin();
163  StringView::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 
183 template <typename ALLOC>
184 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeString(StringView input)
185 {
186  StringView::const_iterator inputIt = input.begin() + 1; // we know that at the beginning is '"'
187  string<ALLOC> 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 
261 template <typename ALLOC>
262 bool BasicJsonDecoder<ALLOC>::decodeUnicodeEscape(
263  StringView input, StringView::const_iterator& inputIt, string<ALLOC>& value)
264 {
265  // TODO[Mi-L@]: Simplified just to decode what zserio encodes, for complex solution we could use
266  // std::wstring_convert but it's deprecated in C++17.
267  if (inputIt == input.end() || *inputIt++ != '0')
268  {
269  return false;
270  }
271  if (inputIt == input.end() || *inputIt++ != '0')
272  {
273  return false;
274  }
275 
276  if (inputIt == input.end())
277  {
278  return false;
279  }
280  const char char1 = decodeHex(*inputIt++);
281  if (char1 == -1)
282  {
283  return false;
284  }
285 
286  if (inputIt == input.end())
287  {
288  return false;
289  }
290  const char char2 = decodeHex(*inputIt++);
291  if (char2 == -1)
292  {
293  return false;
294  }
295 
296  value.push_back(static_cast<char>((static_cast<uint32_t>(char1) << 4U) | static_cast<uint32_t>(char2)));
297  return true;
298 }
299 
300 template <typename ALLOC>
301 char BasicJsonDecoder<ALLOC>::decodeHex(char character)
302 {
303  if (character >= '0' && character <= '9')
304  {
305  return static_cast<char>(character - '0');
306  }
307  else if (character >= 'a' && character <= 'f')
308  {
309  return static_cast<char>(character - 'a' + 10);
310  }
311  else if (character >= 'A' && character <= 'F')
312  {
313  return static_cast<char>(character - 'A' + 10);
314  }
315 
316  return -1;
317 }
318 
319 template <typename ALLOC>
320 size_t BasicJsonDecoder<ALLOC>::checkNumber(StringView input, bool& isDouble, bool& isSigned)
321 {
322  StringView::const_iterator inputIt = input.begin();
323  bool acceptExpSign = false;
324  bool isScientificDouble = false;
325  isDouble = false;
326 
327  if (*inputIt == '-') // we know that at the beginning is at least one character
328  {
329  ++inputIt;
330  isSigned = true;
331  }
332  else
333  {
334  isSigned = false;
335  }
336 
337  while (inputIt != input.end())
338  {
339  if (acceptExpSign)
340  {
341  acceptExpSign = false;
342  if (*inputIt == '+' || *inputIt == '-')
343  {
344  ++inputIt;
345  continue;
346  }
347  }
348 
349  if (*inputIt >= '0' && *inputIt <= '9')
350  {
351  ++inputIt;
352  continue;
353  }
354 
355  if ((*inputIt == 'e' || *inputIt == 'E') && !isScientificDouble)
356  {
357  isDouble = true;
358  isScientificDouble = true;
359  acceptExpSign = true;
360  ++inputIt;
361  continue;
362  }
363 
364  if (*inputIt == '.' && !isDouble)
365  {
366  isDouble = true;
367  ++inputIt;
368  continue;
369  }
370 
371  break; // end of a number
372  }
373 
374  const size_t numberLen = static_cast<size_t>(inputIt - input.begin());
375  if (isSigned && numberLen == 1)
376  {
377  return 0; // single minus is not a number
378  }
379 
380  return numberLen;
381 }
382 
383 template <typename ALLOC>
384 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeNumber(StringView input)
385 {
386  bool isDouble = false;
387  bool isSigned = false;
388  const size_t numChars = checkNumber(input, isDouble, isSigned);
389  if (numChars == 0)
390  {
391  return DecoderResult(1, get_allocator());
392  }
393 
394  // for decodeSigned and decodeUnsigned, we know that all numChars will be processed because checkNumber
395  // already checked this
396  if (isDouble)
397  {
398  return decodeDouble(input, numChars);
399  }
400  else if (isSigned)
401  {
402  return decodeSigned(input);
403  }
404  else
405  {
406  return decodeUnsigned(input);
407  }
408 }
409 
410 template <typename ALLOC>
411 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeSigned(StringView input)
412 {
413  char* pEnd = nullptr;
414  errno = 0; // no library function sets its value back to zero once changed
415  const int64_t value = std::strtoll(input.begin(), &pEnd, 10);
416 
417  const bool overflow = (errno == ERANGE);
418 
419  return DecoderResult(static_cast<size_t>(pEnd - input.begin()), value, overflow, get_allocator());
420 }
421 
422 template <typename ALLOC>
423 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeUnsigned(StringView input)
424 {
425  char* pEnd = nullptr;
426  errno = 0; // no library function sets its value back to zero once changed
427  const uint64_t value = std::strtoull(input.begin(), &pEnd, 10);
428 
429  const bool overflow = (errno == ERANGE);
430 
431  return DecoderResult(static_cast<size_t>(pEnd - input.begin()), value, overflow, get_allocator());
432 }
433 
434 template <typename ALLOC>
435 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeDouble(
436  StringView input, size_t numChars)
437 {
438  char* pEnd = nullptr;
439  const double value = std::strtod(input.begin(), &pEnd);
440  if (static_cast<size_t>(pEnd - input.begin()) != numChars)
441  {
442  return DecoderResult(numChars, get_allocator());
443  }
444 
445  return DecoderResult(numChars, value, get_allocator());
446 }
447 
448 } // namespace zserio
449 
450 #endif // ZSERIO_JSON_DECODER_H_INC
BasicJsonDecoder(const ALLOC &allocator)
Definition: JsonDecoder.h:100
DecoderResult decodeValue(StringView input)
Definition: JsonDecoder.h:111
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:68
DecoderResult(size_t numRead, const ALLOC &allocator)
Definition: JsonDecoder.h:39
DecoderResult(size_t numRead, T &&decodedValue, const ALLOC &allocator)
Definition: JsonDecoder.h:53