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