Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:02:32

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

0003  * \file portable_oarchive.hpp

0004  * \brief Provides an archive to create 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 Oliver Putz pointed out that -0.0 was not serialized correctly, so

0038  *       version 4.3 provides a fix for that. Thanks Ollie!

0039  *

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

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

0042  *       by Francois Mauger.

0043  *

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

0045  *       Thanks to Francois Mauger for his suggestions.

0046  *

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

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

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

0050  *       Ramey for the hint.

0051  *

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

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

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

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

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

0057  *

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

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

0060  *       supplies the underlying serialization library version. Backwards

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

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

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

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

0065  *

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

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

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

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

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

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

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

0073  *

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

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

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

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

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

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

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

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

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

0083  *

0084  * \copyright The boost software license applies.

0085  */
0086 /*****************************************************************************/
0087 
0088 #ifndef CondFormats_Serialization_portable_oarchive_hpp
0089 #define CondFormats_Serialization_portable_oarchive_hpp
0090 
0091 #include <ostream>
0092 
0093 // basic headers

0094 #include <boost/version.hpp>
0095 #include <boost/utility/enable_if.hpp>
0096 #include <boost/archive/basic_binary_oprimitive.hpp>
0097 #include <boost/archive/basic_binary_oarchive.hpp>
0098 
0099 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
0100 #include <boost/archive/shared_ptr_helper.hpp>
0101 #endif
0102 
0103 #if BOOST_VERSION >= 104500
0104 #include <boost/program_options/config.hpp>
0105 #include <boost/program_options/detail/convert.hpp>
0106 #endif
0107 
0108 // funny polymorphics

0109 #if BOOST_VERSION < 103500
0110 #include <boost/archive/detail/polymorphic_oarchive_impl.hpp>
0111 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_impl<T>
0112 
0113 #elif BOOST_VERSION < 103600
0114 #include <boost/archive/detail/polymorphic_oarchive_dispatch.hpp>
0115 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_dispatch<T>
0116 
0117 #else
0118 #include <boost/archive/detail/polymorphic_oarchive_route.hpp>
0119 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_route<T>
0120 #endif
0121 
0122 // endian and fpclassify

0123 #include <boost/math/special_functions/fpclassify.hpp>
0124 #include <boost/endian/conversion.hpp>
0125 
0126 // namespace alias fp_classify

0127 #if BOOST_VERSION < 103800
0128 namespace fp = boost::math;
0129 #elif BOOST_VERSION >= 106900
0130 namespace fp = boost::math;
0131 #else
0132 namespace fp = boost::spirit::math;
0133 #endif
0134 
0135 #if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING
0136 // used for wstring to utf8 conversion

0137 #include <boost/program_options/config.hpp>
0138 #include <boost/program_options/detail/convert.hpp>
0139 #endif
0140 
0141 // generic type traits for numeric types

0142 #include <boost/type_traits/is_integral.hpp>
0143 #include <boost/type_traits/is_signed.hpp>
0144 #include <boost/type_traits/is_arithmetic.hpp>
0145 #include <boost/type_traits/is_floating_point.hpp>
0146 
0147 #include "portable_archive_exception.hpp"
0148 
0149 // hint from Johan Rade: on VMS there is still support for

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

