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