// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers /*! \file serialization.h * \brief Simple DSL AAPI based on * * \detailed is_blob_type and has_free_serializer are * both descriptors for dispatching on to the serialize function. * * The API itself defines a domain specific language via dirty macro * hacks. Greenspun's tenth rule is very much in action throughout * this entire code base. */ #pragma once #include #include #include #include #include #include #include #include #include /*! \struct is_blob_type * * \brief a descriptor for dispatching serialize */ template struct is_blob_type { typedef boost::false_type type; }; /*! \struct has_free_serializer * * \brief a descriptor for dispatching serialize */ template struct has_free_serializer { typedef boost::true_type type; }; /*! \struct is_basic_type * * \brief a descriptor for dispatching serialize */ template struct is_basic_type { typedef boost::false_type type; }; template struct is_basic_type> { typedef boost::true_type type; }; template<> struct is_basic_type { typedef boost::true_type type; }; /*! \struct serializer * * \brief ... wouldn't a class be better? * * \detailed The logic behind serializing data. Places the archive * data into the supplied parameter. This dispatches based on the * supplied \a T template parameter's traits of is_blob_type or it is * an integral (as defined by the is_integral trait). Depends on the * \a Archive parameter to have overloaded the serialize_blob(T v, * size_t size) and serialize_int(T v) base on which trait it * applied. When the class has neither types, it falls to the * overloaded method do_serialize(Archive ar) in T to do the work. */ template struct serializer{ static bool serialize(Archive &ar, T &v) { return serialize(ar, v, typename boost::is_integral::type(), typename is_blob_type::type(), typename is_basic_type::type()); } template static bool serialize(Archive &ar, T &v, boost::false_type, boost::true_type, A a) { ar.serialize_blob(&v, sizeof(v)); return true; } template static bool serialize(Archive &ar, T &v, boost::true_type, boost::false_type, A a) { ar.serialize_int(v); return true; } static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type, boost::false_type) { //serialize_custom(ar, v, typename has_free_serializer::type()); return v.do_serialize(ar); } static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type, boost::true_type) { //serialize_custom(ar, v, typename has_free_serializer::type()); return do_serialize(ar, v); } static void serialize_custom(Archive &ar, T &v, boost::true_type) { } }; /*! \fn do_serialize(Archive &ar, T &v) * * \brief just calls the serialize function defined for ar and v... */ template inline bool do_serialize(Archive &ar, T &v) { return ::serializer::serialize(ar, v); } template inline bool do_serialize(Archive &ar, bool &v) { ar.serialize_blob(&v, sizeof(v)); return true; } // Never used in the code base // #ifndef __GNUC__ // #ifndef constexpr // #define constexpr // #endif // #endif /* the following add a trait to a set and define the serialization DSL*/ /*! \macro BLOB_SERIALIZER * * \brief makes the type have a blob serializer trait defined */ #define BLOB_SERIALIZER(T) \ template<> \ struct is_blob_type { \ typedef boost::true_type type; \ } /*! \macro FREE_SERIALIZER * * \brief adds the has_free_serializer to the type */ #define FREE_SERIALIZER(T) \ template<> \ struct has_free_serializer { \ typedef boost::true_type type; \ } /*! \macro VARIANT_TAG * * \brief Adds the tag \tag to the \a Archive of \a Type */ #define VARIANT_TAG(Archive, Type, Tag) \ template \ struct variant_serialization_traits, Type> { \ static inline typename Archive::variant_tag_type get_tag() { \ return Tag; \ } \ } /*! \macro BEGIN_SERIALIZE * * \brief Begins the environment of the DSL * \detailed for describing how to * serialize an of an archive type */ #define BEGIN_SERIALIZE() \ template class Archive> \ bool do_serialize(Archive &ar) { /*! \macro BEGIN_SERIALIZE_OBJECT * * \brief begins the environment of the DSL * \detailed for described the serialization of an object */ #define BEGIN_SERIALIZE_OBJECT() \ template class Archive> \ bool do_serialize(Archive &ar) { \ ar.begin_object(); \ bool r = do_serialize_object(ar); \ ar.end_object(); \ return r; \ } \ template class Archive> \ bool do_serialize_object(Archive &ar){ /*! \macro PREPARE_CUSTOM_VECTOR_SERIALIZATION */ #define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \ ::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive::is_saving()) /*! \macro PREPARE_CUSTOM_DEQUE_SERIALIZATION */ #define PREPARE_CUSTOM_DEQUE_SERIALIZATION(size, vec) \ ::serialization::detail::prepare_custom_deque_serialization(size, vec, typename Archive::is_saving()) /*! \macro END_SERIALIZE * \brief self-explanatory */ #define END_SERIALIZE() \ return ar.stream().good(); \ } /*! \macro VALUE(f) * \brief the same as FIELD(f) */ #define VALUE(f) \ do { \ ar.tag(#f); \ bool r = ::do_serialize(ar, f); \ if (!r || !ar.stream().good()) return false; \ } while(0); /*! \macro FIELD_N(t,f) * * \brief serializes a field \a f tagged \a t */ #define FIELD_N(t, f) \ do { \ ar.tag(t); \ bool r = ::do_serialize(ar, f); \ if (!r || !ar.stream().good()) return false; \ } while(0); /*! \macro FIELD(f) * * \brief tags the field with the variable name and then serializes it */ #define FIELD(f) \ do { \ ar.tag(#f); \ bool r = ::do_serialize(ar, f); \ if (!r || !ar.stream().good()) return false; \ } while(0); /*! \macro FIELDS(f) * * \brief does not add a tag to the serialized value */ #define FIELDS(f) \ do { \ bool r = ::do_serialize(ar, f); \ if (!r || !ar.stream().good()) return false; \ } while(0); /*! \macro VARINT_FIELD(f) * \brief tags and serializes the varint \a f */ #define VARINT_FIELD(f) \ do { \ ar.tag(#f); \ ar.serialize_varint(f); \ if (!ar.stream().good()) return false; \ } while(0); /*! \macro VARINT_FIELD_N(t, f) * * \brief tags (as \a t) and serializes the varint \a f */ #define VARINT_FIELD_N(t, f) \ do { \ ar.tag(t); \ ar.serialize_varint(f); \ if (!ar.stream().good()) return false; \ } while(0); /*! \macro MAGIC_FIELD(m) */ #define MAGIC_FIELD(m) \ std::string magic = m; \ do { \ ar.tag("magic"); \ ar.serialize_blob((void*)magic.data(), magic.size()); \ if (!ar.stream().good()) return false; \ if (magic != m) return false; \ } while(0); /*! \macro VERSION_FIELD(v) */ #define VERSION_FIELD(v) \ uint32_t version = v; \ do { \ ar.tag("version"); \ ar.serialize_varint(version); \ if (!ar.stream().good()) return false; \ } while(0); namespace serialization { /*! \namespace detail * * \brief declaration and default definition for the functions used the API * */ namespace detail { /*! \fn prepare_custom_vector_serialization * * prepares the vector /vec for serialization */ template void prepare_custom_vector_serialization(size_t size, std::vector& vec, const boost::mpl::bool_& /*is_saving*/) { } template void prepare_custom_vector_serialization(size_t size, std::vector& vec, const boost::mpl::bool_& /*is_saving*/) { vec.resize(size); } template void prepare_custom_deque_serialization(size_t size, std::deque& vec, const boost::mpl::bool_& /*is_saving*/) { } template void prepare_custom_deque_serialization(size_t size, std::deque& vec, const boost::mpl::bool_& /*is_saving*/) { vec.resize(size); } /*! \fn do_check_stream_state * * \brief self explanatory */ template bool do_check_stream_state(Stream& s, boost::mpl::bool_, bool noeof) { return s.good(); } /*! \fn do_check_stream_state * * \brief self explanatory * * \detailed Also checks to make sure that the stream is not at EOF */ template bool do_check_stream_state(Stream& s, boost::mpl::bool_, bool noeof) { bool result = false; if (s.good()) { std::ios_base::iostate state = s.rdstate(); result = noeof || EOF == s.peek(); s.clear(state); } return result; } } /*! \fn check_stream_state * * \brief calls detail::do_check_stream_state for ar */ template bool check_stream_state(Archive& ar, bool noeof = false) { return detail::do_check_stream_state(ar.stream(), typename Archive::is_saving(), noeof); } /*! \fn serialize * * \brief serializes \a v into \a ar */ template inline bool serialize(Archive &ar, T &v) { bool r = do_serialize(ar, v); return r && check_stream_state(ar, false); } /*! \fn serialize * * \brief serializes \a v into \a ar */ template inline bool serialize_noeof(Archive &ar, T &v) { bool r = do_serialize(ar, v); return r && check_stream_state(ar, true); } }