cereal
A C++11 library for serialization
portable_binary.hpp
1 
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  {
41 
42  inline std::uint8_t is_little_endian()
43  {
44  static std::int32_t test = 1;
45  return *reinterpret_cast<std::int8_t*>( &test ) == 1;
46  }
47 
49 
52  template <std::size_t DataSize>
53  inline void swap_bytes( std::uint8_t * data )
54  {
55  for( std::size_t i = 0, end = DataSize / 2; i < end; ++i )
56  std::swap( data[i], data[DataSize - i - 1] );
57  }
58  } // end namespace portable_binary_detail
59 
60  // ######################################################################
62 
78  class PortableBinaryOutputArchive : public OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>
79  {
80  public:
82  class Options
83  {
84  public:
86  enum class Endianness : std::uint8_t
87  { big, little };
88 
90  static Options Default(){ return Options(); }
91 
93  static Options LittleEndian(){ return Options( Endianness::little ); }
94 
96  static Options BigEndian(){ return Options( Endianness::big ); }
97 
99 
100  explicit Options( Endianness outputEndian = getEndianness() ) :
101  itsOutputEndianness( outputEndian ) { }
102 
103  private:
105  inline static Endianness getEndianness()
106  { return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
107 
109  inline std::uint8_t is_little_endian() const
110  { return itsOutputEndianness == Endianness::little; }
111 
112  friend class PortableBinaryOutputArchive;
113  Endianness itsOutputEndianness;
114  };
115 
117 
120  PortableBinaryOutputArchive(std::ostream & stream, Options const & options = Options::Default()) :
121  OutputArchive<PortableBinaryOutputArchive, AllowEmptyClassElision>(this),
122  itsStream(stream),
123  itsConvertEndianness( portable_binary_detail::is_little_endian() ^ options.is_little_endian() )
124  {
125  this->operator()( options.is_little_endian() );
126  }
127 
129 
131  template <std::streamsize DataSize> inline
132  void saveBinary( const void * data, std::streamsize size )
133  {
134  std::streamsize writtenSize = 0;
135 
136  if( itsConvertEndianness )
137  {
138  for( std::streamsize i = 0; i < size; i += DataSize )
139  for( std::streamsize j = 0; j < DataSize; ++j )
140  writtenSize += itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ) + DataSize - j - 1 + i, 1 );
141  }
142  else
143  writtenSize = itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size );
144 
145  if(writtenSize != size)
146  throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
147  }
148 
149  private:
150  std::ostream & itsStream;
151  const uint8_t itsConvertEndianness;
152  };
153 
154  // ######################################################################
156 
180  class PortableBinaryInputArchive : public InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>
181  {
182  public:
184  class Options
185  {
186  public:
188  enum class Endianness : std::uint8_t
189  { big, little };
190 
192  static Options Default(){ return Options(); }
193 
195  static Options LittleEndian(){ return Options( Endianness::little ); }
196 
198  static Options BigEndian(){ return Options( Endianness::big ); }
199 
201 
202  explicit Options( Endianness inputEndian = getEndianness() ) :
203  itsInputEndianness( inputEndian ) { }
204 
205  private:
207  inline static Endianness getEndianness()
208  { return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
209 
211  inline std::uint8_t is_little_endian() const
212  { return itsInputEndianness == Endianness::little; }
213 
214  friend class PortableBinaryInputArchive;
215  Endianness itsInputEndianness;
216  };
217 
219 
222  PortableBinaryInputArchive(std::istream & stream, Options const & options = Options::Default()) :
223  InputArchive<PortableBinaryInputArchive, AllowEmptyClassElision>(this),
224  itsStream(stream),
225  itsConvertEndianness( false )
226  {
227  uint8_t streamLittleEndian;
228  this->operator()( streamLittleEndian );
229  itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian;
230  }
231 
233 
235 
238  template <std::streamsize DataSize> inline
239  void loadBinary( void * const data, std::streamsize size )
240  {
241  // load data
242  auto const readSize = itsStream.rdbuf()->sgetn( reinterpret_cast<char*>( data ), size );
243 
244  if(readSize != size)
245  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  if( itsConvertEndianness )
249  {
250  std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( data );
251  for( std::streamsize i = 0; i < size; i += DataSize )
252  portable_binary_detail::swap_bytes<DataSize>( ptr + i );
253  }
254  }
255 
256  private:
257  std::istream & itsStream;
258  uint8_t itsConvertEndianness;
259  };
260 
261  // ######################################################################
262  // Common BinaryArchive serialization functions
263 
265  template<class T> inline
266  typename std::enable_if<std::is_arithmetic<T>::value, void>::type
267  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  ar.template saveBinary<sizeof(T)>(std::addressof(t), sizeof(t));
273  }
274 
276  template<class T> inline
277  typename std::enable_if<std::is_arithmetic<T>::value, void>::type
278  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  ar.template loadBinary<sizeof(T)>(std::addressof(t), sizeof(t));
284  }
285 
287  template <class Archive, class T> inline
288  CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
289  CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair<T> & t )
290  {
291  ar( t.value );
292  }
293 
295  template <class Archive, class T> inline
296  CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive)
297  CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag<T> & t )
298  {
299  ar( t.size );
300  }
301 
303  template <class T> inline
304  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  ar.template saveBinary<sizeof(TT)>( bd.data, static_cast<std::streamsize>( bd.size ) );
312  }
313 
315  template <class T> inline
316  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  ar.template loadBinary<sizeof(TT)>( bd.data, static_cast<std::streamsize>( bd.size ) );
324  }
325 } // namespace cereal
326 
327 // register archives for polymorphic support
330 
331 // tie input and output archives together
333 
334 #endif // CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_
cereal::InputArchive
The base input archive class.
Definition: cereal.hpp:710
cereal::PortableBinaryOutputArchive::Options::Default
static Options Default()
Default options, preserve system endianness.
Definition: portable_binary.hpp:90
cereal::PortableBinaryInputArchive::loadBinary
void loadBinary(void *const data, std::streamsize size)
Reads size bytes of data from the input stream.
Definition: portable_binary.hpp:239
cereal::PortableBinaryInputArchive::Options::Endianness
Endianness
Represents desired endianness.
Definition: portable_binary.hpp:188
cereal::PortableBinaryOutputArchive::PortableBinaryOutputArchive
PortableBinaryOutputArchive(std::ostream &stream, Options const &options=Options::Default())
Construct, outputting to the provided stream.
Definition: portable_binary.hpp:120
cereal::PortableBinaryInputArchive::PortableBinaryInputArchive
PortableBinaryInputArchive(std::istream &stream, Options const &options=Options::Default())
Construct, loading from the provided stream.
Definition: portable_binary.hpp:222
cereal::PortableBinaryInputArchive::Options::Default
static Options Default()
Default options, preserve system endianness.
Definition: portable_binary.hpp:192
cereal::InputArchive< PortableBinaryInputArchive, AllowEmptyClassElision >::operator()
PortableBinaryInputArchive & operator()(Types &&... args)
Serializes all passed in data.
Definition: cereal.hpp:728
CEREAL_ARCHIVE_RESTRICT
#define CEREAL_ARCHIVE_RESTRICT(INTYPE, OUTTYPE)
A macro to use to restrict which types of archives your function will work for.
Definition: traits.hpp:1315
cereal::PortableBinaryInputArchive
An input archive designed to load data saved using PortableBinaryOutputArchive.
Definition: portable_binary.hpp:180
CEREAL_SERIALIZE_FUNCTION_NAME
#define CEREAL_SERIALIZE_FUNCTION_NAME
The serialization/deserialization function name to search for.
Definition: macros.hpp:78
CEREAL_LOAD_FUNCTION_NAME
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
cereal::PortableBinaryInputArchive::Options::Options
Options(Endianness inputEndian=getEndianness())
Specify specific options for the PortableBinaryInputArchive.
Definition: portable_binary.hpp:202
CEREAL_REGISTER_ARCHIVE
#define CEREAL_REGISTER_ARCHIVE(Archive)
Registers a specific Archive type with cereal.
Definition: cereal.hpp:195
cereal::PortableBinaryOutputArchive::Options::LittleEndian
static Options LittleEndian()
Save as little endian.
Definition: portable_binary.hpp:93
cereal::OutputArchive
The base output archive class.
Definition: cereal.hpp:318
cereal::PortableBinaryInputArchive::Options::BigEndian
static Options BigEndian()
Load into big endian.
Definition: portable_binary.hpp:198
cereal::PortableBinaryOutputArchive::saveBinary
void saveBinary(const void *data, std::streamsize size)
Writes size bytes of data to the output stream.
Definition: portable_binary.hpp:132
CEREAL_SAVE_FUNCTION_NAME
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
CEREAL_SETUP_ARCHIVE_TRAITS
#define CEREAL_SETUP_ARCHIVE_TRAITS(InputArchive, OutputArchive)
Sets up traits that relate an input archive to an output archive.
Definition: traits.hpp:169
CEREAL_NOEXCEPT
#define CEREAL_NOEXCEPT
Defines the CEREAL_NOEXCEPT macro to use instead of noexcept.
Definition: macros.hpp:130
cereal::PortableBinaryOutputArchive::Options::BigEndian
static Options BigEndian()
Save as big endian.
Definition: portable_binary.hpp:96
cereal::PortableBinaryInputArchive::Options
A class containing various advanced options for the PortableBinaryInput archive.
Definition: portable_binary.hpp:184
cereal::portable_binary_detail::is_little_endian
std::uint8_t is_little_endian()
Returns true if the current machine is little endian.
Definition: portable_binary.hpp:42
cereal::portable_binary_detail::swap_bytes
void swap_bytes(std::uint8_t *data)
Swaps the order of bytes for some chunk of memory.
Definition: portable_binary.hpp:53
cereal.hpp
Main cereal functionality.
cereal::Exception
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48
cereal::PortableBinaryOutputArchive::Options::Options
Options(Endianness outputEndian=getEndianness())
Specify specific options for the PortableBinaryOutputArchive.
Definition: portable_binary.hpp:100
cereal::PortableBinaryOutputArchive::Options::Endianness
Endianness
Represents desired endianness.
Definition: portable_binary.hpp:86
cereal::PortableBinaryOutputArchive
An output archive designed to save data in a compact binary representation portable over different ar...
Definition: portable_binary.hpp:78
cereal::OutputArchive< PortableBinaryOutputArchive, AllowEmptyClassElision >::operator()
PortableBinaryOutputArchive & operator()(Types &&... args)
Serializes all passed in data.
Definition: cereal.hpp:331
cereal::PortableBinaryInputArchive::Options::LittleEndian
static Options LittleEndian()
Load into little endian.
Definition: portable_binary.hpp:195
cereal::PortableBinaryOutputArchive::Options
A class containing various advanced options for the PortableBinaryOutput archive.
Definition: portable_binary.hpp:82