CppWAMP
C++11 client library for the WAMP protocol
jsonencoding.hpp
1 /*------------------------------------------------------------------------------
2  Copyright Butterfly Energy Systems 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_JSONENCODING_HPP
8 #define CPPWAMP_INTERNAL_JSONENCODING_HPP
9 
10 #include <jsoncons/json_encoder.hpp>
11 #include "../blob.hpp"
12 #include "../null.hpp"
13 #include "../variantdefs.hpp"
14 #include "base64.hpp"
15 
16 namespace wamp
17 {
18 
19 namespace internal
20 {
21 
22 //------------------------------------------------------------------------------
23 template <typename TEncoder>
24 class JsonVariantEncodingVisitor
25 {
26 public:
27  using Sink = typename TEncoder::sink_type;
28  using Result = void;
29 
30  explicit JsonVariantEncodingVisitor(Sink sink, TEncoder& encoder)
31  : sink_(sink),
32  encoder_(encoder)
33  {
34  enter();
35  }
36 
37  void operator()(Null)
38  {
39  encoder_.null_value();
40  next();
41  }
42 
43  void operator()(Bool b)
44  {
45  encoder_.bool_value(b);
46  next();
47  }
48 
49  void operator()(Int n)
50  {
51  encoder_.int64_value(n);
52  next();
53  }
54 
55  void operator()(UInt n)
56  {
57  encoder_.uint64_value(n);
58  next();
59  }
60 
61  void operator()(Real x)
62  {
63  encoder_.double_value(x);
64  next();
65  }
66 
67  void operator()(const String& s)
68  {
69  encoder_.string_value({s.data(), s.size()});
70  next();
71  }
72 
73  void operator()(const Blob& b)
74  {
75  static constexpr char prefix[] = "\"\\u0000";
76  if (context_.back().needsArraySeparator())
77  sink_.push_back(',');
78  auto data = reinterpret_cast<const typename Sink::value_type*>(prefix);
79  sink_.append(data, sizeof(prefix) - 1);
80  Base64::encode(b.data().data(), b.data().size(), sink_);
81  sink_.push_back('\"');
82  next();
83  }
84 
85  template <typename TVariant>
86  void operator()(const std::vector<TVariant>& array)
87  {
88  enter(true);
89  encoder_.begin_array(array.size());
90  for (const auto& v: array)
91  wamp::apply(*this, v);
92  encoder_.end_array();
93  leave();
94  next();
95  }
96 
97  template <typename TVariant>
98  void operator()(const std::map<String, TVariant>& object)
99  {
100  enter();
101  encoder_.begin_object(object.size());
102  for (const auto& kv: object)
103  {
104  const auto& key = kv.first;
105  encoder_.key({key.data(), key.size()});
106  wamp::apply(*this, kv.second);
107  }
108  encoder_.end_object();
109  leave();
110  next();
111  }
112 
113 private:
114  struct Context
115  {
116  Context(bool isArray = false) : isArray(isArray), isPopulated(false) {}
117 
118  bool needsArraySeparator() const {return isArray && isPopulated;}
119 
120  void populate() {isPopulated = true;}
121 
122  bool isArray : 1;
123  bool isPopulated : 1;
124  };
125 
126  void next() {context_.back().populate();}
127 
128  void enter(bool isArray = false) {context_.push_back(isArray);}
129 
130  void leave()
131  {
132  assert(!context_.empty());
133  context_.pop_back();
134  }
135 
136  Sink sink_;
137  TEncoder& encoder_;
138  std::vector<Context> context_;
139 };
140 
141 //------------------------------------------------------------------------------
142 template <typename TSink>
143 class JsonSinkProxy
144 {
145 public:
146  using value_type = char;
147 
148  JsonSinkProxy() = default;
149 
150  JsonSinkProxy(TSink& out) : sink_(&out) {}
151 
152  void append(const value_type* data, size_t size)
153  {
154  auto ptr = reinterpret_cast<const SinkByte*>(data);
155  sink_->append(ptr, size);
156  }
157 
158  void push_back(value_type byte)
159  {
160  // Emulate std::bit_cast
161  SinkByte value;
162  std::memcpy(&value, &byte, sizeof(byte));
163  sink_->push_back(value);
164  }
165 
166  void flush() {}
167 
168 private:
169  using SinkByte = typename TSink::value_type;
170 
171  TSink* sink_ = nullptr;
172 };
173 
174 //------------------------------------------------------------------------------
175 // This implementation needs to be taken out of the json.ipp module to
176 // avoid a circular dependency with the variant.ipp module.
177 //------------------------------------------------------------------------------
178 template <typename TSink>
179 class JsonEncoderImpl
180 {
181 public:
182  JsonEncoderImpl() : encoder_(Proxy{}) {}
183 
184  template <typename TVariant, typename TOutput>
185  void encode(const TVariant& variant, TOutput& output)
186  {
187  // JsonSinkProxy is needed because shared access to the underlying sink
188  // by JsonVariantEncodingVisitor is required for WAMP's special
189  // handling of Base64-encoded blobs.
190  TSink sink(output);
191  Proxy proxy(sink);
192  encoder_.reset(std::move(proxy));
193  internal::JsonVariantEncodingVisitor<Encoder> visitor(proxy, encoder_);
194  wamp::apply(visitor, variant);
195  }
196 
197 private:
198  using Proxy = internal::JsonSinkProxy<TSink>;
199  using Encoder = jsoncons::basic_compact_json_encoder<char, Proxy>;
200  Encoder encoder_;
201 };
202 
203 } // namespace internal
204 
205 } // namespace wamp
206 
207 #endif // CPPWAMP_INTERNAL_JSONENCODING_HPP
wamp::UInt
std::uint64_t UInt
Variant bound type for unsigned integers.
Definition: variantdefs.hpp:48
wamp::TypeId::array
@ array
For Variant::Array.
wamp::encode
void encode(const Variant &variant, TOutput &&output)
Encodes the given variant to the given byte container or stream.
Definition: codec.hpp:103
wamp
Definition: anyhandler.hpp:36
wamp::Int
std::int64_t Int
Variant bound type for signed integers.
Definition: variantdefs.hpp:47
wamp::Bool
bool Bool
Variant bound type for boolean values.
Definition: variantdefs.hpp:46
wamp::Real
double Real
Variant bound type for floating-point numbers.
Definition: variantdefs.hpp:49
wamp::Encoder
typename F::template Encoder< ValueTypeOf< O >, C > Encoder
Yields the encoder type needed to encode a Variant to the given output type and output category.
Definition: codec.hpp:91
wamp::String
std::string String
Variant bound type for text strings.
Definition: variantdefs.hpp:50