/*! * Copyright (c) 2015 by Contributors * \file serializer.h * \brief serializer template class that helps serialization. * This file do not need to be directly used by most user. */ #ifndef DMLC_SERIALIZER_H_ #define DMLC_SERIALIZER_H_ #include #include #include #include #include #include #include #include "./base.h" #include "./io.h" #include "./logging.h" #include "./type_traits.h" #include "./endian.h" #if DMLC_USE_CXX11 #include #include #endif namespace dmlc { /*! \brief internal namespace for serializers */ namespace serializer { /*! * \brief generic serialization handler * \tparam T the type to be serialized * \tparam need_endian_swap Whether use little endian */ template struct Handler; //! \cond Doxygen_Suppress /*! * \brief Serializer that redirect calls by condition * \tparam cond the condition * \tparam Then the serializer used for then condition * \tparam Else the serializer used for else condition * \tparam Return the type of data the serializer handles */ template struct IfThenElse; template struct IfThenElse { inline static void Write(Stream *strm, const T &data) { Then::Write(strm, data); } inline static bool Read(Stream *strm, T *data) { return Then::Read(strm, data); } }; template struct IfThenElse { inline static void Write(Stream *strm, const T &data) { Else::Write(strm, data); } inline static bool Read(Stream *strm, T *data) { return Else::Read(strm, data); } }; /*! \brief Serializer for POD(plain-old-data) data */ template struct NativePODHandler { inline static void Write(Stream *strm, const T &data) { strm->Write(&data, sizeof(T)); } inline static bool Read(Stream *strm, T *dptr) { return strm->Read((void*)dptr, sizeof(T)) == sizeof(T); // NOLINT(*) } }; /*! \brief Serializer for arithmetic data, handle endianness */ template struct ArithmeticHandler { inline static void Write(Stream *strm, const T &data) { if (DMLC_IO_NO_ENDIAN_SWAP) { strm->Write(&data, sizeof(T)); } else { T copy = data; ByteSwap(©, sizeof(T), 1); strm->Write(©, sizeof(T)); } } inline static bool Read(Stream *strm, T *dptr) { bool ret = strm->Read((void*)dptr, sizeof(T)) == sizeof(T); // NOLINT(*) if (!DMLC_IO_NO_ENDIAN_SWAP) { ByteSwap(dptr, sizeof(T), 1); } return ret; } }; // serializer for class that have save/load function template struct SaveLoadClassHandler { inline static void Write(Stream *strm, const T &data) { data.Save(strm); } inline static bool Read(Stream *strm, T *data) { return data->Load(strm); } }; /*! * \brief dummy class for undefined serialization. * This is used to generate error message when user tries to * serialize something that is not supported. * \tparam T the type to be serialized */ template struct UndefinedSerializerFor { }; /*! * \brief Serializer handler for std::vector where T is POD type. * \tparam T element type */ template struct NativePODVectorHandler { inline static void Write(Stream *strm, const std::vector &vec) { uint64_t sz = static_cast(vec.size()); strm->Write(sz); if (sz != 0) { strm->Write(&vec[0], sizeof(T) * vec.size()); } } inline static bool Read(Stream *strm, std::vector *out_vec) { uint64_t sz; if (!strm->Read(&sz)) return false; size_t size = static_cast(sz); out_vec->resize(size); if (sz != 0) { size_t nbytes = sizeof(T) * size; return strm->Read(&(*out_vec)[0], nbytes) == nbytes; } return true; } }; /*! * \brief Serializer handler for std::vector where T can be composed type * \tparam T element type */ template struct ComposeVectorHandler { inline static void Write(Stream *strm, const std::vector &vec) { uint64_t sz = static_cast(vec.size()); strm->Write(sz); strm->WriteArray(dmlc::BeginPtr(vec), vec.size()); } inline static bool Read(Stream *strm, std::vector *out_vec) { uint64_t sz; if (!strm->Read(&sz)) return false; size_t size = static_cast(sz); out_vec->resize(size); return strm->ReadArray(dmlc::BeginPtr(*out_vec), size); } }; /*! * \brief Serializer handler for std::basic_string where T is POD type. * \tparam T element type */ template struct NativePODStringHandler { inline static void Write(Stream *strm, const std::basic_string &vec) { uint64_t sz = static_cast(vec.length()); strm->Write(sz); if (sz != 0) { strm->Write(&vec[0], sizeof(T) * vec.length()); } } inline static bool Read(Stream *strm, std::basic_string *out_vec) { uint64_t sz; if (!strm->Read(&sz)) return false; size_t size = static_cast(sz); out_vec->resize(size); if (sz != 0) { size_t nbytes = sizeof(T) * size; return strm->Read(&(*out_vec)[0], nbytes) == nbytes; } return true; } }; /*! \brief Serializer for std::pair */ template struct PairHandler { inline static void Write(Stream *strm, const std::pair &data) { Handler::Write(strm, data.first); Handler::Write(strm, data.second); } inline static bool Read(Stream *strm, std::pair *data) { return Handler::Read(strm, &(data->first)) && Handler::Read(strm, &(data->second)); } }; // set type handler that can handle most collection type case template struct CollectionHandler { inline static void Write(Stream *strm, const ContainerType &data) { // dump data to vector std::vector vdata(data.begin(), data.end()); // serialize the vector Handler >::Write(strm, vdata); } inline static bool Read(Stream *strm, ContainerType *data) { std::vector vdata; if (!Handler >::Read(strm, &vdata)) return false; data->clear(); data->insert(vdata.begin(), vdata.end()); return true; } }; // handler that can handle most list type case // this type insert function takes additional iterator template struct ListHandler { inline static void Write(Stream *strm, const ListType &data) { typedef typename ListType::value_type ElemType; // dump data to vector std::vector vdata(data.begin(), data.end()); // serialize the vector Handler >::Write(strm, vdata); } inline static bool Read(Stream *strm, ListType *data) { typedef typename ListType::value_type ElemType; std::vector vdata; if (!Handler >::Read(strm, &vdata)) return false; data->clear(); data->insert(data->begin(), vdata.begin(), vdata.end()); return true; } }; //! \endcond /*! * \brief generic serialization handler for type T * * User can define specialization of this class to support * composite serialization of their own class. * * \tparam T the type to be serialized */ template struct Handler { /*! * \brief write data to stream * \param strm the stream we write the data. * \param data the data obeject to be serialized */ inline static void Write(Stream *strm, const T &data) { IfThenElse::value, ArithmeticHandler, IfThenElse::value && DMLC_IO_NO_ENDIAN_SWAP, NativePODHandler, IfThenElse::value, SaveLoadClassHandler, UndefinedSerializerFor, T>, T>, T> ::Write(strm, data); } /*! * \brief read data to stream * \param strm the stream to read the data. * \param data the pointer to the data obeject to read * \return whether the read is successful */ inline static bool Read(Stream *strm, T *data) { return IfThenElse::value, ArithmeticHandler, IfThenElse::value && DMLC_IO_NO_ENDIAN_SWAP, NativePODHandler, IfThenElse::value, SaveLoadClassHandler, UndefinedSerializerFor, T>, T>, T> ::Read(strm, data); } }; //! \cond Doxygen_Suppress template struct Handler > { inline static void Write(Stream *strm, const std::vector &data) { IfThenElse::value && DMLC_IO_NO_ENDIAN_SWAP, NativePODVectorHandler, ComposeVectorHandler, std::vector > ::Write(strm, data); } inline static bool Read(Stream *strm, std::vector *data) { return IfThenElse::value && DMLC_IO_NO_ENDIAN_SWAP, NativePODVectorHandler, ComposeVectorHandler, std::vector > ::Read(strm, data); } }; template struct Handler > { inline static void Write(Stream *strm, const std::basic_string &data) { IfThenElse::value && (DMLC_IO_NO_ENDIAN_SWAP || sizeof(T) == 1), NativePODStringHandler, UndefinedSerializerFor, std::basic_string > ::Write(strm, data); } inline static bool Read(Stream *strm, std::basic_string *data) { return IfThenElse::value && (DMLC_IO_NO_ENDIAN_SWAP || sizeof(T) == 1), NativePODStringHandler, UndefinedSerializerFor, std::basic_string > ::Read(strm, data); } }; template struct Handler > { inline static void Write(Stream *strm, const std::pair &data) { IfThenElse::value && dmlc::is_pod::value && DMLC_IO_NO_ENDIAN_SWAP, NativePODHandler >, PairHandler, std::pair > ::Write(strm, data); } inline static bool Read(Stream *strm, std::pair *data) { return IfThenElse::value && dmlc::is_pod::value && DMLC_IO_NO_ENDIAN_SWAP, NativePODHandler >, PairHandler, std::pair > ::Read(strm, data); } }; template struct Handler > : public CollectionHandler, std::pair > { }; template struct Handler > : public CollectionHandler, std::pair > { }; template struct Handler > : public CollectionHandler, T> { }; template struct Handler > : public CollectionHandler, T> { }; template struct Handler > : public ListHandler > { }; template struct Handler > : public ListHandler > { }; #if DMLC_USE_CXX11 template struct Handler > : public CollectionHandler, std::pair > { }; template struct Handler > : public CollectionHandler, std::pair > { }; template struct Handler > : public CollectionHandler, T> { }; template struct Handler > : public CollectionHandler, T> { }; #endif //! \endcond } // namespace serializer } // namespace dmlc #endif // DMLC_SERIALIZER_H_