Turi Create  4.0
oarchive.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 // This file should not be included directly. use serialize.hpp
7 #ifndef TURI_SERIALIZE_HPP
8 #include <core/storage/serialization/serialize.hpp>
9 
10 #else
11 
12 #ifndef TURI_OARCHIVE_HPP
13 #define TURI_OARCHIVE_HPP
14 
15 #include <iostream>
16 #include <string>
17 #include <core/logging/assertions.hpp>
18 #include <core/storage/serialization/is_pod.hpp>
19 #include <core/storage/serialization/has_save.hpp>
20 #include <core/util/branch_hints.hpp>
21 namespace turi {
22  class dir_archive;
23  /**
24  * \ingroup group_serialization
25  * \brief The serialization output archive object which, provided
26  * with a reference to an ostream, will write to the ostream,
27  * providing serialization capabilities.
28  *
29  * Given a standard output stream, you can construct an oarchive
30  * object by:
31  * \code
32  * // where strm is an ostream object
33  * turi::oarchive oarc(strm);
34  * \endcode
35  *
36  * For instance, to serialize to a file,
37  * \code
38  * std::ofstream fout("outputfile.bin");
39  * turi::oarchive oarc(fout);
40  * \endcode
41  *
42  * Once the oarc object is constructed, \ref sec_serializable objects can be
43  * written to it using the << stream operator.
44  *
45  * \code
46  * oarc << a << b << c;
47  * \endcode
48  *
49  * Alternatively, data can be directly written to the stream
50  * using the oarchive::write() function.
51  *
52  * Written data can be deserialized using turi::iarchive.
53  * For more usage details, see \ref serialization
54  *
55  * The oarchive object should not be used once the associated stream
56  * object is closed or is destroyed.
57  *
58  * The oarc object
59  * does <b> not </b> flush the associated stream, and the user may need to
60  * manually flush the associated stream to clear any stream buffers.
61  * For instance, while the std::stringstream may be used for both output
62  * and input, it is necessary to flush the stream before all bytes written to
63  * the stringstream are available for input.
64  *
65  * If the oarchive is constructed without an ostream, writes go into a
66  * newly allocated buffer in oarc.buf. The serialized length is oarc.off
67  * and the entire buffer allocated length is oarc.len.
68  *
69  * If the oarchive is constructed with a std::vector<char>&, it will behave
70  * as like the above case, but it will keep a reference to the
71  * std::vector<char> and use that for resizing. At completion,
72  * the std::vector<char> will contain the data and oarc.off contains the
73  * serialized length.
74  *
75  * and the entire buffer allocated length is oarc.len.
76  *
77  * To use this class, include
78  * core/storage/serialization/serialization_includes.hpp
79  */
80  class oarchive{
81  public:
82  std::ostream* out = NULL;
83  dir_archive* dir = NULL;
84  std::vector<char>* vchar = NULL; // if set, buf is a pointer into vchar
85  char* buf = NULL;
86  size_t off = 0;
87  size_t len = 0;
88  /// constructor. Takes a generic std::ostream object
89  inline oarchive(std::ostream& outstream)
90  : out(&outstream) {}
91 
92  inline oarchive(void) {}
93 
94  inline oarchive(std::vector<char>& vec) {
95  vchar = &vec;
96  buf = vchar->data();
97  off = 0;
98  len = vchar->size();
99  }
100 
101  inline oarchive(dir_archive& dirarc)
102  : out(dirarc.get_output_stream()),dir(&dirarc) {}
103 
104  inline void expand_buf(size_t s) {
105  if (__unlikely__(off + s > len)) {
106  len = 2 * (s + len);
107  if (vchar != NULL) {
108  vchar->resize(len);
109  buf = vchar->data();
110  } else {
111  buf = (char*)realloc(buf, len);
112  }
113  }
114  }
115  /** Directly writes "s" bytes from the memory location
116  * pointed to by "c" into the stream.
117  */
118  inline void write(const char* c, std::streamsize s) {
119  if (out == NULL) {
120  expand_buf(s);
121  memcpy(buf + off, c, s);
122  off += s;
123  } else {
124  out->write(c, s);
125  }
126  }
127  template <typename T>
128  inline void direct_assign(const T& t) {
129  if (out == NULL) {
130  expand_buf(sizeof(T));
131  std::memcpy(buf + off, &t, sizeof(T));
132  off += sizeof(T);
133  }
134  else {
135  out->write(reinterpret_cast<const char*>(&t), sizeof(T));
136  }
137  }
138 
139  inline void advance(size_t s) {
140  if (out == NULL) {
141  expand_buf(s);
142  off += s;
143  } else {
144  out->seekp(s, std::ios_base::cur);
145  }
146  }
147 
148  /// Returns true if the underlying stream is in a failure state
149  inline bool fail() {
150  return out == NULL ? false : out->fail();
151  }
152 
153  std::string get_prefix() {
154  ASSERT_NE(dir, NULL);
155  return dir->get_next_write_prefix();
156  }
157 
158  inline ~oarchive() { }
159  };
160 
161  /**
162  * \ingroup group_serialization
163  * \brief
164  * When this archive is used to serialize an object,
165  * and the object does not support serialization,
166  * failure will only occur at runtime. Otherwise equivalent to
167  * turi::oarchive
168  */
170  public:
171  oarchive* oarc;
172  bool mine;
173 
174  /// constructor. Takes a generic std::ostream object
175  inline oarchive_soft_fail(std::ostream& outstream)
176  : oarc(new oarchive(outstream)), mine(true) { }
177 
178  inline oarchive_soft_fail(oarchive& oarc):oarc(&oarc), mine(false) {
179  }
180 
181  inline oarchive_soft_fail(void)
182  : oarc(new oarchive) {}
183 
184  /** Directly writes "s" bytes from the memory location
185  * pointed to by "c" into the stream.
186  */
187  inline void write(const char* c, std::streamsize s) {
188  oarc->write(c, s);
189  }
190  template <typename T>
191  inline void direct_assign(const T& t) {
192  oarc->direct_assign(t);
193  }
194 
195  inline bool fail() {
196  return oarc->fail();
197  }
198 
199  std::string get_prefix() {
200  return oarc->get_prefix();
201  }
202 
203  inline ~oarchive_soft_fail() {
204  if (mine) delete oarc;
205  }
206  };
207 
208  namespace archive_detail {
209 
210  /// called by the regular archive The regular archive will do a hard fail
211  template <typename OutArcType, typename T>
212  struct serialize_hard_or_soft_fail {
213  inline static void exec(OutArcType& oarc, const T& t) {
214  t.save(oarc);
215  }
216  };
217 
218  /// called by the soft fail archive
219  template <typename T>
220  struct serialize_hard_or_soft_fail<oarchive_soft_fail, T> {
221  inline static void exec(oarchive_soft_fail& oarc, const T& t) {
222  // create a regular oarchive and
223  // use the save_or_fail function which will
224  // perform a soft fail
225  save_or_fail(*(oarc.oarc), t);
226  }
227  };
228 
229 
230  /**
231  Implementation of the serializer for different types.
232  This is the catch-all. If it gets here, it must be a non-POD and is a class.
233  We therefore call the .save function.
234  Here we pick between the archive types using serialize_hard_or_soft_fail
235  */
236  template <typename OutArcType, typename T, bool IsPOD, typename Enable = void>
237  struct serialize_impl {
238  static void exec(OutArcType& oarc, const T& t) {
239  serialize_hard_or_soft_fail<OutArcType, T>::exec(oarc, t);
240  }
241  };
242 
243  /** Catch if type is a POD */
244  template <typename OutArcType, typename T>
245  struct serialize_impl<OutArcType, T, true> {
246  inline static void exec(OutArcType& oarc, const T& t) {
247  oarc.direct_assign(t);
248  //oarc.write(reinterpret_cast<const char*>(&t), sizeof(T));
249  }
250  };
251 
252  /**
253  Re-dispatch if for some reasons T already has a const
254  */
255  template <typename OutArcType, typename T>
256  struct serialize_impl<OutArcType, const T, true> {
257  inline static void exec(OutArcType& oarc, const T& t) {
258  serialize_impl<OutArcType, T, true>::exec(oarc, t);
259  }
260  };
261 
262  /**
263  Re-dispatch if for some reasons T already has a const
264  */
265  template <typename OutArcType, typename T>
266  struct serialize_impl<OutArcType, const T, false> {
267  inline static void exec(OutArcType& oarc, const T& t) {
268  serialize_impl<OutArcType, T, false>::exec(oarc, t);
269  }
270  };
271  }// archive_detail
272 
273 
274  /// \cond TURI_INTERNAL
275 
276  /**
277  Overloads the operator<< in the oarchive to
278  allow the use of the stream syntax for serialization.
279  It simply re-dispatches into the serialize_impl classes
280  */
281  template <typename T>
282  inline oarchive& operator<<(oarchive& oarc, const T& t) {
283  archive_detail::serialize_impl<oarchive,
284  T,
285  gl_is_pod<T>::value >::exec(oarc, t);
286  return oarc;
287  }
288 
289  /**
290  Overloads the operator<< in the oarchive_soft_fail to
291  allow the use of the stream syntax for serialization.
292  It simply re-dispatches into the serialize_impl classes
293  */
294  template <typename T>
295  inline oarchive_soft_fail& operator<<(oarchive_soft_fail& oarc,
296  const T& t) {
297  archive_detail::serialize_impl<oarchive_soft_fail,
298  T,
299  gl_is_pod<T>::value >::exec(oarc, t);
300  return oarc;
301  }
302 
303 
304  /**
305  Serializes an arbitrary pointer + length to an archive
306  */
307  inline oarchive& serialize(oarchive& oarc,
308  const void* str,
309  const size_t length) {
310  // save the length
311  oarc.write(reinterpret_cast<const char*>(str),
312  (std::streamsize)length);
313  assert(!oarc.fail());
314  return oarc;
315  }
316 
317 
318  /**
319  Serializes an arbitrary pointer + length to an archive
320  */
321  inline oarchive_soft_fail& serialize(oarchive_soft_fail& oarc,
322  const void* str,
323  const size_t length) {
324  // save the length
325  oarc.write(reinterpret_cast<const char*>(str),
326  (std::streamsize)length);
327  assert(!oarc.fail());
328  return oarc;
329  }
330 
331  /// \endcond TURI_INTERNAL
332 
333 }
334  /**
335  \ingroup group_serialization
336  \brief Macro to make it easy to define out-of-place saves
337 
338  In the event that it is impractical to implement a save() and load()
339  function in the class one wnats to serialize, it is necessary to define
340  an "out of save" save and load.
341 
342  See \ref sec_serializable_out_of_place for an example
343 
344  \note important! this must be defined in the global namespace!
345  */
346 #define BEGIN_OUT_OF_PLACE_SAVE(arc, tname, tval) \
347  namespace turi{ namespace archive_detail { \
348  template <typename OutArcType> struct serialize_impl<OutArcType, tname, false> { \
349  static void exec(OutArcType& arc, const tname & tval) {
350 
351 #define END_OUT_OF_PLACE_SAVE() } }; } }
352 
353 
354 #endif
355 
356 #endif
std::string get_next_write_prefix()
void write(const char *c, std::streamsize s)
Definition: oarchive.hpp:187
#define __unlikely__(x)
oarchive_soft_fail(std::ostream &outstream)
constructor. Takes a generic std::ostream object
Definition: oarchive.hpp:175
When this archive is used to serialize an object, and the object does not support serialization...
Definition: oarchive.hpp:169
general_ofstream * get_output_stream()
void write(const char *c, std::streamsize s)
Definition: oarchive.hpp:118
The serialization output archive object which, provided with a reference to an ostream, will write to the ostream, providing serialization capabilities.
Definition: oarchive.hpp:80
oarchive(std::ostream &outstream)
constructor. Takes a generic std::ostream object
Definition: oarchive.hpp:89
Tests if T is a POD type.
Definition: is_pod.hpp:28
bool fail()
Returns true if the underlying stream is in a failure state.
Definition: oarchive.hpp:149