CppWAMP
C++11 client library for the WAMP protocol
variantdecoding.hpp
1 /*------------------------------------------------------------------------------
2  Copyright Butterfly Energy Systems 2014-2015, 2022.
3  Distributed under the Boost Software License, Version 1.0.
4  http://www.boost.org/LICENSE_1_0.txt
5 ------------------------------------------------------------------------------*/
6 
7 #ifndef CPPWAMP_INTERNAL_VARIANTDECODING_HPP
8 #define CPPWAMP_INTERNAL_VARIANTDECODING_HPP
9 
10 #include <cassert>
11 #include <cstdint>
12 #include <limits>
13 #include <utility>
14 #include <vector>
15 #include <jsoncons/byte_string.hpp>
16 #include <jsoncons/config/version.hpp>
17 #include <jsoncons/json_visitor.hpp>
18 #include <jsoncons/json_visitor2.hpp>
19 #include <jsoncons/ser_context.hpp>
20 #include <jsoncons/source.hpp>
21 #include <jsoncons/tag_type.hpp>
22 #include "../codec.hpp"
23 #include "../variant.hpp"
24 #include "base64.hpp"
25 
26 #define CPPWAMP_JSONCONS_VERSION \
27  (JSONCONS_VERSION_MAJOR * 10000000u + \
28  JSONCONS_VERSION_MINOR * 1000u + \
29  JSONCONS_VERSION_PATCH)
30 
31 #define CPPWAMP_JSONCONS_CURSOR_PROVIDES_SIZE (CPPWAMP_JSONCONS_VERSION > 168007)
32 
33 namespace wamp
34 {
35 
36 namespace internal
37 {
38 
39 //------------------------------------------------------------------------------
40 template <typename TVisitor>
41 class VariantDecodingVisitorBase : public TVisitor
42 {
43 public:
44  using string_view_type = typename TVisitor::string_view_type;
45 
46  void reset()
47  {
48  contextStack_.clear();
49  key_.clear();
50  variant_ = Variant();
51  hasRoot_ = false;
52  }
53 
54  const Variant& variant() const & {return variant_;}
55 
56  Variant&& variant() && {return std::move(variant_);}
57 
58  bool empty() const {return !hasRoot_;}
59 
60 protected:
61  using Tag = jsoncons::semantic_tag;
62  using Where = jsoncons::ser_context;
63 
64  template <typename T>
65  std::error_code put(T&& value, const Where& where, bool isComposite = false)
66  {
67  if (contextStack_.empty())
68  return addRoot(std::forward<T>(value), isComposite);
69 
70  switch (context().variant().typeId())
71  {
72  case TypeId::array:
73  return addArrayElement(std::forward<T>(value), isComposite);
74  case TypeId::object:
75  return addObjectElement(std::forward<T>(value), isComposite, where);
76  default:
77  assert(false);
78  break;
79  }
80 
81  return {};
82  }
83 
84  void putKey(String&& key)
85  {
86  key_ = std::move(key);
87  context().setKeyIsDone();
88  }
89 
90  void putStringOrKey(String&& str, const Where& where)
91  {
92  if (!contextStack_.empty() && context().expectsKey())
93  putKey(std::move(str));
94  else
95  put(std::move(str), where);
96  }
97 
98  void endComposite()
99  {
100  assert(!contextStack_.empty());
101  contextStack_.pop_back();
102  }
103 
104 private:
105  using Base = TVisitor;
106  using ByteStringView = jsoncons::byte_string_view;
107 
108  class Context
109  {
110  public:
111  Context() = default;
112 
113  Context(Variant& v)
114  : variant_(&v)
115  {}
116 
117  Variant& variant() {return *variant_;}
118 
119  bool expectsKey() const
120  {
121  return variant_->is<Object>() && !keyIsDone_;
122  }
123 
124  bool keyIsDone() const {return keyIsDone_;}
125 
126  void setKeyIsDone(bool done = true) {keyIsDone_ = done;}
127 
128  private:
129  Variant* variant_ = nullptr;
130  bool keyIsDone_ = false;
131  };
132 
133  static std::string makeErrorMessage(std::string what, const Where& where)
134  {
135  what += " at column ";
136  what += std::to_string(where.column());
137  what += ", line ";
138  what += std::to_string(where.line());
139  what += ", position ";
140  what += std::to_string(where.position());
141  return what;
142  }
143 
144  template <typename T>
145  std::error_code putInteger(T integer, const Where& where)
146  {
147  return put(static_cast<Variant::Int>(integer), where);
148  }
149 
150  template <typename T>
151  std::error_code addRoot(T&& value, bool isComposite)
152  {
153  variant_ = std::forward<T>(value);
154  hasRoot_ = true;
155  if (isComposite)
156  contextStack_.push_back(variant_);
157  return {};
158  }
159 
160  template <typename T>
161  std::error_code addArrayElement(T&& value, bool isComposite)
162  {
163  auto& array = context().variant().template as<TypeId::array>();
164  array.push_back(std::forward<T>(value));
165  if (isComposite)
166  contextStack_.push_back(array.back());
167  return {};
168  }
169 
170  template <typename T>
171  std::error_code addObjectElement(T&& value, bool isComposite,
172  const Where& where)
173  {
174  auto& ctx = context();
175  if (!ctx.keyIsDone())
176  return make_error_code(DecodingErrc::expectedStringKey);
177 
178  Variant* newElement = nullptr;
179  auto& object = ctx.variant().template as<TypeId::object>();
180  auto found = object.find(key_);
181  if (found == object.end())
182  {
183  auto result = object.emplace(std::move(key_),
184  std::forward<T>(value));
185  newElement = &(result.first->second);
186  }
187  else
188  {
189  found->second = std::forward<T>(value);
190  newElement = &(found->second);
191  }
192  ctx.setKeyIsDone(false);
193  if (isComposite)
194  contextStack_.push_back(*newElement);
195 
196  return {};
197  }
198 
199  Context& context()
200  {
201  assert(!contextStack_.empty());
202  return contextStack_.back();
203  }
204 
205 
206  // visit overrides common to jsoncons::json_visitor
207  // and jsoncons::json_visitor2
208 
209  void visit_flush() override {}
210 
211  bool visit_begin_object(Tag, const Where& where, std::error_code&) override
212  {
213  put(Object{}, where, true);
214  return true;
215  }
216 
217  bool visit_end_object(const Where&, std::error_code&) override
218  {
219  endComposite();
220  return true;
221  }
222 
223  bool visit_begin_array(Tag, const Where& where, std::error_code&) override
224  {
225  put(Array{}, where, true);
226  return true;
227  }
228 
229  bool visit_begin_array(std::size_t length, Tag, const Where& where,
230  std::error_code&) override
231  {
232  Array a;
233  if (length > 0)
234  a.reserve(length);
235  put(std::move(a), where, true);
236  return true;
237  }
238 
239  bool visit_end_array(const Where&, std::error_code&) override
240  {
241  endComposite();
242  return true;
243  }
244 
245  bool visit_null(Tag, const Where& where, std::error_code& ec) override
246  {
247  ec = put(null, where);
248  return !ec;
249  }
250 
251  bool visit_bool(bool value, Tag, const Where& where,
252  std::error_code& ec) override
253  {
254  ec = put(value, where);
255  return !ec;
256  }
257 
258  bool visit_byte_string(const ByteStringView& bsv, Tag, const Where& where,
259  std::error_code& ec) override
260  {
261  ec = put(Blob(Blob::Data(bsv.begin(), bsv.end())), where);
262  return !ec;
263  }
264 
265  bool visit_uint64(uint64_t n, Tag, const Where& where,
266  std::error_code& ec) override
267  {
268  if (n <= std::numeric_limits<Variant::Int>::max())
269  ec = putInteger(n, where);
270  else
271  ec = put(n, where);
272  return !ec;
273  }
274 
275  bool visit_int64(int64_t n, Tag, const Where& where,
276  std::error_code& ec) override
277  {
278  ec = putInteger(n, where);
279  return !ec;
280  }
281 
282  bool visit_double(double x, Tag, const Where& where,
283  std::error_code& ec) override
284  {
285  ec = put(x, where);
286  return !ec;
287  }
288 
289  std::vector<Context> contextStack_;
290  String key_;
291  Variant variant_;
292  bool hasRoot_ = false;
293 };
294 
295 //------------------------------------------------------------------------------
296 class VariantJsonDecodingVisitor :
297  public VariantDecodingVisitorBase<jsoncons::json_visitor>
298 {
299 private:
300  bool visit_key(const string_view_type& name, const Where&,
301  std::error_code&) override
302  {
303  putKey(String(name.data(), name.size()));
304  return true;
305  }
306 
307  bool visit_string(const string_view_type& sv, Tag, const Where& where,
308  std::error_code& ec) override
309  {
310  if ( (sv.size() > 0) && (sv[0] == '\0') )
311  {
312  Blob::Data bytes;
313  ec = Base64::decode(sv.data() + 1, sv.size() - 1, bytes);
314  if (!ec)
315  put(Blob(std::move(bytes)), where);
316  }
317  else
318  put(String(sv.data(), sv.size()), where);
319  return true;
320  }
321 };
322 
323 //------------------------------------------------------------------------------
324 class VariantDecodingVisitor :
325  public VariantDecodingVisitorBase<jsoncons::json_visitor2>
326 {
327 private:
328  bool visit_string(const string_view_type& sv, Tag, const Where& where,
329  std::error_code&) override
330  {
331  putStringOrKey(String(sv.data(), sv.size()), where);
332  return true;
333  }
334 };
335 
336 //------------------------------------------------------------------------------
337 template <typename TConfig>
338 class GenericDecoder
339 {
340 public:
341  template <typename... TArgs>
342  explicit GenericDecoder(std::string codecName, TArgs&&... inputStubArgs)
343  : inputStub_(std::forward<TArgs>(inputStubArgs)...),
344  parser_(inputStub_),
345  codecName_(std::move(codecName))
346  {}
347 
348  template <typename TSourceable>
349  std::error_code decode(TSourceable&& input, Variant& variant)
350  {
351  Source source(std::forward<TSourceable>(input));
352  parser_.reset(std::move(source));
353  visitor_.reset();
354  std::error_code ec;
355  parser_.parse(visitor_, ec);
356  if (!ec)
357  variant = std::move(visitor_).variant();
358  reset();
359  return ec;
360  }
361 
362 private:
363  using Input = typename TConfig::Input;
364  using Source = typename TConfig::Source;
365  using Parser = typename TConfig::Parser;
366  using Visitor = internal::VariantDecodingVisitor;
367 
368  void reset()
369  {
370  parser_.reset();
371  visitor_.reset();
372  }
373 
374  Input inputStub_;
375  Parser parser_;
376  Visitor visitor_;
377  std::string codecName_;
378 };
379 
380 } // namespace internal
381 
382 } // namespace wamp
383 
384 #endif // CPPWAMP_INTERNAL_VARIANTDECODING_HPP
wamp::TypeId::array
@ array
For Variant::Array.
wamp::Object
std::map< String, Variant > Object
Variant bound type for maps of variants.
Definition: variantdefs.hpp:52
wamp
Definition: anyhandler.hpp:36
wamp::Array
std::vector< Variant > Array
Variant bound type for arrays of variants.
Definition: variantdefs.hpp:51
wamp::Blob::Data
std::vector< uint8_t > Data
Array of bytes used to contain the binary data.
Definition: blob.hpp:32
wamp::decode
std::error_code decode(TInput &&input, Variant &variant)
Decodes from the given byte sequence or stream to the given variant.
Definition: codec.hpp:172
wamp::DecodingErrc::expectedStringKey
@ expectedStringKey
Expected a string key.
wamp::TypeId::object
@ object
For Variant::Object.
wamp::Variant::Int
wamp::Int Int
Signed integer type.
Definition: variant.hpp:177
wamp::String
std::string String
Variant bound type for text strings.
Definition: variantdefs.hpp:50