cereal_fwd
Fork of cereal library adding archive supporting forward compatibility
extendable_binary_details.hpp
Go to the documentation of this file.
1 
3 /*
4  Copyright (c) 2016, Randolph Voorhies, Shane Grant, Michal Breiter
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 OR MICHAL BREITER 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_DETAILS_EXTENDABLE_BINARY_DETAILS_HPP_
30 #define CEREAL_DETAILS_EXTENDABLE_BINARY_DETAILS_HPP_
31 
32 #include <cereal/cereal.hpp>
33 #include <limits>
34 #include <sstream>
35 #include <queue>
36 #include <assert.h>
37 
38 namespace cereal
39 {
40  namespace extendable_binary_detail
41  {
43 
44  inline std::uint8_t is_little_endian()
45  {
46  static std::int32_t test = 1;
47  return *reinterpret_cast<std::int8_t*>( &test ) == 1;
48  }
49 
51 
54  template <std::size_t DataSize>
55  inline void swap_bytes( std::uint8_t * data )
56  {
57  for( std::size_t i = 0, end = DataSize / 2; i < end; ++i )
58  std::swap( data[i], data[DataSize - i - 1] );
59  }
60 
62 
63  enum class FieldType
64  {
66  omitted_field = 0x0,
70  positive_integer = 0x1,
74  negative_integer = 0x2,
76  floating_point = 0x3,
78  integer_packed = 0x4,
80  class_t = 0x5,
85  pointer = 0x6,
88  size_tag = 0x7,
91  packed_array = 0x8,
93  packed_struct = 0x9,
96  last_field = 0xa,
97  LAST_RESERVED_UNUSED
98  };
99 
101 
102  enum class ClassMarkers : std::uint8_t
103  {
105  None = 0,
108  EmptyClass = 0x1 << 0,
111  HasVersion = 0x1 << 1,
112  };
113 
114  ClassMarkers& operator|=(ClassMarkers& l, ClassMarkers r)
115  {
116  l = static_cast<ClassMarkers>(static_cast<std::uint8_t>(l) | static_cast<std::uint8_t>(r));
117  return l;
118  }
119 
120  std::uint8_t operator&(ClassMarkers l, ClassMarkers r)
121  {
122  return static_cast<std::uint8_t>(l) & static_cast<std::uint8_t>(r);
123  }
124 
126 
127  enum class PointerMarkers : std::uint8_t
128  {
130  None = 0,
132  Empty = 0x1 << 0, // None (0) can be used
135  IsSharedPtr = 0x1 << 1,
138  IsPolymorphicPointer = 0x1 << 2
139  };
140 
141  PointerMarkers& operator|=(PointerMarkers& l, PointerMarkers r)
142  {
143  l = static_cast<PointerMarkers>(static_cast<std::uint8_t>(l) | static_cast<std::uint8_t>(r));
144  return l;
145  }
146 
147  std::uint8_t operator&(PointerMarkers l, PointerMarkers r)
148  {
149  return static_cast<std::uint8_t>(l) & static_cast<std::uint8_t>(r);
150  }
151 
153 
154  enum { maxVarintSize = 10 };
155 
156  inline std::uint8_t writeType(FieldType fieldType, std::uint8_t other) {
157  return (static_cast<std::uint8_t>(fieldType) << 4) | (0xf & other);
158  }
159 
160  inline std::pair<FieldType, std::uint8_t> readType(std::uint8_t input) {
161  std::uint8_t fieldType = (input >> 4);
162  if(static_cast<std::uint8_t>(fieldType) >= static_cast<std::uint8_t>(FieldType::LAST_RESERVED_UNUSED)) {
163  throw Exception("FieldType has unknown value " + std::to_string(fieldType) + " all:" + std::to_string(input));
164  }
165  return std::make_pair(static_cast<FieldType>(fieldType), (0xf & input));
166  }
167 
169 
172  template <class T> inline
173  std::uint8_t getHighestBit(T v)
174  {
175  // TODO fix for big endian
176  std::uint8_t n = 0;
177  while( v != 0 ) {
178  v >>= 8;
179  n++;
180  }
181  return n;
182  }
183 
185 
186  inline std::uint8_t getHighestBit(std::uint8_t)
187  {
188  return 1;
189  }
190 
191  template <class T> inline
192  T getqNaN();
193 
194  template <> inline
195  float getqNaN()
196  {
197  union { float f; std::uint32_t i; } f;
198  f.i = 0x7fc00000;
199  return f.f;
200  }
201 
202  template <> inline
203  double getqNaN()
204  {
205  union { double d; std::uint64_t i; } d;
206  d.i = 0x7FF8000000000000ull;
207  return d.d;
208  }
209 
211  template <class T> inline
212  T getqNaN()
213  {
214  static_assert(traits::detail::delay_static_assert<T>::value, "unknown qNaN value");
215  }
216 
218  inline std::uint8_t getFloatSizeFromTagSize(std::uint8_t tagSize)
219  {
220  switch (tagSize) {
221  case 1:
222  return 4;
223  case 2:
224  return 8;
225  default:
226  throw Exception("Unsupported floating point size");
227  }
228  }
229 
231 
232  template <class T> inline
233  std::uint8_t getTagSizeFromFloatType()
234  {
235  static_assert(sizeof(T) == 4 || sizeof(T) == 8, "unsupported floating point type");
236  static_assert(std::is_floating_point<T>::value, "expected floating point type");
237  switch (sizeof(T)) {
238  case 4:
239  return 1;
240  case 8:
241  return 2;
242  }
243  }
244 
246  inline std::uint8_t getIntSizeFromTagSize(std::uint8_t tagSize)
247  {
248  switch (tagSize) {
249  case 0:
250  case 1:
251  case 2:
252  case 3:
253  case 4:
254  case 5:
255  case 6:
256  case 7:
257  case 8:
258  return tagSize;
259  case 9:
260  return 16;
261  case 10:
262  return 32;
263  default:
264  throw Exception("Unsupported int size");
265  }
266  }
267 
268 
270  inline std::uint8_t getIntSizeTagFromByteCount(unsigned int byteCount) {
271  if(byteCount <= 8) {
272  return byteCount;
273  } else if(byteCount <= 16) {
274  return 9;
275  } else if(byteCount <= 32) {
276  return 10;
277  } else {
278  throw Exception("Unsupported int size");
279  }
280  }
281 
283  struct StreamPos
284  {
285  std::streamoff start;
286  std::streamoff end;
287  };
288 
290 
300  {
301  public:
303 
307  StreamAdapter(std::istream & stream, std::stringstream & sharedObjectStream, std::size_t maxBytesInSharedStream)
308  : nowReading(nullptr), bytesLeft(0), endOfWritingStream(0), startOfStream(sharedObjectStream.tellg()),
309  mainStream(stream), backStream(sharedObjectStream), maxBytesSharedStream(maxBytesInSharedStream)
310  {}
311 
313 
315  void pushReadingPos(StreamPos & streamPos)
316  {
317  if (nowReading == nullptr) {
318  // save backStream pos for restoration
319  endOfWritingStream = backStream.tellg();
320  } else {
321  // save current position when we get back to this level
322  backStreams.push(std::make_pair(backStream.tellg(), nowReading));
323  }
324  backStream.seekg(streamPos.start);
325  nowReading = &streamPos;
326  bytesLeft = streamPos.end - streamPos.start;
327  }
328 
330 
336  inline std::size_t readBinary(void *const data, std::size_t size)
337  {
338  std::size_t readSize;
339  if (nowReading == nullptr) {
340  readSize = static_cast<std::size_t>( mainStream.rdbuf()->sgetn(reinterpret_cast<char *>( data ), size));
341  } else {
342  if (size > bytesLeft) {
343  throw Exception("went to far reading skipped shared object stream");
344  }
345  readSize = static_cast<std::size_t>( backStream.rdbuf()->sgetn(reinterpret_cast<char *>( data ), size));
346  bytesLeft -= readSize;
347  if (0 == bytesLeft) {
348  popStream();
349  }
350  }
351  return readSize;
352  }
353 
355 
357  inline void skipData(std::size_t size)
358  {
359  bool streamError;
360  if (nowReading == nullptr) {
361  mainStream.ignore(size);
362  streamError = !mainStream; // we don't care about eof here
363  } else {
364  if (size > bytesLeft) {
365  throw Exception("went to far reading skipped shared object stream");
366  }
367  backStream.ignore(size);
368  streamError = !backStream; // we don't care about eof here
369  bytesLeft -= size;
370  if (0 == bytesLeft) {
371  popStream();
372  }
373  }
374  if (streamError)
375  throw Exception("Failed to skip " + std::to_string(size) + " bytes from input stream!");
376  }
377 
379 
382  inline void checkIfMaxSize(std::size_t size, std::ostream& stream)
383  {
384  std::size_t posNow = static_cast<std::size_t>(stream.tellp());
385  if (posNow - startOfStream + size > maxBytesSharedStream) {
386  throw Exception("Shared obiect shared data limit hit");
387  }
388  }
389 
391 
394  inline void readToOtherStream(std::size_t size, std::ostream & stream)
395  {
396  checkIfMaxSize(size, stream);
397  auto copyN = [](std::istream & from, std::size_t sizeToCopy, std::ostream & to) {
398  auto start = std::istreambuf_iterator<char>(from);
399  auto end = std::istreambuf_iterator<char>();
400  auto dest = std::ostreambuf_iterator<char>(to);
401  for (; sizeToCopy > 0 && start != end; ++start, ++dest, --sizeToCopy) {
402  *dest = *start;
403  }
404  if (0 != sizeToCopy)
405  throw Exception("Failed to skip data from input stream!");
406  };
407 
408  if (nowReading == nullptr) {
409  copyN(mainStream, size, stream);
410  } else {
411  // shouldn't be here, there is no point in copying data if we are reading from backStream
412  assert(false);
413  if (size > bytesLeft) {
414  throw Exception("went to far reading skipped shared object stream");
415  }
416  copyN(mainStream, size, stream);
417  bytesLeft -= size;
418  if (0 == bytesLeft) {
419  popStream();
420  }
421  }
422  }
423 
424  private:
425 
427  void popStream()
428  {
429  if (backStreams.empty()) {
430  // move backStream back where it was for writing
431  backStream.seekg(endOfWritingStream);
432  nowReading = nullptr;
433  bytesLeft = 0;
434  } else {
435  const auto & next = backStreams.back();
436  backStream.seekg(next.first);
437  // we read something before so next.first has to be used
438  bytesLeft = next.second->end - next.first;
439  nowReading = next.second;
440  backStreams.pop();
441  }
442  }
443 
444  private:
445  StreamPos *nowReading;
446  std::size_t bytesLeft;
447  std::size_t endOfWritingStream;
448  const std::size_t startOfStream;
450  std::queue<std::pair<std::size_t, StreamPos *>> backStreams;
451  std::istream & mainStream;
452  std::istream & backStream;
453  const std::size_t maxBytesSharedStream;
454  };
455  } // namespace extendable_binary_detail
456 } // namespace cereal
457 #endif
void readToOtherStream(std::size_t size, std::ostream &stream)
Reads size bytes from the input stream and copies them to other stream.
Definition: extendable_binary_details.hpp:394
void checkIfMaxSize(std::size_t size, std::ostream &stream)
Check if by adding size bytes limit for max copied data is hit.
Definition: extendable_binary_details.hpp:382
std::size_t readBinary(void *const data, std::size_t size)
Reads binary data from stream which is on top.
Definition: extendable_binary_details.hpp:336
Used to delay a static_assert until template instantiation.
Definition: traits.hpp:57
std::uint8_t is_little_endian()
Returns true if the current machine is little endian.
Definition: extendable_binary_details.hpp:44
std::uint8_t getIntSizeFromTagSize(std::uint8_t tagSize)
Get number of bytes to load based on value of identifier for integer tag.
Definition: extendable_binary_details.hpp:246
FieldType
Describes type of saved field.
Definition: extendable_binary_details.hpp:63
std::uint8_t getHighestBit(T v)
Get number of bytes needed to save value.
Definition: extendable_binary_details.hpp:173
Class used as an adapter to queue of streams.
Definition: extendable_binary_details.hpp:299
void swap_bytes(std::uint8_t *data)
Swaps the order of bytes for some chunk of memory.
Definition: extendable_binary_details.hpp:55
Definition: access.hpp:40
void pushReadingPos(StreamPos &streamPos)
Pushes new reading position on the stream.
Definition: extendable_binary_details.hpp:315
std::uint8_t getTagSizeFromFloatType()
Get size identifier for floating point type.
Definition: extendable_binary_details.hpp:233
T getqNaN()
Get quiet not a number value for type.
Definition: extendable_binary_details.hpp:212
PointerMarkers
Markers for FieldType::pointer.
Definition: extendable_binary_details.hpp:127
Main cereal functionality.
StreamAdapter(std::istream &stream, std::stringstream &sharedObjectStream, std::size_t maxBytesInSharedStream)
Construct new object with main and secondary stream.
Definition: extendable_binary_details.hpp:307
ClassMarkers
Markers for FieldType::class_t.
Definition: extendable_binary_details.hpp:102
Struct to keep position of start and end in stream.
Definition: extendable_binary_details.hpp:283
std::uint8_t getIntSizeTagFromByteCount(unsigned int byteCount)
Get identifier for size for integer tag based on number of bytes being stored.
Definition: extendable_binary_details.hpp:270
void skipData(std::size_t size)
Discards size bytes from the input stream.
Definition: extendable_binary_details.hpp:357
std::uint8_t getFloatSizeFromTagSize(std::uint8_t tagSize)
Get size of floating point data for size identifier.
Definition: extendable_binary_details.hpp:218
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48