Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-02-14 12:51:30

0001 /*****************************************************************************/
0002 /**

0003  * \file portable_iarchive.hpp

0004  * \brief Provides an archive to read from portable binary files.

0005  * \author christian.pfligersdorffer@gmx.at

0006  * \version 5.1

0007  *

0008  * This pair of archives brings the advantages of binary streams to the cross

0009  * platform boost::serialization user. While being almost as fast as the native

0010  * binary archive it allows its files to be exchanged between cpu architectures

0011  * using different byte order (endianness). Speaking of speed: in serializing

0012  * numbers the (portable) binary approach is approximately ten times faster than

0013  * the ascii implementation (that is inherently portable)!

0014  *

0015  * Based on the portable archive example by Robert Ramey this implementation

0016  * uses Beman Dawes endian library and fp_utilities from Johan Rade, both being

0017  * in boost since 1.36. Prior to that you need to add them both (header only)

0018  * to your boost directory before you're able to use the archives provided. 

0019  * Our archives have been tested successfully for boost versions 1.33 to 1.49!

0020  *

0021  * \note Correct behaviour has so far been confirmed using PowerPC-32, x86-32

0022  *       and x86-64 platforms featuring different byte order. So there is a good

0023  *       chance it will instantly work for your specific setup. If you encounter

0024  *       problems or have suggestions please contact the author.

0025  *

0026  * \note Version 5.1 is now compatible with boost up to version 1.59. Thanks to

0027  *       ecotax for pointing to the issue with shared_ptr_helper.

0028  *

0029  * \note Version 5.0 is now compatible with boost up to version 1.49 and enables

0030  *       serialization of std::wstring by converting it to/from utf8 (thanks to

0031  *       Arash Abghari for this suggestion). With that all unit tests from the

0032  *       serialization library pass again with the notable exception of user

0033  *       defined primitive types. Those are not supported and as a result any

0034  *       user defined type to be used with the portable archives are required 

0035  *       to be at least object_serializable.

0036  *

0037  * \note Version 4.2 maintains compatibility with the latest boost 1.45 and adds

0038  *       serialization of special floating point values inf and NaN as proposed

0039  *       by Francois Mauger.

0040  *

0041  * \note Version 4.1 makes the archives work together with boost 1.40 and 1.41.

0042  *       Thanks to Francois Mauger for his suggestions.

0043  *

0044  * \note Version 4 removes one level of the inheritance hierarchy and directly

0045  *       builds upon binary primitive and basic binary archive, thereby fixing

0046  *       the last open issue regarding array serialization. Thanks to Robert

0047  *       Ramey for the hint.

0048  *

0049  * \note A few fixes introduced in version 3.1 let the archives pass all of the

0050  *       serialization tests. Thanks to Sergey Morozov for running the tests.

0051  *       Wouter Bijlsma pointed out where to find the fp_utilities and endian

0052  *       libraries headers inside the boost distribution. I would never have

0053  *       found them so thank him it works out of the box since boost 1.36.

0054  *

0055  * \note With Version 3.0 the archives have been made portable across different

0056  *       boost versions. For that purpose a header is added to the data that

0057  *       supplies the underlying serialization library version. Backwards

0058  *       compatibility is maintained by assuming library version boost 1.33 if

0059  *       the iarchive is created using the no_header flag. Whether a header is

0060  *       present or not can be guessed by peeking into the stream: the header's

0061  *       first byte is the magic number 127 coinciding with 'e'|'o'|'s' :-)

0062  *

0063  * \note Version 2.1 removes several compiler warnings and enhances floating

0064  *       point diagnostics to inform the user if some preconditions are violated

0065  *       on his platform. We do not strive for the universally portable solution

0066  *       in binary floating point serialization as desired by some boost users.

0067  *       Instead we support only the most widely used IEEE 754 format and try to

0068  *       detect when requirements are not met and hence our approach must fail.

0069  *       Contributions we made by Johan Rade and Ákos Maróy.

0070  *

0071  * \note Version 2.0 fixes a serious bug that effectively transformed most

0072  *       of negative integral values into positive values! For example the two

0073  *       numbers -12 and 234 were stored in the same 8-bit pattern and later

0074  *       always restored to 234. This was fixed in this version in a way that

0075  *       does not change the interpretation of existing archives that did work

0076  *       because there were no negative numbers. The other way round archives

0077  *       created by version 2.0 and containing negative numbers will raise an

0078  *       integer type size exception when reading it with version 1.0. Thanks

0079  *       to Markus Frohnmaier for testing the archives and finding the bug.

0080  *

0081  * \copyright The boost software license applies.

0082  */
0083 /*****************************************************************************/
0084 
0085 #ifndef CondFormats_Serialization_portable_iarchive_hpp
0086 #define CondFormats_Serialization_portable_iarchive_hpp
0087 
0088 #include <istream>
0089 
0090 // basic headers

