pstore2
fragment.hpp
1 //===- include/pstore/mcrepo/fragment.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 //===----------------------------------------------------------------------===//
16 #ifndef PSTORE_MCREPO_FRAGMENT_HPP
17 #define PSTORE_MCREPO_FRAGMENT_HPP
18 
19 #include "pstore/mcrepo/bss_section.hpp"
23 #include "pstore/support/pointee_adaptor.hpp"
24 
25 namespace pstore {
26  namespace repo {
27 
28  namespace details {
29 
32  template <typename Iterator>
34  public:
35  using value_type = section_kind const;
36  using difference_type = typename std::iterator_traits<Iterator>::difference_type;
37  using pointer = value_type *;
38  using reference = value_type &;
39  using iterator_category = std::input_iterator_tag;
40 
41  content_type_iterator () = default;
42  explicit content_type_iterator (Iterator it)
43  : it_{it} {}
45  : it_{rhs.it_} {}
46  content_type_iterator & operator= (content_type_iterator const & rhs) {
47  it_ = rhs.it_;
48  return *this;
49  }
50  bool operator== (content_type_iterator const & rhs) const { return it_ == rhs.it_; }
51  bool operator!= (content_type_iterator const & rhs) const {
52  return !(operator== (rhs));
53  }
54  content_type_iterator & operator++ () {
55  ++it_;
56  return *this;
57  }
58  content_type_iterator operator++ (int) {
59  auto const old = *this;
60  it_++;
61  return old;
62  }
63 
64  reference operator* () const { return (*it_).kind (); }
65  pointer operator-> () const { return &(*it_).kind (); }
66  reference operator[] (difference_type n) const { return it_[n].kind (); }
67 
68  private:
69  Iterator it_{};
70  };
71 
72  template <typename Iterator>
73  inline content_type_iterator<Iterator> make_content_type_iterator (Iterator it) {
75  }
76 
77  } // end namespace details
78 
79 
82  template <section_kind T>
83  struct enum_to_section {
84  using type = generic_section;
85  };
86  template <section_kind T>
87  using enum_to_section_t = typename enum_to_section<T>::type;
88 
89  template <>
90  struct enum_to_section<section_kind::bss> {
91  using type = bss_section;
92  };
93  template <>
94  struct enum_to_section<section_kind::debug_line> {
95  using type = debug_line_section;
96  };
97  template <>
98  struct enum_to_section<section_kind::linked_definitions> {
99  using type = linked_definitions;
100  };
101 
102  //* __ _ *
103  //* / _|_ _ __ _ __ _ _ __ ___ _ _| |_ *
104  //* | _| '_/ _` / _` | ' \/ -_) ' \ _| *
105  //* |_| |_| \__,_\__, |_|_|_\___|_||_\__| *
106  //* |___/ *
107  class fragment {
108  public:
109  // TODO: 32-bits would be plenty for an intra-fragment offset.
111  using const_iterator = member_array::indices::const_iterator;
112 
125  template <typename Transaction, typename Iterator>
126  static pstore::extent<fragment> alloc (Transaction & transaction, Iterator first,
127  Iterator last);
128 
135  static std::shared_ptr<fragment const> load (database const & db,
136  extent<fragment> const & location);
137 
144  static std::shared_ptr<fragment> load (transaction_base & transaction,
145  extent<fragment> const & location);
146 
152  bool has_section (section_kind const kind) const noexcept {
153  return arr_.has_index (kind);
154  }
155 
158 
161  template <section_kind Key>
162  auto at () const noexcept -> typename enum_to_section<Key>::type const & {
163  return at_impl<Key> (*this);
164  }
167  template <section_kind Key>
168  auto at () noexcept -> typename enum_to_section<Key>::type & {
169  return at_impl<Key> (*this);
170  }
171 
174  template <section_kind Key>
175  auto atp () const noexcept -> typename enum_to_section<Key>::type const * {
176  return atp_impl<Key> (*this);
177  }
180  template <section_kind Key>
181  auto atp () noexcept -> typename enum_to_section<Key>::type * {
182  return atp_impl<Key> (*this);
183  }
185 
187  std::size_t size () const noexcept { return arr_.size (); }
188 
190  member_array const & members () const noexcept { return arr_; }
191 
192  section_kind front () const noexcept { return arr_.get_indices ().front (); }
193 
194  const_iterator begin () const noexcept { return arr_.get_indices ().begin (); }
195  const_iterator end () const noexcept { return arr_.get_indices ().end (); }
196 
205  template <typename Iterator>
206  static std::size_t size_bytes (Iterator first, Iterator last);
207 
209  std::size_t size_bytes () const;
210 
211  private:
212  template <typename IteratorIdx>
213  constexpr fragment (IteratorIdx const first_index, IteratorIdx const last_index)
214  : arr_{first_index, last_index} {
215 
216  // Verify that the structure layout is the same regardless of the compiler and
217  // target platform.
218  PSTORE_STATIC_ASSERT (alignof (fragment) == 16);
219  PSTORE_STATIC_ASSERT (sizeof (fragment) == 32);
220  PSTORE_STATIC_ASSERT (offsetof (fragment, signature_) == 0);
221  PSTORE_STATIC_ASSERT (offsetof (fragment, padding1_) == 8);
222  PSTORE_STATIC_ASSERT (offsetof (fragment, arr_) == 16);
223  padding1_ = 0; // assignment to silence an "unused" warning from clang.
224  }
225 
228  template <typename ReturnType, typename GetOp>
229  static ReturnType load_impl (extent<fragment> const & location, GetOp get);
230 
231  template <typename Iterator>
232  static void check_range_is_sorted (Iterator first, Iterator last);
233 
239  template <typename InstanceType>
240  InstanceType const & offset_to_instance (std::uint64_t offset) const noexcept {
241  return offset_to_instance_impl<InstanceType const> (*this, offset);
242  }
243  template <typename InstanceType>
244  InstanceType & offset_to_instance (std::uint64_t offset) noexcept {
245  return offset_to_instance_impl<InstanceType> (*this, offset);
246  }
248 
249 
252  template <typename InstanceType, typename Fragment>
253  static InstanceType & offset_to_instance_impl (Fragment && f,
254  std::uint64_t offset) noexcept;
255 
256  template <section_kind Key, typename InstanceType = typename enum_to_section<Key>::type>
257  static std::uint64_t section_offset_is_valid (fragment const & f,
258  extent<fragment> const & fext,
259  std::uint64_t min_offset,
260  std::uint64_t offset, std::size_t size);
261 
262 
263  static bool fragment_appears_valid (fragment const & f, extent<fragment> const & fext);
264 
274  template <section_kind Key, typename Fragment,
275  typename ResultType = typename inherit_const<
276  Fragment, typename enum_to_section<Key>::type>::type>
277  static ResultType & at_impl (Fragment && f) noexcept {
278  PSTORE_ASSERT (f.has_section (Key));
279  return f.template offset_to_instance<ResultType> (f.arr_[Key]);
280  }
281 
291  template <section_kind Key, typename Fragment,
292  typename ResultType = typename inherit_const<
293  Fragment, typename enum_to_section<Key>::type>::type>
294  static ResultType * atp_impl (Fragment && f) noexcept {
295  return f.has_section (Key) ? &f.template at<Key> () : nullptr;
296  }
297 
308  template <typename Iterator>
309  static void populate (void * ptr, Iterator first, Iterator last);
310 
311  static constexpr std::array<char, 8> fragment_signature_ = {
312  {'F', 'r', 'a', 'g', 'm', 'e', 'n', 't'}};
313 
314  std::array<char, 8> signature_ = fragment_signature_;
315  std::uint64_t padding1_ = 0;
316 
320  alignas (16) member_array arr_;
321  };
322 
323  // operator<<
324  // ~~~~~~~~~~
325  template <typename OStream>
326  OStream & operator<< (OStream & os, section_kind const kind) {
327  auto * name = "*unknown*";
328 #define X(k) \
329 case section_kind::k: name = #k; break;
330  switch (kind) {
331  PSTORE_MCREPO_SECTION_KINDS
332  case section_kind::last: PSTORE_ASSERT (false); break;
333  }
334 #undef X
335  os << name;
336  return os;
337  }
338 
339  // populate [private, static]
340  // ~~~~~~~~
341  template <typename Iterator>
342  void fragment::populate (void * const ptr, Iterator const first, Iterator const last) {
343  // Construct the basic fragment structure into this memory.
344  auto * const fragment_ptr =
345  new (ptr) fragment (details::make_content_type_iterator (first),
346  details::make_content_type_iterator (last));
347  // Point past the end of the sparse array.
348  auto * out = reinterpret_cast<std::uint8_t *> (fragment_ptr) +
349  offsetof (fragment, arr_) + fragment_ptr->arr_.size_bytes ();
350 
351  // Copy the contents of each of the segments to the fragment.
352  std::for_each (
353  first, last, [&out, &fragment_ptr] (section_creation_dispatcher const & c) {
354  out = reinterpret_cast<std::uint8_t *> (c.aligned (out));
355  fragment_ptr->arr_[c.kind ()] = reinterpret_cast<std::uintptr_t> (out) -
356  reinterpret_cast<std::uintptr_t> (fragment_ptr);
357  out = c.write (out);
358  });
359 #ifndef NDEBUG
360  {
361  auto const size = fragment::size_bytes (first, last);
362  PSTORE_ASSERT (out >= ptr &&
363  static_cast<std::size_t> (
364  out - reinterpret_cast<std::uint8_t *> (ptr)) == size);
365  PSTORE_ASSERT (size == fragment_ptr->size_bytes ());
366  }
367 #endif
368  }
369 
370  // alloc
371  // ~~~~~
372  template <typename Transaction, typename Iterator>
373  auto fragment::alloc (Transaction & transaction, Iterator const first, Iterator const last)
375  fragment::check_range_is_sorted (first, last);
376  // Compute the number of bytes of storage that we'll need for this fragment.
377  auto const size = fragment::size_bytes (first, last);
378 
379  // Allocate storage for the fragment including its three arrays.
380  // We can't use the version of alloc_rw() which returns typed_address<> since we need
381  // an explicit number of bytes allocated for the fragment.
382  std::pair<std::shared_ptr<void>, pstore::address> const storage =
383  transaction.alloc_rw (size, alignof (fragment));
384  fragment::populate (storage.first.get (), first, last);
385  return {typed_address<fragment> (storage.second), size};
386  }
387 
388 
389  // load impl
390  // ~~~~~~~~~
391  template <typename ReturnType, typename GetOp>
392  auto fragment::load_impl (extent<fragment> const & fext, GetOp get) -> ReturnType {
393  if (fext.size < sizeof (fragment)) {
394  raise (error_code::bad_fragment_record);
395  }
396 
397  ReturnType f = get (fext);
398  if (!fragment::fragment_appears_valid (*f, fext)) {
399  raise (error_code::bad_fragment_record);
400  }
401  return f;
402  }
403 
404  // load
405  // ~~~~~
406  inline auto fragment::load (transaction_base & transaction,
407  pstore::extent<fragment> const & location)
408  -> std::shared_ptr<fragment> {
409  return load_impl<std::shared_ptr<fragment>> (
410  location,
411  [&transaction] (extent<fragment> const & x) { return transaction.getrw (x); });
412  }
413 
414 
415  // offset to instance
416  // ~~~~~~~~~~~~~~~~~~
417  // This is the implementation used by both const and non-const flavors of
418  // offset_to_instance().
419  template <typename InstanceType, typename Fragment>
420  inline InstanceType & fragment::offset_to_instance_impl (Fragment && f,
421  std::uint64_t offset) noexcept {
422  return *reinterpret_cast<InstanceType *> (
423  reinterpret_cast<typename inherit_const<decltype (f), std::uint8_t>::type *> (&f) +
424  offset);
425  }
426 
427  // check range is sorted
428  // ~~~~~~~~~~~~~~~~~~~~~
429  template <typename Iterator>
430  void fragment::check_range_is_sorted (Iterator first, Iterator last) {
431  (void) first;
432  (void) last;
433 #ifndef NDEBUG
434  using value_type = typename std::iterator_traits<Iterator>::value_type;
435  PSTORE_ASSERT (
436  std::is_sorted (first, last, [] (value_type const & a, value_type const & b) {
437  section_kind const akind = a.kind ();
438  section_kind const bkind = b.kind ();
439  using utype = std::underlying_type<section_kind>::type;
440  return static_cast<utype> (akind) < static_cast<utype> (bkind);
441  }));
442 #endif
443  }
444 
445  // size bytes [static]
446  // ~~~~~~~~~~
447  template <typename Iterator>
448  std::size_t fragment::size_bytes (Iterator first, Iterator last) {
449  fragment::check_range_is_sorted (first, last);
450 
451  auto const num_contents = std::distance (first, last);
452  PSTORE_ASSERT (num_contents >= 0);
453  auto const unum_contents =
454  static_cast<typename std::make_unsigned<decltype (num_contents)>::type> (
455  num_contents);
456 
457  // Space needed by the signature and section offset array.
458  std::size_t size_bytes =
459  offsetof (fragment, arr_) + decltype (fragment::arr_)::size_bytes (unum_contents);
460  // Now the storage for each of the contents
461  std::for_each (first, last, [&size_bytes] (section_creation_dispatcher const & c) {
462  size_bytes = c.aligned (size_bytes);
463  size_bytes += c.size_bytes ();
464  });
465  return size_bytes;
466  }
467 
468 
470  unsigned section_align (fragment const & fragment, section_kind kind);
471 
474  std::size_t section_size (fragment const & fragment, section_kind kind);
475 
477  container<internal_fixup> section_ifixups (fragment const & fragment, section_kind kind);
478 
480  container<external_fixup> section_xfixups (fragment const & fragment, section_kind kind);
481 
483  container<std::uint8_t> section_value (fragment const & fragment, section_kind kind);
484  } // end namespace repo
485 } // end namespace pstore
486 
487 #endif // PSTORE_MCREPO_FRAGMENT_HPP
Contains declarations for the "linked-definition" section type.
bool has_section(section_kind const kind) const noexcept
Returns true if the fragment contains a section of the kind given by kind, false otherwise.
Definition: fragment.hpp:152
auto atp() noexcept -> typename enum_to_section< Key >::type *
Returns a pointer to the section data given the section kind or nullptr if the section is not present...
Definition: fragment.hpp:181
An extent is a contiguous area of storage reserved for a data BLOB, represented as a range...
Definition: address.hpp:441
auto at() const noexcept -> typename enum_to_section< Key >::type const &
Returns a const reference to the section data given the section kind.
Definition: fragment.hpp:162
Definition: address.hpp:81
A simple wrapper around the elements of one of the three arrays that make up a section.
Definition: section.hpp:152
Definition: debug_line_section.hpp:38
member_array const & members() const noexcept
Returns the array of section offsets.
Definition: fragment.hpp:190
auto at() noexcept -> typename enum_to_section< Key >::type &
Returns a reference to the section data given the section kind.
Definition: fragment.hpp:168
transaction< transaction_lock > begin(database &db, transaction_lock &lock)
Creates a new transaction. Every operation performed on a transaction instance can be potentially und...
Definition: transaction.hpp:311
Definition: fragment.hpp:107
Definition: transaction.hpp:191
Definition: address.hpp:231
Definition: generic_section.hpp:177
static constexpr std::size_t size_bytes(std::size_t members) noexcept
Returns the number of bytes of storage that are required for an instance of section_sparray<T> where ...
Definition: section_sparray.hpp:280
Definition: print.cpp:18
Definition: bss_section.hpp:31
Maps from the section kind enumeration to the type that is used to represent a section of that kind...
Definition: fragment.hpp:83
std::uint64_t size
The size of the data associated with this extent.
Definition: address.hpp:461
Represents the definitions linked to a fragment.
Definition: linked_definitions_section.hpp:62
An iterator adaptor which produces a value_type of &#39;section_kind const&#39; from values deferenced from t...
Definition: fragment.hpp:33
Definition: storage.hpp:58
Definition: nonpod2.cpp:40
Definition: database.hpp:40
A wrapper for sparse_array<> which accepts indices of type section_kind.
Provides a member typedef inherit_const::type, which is defined as R const if T is a const type and R...
Definition: inherit_const.hpp:108
std::size_t size() const noexcept
Return the number of sections in the fragment.
Definition: fragment.hpp:187
static pstore::extent< fragment > alloc(Transaction &transaction, Iterator first, Iterator last)
Prepares an instance of a fragment with the collection of sections defined by the iterator range [fir...
std::size_t size_bytes() const
Returns the number of bytes of storage that are accupied by this fragment.
Definition: fragment.cpp:164
virtual std::size_t size_bytes() const =0
Returns the number of bytes of storage that are required for an instance of the section data...
virtual std::uint8_t * write(std::uint8_t *out) const =0
Copies the section instance data to the memory starting at out.
A section creation dispatcher is used to instantiate and construct each of a fragment&#39;s sections in p...
Definition: section.hpp:91
std::size_t aligned(IntType a) const
Definition: section.hpp:108
static std::shared_ptr< fragment const > load(database const &db, extent< fragment > const &location)
Provides a pointer to an individual fragment instance given a database and a extent describing its ad...
Definition: fragment.cpp:94
typename std::conditional< std::is_const< typename std::remove_reference< T >::type >::value, RC, R >::type type
If T is const, R const otherwise R.
Definition: inherit_const.hpp:112
auto atp() const noexcept -> typename enum_to_section< Key >::type const *
Returns a pointer to the section data given the section kind or nullptr if the section is not present...
Definition: fragment.hpp:175
Declares the type used to record debug_line sections in a fragment.
The database transaction class.
Definition: transaction.hpp:43