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