proxygen
FBVector.h
Go to the documentation of this file.
1 /*
2  * Copyright 2011-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * Nicholas Ormrod (njormrod)
19  * Andrei Alexandrescu (aalexandre)
20  *
21  * FBVector is Facebook's drop-in implementation of std::vector. It has special
22  * optimizations for use with relocatable types and jemalloc.
23  */
24 
25 #pragma once
26 
27 //=============================================================================
28 // headers
29 
30 #include <algorithm>
31 #include <cassert>
32 #include <iterator>
33 #include <memory>
34 #include <stdexcept>
35 #include <type_traits>
36 #include <utility>
37 
38 #include <folly/FormatTraits.h>
39 #include <folly/Likely.h>
40 #include <folly/Traits.h>
41 #include <folly/lang/Exception.h>
42 #include <folly/memory/Malloc.h>
43 
44 //=============================================================================
45 // forward declaration
46 
47 namespace folly {
48 template <class T, class Allocator = std::allocator<T>>
49 class fbvector;
50 } // namespace folly
51 
52 //=============================================================================
53 // unrolling
54 
55 #define FOLLY_FBV_UNROLL_PTR(first, last, OP) \
56  do { \
57  for (; (last) - (first) >= 4; (first) += 4) { \
58  OP(((first) + 0)); \
59  OP(((first) + 1)); \
60  OP(((first) + 2)); \
61  OP(((first) + 3)); \
62  } \
63  for (; (first) != (last); ++(first)) \
64  OP((first)); \
65  } while (0);
66 
67 //=============================================================================
69 // //
70 // fbvector class //
71 // //
73 
74 namespace folly {
75 
76 template <class T, class Allocator>
77 class fbvector {
78  //===========================================================================
79  //---------------------------------------------------------------------------
80  // implementation
81  private:
82  typedef std::allocator_traits<Allocator> A;
83 
84  struct Impl : public Allocator {
85  // typedefs
86  typedef typename A::pointer pointer;
87  typedef typename A::size_type size_type;
88 
89  // data
90  pointer b_, e_, z_;
91 
92  // constructors
93  Impl() : Allocator(), b_(nullptr), e_(nullptr), z_(nullptr) {}
94  /* implicit */ Impl(const Allocator& alloc)
95  : Allocator(alloc), b_(nullptr), e_(nullptr), z_(nullptr) {}
96  /* implicit */ Impl(Allocator&& alloc)
97  : Allocator(std::move(alloc)), b_(nullptr), e_(nullptr), z_(nullptr) {}
98 
99  /* implicit */ Impl(size_type n, const Allocator& alloc = Allocator())
100  : Allocator(alloc) {
101  init(n);
102  }
103 
104  Impl(Impl&& other) noexcept
105  : Allocator(std::move(other)),
106  b_(other.b_),
107  e_(other.e_),
108  z_(other.z_) {
109  other.b_ = other.e_ = other.z_ = nullptr;
110  }
111 
112  // destructor
113  ~Impl() {
114  destroy();
115  }
116 
117  // allocation
118  // note that 'allocate' and 'deallocate' are inherited from Allocator
119  T* D_allocate(size_type n) {
121  return static_cast<T*>(checkedMalloc(n * sizeof(T)));
122  } else {
123  return std::allocator_traits<Allocator>::allocate(*this, n);
124  }
125  }
126 
127  void D_deallocate(T* p, size_type n) noexcept {
129  free(p);
130  } else {
131  std::allocator_traits<Allocator>::deallocate(*this, p, n);
132  }
133  }
134 
135  // helpers
136  void swapData(Impl& other) {
137  std::swap(b_, other.b_);
138  std::swap(e_, other.e_);
139  std::swap(z_, other.z_);
140  }
141 
142  // data ops
143  inline void destroy() noexcept {
144  if (b_) {
145  // THIS DISPATCH CODE IS DUPLICATED IN fbvector::D_destroy_range_a.
146  // It has been inlined here for speed. It calls the static fbvector
147  // methods to perform the actual destruction.
149  S_destroy_range(b_, e_);
150  } else {
151  S_destroy_range_a(*this, b_, e_);
152  }
153 
154  D_deallocate(b_, size_type(z_ - b_));
155  }
156  }
157 
158  void init(size_type n) {
159  if (UNLIKELY(n == 0)) {
160  b_ = e_ = z_ = nullptr;
161  } else {
162  size_type sz = folly::goodMallocSize(n * sizeof(T)) / sizeof(T);
163  b_ = D_allocate(sz);
164  e_ = b_;
165  z_ = b_ + sz;
166  }
167  }
168 
169  void set(pointer newB, size_type newSize, size_type newCap) {
170  z_ = newB + newCap;
171  e_ = newB + newSize;
172  b_ = newB;
173  }
174 
175  void reset(size_type newCap) {
176  destroy();
177  try {
178  init(newCap);
179  } catch (...) {
180  init(0);
181  throw;
182  }
183  }
184  void reset() { // same as reset(0)
185  destroy();
186  b_ = e_ = z_ = nullptr;
187  }
188  } impl_;
189 
190  static void swap(Impl& a, Impl& b) {
191  using std::swap;
193  swap(static_cast<Allocator&>(a), static_cast<Allocator&>(b));
194  }
195  a.swapData(b);
196  }
197 
198  //===========================================================================
199  //---------------------------------------------------------------------------
200  // types and constants
201  public:
202  typedef T value_type;
203  typedef value_type& reference;
204  typedef const value_type& const_reference;
205  typedef T* iterator;
206  typedef const T* const_iterator;
207  typedef size_t size_type;
209  typedef Allocator allocator_type;
210  typedef typename A::pointer pointer;
211  typedef typename A::const_pointer const_pointer;
212  typedef std::reverse_iterator<iterator> reverse_iterator;
213  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
214 
215  private:
216  typedef bool_constant<
218  sizeof(T) <= 16 // don't force large structures to be passed by value
219  >
220  should_pass_by_value;
221  typedef
223  VT;
224  typedef
226 
228  usingStdAllocator;
229  typedef bool_constant<
232  moveIsSwap;
233 
234  //===========================================================================
235  //---------------------------------------------------------------------------
236  // allocator helpers
237  private:
238  //---------------------------------------------------------------------------
239  // allocate
240 
241  T* M_allocate(size_type n) {
242  return impl_.D_allocate(n);
243  }
244 
245  //---------------------------------------------------------------------------
246  // deallocate
247 
248  void M_deallocate(T* p, size_type n) noexcept {
249  impl_.D_deallocate(p, n);
250  }
251 
252  //---------------------------------------------------------------------------
253  // construct
254 
255  // GCC is very sensitive to the exact way that construct is called. For
256  // that reason there are several different specializations of construct.
257 
258  template <typename U, typename... Args>
259  void M_construct(U* p, Args&&... args) {
261  new (p) U(std::forward<Args>(args)...);
262  } else {
263  std::allocator_traits<Allocator>::construct(
264  impl_, p, std::forward<Args>(args)...);
265  }
266  }
267 
268  template <typename U, typename... Args>
269  static void S_construct(U* p, Args&&... args) {
270  new (p) U(std::forward<Args>(args)...);
271  }
272 
273  template <typename U, typename... Args>
274  static void S_construct_a(Allocator& a, U* p, Args&&... args) {
275  std::allocator_traits<Allocator>::construct(
276  a, p, std::forward<Args>(args)...);
277  }
278 
279  // scalar optimization
280  // TODO we can expand this optimization to: default copyable and assignable
281  template <
282  typename U,
283  typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type>
284  void M_construct(U* p, U arg) {
286  *p = arg;
287  } else {
288  std::allocator_traits<Allocator>::construct(impl_, p, arg);
289  }
290  }
291 
292  template <
293  typename U,
294  typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type>
295  static void S_construct(U* p, U arg) {
296  *p = arg;
297  }
298 
299  template <
300  typename U,
301  typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type>
302  static void S_construct_a(Allocator& a, U* p, U arg) {
303  std::allocator_traits<Allocator>::construct(a, p, arg);
304  }
305 
306  // const& optimization
307  template <
308  typename U,
309  typename Enable =
311  void M_construct(U* p, const U& value) {
313  new (p) U(value);
314  } else {
315  std::allocator_traits<Allocator>::construct(impl_, p, value);
316  }
317  }
318 
319  template <
320  typename U,
321  typename Enable =
323  static void S_construct(U* p, const U& value) {
324  new (p) U(value);
325  }
326 
327  template <
328  typename U,
329  typename Enable =
331  static void S_construct_a(Allocator& a, U* p, const U& value) {
332  std::allocator_traits<Allocator>::construct(a, p, value);
333  }
334 
335  //---------------------------------------------------------------------------
336  // destroy
337 
338  void M_destroy(T* p) noexcept {
341  p->~T();
342  }
343  } else {
345  }
346  }
347 
348  //===========================================================================
349  //---------------------------------------------------------------------------
350  // algorithmic helpers
351  private:
352  //---------------------------------------------------------------------------
353  // destroy_range
354 
355  // wrappers
357  D_destroy_range_a(pos, impl_.e_);
358  impl_.e_ = pos;
359  }
360 
361  // dispatch
362  // THIS DISPATCH CODE IS DUPLICATED IN IMPL. SEE IMPL FOR DETAILS.
365  S_destroy_range(first, last);
366  } else {
367  S_destroy_range_a(impl_, first, last);
368  }
369  }
370 
371  // allocator
372  static void S_destroy_range_a(Allocator& a, T* first, T* last) noexcept {
373  for (; first != last; ++first) {
375  }
376  }
377 
378  // optimized
379  static void S_destroy_range(T* first, T* last) noexcept {
381 #define FOLLY_FBV_OP(p) (p)->~T()
382  // EXPERIMENTAL DATA on fbvector<vector<int>> (where each vector<int> has
383  // size 0).
384  // The unrolled version seems to work faster for small to medium sized
385  // fbvectors. It gets a 10% speedup on fbvectors of size 1024, 64, and
386  // 16.
387  // The simple loop version seems to work faster for large fbvectors. The
388  // unrolled version is about 6% slower on fbvectors on size 16384.
389  // The two methods seem tied for very large fbvectors. The unrolled
390  // version is about 0.5% slower on size 262144.
391 
392  // for (; first != last; ++first) first->~T();
394 #undef FOLLY_FBV_OP
395  }
396  }
397 
398  //---------------------------------------------------------------------------
399  // uninitialized_fill_n
400 
401  // wrappers
402  void M_uninitialized_fill_n_e(size_type sz) {
404  impl_.e_ += sz;
405  }
407  void M_uninitialized_fill_n_e(size_type sz, VT value) {
408  D_uninitialized_fill_n_a(impl_.e_, sz, value);
409  impl_.e_ += sz;
410  }
411 
412  // dispatch
413  void D_uninitialized_fill_n_a(T* dest, size_type sz) {
415  S_uninitialized_fill_n(dest, sz);
416  } else {
417  S_uninitialized_fill_n_a(impl_, dest, sz);
418  }
419  }
421  void D_uninitialized_fill_n_a(T* dest, size_type sz, VT value) {
423  S_uninitialized_fill_n(dest, sz, value);
424  } else {
425  S_uninitialized_fill_n_a(impl_, dest, sz, value);
426  }
427  }
428 
429  // allocator
430  template <typename... Args>
431  static void S_uninitialized_fill_n_a(
432  Allocator& a,
433  T* dest,
434  size_type sz,
435  Args&&... args) {
436  auto b = dest;
437  auto e = dest + sz;
438  try {
439  for (; b != e; ++b) {
440  std::allocator_traits<Allocator>::construct(
441  a, b, std::forward<Args>(args)...);
442  }
443  } catch (...) {
444  S_destroy_range_a(a, dest, b);
445  throw;
446  }
447  }
448 
449  // optimized
450  static void S_uninitialized_fill_n(T* dest, size_type n) {
452  if (LIKELY(n != 0)) {
453  std::memset(dest, 0, sizeof(T) * n);
454  }
455  } else {
456  auto b = dest;
457  auto e = dest + n;
458  try {
459  for (; b != e; ++b) {
460  S_construct(b);
461  }
462  } catch (...) {
463  --b;
464  for (; b >= dest; --b) {
465  b->~T();
466  }
467  throw;
468  }
469  }
470  }
472  static void S_uninitialized_fill_n(T* dest, size_type n, const T& value) {
473  auto b = dest;
474  auto e = dest + n;
475  try {
476  for (; b != e; ++b) {
477  S_construct(b, value);
478  }
479  } catch (...) {
480  S_destroy_range(dest, b);
481  throw;
482  }
483  }
484 
485  //---------------------------------------------------------------------------
486  // uninitialized_copy
487 
488  // it is possible to add an optimization for the case where
489  // It = move(T*) and IsRelocatable<T> and Is0Initiailizable<T>
490 
491  // wrappers
492  template <typename It>
493  void M_uninitialized_copy_e(It first, It last) {
494  D_uninitialized_copy_a(impl_.e_, first, last);
495  impl_.e_ += std::distance(first, last);
496  }
497 
498  template <typename It>
499  void M_uninitialized_move_e(It first, It last) {
500  D_uninitialized_move_a(impl_.e_, first, last);
501  impl_.e_ += std::distance(first, last);
502  }
503 
504  // dispatch
505  template <typename It>
506  void D_uninitialized_copy_a(T* dest, It first, It last) {
509  S_uninitialized_copy_bits(dest, first, last);
510  } else {
511  S_uninitialized_copy(dest, first, last);
512  }
513  } else {
514  S_uninitialized_copy_a(impl_, dest, first, last);
515  }
516  }
517 
518  template <typename It>
519  void D_uninitialized_move_a(T* dest, It first, It last) {
521  dest, std::make_move_iterator(first), std::make_move_iterator(last));
522  }
523 
524  // allocator
525  template <typename It>
526  static void S_uninitialized_copy_a(Allocator& a, T* dest, It first, It last) {
527  auto b = dest;
528  try {
529  for (; first != last; ++first, ++b) {
530  std::allocator_traits<Allocator>::construct(a, b, *first);
531  }
532  } catch (...) {
533  S_destroy_range_a(a, dest, b);
534  throw;
535  }
536  }
537 
538  // optimized
539  template <typename It>
540  static void S_uninitialized_copy(T* dest, It first, It last) {
541  auto b = dest;
542  try {
543  for (; first != last; ++first, ++b) {
544  S_construct(b, *first);
545  }
546  } catch (...) {
547  S_destroy_range(dest, b);
548  throw;
549  }
550  }
551 
552  static void
553  S_uninitialized_copy_bits(T* dest, const T* first, const T* last) {
554  if (last != first) {
555  std::memcpy((void*)dest, (void*)first, (last - first) * sizeof(T));
556  }
557  }
559  static void S_uninitialized_copy_bits(
560  T* dest,
561  std::move_iterator<T*> first,
562  std::move_iterator<T*> last) {
563  T* bFirst = first.base();
564  T* bLast = last.base();
565  if (bLast != bFirst) {
566  std::memcpy((void*)dest, (void*)bFirst, (bLast - bFirst) * sizeof(T));
567  }
568  }
569 
570  template <typename It>
571  static void S_uninitialized_copy_bits(T* dest, It first, It last) {
572  S_uninitialized_copy(dest, first, last);
573  }
574 
575  //---------------------------------------------------------------------------
576  // copy_n
577 
578  // This function is "unsafe": it assumes that the iterator can be advanced at
579  // least n times. However, as a private function, that unsafety is managed
580  // wholly by fbvector itself.
581 
582  template <typename It>
583  static It S_copy_n(T* dest, It first, size_type n) {
584  auto e = dest + n;
585  for (; dest != e; ++dest, ++first) {
586  *dest = *first;
587  }
588  return first;
589  }
591  static const T* S_copy_n(T* dest, const T* first, size_type n) {
593  std::memcpy((void*)dest, (void*)first, n * sizeof(T));
594  return first + n;
595  } else {
596  return S_copy_n<const T*>(dest, first, n);
597  }
598  }
599 
600  static std::move_iterator<T*>
601  S_copy_n(T* dest, std::move_iterator<T*> mIt, size_type n) {
603  T* first = mIt.base();
604  std::memcpy((void*)dest, (void*)first, n * sizeof(T));
605  return std::make_move_iterator(first + n);
606  } else {
607  return S_copy_n<std::move_iterator<T*>>(dest, mIt, n);
608  }
609  }
610 
611  //===========================================================================
612  //---------------------------------------------------------------------------
613  // relocation helpers
614  private:
615  // Relocation is divided into three parts:
616  //
617  // 1: relocate_move
618  // Performs the actual movement of data from point a to point b.
619  //
620  // 2: relocate_done
621  // Destroys the old data.
622  //
623  // 3: relocate_undo
624  // Destoys the new data and restores the old data.
625  //
626  // The three steps are used because there may be an exception after part 1
627  // has completed. If that is the case, then relocate_undo can nullify the
628  // initial move. Otherwise, relocate_done performs the last bit of tidying
629  // up.
630  //
631  // The relocation trio may use either memcpy, move, or copy. It is decided
632  // by the following case statement:
633  //
634  // IsRelocatable && usingStdAllocator -> memcpy
635  // has_nothrow_move && usingStdAllocator -> move
636  // cannot copy -> move
637  // default -> copy
638  //
639  // If the class is non-copyable then it must be movable. However, if the
640  // move constructor is not noexcept, i.e. an error could be thrown, then
641  // relocate_undo will be unable to restore the old data, for fear of a
642  // second exception being thrown. This is a known and unavoidable
643  // deficiency. In lieu of a strong exception guarantee, relocate_undo does
644  // the next best thing: it provides a weak exception guarantee by
645  // destorying the new data, but leaving the old data in an indeterminate
646  // state. Note that that indeterminate state will be valid, since the
647  // old data has not been destroyed; it has merely been the source of a
648  // move, which is required to leave the source in a valid state.
649 
650  // wrappers
651  void M_relocate(T* newB) {
652  relocate_move(newB, impl_.b_, impl_.e_);
653  relocate_done(newB, impl_.b_, impl_.e_);
654  }
655 
656  // dispatch type trait
657  typedef bool_constant<
660 
661  typedef bool_constant<
666 
667  // move
668  void relocate_move(T* dest, T* first, T* last) {
669  relocate_move_or_memcpy(dest, first, last, relocate_use_memcpy());
670  }
673  if (first != nullptr) {
674  std::memcpy((void*)dest, (void*)first, (last - first) * sizeof(T));
675  }
676  }
679  relocate_move_or_copy(dest, first, last, relocate_use_move());
680  }
682  void relocate_move_or_copy(T* dest, T* first, T* last, std::true_type) {
683  D_uninitialized_move_a(dest, first, last);
684  }
686  void relocate_move_or_copy(T* dest, T* first, T* last, std::false_type) {
687  D_uninitialized_copy_a(dest, first, last);
688  }
689 
690  // done
691  void relocate_done(T* /*dest*/, T* first, T* last) noexcept {
693  // used memcpy; data has been relocated, do not call destructor
694  } else {
695  D_destroy_range_a(first, last);
696  }
697  }
698 
699  // undo
700  void relocate_undo(T* dest, T* first, T* last) noexcept {
702  // used memcpy, old data is still valid, nothing to do
703  } else if (
706  // noexcept move everything back, aka relocate_move
707  relocate_move(first, dest, dest + (last - first));
709  // weak guarantee
710  D_destroy_range_a(dest, dest + (last - first));
711  } else {
712  // used copy, old data is still valid
713  D_destroy_range_a(dest, dest + (last - first));
714  }
715  }
716 
717  //===========================================================================
718  //---------------------------------------------------------------------------
719  // construct/copy/destroy
720  public:
721  fbvector() = default;
723  explicit fbvector(const Allocator& a) : impl_(a) {}
725  explicit fbvector(size_type n, const Allocator& a = Allocator())
726  : impl_(n, a) {
728  }
730  fbvector(size_type n, VT value, const Allocator& a = Allocator())
731  : impl_(n, a) {
732  M_uninitialized_fill_n_e(n, value);
733  }
734 
735  template <
736  class It,
737  class Category = typename std::iterator_traits<It>::iterator_category>
738  fbvector(It first, It last, const Allocator& a = Allocator())
739  : fbvector(first, last, a, Category()) {}
741  fbvector(const fbvector& other)
742  : impl_(
743  other.size(),
744  A::select_on_container_copy_construction(other.impl_)) {
745  M_uninitialized_copy_e(other.begin(), other.end());
746  }
748  fbvector(fbvector&& other) noexcept : impl_(std::move(other.impl_)) {}
750  fbvector(const fbvector& other, const Allocator& a)
751  : fbvector(other.begin(), other.end(), a) {}
753  /* may throw */ fbvector(fbvector&& other, const Allocator& a) : impl_(a) {
754  if (impl_ == other.impl_) {
755  impl_.swapData(other.impl_);
756  } else {
757  impl_.init(other.size());
758  M_uninitialized_move_e(other.begin(), other.end());
759  }
760  }
762  fbvector(std::initializer_list<T> il, const Allocator& a = Allocator())
763  : fbvector(il.begin(), il.end(), a) {}
764 
765  ~fbvector() = default; // the cleanup occurs in impl_
767  fbvector& operator=(const fbvector& other) {
768  if (UNLIKELY(this == &other)) {
769  return *this;
770  }
771 
774  if (impl_ != other.impl_) {
775  // can't use other's different allocator to clean up self
776  impl_.reset();
777  }
778  (Allocator&)impl_ = (Allocator&)other.impl_;
779  }
780 
781  assign(other.begin(), other.end());
782  return *this;
783  }
785  fbvector& operator=(fbvector&& other) {
786  if (UNLIKELY(this == &other)) {
787  return *this;
788  }
789  moveFrom(std::move(other), moveIsSwap());
790  return *this;
791  }
793  fbvector& operator=(std::initializer_list<T> il) {
794  assign(il.begin(), il.end());
795  return *this;
796  }
797 
798  template <
799  class It,
800  class Category = typename std::iterator_traits<It>::iterator_category>
801  void assign(It first, It last) {
802  assign(first, last, Category());
803  }
805  void assign(size_type n, VT value) {
806  if (n > capacity()) {
807  // Not enough space. Do not reserve in place, since we will
808  // discard the old values anyways.
809  if (dataIsInternalAndNotVT(value)) {
810  T copy(std::move(value));
811  impl_.reset(n);
812  M_uninitialized_fill_n_e(n, copy);
813  } else {
814  impl_.reset(n);
815  M_uninitialized_fill_n_e(n, value);
816  }
817  } else if (n <= size()) {
818  auto newE = impl_.b_ + n;
819  std::fill(impl_.b_, newE, value);
820  M_destroy_range_e(newE);
821  } else {
822  std::fill(impl_.b_, impl_.e_, value);
823  M_uninitialized_fill_n_e(n - size(), value);
824  }
825  }
827  void assign(std::initializer_list<T> il) {
828  assign(il.begin(), il.end());
829  }
831  allocator_type get_allocator() const noexcept {
832  return impl_;
833  }
834 
835  private:
836  // contract dispatch for iterator types fbvector(It first, It last)
837  template <class ForwardIterator>
838  fbvector(
839  ForwardIterator first,
840  ForwardIterator last,
841  const Allocator& a,
842  std::forward_iterator_tag)
843  : impl_(size_type(std::distance(first, last)), a) {
844  M_uninitialized_copy_e(first, last);
845  }
846 
847  template <class InputIterator>
848  fbvector(
849  InputIterator first,
850  InputIterator last,
851  const Allocator& a,
852  std::input_iterator_tag)
853  : impl_(a) {
854  for (; first != last; ++first) {
855  emplace_back(*first);
856  }
857  }
858 
859  // contract dispatch for allocator movement in operator=(fbvector&&)
860  void moveFrom(fbvector&& other, std::true_type) {
861  swap(impl_, other.impl_);
862  }
863  void moveFrom(fbvector&& other, std::false_type) {
864  if (impl_ == other.impl_) {
865  impl_.swapData(other.impl_);
866  } else {
867  impl_.reset(other.size());
868  M_uninitialized_move_e(other.begin(), other.end());
869  }
870  }
871 
872  // contract dispatch for iterator types in assign(It first, It last)
873  template <class ForwardIterator>
874  void assign(
875  ForwardIterator first,
876  ForwardIterator last,
877  std::forward_iterator_tag) {
878  const auto newSize = size_type(std::distance(first, last));
879  if (newSize > capacity()) {
880  impl_.reset(newSize);
881  M_uninitialized_copy_e(first, last);
882  } else if (newSize <= size()) {
883  auto newEnd = std::copy(first, last, impl_.b_);
884  M_destroy_range_e(newEnd);
885  } else {
886  auto mid = S_copy_n(impl_.b_, first, size());
887  M_uninitialized_copy_e<decltype(last)>(mid, last);
888  }
889  }
890 
891  template <class InputIterator>
892  void
893  assign(InputIterator first, InputIterator last, std::input_iterator_tag) {
894  auto p = impl_.b_;
895  for (; first != last && p != impl_.e_; ++first, ++p) {
896  *p = *first;
897  }
898  if (p != impl_.e_) {
900  } else {
901  for (; first != last; ++first) {
902  emplace_back(*first);
903  }
904  }
905  }
906 
907  // contract dispatch for aliasing under VT optimization
908  bool dataIsInternalAndNotVT(const T& t) {
910  return false;
911  }
912  return dataIsInternal(t);
913  }
914  bool dataIsInternal(const T& t) {
915  return UNLIKELY(
916  impl_.b_ <= std::addressof(t) && std::addressof(t) < impl_.e_);
917  }
918 
919  //===========================================================================
920  //---------------------------------------------------------------------------
921  // iterators
922  public:
923  iterator begin() noexcept {
924  return impl_.b_;
925  }
926  const_iterator begin() const noexcept {
927  return impl_.b_;
928  }
929  iterator end() noexcept {
930  return impl_.e_;
931  }
932  const_iterator end() const noexcept {
933  return impl_.e_;
934  }
935  reverse_iterator rbegin() noexcept {
936  return reverse_iterator(end());
937  }
938  const_reverse_iterator rbegin() const noexcept {
939  return const_reverse_iterator(end());
940  }
941  reverse_iterator rend() noexcept {
942  return reverse_iterator(begin());
943  }
944  const_reverse_iterator rend() const noexcept {
945  return const_reverse_iterator(begin());
946  }
948  const_iterator cbegin() const noexcept {
949  return impl_.b_;
950  }
951  const_iterator cend() const noexcept {
952  return impl_.e_;
953  }
954  const_reverse_iterator crbegin() const noexcept {
955  return const_reverse_iterator(end());
956  }
957  const_reverse_iterator crend() const noexcept {
958  return const_reverse_iterator(begin());
959  }
960 
961  //===========================================================================
962  //---------------------------------------------------------------------------
963  // capacity
964  public:
965  size_type size() const noexcept {
966  return size_type(impl_.e_ - impl_.b_);
967  }
969  size_type max_size() const noexcept {
970  // good luck gettin' there
971  return ~size_type(0);
972  }
974  void resize(size_type n) {
975  if (n <= size()) {
977  } else {
978  reserve(n);
980  }
981  }
983  void resize(size_type n, VT t) {
984  if (n <= size()) {
986  } else if (dataIsInternalAndNotVT(t) && n > capacity()) {
987  T copy(t);
988  reserve(n);
989  M_uninitialized_fill_n_e(n - size(), copy);
990  } else {
991  reserve(n);
992  M_uninitialized_fill_n_e(n - size(), t);
993  }
994  }
996  size_type capacity() const noexcept {
997  return size_type(impl_.z_ - impl_.b_);
998  }
1000  bool empty() const noexcept {
1001  return impl_.b_ == impl_.e_;
1002  }
1004  void reserve(size_type n) {
1005  if (n <= capacity()) {
1006  return;
1007  }
1008  if (impl_.b_ && reserve_in_place(n)) {
1009  return;
1010  }
1011 
1012  auto newCap = folly::goodMallocSize(n * sizeof(T)) / sizeof(T);
1013  auto newB = M_allocate(newCap);
1014  try {
1015  M_relocate(newB);
1016  } catch (...) {
1017  M_deallocate(newB, newCap);
1018  throw;
1019  }
1020  if (impl_.b_) {
1021  M_deallocate(impl_.b_, size_type(impl_.z_ - impl_.b_));
1022  }
1023  impl_.z_ = newB + newCap;
1024  impl_.e_ = newB + (impl_.e_ - impl_.b_);
1025  impl_.b_ = newB;
1026  }
1028  void shrink_to_fit() noexcept {
1029  if (empty()) {
1030  impl_.reset();
1031  return;
1032  }
1033 
1034  auto const newCapacityBytes = folly::goodMallocSize(size() * sizeof(T));
1035  auto const newCap = newCapacityBytes / sizeof(T);
1036  auto const oldCap = capacity();
1037 
1038  if (newCap >= oldCap) {
1039  return;
1040  }
1041 
1042  void* p = impl_.b_;
1043  // xallocx() will shrink to precisely newCapacityBytes (which was generated
1044  // by goodMallocSize()) if it successfully shrinks in place.
1046  newCapacityBytes >= folly::jemallocMinInPlaceExpandable &&
1047  xallocx(p, newCapacityBytes, 0, 0) == newCapacityBytes) {
1048  impl_.z_ += newCap - oldCap;
1049  } else {
1050  T* newB; // intentionally uninitialized
1051  try {
1052  newB = M_allocate(newCap);
1053  try {
1054  M_relocate(newB);
1055  } catch (...) {
1056  M_deallocate(newB, newCap);
1057  return; // swallow the error
1058  }
1059  } catch (...) {
1060  return;
1061  }
1062  if (impl_.b_) {
1063  M_deallocate(impl_.b_, size_type(impl_.z_ - impl_.b_));
1064  }
1065  impl_.z_ = newB + newCap;
1066  impl_.e_ = newB + (impl_.e_ - impl_.b_);
1067  impl_.b_ = newB;
1068  }
1069  }
1070 
1071  private:
1072  bool reserve_in_place(size_type n) {
1074  return false;
1075  }
1076 
1077  // jemalloc can never grow in place blocks smaller than 4096 bytes.
1078  if ((impl_.z_ - impl_.b_) * sizeof(T) <
1080  return false;
1081  }
1082 
1083  auto const newCapacityBytes = folly::goodMallocSize(n * sizeof(T));
1084  void* p = impl_.b_;
1085  if (xallocx(p, newCapacityBytes, 0, 0) == newCapacityBytes) {
1086  impl_.z_ = impl_.b_ + newCapacityBytes / sizeof(T);
1087  return true;
1088  }
1089  return false;
1090  }
1091 
1092  //===========================================================================
1093  //---------------------------------------------------------------------------
1094  // element access
1095  public:
1096  reference operator[](size_type n) {
1097  assert(n < size());
1098  return impl_.b_[n];
1099  }
1100  const_reference operator[](size_type n) const {
1101  assert(n < size());
1102  return impl_.b_[n];
1103  }
1104  const_reference at(size_type n) const {
1105  if (UNLIKELY(n >= size())) {
1106  throw_exception<std::out_of_range>(
1107  "fbvector: index is greater than size.");
1108  }
1109  return (*this)[n];
1110  }
1111  reference at(size_type n) {
1112  auto const& cThis = *this;
1113  return const_cast<reference>(cThis.at(n));
1114  }
1115  reference front() {
1116  assert(!empty());
1117  return *impl_.b_;
1118  }
1119  const_reference front() const {
1120  assert(!empty());
1121  return *impl_.b_;
1122  }
1123  reference back() {
1124  assert(!empty());
1125  return impl_.e_[-1];
1126  }
1127  const_reference back() const {
1128  assert(!empty());
1129  return impl_.e_[-1];
1130  }
1131 
1132  //===========================================================================
1133  //---------------------------------------------------------------------------
1134  // data access
1135  public:
1136  T* data() noexcept {
1137  return impl_.b_;
1138  }
1139  const T* data() const noexcept {
1140  return impl_.b_;
1141  }
1142 
1143  //===========================================================================
1144  //---------------------------------------------------------------------------
1145  // modifiers (common)
1146  public:
1147  template <class... Args>
1148  void emplace_back(Args&&... args) {
1149  if (impl_.e_ != impl_.z_) {
1150  M_construct(impl_.e_, std::forward<Args>(args)...);
1151  ++impl_.e_;
1152  } else {
1153  emplace_back_aux(std::forward<Args>(args)...);
1154  }
1155  }
1157  void push_back(const T& value) {
1158  if (impl_.e_ != impl_.z_) {
1159  M_construct(impl_.e_, value);
1160  ++impl_.e_;
1161  } else {
1162  emplace_back_aux(value);
1163  }
1164  }
1166  void push_back(T&& value) {
1167  if (impl_.e_ != impl_.z_) {
1168  M_construct(impl_.e_, std::move(value));
1169  ++impl_.e_;
1170  } else {
1171  emplace_back_aux(std::move(value));
1172  }
1173  }
1175  void pop_back() {
1176  assert(!empty());
1177  --impl_.e_;
1178  M_destroy(impl_.e_);
1179  }
1181  void swap(fbvector& other) noexcept {
1183  swap(impl_, other.impl_);
1184  } else {
1185  impl_.swapData(other.impl_);
1186  }
1187  }
1189  void clear() noexcept {
1191  }
1192 
1193  private:
1194  // std::vector implements a similar function with a different growth
1195  // strategy: empty() ? 1 : capacity() * 2.
1196  //
1197  // fbvector grows differently on two counts:
1198  //
1199  // (1) initial size
1200  // Instead of growing to size 1 from empty, fbvector allocates at least
1201  // 64 bytes. You may still use reserve to reserve a lesser amount of
1202  // memory.
1203  // (2) 1.5x
1204  // For medium-sized vectors, the growth strategy is 1.5x. See the docs
1205  // for details.
1206  // This does not apply to very small or very large fbvectors. This is a
1207  // heuristic.
1208  // A nice addition to fbvector would be the capability of having a user-
1209  // defined growth strategy, probably as part of the allocator.
1210  //
1212  size_type computePushBackCapacity() const {
1213  if (capacity() == 0) {
1214  return std::max(64 / sizeof(T), size_type(1));
1215  }
1216  if (capacity() < folly::jemallocMinInPlaceExpandable / sizeof(T)) {
1217  return capacity() * 2;
1218  }
1219  if (capacity() > 4096 * 32 / sizeof(T)) {
1220  return capacity() * 2;
1221  }
1222  return (capacity() * 3 + 1) / 2;
1223  }
1224 
1225  template <class... Args>
1226  void emplace_back_aux(Args&&... args);
1227 
1228  //===========================================================================
1229  //---------------------------------------------------------------------------
1230  // modifiers (erase)
1231  public:
1232  iterator erase(const_iterator position) {
1233  return erase(position, position + 1);
1234  }
1236  iterator erase(const_iterator first, const_iterator last) {
1237  assert(isValid(first) && isValid(last));
1238  assert(first <= last);
1239  if (first != last) {
1240  if (last == end()) {
1241  M_destroy_range_e((iterator)first);
1242  } else {
1244  D_destroy_range_a((iterator)first, (iterator)last);
1245  if (last - first >= cend() - last) {
1246  std::memcpy((void*)first, (void*)last, (cend() - last) * sizeof(T));
1247  } else {
1248  std::memmove((iterator)first, last, (cend() - last) * sizeof(T));
1249  }
1250  impl_.e_ -= (last - first);
1251  } else {
1252  std::copy(
1253  std::make_move_iterator((iterator)last),
1254  std::make_move_iterator(end()),
1255  (iterator)first);
1256  auto newEnd = impl_.e_ - std::distance(first, last);
1257  M_destroy_range_e(newEnd);
1258  }
1259  }
1260  }
1261  return (iterator)first;
1262  }
1263 
1264  //===========================================================================
1265  //---------------------------------------------------------------------------
1266  // modifiers (insert)
1267  private: // we have the private section first because it defines some macros
1268  bool isValid(const_iterator it) {
1269  return cbegin() <= it && it <= cend();
1270  }
1272  size_type computeInsertCapacity(size_type n) {
1273  size_type nc = std::max(computePushBackCapacity(), size() + n);
1274  size_type ac = folly::goodMallocSize(nc * sizeof(T)) / sizeof(T);
1275  return ac;
1276  }
1277 
1278  //---------------------------------------------------------------------------
1279  //
1280  // make_window takes an fbvector, and creates an uninitialized gap (a
1281  // window) at the given position, of the given size. The fbvector must
1282  // have enough capacity.
1283  //
1284  // Explanation by picture.
1285  //
1286  // 123456789______
1287  // ^
1288  // make_window here of size 3
1289  //
1290  // 1234___56789___
1291  //
1292  // If something goes wrong and the window must be destroyed, use
1293  // undo_window to provide a weak exception guarantee. It destroys
1294  // the right ledge.
1295  //
1296  // 1234___________
1297  //
1298  //---------------------------------------------------------------------------
1299  //
1300  // wrap_frame takes an inverse window and relocates an fbvector around it.
1301  // The fbvector must have at least as many elements as the left ledge.
1302  //
1303  // Explanation by picture.
1304  //
1305  // START
1306  // fbvector: inverse window:
1307  // 123456789______ _____abcde_______
1308  // [idx][ n ]
1309  //
1310  // RESULT
1311  // _______________ 12345abcde6789___
1312  //
1313  //---------------------------------------------------------------------------
1314  //
1315  // insert_use_fresh_memory returns true iff the fbvector should use a fresh
1316  // block of memory for the insertion. If the fbvector does not have enough
1317  // spare capacity, then it must return true. Otherwise either true or false
1318  // may be returned.
1319  //
1320  //---------------------------------------------------------------------------
1321  //
1322  // These three functions, make_window, wrap_frame, and
1323  // insert_use_fresh_memory, can be combined into a uniform interface.
1324  // Since that interface involves a lot of case-work, it is built into
1325  // some macros: FOLLY_FBVECTOR_INSERT_(PRE|START|TRY|END)
1326  // Macros are used in an attempt to let GCC perform better optimizations,
1327  // especially control flow optimization.
1328  //
1329 
1330  //---------------------------------------------------------------------------
1331  // window
1333  void make_window(iterator position, size_type n) {
1334  // The result is guaranteed to be non-negative, so use an unsigned type:
1335  size_type tail = size_type(std::distance(position, impl_.e_));
1336 
1337  if (tail <= n) {
1338  relocate_move(position + n, position, impl_.e_);
1339  relocate_done(position + n, position, impl_.e_);
1340  impl_.e_ += n;
1341  } else {
1343  std::memmove(position + n, position, tail * sizeof(T));
1344  impl_.e_ += n;
1345  } else {
1347  try {
1348  std::copy_backward(
1349  std::make_move_iterator(position),
1350  std::make_move_iterator(impl_.e_ - n),
1351  impl_.e_);
1352  } catch (...) {
1353  D_destroy_range_a(impl_.e_ - n, impl_.e_ + n);
1354  impl_.e_ -= n;
1355  throw;
1356  }
1357  impl_.e_ += n;
1358  D_destroy_range_a(position, position + n);
1359  }
1360  }
1361  }
1363  void undo_window(iterator position, size_type n) noexcept {
1364  D_destroy_range_a(position + n, impl_.e_);
1365  impl_.e_ = position;
1366  }
1367 
1368  //---------------------------------------------------------------------------
1369  // frame
1371  void wrap_frame(T* ledge, size_type idx, size_type n) {
1372  assert(size() >= idx);
1373  assert(n != 0);
1374 
1375  relocate_move(ledge, impl_.b_, impl_.b_ + idx);
1376  try {
1377  relocate_move(ledge + idx + n, impl_.b_ + idx, impl_.e_);
1378  } catch (...) {
1379  relocate_undo(ledge, impl_.b_, impl_.b_ + idx);
1380  throw;
1381  }
1382  relocate_done(ledge, impl_.b_, impl_.b_ + idx);
1383  relocate_done(ledge + idx + n, impl_.b_ + idx, impl_.e_);
1384  }
1385 
1386  //---------------------------------------------------------------------------
1387  // use fresh?
1389  bool insert_use_fresh(bool at_end, size_type n) {
1390  if (at_end) {
1391  if (size() + n <= capacity()) {
1392  return false;
1393  }
1394  if (reserve_in_place(size() + n)) {
1395  return false;
1396  }
1397  return true;
1398  }
1399 
1400  if (size() + n > capacity()) {
1401  return true;
1402  }
1403 
1404  return false;
1405  }
1406 
1407  //---------------------------------------------------------------------------
1408  // interface
1409 
1410  template <
1411  typename IsInternalFunc,
1412  typename InsertInternalFunc,
1413  typename ConstructFunc,
1414  typename DestroyFunc>
1415  iterator do_real_insert(
1416  const_iterator cpos,
1417  size_type n,
1418  IsInternalFunc&& isInternalFunc,
1419  InsertInternalFunc&& insertInternalFunc,
1420  ConstructFunc&& constructFunc,
1421  DestroyFunc&& destroyFunc) {
1422  if (n == 0) {
1423  return iterator(cpos);
1424  }
1425  bool at_end = cpos == cend();
1426  bool fresh = insert_use_fresh(at_end, n);
1427  if (!at_end) {
1428  if (!fresh && isInternalFunc()) {
1429  // check for internal data (technically not required by the standard)
1430  return insertInternalFunc();
1431  }
1432  assert(isValid(cpos));
1433  }
1434  T* position = const_cast<T*>(cpos);
1435  size_type idx = size_type(std::distance(impl_.b_, position));
1436  T* b;
1437  size_type newCap; /* intentionally uninitialized */
1438 
1439  if (fresh) {
1440  newCap = computeInsertCapacity(n);
1441  b = M_allocate(newCap);
1442  } else {
1443  if (!at_end) {
1444  make_window(position, n);
1445  } else {
1446  impl_.e_ += n;
1447  }
1448  b = impl_.b_;
1449  }
1450 
1451  T* start = b + idx;
1452  try {
1453  // construct the inserted elements
1454  constructFunc(start);
1455  } catch (...) {
1456  if (fresh) {
1457  M_deallocate(b, newCap);
1458  } else {
1459  if (!at_end) {
1460  undo_window(position, n);
1461  } else {
1462  impl_.e_ -= n;
1463  }
1464  }
1465  throw;
1466  }
1467 
1468  if (fresh) {
1469  try {
1470  wrap_frame(b, idx, n);
1471  } catch (...) {
1472  // delete the inserted elements (exception has been thrown)
1473  destroyFunc(start);
1474  M_deallocate(b, newCap);
1475  throw;
1476  }
1477  if (impl_.b_) {
1478  M_deallocate(impl_.b_, capacity());
1479  }
1480  impl_.set(b, size() + n, newCap);
1481  return impl_.b_ + idx;
1482  } else {
1483  return position;
1484  }
1485  }
1486 
1487  public:
1488  template <class... Args>
1489  iterator emplace(const_iterator cpos, Args&&... args) {
1490  return do_real_insert(
1491  cpos,
1492  1,
1493  [&] { return false; },
1494  [&] { return iterator{}; },
1495  [&](iterator start) {
1496  M_construct(start, std::forward<Args>(args)...);
1497  },
1498  [&](iterator start) { M_destroy(start); });
1499  }
1501  iterator insert(const_iterator cpos, const T& value) {
1502  return do_real_insert(
1503  cpos,
1504  1,
1505  [&] { return dataIsInternal(value); },
1506  [&] { return insert(cpos, T(value)); },
1507  [&](iterator start) { M_construct(start, value); },
1508  [&](iterator start) { M_destroy(start); });
1509  }
1511  iterator insert(const_iterator cpos, T&& value) {
1512  return do_real_insert(
1513  cpos,
1514  1,
1515  [&] { return dataIsInternal(value); },
1516  [&] { return insert(cpos, T(std::move(value))); },
1517  [&](iterator start) { M_construct(start, std::move(value)); },
1518  [&](iterator start) { M_destroy(start); });
1519  }
1521  iterator insert(const_iterator cpos, size_type n, VT value) {
1522  return do_real_insert(
1523  cpos,
1524  n,
1525  [&] { return dataIsInternalAndNotVT(value); },
1526  [&] { return insert(cpos, n, T(value)); },
1527  [&](iterator start) { D_uninitialized_fill_n_a(start, n, value); },
1528  [&](iterator start) { D_destroy_range_a(start, start + n); });
1529  }
1530 
1531  template <
1532  class It,
1533  class Category = typename std::iterator_traits<It>::iterator_category>
1534  iterator insert(const_iterator cpos, It first, It last) {
1535  return insert(cpos, first, last, Category());
1536  }
1538  iterator insert(const_iterator cpos, std::initializer_list<T> il) {
1539  return insert(cpos, il.begin(), il.end());
1540  }
1541 
1542  //---------------------------------------------------------------------------
1543  // insert dispatch for iterator types
1544  private:
1545  template <class FIt>
1546  iterator
1547  insert(const_iterator cpos, FIt first, FIt last, std::forward_iterator_tag) {
1548  size_type n = size_type(std::distance(first, last));
1549  return do_real_insert(
1550  cpos,
1551  n,
1552  [&] { return false; },
1553  [&] { return iterator{}; },
1554  [&](iterator start) { D_uninitialized_copy_a(start, first, last); },
1555  [&](iterator start) { D_destroy_range_a(start, start + n); });
1556  }
1557 
1558  template <class IIt>
1559  iterator
1560  insert(const_iterator cpos, IIt first, IIt last, std::input_iterator_tag) {
1561  T* position = const_cast<T*>(cpos);
1562  assert(isValid(position));
1563  size_type idx = std::distance(begin(), position);
1564 
1565  fbvector storage(
1566  std::make_move_iterator(position),
1567  std::make_move_iterator(end()),
1568  A::select_on_container_copy_construction(impl_));
1569  M_destroy_range_e(position);
1570  for (; first != last; ++first) {
1571  emplace_back(*first);
1572  }
1573  insert(
1574  cend(),
1575  std::make_move_iterator(storage.begin()),
1576  std::make_move_iterator(storage.end()));
1577  return impl_.b_ + idx;
1578  }
1579 
1580  //===========================================================================
1581  //---------------------------------------------------------------------------
1582  // lexicographical functions
1583  public:
1584  bool operator==(const fbvector& other) const {
1585  return size() == other.size() && std::equal(begin(), end(), other.begin());
1586  }
1588  bool operator!=(const fbvector& other) const {
1589  return !(*this == other);
1590  }
1592  bool operator<(const fbvector& other) const {
1593  return std::lexicographical_compare(
1594  begin(), end(), other.begin(), other.end());
1595  }
1597  bool operator>(const fbvector& other) const {
1598  return other < *this;
1599  }
1601  bool operator<=(const fbvector& other) const {
1602  return !(*this > other);
1603  }
1605  bool operator>=(const fbvector& other) const {
1606  return !(*this < other);
1607  }
1608 
1609  //===========================================================================
1610  //---------------------------------------------------------------------------
1611  // friends
1612  private:
1613  template <class _T, class _A>
1614  friend _T* relinquish(fbvector<_T, _A>&);
1615 
1616  template <class _T, class _A>
1617  friend void attach(fbvector<_T, _A>&, _T* data, size_t sz, size_t cap);
1618 
1619 }; // class fbvector
1620 
1621 //=============================================================================
1622 //-----------------------------------------------------------------------------
1623 // outlined functions (gcc, you finicky compiler you)
1624 
1625 template <typename T, typename Allocator>
1626 template <class... Args>
1628  size_type byte_sz =
1631  ((impl_.z_ - impl_.b_) * sizeof(T) >=
1633  // Try to reserve in place.
1634  // Ask xallocx to allocate in place at least size()+1 and at most sz space.
1635  // xallocx will allocate as much as possible within that range, which
1636  // is the best possible outcome: if sz space is available, take it all,
1637  // otherwise take as much as possible. If nothing is available, then fail.
1638  // In this fashion, we never relocate if there is a possibility of
1639  // expanding in place, and we never reallocate by less than the desired
1640  // amount unless we cannot expand further. Hence we will not reallocate
1641  // sub-optimally twice in a row (modulo the blocking memory being freed).
1642  size_type lower = folly::goodMallocSize(sizeof(T) + size() * sizeof(T));
1643  size_type upper = byte_sz;
1644  size_type extra = upper - lower;
1645 
1646  void* p = impl_.b_;
1647  size_t actual;
1648 
1649  if ((actual = xallocx(p, lower, extra, 0)) >= lower) {
1650  impl_.z_ = impl_.b_ + actual / sizeof(T);
1651  M_construct(impl_.e_, std::forward<Args>(args)...);
1652  ++impl_.e_;
1653  return;
1654  }
1655  }
1656 
1657  // Reallocation failed. Perform a manual relocation.
1658  size_type sz = byte_sz / sizeof(T);
1659  auto newB = M_allocate(sz);
1660  auto newE = newB + size();
1661  try {
1663  // For linear memory access, relocate before construction.
1664  // By the test condition, relocate is noexcept.
1665  // Note that there is no cleanup to do if M_construct throws - that's
1666  // one of the beauties of relocation.
1667  // Benchmarks for this code have high variance, and seem to be close.
1668  relocate_move(newB, impl_.b_, impl_.e_);
1669  M_construct(newE, std::forward<Args>(args)...);
1670  ++newE;
1671  } else {
1672  M_construct(newE, std::forward<Args>(args)...);
1673  ++newE;
1674  try {
1675  M_relocate(newB);
1676  } catch (...) {
1677  M_destroy(newE - 1);
1678  throw;
1679  }
1680  }
1681  } catch (...) {
1682  M_deallocate(newB, sz);
1683  throw;
1684  }
1685  if (impl_.b_) {
1686  M_deallocate(impl_.b_, size());
1687  }
1688  impl_.b_ = newB;
1689  impl_.e_ = newE;
1690  impl_.z_ = newB + sz;
1691 }
1692 
1693 //=============================================================================
1694 //-----------------------------------------------------------------------------
1695 // specialized functions
1696 
1697 template <class T, class A>
1699  lhs.swap(rhs);
1700 }
1701 
1702 //=============================================================================
1703 //-----------------------------------------------------------------------------
1704 // other
1705 
1706 namespace detail {
1707 
1708 // Format support.
1709 template <class T, class A>
1710 struct IndexableTraits<fbvector<T, A>>
1711  : public IndexableTraitsSeq<fbvector<T, A>> {};
1712 
1713 } // namespace detail
1714 
1715 template <class T, class A>
1716 void compactResize(fbvector<T, A>* v, size_t sz) {
1717  v->resize(sz);
1718  v->shrink_to_fit();
1719 }
1720 
1721 // DANGER
1722 //
1723 // relinquish and attach are not a members function specifically so that it is
1724 // awkward to call them. It is very easy to shoot yourself in the foot with
1725 // these functions.
1726 //
1727 // If you call relinquish, then it is your responsibility to free the data
1728 // and the storage, both of which may have been generated in a non-standard
1729 // way through the fbvector's allocator.
1730 //
1731 // If you call attach, it is your responsibility to ensure that the fbvector
1732 // is fresh (size and capacity both zero), and that the supplied data is
1733 // capable of being manipulated by the allocator.
1734 // It is acceptable to supply a stack pointer IF:
1735 // (1) The vector's data does not outlive the stack pointer. This includes
1736 // extension of the data's life through a move operation.
1737 // (2) The pointer has enough capacity that the vector will never be
1738 // relocated.
1739 // (3) Insert is not called on the vector; these functions have leeway to
1740 // relocate the vector even if there is enough capacity.
1741 // (4) A stack pointer is compatible with the fbvector's allocator.
1742 //
1743 
1744 template <class T, class A>
1746  T* ret = v.data();
1747  v.impl_.b_ = v.impl_.e_ = v.impl_.z_ = nullptr;
1748  return ret;
1749 }
1750 
1751 template <class T, class A>
1752 void attach(fbvector<T, A>& v, T* data, size_t sz, size_t cap) {
1753  assert(v.data() == nullptr);
1754  v.impl_.b_ = data;
1755  v.impl_.e_ = data + sz;
1756  v.impl_.z_ = data + cap;
1757 }
1758 
1759 } // namespace folly
static void S_uninitialized_fill_n_a(Allocator &a, T *dest, size_type sz, Args &&...args)
Definition: FBVector.h:430
void M_relocate(T *newB)
Definition: FBVector.h:650
static void S_destroy_range_a(Allocator &a, T *first, T *last) noexcept
Definition: FBVector.h:372
size_type computeInsertCapacity(size_type n)
Definition: FBVector.h:1271
static void S_destroy_range(T *first, T *last) noexcept
Definition: FBVector.h:379
iterator erase(const_iterator position)
Definition: FBVector.h:1231
iterator end() noexcept
Definition: FBVector.h:928
size_t size_type
Definition: FBVector.h:207
void set(pointer newB, size_type newSize, size_type newCap)
Definition: FBVector.h:169
void * checkedMalloc(size_t size)
Definition: Malloc.h:227
folly::fbvector::Impl impl_
~fbvector()=default
void undo_window(iterator position, size_type n) noexcept
Definition: FBVector.h:1362
char b
LogLevel max
Definition: LogLevel.cpp:31
reverse_iterator rbegin() noexcept
Definition: FBVector.h:934
friend void attach(fbvector< _T, _A > &, _T *data, size_t sz, size_t cap)
void relocate_done(T *, T *first, T *last) noexcept
Definition: FBVector.h:690
iterator do_real_insert(const_iterator cpos, size_type n, IsInternalFunc &&isInternalFunc, InsertInternalFunc &&insertInternalFunc, ConstructFunc &&constructFunc, DestroyFunc &&destroyFunc)
Definition: FBVector.h:1414
T * D_allocate(size_type n)
Definition: FBVector.h:119
bool usingJEMalloc() noexcept
Definition: Malloc.h:147
PskType type
bool dataIsInternal(const T &t)
Definition: FBVector.h:913
bool operator<=(const fbvector &other) const
Definition: FBVector.h:1600
#define FOLLY_FBV_UNROLL_PTR(first, last, OP)
Definition: FBVector.h:55
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
void assign(It first, It last)
Definition: FBVector.h:800
A::size_type size_type
Definition: FBVector.h:87
static void swap(Impl &a, Impl &b)
Definition: FBVector.h:190
#define LIKELY(x)
Definition: Likely.h:47
bool isValid(const_iterator it)
Definition: FBVector.h:1267
void reserve(size_type n)
Definition: FBVector.h:1003
dest
Definition: upload.py:394
STL namespace.
#define FOLLY_FBV_OP(p)
allocator_type get_allocator() const noexcept
Definition: FBVector.h:830
bool insert_use_fresh(bool at_end, size_type n)
Definition: FBVector.h:1388
folly::std T
void M_destroy_range_e(T *pos) noexcept
Definition: FBVector.h:356
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
bool_constant< (std::is_nothrow_move_constructible< HTTPHeaderCode >::value &&usingStdAllocator::value)||!std::is_copy_constructible< HTTPHeaderCode >::value > relocate_use_move
Definition: FBVector.h:664
void M_uninitialized_fill_n_e(size_type sz)
Definition: FBVector.h:401
requires E e noexcept(noexcept(s.error(std::move(e))))
const_reverse_iterator crend() const noexcept
Definition: FBVector.h:956
iterator begin() noexcept
Definition: FBVector.h:922
T * data() noexcept
Definition: FBVector.h:1135
bool operator>=(const fbvector &other) const
Definition: FBVector.h:1604
A::pointer pointer
Definition: FBVector.h:210
Allocator allocator_type
Definition: FBVector.h:209
#define nullptr
Definition: http_parser.c:41
void D_uninitialized_fill_n_a(T *dest, size_type sz)
Definition: FBVector.h:412
static const size_t jemallocMinInPlaceExpandable
Definition: Malloc.h:221
void D_uninitialized_copy_a(T *dest, It first, It last)
Definition: FBVector.h:505
bool_constant< true > true_type
Definition: gtest-port.h:2210
A::pointer pointer
Definition: FBVector.h:86
FOLLY_PUSH_WARNING RHS rhs
Definition: Traits.h:649
void relocate_undo(T *dest, T *first, T *last) noexcept
Definition: FBVector.h:699
Impl(const Allocator &alloc)
Definition: FBVector.h:94
static void destroy()
void push_back(const T &value)
Definition: FBVector.h:1156
fbvector & operator=(const fbvector &other)
Definition: FBVector.h:766
size_t(* xallocx)(void *, size_t, size_t, int)
Definition: MallocImpl.cpp:37
constexpr std::decay< T >::type copy(T &&value) noexcept(noexcept(typename std::decay< T >::type(std::forward< T >(value))))
Definition: Utility.h:72
void emplace_back_aux(Args &&...args)
Definition: FBVector.h:1626
bool dataIsInternalAndNotVT(const T &t)
Definition: FBVector.h:907
std::integral_constant< bool, B > bool_constant
Definition: Traits.h:145
void relocate_move_or_memcpy(T *dest, T *first, T *last, std::true_type)
Definition: FBVector.h:671
bool operator==(const fbvector &other) const
Definition: FBVector.h:1583
static void S_uninitialized_fill_n(T *dest, size_type n)
Definition: FBVector.h:449
static void S_uninitialized_copy_a(Allocator &a, T *dest, It first, It last)
Definition: FBVector.h:525
void M_uninitialized_copy_e(It first, It last)
Definition: FBVector.h:492
static void S_uninitialized_copy(T *dest, It first, It last)
Definition: FBVector.h:539
void swapData(Impl &other)
Definition: FBVector.h:136
bool operator!=(const fbvector &other) const
Definition: FBVector.h:1587
const T * const_iterator
Definition: FBVector.h:206
Impl(size_type n, const Allocator &alloc=Allocator())
Definition: FBVector.h:99
void relocate_move_or_copy(T *dest, T *first, T *last, std::true_type)
Definition: FBVector.h:681
char a
size_type max_size() const noexcept
Definition: FBVector.h:968
fbvector()=default
std::is_trivially_copyable< T > is_trivially_copyable
Definition: Traits.h:313
void moveFrom(fbvector &&other, std::true_type)
Definition: FBVector.h:859
void init(size_type n)
Definition: FBVector.h:158
void wrap_frame(T *ledge, size_type idx, size_type n)
Definition: FBVector.h:1370
void shrink_to_fit() noexcept
Definition: FBVector.h:1027
const_iterator cbegin() const noexcept
Definition: FBVector.h:947
void D_deallocate(T *p, size_type n) noexcept
Definition: FBVector.h:127
static const char *const value
Definition: Conv.cpp:50
void free()
void emplace_back(Args &&...args)
Definition: FBVector.h:1147
bool_constant< folly::IsRelocatable< T >::value &&usingStdAllocator::value > relocate_use_memcpy
Definition: FBVector.h:658
auto start
iterator emplace(const_iterator cpos, Args &&...args)
Definition: FBVector.h:1488
void pop_back()
Definition: FBVector.h:1174
std::allocator_traits< Allocator > A
Definition: FBVector.h:82
void compactResize(fbvector< T, A > *v, size_t sz)
Definition: FBVector.h:1715
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition: FBVector.h:213
size_type computePushBackCapacity() const
Definition: FBVector.h:1211
bool reserve_in_place(size_type n)
Definition: FBVector.h:1071
Impl(Allocator &&alloc)
Definition: FBVector.h:96
void M_uninitialized_move_e(It first, It last)
Definition: FBVector.h:498
bool empty() const noexcept
Definition: FBVector.h:999
size_type capacity() const noexcept
Definition: FBVector.h:995
const value_type & const_reference
Definition: FBVector.h:204
void clear() noexcept
Definition: FBVector.h:1188
A::const_pointer const_pointer
Definition: FBVector.h:211
const_iterator cend() const noexcept
Definition: FBVector.h:950
const
Definition: upload.py:398
static void S_uninitialized_copy_bits(T *dest, const T *first, const T *last)
Definition: FBVector.h:552
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
bool_constant< false > false_type
Definition: gtest-port.h:2209
bool operator>(const fbvector &other) const
Definition: FBVector.h:1596
void D_uninitialized_move_a(T *dest, It first, It last)
Definition: FBVector.h:518
void reset(size_type newCap)
Definition: FBVector.h:175
friend _T * relinquish(fbvector< _T, _A > &)
reverse_iterator rend() noexcept
Definition: FBVector.h:940
const_reference at(size_type n) const
Definition: FBVector.h:1103
reference operator[](size_type n)
Definition: FBVector.h:1095
void destroy() noexcept
Definition: FBVector.h:143
void D_destroy_range_a(T *first, T *last) noexcept
Definition: FBVector.h:363
void resize(size_type n)
Definition: FBVector.h:973
void make_window(iterator position, size_type n)
Definition: FBVector.h:1332
std::reverse_iterator< iterator > reverse_iterator
Definition: FBVector.h:212
size_type size() const noexcept
Definition: FBVector.h:964
#define UNLIKELY(x)
Definition: Likely.h:48
reference back()
Definition: FBVector.h:1122
std::make_signed< size_type >::type difference_type
Definition: FBVector.h:208
iterator insert(const_iterator cpos, const T &value)
Definition: FBVector.h:1500
void relocate_move(T *dest, T *first, T *last)
Definition: FBVector.h:667
value_type & reference
Definition: FBVector.h:203
Impl(Impl &&other) noexcept
Definition: FBVector.h:104
bool operator<(const fbvector &other) const
Definition: FBVector.h:1591
static It S_copy_n(T *dest, It first, size_type n)
Definition: FBVector.h:582
constexpr detail::First first
Definition: Base-inl.h:2553
void swap(fbvector< T, A > &lhs, fbvector< T, A > &rhs) noexcept
Definition: FBVector.h:1697
size_t goodMallocSize(size_t minSize) noexcept
Definition: Malloc.h:201
reference front()
Definition: FBVector.h:1114
const_reverse_iterator crbegin() const noexcept
Definition: FBVector.h:953