Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:51:40

0001 // Copyright Thorsten Ottosen, 2009.
0002 // Distributed under the Boost Software License, Version 1.0. (See
0003 // accompanying file LICENSE_1_0.txt or copy at
0004 // http://www.boost.org/LICENSE_1_0.txt)
0005 
0006 #ifndef BOOST_SIGNALS2_DETAIL_AUTO_BUFFER_HPP_25_02_2009
0007 #define BOOST_SIGNALS2_DETAIL_AUTO_BUFFER_HPP_25_02_2009
0008 
0009 #include <boost/detail/workaround.hpp>
0010 
0011 #if defined(_MSC_VER)
0012 # pragma once
0013 #endif
0014 
0015 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
0016 #pragma warning(push)
0017 #pragma warning(disable:4996)
0018 #endif
0019 
0020 #include <boost/assert.hpp>
0021 #include <boost/config.hpp>
0022 #include <boost/core/allocator_access.hpp>
0023 #include <boost/core/invoke_swap.hpp>
0024 #include <boost/iterator/reverse_iterator.hpp>
0025 #include <boost/iterator/iterator_traits.hpp>
0026 #include <boost/mpl/if.hpp>
0027 #include <boost/signals2/detail/scope_guard.hpp>
0028 #include <boost/type_traits/aligned_storage.hpp>
0029 #include <boost/type_traits/alignment_of.hpp>
0030 #include <boost/type_traits/has_nothrow_copy.hpp>
0031 #include <boost/type_traits/has_nothrow_assign.hpp>
0032 #include <boost/type_traits/has_trivial_assign.hpp>
0033 #include <boost/type_traits/has_trivial_constructor.hpp>
0034 #include <boost/type_traits/has_trivial_destructor.hpp>
0035 #include <algorithm>
0036 #include <cstring>
0037 #include <iterator>
0038 #include <memory>
0039 #include <stdexcept>
0040 
0041 namespace boost
0042 {
0043 namespace signals2
0044 {
0045 namespace detail
0046 {
0047     //
0048     // Policies for creating the stack buffer.
0049     //
0050     template< unsigned N >
0051     struct store_n_objects
0052     {
0053         BOOST_STATIC_CONSTANT( unsigned, value = N );
0054     };
0055 
0056     template< unsigned N >
0057     struct store_n_bytes
0058     {
0059         BOOST_STATIC_CONSTANT( unsigned, value = N );
0060     };
0061 
0062     namespace auto_buffer_detail
0063     {
0064         template< class Policy, class T >
0065         struct compute_buffer_size
0066         {
0067             BOOST_STATIC_CONSTANT( unsigned, value = Policy::value * sizeof(T) );
0068         };
0069 
0070         template< unsigned N, class T >
0071         struct compute_buffer_size< store_n_bytes<N>, T >
0072         {
0073             BOOST_STATIC_CONSTANT( unsigned, value = N );
0074         };
0075 
0076         template< class Policy, class T >
0077         struct compute_buffer_objects
0078         {
0079             BOOST_STATIC_CONSTANT( unsigned, value = Policy::value );
0080         };
0081 
0082         template< unsigned N, class T >
0083         struct compute_buffer_objects< store_n_bytes<N>, T >
0084         {
0085             BOOST_STATIC_CONSTANT( unsigned, value = N / sizeof(T) );
0086         };
0087     }
0088 
0089     struct default_grow_policy
0090     {
0091         template< class SizeType >
0092         static SizeType new_capacity( SizeType capacity )
0093         {
0094             //
0095             // @remark: we grow the capacity quite agressively.
0096             //          this is justified since we aim to minimize
0097             //          heap-allocations, and because we mostly use
0098             //          the buffer locally.
0099             return capacity * 4u;
0100         }
0101 
0102         template< class SizeType >
0103         static bool should_shrink( SizeType, SizeType )
0104         {
0105             //
0106             // @remark: when defining a new grow policy, one might
0107             //          choose that if the waated space is less
0108             //          than a certain percentage, then it is of
0109             //          little use to shrink.
0110             //
0111             return true;
0112         }
0113     };
0114 
0115     template< class T,
0116               class StackBufferPolicy = store_n_objects<256>,
0117               class GrowPolicy        = default_grow_policy,
0118               class Allocator         = std::allocator<T> >
0119     class auto_buffer;
0120 
0121 
0122 
0123     template
0124     <
0125         class T,
0126         class StackBufferPolicy,
0127         class GrowPolicy,
0128         class Allocator
0129     >
0130     class auto_buffer : Allocator
0131     {
0132     private:
0133         enum { N = auto_buffer_detail::
0134                    compute_buffer_objects<StackBufferPolicy,T>::value };
0135 
0136         BOOST_STATIC_CONSTANT( bool, is_stack_buffer_empty = N == 0u );
0137 
0138         typedef auto_buffer<T, store_n_objects<0>, GrowPolicy, Allocator>
0139                                                          local_buffer;
0140 
0141     public:
0142         typedef Allocator                                allocator_type;
0143         typedef T                                        value_type;
0144         typedef typename boost::allocator_size_type<Allocator>::type size_type;
0145         typedef typename boost::allocator_difference_type<Allocator>::type difference_type;
0146         typedef T*                                       pointer;
0147         typedef typename boost::allocator_pointer<Allocator>::type allocator_pointer;
0148         typedef const T*                                 const_pointer;
0149         typedef T&                                       reference;
0150         typedef const T&                                 const_reference;
0151         typedef pointer                                  iterator;
0152         typedef const_pointer                            const_iterator;
0153         typedef boost::reverse_iterator<iterator>        reverse_iterator;
0154         typedef boost::reverse_iterator<const_iterator>  const_reverse_iterator;
0155         typedef typename boost::mpl::if_c< boost::has_trivial_assign<T>::value
0156                                            && sizeof(T) <= sizeof(long double),
0157                                           const value_type,
0158                                           const_reference >::type
0159                                                       optimized_const_reference;
0160     private:
0161 
0162         pointer allocate( size_type capacity_arg )
0163         {
0164             if( capacity_arg > N )
0165                 return &*get_allocator().allocate( capacity_arg );
0166             else
0167                 return static_cast<T*>( members_.address() );
0168         }
0169 
0170         void deallocate( pointer where, size_type capacity_arg )
0171         {
0172             if( capacity_arg <= N )
0173                 return;
0174             get_allocator().deallocate( allocator_pointer(where), capacity_arg );
0175         }
0176 
0177         template< class I >
0178         static void copy_impl( I begin, I end, pointer where, std::random_access_iterator_tag )
0179         {
0180             copy_rai( begin, end, where, boost::has_trivial_assign<T>() );
0181         }
0182 
0183         static void copy_rai( const T* begin, const T* end,
0184                               pointer where, const boost::true_type& )
0185         {
0186             std::memcpy( where, begin, sizeof(T) * std::distance(begin,end) );
0187         }
0188 
0189         template< class I, bool b >
0190         static void copy_rai( I begin, I end,
0191                               pointer where, const boost::integral_constant<bool, b>& )
0192         {
0193             std::uninitialized_copy( begin, end, where );
0194         }
0195 
0196         template< class I >
0197         static void copy_impl( I begin, I end, pointer where, std::bidirectional_iterator_tag )
0198         {
0199             std::uninitialized_copy( begin, end, where );
0200         }
0201 
0202         template< class I >
0203         static void copy_impl( I begin, I end, pointer where )
0204         {
0205             copy_impl( begin, end, where,
0206                        typename std::iterator_traits<I>::iterator_category() );
0207         }
0208 
0209         template< class I, class I2 >
0210         static void assign_impl( I begin, I end, I2 where )
0211         {
0212             assign_impl( begin, end, where, boost::has_trivial_assign<T>() );
0213         }
0214 
0215         template< class I, class I2 >
0216         static void assign_impl( I begin, I end, I2 where, const boost::true_type& )
0217         {
0218             std::memcpy( where, begin, sizeof(T) * std::distance(begin,end) );
0219         }
0220 
0221         template< class I, class I2 >
0222         static void assign_impl( I begin, I end, I2 where, const boost::false_type& )
0223         {
0224             for( ; begin != end; ++begin, ++where )
0225                 *where = *begin;
0226         }
0227 
0228         void unchecked_push_back_n( size_type n, const boost::true_type& )
0229         {
0230             std::uninitialized_fill( end(), end() + n, T() );
0231             size_ += n;
0232         }
0233 
0234         void unchecked_push_back_n( size_type n, const boost::false_type& )
0235         {
0236             for( size_type i = 0u; i < n; ++i )
0237                 unchecked_push_back();
0238         }
0239 
0240         void auto_buffer_destroy( pointer where, const boost::false_type& )
0241         {
0242             (*where).~T();
0243         }
0244 
0245         void auto_buffer_destroy( pointer, const boost::true_type& )
0246         { }
0247 
0248         void auto_buffer_destroy( pointer where )
0249         {
0250             auto_buffer_destroy( where, boost::has_trivial_destructor<T>() );
0251         }
0252 
0253         void auto_buffer_destroy()
0254         {
0255             BOOST_ASSERT( is_valid() );
0256             if( buffer_ ) // do we need this check? Yes, but only
0257                 // for N = 0u + local instances in one_sided_swap()
0258                 auto_buffer_destroy( boost::has_trivial_destructor<T>() );
0259         }
0260 
0261         void destroy_back_n( size_type n, const boost::false_type& )
0262         {
0263             BOOST_ASSERT( n > 0 );
0264             pointer buffer  = buffer_ + size_ - 1u;
0265             pointer new_end = buffer - n;
0266             for( ; buffer > new_end; --buffer )
0267                 auto_buffer_destroy( buffer );
0268         }
0269 
0270         void destroy_back_n( size_type, const boost::true_type& )
0271         { }
0272 
0273         void destroy_back_n( size_type n )
0274         {
0275             destroy_back_n( n, boost::has_trivial_destructor<T>() );
0276         }
0277 
0278         void auto_buffer_destroy( const boost::false_type& x )
0279         {
0280             if( size_ )
0281                 destroy_back_n( size_, x );
0282             deallocate( buffer_, members_.capacity_ );
0283         }
0284 
0285         void auto_buffer_destroy( const boost::true_type& )
0286         {
0287             deallocate( buffer_, members_.capacity_ );
0288         }
0289 
0290         pointer move_to_new_buffer( size_type new_capacity, const boost::false_type& )
0291         {
0292             pointer new_buffer = allocate( new_capacity ); // strong
0293             scope_guard guard = make_obj_guard( *this,
0294                                                 &auto_buffer::deallocate,
0295                                                 new_buffer,
0296                                                 new_capacity );
0297             copy_impl( begin(), end(), new_buffer ); // strong
0298             guard.dismiss();                         // nothrow
0299             return new_buffer;
0300         }
0301 
0302         pointer move_to_new_buffer( size_type new_capacity, const boost::true_type& )
0303         {
0304             pointer new_buffer = allocate( new_capacity ); // strong
0305             copy_impl( begin(), end(), new_buffer );       // nothrow
0306             return new_buffer;
0307         }
0308 
0309         void reserve_impl( size_type new_capacity )
0310         {
0311             pointer new_buffer = move_to_new_buffer( new_capacity,
0312                                                  boost::has_nothrow_copy<T>() );
0313             auto_buffer_destroy();
0314             buffer_   = new_buffer;
0315             members_.capacity_ = new_capacity;
0316             BOOST_ASSERT( size_ <= members_.capacity_ );
0317         }
0318 
0319         size_type new_capacity_impl( size_type n )
0320         {
0321             BOOST_ASSERT( n > members_.capacity_ );
0322             size_type new_capacity = GrowPolicy::new_capacity( members_.capacity_ );
0323             // @todo: consider to check for allocator.max_size()
0324             return (std::max)(new_capacity,n);
0325         }
0326 
0327         static void swap_helper( auto_buffer& l, auto_buffer& r,
0328                                  const boost::true_type& )
0329         {
0330             BOOST_ASSERT( l.is_on_stack() && r.is_on_stack() );
0331 
0332             auto_buffer temp( l.begin(), l.end() );
0333             assign_impl( r.begin(), r.end(), l.begin() );
0334             assign_impl( temp.begin(), temp.end(), r.begin() );
0335             boost::core::invoke_swap( l.size_, r.size_ );
0336             boost::core::invoke_swap( l.members_.capacity_, r.members_.capacity_ );
0337         }
0338 
0339         static void swap_helper( auto_buffer& l, auto_buffer& r,
0340                                  const boost::false_type& )
0341         {
0342             BOOST_ASSERT( l.is_on_stack() && r.is_on_stack() );
0343             size_type min_size    = (std::min)(l.size_,r.size_);
0344             size_type max_size    = (std::max)(l.size_,r.size_);
0345             size_type diff        = max_size - min_size;
0346             auto_buffer* smallest = l.size_ == min_size ? &l : &r;
0347             auto_buffer* largest  = smallest == &l ? &r : &l;
0348 
0349             // @remark: the implementation below is not as fast
0350             //          as it could be if we assumed T had a default
0351             //          constructor.
0352 
0353             size_type i = 0u;
0354             for(  ; i < min_size; ++i )
0355                 boost::core::invoke_swap( (*smallest)[i], (*largest)[i] );
0356 
0357             for( ; i < max_size; ++i )
0358                 smallest->unchecked_push_back( (*largest)[i] );
0359 
0360             largest->pop_back_n( diff );
0361             boost::core::invoke_swap( l.members_.capacity_, r.members_.capacity_ );
0362         }
0363 
0364         void one_sided_swap( auto_buffer& temp ) // nothrow
0365         {
0366             BOOST_ASSERT( !temp.is_on_stack() );
0367             auto_buffer_destroy();
0368             // @remark: must be nothrow
0369             get_allocator()    = temp.get_allocator();
0370             members_.capacity_ = temp.members_.capacity_;
0371             buffer_            = temp.buffer_;
0372             BOOST_ASSERT( temp.size_ >= size_ + 1u );
0373             size_              = temp.size_;
0374             temp.buffer_       = 0;
0375             BOOST_ASSERT( temp.is_valid() );
0376         }
0377 
0378         template< class I >
0379         void insert_impl( const_iterator before, I begin_arg, I end_arg,
0380                           std::input_iterator_tag )
0381         {
0382             for( ; begin_arg != end_arg; ++begin_arg )
0383             {
0384                 before = insert( before, *begin_arg );
0385                 ++before;
0386             }
0387         }
0388 
0389         void grow_back( size_type n, const boost::true_type& )
0390         {
0391             BOOST_ASSERT( size_ + n <= members_.capacity_ );
0392             size_ += n;
0393         }
0394 
0395         void grow_back( size_type n, const boost::false_type& )
0396         {
0397             unchecked_push_back_n(n);
0398         }
0399 
0400         void grow_back( size_type n )
0401         {
0402             grow_back( n, boost::has_trivial_constructor<T>() );
0403         }
0404 
0405         void grow_back_one( const boost::true_type& )
0406         {
0407             BOOST_ASSERT( size_ + 1 <= members_.capacity_ );
0408             size_ += 1;
0409         }
0410 
0411         void grow_back_one( const boost::false_type& )
0412         {
0413             unchecked_push_back();
0414         }
0415 
0416         void grow_back_one()
0417         {
0418             grow_back_one( boost::has_trivial_constructor<T>() );
0419         }
0420 
0421         template< class I >
0422         void insert_impl( const_iterator before, I begin_arg, I end_arg,
0423                           std::forward_iterator_tag )
0424         {
0425             difference_type n = std::distance(begin_arg, end_arg);
0426 
0427             if( size_ + n <= members_.capacity_ )
0428             {
0429                 bool is_back_insertion = before == cend();
0430                 if( !is_back_insertion )
0431                 {
0432                     grow_back( n );
0433                     iterator where = const_cast<T*>(before);
0434                     std::copy( before, cend() - n, where + n );
0435                     assign_impl( begin_arg, end_arg, where );
0436                 }
0437                 else
0438                 {
0439                     unchecked_push_back( begin_arg, end_arg );
0440                 }
0441                 BOOST_ASSERT( is_valid() );
0442                 return;
0443             }
0444 
0445             auto_buffer temp( new_capacity_impl( size_ + n ) );
0446             temp.unchecked_push_back( cbegin(), before );
0447             temp.unchecked_push_back( begin_arg, end_arg );
0448             temp.unchecked_push_back( before, cend() );
0449             one_sided_swap( temp );
0450             BOOST_ASSERT( is_valid() );
0451         }
0452 
0453     public:
0454         bool is_valid() const // invariant
0455         {
0456             // @remark: allowed for N==0 and when
0457             //          using a locally instance
0458             //          in insert()/one_sided_swap()
0459             if( buffer_ == 0 )
0460                 return true;
0461 
0462             if( members_.capacity_ < N )
0463                 return false;
0464 
0465             if( !is_on_stack() && members_.capacity_ <= N )
0466                 return false;
0467 
0468             if( buffer_ == members_.address() )
0469                 if( members_.capacity_ > N )
0470                     return false;
0471 
0472             if( size_ > members_.capacity_ )
0473                 return false;
0474 
0475             return true;
0476         }
0477 
0478         auto_buffer()
0479             : members_( N ),
0480               buffer_( static_cast<T*>(members_.address()) ),
0481               size_( 0u )
0482         {
0483             BOOST_ASSERT( is_valid() );
0484         }
0485 
0486         auto_buffer( const auto_buffer& r )
0487             : members_( (std::max)(r.size_,size_type(N)) ),
0488               buffer_( allocate( members_.capacity_ ) ),
0489               size_( 0 )
0490         {
0491             copy_impl( r.begin(), r.end(), buffer_ );
0492             size_ = r.size_;
0493             BOOST_ASSERT( is_valid() );
0494         }
0495 
0496         auto_buffer& operator=( const auto_buffer& r ) // basic
0497         {
0498             if( this == &r )
0499                 return *this;
0500 
0501             difference_type diff = size_ - r.size_;
0502             if( diff >= 0 )
0503             {
0504                 pop_back_n( static_cast<size_type>(diff) );
0505                 assign_impl( r.begin(), r.end(), begin() );
0506             }
0507             else
0508             {
0509                 if( members_.capacity_ >= r.size() )
0510                 {
0511                     unchecked_push_back_n( static_cast<size_type>(-diff) );
0512                     assign_impl( r.begin(), r.end(), begin() );
0513                 }
0514                 else
0515                 {
0516                     // @remark: we release memory as early as possible
0517                     //          since we only give the basic guarantee
0518                     auto_buffer_destroy();
0519                     buffer_ = 0;
0520                     pointer new_buffer = allocate( r.size() );
0521                     scope_guard guard = make_obj_guard( *this,
0522                                                         &auto_buffer::deallocate,
0523                                                         new_buffer,
0524                                                         r.size() );
0525                     copy_impl( r.begin(), r.end(), new_buffer );
0526                     guard.dismiss();
0527                     buffer_            = new_buffer;
0528                     members_.capacity_ = r.size();
0529                     size_              = members_.capacity_;
0530                 }
0531             }
0532 
0533             BOOST_ASSERT( size() == r.size() );
0534             BOOST_ASSERT( is_valid() );
0535             return *this;
0536         }
0537 
0538         explicit auto_buffer( size_type capacity_arg )
0539             : members_( (std::max)(capacity_arg, size_type(N)) ),
0540               buffer_( allocate(members_.capacity_) ),
0541               size_( 0 )
0542         {
0543             BOOST_ASSERT( is_valid() );
0544         }
0545 
0546         auto_buffer( size_type size_arg, optimized_const_reference init_value )
0547             : members_( (std::max)(size_arg, size_type(N)) ),
0548               buffer_( allocate(members_.capacity_) ),
0549               size_( 0 )
0550         {
0551             std::uninitialized_fill( buffer_, buffer_ + size_arg, init_value );
0552             size_ = size_arg;
0553             BOOST_ASSERT( is_valid() );
0554         }
0555 
0556         auto_buffer( size_type capacity_arg, const allocator_type& a )
0557             : allocator_type( a ),
0558               members_( (std::max)(capacity_arg, size_type(N)) ),
0559               buffer_( allocate(members_.capacity_) ),
0560               size_( 0 )
0561         {
0562             BOOST_ASSERT( is_valid() );
0563         }
0564 
0565         auto_buffer( size_type size_arg, optimized_const_reference init_value,
0566                      const allocator_type& a )
0567             : allocator_type( a ),
0568               members_( (std::max)(size_arg, size_type(N)) ),
0569               buffer_( allocate(members_.capacity_) ),
0570               size_( 0 )
0571         {
0572             std::uninitialized_fill( buffer_, buffer_ + size_arg, init_value );
0573             size_ = size_arg;
0574             BOOST_ASSERT( is_valid() );
0575         }
0576 
0577         template< class ForwardIterator >
0578         auto_buffer( ForwardIterator begin_arg, ForwardIterator end_arg )
0579             :
0580               members_( std::distance(begin_arg, end_arg) ),
0581               buffer_( allocate(members_.capacity_) ),
0582               size_( 0 )
0583         {
0584             copy_impl( begin_arg, end_arg, buffer_ );
0585             size_ = members_.capacity_;
0586             if( members_.capacity_ < N )
0587                 members_.capacity_ = N;
0588             BOOST_ASSERT( is_valid() );
0589         }
0590 
0591         template< class ForwardIterator >
0592         auto_buffer( ForwardIterator begin_arg, ForwardIterator end_arg,
0593                      const allocator_type& a )
0594             : allocator_type( a ),
0595               members_( std::distance(begin_arg, end_arg) ),
0596               buffer_( allocate(members_.capacity_) ),
0597               size_( 0 )
0598         {
0599             copy_impl( begin_arg, end_arg, buffer_ );
0600             size_ = members_.capacity_;
0601             if( members_.capacity_ < N )
0602                 members_.capacity_ = N;
0603             BOOST_ASSERT( is_valid() );
0604         }
0605 
0606         ~auto_buffer()
0607         {
0608             auto_buffer_destroy();
0609         }
0610 
0611     public:
0612         bool empty() const
0613         {
0614             return size_ == 0;
0615         }
0616 
0617         bool full() const
0618         {
0619             return size_ == members_.capacity_;
0620         }
0621 
0622         bool is_on_stack() const
0623         {
0624             return members_.capacity_ <= N;
0625         }
0626 
0627         size_type size() const
0628         {
0629             return size_;
0630         }
0631 
0632         size_type capacity() const
0633         {
0634             return members_.capacity_;
0635         }
0636 
0637     public:
0638         pointer data()
0639         {
0640             return buffer_;
0641         }
0642 
0643         const_pointer data() const
0644         {
0645             return buffer_;
0646         }
0647 
0648         allocator_type& get_allocator()
0649         {
0650             return static_cast<allocator_type&>(*this);
0651         }
0652 
0653         const allocator_type& get_allocator() const
0654         {
0655             return static_cast<const allocator_type&>(*this);
0656         }
0657 
0658     public:
0659         iterator begin()
0660         {
0661             return buffer_;
0662         }
0663 
0664         const_iterator begin() const
0665         {
0666             return buffer_;
0667         }
0668 
0669         iterator end()
0670         {
0671             return buffer_ + size_;
0672         }
0673 
0674         const_iterator end() const
0675         {
0676             return buffer_ + size_;
0677         }
0678 
0679         reverse_iterator rbegin()
0680         {
0681             return reverse_iterator(end());
0682         }
0683 
0684         const_reverse_iterator rbegin() const
0685         {
0686             return const_reverse_iterator(end());
0687         }
0688 
0689         reverse_iterator rend()
0690         {
0691             return reverse_iterator(begin());
0692         }
0693 
0694         const_reverse_iterator rend() const
0695         {
0696             return const_reverse_iterator(begin());
0697         }
0698 
0699         const_iterator cbegin() const
0700         {
0701             return const_cast<const auto_buffer*>(this)->begin();
0702         }
0703 
0704         const_iterator cend() const
0705         {
0706             return const_cast<const auto_buffer*>(this)->end();
0707         }
0708 
0709         const_reverse_iterator crbegin() const
0710         {
0711             return const_cast<const auto_buffer*>(this)->rbegin();
0712         }
0713 
0714         const_reverse_iterator crend() const
0715         {
0716             return const_cast<const auto_buffer*>(this)->rend();
0717         }
0718 
0719     public:
0720         reference front()
0721         {
0722             return buffer_[0];
0723         }
0724 
0725         optimized_const_reference front() const
0726         {
0727             return buffer_[0];
0728         }
0729 
0730         reference back()
0731         {
0732             return buffer_[size_-1];
0733         }
0734 
0735         optimized_const_reference back() const
0736         {
0737             return buffer_[size_-1];
0738         }
0739 
0740         reference operator[]( size_type n )
0741         {
0742             BOOST_ASSERT( n < size_ );
0743             return buffer_[n];
0744         }
0745 
0746         optimized_const_reference operator[]( size_type n ) const
0747         {
0748             BOOST_ASSERT( n < size_ );
0749             return buffer_[n];
0750         }
0751 
0752         void unchecked_push_back()
0753         {
0754             BOOST_ASSERT( !full() );
0755             new (buffer_ + size_) T;
0756             ++size_;
0757         }
0758 
0759         void unchecked_push_back_n( size_type n )
0760         {
0761             BOOST_ASSERT( size_ + n <= members_.capacity_ );
0762             unchecked_push_back_n( n, boost::has_trivial_assign<T>() );
0763         }
0764 
0765         void unchecked_push_back( optimized_const_reference x ) // non-growing
0766         {
0767             BOOST_ASSERT( !full() );
0768             new (buffer_ + size_) T( x );
0769             ++size_;
0770         }
0771 
0772         template< class ForwardIterator >
0773         void unchecked_push_back( ForwardIterator begin_arg,
0774                                   ForwardIterator end_arg ) // non-growing
0775         {
0776             BOOST_ASSERT( size_ + std::distance(begin_arg, end_arg) <= members_.capacity_ );
0777             copy_impl( begin_arg, end_arg, buffer_ + size_ );
0778             size_ += std::distance(begin_arg, end_arg);
0779         }
0780 
0781         void reserve_precisely( size_type n )
0782         {
0783             BOOST_ASSERT( members_.capacity_  >= N );
0784 
0785             if( n <= members_.capacity_ )
0786                 return;
0787             reserve_impl( n );
0788             BOOST_ASSERT( members_.capacity_ == n );
0789         }
0790 
0791         void reserve( size_type n ) // strong
0792         {
0793             BOOST_ASSERT( members_.capacity_  >= N );
0794 
0795             if( n <= members_.capacity_ )
0796                 return;
0797 
0798             reserve_impl( new_capacity_impl( n ) );
0799             BOOST_ASSERT( members_.capacity_ >= n );
0800         }
0801 
0802         void push_back()
0803         {
0804             if( size_ != members_.capacity_ )
0805             {
0806                 unchecked_push_back();
0807             }
0808             else
0809             {
0810                 reserve( size_ + 1u );
0811                 unchecked_push_back();
0812             }
0813         }
0814 
0815         void push_back( optimized_const_reference x )
0816         {
0817             if( size_ != members_.capacity_ )
0818             {
0819                 unchecked_push_back( x );
0820             }
0821             else
0822             {
0823                reserve( size_ + 1u );
0824                unchecked_push_back( x );
0825             }
0826         }
0827 
0828         template< class ForwardIterator >
0829         void push_back( ForwardIterator begin_arg, ForwardIterator end_arg )
0830         {
0831             difference_type diff = std::distance(begin_arg, end_arg);
0832             if( size_ + diff > members_.capacity_ )
0833                 reserve( size_ + diff );
0834             unchecked_push_back( begin_arg, end_arg );
0835         }
0836 
0837         iterator insert( const_iterator before, optimized_const_reference x ) // basic
0838         {
0839             // @todo: consider if we want to support x in 'this'
0840             if( size_ < members_.capacity_ )
0841             {
0842                 bool is_back_insertion = before == cend();
0843                 iterator where = const_cast<T*>(before);
0844 
0845                 if( !is_back_insertion )
0846                 {
0847                     grow_back_one();
0848                     std::copy( before, cend() - 1u, where + 1u );
0849                     *where = x;
0850                     BOOST_ASSERT( is_valid() );
0851                  }
0852                 else
0853                 {
0854                     unchecked_push_back( x );
0855                 }
0856                 return where;
0857             }
0858 
0859             auto_buffer temp( new_capacity_impl( size_ + 1u ) );
0860             temp.unchecked_push_back( cbegin(), before );
0861             iterator result = temp.end();
0862             temp.unchecked_push_back( x );
0863             temp.unchecked_push_back( before, cend() );
0864             one_sided_swap( temp );
0865             BOOST_ASSERT( is_valid() );
0866             return result;
0867         }
0868 
0869         void insert( const_iterator before, size_type n,
0870                      optimized_const_reference x )
0871         {
0872             // @todo: see problems above
0873             if( size_ + n <= members_.capacity_ )
0874             {
0875                 grow_back( n );
0876                 iterator where = const_cast<T*>(before);
0877                 std::copy( before, cend() - n, where + n );
0878                 std::fill( where, where + n, x );
0879                 BOOST_ASSERT( is_valid() );
0880                 return;
0881             }
0882 
0883             auto_buffer temp( new_capacity_impl( size_ + n ) );
0884             temp.unchecked_push_back( cbegin(), before );
0885             std::uninitialized_fill_n( temp.end(), n, x );
0886             temp.size_ += n;
0887             temp.unchecked_push_back( before, cend() );
0888             one_sided_swap( temp );
0889             BOOST_ASSERT( is_valid() );
0890         }
0891 
0892         template< class ForwardIterator >
0893         void insert( const_iterator before,
0894                      ForwardIterator begin_arg, ForwardIterator end_arg ) // basic
0895         {
0896             typedef typename std::iterator_traits<ForwardIterator>
0897                 ::iterator_category category;
0898             insert_impl( before, begin_arg, end_arg, category() );
0899         }
0900 
0901         void pop_back()
0902         {
0903             BOOST_ASSERT( !empty() );
0904             auto_buffer_destroy( buffer_ + size_ - 1, boost::has_trivial_destructor<T>() );
0905             --size_;
0906         }
0907 
0908         void pop_back_n( size_type n )
0909         {
0910             BOOST_ASSERT( n <= size_ );
0911             if( n )
0912             {
0913                 destroy_back_n( n );
0914                 size_ -= n;
0915             }
0916         }
0917 
0918         void clear()
0919         {
0920             pop_back_n( size_ );
0921         }
0922 
0923         iterator erase( const_iterator where )
0924         {
0925             BOOST_ASSERT( !empty() );
0926             BOOST_ASSERT( cbegin() <= where );
0927             BOOST_ASSERT( cend() > where );
0928 
0929             unsigned elements = cend() - where - 1u;
0930 
0931             if( elements > 0u )
0932             {
0933                 const_iterator start = where + 1u;
0934                 std::copy( start, start + elements,
0935                            const_cast<T*>(where) );
0936             }
0937             pop_back();
0938             BOOST_ASSERT( !full() );
0939             iterator result = const_cast<T*>( where );
0940             BOOST_ASSERT( result <= end() );
0941             return result;
0942         }
0943 
0944         iterator erase( const_iterator from, const_iterator to )
0945         {
0946             BOOST_ASSERT( !(std::distance(from,to)>0) ||
0947                           !empty() );
0948             BOOST_ASSERT( cbegin() <= from );
0949             BOOST_ASSERT( cend() >= to );
0950 
0951             unsigned elements = std::distance(to,cend());
0952 
0953             if( elements > 0u )
0954             {
0955                 BOOST_ASSERT( elements > 0u );
0956                 std::copy( to, to + elements,
0957                            const_cast<T*>(from) );
0958             }
0959             pop_back_n( std::distance(from,to) );
0960             BOOST_ASSERT( !full() );
0961             iterator result = const_cast<T*>( from );
0962             BOOST_ASSERT( result <= end() );
0963             return result;
0964         }
0965 
0966         void shrink_to_fit()
0967         {
0968             if( is_on_stack() || !GrowPolicy::should_shrink(size_,members_.capacity_) )
0969                 return;
0970 
0971             reserve_impl( size_ );
0972             members_.capacity_ = (std::max)(size_type(N),members_.capacity_);
0973             BOOST_ASSERT( is_on_stack() || size_ == members_.capacity_ );
0974             BOOST_ASSERT( !is_on_stack() || size_ <= members_.capacity_ );
0975         }
0976 
0977         pointer uninitialized_grow( size_type n ) // strong
0978         {
0979             if( size_ + n > members_.capacity_ )
0980                 reserve( size_ + n );
0981 
0982             pointer res = end();
0983             size_ += n;
0984             return res;
0985         }
0986 
0987         void uninitialized_shrink( size_type n ) // nothrow
0988         {
0989             // @remark: test for wrap-around
0990             BOOST_ASSERT( size_ - n <= members_.capacity_ );
0991             size_ -= n;
0992         }
0993 
0994         void uninitialized_resize( size_type n )
0995         {
0996             if( n > size() )
0997                 uninitialized_grow( n - size() );
0998             else if( n < size() )
0999                 uninitialized_shrink( size() - n );
1000 
1001            BOOST_ASSERT( size() == n );
1002         }
1003 
1004         // nothrow  - if both buffer are on the heap, or
1005         //          - if one buffer is on the heap and one has
1006         //            'has_allocated_buffer() == false', or
1007         //          - if copy-construction cannot throw
1008         // basic    - otherwise (better guarantee impossible)
1009         // requirement: the allocator must be no-throw-swappable
1010         void swap( auto_buffer& r )
1011         {
1012             bool on_stack      = is_on_stack();
1013             bool r_on_stack    = r.is_on_stack();
1014             bool both_on_heap  = !on_stack && !r_on_stack;
1015             if( both_on_heap )
1016             {
1017                 boost::core::invoke_swap( get_allocator(), r.get_allocator() );
1018                 boost::core::invoke_swap( members_.capacity_, r.members_.capacity_ );
1019                 boost::core::invoke_swap( buffer_, r.buffer_ );
1020                 boost::core::invoke_swap( size_, r.size_ );
1021                 BOOST_ASSERT( is_valid() );
1022                 BOOST_ASSERT( r.is_valid() );
1023                 return;
1024             }
1025 
1026             BOOST_ASSERT( on_stack || r_on_stack );
1027             bool exactly_one_on_stack = (on_stack && !r_on_stack) ||
1028                                         (!on_stack && r_on_stack);
1029 
1030             //
1031             // Remark: we now know that we can copy into
1032             //         the unused stack buffer.
1033             //
1034             if( exactly_one_on_stack )
1035             {
1036                 auto_buffer* one_on_stack = on_stack ? this : &r;
1037                 auto_buffer* other        = on_stack ? &r : this;
1038                 pointer new_buffer = static_cast<T*>(other->members_.address());
1039                 copy_impl( one_on_stack->begin(), one_on_stack->end(),
1040                            new_buffer );                            // strong
1041                 one_on_stack->auto_buffer_destroy();                       // nothrow
1042                 boost::core::invoke_swap( get_allocator(), r.get_allocator() );  // assume nothrow
1043                 boost::core::invoke_swap( members_.capacity_, r.members_.capacity_ );
1044                 boost::core::invoke_swap( size_, r.size_ );
1045                 one_on_stack->buffer_ = other->buffer_;
1046                 other->buffer_        = new_buffer;
1047                 BOOST_ASSERT( other->is_on_stack() );
1048                 BOOST_ASSERT( !one_on_stack->is_on_stack() );
1049                 BOOST_ASSERT( is_valid() );
1050                 BOOST_ASSERT( r.is_valid() );
1051                 return;
1052             }
1053 
1054             BOOST_ASSERT( on_stack && r_on_stack );
1055             swap_helper( *this, r, boost::has_trivial_assign<T>() );
1056             BOOST_ASSERT( is_valid() );
1057             BOOST_ASSERT( r.is_valid() );
1058         }
1059 
1060     private:
1061         typedef boost::aligned_storage< N * sizeof(T),
1062                                         boost::alignment_of<T>::value >
1063                                storage;
1064 
1065         struct members_type : storage /* to enable EBO */
1066         {
1067             size_type capacity_;
1068 
1069             members_type( size_type capacity )
1070                : capacity_(capacity)
1071             { }
1072 
1073             void* address() const
1074             { return const_cast<storage&>(static_cast<const storage&>(*this)).address(); }
1075         };
1076 
1077         members_type members_;
1078         pointer      buffer_;
1079         size_type    size_;
1080 
1081     };
1082 
1083     template< class T, class SBP, class GP, class A >
1084     inline void swap( auto_buffer<T,SBP,GP,A>& l, auto_buffer<T,SBP,GP,A>& r )
1085     {
1086         l.swap( r );
1087     }
1088 
1089     template< class T, class SBP, class GP, class A >
1090     inline bool operator==( const auto_buffer<T,SBP,GP,A>& l,
1091                             const auto_buffer<T,SBP,GP,A>& r )
1092     {
1093         if( l.size() != r.size() )
1094             return false;
1095         return std::equal( l.begin(), l.end(), r.begin() );
1096     }
1097 
1098     template< class T, class SBP, class GP, class A >
1099     inline bool operator!=( const auto_buffer<T,SBP,GP,A>& l,
1100                             const auto_buffer<T,SBP,GP,A>& r )
1101     {
1102         return !(l == r);
1103     }
1104 
1105     template< class T, class SBP, class GP, class A >
1106     inline bool operator<( const auto_buffer<T,SBP,GP,A>& l,
1107                            const auto_buffer<T,SBP,GP,A>& r )
1108     {
1109         return std::lexicographical_compare( l.begin(), l.end(),
1110                                              r.begin(), r.end() );
1111     }
1112 
1113     template< class T, class SBP, class GP, class A >
1114     inline bool operator>( const auto_buffer<T,SBP,GP,A>& l,
1115                            const auto_buffer<T,SBP,GP,A>& r )
1116     {
1117         return (r < l);
1118     }
1119 
1120     template< class T, class SBP, class GP, class A >
1121     inline bool operator<=( const auto_buffer<T,SBP,GP,A>& l,
1122                             const auto_buffer<T,SBP,GP,A>& r )
1123     {
1124         return !(l > r);
1125     }
1126 
1127     template< class T, class SBP, class GP, class A >
1128     inline bool operator>=( const auto_buffer<T,SBP,GP,A>& l,
1129                             const auto_buffer<T,SBP,GP,A>& r )
1130     {
1131         return !(l < r);
1132     }
1133 
1134 } // namespace detail
1135 } // namespace signals2
1136 }
1137 
1138 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
1139 #pragma warning(pop)
1140 #endif
1141 
1142 #endif