1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4 
5 #include "caf/json_writer.hpp"
6 
7 #include "caf/detail/append_hex.hpp"
8 #include "caf/detail/print.hpp"
9 
10 namespace caf {
11 
12 namespace {
13 
14 static constexpr const char class_name[] = "caf::json_writer";
15 
can_morph(json_writer::type from,json_writer::type to)16 constexpr bool can_morph(json_writer::type from, json_writer::type to) {
17   return from == json_writer::type::element && to != json_writer::type::member;
18 }
19 
20 constexpr const char* json_type_names[] = {"element", "object", "member",
21                                            "array",   "string", "number",
22                                            "bool",    "null"};
23 
json_type_name(json_writer::type t)24 constexpr const char* json_type_name(json_writer::type t) {
25   return json_type_names[static_cast<uint8_t>(t)];
26 }
27 
28 } // namespace
29 
30 // -- implementation details ---------------------------------------------------
31 
32 template <class T>
number(T x)33 bool json_writer::number(T x) {
34   switch (top()) {
35     case type::element:
36       detail::print(buf_, x);
37       pop();
38       return true;
39     case type::key:
40       add('"');
41       detail::print(buf_, x);
42       add("\": ");
43       return true;
44     case type::array:
45       sep();
46       detail::print(buf_, x);
47       return true;
48     default:
49       fail(type::number);
50       return false;
51   }
52 }
53 
54 // -- constructors, destructors, and assignment operators ----------------------
55 
json_writer()56 json_writer::json_writer() : json_writer(nullptr) {
57   init();
58 }
59 
json_writer(actor_system & sys)60 json_writer::json_writer(actor_system& sys) : super(sys) {
61   init();
62 }
63 
json_writer(execution_unit * ctx)64 json_writer::json_writer(execution_unit* ctx) : super(ctx) {
65   init();
66 }
67 
~json_writer()68 json_writer::~json_writer() {
69   // nop
70 }
71 
72 // -- modifiers ----------------------------------------------------------------
73 
reset()74 void json_writer::reset() {
75   buf_.clear();
76   stack_.clear();
77   push();
78 }
79 
80 // -- overrides ----------------------------------------------------------------
81 
begin_object(type_id_t id,string_view name)82 bool json_writer::begin_object(type_id_t id, string_view name) {
83   auto add_type_annotation = [this, id, name] {
84     CAF_ASSERT(top() == type::key);
85     add(R"_("@type": )_");
86     pop();
87     CAF_ASSERT(top() == type::element);
88     if (auto tname = query_type_name(id); !tname.empty()) {
89       add('"');
90       add(tname);
91       add('"');
92     } else {
93       add('"');
94       add(name);
95       add('"');
96     }
97     pop();
98     return true;
99   };
100   if (inside_object())
101     return begin_associative_array(0);
102   else
103     return begin_associative_array(0) // Put opening paren, ...
104            && begin_key_value_pair()  // ... add implicit @type member, ..
105            && add_type_annotation()   // ... write content ...
106            && end_key_value_pair();   // ... and wait for next field.
107 }
108 
end_object()109 bool json_writer::end_object() {
110   return end_associative_array();
111 }
112 
begin_field(string_view name)113 bool json_writer::begin_field(string_view name) {
114   if (begin_key_value_pair()) {
115     CAF_ASSERT(top() == type::key);
116     add('"');
117     add(name);
118     add("\": ");
119     pop();
120     CAF_ASSERT(top() == type::element);
121     return true;
122   } else {
123     return false;
124   }
125 }
126 
begin_field(string_view name,bool is_present)127 bool json_writer::begin_field(string_view name, bool is_present) {
128   if (skip_empty_fields_ && !is_present) {
129     auto t = top();
130     switch (t) {
131       case type::object:
132         push(type::member);
133         return true;
134       default: {
135         std::string str = "expected object, found ";
136         str += json_type_name(t);
137         emplace_error(sec::runtime_error, class_name, __func__, std::move(str));
138         return false;
139       }
140     }
141   } else if (begin_key_value_pair()) {
142     CAF_ASSERT(top() == type::key);
143     add('"');
144     add(name);
145     add("\": ");
146     pop();
147     CAF_ASSERT(top() == type::element);
148     if (!is_present) {
149       add("null");
150       pop();
151     }
152     return true;
153   } else {
154     return false;
155   }
156 }
157 
begin_field(string_view name,span<const type_id_t> types,size_t index)158 bool json_writer::begin_field(string_view name, span<const type_id_t> types,
159                               size_t index) {
160   if (index >= types.size()) {
161     emplace_error(sec::runtime_error, "index >= types.size()");
162     return false;
163   }
164   if (begin_key_value_pair()) {
165     CAF_ASSERT(top() == type::key);
166     add("\"@");
167     add(name);
168     add(field_type_suffix_);
169     add("\": ");
170     pop();
171     CAF_ASSERT(top() == type::element);
172     pop();
173     if (auto tname = query_type_name(types[index]); !tname.empty()) {
174       add('"');
175       add(tname);
176       add('"');
177     } else {
178       emplace_error(sec::runtime_error, "query_type_name failed");
179       return false;
180     }
181     return end_key_value_pair() && begin_field(name);
182   } else {
183     return false;
184   }
185 }
186 
begin_field(string_view name,bool is_present,span<const type_id_t> types,size_t index)187 bool json_writer::begin_field(string_view name, bool is_present,
188                               span<const type_id_t> types, size_t index) {
189   if (is_present)
190     return begin_field(name, types, index);
191   else
192     return begin_field(name, is_present);
193 }
194 
end_field()195 bool json_writer::end_field() {
196   return end_key_value_pair();
197 }
198 
begin_tuple(size_t size)199 bool json_writer::begin_tuple(size_t size) {
200   return begin_sequence(size);
201 }
202 
end_tuple()203 bool json_writer::end_tuple() {
204   return end_sequence();
205 }
206 
begin_key_value_pair()207 bool json_writer::begin_key_value_pair() {
208   sep();
209   auto t = top();
210   switch (t) {
211     case type::object:
212       push(type::member);
213       push(type::element);
214       push(type::key);
215       return true;
216     default: {
217       std::string str = "expected object, found ";
218       str += json_type_name(t);
219       emplace_error(sec::runtime_error, class_name, __func__, std::move(str));
220       return false;
221     }
222   }
223 }
224 
end_key_value_pair()225 bool json_writer::end_key_value_pair() {
226   return pop_if(type::member);
227 }
228 
begin_sequence(size_t)229 bool json_writer::begin_sequence(size_t) {
230   switch (top()) {
231     default:
232       emplace_error(sec::runtime_error, "unexpected begin_sequence");
233       return false;
234     case type::element:
235       unsafe_morph(type::array);
236       break;
237     case type::array:
238       push(type::array);
239       break;
240   }
241   add('[');
242   ++indentation_level_;
243   nl();
244   return true;
245 }
246 
end_sequence()247 bool json_writer::end_sequence() {
248   if (pop_if(type::array)) {
249     --indentation_level_;
250     nl();
251     add(']');
252     return true;
253   } else {
254     return false;
255   }
256 }
257 
begin_associative_array(size_t)258 bool json_writer::begin_associative_array(size_t) {
259   switch (top()) {
260     default:
261       emplace_error(sec::runtime_error, class_name, __func__,
262                     "unexpected begin_object or begin_associative_array");
263       return false;
264     case type::element:
265       unsafe_morph(type::object);
266       break;
267     case type::array:
268       sep();
269       push(type::object);
270       break;
271   }
272   add('{');
273   ++indentation_level_;
274   nl();
275   return true;
276 }
277 
end_associative_array()278 bool json_writer::end_associative_array() {
279   if (pop_if(type::object)) {
280     --indentation_level_;
281     nl();
282     add('}');
283     if (!stack_.empty())
284       stack_.back().filled = true;
285     return true;
286   } else {
287     return false;
288   }
289 }
290 
value(byte x)291 bool json_writer::value(byte x) {
292   return number(to_integer<uint8_t>(x));
293 }
294 
value(bool x)295 bool json_writer::value(bool x) {
296   auto add_str = [this, x] {
297     if (x)
298       add("true");
299     else
300       add("false");
301   };
302   switch (top()) {
303     case type::element:
304       add_str();
305       pop();
306       return true;
307     case type::key:
308       add('"');
309       add_str();
310       add("\": ");
311       return true;
312     case type::array:
313       sep();
314       add_str();
315       return true;
316     default:
317       fail(type::boolean);
318       return false;
319   }
320 }
321 
value(int8_t x)322 bool json_writer::value(int8_t x) {
323   return number(x);
324 }
325 
value(uint8_t x)326 bool json_writer::value(uint8_t x) {
327   return number(x);
328 }
329 
value(int16_t x)330 bool json_writer::value(int16_t x) {
331   return number(x);
332 }
333 
value(uint16_t x)334 bool json_writer::value(uint16_t x) {
335   return number(x);
336 }
337 
value(int32_t x)338 bool json_writer::value(int32_t x) {
339   return number(x);
340 }
341 
value(uint32_t x)342 bool json_writer::value(uint32_t x) {
343   return number(x);
344 }
345 
value(int64_t x)346 bool json_writer::value(int64_t x) {
347   return number(x);
348 }
349 
value(uint64_t x)350 bool json_writer::value(uint64_t x) {
351   return number(x);
352 }
353 
value(float x)354 bool json_writer::value(float x) {
355   return number(x);
356 }
357 
value(double x)358 bool json_writer::value(double x) {
359   return number(x);
360 }
361 
value(long double x)362 bool json_writer::value(long double x) {
363   return number(x);
364 }
365 
value(string_view x)366 bool json_writer::value(string_view x) {
367   switch (top()) {
368     case type::element:
369       detail::print_escaped(buf_, x);
370       pop();
371       return true;
372     case type::key:
373       detail::print_escaped(buf_, x);
374       add(": ");
375       pop();
376       return true;
377     case type::array:
378       sep();
379       detail::print_escaped(buf_, x);
380       return true;
381     default:
382       fail(type::string);
383       return false;
384   }
385 }
386 
value(const std::u16string &)387 bool json_writer::value(const std::u16string&) {
388   emplace_error(sec::unsupported_operation,
389                 "u16string not supported yet by caf::json_writer");
390   return false;
391 }
392 
value(const std::u32string &)393 bool json_writer::value(const std::u32string&) {
394   emplace_error(sec::unsupported_operation,
395                 "u32string not supported yet by caf::json_writer");
396   return false;
397 }
398 
value(span<const byte> x)399 bool json_writer::value(span<const byte> x) {
400   switch (top()) {
401     case type::element:
402       add('"');
403       detail::append_hex(buf_, reinterpret_cast<const void*>(x.data()),
404                          x.size());
405       add('"');
406       pop();
407       return true;
408     case type::key:
409       unsafe_morph(type::element);
410       add('"');
411       detail::append_hex(buf_, reinterpret_cast<const void*>(x.data()),
412                          x.size());
413       add("\": ");
414       pop();
415       return true;
416     case type::array:
417       sep();
418       add('"');
419       detail::append_hex(buf_, reinterpret_cast<const void*>(x.data()),
420                          x.size());
421       add('"');
422       return true;
423     default:
424       fail(type::string);
425       return false;
426   }
427 }
428 
429 // -- state management ---------------------------------------------------------
430 
init()431 void json_writer::init() {
432   has_human_readable_format_ = true;
433   // Reserve some reasonable storage for the character buffer. JSON grows
434   // quickly, so we can start at 1kb to avoid a couple of small allocations in
435   // the beginning.
436   buf_.reserve(1024);
437   // Even heavily nested objects should fit into 32 levels of nesting.
438   stack_.reserve(32);
439   // Placeholder for what is to come.
440   push();
441 }
442 
top()443 json_writer::type json_writer::top() {
444   if (!stack_.empty())
445     return stack_.back().t;
446   else
447     return type::null;
448 }
449 
450 // Enters a new level of nesting.
push(type t)451 void json_writer::push(type t) {
452   stack_.push_back({t, false});
453 }
454 
455 // Backs up one level of nesting.
pop()456 bool json_writer::pop() {
457   if (!stack_.empty()) {
458     stack_.pop_back();
459     return true;
460   } else {
461     std::string str = "pop() called with an empty stack: begin/end mismatch";
462     emplace_error(sec::runtime_error, std::move(str));
463     return false;
464   }
465 }
466 
pop_if(type t)467 bool json_writer::pop_if(type t) {
468   if (!stack_.empty() && stack_.back() == t) {
469     stack_.pop_back();
470     return true;
471   } else {
472     std::string str = "pop_if failed: expected ";
473     str += json_type_name(t);
474     if (stack_.empty()) {
475       str += ", found an empty stack";
476     } else {
477       str += ", found ";
478       str += json_type_name(stack_.back().t);
479     }
480     emplace_error(sec::runtime_error, std::move(str));
481     return false;
482   }
483 }
484 
pop_if_next(type t)485 bool json_writer::pop_if_next(type t) {
486   if (stack_.size() > 1
487       && (stack_[stack_.size() - 2] == t
488           || can_morph(stack_[stack_.size() - 2].t, t))) {
489     stack_.pop_back();
490     return true;
491   } else {
492     std::string str = "pop_if_next failed: expected ";
493     str += json_type_name(t);
494     if (stack_.size() < 2) {
495       str += ", found a stack of size ";
496       detail::print(str, stack_.size());
497     } else {
498       str += ", found ";
499       str += json_type_name(stack_[stack_.size() - 2].t);
500     }
501     emplace_error(sec::runtime_error, std::move(str));
502     return false;
503   }
504 }
505 
506 // Tries to morph the current top of the stack to t.
morph(type t)507 bool json_writer::morph(type t) {
508   type unused;
509   return morph(t, unused);
510 }
511 
morph(type t,type & prev)512 bool json_writer::morph(type t, type& prev) {
513   if (!stack_.empty()) {
514     if (can_morph(stack_.back().t, t)) {
515       prev = stack_.back().t;
516       stack_.back().t = t;
517       return true;
518     } else {
519       std::string str = "cannot convert ";
520       str += json_type_name(stack_.back().t);
521       str += " to ";
522       str += json_type_name(t);
523       emplace_error(sec::runtime_error, std::move(str));
524       return false;
525     }
526   } else {
527     std::string str = "mismatched begin/end calls on the JSON inspector";
528     emplace_error(sec::runtime_error, std::move(str));
529     return false;
530   }
531 }
532 
unsafe_morph(type t)533 void json_writer::unsafe_morph(type t) {
534   stack_.back().t = t;
535 }
536 
fail(type t)537 void json_writer::fail(type t) {
538   std::string str = "failed to write a ";
539   str += json_type_name(t);
540   str += ": invalid position (begin/end mismatch?)";
541   emplace_error(sec::runtime_error, std::move(str));
542 }
543 
inside_object() const544 bool json_writer::inside_object() const noexcept {
545   auto is_object = [](const entry& x) { return x.t == type::object; };
546   return std::any_of(stack_.begin(), stack_.end(), is_object);
547 }
548 
549 // -- printing ---------------------------------------------------------------
550 
nl()551 void json_writer::nl() {
552   if (indentation_factor_ > 0) {
553     buf_.push_back('\n');
554     buf_.insert(buf_.end(), indentation_factor_ * indentation_level_, ' ');
555   }
556 }
557 
sep()558 void json_writer::sep() {
559   CAF_ASSERT(top() == type::element || top() == type::object
560              || top() == type::array);
561   if (stack_.back().filled) {
562     if (indentation_factor_ > 0) {
563       add(",\n");
564       buf_.insert(buf_.end(), indentation_factor_ * indentation_level_, ' ');
565     } else {
566       add(", ");
567     }
568   } else {
569     stack_.back().filled = true;
570   }
571 }
572 
573 } // namespace caf
574