LCOV - code coverage report

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

              Line data    Source code

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

Generated by: LCOV version 1.14