0091 #include <boost/version.hpp>
0092 #include <boost/utility/enable_if.hpp>
0093 #include <boost/archive/basic_binary_iprimitive.hpp>
0094 #include <boost/archive/basic_binary_iarchive.hpp>
0095 
0096 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
0097 #include <boost/archive/shared_ptr_helper.hpp>
0098 #endif
0099 
0100 // funny polymorphics

0101 #if BOOST_VERSION < 103500
0102 #include <boost/archive/detail/polymorphic_iarchive_impl.hpp>
0103 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_iarchive_impl<T>
0104 
0105 #elif BOOST_VERSION < 103600
0106 #include <boost/archive/detail/polymorphic_iarchive_dispatch.hpp>
0107 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_iarchive_dispatch<T>
0108 
0109 #else
0110 #include <boost/archive/detail/polymorphic_iarchive_route.hpp>
0111 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_iarchive_route<T>
0112 #endif
0113 
0114 // endian and fpclassify

0115 #if BOOST_VERSION < 103600
0116 #include <boost/integer/endian.hpp>
0117 #include <boost/math/fpclassify.hpp>
0118 #elif BOOST_VERSION < 104800
0119 #include <boost/spirit/home/support/detail/integer/endian.hpp>
0120 #include <boost/spirit/home/support/detail/math/fpclassify.hpp>
0121 #elif BOOST_VERSION >= 106900
0122 #include <boost/math/special_functions/fpclassify.hpp>
0123 #include <boost/endian/conversion.hpp>
0124 #else
0125 #include <boost/spirit/home/support/detail/endian/endian.hpp>
0126 #include <boost/spirit/home/support/detail/math/fpclassify.hpp>
0127 #endif
0128 
0129 // namespace alias

0130 #if BOOST_VERSION < 103800
0131 namespace fp = boost::math;
0132 #elif BOOST_VERSION >= 106900
0133 namespace fp = boost::math;
0134 #else
0135 namespace fp = boost::spirit::math;
0136 #endif
0137 
0138 // namespace alias endian

0139 #if BOOST_VERSION < 104800
0140 namespace endian = boost::detail;
0141 #elif BOOST_VERSION >= 106900
0142 namespace endian = boost::endian;
0143 #else
0144 namespace endian = boost::spirit::detail;
0145 #endif
0146 
0147 #if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING
0148 // used for wstring to utf8 conversion

0149 #include <boost/program_options/config.hpp>
0150 #include <boost/program_options/detail/convert.hpp>
0151 #endif
0152 
0153 // generic type traits for numeric types

0154 #include <boost/type_traits/is_integral.hpp>
0155 #include <boost/type_traits/is_unsigned.hpp>
0156 #include <boost/type_traits/is_arithmetic.hpp>
0157 #include <boost/type_traits/is_floating_point.hpp>
0158 
0159 #include "portable_archive_exception.hpp"
0160 
0161 // hint from Johan Rade: on VMS there is still support for

0162 // the VAX floating point format and this macro detects it

