Initial commit.
Final release of the project Anonymizer (2015). Project settings for the Qt Creator (ver. 3.6).
This commit is contained in:
163
3rdparty/include/cereal/archives/adapters.hpp
vendored
Normal file
163
3rdparty/include/cereal/archives/adapters.hpp
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
/*! \file adapters.hpp
|
||||
\brief Archive adapters that provide additional functionality
|
||||
on top of an existing archive */
|
||||
/*
|
||||
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of cereal nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef CEREAL_ARCHIVES_ADAPTERS_HPP_
|
||||
#define CEREAL_ARCHIVES_ADAPTERS_HPP_
|
||||
|
||||
#include <cereal/details/helpers.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace cereal
|
||||
{
|
||||
#ifdef CEREAL_FUTURE_EXPERIMENTAL
|
||||
|
||||
// Forward declaration for friend access
|
||||
template <class U, class A> U & get_user_data( A & );
|
||||
|
||||
//! Wraps an archive and gives access to user data
|
||||
/*! This adapter is useful if you require access to
|
||||
either raw pointers or references within your
|
||||
serialization functions.
|
||||
|
||||
While cereal does not directly support serialization
|
||||
raw pointers or references, it is sometimes the case
|
||||
that you may want to supply something such as a raw
|
||||
pointer or global reference to some constructor.
|
||||
In this situation this adapter would likely be used
|
||||
with the construct class to allow for non-default
|
||||
constructors.
|
||||
|
||||
@note This feature is experimental and may be altered or removed in a future release. See issue #46.
|
||||
|
||||
@code{.cpp}
|
||||
struct MyUserData
|
||||
{
|
||||
int * myRawPointer;
|
||||
std::reference_wrapper<MyOtherType> myReference;
|
||||
};
|
||||
|
||||
struct MyClass
|
||||
{
|
||||
// Note the raw pointer parameter
|
||||
MyClass( int xx, int * rawP );
|
||||
|
||||
int x;
|
||||
|
||||
template <class Archive>
|
||||
void serialize( Archive & ar )
|
||||
{ ar( x ); }
|
||||
|
||||
template <class Archive>
|
||||
static void load_and_construct( Archive & ar, cereal::construct<MyClass> & construct )
|
||||
{
|
||||
int xx;
|
||||
ar( xx );
|
||||
// note the need to use get_user_data to retrieve user data from the archive
|
||||
construct( xx, cereal::get_user_data<MyUserData>( ar ).myRawPointer );
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
MyUserData md;
|
||||
md.myRawPointer = &something;
|
||||
md.myReference = someInstanceOfType;
|
||||
|
||||
std::ifstream is( "data.xml" );
|
||||
cereal::UserDataAdapter<MyUserData, cereal::XMLInputArchive> ar( md, is );
|
||||
|
||||
std::unique_ptr<MyClass> sc;
|
||||
ar( sc ); // use as normal
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@endcode
|
||||
|
||||
@relates get_user_data
|
||||
|
||||
@tparam UserData The type to give the archive access to
|
||||
@tparam Archive The archive to wrap */
|
||||
template <class UserData, class Archive>
|
||||
class UserDataAdapter : public Archive
|
||||
{
|
||||
public:
|
||||
//! Construct the archive with some user data struct
|
||||
/*! This will forward all arguments (other than the user
|
||||
data) to the wrapped archive type. The UserDataAdapter
|
||||
can then be used identically to the wrapped archive type
|
||||
|
||||
@tparam Args The arguments to pass to the constructor of
|
||||
the archive. */
|
||||
template <class ... Args>
|
||||
UserDataAdapter( UserData & ud, Args && ... args ) :
|
||||
Archive( std::forward<Args>( args )... ),
|
||||
userdata( ud )
|
||||
{ }
|
||||
|
||||
private:
|
||||
//! Overload the rtti function to enable dynamic_cast
|
||||
void rtti() {}
|
||||
friend UserData & get_user_data<UserData>( Archive & ar );
|
||||
UserData & userdata; //!< The actual user data
|
||||
};
|
||||
|
||||
//! Retrieves user data from an archive wrapped by UserDataAdapter
|
||||
/*! This will attempt to retrieve the user data associated with
|
||||
some archive wrapped by UserDataAdapter. If this is used on
|
||||
an archive that is not wrapped, a run-time exception will occur.
|
||||
|
||||
@note This feature is experimental and may be altered or removed in a future release. See issue #46.
|
||||
|
||||
@note The correct use of this function cannot be enforced at compile
|
||||
time.
|
||||
|
||||
@relates UserDataAdapter
|
||||
@tparam UserData The data struct contained in the archive
|
||||
@tparam Archive The archive, which should be wrapped by UserDataAdapter
|
||||
@param ar The archive
|
||||
@throws Exception if the archive this is used upon is not wrapped with
|
||||
UserDataAdapter. */
|
||||
template <class UserData, class Archive>
|
||||
UserData & get_user_data( Archive & ar )
|
||||
{
|
||||
try
|
||||
{
|
||||
return dynamic_cast<UserDataAdapter<UserData, Archive> &>( ar ).userdata;
|
||||
}
|
||||
catch( std::bad_cast const & )
|
||||
{
|
||||
throw ::cereal::Exception("Attempting to get user data from archive not wrapped in UserDataAdapter");
|
||||
}
|
||||
}
|
||||
#endif // CEREAL_FUTURE_EXPERIMENTAL
|
||||
} // namespace cereal
|
||||
|
||||
#endif // CEREAL_ARCHIVES_ADAPTERS_HPP_
|
165
3rdparty/include/cereal/archives/binary.hpp
vendored
Normal file
165
3rdparty/include/cereal/archives/binary.hpp
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
/*! \file binary.hpp
|
||||
\brief Binary input and output archives */
|
||||
/*
|
||||
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of cereal nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef CEREAL_ARCHIVES_BINARY_HPP_
|
||||
#define CEREAL_ARCHIVES_BINARY_HPP_
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace cereal
|
||||
{
|
||||
// ######################################################################
|
||||
//! An output archive designed to save data in a compact binary representation
|
||||
/*! This archive outputs data to a stream in an extremely compact binary
|
||||
representation with as little extra metadata as possible.
|
||||
|
||||
This archive does nothing to ensure that the endianness of the saved
|
||||
and loaded data is the same. If you need to have portability over
|
||||
architectures with different endianness, use PortableBinaryOutputArchive.
|
||||
|
||||
When using a binary archive and a file stream, you must use the
|
||||
std::ios::binary format flag to avoid having your data altered
|
||||
inadvertently.
|
||||
|
||||
\ingroup Archives */
|
||||
class BinaryOutputArchive : public OutputArchive<BinaryOutputArchive, AllowEmptyClassElision>
|
||||
{
|
||||
public:
|
||||
//! Construct, outputting to the provided stream
|
||||
/*! @param stream The stream to output to. Can be a stringstream, a file stream, or
|
||||
even cout! */
|
||||
BinaryOutputArchive(std::ostream & stream) :
|
||||
OutputArchive<BinaryOutputArchive, AllowEmptyClassElision>(this),
|
||||
itsStream(stream)
|
||||
{ }
|
||||
|
||||
//! Writes size bytes of data to the output stream
|
||||
void saveBinary( const void * data, std::size_t size )
|
||||
{
|
||||
auto const writtenSize = static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size ) );
|
||||
|
||||
if(writtenSize != size)
|
||||
throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream & itsStream;
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
//! An input archive designed to load data saved using BinaryOutputArchive
|
||||
/* This archive does nothing to ensure that the endianness of the saved
|
||||
and loaded data is the same. If you need to have portability over
|
||||
architectures with different endianness, use PortableBinaryOutputArchive.
|
||||
|
||||
When using a binary archive and a file stream, you must use the
|
||||
std::ios::binary format flag to avoid having your data altered
|
||||
inadvertently.
|
||||
|
||||
\ingroup Archives */
|
||||
class BinaryInputArchive : public InputArchive<BinaryInputArchive, AllowEmptyClassElision>
|
||||
{
|
||||
public:
|
||||
//! Construct, loading from the provided stream
|
||||
BinaryInputArchive(std::istream & stream) :
|
||||
InputArchive<BinaryInputArchive, AllowEmptyClassElision>(this),
|
||||
itsStream(stream)
|
||||
{ }
|
||||
|
||||
//! Reads size bytes of data from the input stream
|
||||
void loadBinary( void * const data, std::size_t size )
|
||||
{
|
||||
auto const readSize = static_cast<std::size_t>( itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size ) );
|
||||
|
||||
if(readSize != size)
|
||||
throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
|
||||
}
|
||||
|
||||
private:
|
||||
std::istream & itsStream;
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// Common BinaryArchive serialization functions
|
||||
|
||||
//! Saving for POD types to binary
|
||||
template<class T> inline
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
|
||||
CEREAL_SAVE_FUNCTION_NAME(BinaryOutputArchive & ar, T const & t)
|
||||
{
|
||||
ar.saveBinary(std::addressof(t), sizeof(t));
|
||||
}
|
||||
|
||||
//! Loading for POD types from binary
|
||||
template<class T> inline
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
|
||||
CEREAL_LOAD_FUNCTION_NAME(BinaryInputArchive & ar, T & t)
|
||||
{
|
||||
ar.loadBinary(std::addressof(t), sizeof(t));
|
||||
}
|
||||
|
||||
//! Serializing NVP types to binary
|
||||
template <class Archive, class T> inline
|
||||
CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive)
|
||||
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t )
|
||||
{
|
||||
ar( t.value );
|
||||
}
|
||||
|
||||
//! Serializing SizeTags to binary
|
||||
template <class Archive, class T> inline
|
||||
CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive)
|
||||
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t )
|
||||
{
|
||||
ar( t.size );
|
||||
}
|
||||
|
||||
//! Saving binary data
|
||||
template <class T> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME(BinaryOutputArchive & ar, BinaryData<T> const & bd)
|
||||
{
|
||||
ar.saveBinary( bd.data, static_cast<std::size_t>( bd.size ) );
|
||||
}
|
||||
|
||||
//! Loading binary data
|
||||
template <class T> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME(BinaryInputArchive & ar, BinaryData<T> & bd)
|
||||
{
|
||||
ar.loadBinary(bd.data, static_cast<std::size_t>(bd.size));
|
||||
}
|
||||
} // namespace cereal
|
||||
|
||||
// register archives for polymorphic support
|
||||
CEREAL_REGISTER_ARCHIVE(cereal::BinaryOutputArchive)
|
||||
CEREAL_REGISTER_ARCHIVE(cereal::BinaryInputArchive)
|
||||
|
||||
// tie input and output archives together
|
||||
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::BinaryInputArchive, cereal::BinaryOutputArchive)
|
||||
|
||||
#endif // CEREAL_ARCHIVES_BINARY_HPP_
|
908
3rdparty/include/cereal/archives/json.hpp
vendored
Normal file
908
3rdparty/include/cereal/archives/json.hpp
vendored
Normal file
@ -0,0 +1,908 @@
|
||||
/*! \file json.hpp
|
||||
\brief JSON input and output archives */
|
||||
/*
|
||||
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of cereal nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef CEREAL_ARCHIVES_JSON_HPP_
|
||||
#define CEREAL_ARCHIVES_JSON_HPP_
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/details/util.hpp>
|
||||
|
||||
namespace cereal
|
||||
{
|
||||
//! An exception thrown when rapidjson fails an internal assertion
|
||||
/*! @ingroup Utility */
|
||||
struct RapidJSONException : Exception
|
||||
{ RapidJSONException( const char * what_ ) : Exception( what_ ) {} };
|
||||
}
|
||||
|
||||
// Override rapidjson assertions to throw exceptions by default
|
||||
#ifndef RAPIDJSON_ASSERT
|
||||
#define RAPIDJSON_ASSERT(x) if(!(x)){ \
|
||||
throw ::cereal::RapidJSONException("rapidjson internal assertion failure: " #x); }
|
||||
#endif // RAPIDJSON_ASSERT
|
||||
|
||||
#include <cereal/external/rapidjson/prettywriter.h>
|
||||
#include <cereal/external/rapidjson/genericstream.h>
|
||||
#include <cereal/external/rapidjson/reader.h>
|
||||
#include <cereal/external/rapidjson/document.h>
|
||||
#include <cereal/external/base64.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace cereal
|
||||
{
|
||||
// ######################################################################
|
||||
//! An output archive designed to save data to JSON
|
||||
/*! This archive uses RapidJSON to build serialie data to JSON.
|
||||
|
||||
JSON archives provides a human readable output but at decreased
|
||||
performance (both in time and space) compared to binary archives.
|
||||
|
||||
JSON benefits greatly from name-value pairs, which if present, will
|
||||
name the nodes in the output. If these are not present, each level
|
||||
of the output will be given an automatically generated delimited name.
|
||||
|
||||
The precision of the output archive controls the number of decimals output
|
||||
for floating point numbers and should be sufficiently large (i.e. at least 20)
|
||||
if there is a desire to have binary equality between the numbers output and
|
||||
those read in. In general you should expect a loss of precision when going
|
||||
from floating point to text and back.
|
||||
|
||||
JSON archives do not output the size information for any dynamically sized structure
|
||||
and instead infer it from the number of children for a node. This means that data
|
||||
can be hand edited for dynamic sized structures and will still be readable. This
|
||||
is accomplished through the cereal::SizeTag object, which will cause the archive
|
||||
to output the data as a JSON array (e.g. marked by [] instead of {}), which indicates
|
||||
that the container is variable sized and may be edited.
|
||||
|
||||
\ingroup Archives */
|
||||
class JSONOutputArchive : public OutputArchive<JSONOutputArchive>, public traits::TextArchive
|
||||
{
|
||||
enum class NodeType { StartObject, InObject, StartArray, InArray };
|
||||
|
||||
typedef rapidjson::GenericWriteStream WriteStream;
|
||||
typedef rapidjson::PrettyWriter<WriteStream> JSONWriter;
|
||||
|
||||
public:
|
||||
/*! @name Common Functionality
|
||||
Common use cases for directly interacting with an JSONOutputArchive */
|
||||
//! @{
|
||||
|
||||
//! A class containing various advanced options for the JSON archive
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
//! Default options
|
||||
static Options Default(){ return Options(); }
|
||||
|
||||
//! Default options with no indentation
|
||||
static Options NoIndent(){ return Options( std::numeric_limits<double>::max_digits10, IndentChar::space, 0 ); }
|
||||
|
||||
//! The character to use for indenting
|
||||
enum class IndentChar : char
|
||||
{
|
||||
space = ' ',
|
||||
tab = '\t',
|
||||
newline = '\n',
|
||||
carriage_return = '\r'
|
||||
};
|
||||
|
||||
//! Specify specific options for the JSONOutputArchive
|
||||
/*! @param precision The precision used for floating point numbers
|
||||
@param indentChar The type of character to indent with
|
||||
@param indentLength The number of indentChar to use for indentation
|
||||
(0 corresponds to no indentation) */
|
||||
explicit Options( int precision = std::numeric_limits<double>::max_digits10,
|
||||
IndentChar indentChar = IndentChar::space,
|
||||
unsigned int indentLength = 4 ) :
|
||||
itsPrecision( precision ),
|
||||
itsIndentChar( static_cast<char>(indentChar) ),
|
||||
itsIndentLength( indentLength ) { }
|
||||
|
||||
private:
|
||||
friend class JSONOutputArchive;
|
||||
int itsPrecision;
|
||||
char itsIndentChar;
|
||||
unsigned int itsIndentLength;
|
||||
};
|
||||
|
||||
//! Construct, outputting to the provided stream
|
||||
/*! @param stream The stream to output to.
|
||||
@param options The JSON specific options to use. See the Options struct
|
||||
for the values of default parameters */
|
||||
JSONOutputArchive(std::ostream & stream, Options const & options = Options::Default() ) :
|
||||
OutputArchive<JSONOutputArchive>(this),
|
||||
itsWriteStream(stream),
|
||||
itsWriter(itsWriteStream, options.itsPrecision),
|
||||
itsNextName(nullptr)
|
||||
{
|
||||
itsWriter.SetIndent( options.itsIndentChar, options.itsIndentLength );
|
||||
itsNameCounter.push(0);
|
||||
itsNodeStack.push(NodeType::StartObject);
|
||||
}
|
||||
|
||||
//! Destructor, flushes the JSON
|
||||
~JSONOutputArchive()
|
||||
{
|
||||
if (itsNodeStack.top() == NodeType::InObject)
|
||||
itsWriter.EndObject();
|
||||
}
|
||||
|
||||
//! Saves some binary data, encoded as a base64 string, with an optional name
|
||||
/*! This will create a new node, optionally named, and insert a value that consists of
|
||||
the data encoded as a base64 string */
|
||||
void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
|
||||
{
|
||||
setNextName( name );
|
||||
writeName();
|
||||
|
||||
auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
|
||||
saveValue( base64string );
|
||||
};
|
||||
|
||||
//! @}
|
||||
/*! @name Internal Functionality
|
||||
Functionality designed for use by those requiring control over the inner mechanisms of
|
||||
the JSONOutputArchive */
|
||||
//! @{
|
||||
|
||||
//! Starts a new node in the JSON output
|
||||
/*! The node can optionally be given a name by calling setNextName prior
|
||||
to creating the node
|
||||
|
||||
Nodes only need to be started for types that are themselves objects or arrays */
|
||||
void startNode()
|
||||
{
|
||||
writeName();
|
||||
itsNodeStack.push(NodeType::StartObject);
|
||||
itsNameCounter.push(0);
|
||||
}
|
||||
|
||||
//! Designates the most recently added node as finished
|
||||
void finishNode()
|
||||
{
|
||||
// if we ended up serializing an empty object or array, writeName
|
||||
// will never have been called - so start and then immediately end
|
||||
// the object/array.
|
||||
//
|
||||
// We'll also end any object/arrays we happen to be in
|
||||
switch(itsNodeStack.top())
|
||||
{
|
||||
case NodeType::StartArray:
|
||||
itsWriter.StartArray();
|
||||
case NodeType::InArray:
|
||||
itsWriter.EndArray();
|
||||
break;
|
||||
case NodeType::StartObject:
|
||||
itsWriter.StartObject();
|
||||
case NodeType::InObject:
|
||||
itsWriter.EndObject();
|
||||
break;
|
||||
}
|
||||
|
||||
itsNodeStack.pop();
|
||||
itsNameCounter.pop();
|
||||
}
|
||||
|
||||
//! Sets the name for the next node created with startNode
|
||||
void setNextName( const char * name )
|
||||
{
|
||||
itsNextName = name;
|
||||
}
|
||||
|
||||
//! Saves a bool to the current node
|
||||
void saveValue(bool b) { itsWriter.Bool_(b); }
|
||||
//! Saves an int to the current node
|
||||
void saveValue(int i) { itsWriter.Int(i); }
|
||||
//! Saves a uint to the current node
|
||||
void saveValue(unsigned u) { itsWriter.Uint(u); }
|
||||
//! Saves an int64 to the current node
|
||||
void saveValue(int64_t i64) { itsWriter.Int64(i64); }
|
||||
//! Saves a uint64 to the current node
|
||||
void saveValue(uint64_t u64) { itsWriter.Uint64(u64); }
|
||||
//! Saves a double to the current node
|
||||
void saveValue(double d) { itsWriter.Double(d); }
|
||||
//! Saves a string to the current node
|
||||
void saveValue(std::string const & s) { itsWriter.String(s.c_str(), static_cast<rapidjson::SizeType>( s.size() )); }
|
||||
//! Saves a const char * to the current node
|
||||
void saveValue(char const * s) { itsWriter.String(s); }
|
||||
|
||||
private:
|
||||
// Some compilers/OS have difficulty disambiguating the above for various flavors of longs, so we provide
|
||||
// special overloads to handle these cases.
|
||||
|
||||
//! 32 bit signed long saving to current node
|
||||
template <class T, traits::EnableIf<sizeof(T) == sizeof(std::int32_t),
|
||||
std::is_signed<T>::value> = traits::sfinae> inline
|
||||
void saveLong(T l){ saveValue( static_cast<std::int32_t>( l ) ); }
|
||||
|
||||
//! non 32 bit signed long saving to current node
|
||||
template <class T, traits::EnableIf<sizeof(T) != sizeof(std::int32_t),
|
||||
std::is_signed<T>::value> = traits::sfinae> inline
|
||||
void saveLong(T l){ saveValue( static_cast<std::int64_t>( l ) ); }
|
||||
|
||||
//! 32 bit unsigned long saving to current node
|
||||
template <class T, traits::EnableIf<sizeof(T) == sizeof(std::int32_t),
|
||||
std::is_unsigned<T>::value> = traits::sfinae> inline
|
||||
void saveLong(T lu){ saveValue( static_cast<std::uint32_t>( lu ) ); }
|
||||
|
||||
//! non 32 bit unsigned long saving to current node
|
||||
template <class T, traits::EnableIf<sizeof(T) != sizeof(std::int32_t),
|
||||
std::is_unsigned<T>::value> = traits::sfinae> inline
|
||||
void saveLong(T lu){ saveValue( static_cast<std::uint64_t>( lu ) ); }
|
||||
|
||||
public:
|
||||
#ifdef _MSC_VER
|
||||
//! MSVC only long overload to current node
|
||||
void saveValue( unsigned long lu ){ saveLong( lu ); };
|
||||
#else // _MSC_VER
|
||||
//! Serialize a long if it would not be caught otherwise
|
||||
template <class T, traits::EnableIf<std::is_same<T, long>::value,
|
||||
!std::is_same<T, std::int32_t>::value,
|
||||
!std::is_same<T, std::int64_t>::value> = traits::sfinae> inline
|
||||
void saveValue( T t ){ saveLong( t ); }
|
||||
|
||||
//! Serialize an unsigned long if it would not be caught otherwise
|
||||
template <class T, traits::EnableIf<std::is_same<T, unsigned long>::value,
|
||||
!std::is_same<T, std::uint32_t>::value,
|
||||
!std::is_same<T, std::uint64_t>::value> = traits::sfinae> inline
|
||||
void saveValue( T t ){ saveLong( t ); }
|
||||
#endif // _MSC_VER
|
||||
|
||||
//! Save exotic arithmetic as strings to current node
|
||||
/*! Handles long long (if distinct from other types), unsigned long (if distinct), and long double */
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value,
|
||||
!std::is_same<T, long>::value,
|
||||
!std::is_same<T, unsigned long>::value,
|
||||
!std::is_same<T, std::int64_t>::value,
|
||||
!std::is_same<T, std::uint64_t>::value,
|
||||
(sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae> inline
|
||||
void saveValue(T const & t)
|
||||
{
|
||||
std::stringstream ss; ss.precision( std::numeric_limits<long double>::max_digits10 );
|
||||
ss << t;
|
||||
saveValue( ss.str() );
|
||||
}
|
||||
|
||||
//! Write the name of the upcoming node and prepare object/array state
|
||||
/*! Since writeName is called for every value that is output, regardless of
|
||||
whether it has a name or not, it is the place where we will do a deferred
|
||||
check of our node state and decide whether we are in an array or an object.
|
||||
|
||||
The general workflow of saving to the JSON archive is:
|
||||
|
||||
1. (optional) Set the name for the next node to be created, usually done by an NVP
|
||||
2. Start the node
|
||||
3. (if there is data to save) Write the name of the node (this function)
|
||||
4. (if there is data to save) Save the data (with saveValue)
|
||||
5. Finish the node
|
||||
*/
|
||||
void writeName()
|
||||
{
|
||||
NodeType const & nodeType = itsNodeStack.top();
|
||||
|
||||
// Start up either an object or an array, depending on state
|
||||
if(nodeType == NodeType::StartArray)
|
||||
{
|
||||
itsWriter.StartArray();
|
||||
itsNodeStack.top() = NodeType::InArray;
|
||||
}
|
||||
else if(nodeType == NodeType::StartObject)
|
||||
{
|
||||
itsNodeStack.top() = NodeType::InObject;
|
||||
itsWriter.StartObject();
|
||||
}
|
||||
|
||||
// Array types do not output names
|
||||
if(nodeType == NodeType::InArray) return;
|
||||
|
||||
if(itsNextName == nullptr)
|
||||
{
|
||||
std::string name = "value" + std::to_string( itsNameCounter.top()++ ) + "\0";
|
||||
saveValue(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
saveValue(itsNextName);
|
||||
itsNextName = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//! Designates that the current node should be output as an array, not an object
|
||||
void makeArray()
|
||||
{
|
||||
itsNodeStack.top() = NodeType::StartArray;
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
private:
|
||||
WriteStream itsWriteStream; //!< Rapidjson write stream
|
||||
JSONWriter itsWriter; //!< Rapidjson writer
|
||||
char const * itsNextName; //!< The next name
|
||||
std::stack<uint32_t> itsNameCounter; //!< Counter for creating unique names for unnamed nodes
|
||||
std::stack<NodeType> itsNodeStack;
|
||||
}; // JSONOutputArchive
|
||||
|
||||
// ######################################################################
|
||||
//! An input archive designed to load data from JSON
|
||||
/*! This archive uses RapidJSON to read in a JSON archive.
|
||||
|
||||
Input JSON should have been produced by the JSONOutputArchive. Data can
|
||||
only be added to dynamically sized containers (marked by JSON arrays) -
|
||||
the input archive will determine their size by looking at the number of child nodes.
|
||||
Only JSON originating from a JSONOutputArchive is officially supported, but data
|
||||
from other sources may work if properly formatted.
|
||||
|
||||
The JSONInputArchive does not require that nodes are loaded in the same
|
||||
order they were saved by JSONOutputArchive. Using name value pairs (NVPs),
|
||||
it is possible to load in an out of order fashion or otherwise skip/select
|
||||
specific nodes to load.
|
||||
|
||||
The default behavior of the input archive is to read sequentially starting
|
||||
with the first node and exploring its children. When a given NVP does
|
||||
not match the read in name for a node, the archive will search for that
|
||||
node at the current level and load it if it exists. After loading an out of
|
||||
order node, the archive will then proceed back to loading sequentially from
|
||||
its new position.
|
||||
|
||||
Consider this simple example where loading of some data is skipped:
|
||||
|
||||
@code{cpp}
|
||||
// imagine the input file has someData(1-9) saved in order at the top level node
|
||||
ar( someData1, someData2, someData3 ); // XML loads in the order it sees in the file
|
||||
ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
|
||||
// match expected NVP name, so we search
|
||||
// for the given NVP and load that value
|
||||
ar( someData7, someData8, someData9 ); // with no NVP given, loading resumes at its
|
||||
// current location, proceeding sequentially
|
||||
@endcode
|
||||
|
||||
\ingroup Archives */
|
||||
class JSONInputArchive : public InputArchive<JSONInputArchive>, public traits::TextArchive
|
||||
{
|
||||
private:
|
||||
typedef rapidjson::GenericReadStream ReadStream;
|
||||
typedef rapidjson::GenericValue<rapidjson::UTF8<>> JSONValue;
|
||||
typedef JSONValue::ConstMemberIterator MemberIterator;
|
||||
typedef JSONValue::ConstValueIterator ValueIterator;
|
||||
typedef rapidjson::Document::GenericValue GenericValue;
|
||||
|
||||
public:
|
||||
/*! @name Common Functionality
|
||||
Common use cases for directly interacting with an JSONInputArchive */
|
||||
//! @{
|
||||
|
||||
//! Construct, reading from the provided stream
|
||||
/*! @param stream The stream to read from */
|
||||
JSONInputArchive(std::istream & stream) :
|
||||
InputArchive<JSONInputArchive>(this),
|
||||
itsNextName( nullptr ),
|
||||
itsReadStream(stream)
|
||||
{
|
||||
itsDocument.ParseStream<0>(itsReadStream);
|
||||
itsIteratorStack.emplace_back(itsDocument.MemberBegin(), itsDocument.MemberEnd());
|
||||
}
|
||||
|
||||
//! Loads some binary data, encoded as a base64 string
|
||||
/*! This will automatically start and finish a node to load the data, and can be called directly by
|
||||
users.
|
||||
|
||||
Note that this follows the same ordering rules specified in the class description in regards
|
||||
to loading in/out of order */
|
||||
void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
|
||||
{
|
||||
itsNextName = name;
|
||||
|
||||
std::string encoded;
|
||||
loadValue( encoded );
|
||||
auto decoded = base64::decode( encoded );
|
||||
|
||||
if( size != decoded.size() )
|
||||
throw Exception("Decoded binary data size does not match specified size");
|
||||
|
||||
std::memcpy( data, decoded.data(), decoded.size() );
|
||||
itsNextName = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
//! @}
|
||||
/*! @name Internal Functionality
|
||||
Functionality designed for use by those requiring control over the inner mechanisms of
|
||||
the JSONInputArchive */
|
||||
//! @{
|
||||
|
||||
//! An internal iterator that handles both array and object types
|
||||
/*! This class is a variant and holds both types of iterators that
|
||||
rapidJSON supports - one for arrays and one for objects. */
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator() : itsIndex( 0 ), itsType(Null_) {}
|
||||
|
||||
Iterator(MemberIterator begin, MemberIterator end) :
|
||||
itsMemberItBegin(begin), itsMemberItEnd(end), itsIndex(0), itsType(Member)
|
||||
{ }
|
||||
|
||||
Iterator(ValueIterator begin, ValueIterator end) :
|
||||
itsValueItBegin(begin), itsValueItEnd(end), itsIndex(0), itsType(Value)
|
||||
{ }
|
||||
|
||||
//! Advance to the next node
|
||||
Iterator & operator++()
|
||||
{
|
||||
++itsIndex;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Get the value of the current node
|
||||
GenericValue const & value()
|
||||
{
|
||||
switch(itsType)
|
||||
{
|
||||
case Value : return itsValueItBegin[itsIndex];
|
||||
case Member: return itsMemberItBegin[itsIndex].value;
|
||||
default: throw cereal::Exception("Invalid Iterator Type!");
|
||||
}
|
||||
}
|
||||
|
||||
//! Get the name of the current node, or nullptr if it has no name
|
||||
const char * name() const
|
||||
{
|
||||
if( itsType == Member && (itsMemberItBegin + itsIndex) != itsMemberItEnd )
|
||||
return itsMemberItBegin[itsIndex].name.GetString();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//! Adjust our position such that we are at the node with the given name
|
||||
/*! @throws Exception if no such named node exists */
|
||||
inline void search( const char * searchName )
|
||||
{
|
||||
const auto len = std::strlen( searchName );
|
||||
size_t index = 0;
|
||||
for( auto it = itsMemberItBegin; it != itsMemberItEnd; ++it, ++index )
|
||||
if( std::strncmp( searchName, it->name.GetString(), len ) == 0 )
|
||||
{
|
||||
itsIndex = index;
|
||||
return;
|
||||
}
|
||||
|
||||
throw Exception("JSON Parsing failed - provided NVP not found");
|
||||
}
|
||||
|
||||
private:
|
||||
MemberIterator itsMemberItBegin, itsMemberItEnd; //!< The member iterator (object)
|
||||
ValueIterator itsValueItBegin, itsValueItEnd; //!< The value iterator (array)
|
||||
size_t itsIndex; //!< The current index of this iterator
|
||||
enum Type {Value, Member, Null_} itsType; //!< Whether this holds values (array) or members (objects) or nothing
|
||||
};
|
||||
|
||||
//! Searches for the expectedName node if it doesn't match the actualName
|
||||
/*! This needs to be called before every load or node start occurs. This function will
|
||||
check to see if an NVP has been provided (with setNextName) and if so, see if that name matches the actual
|
||||
next name given. If the names do not match, it will search in the current level of the JSON for that name.
|
||||
If the name is not found, an exception will be thrown.
|
||||
|
||||
Resets the NVP name after called.
|
||||
|
||||
@throws Exception if an expectedName is given and not found */
|
||||
inline void search()
|
||||
{
|
||||
// The name an NVP provided with setNextName()
|
||||
if( itsNextName )
|
||||
{
|
||||
// The actual name of the current node
|
||||
auto const actualName = itsIteratorStack.back().name();
|
||||
|
||||
// Do a search if we don't see a name coming up, or if the names don't match
|
||||
if( !actualName || std::strcmp( itsNextName, actualName ) != 0 )
|
||||
itsIteratorStack.back().search( itsNextName );
|
||||
}
|
||||
|
||||
itsNextName = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
//! Starts a new node, going into its proper iterator
|
||||
/*! This places an iterator for the next node to be parsed onto the iterator stack. If the next
|
||||
node is an array, this will be a value iterator, otherwise it will be a member iterator.
|
||||
|
||||
By default our strategy is to start with the document root node and then recursively iterate through
|
||||
all children in the order they show up in the document.
|
||||
We don't need to know NVPs to do this; we'll just blindly load in the order things appear in.
|
||||
|
||||
If we were given an NVP, we will search for it if it does not match our the name of the next node
|
||||
that would normally be loaded. This functionality is provided by search(). */
|
||||
void startNode()
|
||||
{
|
||||
search();
|
||||
|
||||
if(itsIteratorStack.back().value().IsArray())
|
||||
itsIteratorStack.emplace_back(itsIteratorStack.back().value().Begin(), itsIteratorStack.back().value().End());
|
||||
else
|
||||
itsIteratorStack.emplace_back(itsIteratorStack.back().value().MemberBegin(), itsIteratorStack.back().value().MemberEnd());
|
||||
}
|
||||
|
||||
//! Finishes the most recently started node
|
||||
void finishNode()
|
||||
{
|
||||
itsIteratorStack.pop_back();
|
||||
++itsIteratorStack.back();
|
||||
}
|
||||
|
||||
//! Retrieves the current node name
|
||||
/*! @return nullptr if no name exists */
|
||||
const char * getNodeName() const
|
||||
{
|
||||
return itsIteratorStack.back().name();
|
||||
}
|
||||
|
||||
//! Sets the name for the next node created with startNode
|
||||
void setNextName( const char * name )
|
||||
{
|
||||
itsNextName = name;
|
||||
}
|
||||
|
||||
//! Loads a value from the current node - small signed overload
|
||||
template <class T, traits::EnableIf<std::is_signed<T>::value,
|
||||
sizeof(T) < sizeof(int64_t)> = traits::sfinae> inline
|
||||
void loadValue(T & val)
|
||||
{
|
||||
search();
|
||||
|
||||
val = static_cast<T>( itsIteratorStack.back().value().GetInt() );
|
||||
++itsIteratorStack.back();
|
||||
}
|
||||
|
||||
//! Loads a value from the current node - small unsigned overload
|
||||
template <class T, traits::EnableIf<std::is_unsigned<T>::value,
|
||||
sizeof(T) < sizeof(uint64_t),
|
||||
!std::is_same<bool, T>::value> = traits::sfinae> inline
|
||||
void loadValue(T & val)
|
||||
{
|
||||
search();
|
||||
|
||||
val = static_cast<T>( itsIteratorStack.back().value().GetUint() );
|
||||
++itsIteratorStack.back();
|
||||
}
|
||||
|
||||
//! Loads a value from the current node - bool overload
|
||||
void loadValue(bool & val) { search(); val = itsIteratorStack.back().value().GetBool_(); ++itsIteratorStack.back(); }
|
||||
//! Loads a value from the current node - int64 overload
|
||||
void loadValue(int64_t & val) { search(); val = itsIteratorStack.back().value().GetInt64(); ++itsIteratorStack.back(); }
|
||||
//! Loads a value from the current node - uint64 overload
|
||||
void loadValue(uint64_t & val) { search(); val = itsIteratorStack.back().value().GetUint64(); ++itsIteratorStack.back(); }
|
||||
//! Loads a value from the current node - float overload
|
||||
void loadValue(float & val) { search(); val = static_cast<float>(itsIteratorStack.back().value().GetDouble()); ++itsIteratorStack.back(); }
|
||||
//! Loads a value from the current node - double overload
|
||||
void loadValue(double & val) { search(); val = itsIteratorStack.back().value().GetDouble(); ++itsIteratorStack.back(); }
|
||||
//! Loads a value from the current node - string overload
|
||||
void loadValue(std::string & val) { search(); val = itsIteratorStack.back().value().GetString(); ++itsIteratorStack.back(); }
|
||||
|
||||
// Special cases to handle various flavors of long, which tend to conflict with
|
||||
// the int32_t or int64_t on various compiler/OS combinations. MSVC doesn't need any of this.
|
||||
#ifndef _MSC_VER
|
||||
private:
|
||||
//! 32 bit signed long loading from current node
|
||||
template <class T> inline
|
||||
typename std::enable_if<sizeof(T) == sizeof(std::int32_t) && std::is_signed<T>::value, void>::type
|
||||
loadLong(T & l){ loadValue( reinterpret_cast<std::int32_t&>( l ) ); }
|
||||
|
||||
//! non 32 bit signed long loading from current node
|
||||
template <class T> inline
|
||||
typename std::enable_if<sizeof(T) == sizeof(std::int64_t) && std::is_signed<T>::value, void>::type
|
||||
loadLong(T & l){ loadValue( reinterpret_cast<std::int64_t&>( l ) ); }
|
||||
|
||||
//! 32 bit unsigned long loading from current node
|
||||
template <class T> inline
|
||||
typename std::enable_if<sizeof(T) == sizeof(std::uint32_t) && !std::is_signed<T>::value, void>::type
|
||||
loadLong(T & lu){ loadValue( reinterpret_cast<std::uint32_t&>( lu ) ); }
|
||||
|
||||
//! non 32 bit unsigned long loading from current node
|
||||
template <class T> inline
|
||||
typename std::enable_if<sizeof(T) == sizeof(std::uint64_t) && !std::is_signed<T>::value, void>::type
|
||||
loadLong(T & lu){ loadValue( reinterpret_cast<std::uint64_t&>( lu ) ); }
|
||||
|
||||
public:
|
||||
//! Serialize a long if it would not be caught otherwise
|
||||
template <class T> inline
|
||||
typename std::enable_if<std::is_same<T, long>::value &&
|
||||
!std::is_same<T, std::int32_t>::value &&
|
||||
!std::is_same<T, std::int64_t>::value, void>::type
|
||||
loadValue( T & t ){ loadLong(t); }
|
||||
|
||||
//! Serialize an unsigned long if it would not be caught otherwise
|
||||
template <class T> inline
|
||||
typename std::enable_if<std::is_same<T, unsigned long>::value &&
|
||||
!std::is_same<T, std::uint32_t>::value &&
|
||||
!std::is_same<T, std::uint64_t>::value, void>::type
|
||||
loadValue( T & t ){ loadLong(t); }
|
||||
#endif // _MSC_VER
|
||||
|
||||
private:
|
||||
//! Convert a string to a long long
|
||||
void stringToNumber( std::string const & str, long long & val ) { val = std::stoll( str ); }
|
||||
//! Convert a string to an unsigned long long
|
||||
void stringToNumber( std::string const & str, unsigned long long & val ) { val = std::stoull( str ); }
|
||||
//! Convert a string to a long double
|
||||
void stringToNumber( std::string const & str, long double & val ) { val = std::stold( str ); }
|
||||
|
||||
public:
|
||||
//! Loads a value from the current node - long double and long long overloads
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value,
|
||||
!std::is_same<T, long>::value,
|
||||
!std::is_same<T, unsigned long>::value,
|
||||
!std::is_same<T, std::int64_t>::value,
|
||||
!std::is_same<T, std::uint64_t>::value,
|
||||
(sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae>
|
||||
inline void loadValue(T & val)
|
||||
{
|
||||
std::string encoded;
|
||||
loadValue( encoded );
|
||||
stringToNumber( encoded, val );
|
||||
}
|
||||
|
||||
//! Loads the size for a SizeTag
|
||||
void loadSize(size_type & size)
|
||||
{
|
||||
size = (itsIteratorStack.rbegin() + 1)->value().Size();
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
private:
|
||||
const char * itsNextName; //!< Next name set by NVP
|
||||
ReadStream itsReadStream; //!< Rapidjson write stream
|
||||
std::vector<Iterator> itsIteratorStack; //!< 'Stack' of rapidJSON iterators
|
||||
rapidjson::Document itsDocument; //!< Rapidjson document
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// JSONArchive prologue and epilogue functions
|
||||
// ######################################################################
|
||||
|
||||
// ######################################################################
|
||||
//! Prologue for NVPs for JSON archives
|
||||
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||
template <class T> inline
|
||||
void prologue( JSONOutputArchive &, NameValuePair<T> const & )
|
||||
{ }
|
||||
|
||||
//! Prologue for NVPs for JSON archives
|
||||
template <class T> inline
|
||||
void prologue( JSONInputArchive &, NameValuePair<T> const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Epilogue for NVPs for JSON archives
|
||||
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||
template <class T> inline
|
||||
void epilogue( JSONOutputArchive &, NameValuePair<T> const & )
|
||||
{ }
|
||||
|
||||
//! Epilogue for NVPs for JSON archives
|
||||
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||
template <class T> inline
|
||||
void epilogue( JSONInputArchive &, NameValuePair<T> const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Prologue for SizeTags for JSON archives
|
||||
/*! SizeTags are strictly ignored for JSON, they just indicate
|
||||
that the current node should be made into an array */
|
||||
template <class T> inline
|
||||
void prologue( JSONOutputArchive & ar, SizeTag<T> const & )
|
||||
{
|
||||
ar.makeArray();
|
||||
}
|
||||
|
||||
//! Prologue for SizeTags for JSON archives
|
||||
template <class T> inline
|
||||
void prologue( JSONInputArchive &, SizeTag<T> const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Epilogue for SizeTags for JSON archives
|
||||
/*! SizeTags are strictly ignored for JSON */
|
||||
template <class T> inline
|
||||
void epilogue( JSONOutputArchive &, SizeTag<T> const & )
|
||||
{ }
|
||||
|
||||
//! Epilogue for SizeTags for JSON archives
|
||||
template <class T> inline
|
||||
void epilogue( JSONInputArchive &, SizeTag<T> const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Prologue for all other types for JSON archives (except minimal types)
|
||||
/*! Starts a new node, named either automatically or by some NVP,
|
||||
that may be given data by the type about to be archived
|
||||
|
||||
Minimal types do not start or finish nodes */
|
||||
template <class T, traits::DisableIf<std::is_arithmetic<T>::value ||
|
||||
traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, JSONOutputArchive>::value ||
|
||||
traits::has_minimal_output_serialization<T, JSONOutputArchive>::value> = traits::sfinae>
|
||||
inline void prologue( JSONOutputArchive & ar, T const & )
|
||||
{
|
||||
ar.startNode();
|
||||
}
|
||||
|
||||
//! Prologue for all other types for JSON archives
|
||||
template <class T, traits::DisableIf<std::is_arithmetic<T>::value ||
|
||||
traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, JSONInputArchive>::value ||
|
||||
traits::has_minimal_input_serialization<T, JSONInputArchive>::value> = traits::sfinae>
|
||||
inline void prologue( JSONInputArchive & ar, T const & )
|
||||
{
|
||||
ar.startNode();
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! Epilogue for all other types other for JSON archives (except minimal types
|
||||
/*! Finishes the node created in the prologue
|
||||
|
||||
Minimal types do not start or finish nodes */
|
||||
template <class T, traits::DisableIf<std::is_arithmetic<T>::value ||
|
||||
traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, JSONOutputArchive>::value ||
|
||||
traits::has_minimal_output_serialization<T, JSONOutputArchive>::value> = traits::sfinae>
|
||||
inline void epilogue( JSONOutputArchive & ar, T const & )
|
||||
{
|
||||
ar.finishNode();
|
||||
}
|
||||
|
||||
//! Epilogue for all other types other for JSON archives
|
||||
template <class T, traits::DisableIf<std::is_arithmetic<T>::value ||
|
||||
traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, JSONInputArchive>::value ||
|
||||
traits::has_minimal_input_serialization<T, JSONInputArchive>::value> = traits::sfinae>
|
||||
inline void epilogue( JSONInputArchive & ar, T const & )
|
||||
{
|
||||
ar.finishNode();
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! Prologue for arithmetic types for JSON archives
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||
void prologue( JSONOutputArchive & ar, T const & )
|
||||
{
|
||||
ar.writeName();
|
||||
}
|
||||
|
||||
//! Prologue for arithmetic types for JSON archives
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||
void prologue( JSONInputArchive &, T const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Epilogue for arithmetic types for JSON archives
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||
void epilogue( JSONOutputArchive &, T const & )
|
||||
{ }
|
||||
|
||||
//! Epilogue for arithmetic types for JSON archives
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||
void epilogue( JSONInputArchive &, T const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Prologue for strings for JSON archives
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void prologue(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const &)
|
||||
{
|
||||
ar.writeName();
|
||||
}
|
||||
|
||||
//! Prologue for strings for JSON archives
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void prologue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Epilogue for strings for JSON archives
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void epilogue(JSONOutputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
|
||||
{ }
|
||||
|
||||
//! Epilogue for strings for JSON archives
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void epilogue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
// Common JSONArchive serialization functions
|
||||
// ######################################################################
|
||||
//! Serializing NVP types to JSON
|
||||
template <class T> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive & ar, NameValuePair<T> const & t )
|
||||
{
|
||||
ar.setNextName( t.name );
|
||||
ar( t.value );
|
||||
}
|
||||
|
||||
template <class T> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, NameValuePair<T> & t )
|
||||
{
|
||||
ar.setNextName( t.name );
|
||||
ar( t.value );
|
||||
}
|
||||
|
||||
//! Saving for arithmetic to JSON
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, T const & t)
|
||||
{
|
||||
ar.saveValue( t );
|
||||
}
|
||||
|
||||
//! Loading arithmetic from JSON
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, T & t)
|
||||
{
|
||||
ar.loadValue( t );
|
||||
}
|
||||
|
||||
//! saving string to JSON
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
|
||||
{
|
||||
ar.saveValue( str );
|
||||
}
|
||||
|
||||
//! loading string from JSON
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
|
||||
{
|
||||
ar.loadValue( str );
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! Saving SizeTags to JSON
|
||||
template <class T> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive &, SizeTag<T> const & )
|
||||
{
|
||||
// nothing to do here, we don't explicitly save the size
|
||||
}
|
||||
|
||||
//! Loading SizeTags from JSON
|
||||
template <class T> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, SizeTag<T> & st )
|
||||
{
|
||||
ar.loadSize( st.size );
|
||||
}
|
||||
} // namespace cereal
|
||||
|
||||
// register archives for polymorphic support
|
||||
CEREAL_REGISTER_ARCHIVE(cereal::JSONInputArchive)
|
||||
CEREAL_REGISTER_ARCHIVE(cereal::JSONOutputArchive)
|
||||
|
||||
// tie input and output archives together
|
||||
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::JSONInputArchive, cereal::JSONOutputArchive)
|
||||
|
||||
#endif // CEREAL_ARCHIVES_JSON_HPP_
|
245
3rdparty/include/cereal/archives/portable_binary.hpp
vendored
Normal file
245
3rdparty/include/cereal/archives/portable_binary.hpp
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
/*! \file binary.hpp
|
||||
\brief Binary input and output archives */
|
||||
/*
|
||||
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of cereal nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
|
||||
#define CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
|
||||
namespace cereal
|
||||
{
|
||||
namespace portable_binary_detail
|
||||
{
|
||||
//! Returns true if the current machine is little endian
|
||||
/*! @ingroup Internal */
|
||||
inline bool is_little_endian()
|
||||
{
|
||||
static std::int32_t test = 1;
|
||||
return *reinterpret_cast<std::int8_t*>( &test ) == 1;
|
||||
}
|
||||
|
||||
//! Swaps the order of bytes for some chunk of memory
|
||||
/*! @param data The data as a uint8_t pointer
|
||||
@tparam DataSize The true size of the data
|
||||
@ingroup Internal */
|
||||
template <std::size_t DataSize>
|
||||
inline void swap_bytes( std::uint8_t * data )
|
||||
{
|
||||
for( std::size_t i = 0, end = DataSize / 2; i < end; ++i )
|
||||
std::swap( data[i], data[DataSize - i - 1] );
|
||||
}
|
||||
} // end namespace portable_binary_detail
|
||||
|
||||
// ######################################################################
|
||||
//! An output archive designed to save data in a compact binary representation portable over different architectures
|
||||
/*! This archive outputs data to a stream in an extremely compact binary
|
||||
representation with as little extra metadata as possible.
|
||||
|
||||
This archive will record the endianness of the data and assuming that
|
||||
the user takes care of ensuring serialized types are the same size
|
||||
across machines, is portable over different architectures.
|
||||
|
||||
When using a binary archive and a file stream, you must use the
|
||||
std::ios::binary format flag to avoid having your data altered
|
||||
inadvertently.
|
||||
|
||||
\warning This archive has not been thoroughly tested across different architectures.
|
||||
Please report any issues, optimizations, or feature requests at
|
||||
<a href="www.github.com/USCiLab/cereal">the project github</a>.
|
||||
|
||||
\ingroup Archives */
|
||||
class PortableBinaryOutputArchive : public OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>
|
||||
{
|
||||
public:
|
||||
//! Construct, outputting to the provided stream
|
||||
/*! @param stream The stream to output to. Can be a stringstream, a file stream, or
|
||||
even cout! */
|
||||
PortableBinaryOutputArchive(std::ostream & stream) :
|
||||
OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
|
||||
itsStream(stream)
|
||||
{
|
||||
this->operator()( portable_binary_detail::is_little_endian() );
|
||||
}
|
||||
|
||||
//! Writes size bytes of data to the output stream
|
||||
void saveBinary( const void * data, std::size_t size )
|
||||
{
|
||||
auto const writtenSize = static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size ) );
|
||||
|
||||
if(writtenSize != size)
|
||||
throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream & itsStream;
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
//! An input archive designed to load data saved using PortableBinaryOutputArchive
|
||||
/*! This archive outputs data to a stream in an extremely compact binary
|
||||
representation with as little extra metadata as possible.
|
||||
|
||||
This archive will load the endianness of the serialized data and
|
||||
if necessary transform it to match that of the local machine. This comes
|
||||
at a significant performance cost compared to non portable archives if
|
||||
the transformation is necessary, and also causes a small performance hit
|
||||
even if it is not necessary.
|
||||
|
||||
It is recommended to use portable archives only if you know that you will
|
||||
be sending binary data to machines with different endianness.
|
||||
|
||||
The archive will do nothing to ensure types are the same size - that is
|
||||
the responsibility of the user.
|
||||
|
||||
When using a binary archive and a file stream, you must use the
|
||||
std::ios::binary format flag to avoid having your data altered
|
||||
inadvertently.
|
||||
|
||||
\warning This archive has not been thoroughly tested across different architectures.
|
||||
Please report any issues, optimizations, or feature requests at
|
||||
<a href="www.github.com/USCiLab/cereal">the project github</a>.
|
||||
|
||||
\ingroup Archives */
|
||||
class PortableBinaryInputArchive : public InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>
|
||||
{
|
||||
public:
|
||||
//! Construct, loading from the provided stream
|
||||
/*! @param stream The stream to read from. */
|
||||
PortableBinaryInputArchive(std::istream & stream) :
|
||||
InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
|
||||
itsStream(stream),
|
||||
itsConvertEndianness( false )
|
||||
{
|
||||
bool streamLittleEndian;
|
||||
this->operator()( streamLittleEndian );
|
||||
itsConvertEndianness = portable_binary_detail::is_little_endian() ^ streamLittleEndian;
|
||||
}
|
||||
|
||||
//! Reads size bytes of data from the input stream
|
||||
/*! @param data The data to save
|
||||
@param size The number of bytes in the data
|
||||
@tparam DataSize T The size of the actual type of the data elements being loaded */
|
||||
template <std::size_t DataSize>
|
||||
void loadBinary( void * const data, std::size_t size )
|
||||
{
|
||||
// load data
|
||||
auto const readSize = static_cast<std::size_t>( itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size ) );
|
||||
|
||||
if(readSize != size)
|
||||
throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
|
||||
|
||||
// flip bits if needed
|
||||
if( itsConvertEndianness )
|
||||
{
|
||||
std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( data );
|
||||
for( std::size_t i = 0; i < size; i += DataSize )
|
||||
portable_binary_detail::swap_bytes<DataSize>( ptr );
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::istream & itsStream;
|
||||
bool itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// Common BinaryArchive serialization functions
|
||||
|
||||
//! Saving for POD types to portable binary
|
||||
template<class T> inline
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
|
||||
CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, T const & t)
|
||||
{
|
||||
static_assert( !std::is_floating_point<T>::value ||
|
||||
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
|
||||
"Portable binary only supports IEEE 754 standardized floating point" );
|
||||
ar.saveBinary(std::addressof(t), sizeof(t));
|
||||
}
|
||||
|
||||
//! Loading for POD types from portable binary
|
||||
template<class T> inline
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
|
||||
CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, T & t)
|
||||
{
|
||||
static_assert( !std::is_floating_point<T>::value ||
|
||||
(std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
|
||||
"Portable binary only supports IEEE 754 standardized floating point" );
|
||||
ar.template loadBinary<sizeof(T)>(std::addressof(t), sizeof(t));
|
||||
}
|
||||
|
||||
//! Serializing NVP types to portable binary
|
||||
template <class Archive, class T> inline
|
||||
CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
|
||||
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t )
|
||||
{
|
||||
ar( t.value );
|
||||
}
|
||||
|
||||
//! Serializing SizeTags to portable binary
|
||||
template <class Archive, class T> inline
|
||||
CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
|
||||
CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t )
|
||||
{
|
||||
ar( t.size );
|
||||
}
|
||||
|
||||
//! Saving binary data to portable binary
|
||||
template <class T> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, BinaryData<T> const & bd)
|
||||
{
|
||||
typedef typename std::remove_pointer<T>::type TT;
|
||||
static_assert( !std::is_floating_point<TT>::value ||
|
||||
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
|
||||
"Portable binary only supports IEEE 754 standardized floating point" );
|
||||
|
||||
ar.saveBinary( bd.data, static_cast<std::size_t>( bd.size ) );
|
||||
}
|
||||
|
||||
//! Loading binary data from portable binary
|
||||
template <class T> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, BinaryData<T> & bd)
|
||||
{
|
||||
typedef typename std::remove_pointer<T>::type TT;
|
||||
static_assert( !std::is_floating_point<TT>::value ||
|
||||
(std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
|
||||
"Portable binary only supports IEEE 754 standardized floating point" );
|
||||
|
||||
ar.template loadBinary<sizeof(TT)>( bd.data, static_cast<std::size_t>( bd.size ) );
|
||||
}
|
||||
} // namespace cereal
|
||||
|
||||
// register archives for polymorphic support
|
||||
CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryOutputArchive)
|
||||
CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryInputArchive)
|
||||
|
||||
// tie input and output archives together
|
||||
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive)
|
||||
|
||||
#endif // CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
|
882
3rdparty/include/cereal/archives/xml.hpp
vendored
Normal file
882
3rdparty/include/cereal/archives/xml.hpp
vendored
Normal file
@ -0,0 +1,882 @@
|
||||
/*! \file xml.hpp
|
||||
\brief XML input and output archives */
|
||||
/*
|
||||
Copyright (c) 2014, Randolph Voorhies, Shane Grant
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of cereal nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef CEREAL_ARCHIVES_XML_HPP_
|
||||
#define CEREAL_ARCHIVES_XML_HPP_
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/details/util.hpp>
|
||||
|
||||
#include <cereal/external/rapidxml/rapidxml.hpp>
|
||||
#include <cereal/external/rapidxml/rapidxml_print.hpp>
|
||||
#include <cereal/external/base64.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
namespace cereal
|
||||
{
|
||||
namespace xml_detail
|
||||
{
|
||||
#ifndef CEREAL_XML_STRING_VALUE
|
||||
//! The default name for the root node in a cereal xml archive.
|
||||
/*! You can define CEREAL_XML_STRING_VALUE to be different assuming you do so
|
||||
before this file is included. */
|
||||
#define CEREAL_XML_STRING_VALUE "cereal"
|
||||
#endif // CEREAL_XML_STRING_VALUE
|
||||
|
||||
//! The name given to the root node in a cereal xml archive
|
||||
static const char * CEREAL_XML_STRING = CEREAL_XML_STRING_VALUE;
|
||||
|
||||
//! Returns true if the character is whitespace
|
||||
inline bool isWhitespace( char c )
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||
}
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! An output archive designed to save data to XML
|
||||
/*! This archive uses RapidXML to build an in memory XML tree of the
|
||||
data it serializes before outputting it to its stream upon destruction.
|
||||
The envisioned way of using this archive is in an RAII fashion, letting
|
||||
the automatic destruction of the object cause the flush to its stream.
|
||||
|
||||
XML archives provides a human readable output but at decreased
|
||||
performance (both in time and space) compared to binary archives.
|
||||
|
||||
XML benefits greatly from name-value pairs, which if present, will
|
||||
name the nodes in the output. If these are not present, each level
|
||||
of the output tree will be given an automatically generated delimited name.
|
||||
|
||||
The precision of the output archive controls the number of decimals output
|
||||
for floating point numbers and should be sufficiently large (i.e. at least 20)
|
||||
if there is a desire to have binary equality between the numbers output and
|
||||
those read in. In general you should expect a loss of precision when going
|
||||
from floating point to text and back.
|
||||
|
||||
XML archives can optionally print the type of everything they serialize, which
|
||||
adds an attribute to each node.
|
||||
|
||||
XML archives do not output the size information for any dynamically sized structure
|
||||
and instead infer it from the number of children for a node. This means that data
|
||||
can be hand edited for dynamic sized structures and will still be readable. This
|
||||
is accomplished through the cereal::SizeTag object, which will also add an attribute
|
||||
to its parent field.
|
||||
\ingroup Archives */
|
||||
class XMLOutputArchive : public OutputArchive<XMLOutputArchive>, public traits::TextArchive
|
||||
{
|
||||
public:
|
||||
/*! @name Common Functionality
|
||||
Common use cases for directly interacting with an XMLOutputArchive */
|
||||
//! @{
|
||||
|
||||
//! A class containing various advanced options for the XML archive
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
//! Default options
|
||||
static Options Default(){ return Options(); }
|
||||
|
||||
//! Default options with no indentation
|
||||
static Options NoIndent(){ return Options( std::numeric_limits<double>::max_digits10, false ); }
|
||||
|
||||
//! Specify specific options for the XMLOutputArchive
|
||||
/*! @param precision The precision used for floating point numbers
|
||||
@param indent Whether to indent each line of XML
|
||||
@param outputType Whether to output the type of each serialized object as an attribute */
|
||||
explicit Options( int precision = std::numeric_limits<double>::max_digits10,
|
||||
bool indent = true,
|
||||
bool outputType = false ) :
|
||||
itsPrecision( precision ),
|
||||
itsIndent( indent ),
|
||||
itsOutputType( outputType ) { }
|
||||
|
||||
private:
|
||||
friend class XMLOutputArchive;
|
||||
int itsPrecision;
|
||||
bool itsIndent;
|
||||
bool itsOutputType;
|
||||
};
|
||||
|
||||
//! Construct, outputting to the provided stream upon destruction
|
||||
/*! @param stream The stream to output to. Note that XML is only guaranteed to flush
|
||||
its output to the stream upon destruction.
|
||||
@param options The XML specific options to use. See the Options struct
|
||||
for the values of default parameters */
|
||||
XMLOutputArchive( std::ostream & stream, Options const & options = Options::Default() ) :
|
||||
OutputArchive<XMLOutputArchive>(this),
|
||||
itsStream(stream),
|
||||
itsOutputType( options.itsOutputType ),
|
||||
itsIndent( options.itsIndent )
|
||||
{
|
||||
// rapidxml will delete all allocations when xml_document is cleared
|
||||
auto node = itsXML.allocate_node( rapidxml::node_declaration );
|
||||
node->append_attribute( itsXML.allocate_attribute( "version", "1.0" ) );
|
||||
node->append_attribute( itsXML.allocate_attribute( "encoding", "utf-8" ) );
|
||||
itsXML.append_node( node );
|
||||
|
||||
// allocate root node
|
||||
auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
|
||||
itsXML.append_node( root );
|
||||
itsNodes.emplace( root );
|
||||
|
||||
// set attributes on the streams
|
||||
itsStream << std::boolalpha;
|
||||
itsStream.precision( options.itsPrecision );
|
||||
itsOS << std::boolalpha;
|
||||
itsOS.precision( options.itsPrecision );
|
||||
}
|
||||
|
||||
//! Destructor, flushes the XML
|
||||
~XMLOutputArchive()
|
||||
{
|
||||
const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting;
|
||||
rapidxml::print( itsStream, itsXML, flags );
|
||||
itsXML.clear();
|
||||
}
|
||||
|
||||
//! Saves some binary data, encoded as a base64 string, with an optional name
|
||||
/*! This can be called directly by users and it will automatically create a child node for
|
||||
the current XML node, populate it with a base64 encoded string, and optionally name
|
||||
it. The node will be finished after it has been populated. */
|
||||
void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
|
||||
{
|
||||
itsNodes.top().name = name;
|
||||
|
||||
startNode();
|
||||
|
||||
auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
|
||||
saveValue( base64string );
|
||||
|
||||
if( itsOutputType )
|
||||
itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", "cereal binary data" ) );
|
||||
|
||||
finishNode();
|
||||
};
|
||||
|
||||
//! @}
|
||||
/*! @name Internal Functionality
|
||||
Functionality designed for use by those requiring control over the inner mechanisms of
|
||||
the XMLOutputArchive */
|
||||
//! @{
|
||||
|
||||
//! Creates a new node that is a child of the node at the top of the stack
|
||||
/*! Nodes will be given a name that has either been pre-set by a name value pair,
|
||||
or generated based upon a counter unique to the parent node. If you want to
|
||||
give a node a specific name, use setNextName prior to calling startNode.
|
||||
|
||||
The node will then be pushed onto the node stack. */
|
||||
void startNode()
|
||||
{
|
||||
// generate a name for this new node
|
||||
const auto nameString = itsNodes.top().getValueName();
|
||||
|
||||
// allocate strings for all of the data in the XML object
|
||||
auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
|
||||
|
||||
// insert into the XML
|
||||
auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() );
|
||||
itsNodes.top().node->append_node( node );
|
||||
itsNodes.emplace( node );
|
||||
}
|
||||
|
||||
//! Designates the most recently added node as finished
|
||||
void finishNode()
|
||||
{
|
||||
itsNodes.pop();
|
||||
}
|
||||
|
||||
//! Sets the name for the next node created with startNode
|
||||
void setNextName( const char * name )
|
||||
{
|
||||
itsNodes.top().name = name;
|
||||
}
|
||||
|
||||
//! Saves some data, encoded as a string, into the current top level node
|
||||
/*! The data will be be named with the most recent name if one exists,
|
||||
otherwise it will be given some default delimited value that depends upon
|
||||
the parent node */
|
||||
template <class T> inline
|
||||
void saveValue( T const & value )
|
||||
{
|
||||
itsOS.clear(); itsOS.seekp( 0, std::ios::beg );
|
||||
itsOS << value << std::ends;
|
||||
|
||||
const auto strValue = itsOS.str();
|
||||
|
||||
// If the first or last character is a whitespace, add xml:space attribute
|
||||
// the string always contains a '\0' added by std::ends, so the last character is at len-2 and an 'empty'
|
||||
// string has a length of 1 or lower
|
||||
const auto len = strValue.length();
|
||||
if ( len > 1 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 2] ) ) )
|
||||
{
|
||||
itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "xml:space", "preserve" ) );
|
||||
}
|
||||
|
||||
// allocate strings for all of the data in the XML object
|
||||
auto dataPtr = itsXML.allocate_string( itsOS.str().c_str(), itsOS.str().length() + 1 );
|
||||
|
||||
// insert into the XML
|
||||
itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) );
|
||||
}
|
||||
|
||||
//! Overload for uint8_t prevents them from being serialized as characters
|
||||
void saveValue( uint8_t const & value )
|
||||
{
|
||||
saveValue( static_cast<uint32_t>( value ) );
|
||||
}
|
||||
|
||||
//! Overload for int8_t prevents them from being serialized as characters
|
||||
void saveValue( int8_t const & value )
|
||||
{
|
||||
saveValue( static_cast<int32_t>( value ) );
|
||||
}
|
||||
|
||||
//! Causes the type to be appended as an attribute to the most recently made node if output type is set to true
|
||||
template <class T> inline
|
||||
void insertType()
|
||||
{
|
||||
if( !itsOutputType )
|
||||
return;
|
||||
|
||||
// generate a name for this new node
|
||||
const auto nameString = util::demangledName<T>();
|
||||
|
||||
// allocate strings for all of the data in the XML object
|
||||
auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
|
||||
|
||||
itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", namePtr ) );
|
||||
}
|
||||
|
||||
//! Appends an attribute to the current top level node
|
||||
void appendAttribute( const char * name, const char * value )
|
||||
{
|
||||
auto namePtr = itsXML.allocate_string( name );
|
||||
auto valuePtr = itsXML.allocate_string( value );
|
||||
itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
|
||||
}
|
||||
|
||||
protected:
|
||||
//! A struct that contains metadata about a node
|
||||
struct NodeInfo
|
||||
{
|
||||
NodeInfo( rapidxml::xml_node<> * n = nullptr,
|
||||
const char * nm = nullptr ) :
|
||||
node( n ),
|
||||
counter( 0 ),
|
||||
name( nm )
|
||||
{ }
|
||||
|
||||
rapidxml::xml_node<> * node; //!< A pointer to this node
|
||||
size_t counter; //!< The counter for naming child nodes
|
||||
const char * name; //!< The name for the next child node
|
||||
|
||||
//! Gets the name for the next child node created from this node
|
||||
/*! The name will be automatically generated using the counter if
|
||||
a name has not been previously set. If a name has been previously
|
||||
set, that name will be returned only once */
|
||||
std::string getValueName()
|
||||
{
|
||||
if( name )
|
||||
{
|
||||
auto n = name;
|
||||
name = nullptr;
|
||||
return {n};
|
||||
}
|
||||
else
|
||||
return "value" + std::to_string( counter++ ) + "\0";
|
||||
}
|
||||
}; // NodeInfo
|
||||
|
||||
//! @}
|
||||
|
||||
private:
|
||||
std::ostream & itsStream; //!< The output stream
|
||||
rapidxml::xml_document<> itsXML; //!< The XML document
|
||||
std::stack<NodeInfo> itsNodes; //!< A stack of nodes added to the document
|
||||
std::ostringstream itsOS; //!< Used to format strings internally
|
||||
bool itsOutputType; //!< Controls whether type information is printed
|
||||
bool itsIndent; //!< Controls whether indenting is used
|
||||
}; // XMLOutputArchive
|
||||
|
||||
// ######################################################################
|
||||
//! An output archive designed to load data from XML
|
||||
/*! This archive uses RapidXML to build an in memory XML tree of the
|
||||
data in the stream it is given before loading any types serialized.
|
||||
|
||||
Input XML should have been produced by the XMLOutputArchive. Data can
|
||||
only be added to dynamically sized containers - the input archive will
|
||||
determine their size by looking at the number of child nodes. Data that
|
||||
did not originate from an XMLOutputArchive is not officially supported,
|
||||
but may be possible to use if properly formatted.
|
||||
|
||||
The XMLInputArchive does not require that nodes are loaded in the same
|
||||
order they were saved by XMLOutputArchive. Using name value pairs (NVPs),
|
||||
it is possible to load in an out of order fashion or otherwise skip/select
|
||||
specific nodes to load.
|
||||
|
||||
The default behavior of the input archive is to read sequentially starting
|
||||
with the first node and exploring its children. When a given NVP does
|
||||
not match the read in name for a node, the archive will search for that
|
||||
node at the current level and load it if it exists. After loading an out of
|
||||
order node, the archive will then proceed back to loading sequentially from
|
||||
its new position.
|
||||
|
||||
Consider this simple example where loading of some data is skipped:
|
||||
|
||||
@code{cpp}
|
||||
// imagine the input file has someData(1-9) saved in order at the top level node
|
||||
ar( someData1, someData2, someData3 ); // XML loads in the order it sees in the file
|
||||
ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
|
||||
// match expected NVP name, so we search
|
||||
// for the given NVP and load that value
|
||||
ar( someData7, someData8, someData9 ); // with no NVP given, loading resumes at its
|
||||
// current location, proceeding sequentially
|
||||
@endcode
|
||||
|
||||
\ingroup Archives */
|
||||
class XMLInputArchive : public InputArchive<XMLInputArchive>, public traits::TextArchive
|
||||
{
|
||||
public:
|
||||
/*! @name Common Functionality
|
||||
Common use cases for directly interacting with an XMLInputArchive */
|
||||
//! @{
|
||||
|
||||
//! Construct, reading in from the provided stream
|
||||
/*! Reads in an entire XML document from some stream and parses it as soon
|
||||
as serialization starts
|
||||
|
||||
@param stream The stream to read from. Can be a stringstream or a file. */
|
||||
XMLInputArchive( std::istream & stream ) :
|
||||
InputArchive<XMLInputArchive>( this ),
|
||||
itsData( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() )
|
||||
{
|
||||
try
|
||||
{
|
||||
itsData.push_back('\0'); // rapidxml will do terrible things without the data being null terminated
|
||||
itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>( reinterpret_cast<char *>( itsData.data() ) );
|
||||
}
|
||||
catch( rapidxml::parse_error const & )
|
||||
{
|
||||
//std::cerr << "-----Original-----" << std::endl;
|
||||
//stream.seekg(0);
|
||||
//std::cout << std::string( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() ) << std::endl;
|
||||
|
||||
//std::cerr << "-----Error-----" << std::endl;
|
||||
//std::cerr << e.what() << std::endl;
|
||||
//std::cerr << e.where<char>() << std::endl;
|
||||
throw Exception("XML Parsing failed - likely due to invalid characters or invalid naming");
|
||||
}
|
||||
|
||||
// Parse the root
|
||||
auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
|
||||
if( root == nullptr )
|
||||
throw Exception("Could not detect cereal root node - likely due to empty or invalid input");
|
||||
else
|
||||
itsNodes.emplace( root );
|
||||
}
|
||||
|
||||
//! Loads some binary data, encoded as a base64 string, optionally specified by some name
|
||||
/*! This will automatically start and finish a node to load the data, and can be called directly by
|
||||
users.
|
||||
|
||||
Note that this follows the same ordering rules specified in the class description in regards
|
||||
to loading in/out of order */
|
||||
void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
|
||||
{
|
||||
setNextName( name );
|
||||
startNode();
|
||||
|
||||
std::string encoded;
|
||||
loadValue( encoded );
|
||||
|
||||
auto decoded = base64::decode( encoded );
|
||||
|
||||
if( size != decoded.size() )
|
||||
throw Exception("Decoded binary data size does not match specified size");
|
||||
|
||||
std::memcpy( data, decoded.data(), decoded.size() );
|
||||
|
||||
finishNode();
|
||||
};
|
||||
|
||||
//! @}
|
||||
/*! @name Internal Functionality
|
||||
Functionality designed for use by those requiring control over the inner mechanisms of
|
||||
the XMLInputArchive */
|
||||
//! @{
|
||||
|
||||
//! Prepares to start reading the next node
|
||||
/*! This places the next node to be parsed onto the nodes stack.
|
||||
|
||||
By default our strategy is to start with the document root node and then
|
||||
recursively iterate through all children in the order they show up in the document.
|
||||
We don't need to know NVPs do to this; we'll just blindly load in the order things appear in.
|
||||
|
||||
We check to see if the specified NVP matches what the next automatically loaded node is. If they
|
||||
match, we just continue as normal, going in order. If they don't match, we attempt to find a node
|
||||
named after the NVP that is being loaded. If that NVP does not exist, we throw an exception. */
|
||||
void startNode()
|
||||
{
|
||||
auto next = itsNodes.top().child; // By default we would move to the next child node
|
||||
auto const expectedName = itsNodes.top().name; // this is the expected name from the NVP, if provided
|
||||
|
||||
// If we were given an NVP name, look for it in the current level of the document.
|
||||
// We only need to do this if either we have exhausted the siblings of the current level or
|
||||
// the NVP name does not match the name of the node we would normally read next
|
||||
if( expectedName && ( next == nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
|
||||
{
|
||||
next = itsNodes.top().search( expectedName );
|
||||
|
||||
if( next == nullptr )
|
||||
throw Exception("XML Parsing failed - provided NVP not found");
|
||||
}
|
||||
|
||||
itsNodes.emplace( next );
|
||||
}
|
||||
|
||||
//! Finishes reading the current node
|
||||
void finishNode()
|
||||
{
|
||||
// remove current
|
||||
itsNodes.pop();
|
||||
|
||||
// advance parent
|
||||
itsNodes.top().advance();
|
||||
|
||||
// Reset name
|
||||
itsNodes.top().name = nullptr;
|
||||
}
|
||||
|
||||
//! Retrieves the current node name
|
||||
//! will return @c nullptr if the node does not have a name
|
||||
const char * getNodeName() const
|
||||
{
|
||||
return itsNodes.top().node->name();
|
||||
}
|
||||
|
||||
//! Sets the name for the next node created with startNode
|
||||
void setNextName( const char * name )
|
||||
{
|
||||
itsNodes.top().name = name;
|
||||
}
|
||||
|
||||
//! Loads a bool from the current top node
|
||||
template <class T, traits::EnableIf<std::is_unsigned<T>::value,
|
||||
std::is_same<T, bool>::value> = traits::sfinae> inline
|
||||
void loadValue( T & value )
|
||||
{
|
||||
std::istringstream is( itsNodes.top().node->value() );
|
||||
is.setf( std::ios::boolalpha );
|
||||
is >> value;
|
||||
}
|
||||
|
||||
//! Loads a char (signed or unsigned) from the current top node
|
||||
template <class T, traits::EnableIf<std::is_integral<T>::value,
|
||||
!std::is_same<T, bool>::value,
|
||||
sizeof(T) == sizeof(char)> = traits::sfinae> inline
|
||||
void loadValue( T & value )
|
||||
{
|
||||
value = *reinterpret_cast<T*>( itsNodes.top().node->value() );
|
||||
}
|
||||
|
||||
//! Load an int8_t from the current top node (ensures we parse entire number)
|
||||
void loadValue( int8_t & value )
|
||||
{
|
||||
int32_t val; loadValue( val ); value = static_cast<int8_t>( val );
|
||||
}
|
||||
|
||||
//! Load a uint8_t from the current top node (ensures we parse entire number)
|
||||
void loadValue( uint8_t & value )
|
||||
{
|
||||
uint32_t val; loadValue( val ); value = static_cast<uint8_t>( val );
|
||||
}
|
||||
|
||||
//! Loads a type best represented as an unsigned long from the current top node
|
||||
template <class T, traits::EnableIf<std::is_unsigned<T>::value,
|
||||
!std::is_same<T, bool>::value,
|
||||
!std::is_same<T, unsigned char>::value,
|
||||
sizeof(T) < sizeof(long long)> = traits::sfinae> inline
|
||||
void loadValue( T & value )
|
||||
{
|
||||
value = static_cast<T>( std::stoul( itsNodes.top().node->value() ) );
|
||||
}
|
||||
|
||||
//! Loads a type best represented as an unsigned long long from the current top node
|
||||
template <class T, traits::EnableIf<std::is_unsigned<T>::value,
|
||||
!std::is_same<T, bool>::value,
|
||||
sizeof(T) >= sizeof(long long)> = traits::sfinae> inline
|
||||
void loadValue( T & value )
|
||||
{
|
||||
value = static_cast<T>( std::stoull( itsNodes.top().node->value() ) );
|
||||
}
|
||||
|
||||
//! Loads a type best represented as an int from the current top node
|
||||
template <class T, traits::EnableIf<std::is_signed<T>::value,
|
||||
!std::is_same<T, char>::value,
|
||||
sizeof(T) <= sizeof(int)> = traits::sfinae> inline
|
||||
void loadValue( T & value )
|
||||
{
|
||||
value = static_cast<T>( std::stoi( itsNodes.top().node->value() ) );
|
||||
}
|
||||
|
||||
//! Loads a type best represented as a long from the current top node
|
||||
template <class T, traits::EnableIf<std::is_signed<T>::value,
|
||||
(sizeof(T) > sizeof(int)),
|
||||
sizeof(T) <= sizeof(long)> = traits::sfinae> inline
|
||||
void loadValue( T & value )
|
||||
{
|
||||
value = static_cast<T>( std::stol( itsNodes.top().node->value() ) );
|
||||
}
|
||||
|
||||
//! Loads a type best represented as a long long from the current top node
|
||||
template <class T, traits::EnableIf<std::is_signed<T>::value,
|
||||
(sizeof(T) > sizeof(long)),
|
||||
sizeof(T) <= sizeof(long long)> = traits::sfinae> inline
|
||||
void loadValue( T & value )
|
||||
{
|
||||
value = static_cast<T>( std::stoll( itsNodes.top().node->value() ) );
|
||||
}
|
||||
|
||||
//! Loads a type best represented as a float from the current top node
|
||||
void loadValue( float & value )
|
||||
{
|
||||
try
|
||||
{
|
||||
value = std::stof( itsNodes.top().node->value() );
|
||||
}
|
||||
catch( std::out_of_range const & )
|
||||
{
|
||||
// special case for denormalized values
|
||||
std::istringstream is( itsNodes.top().node->value() );
|
||||
is >> value;
|
||||
if( std::fpclassify( value ) != FP_SUBNORMAL )
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
//! Loads a type best represented as a double from the current top node
|
||||
void loadValue( double & value )
|
||||
{
|
||||
try
|
||||
{
|
||||
value = std::stod( itsNodes.top().node->value() );
|
||||
}
|
||||
catch( std::out_of_range const & )
|
||||
{
|
||||
// special case for denormalized values
|
||||
std::istringstream is( itsNodes.top().node->value() );
|
||||
is >> value;
|
||||
if( std::fpclassify( value ) != FP_SUBNORMAL )
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
//! Loads a type best represented as a long double from the current top node
|
||||
void loadValue( long double & value )
|
||||
{
|
||||
try
|
||||
{
|
||||
value = std::stold( itsNodes.top().node->value() );
|
||||
}
|
||||
catch( std::out_of_range const & )
|
||||
{
|
||||
// special case for denormalized values
|
||||
std::istringstream is( itsNodes.top().node->value() );
|
||||
is >> value;
|
||||
if( std::fpclassify( value ) != FP_SUBNORMAL )
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
//! Loads a string from the current node from the current top node
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
|
||||
{
|
||||
std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
|
||||
|
||||
str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
|
||||
std::istreambuf_iterator<CharT, Traits>() );
|
||||
}
|
||||
|
||||
//! Loads the size of the current top node
|
||||
template <class T> inline
|
||||
void loadSize( T & value )
|
||||
{
|
||||
value = getNumChildren( itsNodes.top().node );
|
||||
}
|
||||
|
||||
protected:
|
||||
//! Gets the number of children (usually interpreted as size) for the specified node
|
||||
static size_t getNumChildren( rapidxml::xml_node<> * node )
|
||||
{
|
||||
size_t size = 0;
|
||||
node = node->first_node(); // get first child
|
||||
|
||||
while( node != nullptr )
|
||||
{
|
||||
++size;
|
||||
node = node->next_sibling();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
//! A struct that contains metadata about a node
|
||||
/*! Keeps track of some top level node, its number of
|
||||
remaining children, and the current active child node */
|
||||
struct NodeInfo
|
||||
{
|
||||
NodeInfo( rapidxml::xml_node<> * n = nullptr ) :
|
||||
node( n ),
|
||||
child( n->first_node() ),
|
||||
size( XMLInputArchive::getNumChildren( n ) ),
|
||||
name( nullptr )
|
||||
{ }
|
||||
|
||||
//! Advances to the next sibling node of the child
|
||||
/*! If this is the last sibling child will be null after calling */
|
||||
void advance()
|
||||
{
|
||||
if( size > 0 )
|
||||
{
|
||||
--size;
|
||||
child = child->next_sibling();
|
||||
}
|
||||
}
|
||||
|
||||
//! Searches for a child with the given name in this node
|
||||
/*! @param searchName The name to search for (must be null terminated)
|
||||
@return The node if found, nullptr otherwise */
|
||||
rapidxml::xml_node<> * search( const char * searchName )
|
||||
{
|
||||
if( searchName )
|
||||
{
|
||||
size_t new_size = XMLInputArchive::getNumChildren( node );
|
||||
const size_t name_size = rapidxml::internal::measure( searchName );
|
||||
|
||||
for( auto new_child = node->first_node(); new_child != nullptr; new_child = new_child->next_sibling() )
|
||||
{
|
||||
if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
|
||||
{
|
||||
size = new_size;
|
||||
child = new_child;
|
||||
|
||||
return new_child;
|
||||
}
|
||||
--new_size;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rapidxml::xml_node<> * node; //!< A pointer to this node
|
||||
rapidxml::xml_node<> * child; //!< A pointer to its current child
|
||||
size_t size; //!< The remaining number of children for this node
|
||||
const char * name; //!< The NVP name for next next child node
|
||||
}; // NodeInfo
|
||||
|
||||
//! @}
|
||||
|
||||
private:
|
||||
std::vector<char> itsData; //!< The raw data loaded
|
||||
rapidxml::xml_document<> itsXML; //!< The XML document
|
||||
std::stack<NodeInfo> itsNodes; //!< A stack of nodes read from the document
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// XMLArchive prologue and epilogue functions
|
||||
// ######################################################################
|
||||
|
||||
// ######################################################################
|
||||
//! Prologue for NVPs for XML output archives
|
||||
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||
template <class T> inline
|
||||
void prologue( XMLOutputArchive &, NameValuePair<T> const & )
|
||||
{ }
|
||||
|
||||
//! Prologue for NVPs for XML input archives
|
||||
template <class T> inline
|
||||
void prologue( XMLInputArchive &, NameValuePair<T> const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Epilogue for NVPs for XML output archives
|
||||
/*! NVPs do not start or finish nodes - they just set up the names */
|
||||
template <class T> inline
|
||||
void epilogue( XMLOutputArchive &, NameValuePair<T> const & )
|
||||
{ }
|
||||
|
||||
//! Epilogue for NVPs for XML input archives
|
||||
template <class T> inline
|
||||
void epilogue( XMLInputArchive &, NameValuePair<T> const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Prologue for SizeTags for XML output archives
|
||||
/*! SizeTags do not start or finish nodes */
|
||||
template <class T> inline
|
||||
void prologue( XMLOutputArchive & ar, SizeTag<T> const & )
|
||||
{
|
||||
ar.appendAttribute( "size", "dynamic" );
|
||||
}
|
||||
|
||||
template <class T> inline
|
||||
void prologue( XMLInputArchive &, SizeTag<T> const & )
|
||||
{ }
|
||||
|
||||
//! Epilogue for SizeTags for XML output archives
|
||||
/*! SizeTags do not start or finish nodes */
|
||||
template <class T> inline
|
||||
void epilogue( XMLOutputArchive &, SizeTag<T> const & )
|
||||
{ }
|
||||
|
||||
template <class T> inline
|
||||
void epilogue( XMLInputArchive &, SizeTag<T> const & )
|
||||
{ }
|
||||
|
||||
// ######################################################################
|
||||
//! Prologue for all other types for XML output archives (except minimal types)
|
||||
/*! Starts a new node, named either automatically or by some NVP,
|
||||
that may be given data by the type about to be archived
|
||||
|
||||
Minimal types do not start or end nodes */
|
||||
template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
|
||||
traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
|
||||
void prologue( XMLOutputArchive & ar, T const & )
|
||||
{
|
||||
ar.startNode();
|
||||
ar.insertType<T>();
|
||||
}
|
||||
|
||||
//! Prologue for all other types for XML input archives (except minimal types)
|
||||
template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
|
||||
traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
|
||||
void prologue( XMLInputArchive & ar, T const & )
|
||||
{
|
||||
ar.startNode();
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! Epilogue for all other types other for XML output archives (except minimal types)
|
||||
/*! Finishes the node created in the prologue
|
||||
|
||||
Minimal types do not start or end nodes */
|
||||
template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
|
||||
traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae> inline
|
||||
void epilogue( XMLOutputArchive & ar, T const & )
|
||||
{
|
||||
ar.finishNode();
|
||||
}
|
||||
|
||||
//! Epilogue for all other types other for XML output archives (except minimal types)
|
||||
template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
|
||||
traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae> inline
|
||||
void epilogue( XMLInputArchive & ar, T const & )
|
||||
{
|
||||
ar.finishNode();
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
// Common XMLArchive serialization functions
|
||||
// ######################################################################
|
||||
|
||||
//! Saving NVP types to XML
|
||||
template <class T> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive & ar, NameValuePair<T> const & t )
|
||||
{
|
||||
ar.setNextName( t.name );
|
||||
ar( t.value );
|
||||
}
|
||||
|
||||
//! Loading NVP types from XML
|
||||
template <class T> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, NameValuePair<T> & t )
|
||||
{
|
||||
ar.setNextName( t.name );
|
||||
ar( t.value );
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! Saving SizeTags to XML
|
||||
template <class T> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive &, SizeTag<T> const & )
|
||||
{ }
|
||||
|
||||
//! Loading SizeTags from XML
|
||||
template <class T> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, SizeTag<T> & st )
|
||||
{
|
||||
ar.loadSize( st.size );
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! Saving for POD types to xml
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, T const & t)
|
||||
{
|
||||
ar.saveValue( t );
|
||||
}
|
||||
|
||||
//! Loading for POD types from xml
|
||||
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, T & t)
|
||||
{
|
||||
ar.loadValue( t );
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! saving string to xml
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
|
||||
{
|
||||
ar.saveValue( str );
|
||||
}
|
||||
|
||||
//! loading string from xml
|
||||
template<class CharT, class Traits, class Alloc> inline
|
||||
void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
|
||||
{
|
||||
ar.loadValue( str );
|
||||
}
|
||||
} // namespace cereal
|
||||
|
||||
// register archives for polymorphic support
|
||||
CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive)
|
||||
CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive)
|
||||
|
||||
// tie input and output archives together
|
||||
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::XMLInputArchive, cereal::XMLOutputArchive)
|
||||
|
||||
#endif // CEREAL_ARCHIVES_XML_HPP_
|
Reference in New Issue
Block a user