0151 #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT
0152 #error "VAX floating point format is not supported!"
0153 #endif
0154 
0155 namespace eos {
0156 
0157   // forward declaration

0158   class portable_oarchive;
0159 
0160   typedef boost::archive::basic_binary_oprimitive<portable_oarchive
0161 #if BOOST_VERSION < 103400
0162                                                   ,
0163                                                   std::ostream
0164 #else
0165                                                   ,
0166                                                   std::ostream::char_type,
0167                                                   std::ostream::traits_type
0168 #endif
0169                                                   >
0170       portable_oprimitive;
0171 
0172   /**

0173      * \brief Portable binary output archive using little endian format.

0174      *

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

0176      * that data can be transferred across different systems. The archive consists

0177      * primarily of three different save implementations for integral types,

0178      * floating point types and string types. Those functions are templates and use

0179      * enable_if to be correctly selected for overloading.

0180      *

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

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

0183      */
0184   class portable_oarchive : public portable_oprimitive
0185 
0186       // the example derives from common_oarchive but that lacks the

0187       // save_override functions so we chose to stay one level higher

0188       ,
0189                             public boost::archive::basic_binary_oarchive<portable_oarchive>
0190 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
0191       // mix-in helper class for serializing shared_ptr

0192       ,
0193                             public boost::archive::detail::shared_ptr_helper
0194 #endif
0195   {
0196     // workaround for gcc: use a dummy struct

0197     // as additional argument type for overloading

0198     template <int>
0199     struct dummy {
0200       dummy(int) {}
0201     };
0202 
0203     // stores a signed char directly to stream

0204     inline void save_signed_char(const signed char& c) { portable_oprimitive::save(c); }
0205 
0206     // archive initialization

0207     void init(unsigned flags) {
0208       // it is vital to have version information if the archive is

0209       // to be parsed with a newer version of boost::serialization

0210       // therefor we create a header, no header means boost 1.33

0211       if (flags & boost::archive::no_header)
0212         BOOST_ASSERT(archive_version == 3);
0213       else {
0214         // write our minimalistic header (magic byte plus version)

0215         // the boost archives write a string instead - by calling

0216         // boost::archive::basic_binary_oarchive<derived_t>::init()

0217         save_signed_char(magic_byte);
0218 
0219         // write current version

0220         //              save<unsigned>(archive_version);

0221         operator<<(archive_version);
0222       }
0223     }
0224 
0225   public:
0226     /**

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

0228          *

0229          * We cannot call basic_binary_oprimitive::init which stores type

0230          * sizes to the archive in order to detect transfers to non-compatible

0231          * platforms.

0232          *

0233          * We could have called basic_binary_oarchive::init which would create

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

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

0236          */
0237     portable_oarchive(std::ostream& os, unsigned flags = 0)
0238 #if BOOST_VERSION < 103400
0239         : portable_oprimitive(os, flags & boost::archive::no_codecvt)
0240 #else
0241         : portable_oprimitive(*os.rdbuf(), flags & boost::archive::no_codecvt)
0242 #endif
0243           ,
0244           boost::archive::basic_binary_oarchive<portable_oarchive>(flags) {
0245       init(flags);
0246     }
0247 
0248 #if BOOST_VERSION >= 103400
0249     portable_oarchive(std::streambuf& sb, unsigned flags = 0)
0250         : portable_oprimitive(sb, flags & boost::archive::no_codecvt),
0251           boost::archive::basic_binary_oarchive<portable_oarchive>(flags) {
0252       init(flags);
0253     }
0254 #endif
0255 
0256     //! Save narrow strings.

0257     void save(const std::string& s) { portable_oprimitive::save(s); }
0258 
0259 #ifndef BOOST_NO_STD_WSTRING
0260     /**

0261          * \brief Save wide strings.

0262          *

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

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

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

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

0267          *

0268          * We expect the input string to be encoded in the system's native

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

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

0271          */
0272     void save(const std::wstring& s) { save(boost::to_utf8(s)); }
0273 #endif
0274 
0275     /**

0276          * \brief Saving bool type.

0277          *

0278          * Saving bool directly, not by const reference

0279          * because of tracking_type's operator (bool).

0280          *

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

0282          * about save(bool) cannot convert your type const A& into bool then

0283          * you should check your BOOST_CLASS_IMPLEMENTATION setting for A, as

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

0285          * a general manner.

0286          */
0287     void save(const bool& b) {
0288       save_signed_char(b);
0289       if (b)
0290         save_signed_char('T');
0291     }
0292 
0293     /**

0294          * \brief Save integer types.

0295          *

0296          * First we save the size information ie. the number of bytes that hold the

0297          * actual data. We subsequently transform the data using store_little_endian

0298          * and store non-zero bytes to the stream.

0299          */
0300     template <typename T>
0301     typename boost::enable_if<boost::is_integral<T> >::type save(const T& t, dummy<2> = 0) {
0302       if (T temp = t) {
0303         // examine the number of bytes

0304         // needed to represent the number

0305         signed char size = 0;
0306         if (sizeof(T) == 1) {
0307           size = 1;
0308         } else {
0309           do {
0310             temp >>= CHAR_BIT;
0311             ++size;
0312           } while (temp != 0 && temp != (T)-1);
0313         }
0314 
0315         // encode the sign bit into the size

0316         save_signed_char(t > 0 ? size : -size);
0317         BOOST_ASSERT(t > 0 || boost::is_signed<T>::value);
0318 
0319         // we choose to use little endian because this way we just

0320         // save the first size bytes to the stream and skip the rest

0321         temp = boost::endian::native_to_little(t);
0322         save_binary(&temp, size);
0323       }
0324       // zero optimization

0325       else
0326         save_signed_char(0);
0327     }
0328 
0329     /**

0330          * \brief Save floating point types.

0331          * 

0332          * We simply rely on fp_traits_non_native to extract the bit pattern into an (unsigned)

0333          * integral type and store that into the stream. Francois Mauger provided

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

0335          * be serialized in his application.

0336          *

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

0338          * Be warned that the math::detail::fp_traits_non_native<T,U>::get_bits() function 

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

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

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

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

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

0344          * uint64_t. 

0345          * 

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

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

0348          * math::detail::not_all_bits. 

0349          * 

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

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

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

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

0354          * small rounding off errors. 

0355          */
0356     template <typename T>
0357     typename boost::enable_if<boost::is_floating_point<T> >::type save(const T& t, dummy<3> = 0) {
0358       typedef typename fp::detail::size_to_precision<sizeof(T), ::std::is_floating_point<T>::value>::type precision;
0359       typedef typename fp::detail::fp_traits_non_native<T, precision> traits;
0360 
0361       // if the no_infnan flag is set we must throw here

0362       if (get_flags() & no_infnan && !fp::isfinite(t))
0363         throw portable_archive_exception(t);
0364 
0365       // if you end here there are three possibilities:

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

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

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

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

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

0371       typename traits::bits bits;
0372       static_assert(sizeof(bits) == sizeof(T));
0373       static_assert(std::numeric_limits<T>::is_iec559);
0374 
0375       // examine value closely

0376       switch (fp::fpclassify(t)) {
0377 //case FP_ZERO: bits = 0; break;

0378 #if BOOST_VERSION >= 106900
0379         case FP_NAN:
0380           bits = traits::exponent | traits::significand;
0381           break;
0382 #else
0383         case FP_NAN:
0384           bits = traits::exponent | traits::mantissa;
0385           break;
0386 #endif
0387         case FP_INFINITE:
0388           bits = traits::exponent | (t < 0) * traits::sign;
0389           break;
0390         case FP_SUBNORMAL:
0391           assert(std::numeric_limits<T>::has_denorm);  // pass

0392         case FP_ZERO:                                  // note that floats can be ±0.0

0393         case FP_NORMAL:
0394           traits::get_bits(t, bits);
0395           break;
0396         default:
0397           throw portable_archive_exception(t);
0398       }
0399 
0400       save(bits);
0401     }
0402 
0403     // in boost 1.44 version_type was splitted into library_version_type and

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

0405     template <typename T>
0406     typename boost::disable_if<boost::is_arithmetic<T> >::type save(const T& t, dummy<4> = 0) {
0407       // we provide a generic save routine for all types that feature

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

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

0410       // library_version_type, collection_size_type, item_version_type,

0411       // class_id_type, object_id_type, version_type and tracking_type

0412       save((typename boost::uint_t<sizeof(T) * CHAR_BIT>::least)(t));
0413     }
0414   };
0415 
0416   // polymorphic portable binary oarchive typedef

0417   typedef POLYMORPHIC(portable_oarchive) polymorphic_portable_oarchive;
0418 #undef POLYMORPHIC
0419 
0420 }  // namespace eos

