pstore2
archive.hpp
Go to the documentation of this file.
1 //===- include/pstore/serialize/archive.hpp ---------------*- mode: C++ -*-===//
2 //* _ _ *
3 //* __ _ _ __ ___| |__ (_)_ _____ *
4 //* / _` | '__/ __| '_ \| \ \ / / _ \ *
5 //* | (_| | | | (__| | | | |\ V / __/ *
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 //===----------------------------------------------------------------------===//
18 
19 
53 
54 
69 
80 
103 
104 #ifndef PSTORE_SERIALIZE_ARCHIVE_HPP
105 #define PSTORE_SERIALIZE_ARCHIVE_HPP
106 
107 #include <cstring>
108 
109 #include "pstore/serialize/common.hpp"
110 #include "pstore/support/error.hpp"
111 
112 namespace pstore {
113  namespace serialize {
115  namespace archive {
116 
117  template <typename T>
118  auto unsigned_cast (T const & t) -> typename std::make_unsigned<T>::type {
119  using unsigned_type = typename std::make_unsigned<T>::type;
120  PSTORE_ASSERT (t >= 0);
121  return static_cast<unsigned_type> (t);
122  }
123 
130  struct void_type {};
131 
132 
133  // *****************************
134  // * w r i t e r _ b a s e *
135  // *****************************
137 
138  template <typename WriterPolicy>
139  class writer_base {
140  public:
141  using policy_type = WriterPolicy;
142  using result_type = typename policy_type::result_type;
143 
144  writer_base (writer_base const &) = delete;
145  writer_base (writer_base &&) noexcept = default;
146 
147  virtual ~writer_base () noexcept {
148  no_ex_escape ([this] () { this->flush (); });
149  }
150 
151  writer_base & operator= (writer_base &&) noexcept = default;
152  writer_base & operator= (writer_base const &) = delete;
153 
157 
159  template <typename Ty>
160  auto put (Ty const & t) -> result_type {
161  static_assert (std::is_standard_layout<Ty>::value,
162  "writer_base can only write standard-layout types!");
163  PSTORE_ASSERT (!flushed_);
164  result_type r = policy_.put (t);
165  bytes_consumed_ += sizeof (t);
166  return r;
167  }
168 
176  template <typename Span>
177  auto putn (Span sp) -> result_type {
178  using element_type = typename Span::element_type;
179  static_assert (std::is_standard_layout<element_type>::value,
180  "writer_base can only write standard-layout types!");
181  PSTORE_ASSERT (!flushed_);
182  auto r = putn_helper::template putn<Span> (policy_, sp);
183  bytes_consumed_ += unsigned_cast (sp.size_bytes ());
184  return r;
185  }
187 
189  void flush () {
190  if (!flushed_) {
191  policy_.flush ();
192  flushed_ = true;
193  }
194  }
195 
197  std::size_t bytes_consumed () const { return bytes_consumed_; }
198 
201  std::size_t bytes_produced () const {
202  return bytes_produced_helper (*this).get (policy_, nullptr);
203  }
204 
207  WriterPolicy & writer_policy () noexcept { return policy_; }
208  WriterPolicy const & writer_policy () const noexcept { return policy_; }
210 
211  protected:
212  explicit writer_base (WriterPolicy policy = WriterPolicy ())
213  : policy_{std::move (policy)} {}
214 
215  private:
218  class bytes_produced_helper {
219  public:
220  explicit bytes_produced_helper (writer_base const & writer)
221  : writer_{writer} {}
222  // (This is a varargs function so that it is considered last in template
223  // resolution.)
224  template <typename Policy>
225  std::size_t get (Policy const & /*policy*/, ...) { // NOLINT(cert-dcl50-cpp)
226  return writer_.bytes_consumed ();
227  }
228  template <typename Policy>
229  std::size_t get (Policy const & policy, decltype (&Policy::bytes_produced)) {
230  return policy.bytes_produced ();
231  }
232 
233  private:
234  writer_base const & writer_;
235  };
236 
237  struct putn_helper {
238  public:
239  using result_type = typename policy_type::result_type;
240 
241  template <typename Span>
242  static auto putn (WriterPolicy & policy, Span span) -> result_type {
243  return invoke (policy, span, nullptr);
244  }
245 
246  private:
247  // This overload is always in the set of overloads but a function with
248  // ellipsis parameter has the lowest ranking for overload resolution.
249  template <typename P, typename Span>
250  static auto invoke (P & policy, Span span, ...) // NOLINT(cert-dcl50-cpp)
251  -> result_type {
253  for (auto & v : span) {
254  r = policy.put (v);
255  }
256  return r.get ();
257  }
258 
259  // This overload is called if P has a putn<>() method. SFINAE means that we fall
260  // back to the ellipsis overload if it does not.
261  template <typename P, typename Span>
262  static auto invoke (P & policy, Span span, decltype (&P::template putn<Span>))
263  -> result_type {
264  return policy.putn (span);
265  }
266  };
267 
268  WriterPolicy policy_;
269  std::size_t bytes_consumed_ = 0U;
270  bool flushed_{false};
271  };
272 
273 
274  namespace details {
276  public:
277  using result_type = std::size_t;
278  using container = std::vector<std::uint8_t>;
279  using const_iterator = container::const_iterator;
280 
281  explicit vector_writer_policy (container & bytes) noexcept
282  : bytes_ (bytes) {}
283 
284  template <typename Ty>
285  auto put (Ty const & t) -> result_type {
286  auto const old_size = bytes_.size ();
287  auto const * const first = reinterpret_cast<std::uint8_t const *> (&t);
288  std::copy (first, first + sizeof (Ty), std::back_inserter (bytes_));
289  return old_size;
290  }
291 
292  template <typename SpanType>
293  auto putn (SpanType sp) -> result_type {
294  auto const old_size = bytes_.size ();
295  auto const * const first = reinterpret_cast<std::uint8_t const *> (sp.data ());
296  std::copy (first, first + sp.size_bytes (), std::back_inserter (bytes_));
297  return old_size;
298  }
299 
301  std::size_t size () const noexcept { return bytes_.size (); }
302 
303  void flush () noexcept {}
304 
307  container::const_iterator begin () const { return std::begin (bytes_); }
310  container::const_iterator end () const { return std::end (bytes_); }
311 
312  private:
313  container & bytes_;
314  };
315  } // namespace details
316 
317  // *******************************
318  // * v e c t o r _ w r i t e r *
319  // *******************************
320 
323 
324  class vector_writer final : public writer_base<details::vector_writer_policy> {
325  public:
326  explicit vector_writer (std::vector<std::uint8_t> & container)
328  details::vector_writer_policy{container}) {}
329  vector_writer (vector_writer const &) = delete;
330  vector_writer (vector_writer &&) = delete;
331 
332  ~vector_writer () noexcept override;
333 
334  vector_writer & operator= (vector_writer const &) = delete;
335  vector_writer & operator= (vector_writer &&) = delete;
336 
337  using container = policy_type::container;
338  using const_iterator = policy_type::const_iterator;
339 
342  const_iterator begin () const { return writer_policy ().begin (); }
344  const_iterator end () const { return writer_policy ().end (); }
345  };
346 
347 
348 
356  std::ostream & operator<< (std::ostream & os, vector_writer const & writer);
357 
358 
359  // *********************************
360  // * b u f f e r _ w r i t e r *
361  // *********************************
362 
363  namespace details {
365  public:
366  using result_type = void *;
367 
368  buffer_writer_policy (void * const first, void * const last) noexcept
369  : begin_ (static_cast<std::uint8_t *> (first))
370 #ifndef NDEBUG
371  , end_ (static_cast<std::uint8_t *> (last))
372 #endif
373  , it_ (static_cast<std::uint8_t *> (first)) {
374 
375  (void) last;
376  PSTORE_ASSERT (end_ >= it_);
377  }
378 
381  template <typename Ty>
382  auto put (Ty const & v) -> result_type {
383  auto const size = sizeof (v);
384  PSTORE_ASSERT (it_ + size <= end_);
385  auto const result = it_;
386  std::memcpy (it_, &v, size);
387  it_ += size;
388  PSTORE_ASSERT (it_ <= end_);
389  return result;
390  }
391 
393  std::size_t size () const noexcept {
394  PSTORE_ASSERT (it_ >= begin_);
395  static_assert (sizeof (std::size_t) >= sizeof (std::ptrdiff_t),
396  "sizeof size_t should be at least sizeof ptrdiff_t");
397  return static_cast<std::size_t> (it_ - begin_);
398  }
399 
400  void flush () noexcept {}
401 
402  using const_iterator = std::uint8_t const *;
403 
405  const_iterator begin () const noexcept { return begin_; }
407  const_iterator end () const noexcept { return it_; }
408 
409  private:
411  std::uint8_t * begin_;
412 #ifndef NDEBUG
413  std::uint8_t * end_;
415 #endif
416  std::uint8_t * it_;
419  };
420  } // namespace details
421 
422  class buffer_writer final : public writer_base<details::buffer_writer_policy> {
423  public:
429  buffer_writer (void * const first, void * const last)
430  : writer_base<policy_type> (policy_type{first, last}) {}
431  buffer_writer (buffer_writer const &) = delete;
432  buffer_writer (buffer_writer &&) = delete;
433 
439  buffer_writer (void * const first, std::size_t const size)
440  : buffer_writer (static_cast<std::uint8_t *> (first),
441  static_cast<std::uint8_t *> (first) + size) {}
442 
445  template <typename T>
446  explicit buffer_writer (T * t)
447  : buffer_writer (t, sizeof (T)) {}
448 
449  ~buffer_writer () noexcept override;
450 
451  buffer_writer & operator= (buffer_writer const &) = delete;
452  buffer_writer & operator= (buffer_writer &&) = delete;
453 
454  using const_iterator = policy_type::const_iterator;
455 
458  const_iterator begin () const { return writer_policy ().begin (); }
460  const_iterator end () const { return writer_policy ().end (); }
461  };
462 
463 
471  std::ostream & operator<< (std::ostream & os, buffer_writer const & writer);
472 
473 
474  // ***************
475  // * n u l l *
476  // ***************
477 
478  namespace details {
479 
480  class null_policy {
481  public:
482  using result_type = void_type;
483  template <typename Ty>
484  auto put (Ty const &) -> result_type {
485  return {};
486  }
487  template <typename SpanType>
488  auto putn (SpanType) -> result_type {
489  return {};
490  }
491 
492  void flush () noexcept {}
493  };
494 
495  } // end namespace details
496 
499  class null final : public writer_base<details::null_policy> {
500  public:
501  null () = default;
502  null (null const &) = delete;
503  null (null &&) = delete;
504 
505  ~null () noexcept override;
506 
507  null & operator= (null const &) = delete;
508  null & operator= (null &&) = delete;
509  };
510 
511 
512  // *******************************
513  // * r a n g e _ r e a d e r *
514  // *******************************
515 
517  template <typename InputIterator>
518  class range_reader {
519  static_assert (sizeof (typename std::iterator_traits<InputIterator>::value_type) ==
520  1,
521  "archive_reader reads from a byte-wide sequence");
522 
523  public:
525  explicit range_reader (InputIterator first)
526  : first_ (first) {}
527 
528  InputIterator iterator () { return first_; }
529 
532  template <typename Ty>
533  void get (Ty & v) {
534  static_assert (std::is_standard_layout<Ty>::value,
535  "range_reader can only read standard-layout types");
536  auto ptr = reinterpret_cast<std::uint8_t *> (&v);
537  auto const last = ptr + sizeof (Ty);
538  while (ptr != last) {
539  *(ptr++) = *(first_++);
540  }
541  }
542 
543  template <typename SpanType>
544  void getn (SpanType span) {
545  using element_type = typename SpanType::element_type;
546  static_assert (std::is_standard_layout<element_type>::value,
547  "range_reader can only read standard-layout types");
548  auto out = reinterpret_cast<std::uint8_t *> (span.data ());
549  auto last = out + span.size_bytes ();
550  while (out != last) {
551  *(out++) = *(first_++);
552  }
553  }
554 
555  private:
556  InputIterator first_;
557  };
558 
559 
565  template <typename InputIterator>
566  range_reader<InputIterator> make_reader (InputIterator first) {
567  return range_reader<InputIterator>{first};
568  }
569 
570 
571  // *********************************
572  // * b u f f e r _ r e a d e r *
573  // *********************************
574 
577  public:
579  constexpr buffer_reader (void const * const first, void const * const last) noexcept
580  : first_ (static_cast<std::uint8_t const *> (first))
581  , last_ (static_cast<std::uint8_t const *> (last)) {}
582 
585  constexpr buffer_reader (void const * const first, std::size_t const size) noexcept
586  : first_ (static_cast<std::uint8_t const *> (first))
587  , last_ (static_cast<std::uint8_t const *> (first) + size) {}
588 
591  template <typename SpanType>
592  explicit buffer_reader (SpanType const span) noexcept
593  : first_ (reinterpret_cast<std::uint8_t const *> (span.data ()))
594  , last_ (first_ + span.size_bytes ()) {}
595 
598  template <typename T>
599  T get () {
600  typename std::remove_const<T>::type result;
601  static_assert (std::is_standard_layout<T>::value,
602  "buffer_reader(T&) can only read standard-layout types");
603  if (first_ + sizeof (T) > last_) {
604  raise (std::errc::no_buffer_space,
605  "Attempted to read past the end of a buffer.");
606  }
607  std::memcpy (&result, first_, sizeof (T));
608  first_ += sizeof (T);
609  return result;
610  }
611 
612  private:
613  std::uint8_t const * first_;
614  std::uint8_t const * last_;
615  };
616 
617  } // end namespace archive
618  } // end namespace serialize
619 } // end namespace pstore
620 
621 #endif // PSTORE_SERIALIZE_ARCHIVE_HPP
std::size_t bytes_consumed() const
Returns the number of bytes that have been written via this archive.
Definition: archive.hpp:197
A helper class which remembers the first time that it is assigned to.
Definition: common.hpp:46
const_iterator begin() const noexcept
Returns a const_iterator for the beginning of the byte range.
Definition: archive.hpp:405
buffer_writer(void *const first, void *const last)
Constructs the writer using the range [first, last).
Definition: archive.hpp:429
auto put(Ty const &t) -> result_type
Writes one or more instances of a standard-layout type Ty to the output. Must not be used once the st...
Definition: archive.hpp:160
The base class for archive-writer objects.
Definition: archive.hpp:139
const_iterator end() const
Returns a const_iterator for the end of the byte vector managed by the object.
Definition: archive.hpp:460
container::const_iterator begin() const
Returns a const_iterator for the beginning of the byte vector managed by the object.
Definition: archive.hpp:307
std::size_t size() const noexcept
Returns the size of the byte vector managed by the object.
Definition: archive.hpp:301
Definition: chunked_sequence.hpp:607
void flush()
Flushes the stream to the output.
Definition: archive.hpp:189
The archiver put() and [optional] putn() methods can optionally return a value to the caller...
Definition: archive.hpp:130
constexpr buffer_reader(void const *const first, void const *const last) noexcept
Constructs the writer using a pair of pointer to define the range [first, last).
Definition: archive.hpp:579
buffer_writer(T *t)
Constructs a buffer_writer from a pointer to allocated uninitialized storage.
Definition: archive.hpp:446
const_iterator end() const
Returns a const_iterator for the end of the byte vector managed by the object.
Definition: archive.hpp:344
An archive-writer which simply discards any data that it writes.
Definition: archive.hpp:499
Definition: print.cpp:18
buffer_writer(void *const first, std::size_t const size)
Constructs the writer starting at the address given by &#39;first&#39; and with a number of bytes &#39;size&#39;...
Definition: archive.hpp:439
std::ostream & operator<<(std::ostream &os, vector_writer const &writer)
Writes the contents of a vector_writer object to an ostream as a stream of space-separated two-digit ...
Definition: archive.cpp:60
container::const_iterator end() const
Returns a const_iterator for the end of the byte vector managed by the object.
Definition: archive.hpp:310
const_iterator begin() const
Returns a const_iterator for the beginning of the byte vector managed by the object.
Definition: archive.hpp:458
constexpr buffer_reader(void const *const first, std::size_t const size) noexcept
Constructs the writer using a pointer and size to define the range [first, first+size).
Definition: archive.hpp:585
database_reader make_reader(pstore::database const &db, pstore::address const addr) noexcept
A convenience function which provides symmetry with the make_writer() function.
Definition: db_archive.hpp:186
Definition: nonpod2.cpp:40
buffer_reader(SpanType const span) noexcept
Constructs the writer using a pointer and size to define the range [first, first+size).
Definition: archive.hpp:592
An archive-writer which writes data to a std::vector of std::uint8_t bytes.
Definition: archive.hpp:324
Provides an pstore-specific error codes and a suitable error category for them.
const_iterator begin() const
Returns a const_iterator for the beginning of the byte vector managed by the object.
Definition: archive.hpp:342
An archive-reader which consumes data from an iterator.
Definition: archive.hpp:518
An archive-reader which consumes data from a supplied pointer range.
Definition: archive.hpp:576
const_iterator end() const noexcept
Returns a const_iterator for the end of byte range written to the buffer.
Definition: archive.hpp:407
WriterPolicy & writer_policy() noexcept
Definition: archive.hpp:207
auto putn(Span sp) -> result_type
Writes a span of values to the output.
Definition: archive.hpp:177
range_reader(InputIterator first)
Constructs the writer using an input iterator.
Definition: archive.hpp:525
std::size_t size() const noexcept
Returns the number of bytes written to the buffer.
Definition: archive.hpp:393
auto put(Ty const &v) -> result_type
Writes an object to the output buffer.
Definition: archive.hpp:382
std::size_t bytes_produced() const
Returns the number of bytes that the policy object wrote to its final destination.
Definition: archive.hpp:201