0163 #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT
0164 #error "VAX floating point format is not supported!"
0165 #endif
0166 
0167 namespace eos {
0168 
0169   // forward declaration

0170   class portable_iarchive;
0171 
0172   typedef boost::archive::basic_binary_iprimitive<portable_iarchive
0173 #if BOOST_VERSION < 103400
0174                                                   ,
0175                                                   std::istream
0176 #else
0177                                                   ,
0178                                                   std::istream::char_type,
0179                                                   std::istream::traits_type
0180 #endif
0181                                                   >
0182       portable_iprimitive;
0183 
0184   /**

0185      * \brief Portable binary input archive using little endian format.

0186      *

0187      * This archive addresses integer size, endianness and floating point types so

0188      * that data can be transferred across different systems. There may still be

0189      * constraints as to what systems are compatible and the user will have to take

0190      * care that e.g. a very large int being saved on a 64 bit machine will result

0191      * in a portable_archive_exception if loaded into an int on a 32 bit system.

0192      * A possible workaround to this would be to use fixed types like

0193      * boost::uint64_t in your serialization structures.

0194      *

0195      * \note The class is based on the portable binary example by Robert Ramey and

0196      *       uses Beman Dawes endian library plus fp_utilities by Johan Rade.

0197      */
0198   class portable_iarchive : public portable_iprimitive
0199 
0200       // the example derives from common_oarchive but that lacks the

0201       // load_override functions so we chose to stay one level higher

0202       ,
0203                             public boost::archive::basic_binary_iarchive<portable_iarchive>
0204 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
0205       // mix-in helper class for serializing shared_ptr

0206       ,
0207                             public boost::archive::detail::shared_ptr_helper
0208 #endif
0209   {
0210     // only needed for Robert's hack in basic_binary_iarchive::init

0211     friend class boost::archive::basic_binary_iarchive<portable_iarchive>;
0212 
0213     // workaround for gcc: use a dummy struct

0214     // as additional argument type for overloading

0215     template <int>
0216     struct dummy {
0217       dummy(int) {}
0218     };
0219 
0220     // loads directly from stream

0221     inline signed char load_signed_char() {
0222       signed char c;
0223       portable_iprimitive::load(c);
0224       return c;
0225     }
0226 
0227     // archive initialization

0228     void init(unsigned flags) {
0229       using namespace boost::archive;
0230       archive_version_type input_library_version(3);
0231 
0232       // it is vital to have version information!

0233       // if we don't have any we assume boost 1.33

0234       if (flags & no_header)
0235         set_library_version(input_library_version);
0236 
0237       // extract and check the magic eos byte

0238       else if (load_signed_char() != magic_byte)
0239         throw archive_exception(archive_exception::invalid_signature);
0240 
0241       else {
0242         // extract version information

0243         operator>>(input_library_version);
0244 
0245         // throw if file version is newer than we are

0246         if (input_library_version > archive_version)
0247           throw archive_exception(archive_exception::unsupported_version);
0248 
0249         // else set the library version accordingly

0250         else
0251           set_library_version(input_library_version);
0252       }
0253     }
0254 
0255   public:
0256     /**

0257          * \brief Constructor on a stream using ios::binary mode!

0258          *

0259          * We cannot call basic_binary_iprimitive::init which tries to detect

0260          * if the binary archive stems from a different platform by examining

0261          * type sizes.

0262          *

0263          * We could have called basic_binary_iarchive::init which would create

0264          * the boost::serialization standard archive header containing also the

0265          * library version. Due to efficiency we stick with our own.

0266          */
0267     portable_iarchive(std::istream& is, unsigned flags = 0)
0268 #if BOOST_VERSION < 103400
0269         : portable_iprimitive(is, flags & boost::archive::no_codecvt)
0270 #else
0271         : portable_iprimitive(*is.rdbuf(), flags & boost::archive::no_codecvt)
0272 #endif
0273           ,
0274           boost::archive::basic_binary_iarchive<portable_iarchive>(flags) {
0275       init(flags);
0276     }
0277 
0278 #if BOOST_VERSION >= 103400
0279     portable_iarchive(std::streambuf& sb, unsigned flags = 0)
0280         : portable_iprimitive(sb, flags & boost::archive::no_codecvt),
0281           boost::archive::basic_binary_iarchive<portable_iarchive>(flags) {
0282       init(flags);
0283     }
0284 #endif
0285 
0286     //! Load narrow strings.

0287     void load(std::string& s) { portable_iprimitive::load(s); }
0288 
0289 #ifndef BOOST_NO_STD_WSTRING
0290     /**

0291          * \brief Load wide strings.

0292          *

0293          * This is rather tricky to get right for true portability as there

0294          * are so many different character encodings around. However, wide

0295          * strings that are encoded in one of the Unicode schemes only need

0296          * to be _transcoded_ which is a lot easier actually.

0297          *

0298          * We generate the output string to be encoded in the system's native

0299          * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't

0300          * know about Mac here so I can't really say about that.

0301          */
0302     void load(std::wstring& s) {
0303       std::string utf8;
0304       load(utf8);
0305       s = boost::from_utf8(utf8);
0306     }
0307 #endif
0308 
0309     /**

0310          * \brief Loading bool type.

0311          *

0312          * Byte pattern is same as with integer types, so this function

0313          * is somewhat redundant but treating bool as integer generates

0314          * a lot of compiler warnings.

0315          *

0316          * \note If you cannot compile your application and it says something

0317          * about load(bool) cannot convert your type A& into bool& then you

0318          * should check your BOOST_CLASS_IMPLEMENTATION setting for A, as

0319          * portable_archive is not able to handle custom primitive types in

0320          * a general manner.

0321          */
0322     void load(bool& b) {
0323       switch (signed char c = load_signed_char()) {
0324         case 0:
0325           b = false;
0326           break;
0327         case 1:
0328           b = load_signed_char();
0329           break;
0330         default:
0331           throw portable_archive_exception(c);
0332       }
0333     }
0334 
0335     /**

0336          * \brief Load integer types.

0337          *

0338          * First we load the size information ie. the number of bytes that 

0339          * hold the actual data. Then we retrieve the data and transform it

0340          * to the original value by using load_little_endian.

0341          */
0342     template <typename T>
0343     typename boost::enable_if<boost::is_integral<T> >::type load(T& t, dummy<2> = 0) {
0344       // get the number of bytes in the stream

0345       if (signed char size = load_signed_char()) {
0346         // check for negative value in unsigned type

0347         if (size < 0 && boost::is_unsigned<T>::value)
0348           throw portable_archive_exception();
0349 
0350         // check that our type T is large enough

0351         else if ((unsigned)abs(size) > sizeof(T))
0352           throw portable_archive_exception(size);
0353 
0354         // reconstruct the value

0355         T temp = size < 0 ? -1 : 0;
0356         load_binary(&temp, abs(size));
0357 
0358 // load the value from little endian - it is then converted

0359 // to the target type T and fits it because size <= sizeof(T)

0360 #if BOOST_VERSION >= 106900
0361         t = endian::little_to_native(temp);
0362 #else
0363         t = endian::load_little_endian<T, sizeof(T)>(&temp);
0364 #endif
0365       }
0366 
0367       else
0368         t = 0;  // zero optimization

0369     }
0370 
0371     /** 

0372          * \brief Load floating point types.

0373          * 

0374          * We simply rely on fp_traits to set the bit pattern from the (unsigned)

0375          * integral type that was stored in the stream. Francois Mauger provided

0376          * standardized behaviour for special values like inf and NaN, that need to

0377          * be serialized in his application.

0378          *

0379          * \note by Johan Rade (author of the floating point utilities library):

0380          * Be warned that the math::detail::fp_traits<T>::type::get_bits() function 

0381          * is *not* guaranteed to give you all bits of the floating point number. It

0382          * will give you all bits if and only if there is an integer type that has

0383          * the same size as the floating point you are copying from. It will not

0384          * give you all bits for double if there is no uint64_t. It will not give

0385          * you all bits for long double if sizeof(long double) > 8 or there is no

0386          * uint64_t. 

0387          * 

0388          * The member fp_traits<T>::type::coverage will tell you whether all bits

0389          * are copied. This is a typedef for either math::detail::all_bits or

0390          * math::detail::not_all_bits. 

0391          * 

0392          * If the function does not copy all bits, then it will copy the most

0393          * significant bits. So if you serialize and deserialize the way you

0394          * describe, and fp_traits<T>::type::coverage is math::detail::not_all_bits,

0395          * then your floating point numbers will be truncated. This will introduce

0396          * small rounding off errors. 

0397          */
0398     template <typename T>
0399     typename boost::enable_if<boost::is_floating_point<T> >::type load(T& t, dummy<3> = 0) {
0400       typedef typename fp::detail::fp_traits<T>::type traits;
0401 
0402       // if you end here there are three possibilities:

0403       // 1. you're serializing a long double which is not portable

0404       // 2. you're serializing a double but have no 64 bit integer

0405       // 3. your machine is using an unknown floating point format

0406       // after reading the note above you still might decide to

0407       // deactivate this static assert and try if it works out.

0408       typename traits::bits bits;
0409       static_assert(sizeof(bits) == sizeof(T));
0410       static_assert(std::numeric_limits<T>::is_iec559);
0411 
0412       load(bits);
0413       traits::set_bits(t, bits);
0414 
0415       // if the no_infnan flag is set we must throw here

0416       if (get_flags() & no_infnan && !fp::isfinite(t))
0417         throw portable_archive_exception(t);
0418 
0419       // if you end here your floating point type does not support

0420       // denormalized numbers. this might be the case even though

0421       // your type conforms to IEC 559 (and thus to IEEE 754)

0422       if (std::numeric_limits<T>::has_denorm == std::denorm_absent && fp::fpclassify(t) == (int)FP_SUBNORMAL)  // GCC4

0423         throw portable_archive_exception(t);
0424     }
0425 
0426     // in boost 1.44 version_type was splitted into library_version_type and

0427     // item_version_type, plus a whole bunch of additional strong typedefs.

0428     template <typename T>
0429     typename boost::disable_if<boost::is_arithmetic<T> >::type load(T& t, dummy<4> = 0) {
0430       // we provide a generic load routine for all types that feature

0431       // conversion operators into an unsigned integer value like those

0432       // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like

0433       // library_version_type, collection_size_type, item_version_type,

0434       // class_id_type, object_id_type, version_type and tracking_type

0435       load((typename boost::uint_t<sizeof(T) * CHAR_BIT>::least&)(t));
0436     }
0437   };
0438 
0439   // polymorphic portable binary iarchive typedef

0440   typedef POLYMORPHIC(portable_iarchive) polymorphic_portable_iarchive;
0441 #undef POLYMORPHIC
0442 
0443 }  // namespace eos

