#pragma once #include "to_bin.hpp" #include "from_bin.hpp" #include "stream.hpp" #include "types.hpp" namespace eosio { /// /// opaque type provides a type safe alternative to input_stream to declare a field /// to be skiped during deserialization of its containing data structure. Afterwards, /// the underlying value can be restored with correct type information. /// /// The serialization opaque consists of a variable length byte count followed by the /// serialized bytes for a value of type T. The purpose to serialized as opaque as oppose /// to T is to allow the client to delay deserialization until the value is actually needed and /// thus saving some CPU cycles or memory requirement. /// /// For example, given a foo_type, /// /// /// struct foo_type { /// uint32_t field1; /// string field2; /// opaque> field3; /// }; /// /// /// the deserialization can be implemented as follows: /// /// /// input_stream serialized_foo_stream(...); /// foo_type foo_value; /// from_bin(foo_value, serialized_foo_stream); /// if (foo_value.field1 > 1 || foo_value.field2 == "meet_precondition") { /// if(!foo_value.field3.empty()) { /// loop_until(foo_value.field3, [](const auto& x) { /// if (x.size() > 1) { /// return true; /// } /// do_something(x); /// return false; /// }); /// } /// } /// template class opaque_base { protected: input_stream bin; opaque_base(input_stream b) : bin(b) {} public: opaque_base() = default; explicit opaque_base(const std::vector& data) : bin(data) {} /** * @pre !this->empty() */ [[deprecated("Use unpack() free function instead.")]] void unpack(T& obj) { eosio::from_bin(obj, bin); } /** * @pre !this->empty() */ [[deprecated("Use unpack() free function instead.")]] T unpack() { T obj; this->unpack(obj); return obj; } bool empty() const { return !bin.remaining(); } size_t num_bytes() const { return bin.remaining(); } template void from(S& stream) { eosio::from_bin(this->bin, stream); } template void to_bin(S& stream) const { eosio::to_bin(this->bin, stream); } input_stream get() const { return bin; } }; template class opaque : public opaque_base { public: using opaque_base::opaque_base; template >> opaque(opaque other) : opaque_base(other.bin) {} template friend opaque as_opaque(input_stream bin); }; template class opaque> : public opaque_base> { public: using opaque_base>::opaque_base; /** Determine the size of the vector to be unpacked. * * @pre !this->empty() */ [[deprecated("Use for_each() or loop_until() free function instead.")]] uint64_t unpack_size() { uint64_t num; varuint64_from_bin(num, this->bin); return num; } [[deprecated("Use for_each() or loop_until() free function instead.")]] void unpack_next(T& obj) { eosio::from_bin(obj, this->bin); } [[deprecated("Use for_each() or loop_until() free function instead.")]] T unpack_next() { T obj; this->unpack_next(obj); return obj; } template friend opaque as_opaque(input_stream bin); }; template constexpr const char* get_type_name(opaque*) { return "bytes"; } template void from_bin(opaque& obj, S& stream) { obj.from(stream); } template void to_bin(const opaque& obj, S& stream) { obj.to_bin(stream); } template opaque as_opaque(input_stream bin) { opaque result; result.bin = bin; return result; } template std::enable_if_t, bool> unpack(opaque opq, U& obj) { if (opq.empty()) return false; input_stream bin = opq.get(); eosio::from_bin(obj, bin); return true; } template void loop_until(opaque> opq, Predicate&& f) { if (opq.empty()) return; input_stream bin = opq.get(); uint64_t num; varuint64_from_bin(num, bin); for (uint64_t i = 0; i < num; ++i) { T obj; eosio::from_bin(obj, bin); if (f(std::move(obj))) return; } } template void for_each(opaque> opq, UnaryFunction&& f) { loop_until(opq, [&f](auto&& x) { f(std::forward(x)); return false; }); } } // namespace eosio