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