LCOV - code coverage report
Current view: top level - cereal/archives - portable_binary.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 516 556 92.8 %
Date: 2019-10-22 20:09:23 Functions: 274 286 95.8 %

          Line data    Source code
       1             : /*! \file binary.hpp
       2             :     \brief Binary input and output archives */
       3             : /*
       4             :   Copyright (c) 2014, Randolph Voorhies, Shane Grant
       5             :   All rights reserved.
       6             : 
       7             :   Redistribution and use in source and binary forms, with or without
       8             :   modification, are permitted provided that the following conditions are met:
       9             :       * Redistributions of source code must retain the above copyright
      10             :         notice, this list of conditions and the following disclaimer.
      11             :       * Redistributions in binary form must reproduce the above copyright
      12             :         notice, this list of conditions and the following disclaimer in the
      13             :         documentation and/or other materials provided with the distribution.
      14             :       * Neither the name of cereal nor the
      15             :         names of its contributors may be used to endorse or promote products
      16             :         derived from this software without specific prior written permission.
      17             : 
      18             :   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
      19             :   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
      20             :   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      21             :   DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
      22             :   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      23             :   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      24             :   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
      25             :   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      26             :   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      27             :   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      28             : */
      29             : #ifndef CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
      30             : #define CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
      31             : 
      32             : #include "cereal/cereal.hpp"
      33             : #include <sstream>
      34             : #include <limits>
      35             : 
      36             : namespace cereal
      37             : {
      38             :   namespace portable_binary_detail
      39             :   {
      40             :     //! Returns true if the current machine is little endian
      41             :     /*! @ingroup Internal */
      42       23824 :     inline std::uint8_t is_little_endian()
      43             :     {
      44             :       static std::int32_t test = 1;
      45       23824 :       return *reinterpret_cast<std::int8_t*>( &test ) == 1;
      46             :     }
      47             : 
      48             :     //! Swaps the order of bytes for some chunk of memory
      49             :     /*! @param data The data as a uint8_t pointer
      50             :         @tparam DataSize The true size of the data
      51             :         @ingroup Internal */
      52             :     template <std::size_t DataSize>
      53       95400 :     inline void swap_bytes( std::uint8_t * data )
      54             :     {
      55      283400 :       for( std::size_t i = 0, end = DataSize / 2; i < end; ++i )
      56      188000 :         std::swap( data[i], data[DataSize - i - 1] );
      57       95400 :     }
      58        4200 :   } // end namespace portable_binary_detail
      59             : 
      60       21000 :   // ######################################################################
      61       16800 :   //! An output archive designed to save data in a compact binary representation portable over different architectures
      62        4200 :   /*! This archive outputs data to a stream in an extremely compact binary
      63       84200 :       representation with as little extra metadata as possible.
      64             : 
      65      252600 :       This archive will record the endianness of the data as well as the desired in/out endianness
      66      168400 :       and assuming that the user takes care of ensuring serialized types are the same size
      67       84200 :       across machines, is portable over different architectures.
      68        2800 : 
      69             :       When using a binary archive and a file stream, you must use the
      70        5600 :       std::ios::binary format flag to avoid having your data altered
      71        2800 :       inadvertently.
      72        2800 : 
      73        4200 :       \warning This archive has not been thoroughly tested across different architectures.
      74             :                Please report any issues, optimizations, or feature requests at
      75        4200 :                <a href="www.github.com/USCiLab/cereal">the project github</a>.
      76           0 : 
      77        4200 :     \ingroup Archives */
      78           0 :   class PortableBinaryOutputArchive : public OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>
      79             :   {
      80           0 :     public:
      81           0 :       //! A class containing various advanced options for the PortableBinaryOutput archive
      82           0 :       class Options
      83             :       {
      84             :         public:
      85             :           //! Represents desired endianness
      86             :           enum class Endianness : std::uint8_t
      87             :           { big, little };
      88             : 
      89             :           //! Default options, preserve system endianness
      90             :           static Options Default(){ return Options(); }
      91             : 
      92             :           //! Save as little endian
      93             :           static Options LittleEndian(){ return Options( Endianness::little ); }
      94             : 
      95             :           //! Save as big endian
      96             :           static Options BigEndian(){ return Options( Endianness::big ); }
      97             : 
      98             :           //! Specify specific options for the PortableBinaryOutputArchive
      99             :           /*! @param outputEndian The desired endianness of saved (output) data */
     100        2002 :           explicit Options( Endianness outputEndian = getEndianness() ) :
     101             :             itsOutputEndianness( outputEndian ) { }
     102             : 
     103             :         private:
     104             :           //! Gets the endianness of the system
     105        4006 :           inline static Endianness getEndianness()
     106             :           { return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
     107             : 
     108             :           //! Checks if Options is set for little endian
     109             :           inline std::uint8_t is_little_endian() const
     110        3002 :           { return itsOutputEndianness == Endianness::little; }
     111        2002 : 
     112             :           friend class PortableBinaryOutputArchive;
     113           4 :           Endianness itsOutputEndianness;
     114             :       };
     115        6208 : 
     116        6012 :       //! Construct, outputting to the provided stream
     117             :       /*! @param stream The stream to output to. Should be opened with std::ios::binary flag.
     118             :           @param options The PortableBinary specific options to use.  See the Options struct
     119        4004 :                          for the values of default parameters */
     120        9018 :       PortableBinaryOutputArchive(std::ostream & stream, Options const & options = Options::Default()) :
     121        5014 :         OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
     122             :         itsStream(stream),
     123             :         itsConvertEndianness( portable_binary_detail::is_little_endian() ^ options.is_little_endian() )
     124        8012 :       {
     125        9212 :         this->operator()( options.is_little_endian() );
     126        1200 :       }
     127             : 
     128             :       ~PortableBinaryOutputArchive() CEREAL_NOEXCEPT = default;
     129        3600 : 
     130        5802 :       //! Writes size bytes of data to the output stream
     131         200 :       template <std::streamsize DataSize> inline
     132             :       void saveBinary( const void * data, std::streamsize size )
     133        2002 :       {
     134         400 :         std::streamsize writtenSize = 0;
     135        6408 : 
     136        2002 :         if( itsConvertEndianness )
     137             :         {
     138        6008 :           for( std::streamsize i = 0; i < size; i += DataSize )
     139             :             for( std::streamsize j = 0; j < DataSize; ++j )
     140        5806 :               writtenSize += itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ) + DataSize - j - 1 + i, 1 );
     141        4006 :         }
     142      212010 :         else
     143        5806 :           writtenSize = itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size );
     144      212010 : 
     145        2000 :         if(writtenSize != size)
     146      213810 :           throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
     147     4023522 :       }
     148        2000 : 
     149     4023522 :     private:
     150         200 :       std::ostream & itsStream;
     151     4023722 :       const uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon saving
     152      908101 :   };
     153      212210 : 
     154      908101 :   // ######################################################################
     155      212010 :   //! An input archive designed to load data saved using PortableBinaryOutputArchive
     156      908101 :   /*! This archive outputs data to a stream in an extremely compact binary
     157      215810 :       representation with as little extra metadata as possible.
     158     4258330 : 
     159      226200 :       This archive will load the endianness of the serialized data and
     160     4385930 :       if necessary transform it to match that of the local machine.  This comes
     161        3800 :       at a significant performance cost compared to non portable archives if
     162     4208330 :       the transformation is necessary, and also causes a small performance hit
     163     2177757 :       even if it is not necessary.
     164           0 : 
     165     2182957 :       It is recommended to use portable archives only if you know that you will
     166           0 :       be sending binary data to machines with different endianness.
     167     2182957 : 
     168       87986 :       The archive will do nothing to ensure types are the same size - that is
     169      184808 :       the responsibility of the user.
     170       87986 : 
     171      184808 :       When using a binary archive and a file stream, you must use the
     172       87986 :       std::ios::binary format flag to avoid having your data altered
     173      185008 :       inadvertently.
     174     1280658 : 
     175       11000 :       \warning This archive has not been thoroughly tested across different architectures.
     176     1287858 :                Please report any issues, optimizations, or feature requests at
     177         200 :                <a href="www.github.com/USCiLab/cereal">the project github</a>.
     178     1278258 : 
     179     2646103 :     \ingroup Archives */
     180           0 :   class PortableBinaryInputArchive : public InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>
     181     2647303 :   {
     182           0 :     public:
     183     2647303 :       //! A class containing various advanced options for the PortableBinaryInput archive
     184      598134 :       class Options
     185        3402 :       {
     186      598134 :         public:
     187        3402 :           //! Represents desired endianness
     188      598134 :           enum class Endianness : std::uint8_t
     189        4802 :           { big, little };
     190     2605917 : 
     191      207400 :           //! Default options, preserve system endianness
     192     2727917 :           static Options Default(){ return Options(); }
     193        1400 : 
     194     2563117 :           //! Load into little endian
     195      781883 :           static Options LittleEndian(){ return Options( Endianness::little ); }
     196           0 : 
     197      783483 :           //! Load into big endian
     198           0 :           static Options BigEndian(){ return Options( Endianness::big ); }
     199      783483 : 
     200       66000 :           //! Specify specific options for the PortableBinaryInputArchive
     201           0 :           /*! @param inputEndian The desired endianness of loaded (input) data */
     202       66200 :           explicit Options( Endianness inputEndian = getEndianness() ) :
     203           0 :             itsInputEndianness( inputEndian ) { }
     204       66000 : 
     205         600 :         private:
     206      187149 :           //! Gets the endianness of the system
     207        3000 :           inline static Endianness getEndianness()
     208      187149 :           { return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
     209         600 : 
     210      185549 :           //! Checks if Options is set for little endian
     211       63800 :           inline std::uint8_t is_little_endian() const
     212         200 :           { return itsInputEndianness == Endianness::little; }
     213       64800 : 
     214           0 :           friend class PortableBinaryInputArchive;
     215       64600 :           Endianness itsInputEndianness;
     216      161981 :       };
     217         200 : 
     218      162181 :       //! Construct, loading from the provided stream
     219           0 :       /*! @param stream The stream to read from. Should be opened with std::ios::binary flag.
     220      161981 :           @param options The PortableBinary specific options to use.  See the Options struct
     221         600 :                          for the values of default parameters */
     222        3400 :       PortableBinaryInputArchive(std::istream & stream, Options const & options = Options::Default()) :
     223        3600 :         InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
     224        1600 :         itsStream(stream),
     225         400 :         itsConvertEndianness( false )
     226             :       {
     227      159781 :         uint8_t streamLittleEndian;
     228           0 :         this->operator()( streamLittleEndian );
     229      161381 :         itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian;
     230           0 :       }
     231      161381 : 
     232         600 :       ~PortableBinaryInputArchive() CEREAL_NOEXCEPT = default;
     233             : 
     234        2402 :       //! Reads size bytes of data from the input stream
     235         200 :       /*! @param data The data to save
     236         400 :           @param size The number of bytes in the data
     237        1200 :           @tparam DataSize T The size of the actual type of the data elements being loaded */
     238         200 :       template <std::streamsize DataSize> inline
     239        1400 :       void loadBinary( void * const data, std::streamsize size )
     240         200 :       {
     241        1200 :         // load data
     242         200 :         auto const readSize = itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size );
     243           0 : 
     244        2002 :         if(readSize != size)
     245        2002 :           throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
     246             : 
     247             :         // flip bits if needed
     248        1200 :         if( itsConvertEndianness )
     249       25802 :         {
     250        3202 :           std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( data );
     251           0 :           for( std::streamsize i = 0; i < size; i += DataSize )
     252       25000 :             portable_binary_detail::swap_bytes<DataSize>( ptr + i );
     253        2002 :         }
     254       25802 :       }
     255        4006 : 
     256             :     private:
     257             :       std::istream & itsStream;
     258       23800 :       uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading
     259             :   };
     260           0 : 
     261           0 :   // ######################################################################
     262           0 :   // Common BinaryArchive serialization functions
     263             : 
     264       25802 :   //! Saving for POD types to portable binary
     265        4006 :   template<class T> inline
     266        4006 :   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
     267        2002 :   CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, T const & t)
     268           0 :   {
     269             :     static_assert( !std::is_floating_point<T>::value ||
     270        6008 :                    (std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
     271        6008 :                    "Portable binary only supports IEEE 754 standardized floating point" );
     272        2002 :     ar.template saveBinary<sizeof(T)>(std::addressof(t), sizeof(t));
     273             :   }
     274        6008 : 
     275        4006 :   //! Loading for POD types from portable binary
     276        1200 :   template<class T> inline
     277           0 :   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
     278           0 :   CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, T & t)
     279           4 :   {
     280           0 :     static_assert( !std::is_floating_point<T>::value ||
     281      212610 :                    (std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
     282           4 :                    "Portable binary only supports IEEE 754 standardized floating point" );
     283             :     ar.template loadBinary<sizeof(T)>(std::addressof(t), sizeof(t));
     284      212610 :   }
     285        4006 : 
     286      213818 :   //! Serializing NVP types to portable binary
     287        1208 :   template <class Archive, class T> inline
     288        4006 :   CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
     289             :   CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t )
     290      212610 :   {
     291        5206 :     ar( t.value );
     292        5206 :   }
     293        4006 : 
     294           0 :   //! Serializing SizeTags to portable binary
     295        6006 :   template <class Archive, class T> inline
     296      214610 :   CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
     297      185208 :   CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t )
     298             :   {
     299             :     ar( t.size );
     300      185008 :   }
     301             : 
     302     4208530 :   //! Saving binary data to portable binary
     303           0 :   template <class T> inline
     304             :   void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, BinaryData<T> const & bd)
     305     4023522 :   {
     306      186208 :     typedef typename std::remove_pointer<T>::type TT;
     307     4023722 :     static_assert( !std::is_floating_point<TT>::value ||
     308         200 :                    (std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
     309       25800 :                    "Portable binary only supports IEEE 754 standardized floating point" );
     310           0 : 
     311     4023522 :     ar.template saveBinary<sizeof(TT)>( bd.data, static_cast<std::streamsize>( bd.size ) );
     312      187208 :   }
     313        6002 : 
     314       25800 :   //! Loading binary data from portable binary
     315       23800 :   template <class T> inline
     316        6002 :   void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, BinaryData<T> & bd)
     317     4023722 :   {
     318     1278658 :     typedef typename std::remove_pointer<T>::type TT;
     319           0 :     static_assert( !std::is_floating_point<TT>::value ||
     320       23800 :                    (std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
     321     1274856 :                    "Portable binary only supports IEEE 754 standardized floating point" );
     322        3802 : 
     323     2185357 :     ar.template loadBinary<sizeof(TT)>( bd.data, static_cast<std::streamsize>( bd.size ) );
     324           0 :   }
     325       23800 : } // namespace cereal
     326      934301 : 
     327     1275056 : // register archives for polymorphic support
     328      914303 : CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryOutputArchive)
     329           0 : CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryInputArchive)
     330         200 : 
     331           0 : // tie input and output archives together
     332      910501 : CEREAL_SETUP_ARCHIVE_TRAITS(cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive)
     333     1275056 : 
     334     2570317 : #endif // CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_

Generated by: LCOV version 1.13