0421 
0422 // required by export

0423 #if BOOST_VERSION < 103500
0424 #define BOOST_ARCHIVE_CUSTOM_OARCHIVE_TYPES eos::portable_oarchive
0425 #else
0426 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_oarchive)
0427 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::polymorphic_portable_oarchive)
0428 #endif
0429 
0430 // if you include this header multiple times and your compiler is picky

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

0432 // define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one

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

0434 // #ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION

0435 //

0436 // #include <boost/archive/impl/basic_binary_oarchive.ipp>

0437 // #include <boost/archive/impl/basic_binary_oprimitive.ipp>

0438 //

0439 // #if BOOST_VERSION < 104000

0440 // #include <boost/archive/impl/archive_pointer_oserializer.ipp>

0441 // #elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED

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

0443 // #define BOOST_ARCHIVE_SERIALIZER_INCLUDED

0444 // #endif

0445 //

0446 // namespace boost {

0447 //   namespace archive {

0448 //

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

0450 //     template class basic_binary_oarchive<eos::portable_oarchive>;

0451 //

0452 //     template class basic_binary_oprimitive<eos::portable_oarchive

0453 // #if BOOST_VERSION < 103400

0454 //                                            ,

0455 //                                            std::ostream

0456 // #else

0457 //                                            ,

0458 //                                            std::ostream::char_type,

0459 //                                            std::ostream::traits_type

0460 // #endif

0461 //                                            >;

0462 //

0463 // #if BOOST_VERSION < 104000

0464 //     template class detail::archive_pointer_oserializer<eos::portable_oarchive>;

0465 // #else

0466 //     template class detail::archive_serializer_map<eos::portable_oarchive>;

0467 //     //template class detail::archive_serializer_map<eos::polymorphic_portable_oarchive>;

0468 // #endif

0469 //

0470 //   }  // namespace archive

0471 // }  // namespace boost

0472 //

0473 // #endif

0474 
0475 #endif