Zserio C++ runtime library  1.0.1
Built for Zserio 2.14.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 <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  return DecoderResult(0, get_allocator());
115 
116  switch (input[0])
117  {
118  case 'n':
119  return decodeLiteral(input, "null"_sv, nullptr);
120  case 't':
121  return decodeLiteral(input, "true"_sv, true);
122  case 'f':
123  return decodeLiteral(input, "false"_sv, false);
124  case 'N':
125  return decodeLiteral(input, "NaN"_sv, static_cast<double>(NAN));
126  case 'I':
127  return decodeLiteral(input, "Infinity"_sv, static_cast<double>(INFINITY));
128  case '"':
129  return decodeString(input);
130  case '-':
131  if (input.size() > 1 && input[1] == 'I')
132  return decodeLiteral(input, "-Infinity"_sv, -static_cast<double>(INFINITY));
133  return decodeNumber(input);
134  default:
135  return decodeNumber(input);
136  }
137  }
138 
139 private:
140  template <typename T>
141  DecoderResult decodeLiteral(StringView input, StringView literal, T&& value);
142  DecoderResult decodeString(StringView input);
143  static bool decodeUnicodeEscape(
144  StringView input, StringView::const_iterator& inputIt, string<ALLOC>& value);
145  static char decodeHex(char character);
146  size_t checkNumber(StringView input, bool& isDouble, bool& isSigned);
147  DecoderResult decodeNumber(StringView input);
148  DecoderResult decodeSigned(StringView input);
149  DecoderResult decodeUnsigned(StringView input);
150  DecoderResult decodeDouble(StringView input, size_t numChars);
151 };
152 
153 template <typename ALLOC>
154 template <typename T>
155 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeLiteral(
156  StringView input, StringView literal, T&& value)
157 {
158  StringView::const_iterator literalIt = literal.begin();
159  StringView::const_iterator inputIt = input.begin();
160  while (inputIt != input.end() && literalIt != literal.end())
161  {
162  if (*inputIt++ != *literalIt++)
163  {
164  // failure, not decoded
165  return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
166  }
167  }
168 
169  if (literalIt != literal.end())
170  {
171  // short input, not decoded
172  return DecoderResult(input.size(), get_allocator());
173  }
174 
175  // success
176  return DecoderResult(literal.size(), std::forward<T>(value), get_allocator());
177 }
178 
179 template <typename ALLOC>
180 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeString(StringView input)
181 {
182  StringView::const_iterator inputIt = input.begin() + 1; // we know that at the beginning is '"'
183  string<ALLOC> value(get_allocator());
184 
185  while (inputIt != input.end())
186  {
187  if (*inputIt == '\\')
188  {
189  ++inputIt;
190  if (inputIt == input.end())
191  {
192  // wrong escape, not decoded
193  return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
194  }
195 
196  char nextChar = *inputIt;
197  switch (nextChar)
198  {
199  case '\\':
200  case '"':
201  value.push_back(nextChar);
202  ++inputIt;
203  break;
204  case 'b':
205  value.push_back('\b');
206  ++inputIt;
207  break;
208  case 'f':
209  value.push_back('\f');
210  ++inputIt;
211  break;
212  case 'n':
213  value.push_back('\n');
214  ++inputIt;
215  break;
216  case 'r':
217  value.push_back('\r');
218  ++inputIt;
219  break;
220  case 't':
221  value.push_back('\t');
222  ++inputIt;
223  break;
224  case 'u': // unicode escape
225  {
226  ++inputIt;
227  if (!decodeUnicodeEscape(input, inputIt, value))
228  {
229  // unsupported unicode escape, not decoded
230  return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
231  }
232  break;
233  }
234  default:
235  ++inputIt;
236  // unknown escape, not decoded
237  return DecoderResult(static_cast<size_t>(inputIt - input.begin()), get_allocator());
238  }
239  }
240  else if (*inputIt == '"')
241  {
242  ++inputIt;
243  // successfully decoded
244  return DecoderResult(
245  static_cast<size_t>(inputIt - input.begin()), std::move(value), get_allocator());
246  }
247  else
248  {
249  value.push_back(*inputIt++);
250  }
251  }
252 
253  // unterminated string, not decoded
254  return DecoderResult(input.size(), get_allocator());
255 }
256 
257 template <typename ALLOC>
258 bool BasicJsonDecoder<ALLOC>::decodeUnicodeEscape(
259  StringView input, StringView::const_iterator& inputIt, string<ALLOC>& value)
260 {
261  // TODO[Mi-L@]: Simplified just to decode what zserio encodes, for complex solution we could use
262  // std::wstring_convert but it's deprecated in C++17.
263  if (inputIt == input.end() || *inputIt++ != '0')
264  return false;
265  if (inputIt == input.end() || *inputIt++ != '0')
266  return false;
267 
268  if (inputIt == input.end())
269  return false;
270  const char char1 = decodeHex(*inputIt++);
271  if (char1 == -1)
272  return false;
273 
274  if (inputIt == input.end())
275  return false;
276  const char char2 = decodeHex(*inputIt++);
277  if (char2 == -1)
278  return false;
279 
280  value.push_back(static_cast<char>((static_cast<uint32_t>(char1) << 4U) | static_cast<uint32_t>(char2)));
281  return true;
282 }
283 
284 template <typename ALLOC>
285 char BasicJsonDecoder<ALLOC>::decodeHex(char character)
286 {
287  if (character >= '0' && character <= '9')
288  return static_cast<char>(character - '0');
289  else if (character >= 'a' && character <= 'f')
290  return static_cast<char>(character - 'a' + 10);
291  else if (character >= 'A' && character <= 'F')
292  return static_cast<char>(character - 'A' + 10);
293 
294  return -1;
295 }
296 
297 template <typename ALLOC>
298 size_t BasicJsonDecoder<ALLOC>::checkNumber(StringView input, bool& isDouble, bool& isSigned)
299 {
300  StringView::const_iterator inputIt = input.begin();
301  bool acceptExpSign = false;
302  bool isScientificDouble = false;
303  isDouble = false;
304 
305  if (*inputIt == '-') // we know that at the beginning is at least one character
306  {
307  ++inputIt;
308  isSigned = true;
309  }
310  else
311  {
312  isSigned = false;
313  }
314 
315  while (inputIt != input.end())
316  {
317  if (acceptExpSign)
318  {
319  acceptExpSign = false;
320  if (*inputIt == '+' || *inputIt == '-')
321  {
322  ++inputIt;
323  continue;
324  }
325  }
326 
327  if (*inputIt >= '0' && *inputIt <= '9')
328  {
329  ++inputIt;
330  continue;
331  }
332 
333  if ((*inputIt == 'e' || *inputIt == 'E') && !isScientificDouble)
334  {
335  isDouble = true;
336  isScientificDouble = true;
337  acceptExpSign = true;
338  ++inputIt;
339  continue;
340  }
341 
342  if (*inputIt == '.' && !isDouble)
343  {
344  isDouble = true;
345  ++inputIt;
346  continue;
347  }
348 
349  break; // end of a number
350  }
351 
352  const size_t numberLen = static_cast<size_t>(inputIt - input.begin());
353  if (isSigned && numberLen == 1)
354  return 0; // single minus is not a number
355 
356  return numberLen;
357 }
358 
359 template <typename ALLOC>
360 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeNumber(StringView input)
361 {
362  bool isDouble = false;
363  bool isSigned = false;
364  const size_t numChars = checkNumber(input, isDouble, isSigned);
365  if (numChars == 0)
366  return DecoderResult(1, get_allocator());
367 
368  // for decodeSigned and decodeUnsigned, we know that all numChars will be processed because checkNumber
369  // already checked this
370  if (isDouble)
371  return decodeDouble(input, numChars);
372  else if (isSigned)
373  return decodeSigned(input);
374  else
375  return decodeUnsigned(input);
376 }
377 
378 template <typename ALLOC>
379 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeSigned(StringView input)
380 {
381  char* pEnd = nullptr;
382  errno = 0; // no library function sets its value back to zero once changed
383  const int64_t value = std::strtoll(input.begin(), &pEnd, 10);
384 
385  const bool overflow = (errno == ERANGE);
386 
387  return DecoderResult(static_cast<size_t>(pEnd - input.begin()), value, overflow, get_allocator());
388 }
389 
390 template <typename ALLOC>
391 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeUnsigned(StringView input)
392 {
393  char* pEnd = nullptr;
394  errno = 0; // no library function sets its value back to zero once changed
395  const uint64_t value = std::strtoull(input.begin(), &pEnd, 10);
396 
397  const bool overflow = (errno == ERANGE);
398 
399  return DecoderResult(static_cast<size_t>(pEnd - input.begin()), value, overflow, get_allocator());
400 }
401 
402 template <typename ALLOC>
403 typename BasicJsonDecoder<ALLOC>::DecoderResult BasicJsonDecoder<ALLOC>::decodeDouble(
404  StringView input, size_t numChars)
405 {
406  char* pEnd = nullptr;
407  const double value = std::strtod(input.begin(), &pEnd);
408  if (static_cast<size_t>(pEnd - input.begin()) != numChars)
409  return DecoderResult(numChars, get_allocator());
410 
411  return DecoderResult(numChars, value, get_allocator());
412 }
413 
414 } // namespace zserio
415 
416 #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:936
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