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_
|