0444 
0445 // this is required by export which registers all of your

0446 // classes with all the inbuilt archives plus our archive.

0447 #if BOOST_VERSION < 103500
0448 #define BOOST_ARCHIVE_CUSTOM_IARCHIVE_TYPES eos::portable_iarchive
0449 #else
0450 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_iarchive)
0451 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::polymorphic_portable_iarchive)
0452 #endif
0453 
0454 // if you include this header multiple times and your compiler is picky

0455 // about multiple template instantiations (eg. gcc is) then you need to

0456 // define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one

0457 // or you move the instantiation section into an implementation file

0458 // #ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION

0459 //

0460 // #include <boost/archive/impl/basic_binary_iarchive.ipp>

0461 // #include <boost/archive/impl/basic_binary_iprimitive.ipp>

0462 //

0463 // #if BOOST_VERSION < 104000

0464 // #include <boost/archive/impl/archive_pointer_iserializer.ipp>

0465 // #elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED

0466 // #include <boost/archive/impl/archive_serializer_map.ipp>

0467 // #define BOOST_ARCHIVE_SERIALIZER_INCLUDED

0468 // #endif

0469 //

0470 // namespace boost {

0471 //   namespace archive {

0472 //

0473 //     // explicitly instantiate for this type of binary stream

0474 //     template class basic_binary_iarchive<eos::portable_iarchive>;

0475 //

0476 //     template class basic_binary_iprimitive<eos::portable_iarchive

0477 // #if BOOST_VERSION < 103400

0478 //                                            ,

0479 //                                            std::istream

0480 // #else

0481 //                                            ,

0482 //                                            std::istream::char_type,

0483 //                                            std::istream::traits_type

0484 // #endif

0485 //                                            >;

0486 //

0487 // #if BOOST_VERSION < 104000

0488 //     template class detail::archive_pointer_iserializer<eos::portable_iarchive>;

0489 // #else

0490 //     template class detail::archive_serializer_map<eos::portable_iarchive>;

0491 //     //template class detail::archive_serializer_map<eos::polymorphic_portable_iarchive>;

0492 // #endif

0493 //

0494 //   }  // namespace archive

0495 // }  // namespace boost

0496 //

0497 // #endif

0498 
0499 #endif