Zserio C++17 runtime library  0.5.0
Built for Zserio 2.17.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 <string_view>
8 #include <utility>
9 
10 #include "zserio/AllocatorHolder.h"
11 #include "zserio/Any.h"
12 #include "zserio/String.h"
13 
14 namespace zserio
15 {
16 
20 template <typename ALLOC = std::allocator<uint8_t>>
21 class BasicJsonDecoder : public AllocatorHolder<ALLOC>
22 {
23 public:
25 
30  {
37  DecoderResult(size_t numRead, const ALLOC& allocator) :
38  numReadChars(numRead),
39  value(allocator),
40  integerOverflow(false)
41  {}
42 
50  template <typename T>
51  DecoderResult(size_t numRead, T&& decodedValue, const ALLOC& allocator) :
52  numReadChars(numRead),
53  value(std::forward<T>(decodedValue), allocator),
54  integerOverflow(false)
55  {}
56 
65  template <typename T>
66  DecoderResult(size_t numRead, T&& decodedValue, bool overflow, const ALLOC& allocator) :
67  numReadChars(numRead),
68  value(createValue(std::forward<T>(decodedValue), overflow, allocator)),
69  integerOverflow(overflow)
70  {}
71 
72  size_t numReadChars;
77  private:
78  template <typename T>
79  BasicAny<ALLOC> createValue(T&& decodedValue, bool overflow, const ALLOC& allocator)
80  {
81  return overflow ? BasicAny<ALLOC>(allocator)
82  : BasicAny<ALLOC>(std::forward<T>(decodedValue), allocator);
83  }
84  };
85 
90  AllocatorHolder<ALLOC>(ALLOC())
91  {}
92 
98  explicit BasicJsonDecoder(const ALLOC& allocator) :
99  AllocatorHolder<ALLOC>(allocator)
100  {}
101 
109  DecoderResult decodeValue(std::string_view input)
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 
143 private:
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 
157 template <typename ALLOC>
158 template <typename T>
159 typename 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 
183 template <typename ALLOC>
184 typename 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 
261 template <typename ALLOC>
262 bool 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 
305 template <typename ALLOC>
306 int32_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 
324 template <typename ALLOC>
325 size_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 
388 template <typename ALLOC>
389 typename 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 
415 template <typename ALLOC>
416 typename 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 
429 template <typename ALLOC>
430 typename 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 
443 template <typename ALLOC>
444 typename 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
BasicJsonDecoder(const ALLOC &allocator)
Definition: JsonDecoder.h:98
DecoderResult decodeValue(std::string_view input)
Definition: JsonDecoder.h:109
Definition: BitBuffer.h:602
std::basic_string< char, std::char_traits< char >, ALLOC > BasicString
Definition: String.h:17
typename std::allocator_traits< ALLOC >::template rebind_alloc< T > RebindAlloc
Definition: RebindAlloc.h:10
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