Zserio C++17 runtime library  1.1.0
Built for Zserio 2.18.1
JsonTokenizer.h
Go to the documentation of this file.
1 #ifndef ZSERIO_JSON_TOKENIZER_H_INC
2 #define ZSERIO_JSON_TOKENIZER_H_INC
3 
4 #include <array>
5 #include <istream>
6 #include <memory>
7 #include <string_view>
8 
9 #include "zserio/Any.h"
11 #include "zserio/JsonDecoder.h"
12 #include "zserio/String.h"
13 
14 namespace zserio
15 {
16 
20 enum class JsonToken : int8_t
21 {
22  UNKNOWN = -1,
26  END_OBJECT,
28  END_ARRAY,
31  VALUE
32 };
33 
38 {
39 public:
41 
43 };
44 
54 
58 template <typename ALLOC = std::allocator<uint8_t>>
60 {
61 public:
62  using allocator_type = ALLOC;
63 
70  BasicJsonTokenizer(std::istream& in, const ALLOC& allocator) :
71  m_buffer(),
72  m_in(in),
73  m_decoder(allocator),
74  m_decoderResult(0, allocator),
75  m_content(readContent(allocator)),
76  m_value(allocator)
77  {
78  m_token = m_content.empty() ? JsonToken::END_OF_FILE : JsonToken::BEGIN_OF_FILE;
79  }
80 
88 
95  {
96  return m_token;
97  }
98 
107  const BasicAny<ALLOC>& getValue() const
108  {
109  return m_value;
110  }
111 
117  size_t getLine() const
118  {
119  return m_lineNumber;
120  }
121 
127  size_t getColumn() const
128  {
129  return m_tokenColumnNumber;
130  }
131 
132 private:
133  BasicString<RebindAlloc<ALLOC, char>> readContent(const ALLOC& allocator);
134 
135  bool decodeNext();
136  bool skipWhitespaces();
137 
138  template <typename T>
139  void setToken(JsonToken token, T&& value);
140  void setToken(JsonToken token, BasicAny<ALLOC>&& value);
141  void setToken(JsonToken token);
142  void setPosition(size_t newPos, size_t newColumnNumber);
143  void setTokenValue();
144 
145  static constexpr size_t BUFFER_SIZE = 64 * 1024;
146  std::array<char, BUFFER_SIZE> m_buffer;
147 
148  std::istream& m_in;
149  BasicJsonDecoder<ALLOC> m_decoder;
150  typename BasicJsonDecoder<ALLOC>::DecoderResult m_decoderResult;
152  size_t m_lineNumber = 1;
153  size_t m_columnNumber = 1;
154  size_t m_tokenColumnNumber = 1;
155  size_t m_pos = 0;
156  JsonToken m_token;
157  BasicAny<ALLOC> m_value;
158 };
159 
160 template <typename ALLOC>
162 {
163  while (!decodeNext())
164  {
165  BasicString<RebindAlloc<ALLOC, char>> newContent = readContent(m_content.get_allocator());
166  if (newContent.empty())
167  {
168  if (m_token == JsonToken::END_OF_FILE)
169  {
170  m_tokenColumnNumber = m_columnNumber;
171  }
172  else
173  {
174  // stream is finished but last token is not EOF => value must be at the end
175  setTokenValue();
176  }
177 
178  return m_token;
179  }
180 
181  m_content = m_content.substr(m_pos) + newContent;
182  m_pos = 0;
183  }
184 
185  return m_token;
186 }
187 
188 template <typename ALLOC>
190 {
191  const size_t count = static_cast<size_t>(m_in.rdbuf()->sgetn(m_buffer.data(), BUFFER_SIZE));
192 
193  return BasicString<RebindAlloc<ALLOC, char>>(m_buffer.data(), count, allocator);
194 }
195 
196 template <typename ALLOC>
197 bool BasicJsonTokenizer<ALLOC>::decodeNext()
198 {
199  if (!skipWhitespaces())
200  {
201  return false;
202  }
203 
204  m_tokenColumnNumber = m_columnNumber;
205 
206  const char nextChar = m_content[m_pos];
207  switch (nextChar)
208  {
209  case '{':
210  setToken(JsonToken::BEGIN_OBJECT, nextChar);
211  setPosition(m_pos + 1, m_columnNumber + 1);
212  break;
213  case '}':
214  setToken(JsonToken::END_OBJECT, nextChar);
215  setPosition(m_pos + 1, m_columnNumber + 1);
216  break;
217  case '[':
218  setToken(JsonToken::BEGIN_ARRAY, nextChar);
219  setPosition(m_pos + 1, m_columnNumber + 1);
220  break;
221  case ']':
222  setToken(JsonToken::END_ARRAY, nextChar);
223  setPosition(m_pos + 1, m_columnNumber + 1);
224  break;
225  case ':':
226  setToken(JsonToken::KEY_SEPARATOR, nextChar);
227  setPosition(m_pos + 1, m_columnNumber + 1);
228  break;
229  case ',':
230  setToken(JsonToken::ITEM_SEPARATOR, nextChar);
231  setPosition(m_pos + 1, m_columnNumber + 1);
232  break;
233  default:
234  m_decoderResult = m_decoder.decodeValue(std::string_view(m_content.data()).substr(m_pos));
235  if (m_pos + m_decoderResult.numReadChars >= m_content.size())
236  {
237  return false; // we are at the end of chunk => read more
238  }
239 
240  setTokenValue();
241  break;
242  }
243 
244  return true;
245 }
246 
247 template <typename ALLOC>
248 bool BasicJsonTokenizer<ALLOC>::skipWhitespaces()
249 {
250  while (true)
251  {
252  if (m_pos >= m_content.size())
253  {
254  setToken(JsonToken::END_OF_FILE);
255  return false;
256  }
257 
258  const char nextChar = m_content[m_pos];
259  switch (nextChar)
260  {
261  case ' ':
262  case '\t':
263  setPosition(m_pos + 1, m_columnNumber + 1);
264  break;
265  case '\n':
266  m_lineNumber++;
267  setPosition(m_pos + 1, 1);
268  break;
269  case '\r':
270  if (m_pos + 1 >= m_content.size())
271  {
272  setToken(JsonToken::END_OF_FILE);
273  return false;
274  }
275  m_lineNumber++;
276  setPosition(m_pos + (m_content[m_pos + 1] == '\n' ? 2 : 1), 1);
277  break;
278  default:
279  return true;
280  }
281  }
282 }
283 
284 template <typename ALLOC>
285 template <typename T>
286 void BasicJsonTokenizer<ALLOC>::setToken(JsonToken token, T&& value)
287 {
288  m_token = token;
289  m_value.set(std::forward<T>(value));
290 }
291 
292 template <typename ALLOC>
293 void BasicJsonTokenizer<ALLOC>::setToken(JsonToken token, BasicAny<ALLOC>&& value)
294 {
295  m_token = token;
296  m_value = std::move(value);
297 }
298 
299 template <typename ALLOC>
300 void BasicJsonTokenizer<ALLOC>::setToken(JsonToken token)
301 {
302  m_token = token;
303  m_value.reset();
304 }
305 
306 template <typename ALLOC>
307 void BasicJsonTokenizer<ALLOC>::setPosition(size_t newPos, size_t newColumnNumber)
308 {
309  m_pos = newPos;
310  m_columnNumber = newColumnNumber;
311 }
312 
313 template <typename ALLOC>
314 void BasicJsonTokenizer<ALLOC>::setTokenValue()
315 {
316  if (!m_decoderResult.value.hasValue())
317  {
318  throw JsonParserException("JsonTokenizer:")
319  << m_lineNumber << ":" << m_tokenColumnNumber << ": "
320  << (m_decoderResult.integerOverflow ? "Value is outside of the 64-bit integer range!"
321  : "Unknown token!");
322  }
323 
324  setToken(JsonToken::VALUE, std::move(m_decoderResult.value));
325  setPosition(m_pos + m_decoderResult.numReadChars, m_columnNumber + m_decoderResult.numReadChars);
326 }
327 
328 } // namespace zserio
329 
330 #endif // ZSERIO_JSON_TOKENIZER_H_INC
const BasicAny< ALLOC > & getValue() const
JsonToken getToken() const
Definition: JsonTokenizer.h:94
BasicJsonTokenizer(std::istream &in, const ALLOC &allocator)
Definition: JsonTokenizer.h:70
CppRuntimeException(const char *message="")
std::basic_string< char, std::char_traits< char >, ALLOC > BasicString
Definition: String.h:17
CppRuntimeException & operator<<(CppRuntimeException &exception, const BasicBitBuffer< ALLOC > &bitBuffer)
Definition: BitBuffer.h:553