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_reader.hpp"
6
7 #include "caf/detail/bounds_checker.hpp"
8 #include "caf/detail/print.hpp"
9 #include "caf/string_algorithms.hpp"
10
11 namespace {
12
13 static constexpr const char class_name[] = "caf::json_reader";
14
pretty_name(caf::json_reader::position pos)15 caf::string_view pretty_name(caf::json_reader::position pos) {
16 switch (pos) {
17 default:
18 return "invalid input";
19 case caf::json_reader::position::value:
20 return "json::value";
21 case caf::json_reader::position::object:
22 return "json::object";
23 case caf::json_reader::position::null:
24 return "null";
25 case caf::json_reader::position::key:
26 return "json::key";
27 case caf::json_reader::position::sequence:
28 return "json::array";
29 case caf::json_reader::position::members:
30 return "json::members";
31 }
32 }
33
type_clash(caf::string_view want,caf::string_view got)34 std::string type_clash(caf::string_view want, caf::string_view got) {
35 std::string result = "type clash: expected ";
36 result.insert(result.end(), want.begin(), want.end());
37 result += ", got ";
38 result.insert(result.end(), got.begin(), got.end());
39 return result;
40 }
41
type_clash(caf::string_view want,caf::json_reader::position got)42 std::string type_clash(caf::string_view want, caf::json_reader::position got) {
43 return type_clash(want, pretty_name(got));
44 }
45
type_clash(caf::json_reader::position want,caf::json_reader::position got)46 std::string type_clash(caf::json_reader::position want,
47 caf::json_reader::position got) {
48 return type_clash(pretty_name(want), pretty_name(got));
49 }
50
type_clash(caf::string_view want,const caf::detail::json::value & got)51 std::string type_clash(caf::string_view want,
52 const caf::detail::json::value& got) {
53 using namespace caf::literals;
54 using caf::detail::json::value;
55 switch (got.data.index()) {
56 case value::integer_index:
57 return type_clash(want, "json::integer"_sv);
58 case value::double_index:
59 return type_clash(want, "json::real"_sv);
60 case value::bool_index:
61 return type_clash(want, "json::boolean"_sv);
62 case value::string_index:
63 return type_clash(want, "json::string"_sv);
64 case value::array_index:
65 return type_clash(want, "json::array"_sv);
66 case value::object_index:
67 return type_clash(want, "json::object"_sv);
68 default:
69 return type_clash(want, "json::null"_sv);
70 }
71 }
72
73 const caf::detail::json::member*
find_member(const caf::detail::json::object * obj,caf::string_view key)74 find_member(const caf::detail::json::object* obj, caf::string_view key) {
75 for (const auto& member : *obj)
76 if (member.key == key)
77 return &member;
78 return nullptr;
79 }
80
field_type(const caf::detail::json::object * obj,caf::string_view name,caf::string_view suffix)81 caf::string_view field_type(const caf::detail::json::object* obj,
82 caf::string_view name, caf::string_view suffix) {
83 namespace json = caf::detail::json;
84 for (const auto& member : *obj) {
85 if (member.val && member.val->data.index() == json::value::string_index
86 && !member.key.empty() && member.key[0] == '@') {
87 auto key = member.key;
88 key.remove_prefix(1);
89 if (caf::starts_with(key, name)) {
90 key.remove_prefix(name.size());
91 if (key == suffix)
92 return std::get<caf::string_view>(member.val->data);
93 }
94 }
95 }
96 return {};
97 }
98
99 } // namespace
100
101 #define FN_DECL static constexpr const char* fn = __func__
102
103 #define INVALID_AND_PAST_THE_END_CASES \
104 case position::invalid: \
105 emplace_error(sec::runtime_error, class_name, fn, \
106 "found an invalid position"); \
107 return false; \
108 case position::past_the_end: \
109 emplace_error(sec::runtime_error, class_name, fn, \
110 "tried reading past the end"); \
111 return false;
112
113 #define SCOPE(expected_position) \
114 if (auto got = pos(); got != expected_position) { \
115 emplace_error(sec::runtime_error, class_name, __func__, \
116 type_clash(expected_position, got)); \
117 return false; \
118 }
119
120 namespace caf {
121
122 // -- constructors, destructors, and assignment operators ----------------------
123
json_reader()124 json_reader::json_reader() : super() {
125 has_human_readable_format_ = true;
126 }
127
json_reader(actor_system & sys)128 json_reader::json_reader(actor_system& sys) : super(sys) {
129 has_human_readable_format_ = true;
130 }
131
json_reader(execution_unit * ctx)132 json_reader::json_reader(execution_unit* ctx) : super(ctx) {
133 has_human_readable_format_ = true;
134 }
135
~json_reader()136 json_reader::~json_reader() {
137 // nop
138 }
139
140 // -- modifiers --------------------------------------------------------------
141
load(string_view json_text)142 bool json_reader::load(string_view json_text) {
143 reset();
144 string_parser_state ps{json_text.begin(), json_text.end()};
145 root_ = detail::json::parse(ps, &buf_);
146 if (ps.code != pec::success) {
147 set_error(make_error(ps));
148 st_ = nullptr;
149 return false;
150 } else {
151 err_.reset();
152 detail::monotonic_buffer_resource::allocator<stack_type> alloc{&buf_};
153 st_ = new (alloc.allocate(1)) stack_type(stack_allocator{&buf_});
154 st_->reserve(16);
155 st_->emplace_back(root_);
156 return true;
157 }
158 }
159
revert()160 void json_reader::revert() {
161 if (st_) {
162 CAF_ASSERT(root_ != nullptr);
163 err_.reset();
164 st_->clear();
165 st_->emplace_back(root_);
166 }
167 }
168
reset()169 void json_reader::reset() {
170 buf_.reclaim();
171 st_ = nullptr;
172 err_.reset();
173 }
174
175 // -- interface functions ------------------------------------------------------
176
fetch_next_object_type(type_id_t & type)177 bool json_reader::fetch_next_object_type(type_id_t& type) {
178 string_view type_name;
179 if (fetch_next_object_name(type_name)) {
180 if (auto id = query_type_id(type_name); id != invalid_type_id) {
181 type = id;
182 return true;
183 } else {
184 std::string what = "no type ID available for @type: ";
185 what.insert(what.end(), type_name.begin(), type_name.end());
186 emplace_error(sec::runtime_error, class_name, __func__, std::move(what));
187 return false;
188 }
189 } else {
190 return false;
191 }
192 }
193
fetch_next_object_name(string_view & type_name)194 bool json_reader::fetch_next_object_name(string_view& type_name) {
195 FN_DECL;
196 return consume<false>(fn, [this, &type_name](const detail::json::value& val) {
197 if (val.data.index() == detail::json::value::object_index) {
198 auto& obj = get<detail::json::object>(val.data);
199 if (auto mem_ptr = find_member(&obj, "@type")) {
200 if (mem_ptr->val->data.index() == detail::json::value::string_index) {
201 type_name = std::get<string_view>(mem_ptr->val->data);
202 return true;
203 } else {
204 emplace_error(sec::runtime_error, class_name, fn,
205 "expected a string argument to @type");
206 return false;
207 }
208 } else {
209 emplace_error(sec::runtime_error, class_name, fn,
210 "found no @type member");
211 return false;
212 }
213 } else {
214 emplace_error(sec::runtime_error, class_name, fn,
215 type_clash("json::object", val));
216 return false;
217 }
218 });
219 }
220
begin_object(type_id_t,string_view)221 bool json_reader::begin_object(type_id_t, string_view) {
222 FN_DECL;
223 return consume<false>(fn, [this](const detail::json::value& val) {
224 if (val.data.index() == detail::json::value::object_index) {
225 push(&get<detail::json::object>(val.data));
226 return true;
227 } else {
228 emplace_error(sec::runtime_error, class_name, fn,
229 type_clash("json::object", val));
230 return false;
231 }
232 });
233 }
234
end_object()235 bool json_reader::end_object() {
236 FN_DECL;
237 SCOPE(position::object);
238 pop();
239 auto current_pos = pos();
240 switch (current_pos) {
241 INVALID_AND_PAST_THE_END_CASES
242 case position::value:
243 pop();
244 return true;
245 case position::sequence:
246 top<position::sequence>().advance();
247 return true;
248 default:
249 emplace_error(sec::runtime_error, class_name, __func__,
250 type_clash("json::value or json::array", current_pos));
251 return false;
252 }
253 }
254
begin_field(string_view name)255 bool json_reader::begin_field(string_view name) {
256 SCOPE(position::object);
257 if (auto member = find_member(top<position::object>(), name)) {
258 push(member->val);
259 return true;
260 } else {
261 emplace_error(sec::runtime_error, class_name, __func__,
262 "no such field: " + to_string(name));
263 return false;
264 }
265 }
266
begin_field(string_view name,bool & is_present)267 bool json_reader::begin_field(string_view name, bool& is_present) {
268 SCOPE(position::object);
269 if (auto member = find_member(top<position::object>(), name);
270 member != nullptr
271 && member->val->data.index() != detail::json::value::null_index) {
272 push(member->val);
273 is_present = true;
274 } else {
275 is_present = false;
276 }
277 return true;
278 }
279
begin_field(string_view name,span<const type_id_t> types,size_t & index)280 bool json_reader::begin_field(string_view name, span<const type_id_t> types,
281 size_t& index) {
282 bool is_present = false;
283 if (begin_field(name, is_present, types, index)) {
284 if (is_present) {
285 return true;
286 } else {
287 emplace_error(sec::runtime_error, class_name, __func__,
288 "mandatory field missing");
289 return false;
290 }
291 } else {
292 return false;
293 }
294 }
295
begin_field(string_view name,bool & is_present,span<const type_id_t> types,size_t & index)296 bool json_reader::begin_field(string_view name, bool& is_present,
297 span<const type_id_t> types, size_t& index) {
298 SCOPE(position::object);
299 if (auto member = find_member(top<position::object>(), name);
300 member != nullptr
301 && member->val->data.index() != detail::json::value::null_index) {
302 auto ft = field_type(top<position::object>(), name, field_type_suffix_);
303 if (auto id = query_type_id(ft); id != invalid_type_id) {
304 if (auto i = std::find(types.begin(), types.end(), id);
305 i != types.end()) {
306 index = static_cast<size_t>(std::distance(types.begin(), i));
307 push(member->val);
308 is_present = true;
309 return true;
310 }
311 }
312 }
313 is_present = false;
314 return true;
315 }
316
end_field()317 bool json_reader::end_field() {
318 SCOPE(position::object);
319 // Note: no pop() here, because the value(s) were already consumed.
320 return true;
321 }
322
begin_tuple(size_t size)323 bool json_reader::begin_tuple(size_t size) {
324 size_t list_size = 0;
325 if (begin_sequence(list_size)) {
326 if (list_size == size) {
327 return true;
328 } else {
329 std::string msg;
330 msg += "expected tuple of size ";
331 detail::print(msg, size);
332 msg += ", got a list of size ";
333 detail::print(msg, list_size);
334 emplace_error(sec::conversion_failed, class_name, __func__,
335 std::move(msg));
336 return false;
337 }
338 } else {
339 return false;
340 }
341 }
342
end_tuple()343 bool json_reader::end_tuple() {
344 return end_sequence();
345 }
346
begin_key_value_pair()347 bool json_reader::begin_key_value_pair() {
348 SCOPE(position::members);
349 if (auto& xs = top<position::members>(); !xs.at_end()) {
350 auto& current = xs.current();
351 push(current.val);
352 push(current.key);
353 return true;
354 } else {
355 emplace_error(sec::runtime_error, class_name, __func__,
356 "tried reading a JSON::object sequentially past its end");
357 return false;
358 }
359 }
360
end_key_value_pair()361 bool json_reader::end_key_value_pair() {
362 SCOPE(position::members);
363 top<position::members>().advance();
364 return true;
365 }
366
begin_sequence(size_t & size)367 bool json_reader::begin_sequence(size_t& size) {
368 FN_DECL;
369 return consume<false>(fn, [this, &size](const detail::json::value& val) {
370 if (val.data.index() == detail::json::value::array_index) {
371 auto& ls = get<detail::json::array>(val.data);
372 size = ls.size();
373 push(sequence{ls.begin(), ls.end()});
374 return true;
375 } else {
376 emplace_error(sec::runtime_error, class_name, fn,
377 type_clash("json::array", val));
378 return false;
379 }
380 });
381 }
382
end_sequence()383 bool json_reader::end_sequence() {
384 SCOPE(position::sequence);
385 if (top<position::sequence>().at_end()) {
386 pop();
387 // We called consume<false> at first, so we need to call it again with
388 // <true> for advancing the position now.
389 return consume<true>(__func__,
390 [](const detail::json::value&) { return true; });
391 } else {
392 emplace_error(sec::runtime_error, class_name, __func__,
393 "failed to consume all elements from json::array");
394 return false;
395 }
396 }
397
begin_associative_array(size_t & size)398 bool json_reader::begin_associative_array(size_t& size) {
399 FN_DECL;
400 return consume<false>(fn, [this, &size](const detail::json::value& val) {
401 if (val.data.index() == detail::json::value::object_index) {
402 auto* obj = std::addressof(get<detail::json::object>(val.data));
403 pop();
404 size = obj->size();
405 push(members{obj->begin(), obj->end()});
406 return true;
407 } else {
408 emplace_error(sec::runtime_error, class_name, fn,
409 type_clash("json::object", val));
410 return false;
411 }
412 });
413 }
414
end_associative_array()415 bool json_reader::end_associative_array() {
416 SCOPE(position::members);
417 if (top<position::members>().at_end()) {
418 pop();
419 return true;
420 } else {
421 emplace_error(sec::runtime_error, class_name, __func__,
422 "failed to consume all elements in an associative array");
423 return false;
424 }
425 }
426
value(byte & x)427 bool json_reader::value(byte& x) {
428 auto tmp = uint8_t{0};
429 if (value(tmp)) {
430 x = static_cast<byte>(tmp);
431 return true;
432 } else {
433 return false;
434 }
435 }
436
value(bool & x)437 bool json_reader::value(bool& x) {
438 FN_DECL;
439 return consume<true>(fn, [this, &x](const detail::json::value& val) {
440 if (val.data.index() == detail::json::value::bool_index) {
441 x = std::get<bool>(val.data);
442 return true;
443 } else {
444 emplace_error(sec::runtime_error, class_name, fn,
445 type_clash("json::boolean", val));
446 return false;
447 }
448 });
449 }
450
451 template <bool PopOrAdvanceOnSuccess, class F>
consume(const char * fn,F f)452 bool json_reader::consume(const char* fn, F f) {
453 auto current_pos = pos();
454 switch (current_pos) {
455 INVALID_AND_PAST_THE_END_CASES
456 case position::value:
457 if (f(*top<position::value>())) {
458 if constexpr (PopOrAdvanceOnSuccess)
459 pop();
460 return true;
461 } else {
462 return false;
463 }
464 case position::key:
465 if (f(detail::json::value{top<position::key>()})) {
466 if constexpr (PopOrAdvanceOnSuccess)
467 pop();
468 return true;
469 } else {
470 return false;
471 }
472 case position::sequence:
473 if (auto& ls = top<position::sequence>(); !ls.at_end()) {
474 auto& curr = ls.current();
475 if constexpr (PopOrAdvanceOnSuccess)
476 ls.advance();
477 return f(curr);
478 } else {
479 emplace_error(sec::runtime_error, class_name, fn,
480 "tried reading a json::array past the end");
481 return false;
482 }
483 default:
484 emplace_error(sec::runtime_error, class_name, fn,
485 type_clash("json::value", current_pos));
486 return false;
487 }
488 }
489
490 template <class T>
integer(T & x)491 bool json_reader::integer(T& x) {
492 static constexpr const char* fn = "value";
493 return consume<true>(fn, [this, &x](const detail::json::value& val) {
494 if (val.data.index() == detail::json::value::integer_index) {
495 auto i64 = std::get<int64_t>(val.data);
496 if (detail::bounds_checker<T>::check(i64)) {
497 x = static_cast<T>(i64);
498 return true;
499 } else {
500 emplace_error(sec::runtime_error, class_name, fn,
501 "integer out of bounds");
502 return false;
503 }
504 } else {
505 emplace_error(sec::runtime_error, class_name, fn,
506 type_clash("json::integer", val));
507 return false;
508 }
509 });
510 }
511
value(int8_t & x)512 bool json_reader::value(int8_t& x) {
513 return integer(x);
514 }
515
value(uint8_t & x)516 bool json_reader::value(uint8_t& x) {
517 return integer(x);
518 }
519
value(int16_t & x)520 bool json_reader::value(int16_t& x) {
521 return integer(x);
522 }
523
value(uint16_t & x)524 bool json_reader::value(uint16_t& x) {
525 return integer(x);
526 }
527
value(int32_t & x)528 bool json_reader::value(int32_t& x) {
529 return integer(x);
530 }
531
value(uint32_t & x)532 bool json_reader::value(uint32_t& x) {
533 return integer(x);
534 }
535
value(int64_t & x)536 bool json_reader::value(int64_t& x) {
537 return integer(x);
538 }
539
value(uint64_t & x)540 bool json_reader::value(uint64_t& x) {
541 return integer(x);
542 }
543
value(float & x)544 bool json_reader::value(float& x) {
545 auto tmp = 0.0;
546 if (value(tmp)) {
547 x = static_cast<float>(tmp);
548 return true;
549 } else {
550 return false;
551 }
552 }
553
value(double & x)554 bool json_reader::value(double& x) {
555 FN_DECL;
556 return consume<true>(fn, [this, &x](const detail::json::value& val) {
557 if (val.data.index() == detail::json::value::double_index) {
558 x = std::get<double>(val.data);
559 return true;
560 } else {
561 emplace_error(sec::runtime_error, class_name, fn,
562 type_clash("json::real", val));
563 return false;
564 }
565 });
566 }
567
value(long double & x)568 bool json_reader::value(long double& x) {
569 auto tmp = 0.0;
570 if (value(tmp)) {
571 x = static_cast<long double>(tmp);
572 return true;
573 } else {
574 return false;
575 }
576 }
577
value(std::string & x)578 bool json_reader::value(std::string& x) {
579 FN_DECL;
580 return consume<true>(fn, [this, &x](const detail::json::value& val) {
581 if (val.data.index() == detail::json::value::string_index) {
582 detail::print_unescaped(x, std::get<string_view>(val.data));
583 return true;
584 } else {
585 emplace_error(sec::runtime_error, class_name, fn,
586 type_clash("json::string", val));
587 return false;
588 }
589 });
590 }
591
value(std::u16string &)592 bool json_reader::value(std::u16string&) {
593 emplace_error(sec::runtime_error, class_name, __func__,
594 "u16string support not implemented yet");
595 return false;
596 }
597
value(std::u32string &)598 bool json_reader::value(std::u32string&) {
599 emplace_error(sec::runtime_error, class_name, __func__,
600 "u32string support not implemented yet");
601 return false;
602 }
603
value(span<byte>)604 bool json_reader::value(span<byte>) {
605 emplace_error(sec::runtime_error, class_name, __func__,
606 "byte span support not implemented yet");
607 return false;
608 }
609
pos() const610 json_reader::position json_reader::pos() const noexcept {
611 if (st_ == nullptr)
612 return position::invalid;
613 else if (st_->empty())
614 return position::past_the_end;
615 else
616 return static_cast<position>(st_->back().index());
617 }
618
619 } // namespace caf
620