8namespace sysio {
namespace chain {
12 using boost::algorithm::starts_with;
13 using boost::algorithm::ends_with;
15 using std::string_view;
38 else if ( is_optional )
47 return std::make_pair<abi_serializer::unpack_function, abi_serializer::pack_function>(
51 else if ( is_optional )
61 return std::make_pair<abi_serializer::unpack_function, abi_serializer::pack_function>(
65 else if ( is_optional )
74 configure_built_in_types();
79 configure_built_in_types();
80 set_abi(abi, max_serialization_time);
84 std::pair<abi_serializer::unpack_function, abi_serializer::pack_function> unpack_pack ) {
85 built_in_types[
name] = std::move( unpack_pack );
88 void abi_serializer::configure_built_in_types() {
134 SYS_ASSERT(
starts_with(abi.version,
"sysio::abi/1."), unsupported_abi_version_exception,
"ABI has an unsupported version");
140 error_messages.clear();
142 action_results.clear();
144 for(
const auto& st : abi.structs )
145 structs[st.name] = st;
147 for(
const auto& td : abi.types ) {
148 SYS_ASSERT(!_is_type(td.new_type_name, ctx), duplicate_abi_type_def_exception,
149 "type already exists", (
"new_type_name",
impl::limit_size(td.new_type_name)));
150 typedefs[td.new_type_name] = td.type;
153 for(
const auto&
a : abi.actions )
154 actions[
a.name] =
a.type;
156 for(
const auto& t : abi.tables )
157 tables[t.name] = t.type;
159 for(
const auto& e : abi.error_messages )
160 error_messages[e.error_code] = e.error_msg;
162 for(
const auto& v : abi.variants.value )
165 for(
const auto&
r : abi.action_results.value )
166 action_results[
r.name] =
r.result_type;
172 SYS_ASSERT( typedefs.size() == abi.types.size(), duplicate_abi_type_def_exception,
"duplicate type definition detected" );
173 SYS_ASSERT( structs.size() == abi.structs.size(), duplicate_abi_struct_def_exception,
"duplicate struct definition detected" );
174 SYS_ASSERT( actions.size() == abi.actions.size(), duplicate_abi_action_def_exception,
"duplicate action definition detected" );
175 SYS_ASSERT( tables.size() == abi.tables.size(), duplicate_abi_table_def_exception,
"duplicate table definition detected" );
176 SYS_ASSERT( error_messages.size() == abi.error_messages.size(), duplicate_abi_err_msg_def_exception,
"duplicate error message definition detected" );
177 SYS_ASSERT(
variants.size() == abi.variants.value.size(), duplicate_abi_variant_def_exception,
"duplicate variant definition detected" );
178 SYS_ASSERT( action_results.size() == abi.action_results.value.size(), duplicate_abi_action_results_def_exception,
"duplicate action results definition detected" );
188 return built_in_types.find(type) != built_in_types.end();
192 return boost::starts_with(type,
"uint") || boost::starts_with(type,
"int");
197 if( boost::starts_with(type,
"uint") ) {
198 return boost::lexical_cast<int>(type.substr(4));
200 return boost::lexical_cast<int>(type.substr(3));
205 return structs.find(
resolve_type(type)) != structs.end();
209 return ends_with(type,
"[]");
213 return ends_with(type,
"?");
218 return _is_type(type, ctx);
227 return type.substr(0, type.size()-2);
229 return type.substr(0, type.size()-1);
235 std::string_view abi_serializer::_remove_bin_extension(
const std::string_view& type) {
236 if( ends_with(type,
"$") )
237 return type.substr(0, type.size()-1);
245 if( built_in_types.find(type) != built_in_types.end() )
return true;
246 if( typedefs.find(type) != typedefs.end() )
return _is_type(typedefs.find(type)->second, ctx);
247 if( structs.find(type) != structs.end() )
return true;
259 for(
const auto& t : typedefs ) {
try {
261 auto itr = typedefs.find(t.second);
262 while( itr != typedefs.end() ) {
264 SYS_ASSERT( find(types_seen.begin(), types_seen.end(), itr->second) == types_seen.end(), abi_circular_def_exception,
265 "Circular reference in type ${type}", (
"type",
impl::limit_size(t.first)) );
266 types_seen.emplace_back(itr->second);
267 itr = typedefs.find(itr->second);
270 for(
const auto& t : typedefs ) {
try {
273 for(
const auto&
s : structs ) {
try {
275 const struct_def* current = &
s.second;
276 vector<std::string_view> types_seen{current->name};
279 const struct_def& base =
get_struct(current->base);
280 SYS_ASSERT(
find(types_seen.begin(), types_seen.end(), base.name) == types_seen.end(), abi_circular_def_exception,
281 "Circular reference in struct ${type}", (
"type",
impl::limit_size(
s.second.name)) );
282 types_seen.emplace_back(base.name);
286 for(
const auto&
field :
s.second.fields ) {
try {
288 SYS_ASSERT(_is_type(_remove_bin_extension(
field.type), ctx), invalid_type_inside_abi,
292 for(
const auto&
s : variants ) {
try {
293 for(
const auto& type :
s.second.types ) {
try {
298 for(
const auto&
a : actions ) {
try {
303 for(
const auto& t : tables ) {
try {
308 for(
const auto&
r : action_results ) {
try {
315 auto itr = typedefs.find(type);
316 if( itr != typedefs.end() ) {
317 for(
auto i = typedefs.size(); i > 0; --i ) {
318 const std::string_view& t = itr->second;
319 itr = typedefs.find( t );
320 if( itr == typedefs.end() )
return t;
330 auto s_itr = structs.find(type);
331 SYS_ASSERT( s_itr != structs.end(), invalid_type_inside_abi,
"Unknown type ${type}", (
"type",ctx.
maybe_shorten(type)) );
333 const auto& st = s_itr->second;
335 _binary_to_variant(
resolve_type(st.base), stream, obj, ctx);
337 bool encountered_extension =
false;
338 for(
uint32_t i = 0; i < st.fields.size(); ++i ) {
339 const auto&
field = st.fields[i];
340 bool extension = ends_with(
field.type,
"$");
341 encountered_extension |= extension;
342 if( !stream.remaining() ) {
346 if( encountered_extension ) {
347 SYS_THROW( abi_exception,
"Encountered field '${f}' without binary extension designation while processing struct '${p}'",
350 SYS_THROW( unpack_exception,
"Stream unexpectedly ended; unable to unpack field '${f}' of struct '${p}'",
354 auto h1 = ctx.
push_to_path( impl::field_path_item{ .parent_struct_itr = s_itr, .field_ordinal = i } );
356 auto v = _binary_to_variant(field_type, stream, ctx);
357 if( ctx.
is_logging() && v.is_string() && field_type ==
"bytes" ) {
359 auto size = v.get_string().
size() / 2;
360 sub_obj(
"size", size );
361 if( size > impl::hex_log_max_size ) {
362 sub_obj(
"trimmed_hex", v.get_string().substr( 0, impl::hex_log_max_size*2 ) );
364 sub_obj(
"hex", std::move( v ) );
366 obj(
field.name, std::move(sub_obj) );
368 obj(
field.name, std::move(v) );
374 impl::binary_to_variant_context& ctx )
const
376 auto h = ctx.enter_scope();
379 auto btype = built_in_types.find(ftype );
380 if( btype != built_in_types.end() ) {
382 return btype->second.first(stream,
is_array(rtype),
is_optional(rtype), ctx.get_yield_function());
383 }
SYS_RETHROW_EXCEPTIONS( unpack_exception,
"Unable to unpack ${class} type '${type}' while processing '${p}'",
384 (
"class",
is_array(rtype) ?
"array of built-in" :
is_optional(rtype) ?
"optional of built-in" :
"built-in")
388 ctx.hint_array_type_if_in_array();
392 }
SYS_RETHROW_EXCEPTIONS( unpack_exception,
"Unable to unpack size of array '${p}'", (
"p", ctx.get_path_string()) )
393 vector<
fc::variant> vars;
394 auto h1 = ctx.push_to_path( impl::array_index_path_item{} );
395 for(
decltype(size.
value) i = 0; i < size; ++i ) {
396 ctx.set_array_index_of_path_back(i);
397 auto v = _binary_to_variant(ftype, stream, ctx);
401 SYS_ASSERT( !v.is_null(), unpack_exception,
"Invalid packed array '${p}'", (
"p", ctx.get_path_string()) );
402 vars.emplace_back(std::move(v));
407 "packed size does not match unpacked array size, packed size ${p} actual size ${a}",
408 (
"p", size)(
"a", vars.size()) );
414 }
SYS_RETHROW_EXCEPTIONS( unpack_exception,
"Unable to unpack presence flag of optional '${p}'", (
"p", ctx.get_path_string()) )
415 return flag ? _binary_to_variant(ftype, stream, ctx) :
fc::variant();
419 ctx.hint_variant_type_if_in_array( v_itr );
420 fc::unsigned_int select;
422 fc::raw::unpack(stream, select);
423 }
SYS_RETHROW_EXCEPTIONS( unpack_exception,
"Unable to unpack tag of variant '${p}'", (
"p", ctx.get_path_string()) )
424 SYS_ASSERT( (
size_t)select < v_itr->second.types.size(), unpack_exception,
425 "Unpacked invalid tag (${select}) for variant '${p}'", (
"select", select.value)(
"p",ctx.get_path_string()) );
426 auto h1 = ctx.push_to_path( impl::variant_path_item{ .variant_itr = v_itr, .variant_ordinal = static_cast<uint32_t>(select) } );
427 return vector<fc::variant>{v_itr->second.types[select], _binary_to_variant(v_itr->second.types[select], stream, ctx)};
432 _binary_to_variant(rtype, stream, mvo, ctx);
434 SYS_ASSERT( mvo.
size() > 0, unpack_exception,
"Unable to unpack '${p}' from stream", (
"p", ctx.get_path_string()) );
438 fc::variant abi_serializer::_binary_to_variant(
const std::string_view& type,
const bytes& binary, impl::binary_to_variant_context& ctx )
const
440 auto h = ctx.enter_scope();
442 return _binary_to_variant(type, ds, ctx);
448 return _binary_to_variant(type, binary, ctx);
458 return _binary_to_variant(type, binary, ctx);
471 auto s_itr = structs.end();
474 if( btype != built_in_types.end() ) {
485 for (
const auto& var : vars) {
498 auto& v = v_itr->second;
500 "Expected input to be an array of two items while processing variant '${p}'", (
"p", ctx.
get_path_string()) );
501 SYS_ASSERT( var[
size_t(0)].is_string(), pack_exception,
502 "Encountered non-string as first item of input array while processing variant '${p}'", (
"p", ctx.
get_path_string()) );
503 auto variant_type_str = var[size_t(0)].
get_string();
504 auto it =
find(v.types.begin(), v.types.end(), variant_type_str);
505 SYS_ASSERT( it != v.types.end(), pack_exception,
506 "Specified type '${t}' in input array is not valid within the variant '${p}'",
509 auto h1 = ctx.
push_to_path( impl::variant_path_item{ .variant_itr = v_itr, .variant_ordinal =
static_cast<uint32_t>(it - v.types.begin()) } );
510 _variant_to_binary( *it, var[
size_t(1)], ds, ctx );
511 }
else if( (s_itr = structs.find(rtype)) != structs.end() ) {
513 const auto& st = s_itr->second;
520 _variant_to_binary(
resolve_type(st.base), var, ds, ctx);
522 bool disallow_additional_fields =
false;
523 for(
uint32_t i = 0; i < st.fields.size(); ++i ) {
524 const auto&
field = st.fields[i];
525 if( vo.contains(
string(
field.name).c_str() ) ) {
526 if( disallow_additional_fields )
527 SYS_THROW( pack_exception,
"Unexpected field '${f}' found in input object while processing struct '${p}'",
530 auto h1 = ctx.
push_to_path( impl::field_path_item{ .parent_struct_itr = s_itr, .field_ordinal = i } );
532 _variant_to_binary(_remove_bin_extension(
field.type), vo[
field.name], ds, ctx);
535 disallow_additional_fields =
true;
536 }
else if( disallow_additional_fields ) {
537 SYS_THROW( abi_exception,
"Encountered field '${f}' without binary extension designation while processing struct '${p}'",
540 SYS_THROW( pack_exception,
"Missing field '${f}' in input object while processing struct '${p}'",
547 "Using input array to specify the fields of the derived struct '${p}'; input arrays are currently only allowed for structs without a base",
549 for(
uint32_t i = 0; i < st.fields.size(); ++i ) {
550 const auto&
field = st.fields[i];
551 if( va.size() > i ) {
552 auto h1 = ctx.
push_to_path( impl::field_path_item{ .parent_struct_itr = s_itr, .field_ordinal = i } );
554 _variant_to_binary(_remove_bin_extension(
field.type), va[i], ds, ctx);
558 SYS_THROW( pack_exception,
"Early end to input array specifying the fields of struct '${p}'; require input for field '${f}'",
563 SYS_THROW( pack_exception,
"Unexpected input encountered while processing struct '${p}'", (
"p",ctx.
get_path_string()) );
570 bytes abi_serializer::_variant_to_binary(
const std::string_view& type,
const fc::variant& var, impl::variant_to_binary_context& ctx )
const
572 auto h = ctx.enter_scope();
573 if( !_is_type(type, ctx) ) {
577 bytes temp( 1024*1024 );
579 _variant_to_binary(type, var, ds, ctx);
580 temp.resize(
ds.tellp());
587 return _variant_to_binary(type, var, ctx);
597 _variant_to_binary(type, var, ds, ctx);
605 auto itr = actions.find(
action);
606 if( itr != actions.end() )
return itr->second;
610 auto itr = tables.find(
action);
611 if( itr != tables.end() )
return itr->second;
616 auto itr = action_results.find(action_result);
617 if( itr != action_results.end() )
return itr->second;
622 auto itr = error_messages.find( error_code );
623 if( itr == error_messages.end() )
624 return std::optional<string>();
632 std::function<void()> callback = [old_recursion_depth=
recursion_depth,
this](){
639 return {std::move(callback)};
648 auto itr1 =
abis.structs.find(rtype);
649 if( itr1 !=
abis.structs.end() ) {
652 auto itr2 =
abis.variants.find(rtype);
653 if( itr2 !=
abis.variants.end() ) {
661 std::function<void()> callback = [
this](){
663 "invariant failure in variant_to_binary_context: path is empty on scope exit" );
667 path.push_back( item );
669 return {std::move(callback)};
675 auto& b =
path.back();
677 SYS_ASSERT( std::holds_alternative<array_index_path_item>(b), abi_exception,
"trying to set array index without first pushing new array index item" );
679 std::get<array_index_path_item>(b).array_index = i;
683 if(
path.size() == 0 || !std::holds_alternative<array_index_path_item>(
path.back()) )
690 if(
path.size() == 0 || !std::holds_alternative<array_index_path_item>(
path.back()) )
697 if(
path.size() == 0 || !std::holds_alternative<array_index_path_item>(
path.back()) )
708 void output_name( std::ostream&
s,
const string_view& str,
bool shorten,
size_t max_length = 64 ) {
709 constexpr size_t min_num_characters_at_ends = 4;
710 constexpr size_t preferred_num_tail_end_characters = 6;
711 constexpr const char* fill_in =
"...";
713 static_assert( min_num_characters_at_ends <= preferred_num_tail_end_characters,
714 "preferred number of tail end characters cannot be less than the imposed absolute minimum" );
716 constexpr size_t fill_in_length =
const_strlen( fill_in );
717 constexpr size_t min_length = fill_in_length + 2*min_num_characters_at_ends;
718 constexpr size_t preferred_min_length = fill_in_length + 2*preferred_num_tail_end_characters;
720 max_length = std::max( max_length, min_length );
722 if( !shorten || str.size() <= max_length ) {
727 size_t actual_num_tail_end_characters = preferred_num_tail_end_characters;
728 if( max_length < preferred_min_length ) {
729 actual_num_tail_end_characters = min_num_characters_at_ends + (max_length - min_length)/2;
732 s.write( str.data(), max_length - fill_in_length - actual_num_tail_end_characters );
733 s.write( fill_in, fill_in_length );
734 s.write( str.data() + (str.size() - actual_num_tail_end_characters), actual_num_tail_end_characters );
812 std::stringstream&
s;
820 if( std::holds_alternative<struct_type_path_root>(th) ) {
821 const auto& str = std::get<struct_type_path_root>(th).struct_itr->first;
823 }
else if( std::holds_alternative<variant_type_path_root>(th) ) {
824 const auto& str = std::get<variant_type_path_root>(th).variant_itr->first;
826 }
else if( std::holds_alternative<array_type_path_root>(th) ) {
851 for(
size_t i = 0, n =
path.size(); i < n; ++i ) {
852 if( full_path && !std::holds_alternative<array_index_path_item>(
path[i]) )
860 if( std::holds_alternative<empty_path_item>(
visitor.last_path_item) ) {
864 std::visit(vis2,
visitor.last_path_item);
873 return std::string(str);
887 std::function<void()> callback = [old_allow_extensions=
allow_extensions,
this](){
895 return {std::move(callback)};