pstore2
types.hpp
Go to the documentation of this file.
1 //===- include/pstore/serialize/types.hpp -----------------*- mode: C++ -*-===//
2 //* _ *
3 //* | |_ _ _ _ __ ___ ___ *
4 //* | __| | | | '_ \ / _ \/ __| *
5 //* | |_| |_| | |_) | __/\__ \ *
6 //* \__|\__, | .__/ \___||___/ *
7 //* |___/|_| *
8 //===----------------------------------------------------------------------===//
9 //
10 // Part of the pstore project, under the Apache License v2.0 with LLVM Exceptions.
11 // See https://github.com/SNSystems/pstore/blob/master/LICENSE.txt for license
12 // information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //===----------------------------------------------------------------------===//
31 
32 
43 
44 
55 
56 
65 
66 
77 
78 
91 
92 #ifndef PSTORE_SERIALIZE_TYPES_HPP
93 #define PSTORE_SERIALIZE_TYPES_HPP
94 
95 #include <algorithm>
96 #include <cstring>
97 
98 #include "pstore/serialize/common.hpp"
99 #include "pstore/support/gsl.hpp"
100 #include "pstore/support/portab.hpp"
101 
102 namespace pstore {
103  namespace serialize {
104 
105  template <typename Archive>
106  using archive_result_type = typename std::remove_reference<Archive>::type::result_type;
107 
116 
117  template <typename Ty, typename Enable = void>
118  struct serializer {
132  template <typename Archive>
133  static auto write (Archive && archive, Ty const & v) -> archive_result_type<Archive> {
134  return v.write (std::forward<Archive> (archive));
135  }
136 
152  template <typename Archive>
153  static void read (Archive && archive, Ty & v) {
154  PSTORE_ASSERT (reinterpret_cast<std::uintptr_t> (&v) % alignof (Ty) == 0);
155  new (&v) Ty (std::forward<Archive> (archive));
156  }
157  };
158 
159 
160  namespace details {
161 
162  struct getn_helper {
163  public:
164  template <typename Archive, typename Span>
165  static void getn (Archive && archive, Span span) {
166  getn_helper::invoke<Archive, Span> (std::forward<Archive> (archive), span,
167  nullptr);
168  }
169 
170  private:
173  template <typename Archive, typename Span>
174  static void invoke (Archive && archive, Span span, ...) {
175  for (auto & v : span) {
176  archive.get (v);
177  }
178  }
179 
181  template <typename Archive, typename Span>
182  static void
183  invoke (Archive && archive, Span span,
184  decltype (&std::remove_reference<Archive>::type::template getn<Span>)) {
185  archive.getn (span);
186  }
187  };
188 
189  struct readn_helper {
190  public:
191  template <typename Archive, typename SpanType>
192  static void readn (Archive && archive, SpanType span) {
193  readn_helper::invoke (std::forward<Archive> (archive), span, nullptr);
194  }
195 
196  private:
199  template <typename Archive, typename SpanType>
200  static void invoke (Archive && archive, SpanType span, ...) {
201  for (auto & v : span) {
203  std::forward<Archive> (archive), v);
204  }
205  }
206 
210  template <typename Archive, typename SpanType>
211  static void
212  invoke (Archive && archive, SpanType span,
214  typename std::remove_reference<Archive>::type, SpanType>)) {
216  std::forward<Archive> (archive), span);
217  }
218  };
219 
220 
221  //* _ _ _ _ *
222  //* __ __ ___ _(_) |_ ___ _ _ | |_ ___| |_ __ ___ _ _ *
223  //* \ V V / '_| | _/ -_) ' \ | ' \/ -_) | '_ \/ -_) '_| *
224  //* \_/\_/|_| |_|\__\___|_||_| |_||_\___|_| .__/\___|_| *
225  //* |_| *
233 
234  struct writen_helper {
235  public:
236  template <typename Archive, typename SpanType>
237  static auto writen (Archive && archive, SpanType span)
238  -> archive_result_type<Archive> {
239  return writen_helper::invoke (std::forward<Archive> (archive), span, nullptr);
240  }
241 
242  private:
243  template <typename Archive, typename SpanType>
244  static auto invoke (Archive && archive, SpanType span, ...)
245  -> archive_result_type<Archive> {
246  // A simmple implementation of writen() which loops over the span calling
247  // write() for each element.
249  for (auto & v : span) {
251  std::forward<Archive> (archive), v);
252  }
253  return r.get ();
254  }
255 
256  // This overload is called if Archive has a writen() method. SFINAE means that we
257  // fall back to the ellipsis overload if it does not.
258  template <typename Archive, typename SpanType>
259  static auto
260  invoke (Archive && archive, SpanType span,
262  typename std::remove_reference<Archive>::type, SpanType>))
263  -> archive_result_type<Archive> {
265  std::forward<Archive> (archive), span);
266  }
267  };
268 
269  } // namespace details
270 
272  template <typename Ty>
273  struct serializer<Ty, typename std::enable_if<std::is_trivial<Ty>::value>::type> {
278  template <typename Archive>
279  static auto write (Archive && archive, Ty const & v) -> archive_result_type<Archive> {
280  return archive.put (v);
281  }
282 
287  template <typename Archive, typename SpanType>
288  static auto writen (Archive && archive, SpanType span) -> archive_result_type<Archive> {
289  static_assert (std::is_same<typename SpanType::element_type, Ty>::value,
290  "span type does not match the serializer type");
291  return archive.putn (span);
292  }
293 
299  template <typename Archive>
300  static void read (Archive && archive, Ty & out) {
301  PSTORE_ASSERT (reinterpret_cast<std::uintptr_t> (&out) % alignof (Ty) == 0);
302  archive.get (out);
303  }
304 
309  template <typename Archive, typename Span>
310  static void readn (Archive && archive, Span span) {
311  static_assert (std::is_same<typename Span::element_type, Ty>::value,
312  "span type does not match the serializer type");
313  details::getn_helper::getn (std::forward<Archive> (archive), span);
314  }
315  };
316 
317 
320  template <typename T1, typename T2>
321  struct is_compatible : std::false_type {};
322  template <typename T>
323  struct is_compatible<T, T> : std::true_type {};
324 
325  template <typename T1, typename T2>
326  struct is_compatible<T1 const, T2> : is_compatible<T1, T2> {};
327  template <typename T1, typename T2>
328  struct is_compatible<T1, T2 const> : is_compatible<T1, T2> {};
329  template <typename T1, typename T2>
330  struct is_compatible<T1 const, T2 const> : is_compatible<T1, T2> {};
331 
332 
333  // TODO: enable when we're able to switch to C++1z.
334  // template <typename T1, typename T2>
335  // constexpr bool is_compatible_v = is_compatible<T1, T2>::value;
336 
337  template <typename Archive, typename ElementType>
338  void read_uninit (Archive && archive, ElementType & uninit) {
339  serializer<ElementType>::read (std::forward<Archive> (archive), uninit);
340  }
342  template <typename Archive, typename ElementType>
343  void read_uninit (Archive && archive, gsl::span<ElementType, 1> uninit_span) {
344  static_assert (uninit_span.size () == 1, "Expected span to be 1 in specialization");
345  serializer<ElementType>::read (std::forward<Archive> (archive), uninit_span[0]);
346  }
347 
348  template <typename Archive, typename ElementType, std::ptrdiff_t Extent>
349  void read_uninit (Archive && archive, gsl::span<ElementType, Extent> uninit_span) {
350  details::readn_helper::readn (std::forward<Archive> (archive), uninit_span);
351  }
352 
353 
354 #ifndef NDEBUG
355  template <std::ptrdiff_t SpanExtent>
356  void flood (gsl::span<std::uint8_t, SpanExtent> sp) noexcept {
357  static std::array<std::uint8_t, 4> const deadbeef{{0xDE, 0xAD, 0xBE, 0xEF}};
358 
359  auto it = std::begin (sp);
360  auto end = std::end (sp);
361 
362  for (auto uint32_count = sp.size () / 4U; uint32_count > 0U; --uint32_count) {
363  *(it++) = deadbeef[0];
364  *(it++) = deadbeef[1];
365  *(it++) = deadbeef[2];
366  *(it++) = deadbeef[3];
367  }
368 
369  auto index = std::size_t{0};
370  for (; it != end; ++it) {
371  *it = deadbeef.at (index);
372  index = (index + 1) % deadbeef.size ();
373  }
374  }
375 #else
376  template <std::ptrdiff_t SpanExtent>
377  inline void flood (gsl::span<std::uint8_t, SpanExtent>) noexcept {}
378 #endif
379  template <typename T>
380  inline void flood (T * const t) noexcept {
381  flood (gsl::make_span (reinterpret_cast<std::uint8_t *> (t), sizeof (T)));
382  }
383 
384 
385  namespace details {
386 
387  // A simplified definition of std::aligned_storage to workaround a bug in Visual Studio
388  // 2017 prior to v15.8. Microsoft's fix for that bug introduces a binary
389  // incompatibility. In order to avoid introducing that incompatibility to projects using
390  // pstore, we have our own version of aligned_storage here. (See the description of
391  // Microsoft's "_ENABLE_EXTENDED_ALIGNED_STORAGE" macro.)
392  template <std::size_t Len, std::size_t Align>
394  struct alignas (Align) type {
395  std::uint8_t _[(Len + Align - 1) / Align * Align];
396  };
397 
398  PSTORE_STATIC_ASSERT (alignof (type) >= Align);
399  PSTORE_STATIC_ASSERT (sizeof (type) >= Len);
400  };
401 
402  } // end namespace details
403 
404  //* _ *
405  //* _ _ ___ __ _ __| | *
406  //* | '_/ -_) _` / _` | *
407  //* |_| \___\__,_\__,_| *
408  //* *
411  template <typename Ty, typename Archive>
412  Ty read (Archive && archive) {
413  using T2 = typename std::remove_const<Ty>::type;
415  flood (&uninit_buffer);
416 
417  // Deserialize into the uninitialized buffer.
418  auto & t2 = reinterpret_cast<T2 &> (uninit_buffer);
419  read_uninit (std::forward<Archive> (archive), t2);
420 
421  // This object will destroy the remains of the T2 instance in uninit_buffer.
422  auto dtor = [] (T2 * p) { p->~T2 (); };
423  std::unique_ptr<T2, decltype (dtor)> d (&t2, dtor);
424  return std::move (t2);
425  }
426 
429  template <typename Archive, typename Ty>
430  void read (Archive && archive, gsl::span<Ty, 1> span) {
431  PSTORE_ASSERT (span.size () == 1U);
432  span[0] = read<Ty> (std::forward<Archive> (archive));
433  }
434 
435  template <typename Archive, typename ElementType, std::ptrdiff_t Extent>
436  void read (Archive && archive, gsl::span<ElementType, Extent> span) {
437  for (auto & element : span) {
438  element.~ElementType ();
439  }
440  read_uninit (std::forward<Archive> (archive), span);
441  }
443 
444 
445  //* _ _ *
446  //* __ __ ___ _(_) |_ ___ *
447  //* \ V V / '_| | _/ -_) *
448  //* \_/\_/|_| |_|\__\___| *
449  //* *
452  template <typename Archive, typename Ty>
453  auto write (Archive && archive, Ty const & ty) -> archive_result_type<Archive> {
454  return serializer<Ty>::write (std::forward<Archive> (archive), ty);
455  }
456 
458  template <typename Archive, typename ElementType, std::ptrdiff_t Extent>
459  auto write (Archive && archive, gsl::span<ElementType, Extent> sp)
460  -> archive_result_type<Archive> {
461  return details::writen_helper::writen (std::forward<Archive> (archive), sp);
462  }
463 
465  template <typename Archive, typename ElementType>
466  auto write (Archive && archive, gsl::span<ElementType, 1> sp)
467  -> archive_result_type<Archive> {
468  static_assert (sp.size () == 1, "Expected size to be 1 in specialization");
469  return serializer<ElementType>::write (std::forward<Archive> (archive), sp[0]);
470  }
471 
473  template <typename Archive, typename Ty, typename... Args>
474  auto write (Archive && archive, Ty const & ty, Args const &... args)
475  -> archive_result_type<Archive> {
476  auto result = write (std::forward<Archive> (archive), ty);
477  write (std::forward<Archive> (archive), args...);
478  return result;
479  }
481 
482  } // namespace serialize
483 } // namespace pstore
484 
485 
490 namespace pstore {
491  namespace serialize {
492 
502 
503  template <typename Archive, typename InputIterator>
504  void write_range (Archive && archive, InputIterator begin, InputIterator end) {
505 
506  auto const distance = std::distance (begin, end);
507  PSTORE_ASSERT (distance >= 0);
508  write (archive, static_cast<std::size_t> (distance));
509 
510  using value_type = typename std::iterator_traits<InputIterator>::value_type;
511  std::for_each (begin, end, [&archive] (value_type const & value) {
512  write (std::forward<Archive> (archive), value);
513  });
514  }
515 
528 
529  template <typename Ty, typename Archive, typename OutputIterator>
530  OutputIterator read_range (Archive && archive, OutputIterator output) {
531  auto distance = read<std::size_t> (std::forward<Archive> (archive));
532  while (distance-- > 0) {
533  *output = read<Ty> (std::forward<Archive> (archive));
534  ++output;
535  }
536  return output;
537  }
538  } // namespace serialize
539 } // namespace pstore
541 #endif // PSTORE_SERIALIZE_TYPES_HPP
static void readn(Archive &&archive, Span span)
Reads a standard-layout value of type Ty from an archive.
Definition: types.hpp:310
A helper class which remembers the first time that it is assigned to.
Definition: common.hpp:46
Definition: gsl.hpp:58
The primary template for serialization of non standard layout types.
Definition: types.hpp:118
static void read(Archive &&archive, Ty &out)
Reads a standard-layout value of type Ty from an archive.
Definition: types.hpp:300
void write_range(Archive &&archive, InputIterator begin, InputIterator end)
Writes a range of values [first, last) to the supplied archive.
Definition: types.hpp:504
static auto write(Archive &&archive, Ty const &v) -> archive_result_type< Archive >
Writes an single value of type Ty to an archive.
Definition: types.hpp:133
This template class enables us to provide the "writen()" interface on all serialization classes witho...
Definition: types.hpp:234
OutputIterator read_range(Archive &&archive, OutputIterator output)
Reads a sequence of values from the given archive and adds them to an iterator range beginning at &#39;ou...
Definition: types.hpp:530
Definition: chunked_sequence.hpp:607
static auto writen(Archive &&archive, SpanType span) -> archive_result_type< Archive >
Writes a span of objects to an archive.
Definition: types.hpp:288
Definition: print.cpp:18
static void read(Archive &&archive, Ty &v)
Reads a value of type Ty from an archive.
Definition: types.hpp:153
Definition: nonpod2.cpp:40
If the two types T1 and T2 have a compatible representation when serialized, provides the member cons...
Definition: types.hpp:321
static auto write(Archive &&archive, Ty const &v) -> archive_result_type< Archive >
Writes an individual object to an archive.
Definition: types.hpp:279