123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956 |
- #ifndef CEREAL_ARCHIVES_XML_HPP_
- #define CEREAL_ARCHIVES_XML_HPP_
- #include "cereal/cereal.hpp"
- #include "cereal/details/util.hpp"
- #include "cereal/external/rapidxml/rapidxml.hpp"
- #include "cereal/external/rapidxml/rapidxml_print.hpp"
- #include "cereal/external/base64.hpp"
- #include <sstream>
- #include <stack>
- #include <vector>
- #include <limits>
- #include <string>
- #include <cstring>
- #include <cmath>
- namespace cereal
- {
- namespace xml_detail
- {
- #ifndef CEREAL_XML_STRING_VALUE
-
-
- #define CEREAL_XML_STRING_VALUE "cereal"
- #endif
-
- static const char * CEREAL_XML_STRING = CEREAL_XML_STRING_VALUE;
-
- inline bool isWhitespace( char c )
- {
- return c == ' ' || c == '\t' || c == '\n' || c == '\r';
- }
- }
-
-
-
- class XMLOutputArchive : public OutputArchive<XMLOutputArchive>, public traits::TextArchive
- {
- public:
-
-
-
-
- class Options
- {
- public:
-
- static Options Default(){ return Options(); }
-
-
- explicit Options( int precision_ = std::numeric_limits<double>::max_digits10,
- bool indent_ = true,
- bool outputType_ = false,
- bool sizeAttributes_ = true ) :
- itsPrecision( precision_ ),
- itsIndent( indent_ ),
- itsOutputType( outputType_ ),
- itsSizeAttributes( sizeAttributes_ )
- { }
-
-
-
- Options & precision( int value ){ itsPrecision = value; return * this; }
-
- Options & indent( bool enable ){ itsIndent = enable; return *this; }
-
- Options & outputType( bool enable ){ itsOutputType = enable; return *this; }
-
- Options & sizeAttributes( bool enable ){ itsSizeAttributes = enable; return *this; }
-
- private:
- friend class XMLOutputArchive;
- int itsPrecision;
- bool itsIndent;
- bool itsOutputType;
- bool itsSizeAttributes;
- };
-
-
- XMLOutputArchive( std::ostream & stream, Options const & options = Options::Default() ) :
- OutputArchive<XMLOutputArchive>(this),
- itsStream(stream),
- itsOutputType( options.itsOutputType ),
- itsIndent( options.itsIndent ),
- itsSizeAttributes(options.itsSizeAttributes)
- {
-
- auto node = itsXML.allocate_node( rapidxml::node_declaration );
- node->append_attribute( itsXML.allocate_attribute( "version", "1.0" ) );
- node->append_attribute( itsXML.allocate_attribute( "encoding", "utf-8" ) );
- itsXML.append_node( node );
-
- auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
- itsXML.append_node( root );
- itsNodes.emplace( root );
-
- itsStream << std::boolalpha;
- itsStream.precision( options.itsPrecision );
- itsOS << std::boolalpha;
- itsOS.precision( options.itsPrecision );
- }
-
- ~XMLOutputArchive() CEREAL_NOEXCEPT
- {
- const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting;
- rapidxml::print( itsStream, itsXML, flags );
- itsXML.clear();
- }
-
-
- void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
- {
- itsNodes.top().name = name;
- startNode();
- auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
- saveValue( base64string );
- if( itsOutputType )
- itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", "cereal binary data" ) );
- finishNode();
- }
-
-
-
-
-
- void startNode()
- {
-
- const auto nameString = itsNodes.top().getValueName();
-
- auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
-
- auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() );
- itsNodes.top().node->append_node( node );
- itsNodes.emplace( node );
- }
-
- void finishNode()
- {
- itsNodes.pop();
- }
-
- void setNextName( const char * name )
- {
- itsNodes.top().name = name;
- }
-
-
- template <class T> inline
- void saveValue( T const & value )
- {
- itsOS.clear(); itsOS.seekp( 0, std::ios::beg );
- itsOS << value << std::ends;
- auto strValue = itsOS.str();
-
-
-
- strValue.resize(std::strlen(strValue.c_str()));
-
- const auto len = strValue.length();
- if ( len > 0 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 1] ) ) )
- {
- itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "xml:space", "preserve" ) );
- }
-
- auto dataPtr = itsXML.allocate_string(strValue.c_str(), strValue.length() + 1 );
-
- itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) );
- }
-
- void saveValue( uint8_t const & value )
- {
- saveValue( static_cast<uint32_t>( value ) );
- }
-
- void saveValue( int8_t const & value )
- {
- saveValue( static_cast<int32_t>( value ) );
- }
-
- template <class T> inline
- void insertType()
- {
- if( !itsOutputType )
- return;
-
- const auto nameString = util::demangledName<T>();
-
- auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
- itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", namePtr ) );
- }
-
- void appendAttribute( const char * name, const char * value )
- {
- auto namePtr = itsXML.allocate_string( name );
- auto valuePtr = itsXML.allocate_string( value );
- itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
- }
- bool hasSizeAttributes() const { return itsSizeAttributes; }
- protected:
-
- struct NodeInfo
- {
- NodeInfo( rapidxml::xml_node<> * n = nullptr,
- const char * nm = nullptr ) :
- node( n ),
- counter( 0 ),
- name( nm )
- { }
- rapidxml::xml_node<> * node;
- size_t counter;
- const char * name;
-
-
- std::string getValueName()
- {
- if( name )
- {
- auto n = name;
- name = nullptr;
- return {n};
- }
- else
- return "value" + std::to_string( counter++ ) + "\0";
- }
- };
-
- private:
- std::ostream & itsStream;
- rapidxml::xml_document<> itsXML;
- std::stack<NodeInfo> itsNodes;
- std::ostringstream itsOS;
- bool itsOutputType;
- bool itsIndent;
- bool itsSizeAttributes;
- };
-
-
-
- class XMLInputArchive : public InputArchive<XMLInputArchive>, public traits::TextArchive
- {
- public:
-
-
-
-
- XMLInputArchive( std::istream & stream ) :
- InputArchive<XMLInputArchive>( this ),
- itsData( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() )
- {
- try
- {
- itsData.push_back('\0');
- itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>( reinterpret_cast<char *>( itsData.data() ) );
- }
- catch( rapidxml::parse_error const & )
- {
-
-
-
-
-
-
- throw Exception("XML Parsing failed - likely due to invalid characters or invalid naming");
- }
-
- auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
- if( root == nullptr )
- throw Exception("Could not detect cereal root node - likely due to empty or invalid input");
- else
- itsNodes.emplace( root );
- }
- ~XMLInputArchive() CEREAL_NOEXCEPT = default;
-
-
- void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
- {
- setNextName( name );
- startNode();
- std::string encoded;
- loadValue( encoded );
- auto decoded = base64::decode( encoded );
- if( size != decoded.size() )
- throw Exception("Decoded binary data size does not match specified size");
- std::memcpy( data, decoded.data(), decoded.size() );
- finishNode();
- }
-
-
-
-
-
- void startNode()
- {
- auto next = itsNodes.top().child;
- auto const expectedName = itsNodes.top().name;
-
-
-
- if( expectedName && ( next == nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
- {
- next = itsNodes.top().search( expectedName );
- if( next == nullptr )
- throw Exception("XML Parsing failed - provided NVP (" + std::string(expectedName) + ") not found");
- }
- itsNodes.emplace( next );
- }
-
- void finishNode()
- {
-
- itsNodes.pop();
-
- itsNodes.top().advance();
-
- itsNodes.top().name = nullptr;
- }
-
-
- const char * getNodeName() const
- {
- return itsNodes.top().getChildName();
- }
-
- void setNextName( const char * name )
- {
- itsNodes.top().name = name;
- }
-
- template <class T, traits::EnableIf<std::is_unsigned<T>::value,
- std::is_same<T, bool>::value> = traits::sfinae> inline
- void loadValue( T & value )
- {
- std::istringstream is( itsNodes.top().node->value() );
- is.setf( std::ios::boolalpha );
- is >> value;
- }
-
- template <class T, traits::EnableIf<std::is_integral<T>::value,
- !std::is_same<T, bool>::value,
- sizeof(T) == sizeof(char)> = traits::sfinae> inline
- void loadValue( T & value )
- {
- value = *reinterpret_cast<T*>( itsNodes.top().node->value() );
- }
-
- void loadValue( int8_t & value )
- {
- int32_t val; loadValue( val ); value = static_cast<int8_t>( val );
- }
-
- void loadValue( uint8_t & value )
- {
- uint32_t val; loadValue( val ); value = static_cast<uint8_t>( val );
- }
-
- template <class T, traits::EnableIf<std::is_unsigned<T>::value,
- !std::is_same<T, bool>::value,
- !std::is_same<T, char>::value,
- !std::is_same<T, unsigned char>::value,
- sizeof(T) < sizeof(long long)> = traits::sfinae> inline
- void loadValue( T & value )
- {
- value = static_cast<T>( std::stoul( itsNodes.top().node->value() ) );
- }
-
- template <class T, traits::EnableIf<std::is_unsigned<T>::value,
- !std::is_same<T, bool>::value,
- sizeof(T) >= sizeof(long long)> = traits::sfinae> inline
- void loadValue( T & value )
- {
- value = static_cast<T>( std::stoull( itsNodes.top().node->value() ) );
- }
-
- template <class T, traits::EnableIf<std::is_signed<T>::value,
- !std::is_same<T, char>::value,
- sizeof(T) <= sizeof(int)> = traits::sfinae> inline
- void loadValue( T & value )
- {
- value = static_cast<T>( std::stoi( itsNodes.top().node->value() ) );
- }
-
- template <class T, traits::EnableIf<std::is_signed<T>::value,
- (sizeof(T) > sizeof(int)),
- sizeof(T) <= sizeof(long)> = traits::sfinae> inline
- void loadValue( T & value )
- {
- value = static_cast<T>( std::stol( itsNodes.top().node->value() ) );
- }
-
- template <class T, traits::EnableIf<std::is_signed<T>::value,
- (sizeof(T) > sizeof(long)),
- sizeof(T) <= sizeof(long long)> = traits::sfinae> inline
- void loadValue( T & value )
- {
- value = static_cast<T>( std::stoll( itsNodes.top().node->value() ) );
- }
-
- void loadValue( float & value )
- {
- try
- {
- value = std::stof( itsNodes.top().node->value() );
- }
- catch( std::out_of_range const & )
- {
-
- std::istringstream is( itsNodes.top().node->value() );
- is >> value;
- if( std::fpclassify( value ) != FP_SUBNORMAL )
- throw;
- }
- }
-
- void loadValue( double & value )
- {
- try
- {
- value = std::stod( itsNodes.top().node->value() );
- }
- catch( std::out_of_range const & )
- {
-
- std::istringstream is( itsNodes.top().node->value() );
- is >> value;
- if( std::fpclassify( value ) != FP_SUBNORMAL )
- throw;
- }
- }
-
- void loadValue( long double & value )
- {
- try
- {
- value = std::stold( itsNodes.top().node->value() );
- }
- catch( std::out_of_range const & )
- {
-
- std::istringstream is( itsNodes.top().node->value() );
- is >> value;
- if( std::fpclassify( value ) != FP_SUBNORMAL )
- throw;
- }
- }
-
- template<class CharT, class Traits, class Alloc> inline
- void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
- {
- std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
- str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
- std::istreambuf_iterator<CharT, Traits>() );
- }
-
- template <class T> inline
- void loadSize( T & value )
- {
- value = getNumChildren( itsNodes.top().node );
- }
- protected:
-
- static size_t getNumChildren( rapidxml::xml_node<> * node )
- {
- size_t size = 0;
- node = node->first_node();
- while( node != nullptr )
- {
- ++size;
- node = node->next_sibling();
- }
- return size;
- }
-
-
- struct NodeInfo
- {
- NodeInfo( rapidxml::xml_node<> * n = nullptr ) :
- node( n ),
- child( n->first_node() ),
- size( XMLInputArchive::getNumChildren( n ) ),
- name( nullptr )
- { }
-
-
- void advance()
- {
- if( size > 0 )
- {
- --size;
- child = child->next_sibling();
- }
- }
-
-
- rapidxml::xml_node<> * search( const char * searchName )
- {
- if( searchName )
- {
- size_t new_size = XMLInputArchive::getNumChildren( node );
- const size_t name_size = rapidxml::internal::measure( searchName );
- for( auto new_child = node->first_node(); new_child != nullptr; new_child = new_child->next_sibling() )
- {
- if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
- {
- size = new_size;
- child = new_child;
- return new_child;
- }
- --new_size;
- }
- }
- return nullptr;
- }
-
- const char * getChildName() const
- {
- return child ? child->name() : nullptr;
- }
- rapidxml::xml_node<> * node;
- rapidxml::xml_node<> * child;
- size_t size;
- const char * name;
- };
-
- private:
- std::vector<char> itsData;
- rapidxml::xml_document<> itsXML;
- std::stack<NodeInfo> itsNodes;
- };
-
-
-
-
-
-
- template <class T> inline
- void prologue( XMLOutputArchive &, NameValuePair<T> const & )
- { }
-
- template <class T> inline
- void prologue( XMLInputArchive &, NameValuePair<T> const & )
- { }
-
-
-
- template <class T> inline
- void epilogue( XMLOutputArchive &, NameValuePair<T> const & )
- { }
-
- template <class T> inline
- void epilogue( XMLInputArchive &, NameValuePair<T> const & )
- { }
-
-
-
- template <class T> inline
- void prologue( XMLOutputArchive &, DeferredData<T> const & )
- { }
-
- template <class T> inline
- void prologue( XMLInputArchive &, DeferredData<T> const & )
- { }
-
-
-
- template <class T> inline
- void epilogue( XMLOutputArchive &, DeferredData<T> const & )
- { }
-
-
- template <class T> inline
- void epilogue( XMLInputArchive &, DeferredData<T> const & )
- { }
-
-
-
- template <class T> inline
- void prologue( XMLOutputArchive & ar, SizeTag<T> const & )
- {
- if (ar.hasSizeAttributes())
- {
- ar.appendAttribute("size", "dynamic");
- }
- }
- template <class T> inline
- void prologue( XMLInputArchive &, SizeTag<T> const & )
- { }
-
-
- template <class T> inline
- void epilogue( XMLOutputArchive &, SizeTag<T> const & )
- { }
- template <class T> inline
- void epilogue( XMLInputArchive &, SizeTag<T> const & )
- { }
-
-
-
- template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
- traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
- void prologue( XMLOutputArchive & ar, T const & )
- {
- ar.startNode();
- ar.insertType<T>();
- }
-
- template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
- traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
- void prologue( XMLInputArchive & ar, T const & )
- {
- ar.startNode();
- }
-
-
-
- template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
- traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
- void epilogue( XMLOutputArchive & ar, T const & )
- {
- ar.finishNode();
- }
-
- template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
- traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
- void epilogue( XMLInputArchive & ar, T const & )
- {
- ar.finishNode();
- }
-
-
-
-
- template <class T> inline
- void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive & ar, NameValuePair<T> const & t )
- {
- ar.setNextName( t.name );
- ar( t.value );
- }
-
- template <class T> inline
- void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, NameValuePair<T> & t )
- {
- ar.setNextName( t.name );
- ar( t.value );
- }
-
-
- template <class T> inline
- void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive &, SizeTag<T> const & )
- { }
-
- template <class T> inline
- void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, SizeTag<T> & st )
- {
- ar.loadSize( st.size );
- }
-
-
- template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
- void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, T const & t)
- {
- ar.saveValue( t );
- }
-
- template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
- void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, T & t)
- {
- ar.loadValue( t );
- }
-
-
- template<class CharT, class Traits, class Alloc> inline
- void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
- {
- ar.saveValue( str );
- }
-
- template<class CharT, class Traits, class Alloc> inline
- void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
- {
- ar.loadValue( str );
- }
- }
- CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive)
- CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive)
- CEREAL_SETUP_ARCHIVE_TRAITS(cereal::XMLInputArchive, cereal::XMLOutputArchive)
- #endif
|