![]() |
|
|||
File indexing completed on 2022-11-29 00:56:23
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_non_native 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_non_native<T,U>::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::size_to_precision<sizeof(T), ::std::is_floating_point<T>::value>::type precision; 0401 typedef typename fp::detail::fp_traits_non_native<T, precision> traits; 0402 0403 // if you end here there are three possibilities: 0404 // 1. you're serializing a long double which is not portable 0405 // 2. you're serializing a double but have no 64 bit integer 0406 // 3. your machine is using an unknown floating point format 0407 // after reading the note above you still might decide to 0408 // deactivate this static assert and try if it works out. 0409 typename traits::bits bits; 0410 static_assert(sizeof(bits) == sizeof(T)); 0411 static_assert(std::numeric_limits<T>::is_iec559); 0412 0413 load(bits); 0414 traits::set_bits(t, bits); 0415 0416 // if the no_infnan flag is set we must throw here 0417 if (get_flags() & no_infnan && !fp::isfinite(t)) 0418 throw portable_archive_exception(t); 0419 0420 // if you end here your floating point type does not support 0421 // denormalized numbers. this might be the case even though 0422 // your type conforms to IEC 559 (and thus to IEEE 754) 0423 if (std::numeric_limits<T>::has_denorm == std::denorm_absent && fp::fpclassify(t) == (int)FP_SUBNORMAL) // GCC4 0424 throw portable_archive_exception(t); 0425 } 0426 0427 // in boost 1.44 version_type was splitted into library_version_type and 0428 // item_version_type, plus a whole bunch of additional strong typedefs. 0429 template <typename T> 0430 typename boost::disable_if<boost::is_arithmetic<T> >::type load(T& t, dummy<4> = 0) { 0431 // we provide a generic load routine for all types that feature 0432 // conversion operators into an unsigned integer value like those 0433 // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like 0434 // library_version_type, collection_size_type, item_version_type, 0435 // class_id_type, object_id_type, version_type and tracking_type 0436 load((typename boost::uint_t<sizeof(T) * CHAR_BIT>::least&)(t)); 0437 } 0438 }; 0439 0440 // polymorphic portable binary iarchive typedef 0441 typedef POLYMORPHIC(portable_iarchive) polymorphic_portable_iarchive; 0442 #undef POLYMORPHIC 0443 0444 } // namespace eos 0445 0446 // this is required by export which registers all of your 0447 // classes with all the inbuilt archives plus our archive. 0448 #if BOOST_VERSION < 103500 0449 #define BOOST_ARCHIVE_CUSTOM_IARCHIVE_TYPES eos::portable_iarchive 0450 #else 0451 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_iarchive) 0452 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::polymorphic_portable_iarchive) 0453 #endif 0454 0455 // if you include this header multiple times and your compiler is picky 0456 // about multiple template instantiations (eg. gcc is) then you need to 0457 // define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one 0458 // or you move the instantiation section into an implementation file 0459 // #ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION 0460 // 0461 // #include <boost/archive/impl/basic_binary_iarchive.ipp> 0462 // #include <boost/archive/impl/basic_binary_iprimitive.ipp> 0463 // 0464 // #if BOOST_VERSION < 104000 0465 // #include <boost/archive/impl/archive_pointer_iserializer.ipp> 0466 // #elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED 0467 // #include <boost/archive/impl/archive_serializer_map.ipp> 0468 // #define BOOST_ARCHIVE_SERIALIZER_INCLUDED 0469 // #endif 0470 // 0471 // namespace boost { 0472 // namespace archive { 0473 // 0474 // // explicitly instantiate for this type of binary stream 0475 // template class basic_binary_iarchive<eos::portable_iarchive>; 0476 // 0477 // template class basic_binary_iprimitive<eos::portable_iarchive 0478 // #if BOOST_VERSION < 103400 0479 // , 0480 // std::istream 0481 // #else 0482 // , 0483 // std::istream::char_type, 0484 // std::istream::traits_type 0485 // #endif 0486 // >; 0487 // 0488 // #if BOOST_VERSION < 104000 0489 // template class detail::archive_pointer_iserializer<eos::portable_iarchive>; 0490 // #else 0491 // template class detail::archive_serializer_map<eos::portable_iarchive>; 0492 // //template class detail::archive_serializer_map<eos::polymorphic_portable_iarchive>; 0493 // #endif 0494 // 0495 // } // namespace archive 0496 // } // namespace boost 0497 // 0498 // #endif 0499 0500 #endif
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.2.1 LXR engine. The LXR team |
![]() ![]() |