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 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  {
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::size_t DataSize> inline
132  void saveBinary( const void * data, std::size_t size )
133  {
134  std::size_t writtenSize = 0;
135 
136  if( itsConvertEndianness )
137  {
138  for( std::size_t i = 0; i < size; i += DataSize )
139  for( std::size_t j = 0; j < DataSize; ++j )
140  writtenSize += static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ) + DataSize - j - 1 + i, 1 ) );
141  }
142  else
143  writtenSize = static_cast<std::size_t>( 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::size_t DataSize> inline
239  void loadBinary( void * const data, std::size_t size )
240  {
241  // load data
242  auto const readSize = static_cast<std::size_t>( 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::size_t 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::size_t>( 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::size_t>( bd.size ) );
324  }
325 } // namespace cereal
326 
327 // register archives for polymorphic support
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_
#define CEREAL_SETUP_ARCHIVE_TRAITS(InputArchive, OutputArchive)
Sets up traits that relate an input archive to an output archive.
Definition: traits.hpp:169
PortableBinaryInputArchive & operator()(Types &&...args)
Serializes all passed in data.
Definition: cereal.hpp:617
A class containing various advanced options for the PortableBinaryInput archive.
Definition: portable_binary.hpp:184
A class containing various advanced options for the PortableBinaryOutput archive. ...
Definition: portable_binary.hpp:82
static Options LittleEndian()
Load into little endian.
Definition: portable_binary.hpp:195
STL namespace.
The base input archive class.
Definition: cereal.hpp:599
static Options LittleEndian()
Save as little endian.
Definition: portable_binary.hpp:93
#define CEREAL_SERIALIZE_FUNCTION_NAME
The serialization/deserialization function name to search for.
Definition: macros.hpp:78
static Options Default()
Default options, preserve system endianness.
Definition: portable_binary.hpp:192
PortableBinaryOutputArchive(std::ostream &stream, Options const &options=Options::Default())
Construct, outputting to the provided stream.
Definition: portable_binary.hpp:120
Definition: access.hpp:40
#define CEREAL_REGISTER_ARCHIVE(Archive)
Registers a specific Archive type with cereal.
Definition: cereal.hpp:141
#define CEREAL_ARCHIVE_RESTRICT(INTYPE, OUTTYPE)
A macro to use to restrict which types of archives your function will work for.
Definition: traits.hpp:1293
Main cereal functionality.
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
Options(Endianness outputEndian=getEndianness())
Specify specific options for the PortableBinaryOutputArchive.
Definition: portable_binary.hpp:100
void swap_bytes(std::uint8_t *data)
Swaps the order of bytes for some chunk of memory.
Definition: portable_binary.hpp:53
std::uint8_t is_little_endian()
Returns true if the current machine is little endian.
Definition: portable_binary.hpp:42
static Options BigEndian()
Load into big endian.
Definition: portable_binary.hpp:198
Endianness
Represents desired endianness.
Definition: portable_binary.hpp:86
#define CEREAL_NOEXCEPT
Defines the CEREAL_NOEXCEPT macro to use instead of noexcept.
Definition: macros.hpp:130
PortableBinaryOutputArchive & operator()(Types &&...args)
Serializes all passed in data.
Definition: cereal.hpp:247
Options(Endianness inputEndian=getEndianness())
Specify specific options for the PortableBinaryInputArchive.
Definition: portable_binary.hpp:202
The base output archive class.
Definition: cereal.hpp:234
void saveBinary(const void *data, std::size_t size)
Writes size bytes of data to the output stream.
Definition: portable_binary.hpp:132
An input archive designed to load data saved using PortableBinaryOutputArchive.
Definition: portable_binary.hpp:180
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
static Options Default()
Default options, preserve system endianness.
Definition: portable_binary.hpp:90
static Options BigEndian()
Save as big endian.
Definition: portable_binary.hpp:96
void loadBinary(void *const data, std::size_t size)
Reads size bytes of data from the input stream.
Definition: portable_binary.hpp:239
Endianness
Represents desired endianness.
Definition: portable_binary.hpp:188
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48
PortableBinaryInputArchive(std::istream &stream, Options const &options=Options::Default())
Construct, loading from the provided stream.
Definition: portable_binary.hpp:222
An output archive designed to save data in a compact binary representation portable over different ar...
Definition: portable_binary.hpp:78