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