LCOV - code coverage report
Current view: top level - cereal/archives - xml.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 236 259 91.1 %
Date: 2022-01-16 21:05:07 Functions: 2089 2141 97.6 %

          Line data    Source code
       1             : /*! \file xml.hpp
       2             :     \brief XML input and output archives */
       3             : /*
       4             :   Copyright (c) 2014, Randolph Voorhies, Shane Grant
       5             :   All rights reserved.
       6             : 
       7             :   Redistribution and use in source and binary forms, with or without
       8             :   modification, are permitted provided that the following conditions are met:
       9             :       * Redistributions of source code must retain the above copyright
      10             :         notice, this list of conditions and the following disclaimer.
      11             :       * Redistributions in binary form must reproduce the above copyright
      12             :         notice, this list of conditions and the following disclaimer in the
      13             :         documentation and/or other materials provided with the distribution.
      14             :       * Neither the name of the copyright holder nor the
      15             :         names of its contributors may be used to endorse or promote products
      16             :         derived from this software without specific prior written permission.
      17             : 
      18             :   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
      19             :   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
      20             :   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      21             :   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
      22             :   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      23             :   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      24             :   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
      25             :   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      26             :   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      27             :   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      28             : */
      29             : #ifndef CEREAL_ARCHIVES_XML_HPP_
      30             : #define CEREAL_ARCHIVES_XML_HPP_
      31             : #include "cereal/cereal.hpp"
      32             : #include "cereal/details/util.hpp"
      33             : 
      34             : #include "cereal/external/rapidxml/rapidxml.hpp"
      35             : #include "cereal/external/rapidxml/rapidxml_print.hpp"
      36             : #include "cereal/external/base64.hpp"
      37             : 
      38             : #include <sstream>
      39             : #include <stack>
      40             : #include <vector>
      41             : #include <limits>
      42             : #include <string>
      43             : #include <cstring>
      44             : #include <cmath>
      45             : 
      46             : namespace cereal
      47             : {
      48             :   namespace xml_detail
      49             :   {
      50             :     #ifndef CEREAL_XML_STRING_VALUE
      51             :     //! The default name for the root node in a cereal xml archive.
      52             :     /*! You can define CEREAL_XML_STRING_VALUE to be different assuming you do so
      53             :         before this file is included. */
      54             :     #define CEREAL_XML_STRING_VALUE "cereal"
      55             :     #endif // CEREAL_XML_STRING_VALUE
      56             : 
      57             :     //! The name given to the root node in a cereal xml archive
      58             :     static const char * CEREAL_XML_STRING = CEREAL_XML_STRING_VALUE;
      59             : 
      60             :     //! Returns true if the character is whitespace
      61     5026293 :     inline bool isWhitespace( char c )
      62             :     {
      63     5026293 :       return c == ' ' || c == '\t' || c == '\n' || c == '\r';
      64             :     }
      65             :   }
      66             : 
      67             :   // ######################################################################
      68             :   //! An output archive designed to save data to XML
      69             :   /*! This archive uses RapidXML to build an in memory XML tree of the
      70             :       data it serializes before outputting it to its stream upon destruction.
      71             :       This archive should be used in an RAII fashion, letting
      72             :       the automatic destruction of the object cause the flush to its stream.
      73             : 
      74             :       XML archives provides a human readable output but at decreased
      75             :       performance (both in time and space) compared to binary archives.
      76             : 
      77             :       XML benefits greatly from name-value pairs, which if present, will
      78             :       name the nodes in the output.  If these are not present, each level
      79             :       of the output tree will be given an automatically generated delimited name.
      80             : 
      81             :       The precision of the output archive controls the number of decimals output
      82             :       for floating point numbers and should be sufficiently large (i.e. at least 20)
      83             :       if there is a desire to have binary equality between the numbers output and
      84             :       those read in.  In general you should expect a loss of precision when going
      85             :       from floating point to text and back.
      86             : 
      87             :       XML archives can optionally print the type of everything they serialize, which
      88             :       adds an attribute to each node.
      89             : 
      90             :       XML archives do not output the size information for any dynamically sized structure
      91             :       and instead infer it from the number of children for a node.  This means that data
      92             :       can be hand edited for dynamic sized structures and will still be readable.  This
      93             :       is accomplished through the cereal::SizeTag object, which will also add an attribute
      94             :       to its parent field.
      95             :       \ingroup Archives */
      96             :   class XMLOutputArchive : public OutputArchive<XMLOutputArchive>, public traits::TextArchive
      97             :   {
      98             :     public:
      99             :       /*! @name Common Functionality
     100             :           Common use cases for directly interacting with an XMLOutputArchive */
     101             :       //! @{
     102             : 
     103             :       //! A class containing various advanced options for the XML archive
     104             :       /*! Options can either be directly passed to the constructor, or chained using the
     105             :           modifier functions for an interface analogous to named parameters */
     106             :       class Options
     107             :       {
     108             :         public:
     109             :           //! Default options
     110        3748 :           static Options Default(){ return Options(); }
     111             : 
     112             :           //! Specify specific options for the XMLOutputArchive
     113             :           /*! @param precision_ The precision used for floating point numbers
     114             :               @param indent_ Whether to indent each line of XML
     115             :               @param outputType_ Whether to output the type of each serialized object as an attribute
     116             :               @param sizeAttributes_ Whether dynamically sized containers output the size=dynamic attribute */
     117        3748 :           explicit Options( int precision_ = std::numeric_limits<double>::max_digits10,
     118             :                             bool indent_ = true,
     119             :                             bool outputType_ = false,
     120        3748 :                             bool sizeAttributes_ = true ) :
     121             :             itsPrecision( precision_ ),
     122             :             itsIndent( indent_ ),
     123             :             itsOutputType( outputType_ ),
     124        3748 :             itsSizeAttributes( sizeAttributes_ )
     125        3748 :           { }
     126             : 
     127             :           /*! @name Option Modifiers
     128             :               An interface for setting option settings analogous to named parameters.
     129             : 
     130             :               @code{cpp}
     131             :               cereal::XMLOutputArchive ar( myStream,
     132             :                                            cereal::XMLOutputArchive::Options()
     133             :                                            .indent(true)
     134             :                                            .sizeAttributes(false) );
     135             :               @endcode
     136             :               */
     137             :           //! @{
     138             : 
     139             :           //! Sets the precision used for floaing point numbers
     140             :           Options & precision( int value ){ itsPrecision = value; return * this; }
     141             :           //! Whether to indent each line of XML
     142             :           Options & indent( bool enable ){ itsIndent = enable; return *this; }
     143             :           //! Whether to output the type of each serialized object as an attribute
     144             :           Options & outputType( bool enable ){ itsOutputType = enable; return *this; }
     145             :           //! Whether dynamically sized containers (e.g. vector) output the size=dynamic attribute
     146             :           Options & sizeAttributes( bool enable ){ itsSizeAttributes = enable; return *this; }
     147             : 
     148             :           //! @}
     149             : 
     150             :         private:
     151             :           friend class XMLOutputArchive;
     152             :           int itsPrecision;
     153             :           bool itsIndent;
     154             :           bool itsOutputType;
     155             :           bool itsSizeAttributes;
     156             :       };
     157             : 
     158             :       //! Construct, outputting to the provided stream upon destruction
     159             :       /*! @param stream  The stream to output to.  Note that XML is only guaranteed to flush
     160             :                          its output to the stream upon destruction.
     161             :           @param options The XML specific options to use.  See the Options struct
     162             :                          for the values of default parameters */
     163        3748 :       XMLOutputArchive( std::ostream & stream, Options const & options = Options::Default() ) :
     164             :         OutputArchive<XMLOutputArchive>(this),
     165             :         itsStream(stream),
     166        3748 :         itsOutputType( options.itsOutputType ),
     167        3748 :         itsIndent( options.itsIndent ),
     168        3748 :         itsSizeAttributes(options.itsSizeAttributes)
     169             :       {
     170             :         // rapidxml will delete all allocations when xml_document is cleared
     171        3748 :         auto node = itsXML.allocate_node( rapidxml::node_declaration );
     172        3748 :         node->append_attribute( itsXML.allocate_attribute( "version", "1.0" ) );
     173        3748 :         node->append_attribute( itsXML.allocate_attribute( "encoding", "utf-8" ) );
     174        3748 :         itsXML.append_node( node );
     175             : 
     176             :         // allocate root node
     177        3748 :         auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
     178        3748 :         itsXML.append_node( root );
     179        3748 :         itsNodes.emplace( root );
     180             : 
     181             :         // set attributes on the streams
     182        3748 :         itsStream << std::boolalpha;
     183        3748 :         itsStream.precision( options.itsPrecision );
     184        3748 :         itsOS << std::boolalpha;
     185        3748 :         itsOS.precision( options.itsPrecision );
     186        3748 :       }
     187             : 
     188             :       //! Destructor, flushes the XML
     189        3748 :       ~XMLOutputArchive() CEREAL_NOEXCEPT
     190        3748 :       {
     191        3748 :         const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting;
     192        3748 :         rapidxml::print( itsStream, itsXML, flags );
     193        3748 :         itsXML.clear();
     194        3748 :       }
     195             : 
     196             :       //! Saves some binary data, encoded as a base64 string, with an optional name
     197             :       /*! This can be called directly by users and it will automatically create a child node for
     198             :           the current XML node, populate it with a base64 encoded string, and optionally name
     199             :           it.  The node will be finished after it has been populated.  */
     200             :       void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
     201             :       {
     202             :         itsNodes.top().name = name;
     203             : 
     204             :         startNode();
     205             : 
     206             :         auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
     207             :         saveValue( base64string );
     208             : 
     209             :         if( itsOutputType )
     210             :           itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", "cereal binary data" ) );
     211             : 
     212             :         finishNode();
     213             :       }
     214             : 
     215             :       //! @}
     216             :       /*! @name Internal Functionality
     217             :           Functionality designed for use by those requiring control over the inner mechanisms of
     218             :           the XMLOutputArchive */
     219             :       //! @{
     220             : 
     221             :       //! Creates a new node that is a child of the node at the top of the stack
     222             :       /*! Nodes will be given a name that has either been pre-set by a name value pair,
     223             :           or generated based upon a counter unique to the parent node.  If you want to
     224             :           give a node a specific name, use setNextName prior to calling startNode.
     225             : 
     226             :           The node will then be pushed onto the node stack. */
     227     3874234 :       void startNode()
     228             :       {
     229             :         // generate a name for this new node
     230     7748468 :         const auto nameString = itsNodes.top().getValueName();
     231             : 
     232             :         // allocate strings for all of the data in the XML object
     233     3874234 :         auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
     234             : 
     235             :         // insert into the XML
     236     3874234 :         auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() );
     237     3874234 :         itsNodes.top().node->append_node( node );
     238     3874234 :         itsNodes.emplace( node );
     239     3874234 :       }
     240             : 
     241             :       //! Designates the most recently added node as finished
     242     3874234 :       void finishNode()
     243             :       {
     244     3874234 :         itsNodes.pop();
     245     3874234 :       }
     246             : 
     247             :       //! Sets the name for the next node created with startNode
     248      742617 :       void setNextName( const char * name )
     249             :       {
     250      742617 :         itsNodes.top().name = name;
     251      742617 :       }
     252             : 
     253             :       //! Saves some data, encoded as a string, into the current top level node
     254             :       /*! The data will be be named with the most recent name if one exists,
     255             :           otherwise it will be given some default delimited value that depends upon
     256             :           the parent node */
     257             :       template <class T> inline
     258     2513356 :       void saveValue( T const & value )
     259             :       {
     260     2513356 :         itsOS.clear(); itsOS.seekp( 0, std::ios::beg );
     261     2513356 :         itsOS << value << std::ends;
     262             : 
     263     5026712 :         auto strValue = itsOS.str();
     264             : 
     265             :         // itsOS.str() may contain data from previous calls after the first '\0' that was just inserted
     266             :         // and this data is counted in the length call. We make sure to remove that section so that the
     267             :         // whitespace validation is done properly
     268     2513356 :         strValue.resize(std::strlen(strValue.c_str()));
     269             : 
     270             :         // If the first or last character is a whitespace, add xml:space attribute
     271     2513356 :         const auto len = strValue.length();
     272     2513356 :         if ( len > 0 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 1] ) ) )
     273             :         {
     274          21 :           itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "xml:space", "preserve" ) );
     275             :         }
     276             : 
     277             :         // allocate strings for all of the data in the XML object
     278     2513356 :         auto dataPtr = itsXML.allocate_string(strValue.c_str(), strValue.length() + 1 );
     279             : 
     280             :         // insert into the XML
     281     2513356 :         itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) );
     282     2513356 :       }
     283             : 
     284             :       //! Overload for uint8_t prevents them from being serialized as characters
     285       32011 :       void saveValue( uint8_t const & value )
     286             :       {
     287       32011 :         saveValue( static_cast<uint32_t>( value ) );
     288       32011 :       }
     289             : 
     290             :       //! Overload for int8_t prevents them from being serialized as characters
     291       56747 :       void saveValue( int8_t const & value )
     292             :       {
     293       56747 :         saveValue( static_cast<int32_t>( value ) );
     294       56747 :       }
     295             : 
     296             :       //! Causes the type to be appended as an attribute to the most recently made node if output type is set to true
     297             :       template <class T> inline
     298     3874234 :       void insertType()
     299             :       {
     300     3874234 :         if( !itsOutputType )
     301     3874234 :           return;
     302             : 
     303             :         // generate a name for this new node
     304           0 :         const auto nameString = util::demangledName<T>();
     305             : 
     306             :         // allocate strings for all of the data in the XML object
     307           0 :         auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
     308             : 
     309           0 :         itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", namePtr ) );
     310             :       }
     311             : 
     312             :       //! Appends an attribute to the current top level node
     313       10300 :       void appendAttribute( const char * name, const char * value )
     314             :       {
     315       10300 :         auto namePtr =  itsXML.allocate_string( name );
     316       10300 :         auto valuePtr = itsXML.allocate_string( value );
     317       10300 :         itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
     318       10300 :       }
     319             : 
     320       10300 :       bool hasSizeAttributes() const { return itsSizeAttributes; }
     321             : 
     322             :     protected:
     323             :       //! A struct that contains metadata about a node
     324             :       struct NodeInfo
     325             :       {
     326     3877982 :         NodeInfo( rapidxml::xml_node<> * n = nullptr,
     327     3877982 :                   const char * nm = nullptr ) :
     328             :           node( n ),
     329             :           counter( 0 ),
     330     3877982 :           name( nm )
     331     3877982 :         { }
     332             : 
     333             :         rapidxml::xml_node<> * node; //!< A pointer to this node
     334             :         size_t counter;              //!< The counter for naming child nodes
     335             :         const char * name;           //!< The name for the next child node
     336             : 
     337             :         //! Gets the name for the next child node created from this node
     338             :         /*! The name will be automatically generated using the counter if
     339             :             a name has not been previously set.  If a name has been previously
     340             :             set, that name will be returned only once */
     341     3874234 :         std::string getValueName()
     342             :         {
     343     3874234 :           if( name )
     344             :           {
     345      742617 :             auto n = name;
     346      742617 :             name = nullptr;
     347      742617 :             return {n};
     348             :           }
     349             :           else
     350     6263234 :             return "value" + std::to_string( counter++ ) + "\0";
     351             :         }
     352             :       }; // NodeInfo
     353             : 
     354             :       //! @}
     355             : 
     356             :     private:
     357             :       std::ostream & itsStream;        //!< The output stream
     358             :       rapidxml::xml_document<> itsXML; //!< The XML document
     359             :       std::stack<NodeInfo> itsNodes;   //!< A stack of nodes added to the document
     360             :       std::ostringstream itsOS;        //!< Used to format strings internally
     361             :       bool itsOutputType;              //!< Controls whether type information is printed
     362             :       bool itsIndent;                  //!< Controls whether indenting is used
     363             :       bool itsSizeAttributes;          //!< Controls whether lists have a size attribute
     364             :   }; // XMLOutputArchive
     365             : 
     366             :   // ######################################################################
     367             :   //! An output archive designed to load data from XML
     368             :   /*! This archive uses RapidXML to build an in memory XML tree of the
     369             :       data in the stream it is given before loading any types serialized.
     370             : 
     371             :       As with the output XML archive, the preferred way to use this archive is in
     372             :       an RAII fashion, ensuring its destruction after all data has been read.
     373             : 
     374             :       Input XML should have been produced by the XMLOutputArchive.  Data can
     375             :       only be added to dynamically sized containers - the input archive will
     376             :       determine their size by looking at the number of child nodes.  Data that
     377             :       did not originate from an XMLOutputArchive is not officially supported,
     378             :       but may be possible to use if properly formatted.
     379             : 
     380             :       The XMLInputArchive does not require that nodes are loaded in the same
     381             :       order they were saved by XMLOutputArchive.  Using name value pairs (NVPs),
     382             :       it is possible to load in an out of order fashion or otherwise skip/select
     383             :       specific nodes to load.
     384             : 
     385             :       The default behavior of the input archive is to read sequentially starting
     386             :       with the first node and exploring its children.  When a given NVP does
     387             :       not match the read in name for a node, the archive will search for that
     388             :       node at the current level and load it if it exists.  After loading an out of
     389             :       order node, the archive will then proceed back to loading sequentially from
     390             :       its new position.
     391             : 
     392             :       Consider this simple example where loading of some data is skipped:
     393             : 
     394             :       @code{cpp}
     395             :       // imagine the input file has someData(1-9) saved in order at the top level node
     396             :       ar( someData1, someData2, someData3 );        // XML loads in the order it sees in the file
     397             :       ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
     398             :                                                     // match expected NVP name, so we search
     399             :                                                     // for the given NVP and load that value
     400             :       ar( someData7, someData8, someData9 );        // with no NVP given, loading resumes at its
     401             :                                                     // current location, proceeding sequentially
     402             :       @endcode
     403             : 
     404             :       \ingroup Archives */
     405             :   class XMLInputArchive : public InputArchive<XMLInputArchive>, public traits::TextArchive
     406             :   {
     407             :     public:
     408             :       /*! @name Common Functionality
     409             :           Common use cases for directly interacting with an XMLInputArchive */
     410             :       //! @{
     411             : 
     412             :       //! Construct, reading in from the provided stream
     413             :       /*! Reads in an entire XML document from some stream and parses it as soon
     414             :           as serialization starts
     415             : 
     416             :           @param stream The stream to read from.  Can be a stringstream or a file. */
     417        3848 :       XMLInputArchive( std::istream & stream ) :
     418             :         InputArchive<XMLInputArchive>( this ),
     419        3848 :         itsData( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() )
     420             :       {
     421             :         try
     422             :         {
     423        3848 :           itsData.push_back('\0'); // rapidxml will do terrible things without the data being null terminated
     424        3848 :           itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>( reinterpret_cast<char *>( itsData.data() ) );
     425             :         }
     426           0 :         catch( rapidxml::parse_error const & )
     427             :         {
     428             :           //std::cerr << "-----Original-----" << std::endl;
     429             :           //stream.seekg(0);
     430             :           //std::cout << std::string( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() ) << std::endl;
     431             : 
     432             :           //std::cerr << "-----Error-----" << std::endl;
     433             :           //std::cerr << e.what() << std::endl;
     434             :           //std::cerr << e.where<char>() << std::endl;
     435           0 :           throw Exception("XML Parsing failed - likely due to invalid characters or invalid naming");
     436             :         }
     437             : 
     438             :         // Parse the root
     439        3848 :         auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
     440        3848 :         if( root == nullptr )
     441           0 :           throw Exception("Could not detect cereal root node - likely due to empty or invalid input");
     442             :         else
     443        3848 :           itsNodes.emplace( root );
     444        3848 :       }
     445             : 
     446        3848 :       ~XMLInputArchive() CEREAL_NOEXCEPT = default;
     447             : 
     448             :       //! Loads some binary data, encoded as a base64 string, optionally specified by some name
     449             :       /*! This will automatically start and finish a node to load the data, and can be called directly by
     450             :           users.
     451             : 
     452             :           Note that this follows the same ordering rules specified in the class description in regards
     453             :           to loading in/out of order */
     454             :       void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
     455             :       {
     456             :         setNextName( name );
     457             :         startNode();
     458             : 
     459             :         std::string encoded;
     460             :         loadValue( encoded );
     461             : 
     462             :         auto decoded = base64::decode( encoded );
     463             : 
     464             :         if( size != decoded.size() )
     465             :           throw Exception("Decoded binary data size does not match specified size");
     466             : 
     467             :         std::memcpy( data, decoded.data(), decoded.size() );
     468             : 
     469             :         finishNode();
     470             :       }
     471             : 
     472             :       //! @}
     473             :       /*! @name Internal Functionality
     474             :           Functionality designed for use by those requiring control over the inner mechanisms of
     475             :           the XMLInputArchive */
     476             :       //! @{
     477             : 
     478             :       //! Prepares to start reading the next node
     479             :       /*! This places the next node to be parsed onto the nodes stack.
     480             : 
     481             :           By default our strategy is to start with the document root node and then
     482             :           recursively iterate through all children in the order they show up in the document.
     483             :           We don't need to know NVPs do to this; we'll just blindly load in the order things appear in.
     484             : 
     485             :           We check to see if the specified NVP matches what the next automatically loaded node is.  If they
     486             :           match, we just continue as normal, going in order.  If they don't match, we attempt to find a node
     487             :           named after the NVP that is being loaded.  If that NVP does not exist, we throw an exception. */
     488     3874734 :       void startNode()
     489             :       {
     490     3874734 :         auto next = itsNodes.top().child; // By default we would move to the next child node
     491     3874734 :         auto const expectedName = itsNodes.top().name; // this is the expected name from the NVP, if provided
     492             : 
     493             :         // If we were given an NVP name, look for it in the current level of the document.
     494             :         //    We only need to do this if either we have exhausted the siblings of the current level or
     495             :         //    the NVP name does not match the name of the node we would normally read next
     496     3874734 :         if( expectedName && ( next == nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
     497             :         {
     498         900 :           next = itsNodes.top().search( expectedName );
     499             : 
     500         900 :           if( next == nullptr )
     501           0 :             throw Exception("XML Parsing failed - provided NVP (" + std::string(expectedName) + ") not found");
     502             :         }
     503             : 
     504     3874734 :         itsNodes.emplace( next );
     505     3874734 :       }
     506             : 
     507             :       //! Finishes reading the current node
     508     3874434 :       void finishNode()
     509             :       {
     510             :         // remove current
     511     3874434 :         itsNodes.pop();
     512             : 
     513             :         // advance parent
     514     3874434 :         itsNodes.top().advance();
     515             : 
     516             :         // Reset name
     517     3874434 :         itsNodes.top().name = nullptr;
     518     3874434 :       }
     519             : 
     520             :       //! Retrieves the current node name
     521             :       //! will return @c nullptr if the node does not have a name
     522             :       const char * getNodeName() const
     523             :       {
     524             :         return itsNodes.top().getChildName();
     525             :       }
     526             : 
     527             :       //! Sets the name for the next node created with startNode
     528      742717 :       void setNextName( const char * name )
     529             :       {
     530      742717 :         itsNodes.top().name = name;
     531      742717 :       }
     532             : 
     533             :       //! Loads a bool from the current top node
     534             :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     535             :                                           std::is_same<T, bool>::value> = traits::sfinae> inline
     536       11408 :       void loadValue( T & value )
     537             :       {
     538       34224 :         std::istringstream is( itsNodes.top().node->value() );
     539       11408 :         is.setf( std::ios::boolalpha );
     540       11408 :         is >> value;
     541       11408 :       }
     542             : 
     543             :       //! Loads a char (signed or unsigned) from the current top node
     544             :       template <class T, traits::EnableIf<std::is_integral<T>::value,
     545             :                                           !std::is_same<T, bool>::value,
     546             :                                           sizeof(T) == sizeof(char)> = traits::sfinae> inline
     547         311 :       void loadValue( T & value )
     548             :       {
     549         311 :         value = *reinterpret_cast<T*>( itsNodes.top().node->value() );
     550         311 :       }
     551             : 
     552             :       //! Load an int8_t from the current top node (ensures we parse entire number)
     553       56747 :       void loadValue( int8_t & value )
     554             :       {
     555       56747 :         int32_t val; loadValue( val ); value = static_cast<int8_t>( val );
     556       56747 :       }
     557             : 
     558             :       //! Load a uint8_t from the current top node (ensures we parse entire number)
     559       32111 :       void loadValue( uint8_t & value )
     560             :       {
     561       32111 :         uint32_t val; loadValue( val ); value = static_cast<uint8_t>( val );
     562       32111 :       }
     563             : 
     564             :       //! Loads a type best represented as an unsigned long from the current top node
     565             :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     566             :                                           !std::is_same<T, bool>::value,
     567             :                                           !std::is_same<T, char>::value,
     568             :                                           !std::is_same<T, unsigned char>::value,
     569             :                                           sizeof(T) < sizeof(long long)> = traits::sfinae> inline
     570      138969 :       void loadValue( T & value )
     571             :       {
     572      138969 :         value = static_cast<T>( std::stoul( itsNodes.top().node->value() ) );
     573      138969 :       }
     574             : 
     575             :       //! Loads a type best represented as an unsigned long long from the current top node
     576             :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     577             :                                           !std::is_same<T, bool>::value,
     578             :                                           sizeof(T) >= sizeof(long long)> = traits::sfinae> inline
     579        1853 :       void loadValue( T & value )
     580             :       {
     581        1853 :         value = static_cast<T>( std::stoull( itsNodes.top().node->value() ) );
     582        1853 :       }
     583             : 
     584             :       //! Loads a type best represented as an int from the current top node
     585             :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     586             :                                           !std::is_same<T, char>::value,
     587             :                                           sizeof(T) <= sizeof(int)> = traits::sfinae> inline
     588     2285272 :       void loadValue( T & value )
     589             :       {
     590     2285272 :         value = static_cast<T>( std::stoi( itsNodes.top().node->value() ) );
     591     2285272 :       }
     592             : 
     593             :       //! Loads a type best represented as a long from the current top node
     594             :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     595             :                                           (sizeof(T) > sizeof(int)),
     596             :                                           sizeof(T) <= sizeof(long)> = traits::sfinae> inline
     597        1401 :       void loadValue( T & value )
     598             :       {
     599        1401 :         value = static_cast<T>( std::stol( itsNodes.top().node->value() ) );
     600        1401 :       }
     601             : 
     602             :       //! Loads a type best represented as a long long from the current top node
     603             :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     604             :                                           (sizeof(T) > sizeof(long)),
     605             :                                           sizeof(T) <= sizeof(long long)> = traits::sfinae> inline
     606             :       void loadValue( T & value )
     607             :       {
     608             :         value = static_cast<T>( std::stoll( itsNodes.top().node->value() ) );
     609             :       }
     610             : 
     611             :       //! Loads a type best represented as a float from the current top node
     612         800 :       void loadValue( float & value )
     613             :       {
     614             :         try
     615             :         {
     616         800 :           value = std::stof( itsNodes.top().node->value() );
     617             :         }
     618           0 :         catch( std::out_of_range const & )
     619             :         {
     620             :           // special case for denormalized values
     621           0 :           std::istringstream is( itsNodes.top().node->value() );
     622           0 :           is >> value;
     623           0 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     624           0 :             throw;
     625             :         }
     626         800 :       }
     627             : 
     628             :       //! Loads a type best represented as a double from the current top node
     629        1403 :       void loadValue( double & value )
     630             :       {
     631             :         try
     632             :         {
     633        1403 :           value = std::stod( itsNodes.top().node->value() );
     634             :         }
     635           0 :         catch( std::out_of_range const & )
     636             :         {
     637             :           // special case for denormalized values
     638           0 :           std::istringstream is( itsNodes.top().node->value() );
     639           0 :           is >> value;
     640           0 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     641           0 :             throw;
     642             :         }
     643        1403 :       }
     644             : 
     645             :       //! Loads a type best represented as a long double from the current top node
     646         300 :       void loadValue( long double & value )
     647             :       {
     648             :         try
     649             :         {
     650         300 :           value = std::stold( itsNodes.top().node->value() );
     651             :         }
     652           0 :         catch( std::out_of_range const & )
     653             :         {
     654             :           // special case for denormalized values
     655           0 :           std::istringstream is( itsNodes.top().node->value() );
     656           0 :           is >> value;
     657           0 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     658           0 :             throw;
     659             :         }
     660         300 :       }
     661             : 
     662             :       //! Loads a string from the current node from the current top node
     663             :       template<class CharT, class Traits, class Alloc> inline
     664       71839 :       void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
     665             :       {
     666      143678 :         std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
     667             : 
     668       71839 :         str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
     669             :                     std::istreambuf_iterator<CharT, Traits>() );
     670       71839 :       }
     671             : 
     672             :       //! Loads the size of the current top node
     673             :       template <class T> inline
     674       10300 :       void loadSize( T & value )
     675             :       {
     676       10300 :         value = getNumChildren( itsNodes.top().node );
     677       10300 :       }
     678             : 
     679             :     protected:
     680             :       //! Gets the number of children (usually interpreted as size) for the specified node
     681     3889782 :       static size_t getNumChildren( rapidxml::xml_node<> * node )
     682             :       {
     683     3889782 :         size_t size = 0;
     684     3889782 :         node = node->first_node(); // get first child
     685             : 
     686     8908705 :         while( node != nullptr )
     687             :         {
     688     5018926 :           ++size;
     689     5018926 :           node = node->next_sibling();
     690             :         }
     691             : 
     692     3889782 :         return size;
     693             :       }
     694             : 
     695             :       //! A struct that contains metadata about a node
     696             :       /*! Keeps track of some top level node, its number of
     697             :           remaining children, and the current active child node */
     698             :       struct NodeInfo
     699             :       {
     700     3878582 :         NodeInfo( rapidxml::xml_node<> * n = nullptr ) :
     701             :           node( n ),
     702     7757164 :           child( n->first_node() ),
     703     7757164 :           size( XMLInputArchive::getNumChildren( n ) ),
     704     3878582 :           name( nullptr )
     705     3878582 :         { }
     706             : 
     707             :         //! Advances to the next sibling node of the child
     708             :         /*! If this is the last sibling child will be null after calling */
     709     3874434 :         void advance()
     710             :         {
     711     3874434 :           if( size > 0 )
     712             :           {
     713     3874434 :             --size;
     714     3874434 :             child = child->next_sibling();
     715             :           }
     716     3874434 :         }
     717             : 
     718             :         //! Searches for a child with the given name in this node
     719             :         /*! @param searchName The name to search for (must be null terminated)
     720             :             @return The node if found, nullptr otherwise */
     721         900 :         rapidxml::xml_node<> * search( const char * searchName )
     722             :         {
     723         900 :           if( searchName )
     724             :           {
     725         900 :             size_t new_size = XMLInputArchive::getNumChildren( node );
     726         900 :             const size_t name_size = rapidxml::internal::measure( searchName );
     727             : 
     728        3100 :             for( auto new_child = node->first_node(); new_child != nullptr; new_child = new_child->next_sibling() )
     729             :             {
     730        3100 :               if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
     731             :               {
     732         900 :                 size = new_size;
     733         900 :                 child = new_child;
     734             : 
     735         900 :                 return new_child;
     736             :               }
     737        2200 :               --new_size;
     738             :             }
     739             :           }
     740             : 
     741           0 :           return nullptr;
     742             :         }
     743             : 
     744             :         //! Returns the actual name of the next child node, if it exists
     745             :         const char * getChildName() const
     746             :         {
     747             :           return child ? child->name() : nullptr;
     748             :         }
     749             : 
     750             :         rapidxml::xml_node<> * node;  //!< A pointer to this node
     751             :         rapidxml::xml_node<> * child; //!< A pointer to its current child
     752             :         size_t size;                  //!< The remaining number of children for this node
     753             :         const char * name;            //!< The NVP name for next child node
     754             :       }; // NodeInfo
     755             : 
     756             :       //! @}
     757             : 
     758             :     private:
     759             :       std::vector<char> itsData;       //!< The raw data loaded
     760             :       rapidxml::xml_document<> itsXML; //!< The XML document
     761             :       std::stack<NodeInfo> itsNodes;   //!< A stack of nodes read from the document
     762             :   };
     763             : 
     764             :   // ######################################################################
     765             :   // XMLArchive prologue and epilogue functions
     766             :   // ######################################################################
     767             : 
     768             :   // ######################################################################
     769             :   //! Prologue for NVPs for XML output archives
     770             :   /*! NVPs do not start or finish nodes - they just set up the names */
     771             :   template <class T> inline
     772      742617 :   void prologue( XMLOutputArchive &, NameValuePair<T> const & )
     773      742617 :   { }
     774             : 
     775             :   //! Prologue for NVPs for XML input archives
     776             :   template <class T> inline
     777      742717 :   void prologue( XMLInputArchive &, NameValuePair<T> const & )
     778      742717 :   { }
     779             : 
     780             :   // ######################################################################
     781             :   //! Epilogue for NVPs for XML output archives
     782             :   /*! NVPs do not start or finish nodes - they just set up the names */
     783             :   template <class T> inline
     784      742617 :   void epilogue( XMLOutputArchive &, NameValuePair<T> const & )
     785      742617 :   { }
     786             : 
     787             :   //! Epilogue for NVPs for XML input archives
     788             :   template <class T> inline
     789      742517 :   void epilogue( XMLInputArchive &, NameValuePair<T> const & )
     790      742517 :   { }
     791             : 
     792             :   // ######################################################################
     793             :   //! Prologue for deferred data for XML archives
     794             :   /*! Do nothing for the defer wrapper */
     795             :   template <class T> inline
     796        1000 :   void prologue( XMLOutputArchive &, DeferredData<T> const & )
     797        1000 :   { }
     798             : 
     799             :   //! Prologue for deferred data for XML archives
     800             :   template <class T> inline
     801        1000 :   void prologue( XMLInputArchive &, DeferredData<T> const & )
     802        1000 :   { }
     803             : 
     804             :   // ######################################################################
     805             :   //! Epilogue for deferred for XML archives
     806             :   /*! NVPs do not start or finish nodes - they just set up the names */
     807             :   template <class T> inline
     808        1000 :   void epilogue( XMLOutputArchive &, DeferredData<T> const & )
     809        1000 :   { }
     810             : 
     811             :   //! Epilogue for deferred for XML archives
     812             :   /*! Do nothing for the defer wrapper */
     813             :   template <class T> inline
     814        1000 :   void epilogue( XMLInputArchive &, DeferredData<T> const & )
     815        1000 :   { }
     816             : 
     817             :   // ######################################################################
     818             :   //! Prologue for SizeTags for XML output archives
     819             :   /*! SizeTags do not start or finish nodes */
     820             :   template <class T> inline
     821       10300 :   void prologue( XMLOutputArchive & ar, SizeTag<T> const & )
     822             :   {
     823       10300 :       if (ar.hasSizeAttributes())
     824             :       {
     825       10300 :           ar.appendAttribute("size", "dynamic");
     826             :       }
     827       10300 :   }
     828             : 
     829             :   template <class T> inline
     830       10300 :   void prologue( XMLInputArchive &, SizeTag<T> const & )
     831       10300 :   { }
     832             : 
     833             :   //! Epilogue for SizeTags for XML output archives
     834             :   /*! SizeTags do not start or finish nodes */
     835             :   template <class T> inline
     836       10300 :   void epilogue( XMLOutputArchive &, SizeTag<T> const & )
     837       10300 :   { }
     838             : 
     839             :   template <class T> inline
     840       10300 :   void epilogue( XMLInputArchive &, SizeTag<T> const & )
     841       10300 :   { }
     842             : 
     843             :   // ######################################################################
     844             :   //! Prologue for all other types for XML output archives (except minimal types)
     845             :   /*! Starts a new node, named either automatically or by some NVP,
     846             :       that may be given data by the type about to be archived
     847             : 
     848             :       Minimal types do not start or end nodes */
     849             :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
     850             :                                        traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
     851     3874234 :   void prologue( XMLOutputArchive & ar, T const & )
     852             :   {
     853     3874234 :     ar.startNode();
     854     3874234 :     ar.insertType<T>();
     855     3874234 :   }
     856             : 
     857             :   //! Prologue for all other types for XML input archives (except minimal types)
     858             :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
     859             :                                        traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
     860     3874734 :   void prologue( XMLInputArchive & ar, T const & )
     861             :   {
     862     3874734 :     ar.startNode();
     863     3874734 :   }
     864             : 
     865             :   // ######################################################################
     866             :   //! Epilogue for all other types other for XML output archives (except minimal types)
     867             :   /*! Finishes the node created in the prologue
     868             : 
     869             :       Minimal types do not start or end nodes */
     870             :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
     871             :                                        traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
     872     3874234 :   void epilogue( XMLOutputArchive & ar, T const & )
     873             :   {
     874     3874234 :     ar.finishNode();
     875     3874234 :   }
     876             : 
     877             :   //! Epilogue for all other types other for XML output archives (except minimal types)
     878             :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
     879             :                                        traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
     880     3874434 :   void epilogue( XMLInputArchive & ar, T const & )
     881             :   {
     882     3874434 :     ar.finishNode();
     883     3874434 :   }
     884             : 
     885             :   // ######################################################################
     886             :   // Common XMLArchive serialization functions
     887             :   // ######################################################################
     888             : 
     889             :   //! Saving NVP types to XML
     890             :   template <class T> inline
     891      742617 :   void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive & ar, NameValuePair<T> const & t )
     892             :   {
     893      742617 :     ar.setNextName( t.name );
     894      742617 :     ar( t.value );
     895      742617 :   }
     896             : 
     897             :   //! Loading NVP types from XML
     898             :   template <class T> inline
     899      742717 :   void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, NameValuePair<T> & t )
     900             :   {
     901      742717 :     ar.setNextName( t.name );
     902      742717 :     ar( t.value );
     903      742517 :   }
     904             : 
     905             :   // ######################################################################
     906             :   //! Saving SizeTags to XML
     907             :   template <class T> inline
     908       10300 :   void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive &, SizeTag<T> const & )
     909       10300 :   { }
     910             : 
     911             :   //! Loading SizeTags from XML
     912             :   template <class T> inline
     913       10300 :   void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, SizeTag<T> & st )
     914             :   {
     915       10300 :     ar.loadSize( st.size );
     916       10300 :   }
     917             : 
     918             :   // ######################################################################
     919             :   //! Saving for POD types to xml
     920             :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     921     2441517 :   void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, T const & t)
     922             :   {
     923     2441517 :     ar.saveValue( t );
     924     2441517 :   }
     925             : 
     926             :   //! Loading for POD types from xml
     927             :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     928     2441717 :   void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, T & t)
     929             :   {
     930     2441717 :     ar.loadValue( t );
     931     2441717 :   }
     932             : 
     933             :   // ######################################################################
     934             :   //! saving string to xml
     935             :   template<class CharT, class Traits, class Alloc> inline
     936       71839 :   void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
     937             :   {
     938       71839 :     ar.saveValue( str );
     939       71839 :   }
     940             : 
     941             :   //! loading string from xml
     942             :   template<class CharT, class Traits, class Alloc> inline
     943       71839 :   void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
     944             :   {
     945       71839 :     ar.loadValue( str );
     946       71839 :   }
     947             : } // namespace cereal
     948             : 
     949             : // register archives for polymorphic support
     950             : CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive)
     951             : CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive)
     952             : 
     953             : // tie input and output archives together
     954             : CEREAL_SETUP_ARCHIVE_TRAITS(cereal::XMLInputArchive, cereal::XMLOutputArchive)
     955             : 
     956             : #endif // CEREAL_ARCHIVES_XML_HPP_

Generated by: LCOV version 1.14