#pragma once #include "types.hh" #include template static inline void write(std::ostream& out, T val) { auto n_val = net::ntoh(val); out.write(reinterpret_cast(&n_val), sizeof(n_val)); } // TODO: Add AllowsMissing parameter which will allow to optimize serialized format. // Currently we default to AllowsMissing = true. template class tuple_type final : public abstract_type { private: const std::vector> types; const bool _byte_order_equal; public: using value_type = std::vector; tuple_type(std::vector> types) : abstract_type("tuple") // FIXME: append names of member types , types(types) , _byte_order_equal(std::all_of(types.begin(), types.end(), [] (auto t) { return t->is_byte_order_equal(); })) { } /* * Format: * ... * * if value is missing then len(value) < 0 */ void serialize_value(const value_type& values, std::ostream& out) { if (AllowPrefixes) { assert(values.size() <= types.size()); } else { assert(values.size() == types.size()); } for (auto&& val : values) { if (!val) { write(out, uint32_t(-1)); } else { assert(val->size() <= std::numeric_limits::max()); write(out, uint32_t(val->size())); out.write(val->begin(), val->size()); } } } value_type deserialize_value(std::istream& in) { std::vector result; result.reserve(types.size()); for (auto&& type : types) { uint32_t u; auto n = in.rdbuf()->sgetn(reinterpret_cast(&u), sizeof(u)); if (!n) { if (AllowPrefixes) { return result; } else { throw marshal_exception(); } } if (n != sizeof(u)) { throw marshal_exception(); } auto len = int32_t(net::ntoh(u)); if (len < 0) { result.emplace_back(); } else { result.emplace_back(bytes(bytes::initialized_later(), len)); auto& b = *result.back(); auto n = in.rdbuf()->sgetn(b.begin(), len); if (n != len) { throw marshal_exception(); } } } return result; } object_opt deserialize(std::istream& in) override { return {boost::any(deserialize_value(in))}; } void serialize(const boost::any& obj, std::ostream& out) override { serialize_value(boost::any_cast(obj), out); } virtual bool less(const bytes& b1, const bytes& b2) override { return compare(b1, b2) < 0; } virtual size_t hash(const bytes& v) override { if (_byte_order_equal) { return std::hash()(v); } size_t h = 0; auto current_type = types.begin(); for (auto&& elem : ::deserialize_value(*this, v)) { if (elem) { h ^= (*current_type)->hash(*elem); } ++current_type; } return h; } virtual int32_t compare(const bytes& b1, const bytes& b2) override { if (is_byte_order_comparable()) { return compare_unsigned(b1, b2); } auto v1 = ::deserialize_value(*this, b1); auto v2 = ::deserialize_value(*this, b2); if (v1.size() != v2.size()) { return v1.size() < v2.size() ? -1 : 1; } if (AllowPrefixes) { assert(v1.size() <= types.size()); } else { assert(v1.size() == types.size()); } auto i1 = v1.begin(); auto i2 = v2.begin(); auto current_type = types.begin(); while (i1 != v1.end()) { bytes_opt& e1 = *i1++; bytes_opt& e2 = *i2++; if (bool(e1) != bool(e2)) { return e2 ? -1 : 1; } auto c = (*current_type++)->compare(*e1, *e2); if (c != 0) { return c; } } return 0; } virtual bool is_byte_order_equal() const override { return _byte_order_equal; } virtual bool is_byte_order_comparable() const override { // We're not byte order comparable because we encode component length as signed integer, // which is not byte order comparable. // TODO: make the length byte-order comparable by adding numeric_limits::min() when serializing return false; } }; using tuple_prefix = tuple_type;