LCOV - code coverage report
Current view: top level - cereal/archives - portable_binary.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 70 72 97.2 %
Date: 2022-01-16 21:05:07 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 the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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       11912 :     inline std::uint8_t is_little_endian()
      43             :     {
      44             :       static std::int32_t test = 1;
      45       11912 :       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       47700 :     inline void swap_bytes( std::uint8_t * data )
      54             :     {
      55      141700 :       for( std::size_t i = 0, end = DataSize / 2; i < end; ++i )
      56       94000 :         std::swap( data[i], data[DataSize - i - 1] );
      57       47700 :     }
      58             :   } // end namespace portable_binary_detail
      59             : 
      60             :   // ######################################################################
      61             :   //! An output archive designed to save data in a compact binary representation portable over different architectures
      62             :   /*! This archive outputs data to a stream in an extremely compact binary
      63             :       representation with as little extra metadata as possible.
      64             : 
      65             :       This archive will record the endianness of the data as well as the desired in/out endianness
      66             :       and assuming that the user takes care of ensuring serialized types are the same size
      67             :       across machines, is portable over different architectures.
      68             : 
      69             :       When using a binary archive and a file stream, you must use the
      70             :       std::ios::binary format flag to avoid having your data altered
      71             :       inadvertently.
      72             : 
      73             :       \warning This archive has not been thoroughly tested across different architectures.
      74             :                Please report any issues, optimizations, or feature requests at
      75             :                <a href="www.github.com/USCiLab/cereal">the project github</a>.
      76             : 
      77             :     \ingroup Archives */
      78             :   class PortableBinaryOutputArchive : public OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>
      79             :   {
      80             :     public:
      81             :       //! A class containing various advanced options for the PortableBinaryOutput archive
      82             :       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        3604 :           static Options Default(){ return Options(); }
      91             : 
      92             :           //! Save as little endian
      93           2 :           static Options LittleEndian(){ return Options( Endianness::little ); }
      94             : 
      95             :           //! Save as big endian
      96           2 :           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        3608 :           explicit Options( Endianness outputEndian = getEndianness() ) :
     101        3608 :             itsOutputEndianness( outputEndian ) { }
     102             : 
     103             :         private:
     104             :           //! Gets the endianness of the system
     105        3604 :           inline static Endianness getEndianness()
     106        3604 :           { return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
     107             : 
     108             :           //! Checks if Options is set for little endian
     109        8008 :           inline std::uint8_t is_little_endian() const
     110        8008 :           { return itsOutputEndianness == Endianness::little; }
     111             : 
     112             :           friend class PortableBinaryOutputArchive;
     113             :           Endianness itsOutputEndianness;
     114             :       };
     115             : 
     116             :       //! 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             :                          for the values of default parameters */
     120        4004 :       PortableBinaryOutputArchive(std::ostream & stream, Options const & options = Options::Default()) :
     121             :         OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
     122             :         itsStream(stream),
     123        4004 :         itsConvertEndianness( portable_binary_detail::is_little_endian() ^ options.is_little_endian() )
     124             :       {
     125        4004 :         this->operator()( options.is_little_endian() );
     126        4004 :       }
     127             : 
     128        4004 :       ~PortableBinaryOutputArchive() CEREAL_NOEXCEPT = default;
     129             : 
     130             :       //! Writes size bytes of data to the output stream
     131             :       template <std::streamsize DataSize> inline
     132     2573552 :       void saveBinary( const void * data, std::streamsize size )
     133             :       {
     134     2573552 :         std::streamsize writtenSize = 0;
     135             : 
     136     2573552 :         if( itsConvertEndianness )
     137             :         {
     138       25000 :           for( std::streamsize i = 0; i < size; i += DataSize )
     139      111200 :             for( std::streamsize j = 0; j < DataSize; ++j )
     140       88800 :               writtenSize += itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ) + DataSize - j - 1 + i, 1 );
     141             :         }
     142             :         else
     143     2570952 :           writtenSize = itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size );
     144             : 
     145     2573552 :         if(writtenSize != size)
     146           0 :           throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
     147     2573552 :       }
     148             : 
     149             :     private:
     150             :       std::ostream & itsStream;
     151             :       const uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon saving
     152             :   };
     153             : 
     154             :   // ######################################################################
     155             :   //! An input archive designed to load data saved using PortableBinaryOutputArchive
     156             :   /*! This archive outputs data to a stream in an extremely compact binary
     157             :       representation with as little extra metadata as possible.
     158             : 
     159             :       This archive will load the endianness of the serialized data and
     160             :       if necessary transform it to match that of the local machine.  This comes
     161             :       at a significant performance cost compared to non portable archives if
     162             :       the transformation is necessary, and also causes a small performance hit
     163             :       even if it is not necessary.
     164             : 
     165             :       It is recommended to use portable archives only if you know that you will
     166             :       be sending binary data to machines with different endianness.
     167             : 
     168             :       The archive will do nothing to ensure types are the same size - that is
     169             :       the responsibility of the user.
     170             : 
     171             :       When using a binary archive and a file stream, you must use the
     172             :       std::ios::binary format flag to avoid having your data altered
     173             :       inadvertently.
     174             : 
     175             :       \warning This archive has not been thoroughly tested across different architectures.
     176             :                Please report any issues, optimizations, or feature requests at
     177             :                <a href="www.github.com/USCiLab/cereal">the project github</a>.
     178             : 
     179             :     \ingroup Archives */
     180             :   class PortableBinaryInputArchive : public InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>
     181             :   {
     182             :     public:
     183             :       //! A class containing various advanced options for the PortableBinaryInput archive
     184             :       class Options
     185             :       {
     186             :         public:
     187             :           //! Represents desired endianness
     188             :           enum class Endianness : std::uint8_t
     189             :           { big, little };
     190             : 
     191             :           //! Default options, preserve system endianness
     192        3804 :           static Options Default(){ return Options(); }
     193             : 
     194             :           //! Load into little endian
     195           2 :           static Options LittleEndian(){ return Options( Endianness::little ); }
     196             : 
     197             :           //! Load into big endian
     198           2 :           static Options BigEndian(){ return Options( Endianness::big ); }
     199             : 
     200             :           //! Specify specific options for the PortableBinaryInputArchive
     201             :           /*! @param inputEndian The desired endianness of loaded (input) data */
     202        3808 :           explicit Options( Endianness inputEndian = getEndianness() ) :
     203        3808 :             itsInputEndianness( inputEndian ) { }
     204             : 
     205             :         private:
     206             :           //! Gets the endianness of the system
     207        3804 :           inline static Endianness getEndianness()
     208        3804 :           { return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
     209             : 
     210             :           //! Checks if Options is set for little endian
     211        4204 :           inline std::uint8_t is_little_endian() const
     212        4204 :           { return itsInputEndianness == Endianness::little; }
     213             : 
     214             :           friend class PortableBinaryInputArchive;
     215             :           Endianness itsInputEndianness;
     216             :       };
     217             : 
     218             :       //! Construct, loading from the provided stream
     219             :       /*! @param stream The stream to read from. Should be opened with std::ios::binary flag.
     220             :           @param options The PortableBinary specific options to use.  See the Options struct
     221             :                          for the values of default parameters */
     222        3804 :       PortableBinaryInputArchive(std::istream & stream, Options const & options = Options::Default()) :
     223             :         InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
     224             :         itsStream(stream),
     225        4204 :         itsConvertEndianness( false )
     226             :       {
     227             :         uint8_t streamLittleEndian;
     228        4204 :         this->operator()( streamLittleEndian );
     229        4204 :         itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian;
     230        4204 :       }
     231             : 
     232        4204 :       ~PortableBinaryInputArchive() CEREAL_NOEXCEPT = default;
     233             : 
     234             :       //! Reads size bytes of data from the input stream
     235             :       /*! @param data The data to save
     236             :           @param size The number of bytes in the data
     237             :           @tparam DataSize T The size of the actual type of the data elements being loaded */
     238             :       template <std::streamsize DataSize> inline
     239     2575052 :       void loadBinary( void * const data, std::streamsize size )
     240             :       {
     241             :         // load data
     242     2575052 :         auto const readSize = itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size );
     243             : 
     244     2575052 :         if(readSize != size)
     245           0 :           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     2575052 :         if( itsConvertEndianness )
     249             :         {
     250        3500 :           std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( data );
     251       26800 :           for( std::streamsize i = 0; i < size; i += DataSize )
     252       23300 :             portable_binary_detail::swap_bytes<DataSize>( ptr + i );
     253             :         }
     254     2575052 :       }
     255             : 
     256             :     private:
     257             :       std::istream & itsStream;
     258             :       uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading
     259             :   };
     260             : 
     261             :   // ######################################################################
     262             :   // Common BinaryArchive serialization functions
     263             : 
     264             :   //! Saving for POD types to portable binary
     265             :   template<class T> inline
     266             :   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
     267     2501358 :   CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, T const & t)
     268             :   {
     269             :     static_assert( !std::is_floating_point<T>::value ||
     270             :                    (std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
     271             :                    "Portable binary only supports IEEE 754 standardized floating point" );
     272     2501358 :     ar.template saveBinary<sizeof(T)>(std::addressof(t), sizeof(t));
     273     2501358 :   }
     274             : 
     275             :   //! Loading for POD types from portable binary
     276             :   template<class T> inline
     277             :   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
     278     2502858 :   CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, T & t)
     279             :   {
     280             :     static_assert( !std::is_floating_point<T>::value ||
     281             :                    (std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
     282             :                    "Portable binary only supports IEEE 754 standardized floating point" );
     283     2502858 :     ar.template loadBinary<sizeof(T)>(std::addressof(t), sizeof(t));
     284     2502858 :   }
     285             : 
     286             :   //! Serializing NVP types to portable binary
     287             :   template <class Archive, class T> inline
     288             :   CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
     289     1481998 :   CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t )
     290             :   {
     291     1481998 :     ar( t.value );
     292     1481798 :   }
     293             : 
     294             :   //! Serializing SizeTags to portable binary
     295             :   template <class Archive, class T> inline
     296             :   CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
     297      163188 :   CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t )
     298             :   {
     299      163188 :     ar( t.size );
     300      163188 :   }
     301             : 
     302             :   //! Saving binary data to portable binary
     303             :   template <class T> inline
     304       72194 :   void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, BinaryData<T> const & bd)
     305             :   {
     306             :     typedef typename std::remove_pointer<T>::type TT;
     307             :     static_assert( !std::is_floating_point<TT>::value ||
     308             :                    (std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
     309             :                    "Portable binary only supports IEEE 754 standardized floating point" );
     310             : 
     311       72194 :     ar.template saveBinary<sizeof(TT)>( bd.data, static_cast<std::streamsize>( bd.size ) );
     312       72194 :   }
     313             : 
     314             :   //! Loading binary data from portable binary
     315             :   template <class T> inline
     316       72194 :   void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, BinaryData<T> & bd)
     317             :   {
     318             :     typedef typename std::remove_pointer<T>::type TT;
     319             :     static_assert( !std::is_floating_point<TT>::value ||
     320             :                    (std::is_floating_point<TT>::value && std::numeric_limits<TT>::is_iec559),
     321             :                    "Portable binary only supports IEEE 754 standardized floating point" );
     322             : 
     323       72194 :     ar.template loadBinary<sizeof(TT)>( bd.data, static_cast<std::streamsize>( bd.size ) );
     324       72194 :   }
     325             : } // namespace cereal
     326             : 
     327             : // register archives for polymorphic support
     328             : CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryOutputArchive)
     329             : CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryInputArchive)
     330             : 
     331             : // tie input and output archives together
     332             : CEREAL_SETUP_ARCHIVE_TRAITS(cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive)
     333             : 
     334             : #endif // CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_

Generated by: LCOV version 1.14