pstore2
small_vector.hpp
Go to the documentation of this file.
1 //===- include/pstore/adt/small_vector.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 //===----------------------------------------------------------------------===//
19 
20 #ifndef PSTORE_ADT_SMALL_VECTOR_HPP
21 #define PSTORE_ADT_SMALL_VECTOR_HPP
22 
23 #include <array>
24 #include <cstddef>
25 #include <initializer_list>
26 #include <vector>
27 
31 
32 namespace pstore {
33 
38  template <typename ElementType, std::size_t BodyElements = 256>
39  class small_vector {
40  public:
41  using value_type = ElementType;
42 
43  using reference = value_type &;
44  using const_reference = value_type const &;
45  using pointer = value_type *;
46  using const_pointer = value_type const *;
47 
48  using size_type = std::size_t;
49  using difference_type = std::ptrdiff_t;
50 
53  using reverse_iterator = std::reverse_iterator<iterator>;
54  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
55 
57  small_vector () noexcept;
59  explicit small_vector (std::size_t required_elements);
61  small_vector (std::initializer_list<ElementType> init);
62  small_vector (small_vector && other) noexcept;
63  small_vector (small_vector const & rhs);
64 
65  ~small_vector () noexcept = default;
66 
67  small_vector & operator= (small_vector && other) noexcept;
68  small_vector & operator= (small_vector const & other);
69 
72  ElementType const * data () const noexcept { return buffer_; }
73  ElementType * data () noexcept { return buffer_; }
74 
75  ElementType const & operator[] (std::size_t n) const noexcept { return index (*this, n); }
76  ElementType & operator[] (std::size_t n) noexcept { return index (*this, n); }
77 
78  ElementType & back () { return index (*this, size () - 1U); }
79 
81 
85  std::size_t size () const noexcept { return elements_; }
86  std::size_t size_bytes () const noexcept { return elements_ * sizeof (ElementType); }
87 
89  bool empty () const noexcept { return elements_ == 0; }
90 
93  std::size_t capacity () const noexcept {
94  return std::max (BodyElements, big_buffer_.capacity ());
95  }
96 
106  void reserve (std::size_t new_cap);
107 
116  void resize (std::size_t new_elements);
118 
122  const_iterator begin () const noexcept { return const_iterator{buffer_}; }
123  iterator begin () noexcept { return iterator{buffer_}; }
124  const_iterator cbegin () noexcept { return const_iterator{buffer_}; }
128  reverse_iterator rbegin () noexcept { return reverse_iterator{this->end ()}; }
129  const_reverse_iterator rbegin () const noexcept {
130  return const_reverse_iterator{this->end ()};
131  }
132  const_reverse_iterator rcbegin () noexcept { return const_reverse_iterator{this->cend ()}; }
133 
135  const_iterator end () const noexcept { return const_iterator{buffer_ + elements_}; }
136  iterator end () noexcept { return iterator{buffer_ + elements_}; }
137  const_iterator cend () noexcept { return const_iterator{buffer_ + elements_}; }
138  reverse_iterator rend () noexcept { return reverse_iterator{this->begin ()}; }
139  const_reverse_iterator rend () const noexcept {
140  return const_reverse_iterator{this->begin ()};
141  }
142  const_reverse_iterator rcend () noexcept { return const_reverse_iterator{this->cbegin ()}; }
144 
147 
152  void clear () noexcept;
153 
155  void push_back (ElementType const & v);
156  template <typename... Args>
157  void emplace_back (Args &&... args);
158 
159  template <typename InputIt>
160  void assign (InputIt first, InputIt last);
161 
162  void assign (std::initializer_list<ElementType> ilist) {
163  this->assign (std::begin (ilist), std::end (ilist));
164  }
165 
167  template <typename InputIt>
168  void append (InputIt first, InputIt last);
169  void append (std::initializer_list<ElementType> ilist) {
170  this->append (std::begin (ilist), std::end (ilist));
171  }
172 
174 
175  private:
177  template <typename SmallVector,
178  typename ResultType = typename inherit_const<SmallVector, ElementType>::type>
179  static ResultType & index (SmallVector && sm, std::size_t const n) noexcept {
180  PSTORE_ASSERT (n < sm.size ());
181  return sm.buffer_[n];
182  }
183 
186  std::size_t elements_ = 0;
187 
190  std::array<ElementType, BodyElements> small_buffer_{{}};
191 
194  std::vector<ElementType> big_buffer_;
195 
197  ElementType * buffer_ = nullptr;
198 
201  static constexpr bool is_small (std::size_t const elements) noexcept {
202  return elements <= BodyElements;
203  }
204 
205  void switch_to_big (std::size_t new_elements);
206  template <typename... Args>
207  void emplace_back_big (Args &&... args);
208  ElementType * set_buffer_ptr (std::size_t const required_elements) noexcept {
209  buffer_ = is_small (required_elements) ? small_buffer_.data () : big_buffer_.data ();
210  return buffer_;
211  }
212  };
213 
214  // (ctor)
215  // ~~~~~~
216  template <typename ElementType, std::size_t BodyElements>
217  small_vector<ElementType, BodyElements>::small_vector (std::size_t const required_elements)
218  : elements_{required_elements} {
219 
220  if (!is_small (elements_)) {
221  big_buffer_.resize (elements_);
222  }
223  this->set_buffer_ptr (elements_);
224  }
225 
226  template <typename ElementType, std::size_t BodyElements>
228  this->set_buffer_ptr (elements_);
229  }
230 
231  template <typename ElementType, std::size_t BodyElements>
233  : elements_ (std::move (other.elements_))
234  , small_buffer_ (std::move (other.small_buffer_))
235  , big_buffer_ (std::move (other.big_buffer_)) {
236 
237  this->set_buffer_ptr (elements_);
238  }
239 
240  template <typename ElementType, std::size_t BodyElements>
241  small_vector<ElementType, BodyElements>::small_vector (std::initializer_list<ElementType> init)
242  : small_vector () {
243  this->reserve (init.size ());
244  std::copy (std::begin (init), std::end (init), std::back_inserter (*this));
245  }
246 
247  template <typename ElementType, std::size_t BodyElements>
249  : small_vector () {
250  this->reserve (rhs.size ());
251  std::copy (std::begin (rhs), std::end (rhs), std::back_inserter (*this));
252  }
253 
254  // operator=
255  // ~~~~~~~~~
256  template <typename ElementType, std::size_t BodyElements>
258  -> small_vector & {
259  elements_ = std::move (other.elements_);
260  small_buffer_ = std::move (other.small_buffer_);
261  big_buffer_ = std::move (other.big_buffer_);
262 
263  this->set_buffer_ptr (elements_);
264  return *this;
265  }
266 
267  template <typename ElementType, std::size_t BodyElements>
269  -> small_vector & {
270  if (this != &other) {
271  this->clear ();
272  this->resize (other.size ());
273  std::copy (std::begin (other), std::end (other), std::begin (*this));
274  }
275  return *this;
276  }
277 
278  // reserve
279  // ~~~~~~~
280  template <typename ElementType, std::size_t BodyElements>
282  if (new_cap > capacity ()) {
283  big_buffer_.reserve (new_cap);
284  }
285  }
286 
287  // resize
288  // ~~~~~~
289  template <typename ElementType, std::size_t BodyElements>
290  void small_vector<ElementType, BodyElements>::resize (std::size_t new_elements) {
291  if (new_elements != elements_) {
292  bool const is_small_before = is_small (elements_);
293  bool const is_small_after = is_small (new_elements);
294 
295  big_buffer_.resize (is_small_after ? 0 : new_elements);
296 
297  // Update the buffer pointer and preserve the contents if we've switched from small to
298  // larger buffer or vice-versa.
299  auto * const old_buffer = buffer_;
300  auto * const new_buffer = this->set_buffer_ptr (new_elements);
301 
302  if (is_small_before != is_small_after) {
303  std::copy (old_buffer, old_buffer + elements_, new_buffer);
304  }
305 
306  elements_ = new_elements;
307  }
308  }
309 
310  // clear
311  // ~~~~~
312  template <typename ElementType, std::size_t BodyElements>
314  big_buffer_.clear ();
315  elements_ = 0;
316  this->set_buffer_ptr (elements_);
317  }
318 
319  // push_back
320  // ~~~~~~~~~
321  template <typename ElementType, std::size_t BodyElements>
322  inline void small_vector<ElementType, BodyElements>::push_back (ElementType const & v) {
323  auto const new_elements = elements_ + 1U;
324  if (is_small (new_elements)) {
325  small_buffer_[elements_] = v;
326  } else {
327  if (is_small (elements_)) {
328  this->switch_to_big (new_elements);
329  }
330  big_buffer_.push_back (v);
331  this->set_buffer_ptr (new_elements);
332  }
333  elements_ = new_elements;
334  }
335 
336  // emplace_back
337  // ~~~~~~~~~~~~
338  template <typename ElementType, std::size_t BodyElements>
339  template <typename... Args>
340  inline void small_vector<ElementType, BodyElements>::emplace_back (Args &&... args) {
341  auto const new_elements = elements_ + 1U;
342  if (!is_small (new_elements)) {
343  return emplace_back_big (std::forward<Args> (args)...);
344  }
345  small_buffer_[elements_] = ElementType (std::forward<Args> (args)...);
346  elements_ = new_elements;
347  }
348 
349  // emplace_back_big
350  // ~~~~~~~~~~~~~~~~
351  // The "slow" path for emplace_back which inserts into the "big" vector.
352  template <typename ElementType, std::size_t BodyElements>
353  template <typename... Args>
355  auto const new_elements = elements_ + 1U;
356  if (is_small (elements_)) {
357  this->switch_to_big (new_elements);
358  }
359  big_buffer_.emplace_back (std::forward<Args> (args)...);
360  this->set_buffer_ptr (new_elements);
361  elements_ = new_elements;
362  }
363 
364  // switch_to_big
365  // ~~~~~~~~~~~~~
366  template <typename ElementType, std::size_t BodyElements>
367  void small_vector<ElementType, BodyElements>::switch_to_big (std::size_t new_elements) {
368  big_buffer_.clear ();
369  big_buffer_.reserve (new_elements);
370  std::copy (buffer_, buffer_ + elements_, std::back_inserter (big_buffer_));
371  this->set_buffer_ptr (new_elements);
372  }
373 
374  // assign
375  // ~~~~~~
376  template <typename ElementType, std::size_t BodyElements>
377  template <typename InputIt>
378  void small_vector<ElementType, BodyElements>::assign (InputIt first, InputIt last) {
379  this->clear ();
380  for (; first != last; ++first) {
381  this->push_back (*first);
382  }
383  }
384 
385  // append
386  // ~~~~~~
387  template <typename ElementType, std::size_t BodyElements>
388  template <typename Iterator>
389  void small_vector<ElementType, BodyElements>::append (Iterator first, Iterator last) {
390  for (; first != last; ++first) {
391  this->push_back (*first);
392  }
393  }
394 
395  template <typename ElementType, std::size_t LhsBodyElements, std::size_t RhsBodyElements>
396  bool operator== (small_vector<ElementType, LhsBodyElements> const & lhs,
398  return std::equal (std::begin (lhs), std::end (lhs), std::begin (rhs), std::end (rhs));
399  }
400  template <typename ElementType, std::size_t LhsBodyElements, std::size_t RhsBodyElements>
401  bool operator!= (small_vector<ElementType, LhsBodyElements> const & lhs,
403  return !operator== (lhs, rhs);
404  }
405 
406 } // end namespace pstore
407 #endif // PSTORE_ADT_SMALL_VECTOR_HPP
void clear() noexcept
Removes all elements from the container.
Definition: small_vector.hpp:313
reverse_iterator rbegin() noexcept
Returns a reverse iterator to the first element of the reversed container.
Definition: small_vector.hpp:128
void resize(std::size_t new_elements)
Resizes the container so that it is large enough for accommodate the given number of elements...
Definition: small_vector.hpp:290
A class which provides a vector-like interface to a small, normally stack allocated, buffer which may, if necessary, be resized.
Definition: small_vector.hpp:39
void push_back(ElementType const &v)
Adds an element to the end.
Definition: small_vector.hpp:322
const_iterator end() const noexcept
Returns an iterator to the end of the container.
Definition: small_vector.hpp:135
A utility template intended to simplify the writing of the const- and non-const variants of member fu...
Definition: pointer_based_iterator.hpp:53
Definition: nonpod2.cpp:40
void append(InputIt first, InputIt last)
Add the specified range to the end of the small vector.
void reserve(std::size_t new_cap)
Increase the capacity of the vector to a value that&#39;s greater or equal to new_cap.
Definition: small_vector.hpp:281
An implementation of the standard assert() macro with the exception that it will, on failure...
bool empty() const noexcept
Checks whether the container is empty.
Definition: small_vector.hpp:89
small_vector() noexcept
Constructs the buffer with an initial size of 0.
Definition: small_vector.hpp:227
Provides pointer_based_iterator<> an iterator wrapper for pointers.
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
std::size_t capacity() const noexcept
Returns the number of elements that can be held in currently allocated storage.
Definition: small_vector.hpp:93