LCOV - code coverage report
Current view: top level - cereal/archives - xml.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2197 2220 99.0 %
Date: 2019-10-22 20:09:23 Functions: 2094 2146 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 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    10045817 :     inline bool isWhitespace( char c )
      62             :     {
      63    10045817 :       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        7496 :           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        7496 :           explicit Options( int precision_ = std::numeric_limits<double>::max_digits10,
     118             :                             bool indent_ = true,
     119             :                             bool outputType_ = false,
     120        7496 :                             bool sizeAttributes_ = true ) :
     121             :             itsPrecision( precision_ ),
     122             :             itsIndent( indent_ ),
     123             :             itsOutputType( outputType_ ),
     124        7496 :             itsSizeAttributes( sizeAttributes_ )
     125        7496 :           { }
     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        7496 :       XMLOutputArchive( std::ostream & stream, Options const & options = Options::Default() ) :
     164             :         OutputArchive<XMLOutputArchive>(this),
     165             :         itsStream(stream),
     166        7496 :         itsOutputType( options.itsOutputType ),
     167        7496 :         itsIndent( options.itsIndent ),
     168        7496 :         itsSizeAttributes(options.itsSizeAttributes)
     169             :       {
     170             :         // rapidxml will delete all allocations when xml_document is cleared
     171        7496 :         auto node = itsXML.allocate_node( rapidxml::node_declaration );
     172        7496 :         node->append_attribute( itsXML.allocate_attribute( "version", "1.0" ) );
     173        7496 :         node->append_attribute( itsXML.allocate_attribute( "encoding", "utf-8" ) );
     174        7496 :         itsXML.append_node( node );
     175             : 
     176             :         // allocate root node
     177        7496 :         auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
     178        7496 :         itsXML.append_node( root );
     179        7496 :         itsNodes.emplace( root );
     180             : 
     181             :         // set attributes on the streams
     182        7496 :         itsStream << std::boolalpha;
     183        7496 :         itsStream.precision( options.itsPrecision );
     184        7496 :         itsOS << std::boolalpha;
     185        7496 :         itsOS.precision( options.itsPrecision );
     186        7496 :       }
     187             : 
     188             :       //! Destructor, flushes the XML
     189        7496 :       ~XMLOutputArchive() CEREAL_NOEXCEPT
     190        7496 :       {
     191        7496 :         const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting;
     192        7496 :         rapidxml::print( itsStream, itsXML, flags );
     193        7496 :         itsXML.clear();
     194        7496 :       }
     195           0 : 
     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           0 :       void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
     201        7496 :       {
     202        7496 :         itsNodes.top().name = name;
     203        7496 : 
     204        7496 :         startNode();
     205        7496 : 
     206        7496 :         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             :       void startNode()
     228             :       {
     229             :         // generate a name for this new node
     230             :         const auto nameString = itsNodes.top().getValueName();
     231             : 
     232             :         // allocate strings for all of the data in the XML object
     233             :         auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
     234             : 
     235             :         // insert into the XML
     236             :         auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() );
     237             :         itsNodes.top().node->append_node( node );
     238             :         itsNodes.emplace( node );
     239     7741724 :       }
     240             : 
     241             :       //! Designates the most recently added node as finished
     242    15483448 :       void finishNode()
     243             :       {
     244             :         itsNodes.pop();
     245     7741724 :       }
     246             : 
     247             :       //! Sets the name for the next node created with startNode
     248     7741724 :       void setNextName( const char * name )
     249     7741724 :       {
     250     7741724 :         itsNodes.top().name = name;
     251     7741724 :       }
     252             : 
     253             :       //! Saves some data, encoded as a string, into the current top level node
     254     7741724 :       /*! 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     7741724 :           the parent node */
     257     7741724 :       template <class T> inline
     258             :       void saveValue( T const & value )
     259             :       {
     260     1482980 :         itsOS.clear(); itsOS.seekp( 0, std::ios::beg );
     261             :         itsOS << value << std::ends;
     262     1482980 : 
     263     1482980 :         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             :         strValue.resize(std::strlen(strValue.c_str()));
     269             : 
     270     5023332 :         // If the first or last character is a whitespace, add xml:space attribute
     271             :         const auto len = strValue.length();
     272     5023332 :         if ( len > 0 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 1] ) ) )
     273     5023332 :         {
     274             :           itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "xml:space", "preserve" ) );
     275    10046664 :         }
     276             : 
     277             :         // allocate strings for all of the data in the XML object
     278             :         auto dataPtr = itsXML.allocate_string(strValue.c_str(), strValue.length() + 1 );
     279             : 
     280     5023332 :         // insert into the XML
     281             :         itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) );
     282             :       }
     283     5023332 : 
     284     5023332 :       //! Overload for uint8_t prevents them from being serialized as characters
     285             :       void saveValue( uint8_t const & value )
     286          43 :       {
     287             :         saveValue( static_cast<uint32_t>( value ) );
     288             :       }
     289             : 
     290     5023332 :       //! Overload for int8_t prevents them from being serialized as characters
     291             :       void saveValue( int8_t const & value )
     292             :       {
     293     5023332 :         saveValue( static_cast<int32_t>( value ) );
     294     5023332 :       }
     295      591191 : 
     296             :       //! Causes the type to be appended as an attribute to the most recently made node if output type is set to true
     297      591191 :       template <class T> inline
     298      591191 :       void insertType()
     299             :       {
     300     1182382 :         if( !itsOutputType )
     301             :           return;
     302             : 
     303             :         // generate a name for this new node
     304             :         const auto nameString = util::demangledName<T>();
     305      591191 : 
     306             :         // allocate strings for all of the data in the XML object
     307             :         auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
     308      591191 : 
     309      591191 :         itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", namePtr ) );
     310     3681400 :       }
     311          13 : 
     312     3681400 :       //! Appends an attribute to the current top level node
     313     3681400 :       void appendAttribute( const char * name, const char * value )
     314             :       {
     315      591191 :         auto namePtr =  itsXML.allocate_string( name );
     316           0 :         auto valuePtr = itsXML.allocate_string( value );
     317             :         itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
     318      591191 :       }
     319      591191 : 
     320     1530864 :       bool hasSizeAttributes() const { return itsSizeAttributes; }
     321           0 : 
     322     1530864 :     protected:
     323     1795864 :       //! A struct that contains metadata about a node
     324             :       struct NodeInfo
     325     3326728 :       {
     326      265000 :         NodeInfo( rapidxml::xml_node<> * n = nullptr,
     327             :                   const char * nm = nullptr ) :
     328             :           node( n ),
     329           0 :           counter( 0 ),
     330     1530864 :           name( nm )
     331             :         { }
     332           0 : 
     333     1530864 :         rapidxml::xml_node<> * node; //!< A pointer to this node
     334     1530864 :         size_t counter;              //!< The counter for naming child nodes
     335             :         const char * name;           //!< The name for the next child node
     336      262800 : 
     337             :         //! Gets the name for the next child node created from this node
     338      262800 :         /*! The name will be automatically generated using the counter if
     339      262800 :             a name has not been previously set.  If a name has been previously
     340     1530864 :             set, that name will be returned only once */
     341             :         std::string getValueName()
     342           0 :         {
     343     1530864 :           if( name )
     344     1530864 :           {
     345      204842 :             auto n = name;
     346             :             name = nullptr;
     347      206442 :             return {n};
     348      204842 :           }
     349      263400 :           else
     350      411284 :             return "value" + std::to_string( counter++ ) + "\0";
     351      261800 :         }
     352      261800 :       }; // NodeInfo
     353             : 
     354             :       //! @}
     355      204842 : 
     356             :     private:
     357             :       std::ostream & itsStream;        //!< The output stream
     358      204842 :       rapidxml::xml_document<> itsXML; //!< The XML document
     359      204842 :       std::stack<NodeInfo> itsNodes;   //!< A stack of nodes added to the document
     360      306620 :       std::ostringstream itsOS;        //!< Used to format strings internally
     361           0 :       bool itsOutputType;              //!< Controls whether type information is printed
     362      577220 :       bool itsIndent;                  //!< Controls whether indenting is used
     363      306620 :       bool itsSizeAttributes;          //!< Controls whether lists have a size attribute
     364      270600 :   }; // XMLOutputArchive
     365      475442 : 
     366           0 :   // ######################################################################
     367             :   //! An output archive designed to load data from XML
     368      204842 :   /*! This archive uses RapidXML to build an in memory XML tree of the
     369      204842 :       data in the stream it is given before loading any types serialized.
     370      108565 : 
     371           0 :       As with the output XML archive, the preferred way to use this archive is in
     372      108565 :       an RAII fashion, ensuring its destruction after all data has been read.
     373      129169 : 
     374           0 :       Input XML should have been produced by the XMLOutputArchive.  Data can
     375     2582134 :       only be added to dynamically sized containers - the input archive will
     376       20604 :       determine their size by looking at the number of child nodes.  Data that
     377     2344400 :       did not originate from an XMLOutputArchive is not officially supported,
     378     2384400 :       but may be possible to use if properly formatted.
     379           0 : 
     380      148565 :       The XMLInputArchive does not require that nodes are loaded in the same
     381       40000 :       order they were saved by XMLOutputArchive.  Using name value pairs (NVPs),
     382           0 :       it is possible to load in an out of order fashion or otherwise skip/select
     383      108565 :       specific nodes to load.
     384      108565 : 
     385      928440 :       The default behavior of the input archive is to read sequentially starting
     386       22032 :       with the first node and exploring its children.  When a given NVP does
     387      928440 :       not match the read in name for a node, the archive will search for that
     388      953244 :       node at the current level and load it if it exists.  After loading an out of
     389       22004 :       order node, the archive will then proceed back to loading sequentially from
     390      114565 :       its new position.
     391        6000 : 
     392           0 :       Consider this simple example where loading of some data is skipped:
     393      108565 : 
     394      111765 :       @code{cpp}
     395       25636 :       // imagine the input file has someData(1-9) saved in order at the top level node
     396           0 :       ar( someData1, someData2, someData3 );        // XML loads in the order it sees in the file
     397       83858 :       ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
     398       62840 :                                                     // match expected NVP name, so we search
     399       83026 :                                                     // for the given NVP and load that value
     400      146698 :       ar( someData7, someData8, someData9 );        // with no NVP given, loading resumes at its
     401       64808 :                                                     // current location, proceeding sequentially
     402       21604 :       @endcode
     403       78487 : 
     404        5400 :       \ingroup Archives */
     405       98523 :   class XMLInputArchive : public InputArchive<XMLInputArchive>, public traits::TextArchive
     406       73087 :   {
     407        5600 :     public:
     408       25036 :       /*! @name Common Functionality
     409       24236 :           Common use cases for directly interacting with an XMLInputArchive */
     410     2669620 :       //! @{
     411        2604 : 
     412     2689020 :       //! Construct, reading in from the provided stream
     413     2668624 :       /*! Reads in an entire XML document from some stream and parses it as soon
     414       26004 :           as serialization starts
     415       43636 : 
     416        2800 :           @param stream The stream to read from.  Can be a stringstream or a file. */
     417        2800 :       XMLInputArchive( std::istream & stream ) :
     418       24836 :         InputArchive<XMLInputArchive>( this ),
     419       22436 :         itsData( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() )
     420       28834 :       {
     421           0 :         try
     422       26434 :         {
     423      105169 :           itsData.push_back('\0'); // rapidxml will do terrible things without the data being null terminated
     424         613 :           itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>( reinterpret_cast<char *>( itsData.data() ) );
     425      152403 :         }
     426       79348 :         catch( rapidxml::parse_error const & )
     427       26613 :         {
     428       20800 :           //std::cerr << "-----Original-----" << std::endl;
     429        2800 :           //stream.seekg(0);
     430       29234 :           //std::cout << std::string( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() ) << std::endl;
     431           0 : 
     432           0 :           //std::cerr << "-----Error-----" << std::endl;
     433       26434 :           //std::cerr << e.what() << std::endl;
     434       26434 :           //std::cerr << e.where<char>() << std::endl;
     435          40 :           throw Exception("XML Parsing failed - likely due to invalid characters or invalid naming");
     436        2317 :         }
     437         455 : 
     438      183557 :         // Parse the root
     439        2732 :         auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
     440      210649 :         if( root == nullptr )
     441      181200 :           throw Exception("Could not detect cereal root node - likely due to empty or invalid input");
     442        2600 :         else
     443       29034 :           itsNodes.emplace( root );
     444       26434 :       }
     445        1400 : 
     446           0 :       ~XMLInputArchive() CEREAL_NOEXCEPT = default;
     447        1400 : 
     448        1402 :       //! Loads some binary data, encoded as a base64 string, optionally specified by some name
     449        2222 :       /*! This will automatically start and finish a node to load the data, and can be called directly by
     450        3406 :           users.
     451        4224 : 
     452        2826 :           Note that this follows the same ordering rules specified in the class description in regards
     453       24404 :           to loading in/out of order */
     454        3200 :       void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
     455       44000 :       {
     456       34600 :         setNextName( name );
     457        9200 :         startNode();
     458       10600 : 
     459       23400 :         std::string encoded;
     460      153404 :         loadValue( encoded );
     461           2 : 
     462      142343 :         auto decoded = base64::decode( encoded );
     463      132006 : 
     464        3743 :         if( size != decoded.size() )
     465        4969 :           throw Exception("Decoded binary data size does not match specified size");
     466        2032 : 
     467         802 :         std::memcpy( data, decoded.data(), decoded.size() );
     468     2880200 : 
     469     2879230 :         finishNode();
     470       15030 :       }
     471           0 : 
     472     2892400 :       //! @}
     473     2897834 :       /*! @name Internal Functionality
     474        7402 :           Functionality designed for use by those requiring control over the inner mechanisms of
     475       28634 :           the XMLInputArchive */
     476       66036 :       //! @{
     477        2604 : 
     478       41614 :       //! Prepares to start reading the next node
     479       48816 :       /*! This places the next node to be parsed onto the nodes stack.
     480         829 : 
     481         800 :           By default our strategy is to start with the document root node and then
     482         641 :           recursively iterate through all children in the order they show up in the document.
     483     2875614 :           We don't need to know NVPs do to this; we'll just blindly load in the order things appear in.
     484         218 : 
     485     2897618 :           We check to see if the specified NVP matches what the next automatically loaded node is.  If they
     486       19852 :           match, we just continue as normal, going in order.  If they don't match, we attempt to find a node
     487       24602 :           named after the NVP that is being loaded.  If that NVP does not exist, we throw an exception. */
     488       45834 :       void startNode()
     489       63245 :       {
     490        3590 :         auto next = itsNodes.top().child; // By default we would move to the next child node
     491       45788 :         auto const expectedName = itsNodes.top().name; // this is the expected name from the NVP, if provided
     492     2918200 : 
     493        1213 :         // If we were given an NVP name, look for it in the current level of the document.
     494        1988 :         //    We only need to do this if either we have exhausted the siblings of the current level or
     495        3801 :         //    the NVP name does not match the name of the node we would normally read next
     496        2200 :         if( expectedName && ( next == nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
     497         208 :         {
     498         408 :           next = itsNodes.top().search( expectedName );
     499       23042 : 
     500         802 :           if( next == nullptr )
     501       85834 :             throw Exception("XML Parsing failed - provided NVP (" + std::string(expectedName) + ") not found");
     502       65641 :         }
     503       65202 : 
     504      107200 :         itsNodes.emplace( next );
     505       44700 :       }
     506        2400 : 
     507        3500 :       //! Finishes reading the current node
     508        4800 :       void finishNode()
     509         200 :       {
     510        1400 :         // remove current
     511        2200 :         itsNodes.pop();
     512        1800 : 
     513        1016 :         // advance parent
     514       63274 :         itsNodes.top().advance();
     515       41616 : 
     516       61990 :         // Reset name
     517      101374 :         itsNodes.top().name = nullptr;
     518       46600 :       }
     519        4400 : 
     520        4600 :       //! Retrieves the current node name
     521         600 :       //! will return @c nullptr if the node does not have a name
     522        6000 :       const char * getNodeName() const
     523        6400 :       {
     524        2400 :         return itsNodes.top().getChildName();
     525        3000 :       }
     526           2 : 
     527       64585 :       //! Sets the name for the next node created with startNode
     528       40802 :       void setNextName( const char * name )
     529       65787 :       {
     530      104785 :         itsNodes.top().name = name;
     531      222800 :       }
     532        1400 : 
     533      712800 :       //! Loads a bool from the current top node
     534      709600 :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     535        4400 :                                           std::is_same<T, bool>::value> = traits::sfinae> inline
     536        1202 :       void loadValue( T & value )
     537      526800 :       {
     538      528200 :         std::istringstream is( itsNodes.top().node->value() );
     539        2404 :         is.setf( std::ios::boolalpha );
     540       63200 :         is >> value;
     541       40804 :       }
     542       66606 : 
     543      105600 :       //! Loads a char (signed or unsigned) from the current top node
     544      346002 :       template <class T, traits::EnableIf<std::is_integral<T>::value,
     545      281802 :                                           !std::is_same<T, bool>::value,
     546       27400 :                                           sizeof(T) == sizeof(char)> = traits::sfinae> inline
     547       23400 :       void loadValue( T & value )
     548      808400 :       {
     549      284000 :         value = *reinterpret_cast<T*>( itsNodes.top().node->value() );
     550      527600 :       }
     551        1000 : 
     552        5404 :       //! Load an int8_t from the current top node (ensures we parse entire number)
     553       80513 :       void loadValue( int8_t & value )
     554      445804 :       {
     555       84119 :         int32_t val; loadValue( val ); value = static_cast<int8_t>( val );
     556      520916 :       }
     557      985402 : 
     558         405 :       //! Load a uint8_t from the current top node (ensures we parse entire number)
     559      310600 :       void loadValue( uint8_t & value )
     560       27200 :       {
     561      285200 :         uint32_t val; loadValue( val ); value = static_cast<uint8_t>( val );
     562        2003 :       }
     563        6603 : 
     564         600 :       //! Loads a type best represented as an unsigned long from the current top node
     565        9204 :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     566       88600 :                                           !std::is_same<T, bool>::value,
     567       43604 :                                           !std::is_same<T, char>::value,
     568      367604 :                                           !std::is_same<T, unsigned char>::value,
     569      123601 :                                           sizeof(T) < sizeof(long long)> = traits::sfinae> inline
     570       65600 :       void loadValue( T & value )
     571        1001 :       {
     572       27600 :         value = static_cast<T>( std::stoul( itsNodes.top().node->value() ) );
     573       23400 :       }
     574         800 : 
     575        4801 :       //! Loads a type best represented as an unsigned long long from the current top node
     576        1001 :       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
     577         600 :                                           !std::is_same<T, bool>::value,
     578        3203 :                                           sizeof(T) >= sizeof(long long)> = traits::sfinae> inline
     579      556947 :       void loadValue( T & value )
     580       41000 :       {
     581      560832 :         value = static_cast<T>( std::stoull( itsNodes.top().node->value() ) );
     582      600547 :       }
     583       40891 : 
     584         800 :       //! Loads a type best represented as an int from the current top node
     585        5403 :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     586        2000 :                                           !std::is_same<T, char>::value,
     587        1288 :                                           sizeof(T) <= sizeof(int)> = traits::sfinae> inline
     588        5888 :       void loadValue( T & value )
     589        1400 :       {
     590        2000 :         value = static_cast<T>( std::stoi( itsNodes.top().node->value() ) );
     591        2601 :       }
     592      103560 : 
     593       41042 :       //! Loads a type best represented as a long from the current top node
     594      105001 :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     595      150560 :                                           (sizeof(T) > sizeof(int)),
     596       53223 :                                           sizeof(T) <= sizeof(long)> = traits::sfinae> inline
     597        7864 :       void loadValue( T & value )
     598      263843 :       {
     599      269000 :         value = static_cast<T>( std::stol( itsNodes.top().node->value() ) );
     600       14222 :       }
     601        7222 : 
     602      263600 :       //! Loads a type best represented as a long long from the current top node
     603      264088 :       template <class T, traits::EnableIf<std::is_signed<T>::value,
     604         888 :                                           (sizeof(T) > sizeof(long)),
     605       78326 :                                           sizeof(T) <= sizeof(long long)> = traits::sfinae> inline
     606       40600 :       void loadValue( T & value )
     607       77014 :       {
     608      117654 :         value = static_cast<T>( std::stoll( itsNodes.top().node->value() ) );
     609       41200 :       }
     610        6128 : 
     611        7220 :       //! Loads a type best represented as a float from the current top node
     612        5626 :       void loadValue( float & value )
     613      269846 :       {
     614        2026 :         try
     615      273012 :         {
     616        7812 :           value = std::stof( itsNodes.top().node->value() );
     617       23226 :         }
     618       85585 :         catch( std::out_of_range const & )
     619       60600 :         {
     620       96593 :           // special case for denormalized values
     621      120185 :           std::istringstream is( itsNodes.top().node->value() );
     622      302000 :           is >> value;
     623        2600 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     624         800 :             throw;
     625       14200 :         }
     626         600 :       }
     627         227 : 
     628         400 :       //! Loads a type best represented as a double from the current top node
     629       20261 :       void loadValue( double & value )
     630     2876027 :       {
     631      103234 :         try
     632     2897634 :         {
     633     2969600 :           value = std::stod( itsNodes.top().node->value() );
     634       94800 :         }
     635        6027 :         catch( std::out_of_range const & )
     636        1600 :         {
     637       18600 :           // special case for denormalized values
     638     2881400 :           std::istringstream is( itsNodes.top().node->value() );
     639        1200 :           is >> value;
     640       14013 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     641        2800 :             throw;
     642       20647 :         }
     643       14240 :       }
     644       83861 : 
     645       21634 :       //! Loads a type best represented as a long double from the current top node
     646     2952200 :       void loadValue( long double & value )
     647     3872827 :       {
     648      922013 :         try
     649         600 :         {
     650     2875827 :           value = std::stold( itsNodes.top().node->value() );
     651      921800 :         }
     652      922100 :         catch( std::out_of_range const & )
     653     2877627 :         {
     654         900 :           // special case for denormalized values
     655       24134 :           std::istringstream is( itsNodes.top().node->value() );
     656     2877440 :           is >> value;
     657       86587 :           if( std::fpclassify( value ) != FP_SUBNORMAL )
     658       21634 :             throw;
     659     2940913 :         }
     660     2940826 :       }
     661        4200 : 
     662      922200 :       //! Loads a string from the current node from the current top node
     663        1413 :       template<class CharT, class Traits, class Alloc> inline
     664      922600 :       void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
     665        3400 :       {
     666      400013 :         std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
     667      403818 : 
     668      405700 :         str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
     669        3131 :                     std::istreambuf_iterator<CharT, Traits>() );
     670       65631 :       }
     671      522400 : 
     672       64700 :       //! Loads the size of the current top node
     673       65800 :       template <class T> inline
     674        2000 :       void loadSize( T & value )
     675        2700 :       {
     676        3000 :         value = getNumChildren( itsNodes.top().node );
     677        1200 :       }
     678         700 : 
     679        2800 :     protected:
     680        3608 :       //! Gets the number of children (usually interpreted as size) for the specified node
     681        2700 :       static size_t getNumChildren( rapidxml::xml_node<> * node )
     682        7908 :       {
     683      699512 :         size_t size = 0;
     684         202 :         node = node->first_node(); // get first child
     685     1141064 : 
     686     1138066 :         while( node != nullptr )
     687        3002 :         {
     688        8200 :           ++size;
     689      445562 :           node = node->next_sibling();
     690      445562 :         }
     691        5602 : 
     692        1200 :         return size;
     693        5402 :       }
     694        5802 : 
     695      527400 :       //! A struct that contains metadata about a node
     696      351404 :       /*! Keeps track of some top level node, its number of
     697      654204 :           remaining children, and the current active child node */
     698     1667600 :       struct NodeInfo
     699     1145200 :       {
     700      575964 :         NodeInfo( rapidxml::xml_node<> * n = nullptr ) :
     701      130604 :           node( n ),
     702     1364760 :           child( n->first_node() ),
     703     1450400 :           size( XMLInputArchive::getNumChildren( n ) ),
     704      195944 :           name( nullptr )
     705      193344 :         { }
     706      480946 : 
     707        7202 :         //! Advances to the next sibling node of the child
     708      286000 :         /*! If this is the last sibling child will be null after calling */
     709      556018 :         void advance()
     710        3604 :         {
     711      674228 :           if( size > 0 )
     712      547400 :           {
     713     1048430 :             --size;
     714      285224 :             child = child->next_sibling();
     715     1487268 :           }
     716       41470 :         }
     717      458090 : 
     718      924800 :         //! Searches for a child with the given name in this node
     719      413822 :         /*! @param searchName The name to search for (must be null terminated)
     720      107958 :             @return The node if found, nullptr otherwise */
     721      531000 :         rapidxml::xml_node<> * search( const char * searchName )
     722      844800 :         {
     723      283422 :           if( searchName )
     724      569800 :           {
     725      568022 :             size_t new_size = XMLInputArchive::getNumChildren( node );
     726      284422 :             const size_t name_size = rapidxml::internal::measure( searchName );
     727        5812 : 
     728         400 :             for( auto new_child = node->first_node(); new_child != nullptr; new_child = new_child->next_sibling() )
     729      281612 :             {
     730     1986612 :               if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
     731        2200 :               {
     732     2265600 :                 size = new_size;
     733     1993000 :                 child = new_child;
     734       10022 : 
     735      304200 :                 return new_child;
     736      281422 :               }
     737       26222 :               --new_size;
     738       22200 :             }
     739        1400 :           }
     740        4406 : 
     741        3200 :           return nullptr;
     742        1206 :         }
     743        5006 : 
     744        3200 :         //! Returns the actual name of the next child node, if it exists
     745        4200 :         const char * getChildName() const
     746        6200 :         {
     747       10600 :           return child ? child->name() : nullptr;
     748       20752 :         }
     749         800 : 
     750       23552 :         rapidxml::xml_node<> * node;  //!< A pointer to this node
     751       16552 :         rapidxml::xml_node<> * child; //!< A pointer to its current child
     752        3422 :         size_t size;                  //!< The remaining number of children for this node
     753        3200 :         const char * name;            //!< The NVP name for next child node
     754       20222 :       }; // NodeInfo
     755         262 : 
     756       63200 :       //! @}
     757       26440 : 
     758       30060 :     private:
     759       26400 :       std::vector<char> itsData;       //!< The raw data loaded
     760      279620 :       rapidxml::xml_document<> itsXML; //!< The XML document
     761       26772 :       std::stack<NodeInfo> itsNodes;   //!< A stack of nodes read from the document
     762      263800 :   };
     763      284792 : 
     764       20752 :   // ######################################################################
     765        9800 :   // XMLArchive prologue and epilogue functions
     766       15820 :   // ######################################################################
     767        8000 : 
     768      266803 :   // ######################################################################
     769        6200 :   //! Prologue for NVPs for XML output archives
     770         400 :   /*! NVPs do not start or finish nodes - they just set up the names */
     771         440 :   template <class T> inline
     772          43 :   void prologue( XMLOutputArchive &, NameValuePair<T> const & )
     773        9000 :   { }
     774       38220 : 
     775       16260 :   //! Prologue for NVPs for XML input archives
     776      283400 :   template <class T> inline
     777      295600 :   void prologue( XMLInputArchive &, NameValuePair<T> const & )
     778       16860 :   { }
     779       10800 : 
     780      277400 :   // ######################################################################
     781       10061 :   //! Epilogue for NVPs for XML output archives
     782        3203 :   /*! NVPs do not start or finish nodes - they just set up the names */
     783      269200 :   template <class T> inline
     784        7660 :   void epilogue( XMLOutputArchive &, NameValuePair<T> const & )
     785          41 :   { }
     786      272800 : 
     787       33420 :   //! Epilogue for NVPs for XML input archives
     788       10620 :   template <class T> inline
     789      289800 :   void epilogue( XMLInputArchive &, NameValuePair<T> const & )
     790      285800 :   { }
     791       10600 : 
     792         200 :   // ######################################################################
     793        6600 :   //! Prologue for deferred data for XML archives
     794        4600 :   /*! Do nothing for the defer wrapper */
     795      362932 :   template <class T> inline
     796         200 :   void prologue( XMLOutputArchive &, DeferredData<T> const & )
     797      363331 :   { }
     798      368943 : 
     799        4200 :   //! Prologue for deferred data for XML archives
     800       29612 :   template <class T> inline
     801        6012 :   void prologue( XMLInputArchive &, DeferredData<T> const & )
     802       23600 :   { }
     803       22724 : 
     804         600 :   // ######################################################################
     805         959 :   //! Epilogue for deferred for XML archives
     806      181121 :   /*! NVPs do not start or finish nodes - they just set up the names */
     807         419 :   template <class T> inline
     808      182816 :   void epilogue( XMLOutputArchive &, DeferredData<T> const & )
     809     1101203 :   { }
     810       17200 : 
     811      924000 :   //! Epilogue for deferred for XML archives
     812      935600 :   /*! Do nothing for the defer wrapper */
     813       39000 :   template <class T> inline
     814         200 :   void epilogue( XMLInputArchive &, DeferredData<T> const & )
     815       50800 :   { }
     816       30401 : 
     817      935600 :   // ######################################################################
     818       22202 :   //! Prologue for SizeTags for XML output archives
     819       11200 :   /*! SizeTags do not start or finish nodes */
     820         831 :   template <class T> inline
     821       14000 :   void prologue( XMLOutputArchive & ar, SizeTag<T> const & )
     822          31 :   {
     823     2885630 :       if (ar.hasSizeAttributes())
     824           0 :       {
     825     3807057 :           ar.appendAttribute("size", "dynamic");
     826     3806600 :       }
     827        2427 :   }
     828     6764861 : 
     829      940600 :   template <class T> inline
     830     3885268 :   void prologue( XMLInputArchive &, SizeTag<T> const & )
     831     3910030 :   { }
     832      956634 : 
     833       17814 :   //! Epilogue for SizeTags for XML output archives
     834     2899834 :   /*! SizeTags do not start or finish nodes */
     835      924614 :   template <class T> inline
     836        5414 :   void epilogue( XMLOutputArchive &, SizeTag<T> const & )
     837        1800 :   { }
     838      939427 : 
     839      925630 :   template <class T> inline
     840       17817 :   void epilogue( XMLInputArchive &, SizeTag<T> const & )
     841     1294833 :   { }
     842     4185224 : 
     843       18434 :   // ######################################################################
     844     5754444 :   //! Prologue for all other types for XML output archives (except minimal types)
     845     7062348 :   /*! Starts a new node, named either automatically or by some NVP,
     846     4171960 :       that may be given data by the type about to be archived
     847     3323348 : 
     848        4215 :       Minimal types do not start or end nodes */
     849      846760 :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
     850      445675 :                                        traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
     851     3279800 :   void prologue( XMLOutputArchive & ar, T const & )
     852      401541 :   {
     853     2877727 :     ar.startNode();
     854        1828 :     ar.insertType<T>();
     855     3318617 :   }
     856     4182962 : 
     857       21016 :   //! Prologue for all other types for XML input archives (except minimal types)
     858     4299970 :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
     859        7600 :                                        traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
     860     1644912 :   void prologue( XMLInputArchive & ar, T const & )
     861      541325 :   {
     862     1503104 :     ar.startNode();
     863     1551973 :   }
     864      445150 : 
     865      899869 :   // ######################################################################
     866      131795 :   //! Epilogue for all other types other for XML output archives (except minimal types)
     867      450738 :   /*! Finishes the node created in the prologue
     868      927201 : 
     869        5588 :       Minimal types do not start or end nodes */
     870      445960 :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
     871       14600 :                                        traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
     872         600 :   void epilogue( XMLOutputArchive & ar, T const & )
     873      465460 :   {
     874      168004 :     ar.finishNode();
     875      143504 :   }
     876     1418360 : 
     877     1416860 :   //! Epilogue for all other types other for XML output archives (except minimal types)
     878      136104 :   template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
     879        6000 :                                        traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
     880      934700 :   void epilogue( XMLInputArchive & ar, T const & )
     881      135204 :   {
     882        5600 :     ar.finishNode();
     883      923000 :   }
     884      137404 : 
     885           0 :   // ######################################################################
     886      928600 :   // Common XMLArchive serialization functions
     887      325148 :   // ######################################################################
     888      656299 : 
     889     1135144 :   //! Saving NVP types to XML
     890     1644334 :   template <class T> inline
     891      585000 :   void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive & ar, NameValuePair<T> const & t )
     892       20695 :   {
     893     1292200 :     ar.setNextName( t.name );
     894       48695 :     ar( t.value );
     895      737800 :   }
     896      727402 : 
     897        3400 :   //! Loading NVP types from XML
     898       40468 :   template <class T> inline
     899      809600 :   void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, NameValuePair<T> & t )
     900      441070 :   {
     901      333468 :     ar.setNextName( t.name );
     902      689000 :     ar( t.value );
     903      409600 :   }
     904      686400 : 
     905       15000 :   // ######################################################################
     906      413000 :   //! Saving SizeTags to XML
     907      933400 :   template <class T> inline
     908       11000 :   void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive &, SizeTag<T> const & )
     909     1531800 :   { }
     910     1346002 : 
     911     1005618 :   //! Loading SizeTags from XML
     912     1032131 :   template <class T> inline
     913        7603 :   void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, SizeTag<T> & st )
     914       29516 :   {
     915       33316 :     ar.loadSize( st.size );
     916      534003 :   }
     917       11203 : 
     918      810603 :   // ######################################################################
     919        5209 :   //! Saving for POD types to xml
     920     1088409 :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     921     1092200 :   void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, T const & t)
     922      284800 :   {
     923      809400 :     ar.saveValue( t );
     924        6026 :   }
     925       40626 : 
     926        8404 :   //! Loading for POD types from xml
     927      327804 :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     928       47604 :   void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, T & t)
     929      359563 :   {
     930       10813 :     ar.loadValue( t );
     931      403974 :   }
     932      355160 : 
     933      100164 :   // ######################################################################
     934      331604 :   //! saving string to xml
     935       51765 :   template<class CharT, class Traits, class Alloc> inline
     936       51959 :   void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
     937       29620 :   {
     938       20818 :     ar.saveValue( str );
     939       28211 :   }
     940       53845 : 
     941        2004 :   //! loading string from xml
     942      133438 :   template<class CharT, class Traits, class Alloc> inline
     943       54638 :   void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
     944       88404 :   {
     945       87010 :     ar.loadValue( str );
     946       26810 :   }
     947       25805 : } // namespace cereal
     948        3847 : 
     949        4800 : // register archives for polymorphic support
     950       10460 : CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive)
     951       12082 : CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive)
     952       12403 : 
     953      283907 : // tie input and output archives together
     954        9233 : CEREAL_SETUP_ARCHIVE_TRAITS(cereal::XMLInputArchive, cereal::XMLOutputArchive)
     955      467508 : 
     956      284095 : #endif // CEREAL_ARCHIVES_XML_HPP_

Generated by: LCOV version 1.13