LCOV - code coverage report
Current view: top level - cereal/archives - xml.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 222 245 90.6 %
Date: 2017-02-12 13:57:59 Functions: 1652 1705 96.9 %

          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 cereal 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 RANDOLPH VOORHIES OR SHANE GRANT 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     4949437 :     inline bool isWhitespace( char c )
      62             :     {
      63     4949437 :       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             :       class Options
     105             :       {
     106             :         public:
     107             :           //! Default options
     108        3546 :           static Options Default(){ return Options(); }
     109             : 
     110             :           //! Default options with no indentation
     111             :           static Options NoIndent(){ return Options( std::numeric_limits<double>::max_digits10, false ); }
     112             : 
     113             :           //! Specify specific options for the XMLOutputArchive
     114             :           /*! @param precision The precision used for floating point numbers
     115             :               @param indent Whether to indent each line of XML
     116             :               @param outputType Whether to output the type of each serialized object as an attribute */
     117        3546 :           explicit Options( int precision = std::numeric_limits<double>::max_digits10,
     118             :                             bool indent = true,
     119             :                             bool outputType = false ) :
     120             :             itsPrecision( precision ),
     121             :             itsIndent( indent ),
     122        3546 :             itsOutputType( outputType ) { }
     123             : 
     124             :         private:
     125             :           friend class XMLOutputArchive;
     126             :           int itsPrecision;
     127             :           bool itsIndent;
     128             :           bool itsOutputType;
     129             :       };
     130             : 
     131             :       //! Construct, outputting to the provided stream upon destruction
     132             :       /*! @param stream  The stream to output to.  Note that XML is only guaranteed to flush
     133             :                          its output to the stream upon destruction.
     134             :           @param options The XML specific options to use.  See the Options struct
     135             :                          for the values of default parameters */
     136        3546 :       XMLOutputArchive( std::ostream & stream, Options const & options = Options::Default() ) :
     137             :         OutputArchive<XMLOutputArchive>(this),
     138             :         itsStream(stream),
     139        3546 :         itsOutputType( options.itsOutputType ),
     140        7092 :         itsIndent( options.itsIndent )
     141             :       {
     142             :         // rapidxml will delete all allocations when xml_document is cleared
     143        3546 :         auto node = itsXML.allocate_node( rapidxml::node_declaration );
     144        3546 :         node->append_attribute( itsXML.allocate_attribute( "version", "1.0" ) );
     145        3546 :         node->append_attribute( itsXML.allocate_attribute( "encoding", "utf-8" ) );
     146        3546 :         itsXML.append_node( node );
     147             : 
     148             :         // allocate root node
     149        3546 :         auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
     150        3546 :         itsXML.append_node( root );
     151        3546 :         itsNodes.emplace( root );
     152             : 
     153             :         // set attributes on the streams
     154        3546 :         itsStream << std::boolalpha;
     155        3546 :         itsStream.precision( options.itsPrecision );
     156        3546 :         itsOS << std::boolalpha;
     157        3546 :         itsOS.precision( options.itsPrecision );
     158        3546 :       }
     159             : 
     160             :       //! Destructor, flushes the XML
     161        3546 :       ~XMLOutputArchive() CEREAL_NOEXCEPT
     162        7092 :       {
     163        3546 :         const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting;
     164        3546 :         rapidxml::print( itsStream, itsXML, flags );
     165        3546 :         itsXML.clear();
     166        3546 :       }
     167             : 
     168             :       //! Saves some binary data, encoded as a base64 string, with an optional name
     169             :       /*! This can be called directly by users and it will automatically create a child node for
     170             :           the current XML node, populate it with a base64 encoded string, and optionally name
     171             :           it.  The node will be finished after it has been populated.  */
     172             :       void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
     173             :       {
     174             :         itsNodes.top().name = name;
     175             : 
     176             :         startNode();
     177             : 
     178             :         auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
     179             :         saveValue( base64string );
     180             : 
     181             :         if( itsOutputType )
     182             :           itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", "cereal binary data" ) );
     183             : 
     184             :         finishNode();
     185             :       };
     186             : 
     187             :       //! @}
     188             :       /*! @name Internal Functionality
     189             :           Functionality designed for use by those requiring control over the inner mechanisms of
     190             :           the XMLOutputArchive */
     191             :       //! @{
     192             : 
     193             :       //! Creates a new node that is a child of the node at the top of the stack
     194             :       /*! Nodes will be given a name that has either been pre-set by a name value pair,
     195             :           or generated based upon a counter unique to the parent node.  If you want to
     196             :           give a node a specific name, use setNextName prior to calling startNode.
     197             : 
     198             :           The node will then be pushed onto the node stack. */
     199     3796714 :       void startNode()
     200             :       {
     201             :         // generate a name for this new node
     202     7593428 :         const auto nameString = itsNodes.top().getValueName();
     203             : 
     204             :         // allocate strings for all of the data in the XML object
     205     3796714 :         auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
     206             : 
     207             :         // insert into the XML
     208     3796714 :         auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() );
     209     3796714 :         itsNodes.top().node->append_node( node );
     210     3796714 :         itsNodes.emplace( node );
     211     3796714 :       }
     212             : 
     213             :       //! Designates the most recently added node as finished
     214     3796714 :       void finishNode()
     215             :       {
     216     3796714 :         itsNodes.pop();
     217     3796714 :       }
     218             : 
     219             :       //! Sets the name for the next node created with startNode
     220      715189 :       void setNextName( const char * name )
     221             :       {
     222      715189 :         itsNodes.top().name = name;
     223      715189 :       }
     224             : 
     225             :       //! Saves some data, encoded as a string, into the current top level node
     226             :       /*! The data will be be named with the most recent name if one exists,
     227             :           otherwise it will be given some default delimited value that depends upon
     228             :           the parent node */
     229             :       template <class T> inline
     230     2474928 :       void saveValue( T const & value )
     231             :       {
     232     2474928 :         itsOS.clear(); itsOS.seekp( 0, std::ios::beg );
     233     2474928 :         itsOS << value << std::ends;
     234             : 
     235     4949856 :         auto strValue = itsOS.str();
     236             : 
     237             :         // itsOS.str() may contain data from previous calls after the first '\0' that was just inserted
     238             :         // and this data is counted in the length call. We make sure to remove that section so that the
     239             :         // whitespace validation is done properly
     240     2474928 :         strValue.resize(std::strlen(strValue.c_str()));
     241             : 
     242             :         // If the first or last character is a whitespace, add xml:space attribute
     243     2474928 :         const auto len = strValue.length();
     244     2474928 :         if ( len > 0 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 1] ) ) )
     245             :         {
     246          21 :           itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "xml:space", "preserve" ) );
     247             :         }
     248             : 
     249             :         // allocate strings for all of the data in the XML object
     250     2474928 :         auto dataPtr = itsXML.allocate_string(strValue.c_str(), strValue.length() + 1 );
     251             : 
     252             :         // insert into the XML
     253     2474928 :         itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) );
     254     2474928 :       }
     255             : 
     256             :       //! Overload for uint8_t prevents them from being serialized as characters
     257       31411 :       void saveValue( uint8_t const & value )
     258             :       {
     259       31411 :         saveValue( static_cast<uint32_t>( value ) );
     260       31411 :       }
     261             : 
     262             :       //! Overload for int8_t prevents them from being serialized as characters
     263       56715 :       void saveValue( int8_t const & value )
     264             :       {
     265       56715 :         saveValue( static_cast<int32_t>( value ) );
     266       56715 :       }
     267             : 
     268             :       //! Causes the type to be appended as an attribute to the most recently made node if output type is set to true
     269             :       template <class T> inline
     270     3796714 :       void insertType()
     271             :       {
     272     3796714 :         if( !itsOutputType )
     273     3796714 :           return;
     274             : 
     275             :         // generate a name for this new node
     276           0 :         const auto nameString = util::demangledName<T>();
     277             : 
     278             :         // allocate strings for all of the data in the XML object
     279           0 :         auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
     280             : 
     281           0 :         itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", namePtr ) );
     282             :       }
     283             : 
     284             :       //! Appends an attribute to the current top level node
     285        9700 :       void appendAttribute( const char * name, const char * value )
     286             :       {
     287        9700 :         auto namePtr =  itsXML.allocate_string( name );
     288        9700 :         auto valuePtr = itsXML.allocate_string( value );
     289        9700 :         itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
     290        9700 :       }
     291             : 
     292             :     protected:
     293             :       //! A struct that contains metadata about a node
     294             :       struct NodeInfo
     295             :       {
     296     3800260 :         NodeInfo( rapidxml::xml_node<> * n = nullptr,
     297             :                   const char * nm = nullptr ) :
     298             :           node( n ),
     299             :           counter( 0 ),
     300     3800260 :           name( nm )
     301     3800260 :         { }
     302             : 
     303             :         rapidxml::xml_node<> * node; //!< A pointer to this node
     304             :         size_t counter;              //!< The counter for naming child nodes
     305             :         const char * name;           //!< The name for the next child node
     306             : 
     307             :         //! Gets the name for the next child node created from this node
     308             :         /*! The name will be automatically generated using the counter if
     309             :             a name has not been previously set.  If a name has been previously
     310             :             set, that name will be returned only once */
     311     3796714 :         std::string getValueName()
     312             :         {
     313     3796714 :           if( name )
     314             :           {
     315      715189 :             auto n = name;
     316      715189 :             name = nullptr;
     317      715189 :             return {n};
     318             :           }
     319             :           else
     320     3081525 :             return "value" + std::to_string( counter++ ) + "\0";
     321             :         }
     322             :       }; // NodeInfo
     323             : 
     324             :       //! @}
     325             : 
     326             :     private:
     327             :       std::ostream & itsStream;        //!< The output stream
     328             :       rapidxml::xml_document<> itsXML; //!< The XML document
     329             :       std::stack<NodeInfo> itsNodes;   //!< A stack of nodes added to the document
     330             :       std::ostringstream itsOS;        //!< Used to format strings internally
     331             :       bool itsOutputType;              //!< Controls whether type information is printed
     332             :       bool itsIndent;                  //!< Controls whether indenting is used
     333             :   }; // XMLOutputArchive
     334             : 
     335             :   // ######################################################################
     336             :   //! An output archive designed to load data from XML
     337             :   /*! This archive uses RapidXML to build an in memory XML tree of the
     338             :       data in the stream it is given before loading any types serialized.
     339             : 
     340             :       As with the output XML archive, the preferred way to use this archive is in
     341             :       an RAII fashion, ensuring its destruction after all data has been read.
     342             : 
     343             :       Input XML should have been produced by the XMLOutputArchive.  Data can
     344             :       only be added to dynamically sized containers - the input archive will
     345             :       determine their size by looking at the number of child nodes.  Data that
     346             :       did not originate from an XMLOutputArchive is not officially supported,
     347             :       but may be possible to use if properly formatted.
     348             : 
     349             :       The XMLInputArchive does not require that nodes are loaded in the same
     350             :       order they were saved by XMLOutputArchive.  Using name value pairs (NVPs),
     351             :       it is possible to load in an out of order fashion or otherwise skip/select
     352             :       specific nodes to load.
     353             : 
     354             :       The default behavior of the input archive is to read sequentially starting
     355             :       with the first node and exploring its children.  When a given NVP does
     356             :       not match the read in name for a node, the archive will search for that
     357             :       node at the current level and load it if it exists.  After loading an out of
     358             :       order node, the archive will then proceed back to loading sequentially from
     359             :       its new position.
     360             : 
     361             :       Consider this simple example where loading of some data is skipped:
     362             : 
     363             :       @code{cpp}
     364             :       // imagine the input file has someData(1-9) saved in order at the top level node
     365             :       ar( someData1, someData2, someData3 );        // XML loads in the order it sees in the file
     366             :       ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
     367             :                                                     // match expected NVP name, so we search
     368             :                                                     // for the given NVP and load that value
     369             :       ar( someData7, someData8, someData9 );        // with no NVP given, loading resumes at its
     370             :                                                     // current location, proceeding sequentially
     371             :       @endcode
     372             : 
     373             :       \ingroup Archives */
     374             :   class XMLInputArchive : public InputArchive<XMLInputArchive>, public traits::TextArchive
     375             :   {
     376             :     public:
     377             :       /*! @name Common Functionality
     378             :           Common use cases for directly interacting with an XMLInputArchive */
     379             :       //! @{
     380             : 
     381             :       //! Construct, reading in from the provided stream
     382             :       /*! Reads in an entire XML document from some stream and parses it as soon
     383             :           as serialization starts
     384             : 
     385             :           @param stream The stream to read from.  Can be a stringstream or a file. */
     386        3646 :       XMLInputArchive( std::istream & stream ) :
     387             :         InputArchive<XMLInputArchive>( this ),
     388        3646 :         itsData( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() )
     389             :       {
     390             :         try
     391             :         {
     392        3646 :           itsData.push_back('\0'); // rapidxml will do terrible things without the data being null terminated
     393        3646 :           itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>( reinterpret_cast<char *>( itsData.data() ) );
     394             :         }
     395           0 :         catch( rapidxml::parse_error const & )
     396             :         {
     397             :           //std::cerr << "-----Original-----" << std::endl;
     398             :           //stream.seekg(0);
     399             :           //std::cout << std::string( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() ) << std::endl;
     400             : 
     401             :           //std::cerr << "-----Error-----" << std::endl;
     402             :           //std::cerr << e.what() << std::endl;
     403             :           //std::cerr << e.where<char>() << std::endl;
     404           0 :           throw Exception("XML Parsing failed - likely due to invalid characters or invalid naming");
     405             :         }
     406             : 
     407             :         // Parse the root
     408        3646 :         auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
     409        3646 :         if( root == nullptr )
     410           0 :           throw Exception("Could not detect cereal root node - likely due to empty or invalid input");
     411             :         else
     412        3646 :           itsNodes.emplace( root );
     413        3646 :       }
     414             : 
     415        3646 :       ~XMLInputArchive() CEREAL_NOEXCEPT = default;
     416             : 
     417             :       //! Loads some binary data, encoded as a base64 string, optionally specified by some name
     418             :       /*! This will automatically start and finish a node to load the data, and can be called directly by
     419             :           users.
     420             : 
     421             :           Note that this follows the same ordering rules specified in the class description in regards
     422             :           to loading in/out of order */
     423             :       void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
     424             :       {
     425             :         setNextName( name );
     426             :         startNode();
     427             : 
     428             :         std::string encoded;
     429             :         loadValue( encoded );
     430             : 
     431             :         auto decoded = base64::decode( encoded );
     432             : 
     433             :         if( size != decoded.size() )
     434             :           throw Exception("Decoded binary data size does not match specified size");
     435             : 
     436             :         std::memcpy( data, decoded.data(), decoded.size() );
     437             : 
     438             :         finishNode();
     439             :       };
     440             : 
     441             :       //! @}
     442             :       /*! @name Internal Functionality
     443             :           Functionality designed for use by those requiring control over the inner mechanisms of
     444             :           the XMLInputArchive */
     445             :       //! @{
     446             : 
     447             :       //! Prepares to start reading the next node
     448             :       /*! This places the next node to be parsed onto the nodes stack.
     449             : 
     450             :           By default our strategy is to start with the document root node and then
     451             :           recursively iterate through all children in the order they show up in the document.
     452             :           We don't need to know NVPs do to this; we'll just blindly load in the order things appear in.
     453             : 
     454             :           We check to see if the specified NVP matches what the next automatically loaded node is.  If they
     455             :           match, we just continue as normal, going in order.  If they don't match, we attempt to find a node
     456             :           named after the NVP that is being loaded.  If that NVP does not exist, we throw an exception. */
     457     3797214 :       void startNode()
     458             :       {
     459     3797214 :         auto next = itsNodes.top().child; // By default we would move to the next child node
     460     3797214 :         auto const expectedName = itsNodes.top().name; // this is the expected name from the NVP, if provided
     461             : 
     462             :         // If we were given an NVP name, look for it in the current level of the document.
     463             :         //    We only need to do this if either we have exhausted the siblings of the current level or
     464             :         //    the NVP name does not match the name of the node we would normally read next
     465     3797214 :         if( expectedName && ( next == nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
     466             :         {
     467         900 :           next = itsNodes.top().search( expectedName );
     468             : 
     469         900 :           if( next == nullptr )
     470           0 :             throw Exception("XML Parsing failed - provided NVP (" + std::string(expectedName) + ") not found");
     471             :         }
     472             : 
     473     3797214 :         itsNodes.emplace( next );
     474     3797214 :       }
     475             : 
     476             :       //! Finishes reading the current node
     477     3796914 :       void finishNode()
     478             :       {
     479             :         // remove current
     480     3796914 :         itsNodes.pop();
     481             : 
     482             :         // advance parent
     483     3796914 :         itsNodes.top().advance();
     484             : 
     485             :         // Reset name
     486     3796914 :         itsNodes.top().name = nullptr;
     487     3796914 :       }
     488             : 
     489             :       //! Retrieves the current node name
     490             :       //! will return @c nullptr if the node does not have a name
     491             :       const char * getNodeName() const
     492             :       {
     493             :         return itsNodes.top().getChildName();
     494             :       }
     495             : 
     496             :       //! Sets the name for the next node created with startNode
     497      715289 :       void setNextName( const char * name )
     498             :       {
     499      715289 :         itsNodes.top().name = name;
     500      715289 :       }
     501             : 
     502             :       //! Loads a bool from the current top node
     503             :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     504             :                                           std::is_same<T, bool>::value> = traits::sfinae> inline
     505       11100 :       void loadValue( T & value )
     506             :       {
     507       22200 :         std::istringstream is( itsNodes.top().node->value() );
     508       11100 :         is.setf( std::ios::boolalpha );
     509       11100 :         is >> value;
     510       11100 :       }
     511             : 
     512             :       //! Loads a char (signed or unsigned) from the current top node
     513             :       template <class T, traits::EnableIf<std::is_integral<T>::value,
     514             :                                           !std::is_same<T, bool>::value,
     515             :                                           sizeof(T) == sizeof(char)> = traits::sfinae> inline
     516         111 :       void loadValue( T & value )
     517             :       {
     518         111 :         value = *reinterpret_cast<T*>( itsNodes.top().node->value() );
     519         111 :       }
     520             : 
     521             :       //! Load an int8_t from the current top node (ensures we parse entire number)
     522       56715 :       void loadValue( int8_t & value )
     523             :       {
     524       56715 :         int32_t val; loadValue( val ); value = static_cast<int8_t>( val );
     525       56715 :       }
     526             : 
     527             :       //! Load a uint8_t from the current top node (ensures we parse entire number)
     528       31511 :       void loadValue( uint8_t & value )
     529             :       {
     530       31511 :         uint32_t val; loadValue( val ); value = static_cast<uint8_t>( val );
     531       31511 :       }
     532             : 
     533             :       //! Loads a type best represented as an unsigned long from the current top node
     534             :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     535             :                                           !std::is_same<T, bool>::value,
     536             :                                           !std::is_same<T, char>::value,
     537             :                                           !std::is_same<T, unsigned char>::value,
     538             :                                           sizeof(T) < sizeof(long long)> = traits::sfinae> inline
     539      126198 :       void loadValue( T & value )
     540             :       {
     541      126198 :         value = static_cast<T>( std::stoul( itsNodes.top().node->value() ) );
     542      126198 :       }
     543             : 
     544             :       //! Loads a type best represented as an unsigned long long from the current top node
     545             :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     546             :                                           !std::is_same<T, bool>::value,
     547             :                                           sizeof(T) >= sizeof(long long)> = traits::sfinae> inline
     548        1556 :       void loadValue( T & value )
     549             :       {
     550        1556 :         value = static_cast<T>( std::stoull( itsNodes.top().node->value() ) );
     551        1556 :       }
     552             : 
     553             :       //! Loads a type best represented as an int from the current top node
     554             :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     555             :                                           !std::is_same<T, char>::value,
     556             :                                           sizeof(T) <= sizeof(int)> = traits::sfinae> inline
     557     2271600 :       void loadValue( T & value )
     558             :       {
     559     2271600 :         value = static_cast<T>( std::stoi( itsNodes.top().node->value() ) );
     560     2271600 :       }
     561             : 
     562             :       //! Loads a type best represented as a long from the current top node
     563             :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     564             :                                           (sizeof(T) > sizeof(int)),
     565             :                                           sizeof(T) <= sizeof(long)> = traits::sfinae> inline
     566        1100 :       void loadValue( T & value )
     567             :       {
     568        1100 :         value = static_cast<T>( std::stol( itsNodes.top().node->value() ) );
     569        1100 :       }
     570             : 
     571             :       //! Loads a type best represented as a long long from the current top node
     572             :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     573             :                                           (sizeof(T) > sizeof(long)),
     574             :                                           sizeof(T) <= sizeof(long long)> = traits::sfinae> inline
     575             :       void loadValue( T & value )
     576             :       {
     577             :         value = static_cast<T>( std::stoll( itsNodes.top().node->value() ) );
     578             :       }
     579             : 
     580             :       //! Loads a type best represented as a float from the current top node
     581         600 :       void loadValue( float & value )
     582             :       {
     583             :         try
     584             :         {
     585         600 :           value = std::stof( itsNodes.top().node->value() );
     586             :         }
     587           0 :         catch( std::out_of_range const & )
     588             :         {
     589             :           // special case for denormalized values
     590           0 :           std::istringstream is( itsNodes.top().node->value() );
     591           0 :           is >> value;
     592           0 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     593           0 :             throw;
     594             :         }
     595         600 :       }
     596             : 
     597             :       //! Loads a type best represented as a double from the current top node
     598        1101 :       void loadValue( double & value )
     599             :       {
     600             :         try
     601             :         {
     602        1101 :           value = std::stod( itsNodes.top().node->value() );
     603             :         }
     604           0 :         catch( std::out_of_range const & )
     605             :         {
     606             :           // special case for denormalized values
     607           0 :           std::istringstream is( itsNodes.top().node->value() );
     608           0 :           is >> value;
     609           0 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     610           0 :             throw;
     611             :         }
     612        1101 :       }
     613             : 
     614             :       //! Loads a type best represented as a long double from the current top node
     615         300 :       void loadValue( long double & value )
     616             :       {
     617             :         try
     618             :         {
     619         300 :           value = std::stold( itsNodes.top().node->value() );
     620             :         }
     621           0 :         catch( std::out_of_range const & )
     622             :         {
     623             :           // special case for denormalized values
     624           0 :           std::istringstream is( itsNodes.top().node->value() );
     625           0 :           is >> value;
     626           0 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     627           0 :             throw;
     628             :         }
     629         300 :       }
     630             : 
     631             :       //! Loads a string from the current node from the current top node
     632             :       template<class CharT, class Traits, class Alloc> inline
     633       61462 :       void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
     634             :       {
     635      122924 :         std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
     636             : 
     637       61462 :         str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
     638             :                     std::istreambuf_iterator<CharT, Traits>() );
     639       61462 :       }
     640             : 
     641             :       //! Loads the size of the current top node
     642             :       template <class T> inline
     643        9700 :       void loadSize( T & value )
     644             :       {
     645        9700 :         value = getNumChildren( itsNodes.top().node );
     646        9700 :       }
     647             : 
     648             :     protected:
     649             :       //! Gets the number of children (usually interpreted as size) for the specified node
     650     3811460 :       static size_t getNumChildren( rapidxml::xml_node<> * node )
     651             :       {
     652     3811460 :         size_t size = 0;
     653     3811460 :         node = node->first_node(); // get first child
     654             : 
     655    13673068 :         while( node != nullptr )
     656             :         {
     657     4930804 :           ++size;
     658     4930804 :           node = node->next_sibling();
     659             :         }
     660             : 
     661     3811460 :         return size;
     662             :       }
     663             : 
     664             :       //! A struct that contains metadata about a node
     665             :       /*! Keeps track of some top level node, its number of
     666             :           remaining children, and the current active child node */
     667             :       struct NodeInfo
     668             :       {
     669     3800860 :         NodeInfo( rapidxml::xml_node<> * n = nullptr ) :
     670             :           node( n ),
     671     3800860 :           child( n->first_node() ),
     672     3800860 :           size( XMLInputArchive::getNumChildren( n ) ),
     673    11402580 :           name( nullptr )
     674     3800860 :         { }
     675             : 
     676             :         //! Advances to the next sibling node of the child
     677             :         /*! If this is the last sibling child will be null after calling */
     678     3796914 :         void advance()
     679             :         {
     680     3796914 :           if( size > 0 )
     681             :           {
     682     3796914 :             --size;
     683     3796914 :             child = child->next_sibling();
     684             :           }
     685     3796914 :         }
     686             : 
     687             :         //! Searches for a child with the given name in this node
     688             :         /*! @param searchName The name to search for (must be null terminated)
     689             :             @return The node if found, nullptr otherwise */
     690         900 :         rapidxml::xml_node<> * search( const char * searchName )
     691             :         {
     692         900 :           if( searchName )
     693             :           {
     694         900 :             size_t new_size = XMLInputArchive::getNumChildren( node );
     695         900 :             const size_t name_size = rapidxml::internal::measure( searchName );
     696             : 
     697        3100 :             for( auto new_child = node->first_node(); new_child != nullptr; new_child = new_child->next_sibling() )
     698             :             {
     699        3100 :               if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
     700             :               {
     701         900 :                 size = new_size;
     702         900 :                 child = new_child;
     703             : 
     704         900 :                 return new_child;
     705             :               }
     706        2200 :               --new_size;
     707             :             }
     708             :           }
     709             : 
     710           0 :           return nullptr;
     711             :         }
     712             : 
     713             :         //! Returns the actual name of the next child node, if it exists
     714             :         const char * getChildName() const
     715             :         {
     716             :           return child ? child->name() : nullptr;
     717             :         }
     718             : 
     719             :         rapidxml::xml_node<> * node;  //!< A pointer to this node
     720             :         rapidxml::xml_node<> * child; //!< A pointer to its current child
     721             :         size_t size;                  //!< The remaining number of children for this node
     722             :         const char * name;            //!< The NVP name for next child node
     723             :       }; // NodeInfo
     724             : 
     725             :       //! @}
     726             : 
     727             :     private:
     728             :       std::vector<char> itsData;       //!< The raw data loaded
     729             :       rapidxml::xml_document<> itsXML; //!< The XML document
     730             :       std::stack<NodeInfo> itsNodes;   //!< A stack of nodes read from the document
     731             :   };
     732             : 
     733             :   // ######################################################################
     734             :   // XMLArchive prologue and epilogue functions
     735             :   // ######################################################################
     736             : 
     737             :   // ######################################################################
     738             :   //! Prologue for NVPs for XML output archives
     739             :   /*! NVPs do not start or finish nodes - they just set up the names */
     740             :   template <class T> inline
     741      715189 :   void prologue( XMLOutputArchive &, NameValuePair<T> const & )
     742      715189 :   { }
     743             : 
     744             :   //! Prologue for NVPs for XML input archives
     745             :   template <class T> inline
     746      715289 :   void prologue( XMLInputArchive &, NameValuePair<T> const & )
     747      715289 :   { }
     748             : 
     749             :   // ######################################################################
     750             :   //! Epilogue for NVPs for XML output archives
     751             :   /*! NVPs do not start or finish nodes - they just set up the names */
     752             :   template <class T> inline
     753      715189 :   void epilogue( XMLOutputArchive &, NameValuePair<T> const & )
     754      715189 :   { }
     755             : 
     756             :   //! Epilogue for NVPs for XML input archives
     757             :   template <class T> inline
     758      715089 :   void epilogue( XMLInputArchive &, NameValuePair<T> const & )
     759      715089 :   { }
     760             : 
     761             :   // ######################################################################
     762             :   //! Prologue for SizeTags for XML output archives
     763             :   /*! SizeTags do not start or finish nodes */
     764             :   template <class T> inline
     765        9700 :   void prologue( XMLOutputArchive & ar, SizeTag<T> const & )
     766             :   {
     767        9700 :     ar.appendAttribute( "size", "dynamic" );
     768        9700 :   }
     769             : 
     770             :   template <class T> inline
     771        9700 :   void prologue( XMLInputArchive &, SizeTag<T> const & )
     772        9700 :   { }
     773             : 
     774             :   //! Epilogue for SizeTags for XML output archives
     775             :   /*! SizeTags do not start or finish nodes */
     776             :   template <class T> inline
     777        9700 :   void epilogue( XMLOutputArchive &, SizeTag<T> const & )
     778        9700 :   { }
     779             : 
     780             :   template <class T> inline
     781        9700 :   void epilogue( XMLInputArchive &, SizeTag<T> const & )
     782        9700 :   { }
     783             : 
     784             :   // ######################################################################
     785             :   //! Prologue for all other types for XML output archives (except minimal types)
     786             :   /*! Starts a new node, named either automatically or by some NVP,
     787             :       that may be given data by the type about to be archived
     788             : 
     789             :       Minimal types do not start or end nodes */
     790             :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
     791             :                                        traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
     792     3796714 :   void prologue( XMLOutputArchive & ar, T const & )
     793             :   {
     794     3796714 :     ar.startNode();
     795     3796714 :     ar.insertType<T>();
     796     3796714 :   }
     797             : 
     798             :   //! Prologue for all other types for XML input archives (except minimal types)
     799             :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
     800             :                                        traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
     801     3797214 :   void prologue( XMLInputArchive & ar, T const & )
     802             :   {
     803     3797214 :     ar.startNode();
     804     3797214 :   }
     805             : 
     806             :   // ######################################################################
     807             :   //! Epilogue for all other types other for XML output archives (except minimal types)
     808             :   /*! Finishes the node created in the prologue
     809             : 
     810             :       Minimal types do not start or end nodes */
     811             :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
     812             :                                        traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
     813     3796714 :   void epilogue( XMLOutputArchive & ar, T const & )
     814             :   {
     815     3796714 :     ar.finishNode();
     816     3796714 :   }
     817             : 
     818             :   //! Epilogue for all other types other for XML output archives (except minimal types)
     819             :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
     820             :                                        traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
     821     3796914 :   void epilogue( XMLInputArchive & ar, T const & )
     822             :   {
     823     3796914 :     ar.finishNode();
     824     3796914 :   }
     825             : 
     826             :   // ######################################################################
     827             :   // Common XMLArchive serialization functions
     828             :   // ######################################################################
     829             : 
     830             :   //! Saving NVP types to XML
     831             :   template <class T> inline
     832      715189 :   void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive & ar, NameValuePair<T> const & t )
     833             :   {
     834      715189 :     ar.setNextName( t.name );
     835      715189 :     ar( t.value );
     836      715189 :   }
     837             : 
     838             :   //! Loading NVP types from XML
     839             :   template <class T> inline
     840      715289 :   void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, NameValuePair<T> & t )
     841             :   {
     842      715289 :     ar.setNextName( t.name );
     843      715289 :     ar( t.value );
     844      715089 :   }
     845             : 
     846             :   // ######################################################################
     847             :   //! Saving SizeTags to XML
     848             :   template <class T> inline
     849        9700 :   void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive &, SizeTag<T> const & )
     850        9700 :   { }
     851             : 
     852             :   //! Loading SizeTags from XML
     853             :   template <class T> inline
     854        9700 :   void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, SizeTag<T> & st )
     855             :   {
     856        9700 :     ar.loadSize( st.size );
     857        9700 :   }
     858             : 
     859             :   // ######################################################################
     860             :   //! Saving for POD types to xml
     861             :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     862     2413466 :   void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, T const & t)
     863             :   {
     864     2413466 :     ar.saveValue( t );
     865     2413466 :   }
     866             : 
     867             :   //! Loading for POD types from xml
     868             :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     869     2413666 :   void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, T & t)
     870             :   {
     871     2413666 :     ar.loadValue( t );
     872     2413666 :   }
     873             : 
     874             :   // ######################################################################
     875             :   //! saving string to xml
     876             :   template<class CharT, class Traits, class Alloc> inline
     877       61462 :   void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
     878             :   {
     879       61462 :     ar.saveValue( str );
     880       61462 :   }
     881             : 
     882             :   //! loading string from xml
     883             :   template<class CharT, class Traits, class Alloc> inline
     884       61462 :   void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
     885             :   {
     886       61462 :     ar.loadValue( str );
     887       61462 :   }
     888             : } // namespace cereal
     889             : 
     890             : // register archives for polymorphic support
     891             : CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive)
     892             : CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive)
     893             : 
     894             : // tie input and output archives together
     895             : CEREAL_SETUP_ARCHIVE_TRAITS(cereal::XMLInputArchive, cereal::XMLOutputArchive)
     896             : 
     897             : #endif // CEREAL_ARCHIVES_XML_HPP_

Generated by: LCOV version 1.11