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