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