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