diff --git a/.gitignore b/.gitignore index cdb074f8..c024df33 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ build_bench bench.bat build.bat .p4config -EASTL.psf ## CMake generated files CMakeCache.txt diff --git a/doc/EASTL FAQ.html b/doc/EASTL FAQ.html index e1871697..6ef19dcf 100644 --- a/doc/EASTL FAQ.html +++ b/doc/EASTL FAQ.html @@ -470,7 +470,7 @@


  • Smart pointers
  • Type traits
  • -

    Of these, the last two (smart pointers and type traits) do not have analogs in standard C++. With respect to the other items, EASTL provides extensions and optimizations over the equivalents in standard C++ STL.

    +

    EASTL provides extensions and optimizations over the equivalents in standard C++ STL.

    EASTL is a professional-level implementation which outperforms commercial implementations (where functionality overlaps) and is significantly easier to read and debug.

    Info.2 What uses are EASTL suitable for?

    @@ -525,6 +525,10 @@


  • utility
  • functional
  • iterator
  • +
  • string_view
  • +
  • variant
  • +
  • any
  • +
  • optional
  • EASTL additions/amendments to std STL

    Info.9 What is the legal status of EASTL?

    -

    EASTL is usable for all uses within Electronic Arts, both for internal usage and for shipping products for all platforms. All source code was written by a single EA engineer. Any externally derived code would be explicitly stated as such and approved by the legal department if such code ever gets introduced. As of EASTL v1.0, the red_black_tree.cpp file contains two functions derived from the original HP STL and have received EA legal approval for usage in any product.

    +

    EASTL is usable for all uses within Electronic Arts, both for internal usage and for shipping products for all platforms. Any externally derived code would be explicitly stated as such and approved by the legal department if such code ever gets introduced. As of EASTL v1.0, the red_black_tree.cpp file contains two functions derived from the original HP STL and have received EA legal approval for usage in any product.

    Info.10 Does EASTL deal with compiler exception handling settings?

    EASTL has automatic knowledge of the compiler's enabling/disabling of exceptions. If your compiler is set to disable exceptions, EASTL automatically detects so and executes without them. Also, you can force-enable or force-disable that setting to override the automatic behavior by #defining EASTL_EXCEPTIONS_ENABLED to 0 or 1. See EASTL's config.h for more information.

    diff --git a/doc/EASTL Modules.html b/doc/EASTL Modules.html index 828b0677..1f60cea2 100644 --- a/doc/EASTL Modules.html +++ b/doc/EASTL Modules.html @@ -275,7 +275,7 @@

    Module Behaviour

    - 1 1 -1 +n n+ @@ -290,7 +290,7 @@

    Module Behaviour

    - 1 1 -1 +n n log(n) diff --git a/doc/EASTL.natvis b/doc/EASTL.natvis index 763349e3..30f1cc2b 100644 --- a/doc/EASTL.natvis +++ b/doc/EASTL.natvis @@ -49,30 +49,32 @@ {mPair.mFirst.heap.mpBegin,s} - mPair.mFirst.heap.mpEnd - mPair.mFirst.heap.mpBegin - mPair.mFirst.heap.mpCapacity - mPair.mFirst.heap.mpBegin - mPair.mFirst.heap.mpBegin,sb + mPair.mFirst.heap.mnSize + (mPair.mFirst.heap.mnCapacity + & ~kHeapMask) + mPair.mFirst.heap.mpBegin,sb - mPair.mFirst.sso.mnSize - SSOLayout::SSO_SIZE_IN_BYTES-0 - mPair.mFirst.sso.mpBegin,sb + mPair.mFirst.sso.mnSize + SSOLayout::SSO_CAPACITY + mPair.mFirst.sso.mData,sb - mPair.mFirst.sso.mpBegin != mPair.mFirst.sso.mBuffer + !!(mPair.mFirst.sso.mnSize & kSSOMask) {mPair.mFirst.heap.mpBegin,su} - mPair.mFirst.heap.mpEnd - mPair.mFirst.heap.mpBegin - mPair.mFirst.heap.mpCapacity - mPair.mFirst.heap.mpBegin - mPair.mFirst.heap.mpBegin,sub + mPair.mFirst.heap.mnSize + (mPair.mFirst.heap.mnCapacity + & ~kHeapMask) + mPair.mFirst.heap.mpBegin,sb - mPair.mFirst.sso.mnSize - SSOLayout::SSO_SIZE_IN_BYTES-0 - mPair.mFirst.sso.mpBegin,sub + mPair.mFirst.sso.mnSize + SSOLayout::SSO_CAPACITY + mPair.mFirst.sso.mData,sb - mPair.mFirst.sso.mpBegin != mPair.mFirst.sso.mBuffer + !!(mPair.mFirst.sso.mnSize & kSSOMask) diff --git a/include/EASTL/any.h b/include/EASTL/any.h index 5075113f..35e82373 100644 --- a/include/EASTL/any.h +++ b/include/EASTL/any.h @@ -379,9 +379,10 @@ namespace eastl any(ValueType&& value, typename eastl::enable_if::type, any>::value>::type* = 0) { - static_assert(is_copy_constructible>::value, "ValueType must be copy-constructible"); - storage_handler>::construct(m_storage, eastl::forward(value)); - m_handler = &storage_handler::handler_func; + typedef decay_t DecayedValueType; + static_assert(is_copy_constructible::value, "ValueType must be copy-constructible"); + storage_handler::construct(m_storage, eastl::forward(value)); + m_handler = &storage_handler::handler_func; } template diff --git a/include/EASTL/deque.h b/include/EASTL/deque.h index 82d27ec8..69a3af4a 100644 --- a/include/EASTL/deque.h +++ b/include/EASTL/deque.h @@ -79,24 +79,15 @@ #include #include -#ifdef _MSC_VER - #pragma warning(push, 0) - #include - #include - #pragma warning(pop) -#else - #include - #include -#endif +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() #if EASTL_EXCEPTIONS_ENABLED - #ifdef _MSC_VER - #pragma warning(push, 0) - #endif + EA_DISABLE_ALL_VC_WARNINGS() #include // std::out_of_range, std::length_error. - #ifdef _MSC_VER - #pragma warning(pop) - #endif + EA_RESTORE_ALL_VC_WARNINGS() #endif #ifdef _MSC_VER @@ -273,15 +264,8 @@ namespace eastl typedef DequeIterator iterator; typedef DequeIterator const_iterator; - #if defined(_MSC_VER) && (_MSC_VER >= 1400) && (_MSC_VER <= 1600) && !EASTL_STD_CPP_ONLY // _MSC_VER of 1400 means VS2005, 1600 means VS2010. VS2012 generates errors with usage of enum:size_type. - enum : size_type { // Use Microsoft enum language extension, allowing for smaller debug symbols than using a static const. Users have been affected by this. - npos = (size_type)-1, - kMaxSize = (size_type)-2 - }; - #else - static const size_type npos = (size_type)-1; /// 'npos' means non-valid position or simply non-position. - static const size_type kMaxSize = (size_type)-2; /// -1 is reserved for 'npos'. It also happens to be slightly beneficial that kMaxSize is a value less than -1, as it helps us deal with potential integer wraparound issues. - #endif + static const size_type npos = (size_type)-1; /// 'npos' means non-valid position or simply non-position. + static const size_type kMaxSize = (size_type)-2; /// -1 is reserved for 'npos'. It also happens to be slightly beneficial that kMaxSize is a value less than -1, as it helps us deal with potential integer wraparound issues. enum { @@ -352,7 +336,6 @@ namespace eastl class deque : public DequeBase { public: - typedef DequeBase base_type; typedef deque this_type; typedef T value_type; @@ -390,10 +373,8 @@ namespace eastl explicit deque(size_type n, const allocator_type& allocator = EASTL_DEQUE_DEFAULT_ALLOCATOR); deque(size_type n, const value_type& value, const allocator_type& allocator = EASTL_DEQUE_DEFAULT_ALLOCATOR); deque(const this_type& x); - #if EASTL_MOVE_SEMANTICS_ENABLED - deque(this_type&& x); - deque(this_type&& x, const allocator_type& allocator); - #endif + deque(this_type&& x); + deque(this_type&& x, const allocator_type& allocator); deque(std::initializer_list ilist, const allocator_type& allocator = EASTL_DEQUE_DEFAULT_ALLOCATOR); template @@ -403,18 +384,16 @@ namespace eastl this_type& operator=(const this_type& x); this_type& operator=(std::initializer_list ilist); - #if EASTL_MOVE_SEMANTICS_ENABLED - this_type& operator=(this_type&& x); - #endif + this_type& operator=(this_type&& x); void swap(this_type& x); void assign(size_type n, const value_type& value); + void assign(std::initializer_list ilist); - template // It turns out that the C++ std::deque specifies a two argument + template // It turns out that the C++ std::deque specifies a two argument void assign(InputIterator first, InputIterator last); // version of assign that takes (int size, int value). These are not // iterators, so we need to do a template compiler trick to do the right thing. - void assign(std::initializer_list ilist); iterator begin() EA_NOEXCEPT; const_iterator begin() const EA_NOEXCEPT; @@ -455,54 +434,34 @@ namespace eastl void push_front(const value_type& value); reference push_front(); - #if EASTL_MOVE_SEMANTICS_ENABLED - void push_front(value_type&& value); - #endif + void push_front(value_type&& value); void push_back(const value_type& value); reference push_back(); - #if EASTL_MOVE_SEMANTICS_ENABLED - void push_back(value_type&& value); - #endif + void push_back(value_type&& value); void pop_front(); void pop_back(); - #if EASTL_MOVE_SEMANTICS_ENABLED && EASTL_VARIADIC_TEMPLATES_ENABLED - template - iterator emplace(const_iterator position, Args&&... args); - - template - void emplace_front(Args&&... args); + template + iterator emplace(const_iterator position, Args&&... args); - template - void emplace_back(Args&&... args); - #else - #if EASTL_MOVE_SEMANTICS_ENABLED - iterator emplace(const_iterator position, value_type&& value); - void emplace_front(value_type&& value); - void emplace_back(value_type&& value); - #endif + template + void emplace_front(Args&&... args); - iterator emplace(const_iterator position, const value_type& value); - void emplace_front(const value_type& value); - void emplace_back(const value_type& value); - #endif + template + void emplace_back(Args&&... args); iterator insert(const_iterator position, const value_type& value); - #if EASTL_MOVE_SEMANTICS_ENABLED - iterator insert(const_iterator position, value_type&& value); - #endif - void insert(const_iterator position, size_type n, const value_type& value); + iterator insert(const_iterator position, value_type&& value); + void insert(const_iterator position, size_type n, const value_type& value); + iterator insert(const_iterator position, std::initializer_list ilist); template void insert(const_iterator position, InputIterator first, InputIterator last); - iterator insert(const_iterator position, std::initializer_list ilist); - - iterator erase(const_iterator position); - iterator erase(const_iterator first, const_iterator last); - + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); reverse_iterator erase(reverse_iterator position); reverse_iterator erase(reverse_iterator first, reverse_iterator last); @@ -864,16 +823,15 @@ namespace eastl //if(n) //{ const size_type nNewPtrArraySize = (size_type)((n / kDequeSubarraySize) + 1); // Always have at least one, even if n is zero. - const size_type kMinPtrArraySize_ = kMinPtrArraySize; // GCC 4.0 blows up unless we define this constant. + const size_type kMinPtrArraySize_ = kMinPtrArraySize; - mnPtrArraySize = eastl::max_alt(kMinPtrArraySize_, (nNewPtrArraySize + 2)); // GCC 4.0 blows up on this. + mnPtrArraySize = eastl::max_alt(kMinPtrArraySize_, (nNewPtrArraySize + 2)); mpPtrArray = DoAllocatePtrArray(mnPtrArraySize); value_type** const pPtrArrayBegin = (mpPtrArray + ((mnPtrArraySize - nNewPtrArraySize) / 2)); // Try to place it in the middle. value_type** const pPtrArrayEnd = pPtrArrayBegin + nNewPtrArraySize; value_type** pPtrArrayCurrent = pPtrArrayBegin; - // I am sorry for the mess of #ifs and indentations below. #if EASTL_EXCEPTIONS_ENABLED try { @@ -1267,22 +1225,20 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - inline deque::deque(this_type&& x) - : base_type((size_type)0, x.mAllocator) - { - swap(x); - } + template + inline deque::deque(this_type&& x) + : base_type((size_type)0, x.mAllocator) + { + swap(x); + } - template - inline deque::deque(this_type&& x, const allocator_type& allocator) - : base_type((size_type)0, allocator) - { - swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. - } - #endif + template + inline deque::deque(this_type&& x, const allocator_type& allocator) + : base_type((size_type)0, allocator) + { + swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. + } template @@ -1346,19 +1302,17 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - inline typename deque::this_type& - deque::operator=(this_type&& x) + template + inline typename deque::this_type& + deque::operator=(this_type&& x) + { + if(this != &x) { - if(this != &x) - { - set_capacity(0); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. - swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. - } - return *this; + set_capacity(0); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. + swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. } - #endif + return *this; + } template @@ -1528,6 +1482,8 @@ namespace eastl template inline void deque::shrink_to_fit() { + this_type x(eastl::make_move_iterator(begin()), eastl::make_move_iterator(end())); + swap(x); } @@ -1696,13 +1652,11 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - void deque::push_front(value_type&& value) - { - emplace_front(eastl::move(value)); - } - #endif + template + void deque::push_front(value_type&& value) + { + emplace_front(eastl::move(value)); + } template @@ -1721,13 +1675,11 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - void deque::push_back(value_type&& value) - { - emplace_back(eastl::move(value)); - } - #endif + template + void deque::push_back(value_type&& value) + { + emplace_back(eastl::move(value)); + } template @@ -1799,400 +1751,130 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED && EASTL_VARIADIC_TEMPLATES_ENABLED - template - template - typename deque::iterator - deque::emplace(const_iterator position, Args&&... args) + template + template + typename deque::iterator + deque::emplace(const_iterator position, Args&&... args) + { + if(EASTL_UNLIKELY(position.mpCurrent == mItEnd.mpCurrent)) // If we are doing the same thing as push_back... { - if(EASTL_UNLIKELY(position.mpCurrent == mItEnd.mpCurrent)) // If we are doing the same thing as push_back... - { - emplace_back(eastl::forward(args)...); - return iterator(mItEnd, typename iterator::Decrement()); // Unfortunately, we need to make an iterator here, as the above push_back is an operation that can invalidate existing iterators. - } - else if(EASTL_UNLIKELY(position.mpCurrent == mItBegin.mpCurrent)) // If we are doing the same thing as push_front... - { - emplace_front(eastl::forward(args)...); - return mItBegin; - } - - iterator itPosition(position, typename iterator::FromConst()); - #if EASTL_USE_FORWARD_WORKAROUND - auto valueSaved = value_type(eastl::forward(args)...); //Workaround for compiler bug in VS2013 - #else - value_type valueSaved(eastl::forward(args)...); // We need to save this because value may come from within our container. It would be somewhat tedious to make a workaround that could avoid this. - #endif - const difference_type i(itPosition - mItBegin); - - #if EASTL_ASSERT_ENABLED - EASTL_ASSERT(!empty()); // The push_front and push_back calls below assume that we are non-empty. It turns out this is never called unless so. - - if(EASTL_UNLIKELY(!(validate_iterator(itPosition) & isf_valid))) - EASTL_FAIL_MSG("deque::emplace -- invalid iterator"); - #endif - - if(i < (difference_type)(size() / 2)) // Should we insert at the front or at the back? We divide the range in half. - { - emplace_front(*mItBegin); // This operation potentially invalidates all existing iterators and so we need to assign them anew relative to mItBegin below. - - itPosition = mItBegin + i; + emplace_back(eastl::forward(args)...); + return iterator(mItEnd, typename iterator::Decrement()); // Unfortunately, we need to make an iterator here, as the above push_back is an operation that can invalidate existing iterators. + } + else if(EASTL_UNLIKELY(position.mpCurrent == mItBegin.mpCurrent)) // If we are doing the same thing as push_front... + { + emplace_front(eastl::forward(args)...); + return mItBegin; + } - const iterator newPosition (itPosition, typename iterator::Increment()); - iterator oldBegin (mItBegin, typename iterator::Increment()); - const iterator oldBeginPlus1(oldBegin, typename iterator::Increment()); + iterator itPosition(position, typename iterator::FromConst()); + value_type valueSaved(eastl::forward(args)...); // We need to save this because value may come from within our container. It would be somewhat tedious to make a workaround that could avoid this. + const difference_type i(itPosition - mItBegin); - oldBegin.copy(oldBeginPlus1, newPosition, eastl::has_trivial_relocate()); - } - else - { - emplace_back(*iterator(mItEnd, typename iterator::Decrement())); + #if EASTL_ASSERT_ENABLED + EASTL_ASSERT(!empty()); // The push_front and push_back calls below assume that we are non-empty. It turns out this is never called unless so. - itPosition = mItBegin + i; + if(EASTL_UNLIKELY(!(validate_iterator(itPosition) & isf_valid))) + EASTL_FAIL_MSG("deque::emplace -- invalid iterator"); + #endif - iterator oldBack (mItEnd, typename iterator::Decrement()); - const iterator oldBackMinus1(oldBack, typename iterator::Decrement()); + if(i < (difference_type)(size() / 2)) // Should we insert at the front or at the back? We divide the range in half. + { + emplace_front(*mItBegin); // This operation potentially invalidates all existing iterators and so we need to assign them anew relative to mItBegin below. - oldBack.copy_backward(itPosition, oldBackMinus1, eastl::has_trivial_relocate()); - } + itPosition = mItBegin + i; - *itPosition = eastl::move(valueSaved); + const iterator newPosition (itPosition, typename iterator::Increment()); + iterator oldBegin (mItBegin, typename iterator::Increment()); + const iterator oldBeginPlus1(oldBegin, typename iterator::Increment()); - return itPosition; + oldBegin.copy(oldBeginPlus1, newPosition, eastl::has_trivial_relocate()); } - - template - template - void deque::emplace_front(Args&&... args) + else { - if(mItBegin.mpCurrent != mItBegin.mpBegin) // If we have room in the first subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. - ::new((void*)--mItBegin.mpCurrent) value_type(eastl::forward(args)...); // Construct in place. If args is a single arg of type value_type&& then it this will be a move construction. - else - { - // To consider: Detect if value isn't coming from within this container and handle that efficiently. - #if EASTL_USE_FORWARD_WORKAROUND - auto valueSaved = value_type(eastl::forward(args)...); //Workaround for compiler bug in VS2013 - #else - value_type valueSaved(eastl::forward(args)...); // We need to make a temporary, because args may be a value_type that comes from within our container and the operations below may change the container. But we can use move instead of copy. - #endif + emplace_back(*iterator(mItEnd, typename iterator::Decrement())); - if(mItBegin.mpCurrentArrayPtr == mpPtrArray) // If there are no more pointers in front of the current (first) one... - DoReallocPtrArray(1, kSideFront); + itPosition = mItBegin + i; - mItBegin.mpCurrentArrayPtr[-1] = DoAllocateSubarray(); + iterator oldBack (mItEnd, typename iterator::Decrement()); + const iterator oldBackMinus1(oldBack, typename iterator::Decrement()); - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - mItBegin.SetSubarray(mItBegin.mpCurrentArrayPtr - 1); - mItBegin.mpCurrent = mItBegin.mpEnd - 1; - ::new((void*)mItBegin.mpCurrent) value_type(eastl::move(valueSaved)); - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - ++mItBegin; // The exception could only occur in the new operation above, after we have incremented mItBegin. So we need to undo it. - DoFreeSubarray(mItBegin.mpCurrentArrayPtr[-1]); - throw; - } - #endif - } + oldBack.copy_backward(itPosition, oldBackMinus1, eastl::has_trivial_relocate()); } - template - template - void deque::emplace_back(Args&&... args) - { - if((mItEnd.mpCurrent + 1) != mItEnd.mpEnd) // If we have room in the last subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. - ::new((void*)mItEnd.mpCurrent++) value_type(eastl::forward(args)...); // Construct in place. If args is a single arg of type value_type&& then it this will be a move construction. - else - { - // To consider: Detect if value isn't coming from within this container and handle that efficiently. - #if EASTL_USE_FORWARD_WORKAROUND - auto valueSaved = value_type(eastl::forward(args)...); //Workaround for compiler bug in VS2013 - #else - value_type valueSaved(eastl::forward(args)...); // We need to make a temporary, because args may be a value_type that comes from within our container and the operations below may change the container. But we can use move instead of copy. - #endif - if(((mItEnd.mpCurrentArrayPtr - mpPtrArray) + 1) >= (difference_type)mnPtrArraySize) // If there are no more pointers after the current (last) one. - DoReallocPtrArray(1, kSideBack); - - mItEnd.mpCurrentArrayPtr[1] = DoAllocateSubarray(); + *itPosition = eastl::move(valueSaved); - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - ::new((void*)mItEnd.mpCurrent) value_type(eastl::move(valueSaved)); // We can move valueSaved into position. - mItEnd.SetSubarray(mItEnd.mpCurrentArrayPtr + 1); - mItEnd.mpCurrent = mItEnd.mpBegin; - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - // No need to execute '--mItEnd', as the exception could only occur in the new operation above before we set mItEnd. - DoFreeSubarray(mItEnd.mpCurrentArrayPtr[1]); - throw; - } - #endif - } - } - #else - //////////////////////////////////////////////////////////////////////////////////////////////////// - // Note: The following two sets of three functions are nearly copies of the above three functions. - // We (nearly) duplicate code here instead of trying to fold the all nine of these functions into - // three more generic functions because: 1) you can't really make just three functions but rather - // would need to break them apart somewhat, and 2) these duplications are eventually going away - // because they aren't needed with C++11 compilers, though that may not be until the year 2020. - //////////////////////////////////////////////////////////////////////////////////////////////////// - - #if EASTL_MOVE_SEMANTICS_ENABLED - template - typename deque::iterator - deque::emplace(const_iterator position, value_type&& value) - { - if(EASTL_UNLIKELY(position.mpCurrent == mItEnd.mpCurrent)) // If we are doing the same thing as push_back... - { - emplace_back(eastl::move(value)); - return iterator(mItEnd, typename iterator::Decrement()); // Unfortunately, we need to make an iterator here, as the above push_back is an operation that can invalidate existing iterators. - } - else if(EASTL_UNLIKELY(position.mpCurrent == mItBegin.mpCurrent)) // If we are doing the same thing as push_front... - { - emplace_front(eastl::move(value)); - return mItBegin; - } + return itPosition; + } - iterator itPosition(position, typename iterator::FromConst()); - value_type valueSaved(eastl::move(value)); // We need to save this because value may come from within our container. It would be somewhat tedious to make a workaround that could avoid this. - const difference_type i(itPosition - mItBegin); + template + template + void deque::emplace_front(Args&&... args) + { + if(mItBegin.mpCurrent != mItBegin.mpBegin) // If we have room in the first subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. + ::new((void*)--mItBegin.mpCurrent) value_type(eastl::forward(args)...); // Construct in place. If args is a single arg of type value_type&& then it this will be a move construction. + else + { + // To consider: Detect if value isn't coming from within this container and handle that efficiently. + value_type valueSaved(eastl::forward(args)...); // We need to make a temporary, because args may be a value_type that comes from within our container and the operations below may change the container. But we can use move instead of copy. - #if EASTL_ASSERT_ENABLED - EASTL_ASSERT(!empty()); // The push_front and push_back calls below assume that we are non-empty. It turns out this is never called unless so. + if(mItBegin.mpCurrentArrayPtr == mpPtrArray) // If there are no more pointers in front of the current (first) one... + DoReallocPtrArray(1, kSideFront); - if(EASTL_UNLIKELY(!(validate_iterator(itPosition) & isf_valid))) - EASTL_FAIL_MSG("deque::emplace -- invalid iterator"); - #endif + mItBegin.mpCurrentArrayPtr[-1] = DoAllocateSubarray(); - if(i < (difference_type)(size() / 2)) // Should we insert at the front or at the back? We divide the range in half. + #if EASTL_EXCEPTIONS_ENABLED + try { - emplace_front(*mItBegin); // This operation potentially invalidates all existing iterators and so we need to assign them anew relative to mItBegin below. - - itPosition = mItBegin + i; - - const iterator newPosition (itPosition, typename iterator::Increment()); - iterator oldBegin (mItBegin, typename iterator::Increment()); - const iterator oldBeginPlus1(oldBegin, typename iterator::Increment()); - - oldBegin.copy(oldBeginPlus1, newPosition, eastl::has_trivial_relocate()); + #endif + mItBegin.SetSubarray(mItBegin.mpCurrentArrayPtr - 1); + mItBegin.mpCurrent = mItBegin.mpEnd - 1; + ::new((void*)mItBegin.mpCurrent) value_type(eastl::move(valueSaved)); + #if EASTL_EXCEPTIONS_ENABLED } - else + catch(...) { - emplace_back(*iterator(mItEnd, typename iterator::Decrement())); - - itPosition = mItBegin + i; - - iterator oldBack (mItEnd, typename iterator::Decrement()); - const iterator oldBackMinus1(oldBack, typename iterator::Decrement()); - - oldBack.copy_backward(itPosition, oldBackMinus1, eastl::has_trivial_relocate()); + ++mItBegin; // The exception could only occur in the new operation above, after we have incremented mItBegin. So we need to undo it. + DoFreeSubarray(mItBegin.mpCurrentArrayPtr[-1]); + throw; } + #endif + } + } - *itPosition = eastl::move(valueSaved); + template + template + void deque::emplace_back(Args&&... args) + { + if((mItEnd.mpCurrent + 1) != mItEnd.mpEnd) // If we have room in the last subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. + ::new((void*)mItEnd.mpCurrent++) value_type(eastl::forward(args)...); // Construct in place. If args is a single arg of type value_type&& then it this will be a move construction. + else + { + // To consider: Detect if value isn't coming from within this container and handle that efficiently. + value_type valueSaved(eastl::forward(args)...); // We need to make a temporary, because args may be a value_type that comes from within our container and the operations below may change the container. But we can use move instead of copy. + if(((mItEnd.mpCurrentArrayPtr - mpPtrArray) + 1) >= (difference_type)mnPtrArraySize) // If there are no more pointers after the current (last) one. + DoReallocPtrArray(1, kSideBack); - return itPosition; - } + mItEnd.mpCurrentArrayPtr[1] = DoAllocateSubarray(); - template - void deque::emplace_front(value_type&& value) - { - if(mItBegin.mpCurrent != mItBegin.mpBegin) // If we have room in the first subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. - ::new((void*)--mItBegin.mpCurrent) value_type(eastl::move(value)); // Move value into position. - else + #if EASTL_EXCEPTIONS_ENABLED + try { - // To consider: Detect if value isn't coming from within this container and handle that efficiently. - value_type valueSaved(eastl::move(value)); // We need to make a temporary, because value may come from within our container and the operations below may change the container. But we can use move instead of copy. - - if(mItBegin.mpCurrentArrayPtr == mpPtrArray) // If there are no more pointers in front of the current (first) one... - DoReallocPtrArray(1, kSideFront); - - mItBegin.mpCurrentArrayPtr[-1] = DoAllocateSubarray(); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - mItBegin.SetSubarray(mItBegin.mpCurrentArrayPtr - 1); - mItBegin.mpCurrent = mItBegin.mpEnd - 1; - ::new((void*)mItBegin.mpCurrent) value_type(eastl::move(valueSaved)); - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - ++mItBegin; // The exception could only occur in the new operation above, after we have incremented mItBegin. So we need to undo it. - DoFreeSubarray(mItBegin.mpCurrentArrayPtr[-1]); - throw; - } - #endif + #endif + ::new((void*)mItEnd.mpCurrent) value_type(eastl::move(valueSaved)); // We can move valueSaved into position. + mItEnd.SetSubarray(mItEnd.mpCurrentArrayPtr + 1); + mItEnd.mpCurrent = mItEnd.mpBegin; + #if EASTL_EXCEPTIONS_ENABLED } - } - - template - void deque::emplace_back(value_type&& value) - { - if((mItEnd.mpCurrent + 1) != mItEnd.mpEnd) // If we have room in the last subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. - ::new((void*)mItEnd.mpCurrent++) value_type(eastl::move(value)); // Move value into position. - else + catch(...) { - // To consider: Detect if value isn't coming from within this container and handle that efficiently. - value_type valueSaved(eastl::move(value)); // We need to make a temporary, because value may come from within our container and the operations below may change the container. But we can use move instead of copy. - - if(((mItEnd.mpCurrentArrayPtr - mpPtrArray) + 1) >= (difference_type)mnPtrArraySize) // If there are no more pointers after the current (last) one. - DoReallocPtrArray(1, kSideBack); - - mItEnd.mpCurrentArrayPtr[1] = DoAllocateSubarray(); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - ::new((void*)mItEnd.mpCurrent) value_type(eastl::move(valueSaved)); // We can move valueSaved into position. - mItEnd.SetSubarray(mItEnd.mpCurrentArrayPtr + 1); - mItEnd.mpCurrent = mItEnd.mpBegin; - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - // No need to execute '--mItEnd', as the exception could only occur in the new operation above before we set mItEnd. - DoFreeSubarray(mItEnd.mpCurrentArrayPtr[1]); - throw; - } - #endif + // No need to execute '--mItEnd', as the exception could only occur in the new operation above before we set mItEnd. + DoFreeSubarray(mItEnd.mpCurrentArrayPtr[1]); + throw; } - } - #endif - - template - typename deque::iterator - deque::emplace(const_iterator position, const value_type& value) - { - if(EASTL_UNLIKELY(position.mpCurrent == mItEnd.mpCurrent)) // If we are doing the same thing as push_back... - { - emplace_back(value); - return iterator(mItEnd, typename iterator::Decrement()); // Unfortunately, we need to make an iterator here, as the above push_back is an operation that can invalidate existing iterators. - } - else if(EASTL_UNLIKELY(position.mpCurrent == mItBegin.mpCurrent)) // If we are doing the same thing as push_front... - { - emplace_front(value); - return mItBegin; - } - - iterator itPosition(position, typename iterator::FromConst()); - value_type valueSaved(value); // We need to save this because value may come from within our container. It would be somewhat tedious to make a workaround that could avoid this. - const difference_type i(itPosition - mItBegin); - - #if EASTL_ASSERT_ENABLED - EASTL_ASSERT(!empty()); // The push_front and push_back calls below assume that we are non-empty. It turns out this is never called unless so. - - if(EASTL_UNLIKELY(!(validate_iterator(itPosition) & isf_valid))) - EASTL_FAIL_MSG("deque::emplace -- invalid iterator"); #endif - - if(i < (difference_type)(size() / 2)) // Should we insert at the front or at the back? We divide the range in half. - { - emplace_front(*mItBegin); // This operation potentially invalidates all existing iterators and so we need to assign them anew relative to mItBegin below. - - itPosition = mItBegin + i; - - const iterator newPosition (itPosition, typename iterator::Increment()); - iterator oldBegin (mItBegin, typename iterator::Increment()); - const iterator oldBeginPlus1(oldBegin, typename iterator::Increment()); - - oldBegin.copy(oldBeginPlus1, newPosition, eastl::has_trivial_relocate()); - } - else - { - emplace_back(*iterator(mItEnd, typename iterator::Decrement())); - - itPosition = mItBegin + i; - - iterator oldBack (mItEnd, typename iterator::Decrement()); - const iterator oldBackMinus1(oldBack, typename iterator::Decrement()); - - oldBack.copy_backward(itPosition, oldBackMinus1, eastl::has_trivial_relocate()); - } - - *itPosition = eastl::move(valueSaved); - - return itPosition; - } - - template - void deque::emplace_front(const value_type& value) - { - if(mItBegin.mpCurrent != mItBegin.mpBegin) // If we have room in the first subarray... - ::new((void*)--mItBegin.mpCurrent) value_type(value); // We hope that usually this 'new' pathway gets executed, as it is slightly faster. - else // Note that in this 'else' case we create a temporary, which is less desirable. - { - // To consider: Detect if value isn't coming from within this container and handle that efficiently. - value_type valueSaved(value); // We need to make a temporary, because value may come from within our container and the operations below may change the container. - - if(mItBegin.mpCurrentArrayPtr == mpPtrArray) // If there are no more pointers in front of the current (first) one... - DoReallocPtrArray(1, kSideFront); - - mItBegin.mpCurrentArrayPtr[-1] = DoAllocateSubarray(); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - mItBegin.SetSubarray(mItBegin.mpCurrentArrayPtr - 1); - mItBegin.mpCurrent = mItBegin.mpEnd - 1; - ::new((void*)mItBegin.mpCurrent) value_type(eastl::move(valueSaved)); // We can move valueSaved into position. - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - ++mItBegin; // The exception could only occur in the new operation above, after we have incremented mItBegin. So we need to undo it. - DoFreeSubarray(mItBegin.mpCurrentArrayPtr[-1]); - throw; - } - #endif - } } - - template - void deque::emplace_back(const value_type& value) - { - if((mItEnd.mpCurrent + 1) != mItEnd.mpEnd) // If we have room in the last subarray... - ::new((void*)mItEnd.mpCurrent++) value_type(value); - else - { - // To consider: Detect if value isn't coming from within this container and handle that efficiently. - value_type valueSaved(value); // We need to make a temporary, because value may come from within our container and the operations below may change the container. - - if(((mItEnd.mpCurrentArrayPtr - mpPtrArray) + 1) >= (difference_type)mnPtrArraySize) // If there are no more pointers after the current (last) one. - DoReallocPtrArray(1, kSideBack); - - mItEnd.mpCurrentArrayPtr[1] = DoAllocateSubarray(); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - ::new((void*)mItEnd.mpCurrent) value_type(eastl::move(valueSaved)); // We can move valueSaved into position. - mItEnd.SetSubarray(mItEnd.mpCurrentArrayPtr + 1); - mItEnd.mpCurrent = mItEnd.mpBegin; - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - // No need to execute '--mItEnd', as the exception could only occur in the new operation above before we set mItEnd. - DoFreeSubarray(mItEnd.mpCurrentArrayPtr[1]); - throw; - } - #endif - } - } - #endif + } template @@ -2203,14 +1885,12 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - typename deque::iterator - deque::insert(const_iterator position, value_type&& value) - { - return emplace(position, eastl::move(value)); - } - #endif + template + typename deque::iterator + deque::insert(const_iterator position, value_type&& value) + { + return emplace(position, eastl::move(value)); + } template diff --git a/include/EASTL/fixed_hash_map.h b/include/EASTL/fixed_hash_map.h index 677d83e5..af6663dd 100644 --- a/include/EASTL/fixed_hash_map.h +++ b/include/EASTL/fixed_hash_map.h @@ -96,6 +96,7 @@ namespace eastl enum { kMaxSize = nodeCount }; using base_type::mAllocator; + using base_type::clear; protected: node_type** mBucketBuffer[bucketCount + 1]; // '+1' because the hash table needs a null terminating bucket. @@ -117,17 +118,13 @@ namespace eastl const Predicate& predicate = Predicate()); fixed_hash_map(const this_type& x); - #if EASTL_MOVE_SEMANTICS_ENABLED - fixed_hash_map(this_type&& x); - fixed_hash_map(this_type&& x, const overflow_allocator_type& overflowAllocator); - #endif + fixed_hash_map(this_type&& x); + fixed_hash_map(this_type&& x, const overflow_allocator_type& overflowAllocator); fixed_hash_map(std::initializer_list ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_MAP_DEFAULT_ALLOCATOR); this_type& operator=(const this_type& x); this_type& operator=(std::initializer_list ilist); - #if EASTL_MOVE_SEMANTICS_ENABLED - this_type& operator=(this_type&& x); - #endif + this_type& operator=(this_type&& x); void swap(this_type& x); @@ -138,6 +135,8 @@ namespace eastl const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT; overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT; void set_overflow_allocator(const overflow_allocator_type& allocator); + + void clear(bool clearBuckets); }; // fixed_hash_map @@ -188,6 +187,7 @@ namespace eastl enum { kMaxSize = nodeCount }; using base_type::mAllocator; + using base_type::clear; protected: node_type** mBucketBuffer[bucketCount + 1]; // '+1' because the hash table needs a null terminating bucket. @@ -209,17 +209,13 @@ namespace eastl const Predicate& predicate = Predicate()); fixed_hash_multimap(const this_type& x); - #if EASTL_MOVE_SEMANTICS_ENABLED - fixed_hash_multimap(this_type&& x); - fixed_hash_multimap(this_type&& x, const overflow_allocator_type& overflowAllocator); - #endif + fixed_hash_multimap(this_type&& x); + fixed_hash_multimap(this_type&& x, const overflow_allocator_type& overflowAllocator); fixed_hash_multimap(std::initializer_list ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_MULTIMAP_DEFAULT_ALLOCATOR); this_type& operator=(const this_type& x); this_type& operator=(std::initializer_list ilist); - #if EASTL_MOVE_SEMANTICS_ENABLED - this_type& operator=(this_type&& x); - #endif + this_type& operator=(this_type&& x); void swap(this_type& x); @@ -230,6 +226,8 @@ namespace eastl const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT; overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT; void set_overflow_allocator(const overflow_allocator_type& allocator); + + void clear(bool clearBuckets); }; // fixed_hash_multimap @@ -346,52 +344,50 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - inline fixed_hash_map:: - fixed_hash_map(this_type&& x) - : base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(), - x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer)) - { - // This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here. - mAllocator.copy_overflow_allocator(x.mAllocator); + template + inline fixed_hash_map:: + fixed_hash_map(this_type&& x) + : base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(), + x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer)) + { + // This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here. + mAllocator.copy_overflow_allocator(x.mAllocator); - #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); - #endif + #if EASTL_NAME_ENABLED + mAllocator.set_name(x.mAllocator.get_name()); + #endif - EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); + EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) - base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + if(!bEnableOverflow) + base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. - mAllocator.reset(mNodeBuffer); - base_type::insert(x.begin(), x.end()); - } + mAllocator.reset(mNodeBuffer); + base_type::insert(x.begin(), x.end()); + } - template - inline fixed_hash_map:: - fixed_hash_map(this_type&& x, const overflow_allocator_type& overflowAllocator) - : base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(), - x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator)) - { - // This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here. - mAllocator.copy_overflow_allocator(x.mAllocator); + template + inline fixed_hash_map:: + fixed_hash_map(this_type&& x, const overflow_allocator_type& overflowAllocator) + : base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(), + x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator)) + { + // This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here. + mAllocator.copy_overflow_allocator(x.mAllocator); - #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); - #endif + #if EASTL_NAME_ENABLED + mAllocator.set_name(x.mAllocator.get_name()); + #endif - EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); + EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) - base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + if(!bEnableOverflow) + base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. - mAllocator.reset(mNodeBuffer); - base_type::insert(x.begin(), x.end()); - } - #endif + mAllocator.reset(mNodeBuffer); + base_type::insert(x.begin(), x.end()); + } template @@ -423,15 +419,13 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - inline typename fixed_hash_map::this_type& - fixed_hash_map::operator=(this_type&& x) - { - base_type::operator=(x); - return *this; - } - #endif + template + inline typename fixed_hash_map::this_type& + fixed_hash_map::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } template @@ -457,7 +451,9 @@ namespace eastl inline void fixed_hash_map:: reset_lose_memory() { - base_type::reset_lose_memory(); + base_type::mnBucketCount = (size_type)base_type::mRehashPolicy.GetPrevBucketCount((uint32_t)bucketCount); + base_type::mnElementCount = 0; + base_type::mRehashPolicy.mnNextResize = 0; base_type::get_allocator().reset(mNodeBuffer); } @@ -494,6 +490,21 @@ namespace eastl } + template + inline void fixed_hash_map:: + clear(bool clearBuckets) + { + base_type::DoFreeNodes(base_type::mpBucketArray, base_type::mnBucketCount); + if(clearBuckets) + { + base_type::DoFreeBuckets(base_type::mpBucketArray, base_type::mnBucketCount); + reset_lose_memory(); + } + base_type::mpBucketArray = (node_type**)mBucketBuffer; + base_type::mnElementCount = 0; + } + + /////////////////////////////////////////////////////////////////////// // global operators /////////////////////////////////////////////////////////////////////// @@ -618,52 +629,50 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - inline fixed_hash_multimap:: - fixed_hash_multimap(this_type&& x) - : base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(), - x.equal_function(),fixed_allocator_type(NULL, mBucketBuffer)) - { - // This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here. - mAllocator.copy_overflow_allocator(x.mAllocator); + template + inline fixed_hash_multimap:: + fixed_hash_multimap(this_type&& x) + : base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(), + x.equal_function(),fixed_allocator_type(NULL, mBucketBuffer)) + { + // This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here. + mAllocator.copy_overflow_allocator(x.mAllocator); - #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); - #endif + #if EASTL_NAME_ENABLED + mAllocator.set_name(x.mAllocator.get_name()); + #endif - EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); + EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) - base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + if(!bEnableOverflow) + base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. - mAllocator.reset(mNodeBuffer); - base_type::insert(x.begin(), x.end()); - } + mAllocator.reset(mNodeBuffer); + base_type::insert(x.begin(), x.end()); + } - template - inline fixed_hash_multimap:: - fixed_hash_multimap(this_type&& x, const overflow_allocator_type& overflowAllocator) - : base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(), - x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator)) - { - // This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here. - mAllocator.copy_overflow_allocator(x.mAllocator); + template + inline fixed_hash_multimap:: + fixed_hash_multimap(this_type&& x, const overflow_allocator_type& overflowAllocator) + : base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(), + x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator)) + { + // This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here. + mAllocator.copy_overflow_allocator(x.mAllocator); - #if EASTL_NAME_ENABLED - mAllocator.set_name(x.mAllocator.get_name()); - #endif + #if EASTL_NAME_ENABLED + mAllocator.set_name(x.mAllocator.get_name()); + #endif - EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); + EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2)); - if(!bEnableOverflow) - base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. + if(!bEnableOverflow) + base_type::set_max_load_factor(10000.f); // Set it so that we will never resize. - mAllocator.reset(mNodeBuffer); - base_type::insert(x.begin(), x.end()); - } - #endif + mAllocator.reset(mNodeBuffer); + base_type::insert(x.begin(), x.end()); + } template @@ -695,15 +704,13 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - template - inline typename fixed_hash_multimap::this_type& - fixed_hash_multimap::operator=(this_type&& x) - { - base_type::operator=(x); - return *this; - } - #endif + template + inline typename fixed_hash_multimap::this_type& + fixed_hash_multimap::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } template @@ -729,7 +736,9 @@ namespace eastl inline void fixed_hash_multimap:: reset_lose_memory() { - base_type::reset_lose_memory(); + base_type::mnBucketCount = (size_type)base_type::mRehashPolicy.GetPrevBucketCount((uint32_t)bucketCount); + base_type::mnElementCount = 0; + base_type::mRehashPolicy.mnNextResize = 0; base_type::get_allocator().reset(mNodeBuffer); } @@ -765,6 +774,21 @@ namespace eastl } + template + inline void fixed_hash_multimap:: + clear(bool clearBuckets) + { + base_type::DoFreeNodes(base_type::mpBucketArray, base_type::mnBucketCount); + if(clearBuckets) + { + base_type::DoFreeBuckets(base_type::mpBucketArray, base_type::mnBucketCount); + reset_lose_memory(); + } + base_type::mpBucketArray = (node_type**)mBucketBuffer; + base_type::mnElementCount = 0; + } + + /////////////////////////////////////////////////////////////////////// // global operators /////////////////////////////////////////////////////////////////////// diff --git a/include/EASTL/fixed_string.h b/include/EASTL/fixed_string.h index f4b8665f..3e06a8d2 100644 --- a/include/EASTL/fixed_string.h +++ b/include/EASTL/fixed_string.h @@ -3,7 +3,7 @@ ///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -// This file implements a string which uses a fixed size memory pool. +// This file implements a string which uses a fixed size memory pool. // The bEnableOverflow template parameter allows the container to resort to // heap allocations if the memory pool is exhausted. /////////////////////////////////////////////////////////////////////////////// @@ -27,7 +27,7 @@ namespace eastl /// /// Defines a default container name in the absence of a user-provided name. /// In the case of fixed-size containers, the allocator name always refers - /// to overflow allocations. + /// to overflow allocations. /// #ifndef EASTL_FIXED_STRING_DEFAULT_NAME #define EASTL_FIXED_STRING_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_string" // Unless the user overrides something, this is "EASTL fixed_string". @@ -37,15 +37,15 @@ namespace eastl /// fixed_string /// - /// A fixed_string with bEnableOverflow == true is identical to a regular + /// A fixed_string with bEnableOverflow == true is identical to a regular /// string in terms of its behavior. All the expectations of regular string /// apply to it and no additional expectations come from it. When bEnableOverflow - /// is false, fixed_string behaves like regular string with the exception that + /// is false, fixed_string behaves like regular string with the exception that /// its capacity can never increase. All operations you do on such a fixed_string - /// which require a capacity increase will result in undefined behavior or an + /// which require a capacity increase will result in undefined behavior or an /// C++ allocation exception, depending on the configuration of EASTL. /// - /// Note: The nodeCount value is the amount of characters to allocate, which needs to + /// Note: The nodeCount value is the amount of characters to allocate, which needs to /// take into account a terminating zero. Thus if you want to store strings with a strlen /// of 30, the nodeCount value must be at least 31. /// @@ -55,12 +55,12 @@ namespace eastl /// bEnableOverflow Whether or not we should use the overflow heap if our object pool is exhausted. /// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap. /// - /// Notes: + /// Notes: /// The nodeCount value must be at least 2, one for a character and one for a terminating 0. /// - /// As of this writing, the string class necessarily reallocates when an insert of + /// As of this writing, the string class necessarily reallocates when an insert of /// self is done into self. As a result, the fixed_string class doesn't support - /// inserting self into self unless the bEnableOverflow template parameter is true. + /// inserting self into self unless the bEnableOverflow template parameter is true. /// /// Example usage: /// fixed_string fixedString("hello world"); // Can hold up to a strlen of 128. @@ -74,7 +74,7 @@ namespace eastl class fixed_string : public basic_string > { public: - typedef fixed_vector_allocator fixed_allocator_type; typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; typedef basic_string base_type; @@ -92,6 +92,7 @@ namespace eastl using base_type::append; using base_type::resize; using base_type::clear; + using base_type::capacity; using base_type::size; using base_type::sprintf_va_list; using base_type::DoAllocate; @@ -114,7 +115,7 @@ namespace eastl fixed_string(const value_type* p); fixed_string(size_type n, const value_type& value); fixed_string(const this_type& x); - fixed_string(const this_type& x, const overflow_allocator_type& overflowAllocator); + fixed_string(const this_type& x, const overflow_allocator_type& overflowAllocator); fixed_string(const base_type& x); fixed_string(const value_type* pBegin, const value_type* pEnd); fixed_string(CtorDoNotInitialize, size_type n); @@ -141,7 +142,7 @@ namespace eastl void set_capacity(size_type n); void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. size_type max_size() const; - bool full() const; // Returns true if the fixed space has been fully allocated. Note that if overflow is enabled, the container size can be greater than nodeCount but full() could return true because the fixed space may have a recently freed slot. + bool full() const; // Returns true if the fixed space has been fully allocated. Note that if overflow is enabled, the container size can be greater than nodeCount but full() could return true because the fixed space may have a recently freed slot. bool has_overflowed() const; // Returns true if the allocations spilled over into the overflow allocator. Meaningful only if overflow is enabled. bool can_overflow() const; // Returns the value of the bEnableOverflow template parameter. @@ -173,9 +174,11 @@ namespace eastl get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; } @@ -187,9 +190,11 @@ namespace eastl get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; } @@ -203,9 +208,11 @@ namespace eastl get_allocator().set_name(x.get_allocator().get_name()); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(x); } @@ -221,9 +228,11 @@ namespace eastl get_allocator().set_name(x.get_allocator().get_name()); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(x); } @@ -237,9 +246,11 @@ namespace eastl get_allocator().set_name(x.get_allocator().get_name()); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(x); } @@ -253,9 +264,11 @@ namespace eastl get_allocator().set_name(x.get_allocator().get_name()); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(x, position, n); } @@ -269,9 +282,11 @@ namespace eastl get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(p, n); } @@ -281,14 +296,16 @@ namespace eastl inline fixed_string::fixed_string(const value_type* p) : base_type(fixed_allocator_type(mBuffer.buffer)) { - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; - #if EASTL_NAME_ENABLED get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; + append(p); // There better be enough space to hold the assigned string. } @@ -301,9 +318,11 @@ namespace eastl get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(n, value); // There better be enough space to hold the assigned string. } @@ -317,9 +336,11 @@ namespace eastl get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(pBegin, pEnd); } @@ -333,18 +354,19 @@ namespace eastl get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif - internalLayout().heap.mpBegin = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); - if((internalLayout().heap.mpBegin + n) < internalLayout().heap.mpCapacity) + if(n < nodeCount) { - internalLayout().heap.mpEnd = internalLayout().heap.mpBegin + n; - *internalLayout().heap.mpEnd = 0; + internalLayout().SetHeapSize(n); + *internalLayout().HeapEndPtr() = 0; } else { - internalLayout().heap.mpEnd = mArray; - *internalLayout().heap.mpEnd = 0; + internalLayout().SetHeapSize(0); + *internalLayout().HeapEndPtr() = 0; + resize(n); } } @@ -358,9 +380,10 @@ namespace eastl get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + *internalLayout().HeapBeginPtr() = 0; va_list arguments; va_start(arguments, pFormat); @@ -377,9 +400,11 @@ namespace eastl get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(ilist.begin(), ilist.end()); } @@ -395,9 +420,11 @@ namespace eastl get_allocator().set_name(x.get_allocator().get_name()); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(x); // Let x destruct its own items. } @@ -411,9 +438,11 @@ namespace eastl get_allocator().set_name(x.get_allocator().get_name()); #endif - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; - *internalLayout().heap.mpBegin = 0; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapCapacity(nodeCount - 1); + internalLayout().SetHeapSize(0); + + *internalLayout().HeapBeginPtr() = 0; append(x); // Let x destruct its own items. } @@ -437,7 +466,7 @@ namespace eastl return *this; } - + template inline typename fixed_string:: this_type& fixed_string::operator=(const base_type& x) @@ -460,7 +489,7 @@ namespace eastl inline typename fixed_string:: this_type& fixed_string::operator=(const value_type* p) { - if(internalLayout().heap.mpBegin != p) + if(internalLayout().HeapBeginPtr() != p) { clear(); append(p); @@ -522,8 +551,8 @@ namespace eastl template inline void fixed_string::set_capacity(size_type n) { - const size_type nPrevSize = (size_type)(internalLayout().heap.mpEnd - internalLayout().heap.mpBegin); - const size_type nPrevCapacity = (size_type)((internalLayout().heap.mpCapacity - internalLayout().heap.mpBegin) - 1); // -1 because the terminating 0 isn't included in the calculated capacity value. + const size_type nPrevSize = internalLayout().GetSize(); + const size_type nPrevCapacity = capacity(); if(n == npos) // If the user means to set the capacity so that it equals the size (i.e. free excess capacity)... n = nPrevSize; @@ -532,17 +561,17 @@ namespace eastl { const size_type allocSize = (n + 1); // +1 because the terminating 0 isn't included in the supplied capacity value. So now n refers the amount of memory we need. - if(can_overflow() && (((uintptr_t)internalLayout().heap.mpBegin != (uintptr_t)mBuffer.buffer) || (allocSize > kMaxSize))) // If we are or would be using dynamically allocated memory instead of our fixed-size member buffer... + if(can_overflow() && (((uintptr_t)internalLayout().HeapBeginPtr() != (uintptr_t)mBuffer.buffer) || (allocSize > kMaxSize))) // If we are or would be using dynamically allocated memory instead of our fixed-size member buffer... { T* const pNewData = (allocSize <= kMaxSize) ? (T*)&mBuffer.buffer[0] : DoAllocate(allocSize); - T* const pCopyEnd = (n < nPrevSize) ? (internalLayout().heap.mpBegin + n) : internalLayout().heap.mpEnd; - CharStringUninitializedCopy(internalLayout().heap.mpBegin, pCopyEnd, pNewData); // Copy [internalLayout().heap.mpBegin, pCopyEnd) to pNewData. - if((uintptr_t)internalLayout().heap.mpBegin != (uintptr_t)mBuffer.buffer) - DoFree(internalLayout().heap.mpBegin, (size_type)(internalLayout().heap.mpCapacity - internalLayout().heap.mpBegin)); - - internalLayout().heap.mpEnd = pNewData + (pCopyEnd - internalLayout().heap.mpBegin); - internalLayout().heap.mpBegin = pNewData; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + allocSize; + T* const pCopyEnd = (n < nPrevSize) ? (internalLayout().HeapBeginPtr() + n) : internalLayout().HeapEndPtr(); + CharStringUninitializedCopy(internalLayout().HeapBeginPtr(), pCopyEnd, pNewData); // Copy [internalLayout().heap.mpBegin, pCopyEnd) to pNewData. + if((uintptr_t)internalLayout().HeapBeginPtr() != (uintptr_t)mBuffer.buffer) + DoFree(internalLayout().HeapBeginPtr(), internalLayout().GetHeapCapacity() + 1); + + internalLayout().SetHeapSize((size_type)(pCopyEnd - internalLayout().HeapBeginPtr())); + internalLayout().SetHeapBeginPtr(pNewData); + internalLayout().SetHeapCapacity(allocSize - 1); } // Else the new capacity would be within our fixed buffer. else if(n < nPrevSize) // If the newly requested capacity is less than our size, we do what vector::set_capacity does and resize, even though we actually aren't reducing the capacity. resize(n); @@ -553,8 +582,9 @@ namespace eastl template inline void fixed_string::reset_lose_memory() { - internalLayout().heap.mpBegin = internalLayout().heap.mpEnd = mArray; - internalLayout().heap.mpCapacity = internalLayout().heap.mpBegin + nodeCount; + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapSize(0); + internalLayout().SetHeapCapacity(nodeCount - 1); } @@ -569,9 +599,9 @@ namespace eastl template inline bool fixed_string::full() const { - // If size >= capacity, then we are definitely full. + // If size >= capacity, then we are definitely full. // Also, if our size is smaller but we've switched away from mBuffer due to a previous overflow, then we are considered full. - return ((size_t)(internalLayout().heap.mpEnd - internalLayout().heap.mpBegin) >= kMaxSize) || ((void*)internalLayout().heap.mpBegin != (void*)mBuffer.buffer); + return ((size_t)(internalLayout().HeapEndPtr() - internalLayout().HeapBeginPtr()) >= kMaxSize) || ((void*)internalLayout().HeapBeginPtr() != (void*)mBuffer.buffer); } @@ -582,7 +612,7 @@ namespace eastl // down to a small size where the fixed buffer could take over ownership of the data again. // The only simple fix for this is to take on another member variable which tracks whether this overflow // has occurred at some point in the past. - return ((void*)internalLayout().heap.mpBegin != (void*)mBuffer.buffer); + return ((void*)internalLayout().HeapBeginPtr() != (void*)mBuffer.buffer); } @@ -598,11 +628,12 @@ namespace eastl this_type fixed_string::substr(size_type position, size_type n) const { #if EASTL_STRING_OPT_RANGE_ERRORS - if(position > (size_type)(internalLayout().heap.mpEnd - internalLayout().heap.mpBegin)) + if(position > internalLayout().GetSize()) base_type::ThrowRangeException(); #endif - return fixed_string(internalLayout().heap.mpBegin + position, internalLayout().heap.mpBegin + position + eastl::min_alt(n, (size_type)(internalLayout().heap.mpEnd - internalLayout().heap.mpBegin) - position)); + return fixed_string(internalLayout().HeapBeginPtr() + position, + internalLayout().HeapBeginPtr() + position + eastl::min_alt(n, internalLayout().GetSize() - position)); } @@ -612,7 +643,7 @@ namespace eastl { const size_type nLength = size(); if(n < nLength) - return fixed_string(internalLayout().heap.mpBegin, internalLayout().heap.mpBegin + n); + return fixed_string(internalLayout().HeapBeginPtr(), internalLayout().HeapBeginPtr() + n); return *this; } @@ -623,7 +654,7 @@ namespace eastl { const size_type nLength = size(); if(n < nLength) - return fixed_string(internalLayout().heap.mpEnd - n, internalLayout().heap.mpEnd); + return fixed_string(internalLayout().HeapEndPtr() - n, internalLayout().HeapEndPtr()); return *this; } @@ -645,7 +676,7 @@ namespace eastl template - inline void + inline void fixed_string::set_overflow_allocator(const overflow_allocator_type& allocator) { get_allocator().set_overflow_allocator(allocator); @@ -659,10 +690,10 @@ namespace eastl // Operator + template - fixed_string operator+(const fixed_string& a, + fixed_string operator+(const fixed_string& a, const fixed_string& b) { - // We have a problem here because need to return an fixed_string by value. This will typically result in it + // We have a problem here because need to return an fixed_string by value. This will typically result in it // using stack space equal to its size. That size may be too large to be workable. typedef fixed_string this_type; @@ -674,7 +705,7 @@ namespace eastl template - fixed_string operator+(const typename fixed_string::value_type* p, + fixed_string operator+(const typename fixed_string::value_type* p, const fixed_string& b) { typedef fixed_string this_type; @@ -688,7 +719,7 @@ namespace eastl template - fixed_string operator+(typename fixed_string::value_type c, + fixed_string operator+(typename fixed_string::value_type c, const fixed_string& b) { typedef fixed_string this_type; @@ -701,7 +732,7 @@ namespace eastl template - fixed_string operator+(const fixed_string& a, + fixed_string operator+(const fixed_string& a, const typename fixed_string::value_type* p) { typedef fixed_string this_type; @@ -715,7 +746,7 @@ namespace eastl template - fixed_string operator+(const fixed_string& a, + fixed_string operator+(const fixed_string& a, typename fixed_string::value_type c) { typedef fixed_string this_type; @@ -729,7 +760,7 @@ namespace eastl #if EASTL_MOVE_SEMANTICS_ENABLED template - fixed_string operator+(fixed_string&& a, + fixed_string operator+(fixed_string&& a, fixed_string&& b) { a.append(b); // Using an rvalue by name results in it becoming an lvalue. @@ -737,7 +768,7 @@ namespace eastl } template - fixed_string operator+(fixed_string&& a, + fixed_string operator+(fixed_string&& a, const fixed_string& b) { a.append(b); @@ -745,7 +776,7 @@ namespace eastl } template - fixed_string operator+(const typename fixed_string::value_type* p, + fixed_string operator+(const typename fixed_string::value_type* p, fixed_string&& b) { b.insert(0, p); @@ -753,7 +784,7 @@ namespace eastl } template - fixed_string operator+(fixed_string&& a, + fixed_string operator+(fixed_string&& a, const typename fixed_string::value_type* p) { a.append(p); @@ -761,7 +792,7 @@ namespace eastl } template - fixed_string operator+(fixed_string&& a, + fixed_string operator+(fixed_string&& a, typename fixed_string::value_type c) { a.push_back(c); @@ -773,7 +804,7 @@ namespace eastl // operator ==, !=, <, >, <=, >= come from the string implementations. template - inline void swap(fixed_string& a, + inline void swap(fixed_string& a, fixed_string& b) { // Fixed containers use a special swap that can deal with excessively large buffers. @@ -784,16 +815,3 @@ namespace eastl } // namespace eastl #endif // Header include guard - - - - - - - - - - - - - diff --git a/include/EASTL/fixed_substring.h b/include/EASTL/fixed_substring.h index 47a4c4f2..033052f4 100644 --- a/include/EASTL/fixed_substring.h +++ b/include/EASTL/fixed_substring.h @@ -20,29 +20,29 @@ namespace eastl /// fixed_substring /// - /// Implements a string which is a reference to a segment of characters. + /// Implements a string which is a reference to a segment of characters. /// This class is efficient because it allocates no memory and copies no - /// memory during construction and assignment, but rather refers directly - /// to the segment of chracters. A common use of this is to have a + /// memory during construction and assignment, but rather refers directly + /// to the segment of chracters. A common use of this is to have a /// fixed_substring efficiently refer to a substring within another string. /// /// You cannot directly resize a fixed_substring (e.g. via resize, insert, - /// append, erase), but you can assign a different substring to it. + /// append, erase), but you can assign a different substring to it. /// You can modify the characters within a substring in place. - /// As of this writing, in the name of being lean and simple it is the + /// As of this writing, in the name of being lean and simple it is the /// user's responsibility to not call unsupported resizing functions /// such as those listed above. A detailed listing of the functions which /// are not supported is given below in the class declaration. /// - /// The c_str function doesn't act as one might hope, as it simply + /// The c_str function doesn't act as one might hope, as it simply /// returns the pointer to the beginning of the string segment and the - /// 0-terminator may be beyond the end of the segment. If you want to - /// always be able to use c_str as expected, use the fixed string solution + /// 0-terminator may be beyond the end of the segment. If you want to + /// always be able to use c_str as expected, use the fixed string solution /// we describe below. /// /// Another use of fixed_substring is to provide C++ string-like functionality /// with a C character array. This allows you to work on a C character array - /// as if it were a C++ string as opposed using the C string API. Thus you + /// as if it were a C++ string as opposed using the C string API. Thus you /// can do this: /// /// void DoSomethingForUser(char* timeStr, size_t timeStrCapacity) @@ -67,14 +67,14 @@ namespace eastl /// are going to have a limit as to their maximum size. /// /// Notes: - /// As of this writing, the string class necessarily reallocates when - /// an insert of self is done into self. As a result, the fixed_substring - /// class doesn't support inserting self into self. + /// As of this writing, the string class necessarily reallocates when + /// an insert of self is done into self. As a result, the fixed_substring + /// class doesn't support inserting self into self. /// /// Example usage: /// basic_string str("hello world"); /// fixed_substring sub(str, 2, 5); // sub == "llo w" - /// + /// template class fixed_substring : public basic_string { @@ -92,6 +92,16 @@ namespace eastl using base_type::internalLayout; using base_type::get_allocator; + private: + + void SetInternalHeapLayout(value_type* pBeginPtr, size_type nSize, size_type nCap) + { + internalLayout().SetHeapBeginPtr(pBeginPtr); + internalLayout().SetHeapSize(nSize); + internalLayout().SetHeapCapacity(nCap); + } + + public: fixed_substring() : base_type() @@ -160,46 +170,36 @@ namespace eastl this_type& assign(const base_type& x) { - // By design, we need to cast away const-ness here. - internalLayout().heap.mpBegin = const_cast(x.data()); - internalLayout().heap.mpEnd = internalLayout().heap.mpBegin + x.size(); - internalLayout().heap.mpCapacity = internalLayout().heap.mpEnd; + // By design, we need to cast away const-ness here. + SetInternalHeapLayout(const_cast(x.data()), x.size(), x.size()); return *this; } this_type& assign(const base_type& x, size_type position, size_type n) { - // By design, we need to cast away const-ness here. - internalLayout().heap.mpBegin = const_cast(x.data()) + position; - internalLayout().heap.mpEnd = internalLayout().heap.mpBegin + n; - internalLayout().heap.mpCapacity = internalLayout().heap.mpEnd; + // By design, we need to cast away const-ness here. + SetInternalHeapLayout(const_cast(x.data()) + position, n, n); return *this; } this_type& assign(const value_type* p, size_type n) { - // By design, we need to cast away const-ness here. - internalLayout().heap.mpBegin = const_cast(p); - internalLayout().heap.mpEnd = internalLayout().heap.mpBegin + n; - internalLayout().heap.mpCapacity = internalLayout().heap.mpEnd; + // By design, we need to cast away const-ness here. + SetInternalHeapLayout(const_cast(p), n, n); return *this; } this_type& assign(const value_type* p) { - // By design, we need to cast away const-ness here. - internalLayout().heap.mpBegin = const_cast(p); - internalLayout().heap.mpEnd = internalLayout().heap.mpBegin + CharStrlen(p); - internalLayout().heap.mpCapacity = internalLayout().heap.mpEnd; + // By design, we need to cast away const-ness here. + SetInternalHeapLayout(const_cast(p), (size_type)CharStrlen(p), (size_type)CharStrlen(p)); return *this; } this_type& assign(const value_type* pBegin, const value_type* pEnd) { - // By design, we need to cast away const-ness here. - internalLayout().heap.mpBegin = const_cast(pBegin); - internalLayout().heap.mpEnd = const_cast(pEnd); - internalLayout().heap.mpCapacity = internalLayout().heap.mpEnd; + // By design, we need to cast away const-ness here. + SetInternalHeapLayout(const_cast(pBegin), (size_type)(pEnd - pBegin), (size_type)(pEnd - pBegin)); return *this; } @@ -211,9 +211,9 @@ namespace eastl // 1 Attempt to reallocate // 2 Write a 0 char at the end of the fixed_substring // - // Item #1 will result in a crash, due to the attempt by the underlying - // string class to free the substring memory. Item #2 will result in a 0 - // char being written to the character array. Item #2 may or may not be + // Item #1 will result in a crash, due to the attempt by the underlying + // string class to free the substring memory. Item #2 will result in a 0 + // char being written to the character array. Item #2 may or may not be // a problem, depending on how you use fixed_substring. Thus the following // functions cannot be used safely. @@ -263,15 +263,3 @@ namespace eastl #endif // Header include guard - - - - - - - - - - - - diff --git a/include/EASTL/hash_map.h b/include/EASTL/hash_map.h index c0353d95..b109c7d1 100644 --- a/include/EASTL/hash_map.h +++ b/include/EASTL/hash_map.h @@ -154,18 +154,16 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - hash_map(this_type&& x) - : base_type(eastl::move(x)) - { - } + hash_map(this_type&& x) + : base_type(eastl::move(x)) + { + } - hash_map(this_type&& x, const allocator_type& allocator) - : base_type(eastl::move(x), allocator) - { - } - #endif + hash_map(this_type&& x, const allocator_type& allocator) + : base_type(eastl::move(x), allocator) + { + } /// hash_map @@ -209,12 +207,10 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - this_type& operator=(this_type&& x) - { - return static_cast(base_type::operator=(eastl::move(x))); - } - #endif + this_type& operator=(this_type&& x) + { + return static_cast(base_type::operator=(eastl::move(x))); + } /// insert @@ -266,12 +262,10 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - insert_return_type insert(key_type&& key) - { - return base_type::DoInsertKey(true_type(), eastl::move(key)); - } - #endif + insert_return_type insert(key_type&& key) + { + return base_type::DoInsertKey(true_type(), eastl::move(key)); + } mapped_type& operator[](const key_type& key) @@ -285,13 +279,11 @@ namespace eastl //return (*base_type::insert(value_type(key, mapped_type())).first).second; } - #if EASTL_MOVE_SEMANTICS_ENABLED - mapped_type& operator[](key_type&& key) - { - // The Standard states that this function "inserts the value value_type(std::move(key), mapped_type())" - return (*base_type::DoInsertKey(true_type(), eastl::move(key)).first).second; - } - #endif + mapped_type& operator[](key_type&& key) + { + // The Standard states that this function "inserts the value value_type(std::move(key), mapped_type())" + return (*base_type::DoInsertKey(true_type(), eastl::move(key)).first).second; + } }; // hash_map @@ -368,18 +360,16 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - hash_multimap(this_type&& x) - : base_type(eastl::move(x)) - { - } + hash_multimap(this_type&& x) + : base_type(eastl::move(x)) + { + } - hash_multimap(this_type&& x, const allocator_type& allocator) - : base_type(eastl::move(x), allocator) - { - } - #endif + hash_multimap(this_type&& x, const allocator_type& allocator) + : base_type(eastl::move(x), allocator) + { + } /// hash_multimap @@ -423,12 +413,10 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - this_type& operator=(this_type&& x) - { - return static_cast(base_type::operator=(eastl::move(x))); - } - #endif + this_type& operator=(this_type&& x) + { + return static_cast(base_type::operator=(eastl::move(x))); + } /// insert @@ -443,12 +431,10 @@ namespace eastl } - #if EASTL_MOVE_SEMANTICS_ENABLED - insert_return_type insert(key_type&& key) - { - return base_type::DoInsertKey(false_type(), eastl::move(key)); - } - #endif + insert_return_type insert(key_type&& key) + { + return base_type::DoInsertKey(false_type(), eastl::move(key)); + } }; // hash_multimap diff --git a/include/EASTL/internal/config.h b/include/EASTL/internal/config.h index 66583621..0604e347 100644 --- a/include/EASTL/internal/config.h +++ b/include/EASTL/internal/config.h @@ -89,8 +89,8 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef EASTL_VERSION - #define EASTL_VERSION "3.08.00" - #define EASTL_VERSION_N 30800 + #define EASTL_VERSION "3.09.00" + #define EASTL_VERSION_N 30900 #endif diff --git a/include/EASTL/internal/fixed_pool.h b/include/EASTL/internal/fixed_pool.h index 4c9300f9..5dd5c190 100644 --- a/include/EASTL/internal/fixed_pool.h +++ b/include/EASTL/internal/fixed_pool.h @@ -1458,6 +1458,10 @@ namespace eastl //{ //} + fixed_vector_allocator() + { + } + fixed_vector_allocator(void* /*pNodeBuffer*/) { } diff --git a/include/EASTL/internal/hashtable.h b/include/EASTL/internal/hashtable.h index 8fb80f9a..b289576e 100644 --- a/include/EASTL/internal/hashtable.h +++ b/include/EASTL/internal/hashtable.h @@ -184,12 +184,10 @@ namespace eastl template struct node_iterator_base { - public: typedef hash_node node_type; node_type* mpNode; - public: node_iterator_base(node_type* pNode) : mpNode(pNode) { } @@ -890,7 +888,7 @@ namespace eastl template hashtable(FowardIterator first, FowardIterator last, size_type nBucketCount, const H1&, const H2&, const H&, const Equal&, const ExtractKey&, - const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); // allocator arg removed because VC7.1 fails on the default arg. To do: Make a second version of this function without a default arg. + const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); hashtable(const hashtable& x); @@ -1007,45 +1005,36 @@ namespace eastl template iterator emplace_hint(const_iterator position, Args&&... args); - template - // eastl::pair - insert_return_type try_emplace(const key_type& k, Args&&... args); - template - // eastl::pair - insert_return_type try_emplace(key_type&& k, Args&&... args); - - template - iterator try_emplace(const_iterator position, const key_type& k, Args&&... args); - template - iterator try_emplace(const_iterator position, key_type&& k, Args&&... args); - - insert_return_type insert(value_type&& otherValue); + template insert_return_type try_emplace(const key_type& k, Args&&... args); + template insert_return_type try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator position, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator position, key_type&& k, Args&&... args); + + insert_return_type insert(const value_type& value); + insert_return_type insert(value_type&& otherValue); + iterator insert(const_iterator hint, const value_type& value); + iterator insert(const_iterator hint, value_type&& value); + void insert(std::initializer_list ilist); + template void insert(InputIterator first, InputIterator last); + //insert_return_type insert(node_type&& nh); + //iterator insert(const_iterator hint, node_type&& nh); + + // This overload attempts to mitigate the overhead associated with mismatched cv-quality elements of + // the hashtable pair. It can avoid copy overhead because it will perfect forward the user provided pair types + // until it can constructed in-place in the allocated hashtable node. + // + // Ideally we would remove this overload as it deprecated and removed in C++17 but it currently causes + // performance regressions for hashtables with complex keys (keys that allocate resources). + template , key_type>::value && !eastl::is_literal_type

    ::value && // prevent the single element pair ctor + eastl::is_constructible::value>::type> + insert_return_type insert(P&& otherValue); + // Non-standard extension template // See comments below for the const value_type& equivalent to this function. insert_return_type insert(hash_code_t c, node_type* pNodeNew, P&& otherValue); - // Currently limited to value_type instead of P because it collides with insert(InputIterator, InputIterator). - // To allow this to work with templated P we need to implement a compile-time specialization for the - // case that P&& is const_iterator and have that specialization handle insert(InputIterator, InputIterator) - // instead of insert(InputIterator, InputIterator). Curiously, neither libstdc++ nor libc++ - // implement this function either, which suggests they ran into the same problem I did here - // and haven't yet resolved it (at least as of March 2014, GCC 4.8.1). - iterator insert(const_iterator hint, value_type&& value); - - insert_return_type insert(const value_type& value); - iterator insert(const_iterator, const value_type& value); - - void insert(std::initializer_list ilist); - - template - void insert(InputIterator first, InputIterator last); - - // This standard overload attempts to mitigate the overhead associated with mismatched cv-quality elements of - // the hashtable pair. It can avoid copy overhead because it will perfect forward the user provided pair types - // until it can constructed in-place. - template ::value>::type> - insert_return_type insert(P&& otherValue); - // We provide a version of insert which lets the caller directly specify the hash value and // a potential node to insert if needed. This allows for less thread contention in the case // of a thread-shared hash table that's accessed during a mutex lock, because the hash calculation @@ -1216,8 +1205,6 @@ namespace eastl template iterator DoInsertValue(BoolConstantT, Args&&... args); - template - node_type* DoAllocateNode(Args&&... args); template eastl::pair DoInsertValueExtra(BoolConstantT, @@ -1243,7 +1230,6 @@ namespace eastl template iterator DoInsertValue(BoolConstantT, value_type&& value, DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - node_type* DoAllocateNode(value_type&& value); template eastl::pair DoInsertValueExtra(BoolConstantT, @@ -1269,7 +1255,9 @@ namespace eastl template iterator DoInsertValue(BoolConstantT, const value_type& value, DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - + template + node_type* DoAllocateNode(Args&&... args); + node_type* DoAllocateNode(value_type&& value); node_type* DoAllocateNode(const value_type& value); eastl::pair DoInsertKey(true_type, const key_type& key); @@ -2821,7 +2809,6 @@ namespace eastl } - template void hashtable::insert(std::initializer_list ilist) diff --git a/include/EASTL/internal/type_compound.h b/include/EASTL/internal/type_compound.h index f326d92c..ed3ab7d0 100644 --- a/include/EASTL/internal/type_compound.h +++ b/include/EASTL/internal/type_compound.h @@ -277,36 +277,6 @@ namespace eastl template struct is_convertible : public integral_constant{}; - #elif defined(__GNUC__) && (__GNUC__ <= 2) - #define EASTL_TYPE_TRAIT_is_convertible_CONFORMANCE 0 - - template - struct is_convertible : public false_type{}; - - #elif defined(EA_COMPILER_NO_DECLTYPE) - #define EASTL_TYPE_TRAIT_is_convertible_CONFORMANCE 0 - - // This causes compile failures for some cases and we need to revise it, though the C++11 pathways here are really the future anyway. - // Some variations of EDG generate a warning about the usage below: Test(...) - aggregate type passed through ellipsis (it's not portable to pass a non-POD as ellipsis). - template - struct is_convertible_helper { - static yes_type Test(To); // May need to use __cdecl under VC++. - static no_type Test(...); // May need to use __cdecl under VC++. - static From from; - typedef integral_constant result; - }; - - // void is not convertible to non-void - template - struct is_convertible_helper { typedef false_type result; }; - - // Anything is convertible to void - template - struct is_convertible_helper { typedef true_type result; }; - - template - struct is_convertible : public is_convertible_helper::value, is_void::value>::result {}; - #else #define EASTL_TYPE_TRAIT_is_convertible_CONFORMANCE 1 @@ -337,6 +307,12 @@ namespace eastl #endif + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + EA_CONSTEXPR bool is_convertible_v = is_convertible::value; + #endif + + /////////////////////////////////////////////////////////////////////// // is_explicitly_convertible // diff --git a/include/EASTL/internal/type_pod.h b/include/EASTL/internal/type_pod.h index e4c02a1a..0542ce5b 100644 --- a/include/EASTL/internal/type_pod.h +++ b/include/EASTL/internal/type_pod.h @@ -589,6 +589,11 @@ namespace eastl struct is_literal_type : public eastl::integral_constant::type>::type>::value>{}; #endif + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_literal_type_v = is_literal_type::value; + #endif + /////////////////////////////////////////////////////////////////////// @@ -652,8 +657,7 @@ namespace eastl // up obj1 are copied into obj2, obj2 shall subsequently hold the // same value as obj1. In other words, you can memcpy/memmove it. /////////////////////////////////////////////////////////////////////// - - #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(_MSC_VER) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_trivially_copyable))) + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(_MSC_VER) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 5003)) || (defined(EA_COMPILER_CLANG) && EA_COMPILER_HAS_FEATURE(is_trivially_copyable))) #define EASTL_TYPE_TRAIT_is_trivially_copyable_CONFORMANCE 1 // https://connect.microsoft.com/VisualStudio/feedback/details/808827/c-std-is-trivially-copyable-produces-wrong-result-for-arrays @@ -697,6 +701,11 @@ namespace eastl template <> struct is_trivially_copyable : public eastl::integral_constant { }; \ } + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_copyable_v = is_trivially_copyable::value; + #endif + /////////////////////////////////////////////////////////////////////// // is_constructible @@ -705,102 +714,74 @@ namespace eastl // /////////////////////////////////////////////////////////////////////// - #if defined(EA_COMPILER_NO_VARIADIC_TEMPLATES) || defined(EA_COMPILER_NO_DECLTYPE) || EASTL_NO_RVALUE_REFERENCES - - // We currently take a simplified approach whereby we lump all deficient compilers together here, rather than - // implementing a version for compilers that support rvalue references but not variadic templates. However, - // we should eventually implement such a thing because VS2010 and VS2012 are exactly that: compilers that support - // decltype and rvalue references but not variadic templates. - - #define EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE 0 + #define EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE 1 - // Start by assuming the type is constructible, then below we subtract types that are known not-constructible. - // This can result in false-positives, which we normally avoid, but the use cases of this trait are such that - // we'd rather have false positives than false negatives. But we can revisit that if you have a problem. - - template - struct is_constructible_helper - { static const bool value = !eastl::is_void::value && !eastl::is_function::value && !eastl::is_abstract::value; }; - - template - struct is_constructible - { static const bool value = eastl::is_constructible_helper::type>::value && - !eastl::is_array_of_unknown_bounds::value && - !eastl::is_reference::value && // references can be copy constructed, but here we are testing for only default construction, which isn't possible for references. - eastl::is_same::value && // We have no way of checking argument matches without decltype support, - eastl::is_same::value; // so disallow them. This will result in cases being potentially false negatives. - }; // (To do:) However, we could easily add support for constructing a scalar from another single scalar or reference from something. + // We implement a copy of move here has move_internal. We are currently stuck doing this because our move + // implementation is in and currently #includes us, and so we have a header + // chicken-and-egg problem. To do: Resolve this, probably by putting eastl::move somewhere else. + template + inline typename eastl::remove_reference::type&& move_internal(T&& x) EA_NOEXCEPT + { return ((typename eastl::remove_reference::type&&)x); } - #else - #define EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE 1 + template + typename first_type_select()...)))>::type is(T&&, Args&& ...); - // We implement a copy of move here has move_internal. We are currently stuck doing this because our move - // implementation is in and currently #includes us, and so we have a header - // chicken-and-egg problem. To do: Resolve this, probably by putting eastl::move somewhere else. - template - inline typename eastl::remove_reference::type&& move_internal(T&& x) EA_NOEXCEPT - { return ((typename eastl::remove_reference::type&&)x); } + template + struct can_construct_scalar_helper + { + static eastl::true_type can(T); + static eastl::false_type can(...); + }; - template - typename first_type_select()...)))>::type is(T&&, Args&& ...); + template + eastl::false_type is(argument_sink, Args&& ...); - template - struct can_construct_scalar_helper - { - static eastl::true_type can(T); - static eastl::false_type can(...); - }; + // Except for scalars and references (handled below), check for constructibility via decltype. + template + struct is_constructible_helper_2 // argument_sink will catch all T that is not constructible from the Args and denote false_type + : public eastl::identity(), eastl::declval()...))>::type {}; - template - eastl::false_type is(argument_sink, Args&& ...); + template + struct is_constructible_helper_2 + : public eastl::is_scalar {}; - // Except for scalars and references (handled below), check for constructibility via decltype. - template - struct is_constructible_helper_2 // argument_sink will catch all T that is not constructible from the Args and denote false_type - : public eastl::identity(), eastl::declval()...))>::type {}; + template // We handle the case of multiple arguments below (by disallowing them). + struct is_constructible_helper_2 + : public eastl::identity::can(eastl::declval()))>::type {}; - template - struct is_constructible_helper_2 - : public eastl::is_scalar {}; + // Scalars and references can be constructed only with 0 or 1 argument. e.g the following is an invalid expression: int(17, 23) + template + struct is_constructible_helper_2 + : public eastl::false_type {}; - template // We handle the case of multiple arguments below (by disallowing them). - struct is_constructible_helper_2 - : public eastl::identity::can(eastl::declval()))>::type {}; + template + struct is_constructible_helper_1 + : public is_constructible_helper_2::value || eastl::is_reference::value, T, Args...> {}; - // Scalars and references can be constructed only with 0 or 1 argument. e.g the following is an invalid expression: int(17, 23) - template - struct is_constructible_helper_2 - : public eastl::false_type {}; + // Unilaterally dismiss void, abstract, unknown bound arrays, and function types as not constructible. + template + struct is_constructible_helper_1 + : public false_type {}; - template - struct is_constructible_helper_1 - : public is_constructible_helper_2::value || eastl::is_reference::value, T, Args...> {}; + // is_constructible + template + struct is_constructible + : public is_constructible_helper_1<(eastl::is_abstract::type>::value || + eastl::is_array_of_unknown_bounds::value || + eastl::is_function::type>::value || + eastl::has_void_arg::value), + T, Args...> {}; - // Unilaterally dismiss void, abstract, unknown bound arrays, and function types as not constructible. - template - struct is_constructible_helper_1 - : public false_type {}; + // Array types are constructible if constructed with no arguments and if their element type is default-constructible + template + struct is_constructible_helper_2 + : public eastl::is_constructible::type> {}; - // is_constructible - template - struct is_constructible - : public is_constructible_helper_1<(eastl::is_abstract::type>::value || - eastl::is_array_of_unknown_bounds::value || - eastl::is_function::type>::value || - eastl::has_void_arg::value), - T, Args...> {}; - - // Array types are constructible if constructed with no arguments and if their element type is default-constructible - template - struct is_constructible_helper_2 - : public eastl::is_constructible::type> {}; - - // Arrays with arguments are not constructible. e.g. the following is an invalid expression: int[3](37, 34, 12) - template - struct is_constructible_helper_2 - : public eastl::false_type {}; + // Arrays with arguments are not constructible. e.g. the following is an invalid expression: int[3](37, 34, 12) + template + struct is_constructible_helper_2 + : public eastl::false_type {}; - #endif // You need to manually declare const/volatile variants individually if you want them. #define EASTL_DECLARE_IS_CONSTRUCTIBLE(T, U, isConstructible) \ @@ -809,8 +790,8 @@ namespace eastl } #if EASTL_VARIABLE_TEMPLATES_ENABLED - template - EA_CONSTEXPR bool is_constructible_v = is_constructible::value; + template + EA_CONSTEXPR bool is_constructible_v = is_constructible::value; #endif diff --git a/include/EASTL/intrusive_list.h b/include/EASTL/intrusive_list.h index 32ec2f91..16aa360f 100644 --- a/include/EASTL/intrusive_list.h +++ b/include/EASTL/intrusive_list.h @@ -54,7 +54,7 @@ // uses the name 'find' because: // - So as not to confuse the member function with the well-defined free function from algorithm.h. // - Because it is not API-compatible with eastl::find(). -// - Because it simply locates an object within the list based on its node entry and doesn't p[erform before any value-based searches or comparisons. +// - Because it simply locates an object within the list based on its node entry and doesn't perform before any value-based searches or comparisons. // // Differences between intrusive_list and std::list: // diff --git a/include/EASTL/intrusive_ptr.h b/include/EASTL/intrusive_ptr.h index 12f838d2..af4e686f 100644 --- a/include/EASTL/intrusive_ptr.h +++ b/include/EASTL/intrusive_ptr.h @@ -40,7 +40,7 @@ namespace eastl /// intrusive_ptr /// /// This is a class that acts like the C++ auto_ptr class except that instead - /// of deleting its member data when it goes out of scope, it Releases its + /// of deleting its member data when it goes out of scope, it releases its /// member data when it goes out of scope. This class thus requires that the /// templated data type have an AddRef and Release function (or whatever is /// configured to be the two refcount functions). @@ -66,7 +66,6 @@ namespace eastl template class intrusive_ptr { - protected: // Friend declarations. template friend class intrusive_ptr; @@ -116,6 +115,15 @@ namespace eastl intrusive_ptr_add_ref(mpObject); } + + /// intrusive_ptr + /// move constructor + intrusive_ptr(intrusive_ptr&& ip) + : mpObject(nullptr) + { + swap(ip); + } + /// intrusive_ptr /// Provides a constructor which copies a pointer from another intrusive_ptr. /// The incoming pointer is AddRefd. The source intrusive_ptr object maintains @@ -141,14 +149,23 @@ namespace eastl } - /// operator = + /// operator= /// Assignment to self type. - intrusive_ptr& operator=(const intrusive_ptr& ip) + intrusive_ptr& operator=(const intrusive_ptr& ip) { return operator=(ip.mpObject); } + /// operator= + /// Move assignment operator + intrusive_ptr& operator=(intrusive_ptr&& ip) + { + swap(ip); + return *this; + } + + /// operator = /// Assigns an intrusive_ptr object to this intrusive_ptr object. /// The incoming pointer is AddRefd. The source intrusive_ptr object diff --git a/include/EASTL/string.h b/include/EASTL/string.h index d56cd72f..000cf246 100644 --- a/include/EASTL/string.h +++ b/include/EASTL/string.h @@ -6,7 +6,7 @@ // Implements a basic_string class, much like the C++ std::basic_string. // The primary distinctions between basic_string and std::basic_string are: // - basic_string has a few extension functions that allow for increased performance. -// - basic_string has a few extension functions that make use easier, +// - basic_string has a few extension functions that make use easier, // such as a member sprintf function and member tolower/toupper functions. // - basic_string supports debug memory naming natively. // - basic_string is easier to read, debug, and visualize. @@ -14,22 +14,22 @@ // size(), etc. in order to improve debug performance and optimizer success. // - basic_string is savvy to an environment that doesn't have exception handling, // as is sometimes the case with console or embedded environments. -// - basic_string has less deeply nested function calls and allows the user to +// - basic_string has less deeply nested function calls and allows the user to // enable forced inlining in debug builds in order to reduce bloat. -// - basic_string doesn't use char traits. As a result, EASTL assumes that -// strings will hold characters and not exotic things like widgets. At the +// - basic_string doesn't use char traits. As a result, EASTL assumes that +// strings will hold characters and not exotic things like widgets. At the // very least, basic_string assumes that the value_type is a POD. -// - basic_string::size_type is defined as eastl_size_t instead of size_t in +// - basic_string::size_type is defined as eastl_size_t instead of size_t in // order to save memory and run faster on 64 bit systems. // - basic_string data is guaranteed to be contiguous. // - basic_string data is guaranteed to be 0-terminated, and the c_str() function // is guaranteed to return the same pointer as the data() which is guaranteed // to be the same value as &string[0]. -// - basic_string has a set_capacity() function which frees excess capacity. -// The only way to do this with std::basic_string is via the cryptic non-obvious +// - basic_string has a set_capacity() function which frees excess capacity. +// The only way to do this with std::basic_string is via the cryptic non-obvious // trick of using: basic_string(x).swap(x); -// - basic_string has a force_size() function, which unilaterally moves the string -// end position (mpEnd) to the given location. Useful for when the user writes +// - basic_string has a force_size() function, which unilaterally moves the string +// end position (mpEnd) to the given location. Useful for when the user writes // into the string via some extenal means such as C strcpy or sprintf. /////////////////////////////////////////////////////////////////////////////// @@ -37,7 +37,7 @@ // Copy on Write (cow) // // This string implementation does not do copy on write (cow). This is by design, -// as cow penalizes 95% of string uses for the benefit of only 5% of the uses +// as cow penalizes 95% of string uses for the benefit of only 5% of the uses // (these percentages are qualitative, not quantitative). The primary benefit of // cow is that it allows for the sharing of string data between two string objects. // Thus if you say this: @@ -46,36 +46,36 @@ // the "hello" will be shared between a and b. If you then say this: // a = "world"; // then a will release its reference to "hello" and leave b with the only reference -// to it. Normally this functionality is accomplished via reference counting and +// to it. Normally this functionality is accomplished via reference counting and // with atomic operations or mutexes. // -// The C++ standard does not say anything about basic_string and cow. However, +// The C++ standard does not say anything about basic_string and cow. However, // for a basic_string implementation to be standards-conforming, a number of // issues arise which dictate some things about how one would have to implement // a cow string. The discussion of these issues will not be rehashed here, as you -// can read the references below for better detail than can be provided in the -// space we have here. However, we can say that the C++ standard is sensible and +// can read the references below for better detail than can be provided in the +// space we have here. However, we can say that the C++ standard is sensible and // that anything we try to do here to allow for an efficient cow implementation // would result in a generally unacceptable string interface. // // The disadvantages of cow strings are: // - A reference count needs to exist with the string, which increases string memory usage. -// - With thread safety, atomic operations and mutex locks are expensive, especially +// - With thread safety, atomic operations and mutex locks are expensive, especially // on weaker memory systems such as console gaming platforms. -// - All non-const string accessor functions need to do a sharing check the the -// first such check needs to detach the string. Similarly, all string assignments -// need to do a sharing check as well. If you access the string before doing an -// assignment, the assignment doesn't result in a shared string, because the string +// - All non-const string accessor functions need to do a sharing check the the +// first such check needs to detach the string. Similarly, all string assignments +// need to do a sharing check as well. If you access the string before doing an +// assignment, the assignment doesn't result in a shared string, because the string // has already been detached. -// - String sharing doesn't happen the large majority of the time. In some cases, -// the total sum of the reference count memory can exceed any memory savings -// gained by the strings that share representations. -// -// The addition of a string_cow class is under consideration for this library. +// - String sharing doesn't happen the large majority of the time. In some cases, +// the total sum of the reference count memory can exceed any memory savings +// gained by the strings that share representations. +// +// The addition of a string_cow class is under consideration for this library. // There are conceivably some systems which have string usage patterns which would -// benefit from cow sharing. Such functionality is best saved for a separate string +// benefit from cow sharing. Such functionality is best saved for a separate string // implementation so that the other string uses aren't penalized. -// +// // References: // This is a good starting HTML reference on the topic: // http://www.gotw.ca/publications/optimizations.htm @@ -148,7 +148,7 @@ EA_RESTORE_ALL_VC_WARNINGS() /////////////////////////////////////////////////////////////////////////////// // EASTL_STRING_INITIAL_CAPACITY // -// As of this writing, this must be > 0. Note that an initially empty string +// As of this writing, this must be > 0. Note that an initially empty string // has a capacity of zero (it allocates no memory). // const eastl_size_t EASTL_STRING_INITIAL_CAPACITY = 8; @@ -159,13 +159,13 @@ const eastl_size_t EASTL_STRING_INITIAL_CAPACITY = 8; // Vsnprintf // // The user is expected to supply these functions one way or another. Note that -// these functions are expected to accept parameters as per the C99 standard. -// These functions can deal with C99 standard return values or Microsoft non-standard +// these functions are expected to accept parameters as per the C99 standard. +// These functions can deal with C99 standard return values or Microsoft non-standard // return values but act more efficiently if implemented via the C99 style. // -// In the case of EASTL_EASTDC_VSNPRINTF == 1, the user is expected to either -// link EAStdC or provide the functions below that act the same. In the case of -// EASTL_EASTDC_VSNPRINTF == 0, the user is expected to provide the function +// In the case of EASTL_EASTDC_VSNPRINTF == 1, the user is expected to either +// link EAStdC or provide the functions below that act the same. In the case of +// EASTL_EASTDC_VSNPRINTF == 0, the user is expected to provide the function // implementations, and may simply use C vsnprintf if desired, though it's not // completely portable between compilers. // @@ -249,21 +249,20 @@ namespace eastl #endif - /////////////////////////////////////////////////////////////////////////////// /// basic_string /// /// Implements a templated string class, somewhat like C++ std::basic_string. /// - /// Notes: + /// Notes: /// As of this writing, an insert of a string into itself necessarily /// triggers a reallocation, even if there is enough capacity in self - /// to handle the increase in size. This is due to the slightly tricky + /// to handle the increase in size. This is due to the slightly tricky /// nature of the operation of modifying one's self with one's self, /// and thus the source and destination are being modified during the /// operation. It might be useful to rectify this to the extent possible. /// - /// Our usage of noexcept specifiers is a little different from the + /// Our usage of noexcept specifiers is a little different from the /// requirements specified by std::basic_string in C++11. This is because /// our allocators are instances and not types and thus can be non-equal /// and result in exceptions during assignments that theoretically can't @@ -287,40 +286,76 @@ namespace eastl typedef ptrdiff_t difference_type; typedef Allocator allocator_type; - static const size_type npos = (size_type)-1; /// 'npos' means non-valid position or simply non-position. - static const size_type kMaxSize = (size_type)-2; /// -1 is reserved for 'npos'. It also happens to be slightly beneficial that kMaxSize is a value less than -1, as it helps us deal with potential integer wraparound issues. + static const size_type npos = (size_type)-1; /// 'npos' means non-valid position or simply non-position. public: - // CtorDoNotInitialize exists so that we can create a constructor that allocates but doesn't + // CtorDoNotInitialize exists so that we can create a constructor that allocates but doesn't // initialize and also doesn't collide with any other constructor declaration. struct CtorDoNotInitialize{}; - // CtorSprintf exists so that we can create a constructor that accepts printf-style + // CtorSprintf exists so that we can create a constructor that accepts printf-style // arguments but also doesn't collide with any other constructor declaration. struct CtorSprintf{}; - // CtorConvert exists so that we can have a constructor that implements string encoding + // CtorConvert exists so that we can have a constructor that implements string encoding // conversion, such as between UCS2 char16_t and UTF8 char8_t. struct CtorConvert{}; + protected: + // Masks used to determine if we are in SSO or Heap + #ifdef EA_SYSTEM_BIG_ENDIAN + // Big Endian use LSB, unless we want to reorder struct layouts on endianness, Bit is set when we are in Heap + static constexpr size_type kHeapMask = 0x1; + static constexpr size_type kSSOMask = 0x1; + #else + // Little Endian use MSB + static constexpr size_type kHeapMask = ~(size_type(~size_type(0)) >> 1); + static constexpr size_type kSSOMask = 0x80; + #endif + + public: + #ifdef EA_SYSTEM_BIG_ENDIAN + static constexpr size_type kMaxSize = (~kHeapMask) >> 1; + #else + static constexpr size_type kMaxSize = ~kHeapMask; + #endif + protected: // The view of memory when the string data is obtained from the allocator. struct HeapLayout { - value_type* mpBegin; // Begin of string. - value_type* mpEnd; // End of string. *mpEnd is always '0', as we 0-terminate our string. mpEnd is always < mpCapacity. - value_type* mpCapacity; // End of allocated space, including the space needed to store the trailing '0' char. mpCapacity is always at least mpEnd + 1. + value_type* mpBegin; // Begin of string. + size_type mnSize; // Size of the string. Number of characters currently in the string, not including the trailing '0' + size_type mnCapacity; // Capacity of the string. Number of characters string can hold, not including the trailing '0' }; - - // The view of memory when the string data is able to store the string data locally (without a heap allocation). + + template + struct SSOPadding + { + char padding[sizeof(CharT) - sizeof(char)]; + }; + + template + struct SSOPadding + { + // template specialization to remove the padding structure to avoid warnings on zero length arrays + // also, this allows us to take advantage of the empty-base-class optimization. + }; + + // The view of memory when the string data is able to store the string data locally (without a heap allocation). struct SSOLayout { - // NOTE(rparolin): The sizes subtracted must match the data members in the SSO layout structure. - enum { SSO_SIZE_IN_BYTES = sizeof(HeapLayout) - sizeof(value_type*) - sizeof(char) }; + enum { SSO_CAPACITY = (sizeof(HeapLayout) - sizeof(char)) / sizeof(value_type) }; - value_type* mpBegin; // Begin of string. - char mnSize; // Character count. - char mBuffer[SSO_SIZE_IN_BYTES]; // Local buffer for string data. + // mnSize must correspond to the last byte of HeapLayout.mnCapacity, so we don't want the compiler to insert + // padding after mnSize if sizeof(value_type) != 1; Also ensures both layouts are the same size. + struct SSOSize : SSOPadding + { + char mnSize; + }; + + value_type mData[SSO_CAPACITY]; // Local buffer for string data. + SSOSize mSizeField; }; // This view of memory is a utility structure for easy copying of the string data. @@ -329,15 +364,14 @@ namespace eastl char mBuffer[sizeof(HeapLayout)]; }; - static_assert(sizeof(SSOLayout) == sizeof(HeapLayout), "heap and sso layout structures must be the same size"); - static_assert(sizeof(HeapLayout) == sizeof(RawLayout), "heap and raw layout structures must be the same size"); + static_assert(sizeof(SSOLayout) == sizeof(HeapLayout), "heap and sso layout structures must be the same size"); + static_assert(sizeof(HeapLayout) == sizeof(RawLayout), "heap and raw layout structures must be the same size"); - // This implements the 'short string optimization' or SSO. SSO reuses the existing storage of string class to // hold string data short enough to fit therefore avoiding a heap allocation. The number of characters stored in // the string SSO buffer is variable and depends on the string character width. This implementation favors a // consistent string size than increasing the size of the string local data to accommodate a consistent number - // of characters despite character width. + // of characters despite character width. struct Layout { union @@ -347,82 +381,100 @@ namespace eastl RawLayout raw; }; - Layout() = default; + // start as SSO by default + Layout() { ResetToSSO(); SetSSOSize(0); } Layout(const Layout& other) { Copy(*this, other); } Layout(Layout&& other) { Move(*this, other); } Layout& operator=(const Layout& other) { Copy(*this, other); return *this; } Layout& operator=(Layout&& other) { Move(*this, other); return *this; } - inline bool IsSSO() const EA_NOEXCEPT { return heap.mpBegin == SSOBufferPtr(); } - inline value_type* SSOBufferPtr() EA_NOEXCEPT { return reinterpret_cast(sso.mBuffer); } - inline const value_type* SSOBufferPtr() const EA_NOEXCEPT { return reinterpret_cast(sso.mBuffer); } - inline size_type GetSize() const EA_NOEXCEPT { return IsSSO() ? sso.mnSize : size_type(heap.mpEnd - heap.mpBegin); } - inline size_type GetRemainingCapacity() const EA_NOEXCEPT { return size_type(CapacityPtr() - EndPtr()); } - - inline value_type* BeginPtr() EA_NOEXCEPT { return IsSSO() ? SSOBufferPtr() : heap.mpBegin; } - inline const value_type* BeginPtr() const EA_NOEXCEPT { return IsSSO() ? SSOBufferPtr() : heap.mpBegin; } - inline value_type* EndPtr() EA_NOEXCEPT { return IsSSO() ? SSOBufferPtr() + sso.mnSize : heap.mpEnd; } - inline const value_type* EndPtr() const EA_NOEXCEPT { return IsSSO() ? SSOBufferPtr() + sso.mnSize : heap.mpEnd; } - inline value_type* CapacityPtr() EA_NOEXCEPT { return IsSSO() ? ((value_type*)(sso.mBuffer + SSOLayout::SSO_SIZE_IN_BYTES)) : heap.mpCapacity; } - inline const value_type* CapacityPtr() const EA_NOEXCEPT { return IsSSO() ? ((value_type*)(sso.mBuffer + SSOLayout::SSO_SIZE_IN_BYTES)) : heap.mpCapacity; } + // We are using Heap when the bit is set, easier to conceptualize checking IsHeap instead of IsSSO + inline bool IsHeap() const EA_NOEXCEPT { return !!(sso.mSizeField.mnSize & kSSOMask); } + inline bool IsSSO() const EA_NOEXCEPT { return !IsHeap(); } + inline value_type* SSOBufferPtr() EA_NOEXCEPT { return sso.mData; } + inline const value_type* SSOBufferPtr() const EA_NOEXCEPT { return sso.mData; } - void Copy(Layout& dst, const Layout& src) + // Largest value for SSO.mnSize == 23, which has two LSB bits set, but on big-endian (BE) + // use least significant bit (LSB) to denote heap so shift. + inline size_type GetSSOSize() const EA_NOEXCEPT { - dst.raw = src.raw; - - if (src.IsSSO()) - { - // NOTE(rparolin): Refresh the pointer used to check if SSO is enabled. - dst.sso.mpBegin = dst.SSOBufferPtr(); - } + #ifdef EA_SYSTEM_BIG_ENDIAN + return SSOLayout::SSO_CAPACITY - (sso.mSizeField.mnSize >> 2); + #else + return (SSOLayout::SSO_CAPACITY - sso.mSizeField.mnSize); + #endif } + inline size_type GetHeapSize() const EA_NOEXCEPT { return heap.mnSize; } + inline size_type GetSize() const EA_NOEXCEPT { return IsHeap() ? GetHeapSize() : GetSSOSize(); } - void Move(Layout& dst, Layout& src) + inline void SetSSOSize(size_type size) EA_NOEXCEPT { - const bool isSrcSSO = src.IsSSO(); + #ifdef EA_SYSTEM_BIG_ENDIAN + sso.mSizeField.mnSize = (char)((SSOLayout::SSO_CAPACITY - size) << 2); + #else + sso.mSizeField.mnSize = (char)(SSOLayout::SSO_CAPACITY - size); + #endif + } - eastl::swap(dst.raw, src.raw); + inline void SetHeapSize(size_type size) EA_NOEXCEPT { heap.mnSize = size; } + inline void SetSize(size_type size) EA_NOEXCEPT { IsHeap() ? SetHeapSize(size) : SetSSOSize(size); } - if (isSrcSSO) - { - // NOTE(rparolin): Refresh the pointer used to check if SSO is enabled. - dst.sso.mpBegin = dst.SSOBufferPtr(); - } - } + inline size_type GetRemainingCapacity() const EA_NOEXCEPT { return size_type(CapacityPtr() - EndPtr()); } - inline void SetEndPtr(value_type* pEnd) EA_NOEXCEPT - { - if(IsSSO()) - sso.mnSize = char(pEnd - SSOBufferPtr()); - else - heap.mpEnd = pEnd; - } + inline value_type* HeapBeginPtr() EA_NOEXCEPT { return heap.mpBegin; }; + inline const value_type* HeapBeginPtr() const EA_NOEXCEPT { return heap.mpBegin; }; - inline void SetBeginPtr(value_type* pBegin) EA_NOEXCEPT - { - // NOTE(rparolin): sso.mpBegin and heap.mpBegin occupy the same memory location so only one write is necessary. - // - // (IsSSO() ? sso.mpBegin : sso.mpEnd) = pBegin; - heap.mpBegin = pBegin; - } + inline value_type* SSOBeginPtr() EA_NOEXCEPT { return sso.mData; } + inline const value_type* SSOBeginPtr() const EA_NOEXCEPT { return sso.mData; } + + inline value_type* BeginPtr() EA_NOEXCEPT { return IsHeap() ? HeapBeginPtr() : SSOBeginPtr(); } + inline const value_type* BeginPtr() const EA_NOEXCEPT { return IsHeap() ? HeapBeginPtr() : SSOBeginPtr(); } + + inline value_type* HeapEndPtr() EA_NOEXCEPT { return heap.mpBegin + heap.mnSize; } + inline const value_type* HeapEndPtr() const EA_NOEXCEPT { return heap.mpBegin + heap.mnSize; } + + inline value_type* SSOEndPtr() EA_NOEXCEPT { return sso.mData + GetSSOSize(); } + inline const value_type* SSOEndPtr() const EA_NOEXCEPT { return sso.mData + GetSSOSize(); } + + // Points to end of character stream, *ptr == '0' + inline value_type* EndPtr() EA_NOEXCEPT { return IsHeap() ? HeapEndPtr() : SSOEndPtr(); } + inline const value_type* EndPtr() const EA_NOEXCEPT { return IsHeap() ? HeapEndPtr() : SSOEndPtr(); } - inline void SetCapacityPtr(value_type* pCapacity) EA_NOEXCEPT + inline value_type* HeapCapacityPtr() EA_NOEXCEPT { return heap.mpBegin + GetHeapCapacity(); } + inline const value_type* HeapCapacityPtr() const EA_NOEXCEPT { return heap.mpBegin + GetHeapCapacity(); } + + inline value_type* SSOCapcityPtr() EA_NOEXCEPT { return sso.mData + SSOLayout::SSO_CAPACITY; } + inline const value_type* SSOCapcityPtr() const EA_NOEXCEPT { return sso.mData + SSOLayout::SSO_CAPACITY; } + + // Points to end of the buffer at the terminating '0', *ptr == '0' <- not true for SSO + inline value_type* CapacityPtr() EA_NOEXCEPT { return IsHeap() ? HeapCapacityPtr() : SSOCapcityPtr(); } + inline const value_type* CapacityPtr() const EA_NOEXCEPT { return IsHeap() ? HeapCapacityPtr() : SSOCapcityPtr(); } + + inline void SetHeapBeginPtr(value_type* pBegin) EA_NOEXCEPT { heap.mpBegin = pBegin; } + + inline void SetHeapCapacity(size_type cap) EA_NOEXCEPT { - if(!IsSSO()) - heap.mpCapacity = pCapacity; - // else - // { - // // do nothing, the SSO buffer capacity is statically sized. - // } + #ifdef EA_SYSTEM_BIG_ENDIAN + heap.mnCapacity = (cap << 1) | kHeapMask; + #else + heap.mnCapacity = (cap | kHeapMask); + #endif } - inline void ClearSSOBuffer() EA_NOEXCEPT + inline size_type GetHeapCapacity() const EA_NOEXCEPT { - if(IsSSO()) - { - *SSOBufferPtr() = 0; // casts to necessary the character width. - } + #ifdef EA_SYSTEM_BIG_ENDIAN + return (heap.mnCapacity >> 1); + #else + return (heap.mnCapacity & ~kHeapMask); + #endif } + + inline void Copy(Layout& dst, const Layout& src) EA_NOEXCEPT { dst.raw = src.raw; } + inline void Move(Layout& dst, Layout& src) EA_NOEXCEPT { eastl::swap(dst.raw, src.raw); } + inline void Swap(Layout& a, Layout& b) EA_NOEXCEPT { eastl::swap(a.raw, b.raw); } + + inline void ResetToSSO() EA_NOEXCEPT { memset(sso.mData, 0, sizeof(SSOLayout)); } }; eastl::compressed_pair mPair; @@ -441,7 +493,7 @@ namespace eastl EASTL_STRING_EXPLICIT basic_string(const value_type* p, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); basic_string(size_type n, value_type c, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); basic_string(const this_type& x); - //basic_string(const this_type& x, const allocator_type& allocator); + basic_string(const this_type& x, const allocator_type& allocator); basic_string(const value_type* pBegin, const value_type* pEnd, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); basic_string(CtorDoNotInitialize, size_type n, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); basic_string(CtorSprintf, const value_type* pFormat, ...); @@ -478,7 +530,7 @@ namespace eastl #if EASTL_OPERATOR_EQUALS_OTHER_ENABLED this_type& operator=(value_type* p) { return operator=((const value_type*)p); } // We need this because otherwise the const value_type* version can collide with the const OtherStringType& version below. - + template this_type& operator=(const OtherCharType* p); @@ -490,12 +542,12 @@ namespace eastl // Assignment operations this_type& assign(const this_type& x); - this_type& assign(const this_type& x, size_type position, size_type n); + this_type& assign(const this_type& x, size_type position, size_type n = npos); this_type& assign(const value_type* p, size_type n); this_type& assign(const value_type* p); this_type& assign(size_type n, value_type c); this_type& assign(const value_type* pBegin, const value_type* pEnd); - this_type& assign(this_type&& x); // TODO(c++17): noexcept(allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value); + this_type& assign(this_type&& x); // TODO(c++17): noexcept(allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value); this_type& assign(std::initializer_list); template @@ -524,17 +576,19 @@ namespace eastl const_reverse_iterator rend() const EA_NOEXCEPT; const_reverse_iterator crend() const EA_NOEXCEPT; + // Size-related functionality - bool empty() const EA_NOEXCEPT; // Expanded in source code as: (mpBegin == mpEnd) or (mpBegin != mpEnd) - size_type size() const EA_NOEXCEPT; // Expanded in source code as: (size_type)(mpEnd - mpBegin) - size_type length() const EA_NOEXCEPT; // Expanded in source code as: (size_type)(mpEnd - mpBegin) - size_type max_size() const EA_NOEXCEPT; // Expanded in source code as: kMaxSize - size_type capacity() const EA_NOEXCEPT; // Expanded in source code as: (size_type)((mpCapacity - mpBegin) - 1). Thus thus returns the max strlen the container can currently hold without resizing. + bool empty() const EA_NOEXCEPT; + size_type size() const EA_NOEXCEPT; + size_type length() const EA_NOEXCEPT; + size_type max_size() const EA_NOEXCEPT; + size_type capacity() const EA_NOEXCEPT; void resize(size_type n, value_type c); void resize(size_type n); void reserve(size_type = 0); void set_capacity(size_type n = npos); // Revises the capacity to the user-specified value. Resizes the container to match the capacity if the requested capacity n is less than the current size. If n == npos then the capacity is reallocated (if necessary) such that capacity == size. void force_size(size_type n); // Unilaterally moves the string end position (mpEnd) to the given location. Useful for when the user writes into the string via some extenal means such as C strcpy or sprintf. This allows for more efficient use than using resize to achieve this. + void shrink_to_fit(); // Raw access const value_type* data() const EA_NOEXCEPT; @@ -556,7 +610,7 @@ namespace eastl this_type& operator+=(value_type c); this_type& append(const this_type& x); - this_type& append(const this_type& x, size_type position, size_type n); + this_type& append(const this_type& x, size_type position, size_type n = npos); this_type& append(const value_type* p, size_type n); this_type& append(const value_type* p); this_type& append(size_type n, value_type c); @@ -600,8 +654,8 @@ namespace eastl pointer detach() EA_NOEXCEPT; // Replacement operations - this_type& replace(size_type position, size_type n, const this_type& x); - this_type& replace(size_type pos1, size_type n1, const this_type& x, size_type pos2, size_type n2); + this_type& replace(size_type position, size_type n, const this_type& x); + this_type& replace(size_type pos1, size_type n1, const this_type& x, size_type pos2, size_type n2 = npos); this_type& replace(size_type position, size_type n1, const value_type* p, size_type n2); this_type& replace(size_type position, size_type n1, const value_type* p); this_type& replace(size_type position, size_type n1, size_type n2, value_type c); @@ -613,13 +667,13 @@ namespace eastl size_type copy(value_type* p, size_type n, size_type position = 0) const; // Find operations - size_type find(const this_type& x, size_type position = 0) const EA_NOEXCEPT; + size_type find(const this_type& x, size_type position = 0) const EA_NOEXCEPT; size_type find(const value_type* p, size_type position = 0) const; size_type find(const value_type* p, size_type position, size_type n) const; size_type find(value_type c, size_type position = 0) const EA_NOEXCEPT; // Reverse find operations - size_type rfind(const this_type& x, size_type position = npos) const EA_NOEXCEPT; + size_type rfind(const this_type& x, size_type position = npos) const EA_NOEXCEPT; size_type rfind(const value_type* p, size_type position = npos) const; size_type rfind(const value_type* p, size_type position, size_type n) const; size_type rfind(value_type c, size_type position = npos) const EA_NOEXCEPT; @@ -693,6 +747,9 @@ namespace eastl void RangeInitialize(const value_type* pBegin); void SizeInitialize(size_type n, value_type c); + // SSO related functions + bool IsSSO() const EA_NOEXCEPT; + void ThrowLengthException() const; void ThrowRangeException() const; void ThrowInvalidArgumentException() const; @@ -749,6 +806,14 @@ namespace eastl } + template + basic_string::basic_string(const this_type& x, const allocator_type& allocator) + : mPair(allocator) + { + RangeInitialize(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + template template inline basic_string::basic_string(CtorConvert, const OtherStringType& x) @@ -760,11 +825,11 @@ namespace eastl template - basic_string::basic_string(const this_type& x, size_type position, size_type n) + basic_string::basic_string(const this_type& x, size_type position, size_type n) : mPair(x.get_allocator()) { #if EASTL_STRING_OPT_RANGE_ERRORS - if (EASTL_UNLIKELY(position > (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()))) // 21.4.2 p4 + if (EASTL_UNLIKELY(position > x.internalLayout().GetSize())) // 21.4.2 p4 { ThrowRangeException(); AllocateSelf(); @@ -772,19 +837,17 @@ namespace eastl else RangeInitialize( x.internalLayout().BeginPtr() + position, - x.internalLayout().BeginPtr() + position + - eastl::min_alt(n, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()) - position)); + x.internalLayout().BeginPtr() + position + eastl::min_alt(n, x.internalLayout().GetSize() - position)); #else RangeInitialize( x.internalLayout().BeginPtr() + position, - x.internalLayout().BeginPtr() + position + - eastl::min_alt(n, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()) - position)); + x.internalLayout().BeginPtr() + position + eastl::min_alt(n, x.internalLayout().GetSize() - position)); #endif } template - inline basic_string::basic_string(const value_type* p, size_type n, const allocator_type& allocator) + inline basic_string::basic_string(const value_type* p, size_type n, const allocator_type& allocator) : mPair(allocator) { RangeInitialize(p, p + n); @@ -793,20 +856,20 @@ namespace eastl template template - inline basic_string::basic_string(CtorConvert, const OtherCharType* p, const allocator_type& allocator) + inline basic_string::basic_string(CtorConvert, const OtherCharType* p, const allocator_type& allocator) : mPair(allocator) { - AllocateSelf(); // In this case we are converting from one string encoding to another, and we + AllocateSelf(); // In this case we are converting from one string encoding to another, and we append_convert(p); // implement this in the simplest way, by simply default-constructing and calling assign. } template template - inline basic_string::basic_string(CtorConvert, const OtherCharType* p, size_type n, const allocator_type& allocator) + inline basic_string::basic_string(CtorConvert, const OtherCharType* p, size_type n, const allocator_type& allocator) : mPair(allocator) { - AllocateSelf(); // In this case we are converting from one string encoding to another, and we + AllocateSelf(); // In this case we are converting from one string encoding to another, and we append_convert(p, n); // implement this in the simplest way, by simply default-constructing and calling assign. } @@ -835,14 +898,14 @@ namespace eastl } - // CtorDoNotInitialize exists so that we can create a version that allocates but doesn't + // CtorDoNotInitialize exists so that we can create a version that allocates but doesn't // initialize but also doesn't collide with any other constructor declaration. template basic_string::basic_string(CtorDoNotInitialize /*unused*/, size_type n, const allocator_type& allocator) : mPair(allocator) { // Note that we do not call SizeInitialize here. - AllocateSelf(n + 1); // '+1' so that we have room for the terminating 0. + AllocateSelf(n); *internalLayout().EndPtr() = 0; } @@ -853,8 +916,8 @@ namespace eastl basic_string::basic_string(CtorSprintf /*unused*/, const value_type* pFormat, ...) : mPair() { - const size_type n = (size_type)CharStrlen(pFormat) + 1; // We'll need at least this much. '+1' so that we have room for the terminating 0. - AllocateSelf(n); + const size_type n = (size_type)CharStrlen(pFormat); + AllocateSelf(n); va_list arguments; va_start(arguments, pFormat); @@ -880,6 +943,7 @@ namespace eastl x.AllocateSelf(); } + template basic_string::basic_string(this_type&& x, const allocator_type& allocator) : mPair(allocator) @@ -1042,15 +1106,22 @@ namespace eastl template inline bool basic_string::empty() const EA_NOEXCEPT { - return (internalLayout().BeginPtr() == internalLayout().EndPtr()); - } + return (internalLayout().GetSize() == 0); + } + + + template + inline bool basic_string::IsSSO() const EA_NOEXCEPT + { + return internalLayout().IsSSO(); + } template inline typename basic_string::size_type basic_string::size() const EA_NOEXCEPT { - return (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + return internalLayout().GetSize(); } @@ -1058,7 +1129,7 @@ namespace eastl inline typename basic_string::size_type basic_string::length() const EA_NOEXCEPT { - return (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + return internalLayout().GetSize(); } @@ -1074,7 +1145,11 @@ namespace eastl inline typename basic_string::size_type basic_string::capacity() const EA_NOEXCEPT { - return (size_type)((internalLayout().CapacityPtr() - internalLayout().BeginPtr()) - 1); // '-1' because we pretend that we didn't allocate memory for the terminating 0. + if (internalLayout().IsHeap()) + { + return internalLayout().GetHeapCapacity(); + } + return SSOLayout::SSO_CAPACITY; } @@ -1083,7 +1158,7 @@ namespace eastl basic_string::operator[](size_type n) const { #if EASTL_ASSERT_ENABLED // We allow the user to reference the trailing 0 char without asserting. Perhaps we shouldn't. - if(EASTL_UNLIKELY(n > (static_cast(internalLayout().EndPtr() - internalLayout().BeginPtr())))) + if(EASTL_UNLIKELY(n > internalLayout().GetSize())) EASTL_FAIL_MSG("basic_string::operator[] -- out of range"); #endif @@ -1096,7 +1171,7 @@ namespace eastl basic_string::operator[](size_type n) { #if EASTL_ASSERT_ENABLED // We allow the user to reference the trailing 0 char without asserting. Perhaps we shouldn't. - if(EASTL_UNLIKELY(n > (static_cast(internalLayout().EndPtr() - internalLayout().BeginPtr())))) + if(EASTL_UNLIKELY(n > internalLayout().GetSize())) EASTL_FAIL_MSG("basic_string::operator[] -- out of range"); #endif @@ -1150,7 +1225,7 @@ namespace eastl template inline void basic_string::DoAssignConvert(const StringType& x, false_type) { - //if(&x != this) // Unnecessary because &x cannot possibly equal this. + //if(&x != this) // Unnecessary because &x cannot possibly equal this. { #if EASTL_ALLOCATOR_COPY_ENABLED get_allocator() = x.get_allocator(); @@ -1210,7 +1285,7 @@ namespace eastl template void basic_string::resize(size_type n, value_type c) { - const size_type s = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + const size_type s = internalLayout().GetSize(); if(n < s) erase(internalLayout().BeginPtr() + n, internalLayout().EndPtr()); @@ -1222,12 +1297,12 @@ namespace eastl template void basic_string::resize(size_type n) { - // C++ basic_string specifies that resize(n) is equivalent to resize(n, value_type()). + // C++ basic_string specifies that resize(n) is equivalent to resize(n, value_type()). // For built-in types, value_type() is the same as zero (value_type(0)). - // We can improve the efficiency (especially for long strings) of this + // We can improve the efficiency (especially for long strings) of this // string class by resizing without assigning to anything. - - const size_type s = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + + const size_type s = internalLayout().GetSize(); if(n < s) erase(internalLayout().BeginPtr() + n, internalLayout().EndPtr()); @@ -1235,7 +1310,7 @@ namespace eastl { #if EASTL_STRING_OPT_CHAR_INIT append(n - s, value_type()); - #else + #else append(n - s); #endif } @@ -1246,48 +1321,77 @@ namespace eastl void basic_string::reserve(size_type n) { #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY(n > kMaxSize)) + if(EASTL_UNLIKELY(n > max_size())) ThrowLengthException(); #endif - // The C++ standard for basic_string doesn't specify if we should or shouldn't - // downsize the container. The standard is overly vague in its description of reserve: - // The member function reserve() is a directive that informs a - // basic_string object of a planned change in size, so that it - // can manage the storage allocation accordingly. - // We will act like the vector container and preserve the contents of - // the container and only reallocate if increasing the size. The user - // can use the set_capacity function to reduce the capacity. + // C++20 says if the passed in capacity is less than the current capacity we do not shrink + // If new_cap is less than or equal to the current capacity(), there is no effect. + // http://en.cppreference.com/w/cpp/string/basic_string/reserve n = eastl::max_alt(n, internalLayout().GetSize()); // Calculate the new capacity, which needs to be >= container size. - if(n >= (size_type)(internalLayout().GetRemainingCapacity())) // If there is something to do... // We use >= because mpCapacity accounts for the trailing zero. + if(n > capacity()) set_capacity(n); } + template + inline void basic_string::shrink_to_fit() + { + set_capacity(internalLayout().GetSize()); + } + + template inline void basic_string::set_capacity(size_type n) { - if(n == npos) // If the user wants to set the capacity to equal the current size... // '-1' because we pretend that we didn't allocate memory for the terminating 0. - n = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); - else if(n < (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr())) - internalLayout().SetEndPtr(internalLayout().BeginPtr() + n); + if(n == npos) + // If the user wants to set the capacity to equal the current size... + // '-1' because we pretend that we didn't allocate memory for the terminating 0. + n = internalLayout().GetSize(); + else if(n < internalLayout().GetSize()) + { + internalLayout().SetSize(n); + *internalLayout().EndPtr() = 0; + } - if(n != (size_type)((internalLayout().CapacityPtr() - internalLayout().BeginPtr()) - 1)) // If there is any capacity change... + if((n < capacity() && internalLayout().IsHeap()) || (n > capacity())) { - if(n) + // In here the string is transition from heap->heap, heap->sso or sso->heap + + if(EASTL_LIKELY(n)) { + + if(n <= SSOLayout::SSO_CAPACITY) + { + // heap->sso + // A heap based layout wants to reduce its size to within sso capacity + // An sso layout wanting to reduce its capacity will not get in here + pointer pOldBegin = internalLayout().BeginPtr(); + const size_type nOldCap = internalLayout().GetHeapCapacity(); + + internalLayout().ResetToSSO(); // reset layout to sso + CharStringUninitializedCopy(pOldBegin, pOldBegin + n, internalLayout().BeginPtr()); + // *EndPtr() = 0 is already done by the ResetToSSO + internalLayout().SetSSOSize(n); + + DoFree(pOldBegin, nOldCap + 1); + + return; + } + pointer pNewBegin = DoAllocate(n + 1); // We need the + 1 to accomodate the trailing 0. - pointer pNewEnd = pNewBegin; + size_type nSavedSize = internalLayout().GetSize(); // save the size in case we transition from sso->heap - pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pNewBegin); - *pNewEnd = 0; + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pNewBegin); + *pNewEnd = 0; DeallocateSelf(); - internalLayout().SetBeginPtr(pNewBegin); - internalLayout().SetEndPtr(pNewEnd); - internalLayout().SetCapacityPtr(pNewBegin + (n + 1)); + + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(n); + internalLayout().SetHeapSize(nSavedSize); } else { @@ -1302,30 +1406,27 @@ namespace eastl inline void basic_string::force_size(size_type n) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(n >= (size_type)(internalLayout().CapacityPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(n > capacity())) ThrowRangeException(); #elif EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(n >= (size_type)(internalLayout().CapacityPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(n > capacity())) EASTL_FAIL_MSG("basic_string::force_size -- out of range"); #endif - internalLayout().SetEndPtr(internalLayout().BeginPtr() + n); + internalLayout().SetSize(n); } template inline void basic_string::clear() EA_NOEXCEPT { - if(internalLayout().BeginPtr() != internalLayout().EndPtr()) - { - *internalLayout().BeginPtr() = value_type(0); - internalLayout().SetEndPtr(internalLayout().BeginPtr()); - } - } + internalLayout().SetSize(0); + *internalLayout().BeginPtr() = value_type(0); + } template - inline typename basic_string::pointer + inline typename basic_string::pointer basic_string::detach() EA_NOEXCEPT { // The detach function is an extension function which simply forgets the @@ -1339,9 +1440,9 @@ namespace eastl if (internalLayout().IsSSO()) { - const size_type n = internalLayout().GetSize() + 1; // We'll need at least this much. '+1' so that we have room for the terminating 0. + const size_type n = internalLayout().GetSize() + 1; // +1' so that we have room for the terminating 0. pDetached = DoAllocate(n); - auto* pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pDetached); + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pDetached); *pNewEnd = 0; } else @@ -1359,10 +1460,10 @@ namespace eastl basic_string::at(size_type n) const { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(n >= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(n >= internalLayout().GetSize())) ThrowRangeException(); #elif EASTL_ASSERT_ENABLED // We assert if the user references the trailing 0 char. - if(EASTL_UNLIKELY(n >= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(n >= internalLayout().GetSize())) EASTL_FAIL_MSG("basic_string::at -- out of range"); #endif @@ -1375,10 +1476,10 @@ namespace eastl basic_string::at(size_type n) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(n >= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(n >= internalLayout().GetSize())) ThrowRangeException(); #elif EASTL_ASSERT_ENABLED // We assert if the user references the trailing 0 char. - if(EASTL_UNLIKELY(n >= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(n >= internalLayout().GetSize())) EASTL_FAIL_MSG("basic_string::at -- out of range"); #endif @@ -1393,7 +1494,7 @@ namespace eastl #if EASTL_EMPTY_REFERENCE_ASSERT_ENABLED // We allow the user to reference the trailing 0 char without asserting. #elif EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(internalLayout().EndPtr() <= internalLayout().BeginPtr())) // We assert if the user references the trailing 0 char. + if(EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. EASTL_FAIL_MSG("basic_string::front -- empty string"); #endif @@ -1408,7 +1509,7 @@ namespace eastl #if EASTL_EMPTY_REFERENCE_ASSERT_ENABLED // We allow the user to reference the trailing 0 char without asserting. #elif EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(internalLayout().EndPtr() <= internalLayout().BeginPtr())) // We assert if the user references the trailing 0 char. + if(EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. EASTL_FAIL_MSG("basic_string::front -- empty string"); #endif @@ -1423,7 +1524,7 @@ namespace eastl #if EASTL_EMPTY_REFERENCE_ASSERT_ENABLED // We allow the user to reference the trailing 0 char without asserting. #elif EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(internalLayout().EndPtr() <= internalLayout().BeginPtr())) // We assert if the user references the trailing 0 char. + if(EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. EASTL_FAIL_MSG("basic_string::back -- empty string"); #endif @@ -1438,7 +1539,7 @@ namespace eastl #if EASTL_EMPTY_REFERENCE_ASSERT_ENABLED // We allow the user to reference the trailing 0 char without asserting. #elif EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(internalLayout().EndPtr() <= internalLayout().BeginPtr())) // We assert if the user references the trailing 0 char. + if(EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. EASTL_FAIL_MSG("basic_string::back -- empty string"); #endif @@ -1479,11 +1580,11 @@ namespace eastl inline basic_string& basic_string::append(const this_type& x, size_type position, size_type n) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()))) // position must be < x.mpEnd, but position + n may be > mpEnd. + if(EASTL_UNLIKELY(position >= x.internalLayout().GetSize())) // position must be < x.mpEnd, but position + n may be > mpEnd. ThrowRangeException(); #endif - return append(x.internalLayout().BeginPtr() + position, + return append(x.internalLayout().BeginPtr() + position, x.internalLayout().BeginPtr() + position + eastl::min_alt(n, x.internalLayout().GetSize() - position)); } @@ -1539,7 +1640,7 @@ namespace eastl value_type* pSelfBufferCurrent = selfBuffer; DecodePart(pOther, pOtherEnd, pSelfBufferCurrent, selfBufferEnd); // Write pOther to pSelfBuffer, converting encoding as we go. We currently ignore the return value, as we don't yet have a plan for handling encoding errors. append(selfBuffer, pSelfBufferCurrent); - } + } return *this; } @@ -1548,24 +1649,23 @@ namespace eastl template basic_string& basic_string::append(size_type n, value_type c) { - const size_type s = (size_type)(internalLayout().GetSize()); + const size_type nSize = internalLayout().GetSize(); #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY((n > kMaxSize) || (s > (kMaxSize - n)))) + if(EASTL_UNLIKELY((n > max_size()) || (nSize > (max_size() - n)))) ThrowLengthException(); #endif - const size_type nCapacity = internalLayout().GetRemainingCapacity() - 1; + const size_type nCapacity = capacity(); - if((s + n) > nCapacity) - reserve(eastl::max_alt((size_type)GetNewCapacity(nCapacity), (size_type)(s + n))); + if((nSize + n) > nCapacity) + reserve(eastl::max_alt(GetNewCapacity(nCapacity), (nSize + n))); if(n > 0) { - CharStringUninitializedFillN(internalLayout().EndPtr() + 1, n - 1, c); - *internalLayout().EndPtr() = c; - internalLayout().SetEndPtr(internalLayout().EndPtr() + n); - *internalLayout().EndPtr() = 0; + pointer pNewEnd = CharStringUninitializedFillN(internalLayout().EndPtr(), n, c); + *pNewEnd = 0; + internalLayout().SetSize(nSize + n); } return *this; @@ -1577,44 +1677,40 @@ namespace eastl { if(pBegin != pEnd) { - const size_type nOldSize = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + const size_type nOldSize = internalLayout().GetSize(); const size_type n = (size_type)(pEnd - pBegin); #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY(((size_t)n > kMaxSize) || (nOldSize > (kMaxSize - n)))) + if(EASTL_UNLIKELY((n > max_size()) || (nOldSize > (max_size() - n)))) ThrowLengthException(); #endif - const size_type nCapacity = (size_type)((internalLayout().CapacityPtr() - internalLayout().BeginPtr()) - 1); + const size_type nCapacity = capacity(); if((nOldSize + n) > nCapacity) { - const size_type nLength = eastl::max_alt((size_type)GetNewCapacity(nCapacity), (size_type)(nOldSize + n)) + 1; // + 1 to accomodate the trailing 0. + const size_type nLength = eastl::max_alt(GetNewCapacity(nCapacity), (nOldSize + n)); - pointer pNewBegin = DoAllocate(nLength); - pointer pNewEnd = pNewBegin; + pointer pNewBegin = DoAllocate(nLength + 1); - pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pNewBegin); - pNewEnd = CharStringUninitializedCopy(pBegin, pEnd, pNewEnd); - *pNewEnd = 0; + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pNewBegin); + pNewEnd = CharStringUninitializedCopy(pBegin, pEnd, pNewEnd); + *pNewEnd = 0; DeallocateSelf(); - internalLayout().SetBeginPtr(pNewBegin); - internalLayout().SetEndPtr(pNewEnd); - internalLayout().SetCapacityPtr(pNewBegin + nLength); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nLength); + internalLayout().SetHeapSize(nOldSize + n); } else { - const value_type* pTemp = pBegin; - ++pTemp; - CharStringUninitializedCopy(pTemp, pEnd, internalLayout().EndPtr() + 1); - internalLayout().EndPtr()[n] = 0; - *internalLayout().EndPtr() = *pBegin; - internalLayout().SetEndPtr(internalLayout().EndPtr() + n); + pointer pNewEnd = CharStringUninitializedCopy(pBegin, pEnd, internalLayout().EndPtr()); + *pNewEnd = 0; + internalLayout().SetSize(nOldSize + n); } } - return *this; + return *this; } @@ -1641,23 +1737,25 @@ namespace eastl va_copy(argumentsSaved, arguments); #endif - nReturnValue = eastl::Vsnprintf(internalLayout().EndPtr(), (size_t)internalLayout().GetRemainingCapacity(), pFormat, arguments); + nReturnValue = eastl::Vsnprintf(internalLayout().EndPtr(), (size_t)internalLayout().GetRemainingCapacity() + 1, pFormat, arguments); - if(nReturnValue >= (int)internalLayout().GetRemainingCapacity()) // If there wasn't enough capacity... + if(nReturnValue > (int)internalLayout().GetRemainingCapacity()) // If there wasn't enough capacity... { // In this case we definitely have C99 Vsnprintf behaviour. #if EASTL_VA_COPY_ENABLED va_end(arguments); va_copy(arguments, argumentsSaved); #endif - resize(nInitialSize + nReturnValue); - nReturnValue = eastl::Vsnprintf(internalLayout().BeginPtr() + nInitialSize, (size_t)(nReturnValue + 1), pFormat, arguments); // '+1' because vsnprintf wants to know the size of the buffer including the terminating zero. + // We must reset the size back to where we were incase we overrun the SSO buffer and '0' gets written into sso.mnSize. Then our size would have changed, which will affect .EndPtr() calcs + internalLayout().SetSize(nInitialSize); + reserve(nInitialSize + nReturnValue); + nReturnValue = eastl::Vsnprintf(internalLayout().EndPtr(), (size_t)internalLayout().GetRemainingCapacity() + 1, pFormat, arguments); // '+1' because vsnprintf wants to know the size of the buffer including the terminating zero. } else if(nReturnValue < 0) // If vsnprintf is non-C99-standard (e.g. it is VC++ _vsnprintf)... { // In this case we either have C89 extension behaviour or C99 behaviour. size_type n = eastl::max_alt((size_type)(EASTL_STRING_INITIAL_CAPACITY - 1), (size_type)(size() * 2)); // '-1' because the resize call below will add one for NULL terminator and we want to keep allocations on fixed block sizes. - + for(; (nReturnValue < 0) && (n < 1000000); n *= 2) { #if EASTL_VA_COPY_ENABLED @@ -1676,15 +1774,15 @@ namespace eastl } } } - + if(nReturnValue >= 0) - internalLayout().SetEndPtr(internalLayout().BeginPtr() + nInitialSize + nReturnValue); // We are guaranteed from the above logic that mpEnd <= mpCapacity. + internalLayout().SetSize(nInitialSize + nReturnValue); #if EASTL_VA_COPY_ENABLED // va_end for arguments will be called by the caller. va_end(argumentsSaved); #endif - + return *this; } @@ -1695,7 +1793,7 @@ namespace eastl va_start(arguments, pFormat); append_sprintf_va_list(pFormat, arguments); va_end(arguments); - + return *this; } @@ -1703,18 +1801,7 @@ namespace eastl template inline void basic_string::push_back(value_type c) { - auto& il = internalLayout(); - auto pEndPtr = il.EndPtr() + 1; - auto pCapacityPtr = il.CapacityPtr(); - - if ((pEndPtr >= pCapacityPtr) || (size_type)(pCapacityPtr - pEndPtr) < sizeof(value_type)) // If we are out of space... (note that we test for + 1 because we have a trailing 0) - reserve(eastl::max_alt(GetNewCapacity((size_type)((il.CapacityPtr() - il.BeginPtr()) - 1)), - (size_type)(il.EndPtr() - il.BeginPtr()) + 1)); - - pEndPtr = il.EndPtr(); // re-fetch endptr b/c we could have reallocated in the above reserve - *pEndPtr++ = c; - *pEndPtr = 0; - il.SetEndPtr(pEndPtr); + append((size_type)1, c); } @@ -1722,19 +1809,19 @@ namespace eastl inline void basic_string::pop_back() { #if EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(internalLayout().EndPtr() <= internalLayout().BeginPtr())) + if(EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) EASTL_FAIL_MSG("basic_string::pop_back -- empty string"); #endif internalLayout().EndPtr()[-1] = value_type(0); - internalLayout().SetEndPtr(internalLayout().EndPtr() - 1); + internalLayout().SetSize(internalLayout().GetSize() - 1); } template inline basic_string& basic_string::assign(const this_type& x) { - // The C++11 Standard 21.4.6.3 p6 specifies that assign from this_type assigns contents only and not the allocator. + // The C++11 Standard 21.4.6.3 p6 specifies that assign from this_type assigns contents only and not the allocator. return assign(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); } @@ -1743,15 +1830,14 @@ namespace eastl inline basic_string& basic_string::assign(const this_type& x, size_type position, size_type n) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > x.internalLayout().GetSize())) ThrowRangeException(); #endif // The C++11 Standard 21.4.6.3 p6 specifies that assign from this_type assigns contents only and not the allocator. return assign( x.internalLayout().BeginPtr() + position, - x.internalLayout().BeginPtr() + position + - eastl::min_alt(n, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()) - position)); + x.internalLayout().BeginPtr() + position + eastl::min_alt(n, x.internalLayout().GetSize() - position)); } @@ -1772,15 +1858,15 @@ namespace eastl template basic_string& basic_string::assign(size_type n, value_type c) { - if(n <= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr())) + if(n <= internalLayout().GetSize()) { CharTypeAssignN(internalLayout().BeginPtr(), n, c); erase(internalLayout().BeginPtr() + n, internalLayout().EndPtr()); } else { - CharTypeAssignN(internalLayout().BeginPtr(), (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()), c); - append(n - (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()), c); + CharTypeAssignN(internalLayout().BeginPtr(), internalLayout().GetSize(), c); + append(n - internalLayout().GetSize(), c); } return *this; } @@ -1789,16 +1875,16 @@ namespace eastl template basic_string& basic_string::assign(const value_type* pBegin, const value_type* pEnd) { - const ptrdiff_t n = pEnd - pBegin; - if(static_cast(n) <= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr())) + const size_type n = (size_type)(pEnd - pBegin); + if(n <= internalLayout().GetSize()) { memmove(internalLayout().BeginPtr(), pBegin, (size_t)n * sizeof(value_type)); erase(internalLayout().BeginPtr() + n, internalLayout().EndPtr()); } else { - memmove(internalLayout().BeginPtr(), pBegin, (size_t)(internalLayout().EndPtr() - internalLayout().BeginPtr()) * sizeof(value_type)); - append(pBegin + (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()), pEnd); + memmove(internalLayout().BeginPtr(), pBegin, (size_t)(internalLayout().GetSize()) * sizeof(value_type)); + append(pBegin + internalLayout().GetSize(), pEnd); } return *this; } @@ -1859,12 +1945,12 @@ namespace eastl basic_string& basic_string::insert(size_type position, const this_type& x) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) > (kMaxSize - (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())))) + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - x.internalLayout().GetSize()))) ThrowLengthException(); #endif @@ -1877,14 +1963,14 @@ namespace eastl basic_string& basic_string::insert(size_type position, const this_type& x, size_type beg, size_type n) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY((position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr())) || (beg > (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())))) + if(EASTL_UNLIKELY((position > internalLayout().GetSize()) || (beg > x.internalLayout().GetSize()))) ThrowRangeException(); #endif - size_type nLength = eastl::min_alt(n, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()) - beg); + size_type nLength = eastl::min_alt(n, x.internalLayout().GetSize() - beg); #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) > (kMaxSize - nLength))) + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - nLength))) ThrowLengthException(); #endif @@ -1897,12 +1983,12 @@ namespace eastl basic_string& basic_string::insert(size_type position, const value_type* p, size_type n) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) > (kMaxSize - n))) + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - n))) ThrowLengthException(); #endif @@ -1915,14 +2001,14 @@ namespace eastl basic_string& basic_string::insert(size_type position, const value_type* p) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif size_type nLength = (size_type)CharStrlen(p); #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) > (kMaxSize - nLength))) + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - nLength))) ThrowLengthException(); #endif @@ -1935,12 +2021,12 @@ namespace eastl basic_string& basic_string::insert(size_type position, size_type n, value_type c) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) > (kMaxSize - n))) + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - n))) ThrowLengthException(); #endif @@ -1966,7 +2052,7 @@ namespace eastl typename basic_string::iterator basic_string::insert(const_iterator p, size_type n, value_type c) { - const ptrdiff_t nPosition = (p - internalLayout().BeginPtr()); // Save this because we might reallocate. + const difference_type nPosition = (p - internalLayout().BeginPtr()); // Save this because we might reallocate. #if EASTL_ASSERT_ENABLED if(EASTL_UNLIKELY((p < internalLayout().BeginPtr()) || (p > internalLayout().EndPtr()))) @@ -1975,34 +2061,42 @@ namespace eastl if(n) // If there is anything to insert... { - if(size_type(internalLayout().CapacityPtr() - internalLayout().EndPtr()) >= (n + 1)) // If we have enough capacity... + if(internalLayout().GetRemainingCapacity() >= n) // If we have enough capacity... { const size_type nElementsAfter = (size_type)(internalLayout().EndPtr() - p); - iterator pOldEnd = internalLayout().EndPtr(); if(nElementsAfter >= n) // If there's enough space for the new chars between the insert position and the end... { + // Ensure we save the size before we do the copy, as we might overwrite the size field with the NULL + // terminator in the edge case where we are inserting enough characters to equal our capacity + const size_type nSavedSize = internalLayout().GetSize(); CharStringUninitializedCopy((internalLayout().EndPtr() - n) + 1, internalLayout().EndPtr() + 1, internalLayout().EndPtr() + 1); - internalLayout().SetEndPtr(internalLayout().EndPtr() + n); + internalLayout().SetSize(nSavedSize + n); memmove(const_cast(p) + n, p, (size_t)((nElementsAfter - n) + 1) * sizeof(value_type)); CharTypeAssignN(const_cast(p), n, c); } else { + pointer pOldEnd = internalLayout().EndPtr(); + #if EASTL_EXCEPTIONS_ENABLED + const size_type nOldSize = internalLayout().GetSize(); + #endif CharStringUninitializedFillN(internalLayout().EndPtr() + 1, n - nElementsAfter - 1, c); - internalLayout().SetEndPtr(internalLayout().EndPtr() + (n - nElementsAfter)); + internalLayout().SetSize(internalLayout().GetSize() + (n - nElementsAfter)); #if EASTL_EXCEPTIONS_ENABLED try { #endif + // See comment in if block above + const size_type nSavedSize = internalLayout().GetSize(); CharStringUninitializedCopy(p, pOldEnd + 1, internalLayout().EndPtr()); - internalLayout().SetEndPtr(internalLayout().EndPtr() + nElementsAfter); + internalLayout().SetSize(nSavedSize + nElementsAfter); #if EASTL_EXCEPTIONS_ENABLED } catch(...) { - internalLayout().EndPtr() = pOldEnd; + internalLayout().SetSize(nOldSize); throw; } #endif @@ -2012,22 +2106,21 @@ namespace eastl } else { - const size_type nOldSize = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); - const size_type nOldCap = (size_type)((internalLayout().CapacityPtr() - internalLayout().BeginPtr()) - 1); - const size_type nLength = eastl::max_alt((size_type)GetNewCapacity(nOldCap), (size_type)(nOldSize + n)) + 1; // + 1 to accomodate the trailing 0. + const size_type nOldSize = internalLayout().GetSize(); + const size_type nOldCap = capacity(); + const size_type nLength = eastl::max_alt(GetNewCapacity(nOldCap), nOldSize + n); - iterator pNewBegin = DoAllocate(nLength); - iterator pNewEnd = pNewBegin; + iterator pNewBegin = DoAllocate(nLength + 1); - pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), p, pNewBegin); - pNewEnd = CharStringUninitializedFillN(pNewEnd, n, c); - pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); - *pNewEnd = 0; + iterator pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), p, pNewBegin); + pNewEnd = CharStringUninitializedFillN(pNewEnd, n, c); + pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); + *pNewEnd = 0; DeallocateSelf(); - internalLayout().SetBeginPtr(pNewBegin); - internalLayout().SetEndPtr(pNewEnd); - internalLayout().SetCapacityPtr(pNewBegin + nLength); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nLength); + internalLayout().SetHeapSize(nOldSize + n); } } @@ -2039,7 +2132,7 @@ namespace eastl typename basic_string::iterator basic_string::insert(const_iterator p, const value_type* pBegin, const value_type* pEnd) { - const ptrdiff_t nPosition = (p - internalLayout().BeginPtr()); // Save this because we might reallocate. + const difference_type nPosition = (p - internalLayout().BeginPtr()); // Save this because we might reallocate. #if EASTL_ASSERT_ENABLED if(EASTL_UNLIKELY((p < internalLayout().BeginPtr()) || (p > internalLayout().EndPtr()))) @@ -2050,74 +2143,92 @@ namespace eastl if(n) { - const bool bCapacityIsSufficient = ((internalLayout().CapacityPtr() - internalLayout().EndPtr()) >= (difference_type)(n + 1)); + const bool bCapacityIsSufficient = (internalLayout().GetRemainingCapacity() >= n); const bool bSourceIsFromSelf = ((pEnd >= internalLayout().BeginPtr()) && (pBegin <= internalLayout().EndPtr())); - // If bSourceIsFromSelf is true, then we reallocate. This is because we are - // inserting ourself into ourself and thus both the source and destination + if(bSourceIsFromSelf && internalLayout().IsSSO()) + { + // pBegin to pEnd will be <= this->GetSize(), so stackTemp will guaranteed be an SSO String + // If we are inserting ourself into ourself and we are SSO, then on the recursive call we can + // guarantee 0 or 1 allocation depending if we need to realloc + // We don't do this for Heap strings as then this path may do 1 or 2 allocations instead of + // only 1 allocation when we fall through to the last else case below + const this_type stackTemp(pBegin, pEnd, allocator_type()); + return insert(p, stackTemp.data(), stackTemp.data() + stackTemp.size()); + } + + // If bSourceIsFromSelf is true, then we reallocate. This is because we are + // inserting ourself into ourself and thus both the source and destination // be modified, making it rather tricky to attempt to do in place. The simplest // resolution is to reallocate. To consider: there may be a way to implement this // whereby we don't need to reallocate or can often avoid reallocating. if(bCapacityIsSufficient && !bSourceIsFromSelf) { - const ptrdiff_t nElementsAfter = (internalLayout().EndPtr() - p); - iterator pOldEnd = internalLayout().EndPtr(); + const size_type nElementsAfter = (size_type)(internalLayout().EndPtr() - p); - if(nElementsAfter >= (ptrdiff_t)n) // If the newly inserted characters entirely fit within the size of the original string... + if(nElementsAfter >= n) // If there are enough characters between insert pos and end { - memmove(internalLayout().EndPtr() + 1, internalLayout().EndPtr() - n + 1, (size_t)n * sizeof(value_type)); - internalLayout().SetEndPtr(internalLayout().EndPtr() + n); + // Ensure we save the size before we do the copy, as we might overwrite the size field with the NULL + // terminator in the edge case where we are inserting enough characters to equal our capacity + const size_type nSavedSize = internalLayout().GetSize(); + CharStringUninitializedCopy((internalLayout().EndPtr() - n) + 1, internalLayout().EndPtr() + 1, internalLayout().EndPtr() + 1); + internalLayout().SetSize(nSavedSize + n); memmove(const_cast(p) + n, p, (size_t)((nElementsAfter - n) + 1) * sizeof(value_type)); - memmove(const_cast(p), pBegin, (size_t)(pEnd - pBegin) * sizeof(value_type)); + memmove(const_cast(p), pBegin, (size_t)(n) * sizeof(value_type)); } else { + pointer pOldEnd = internalLayout().EndPtr(); + #if EASTL_EXCEPTIONS_ENABLED + const size_type nOldSize = internalLayout().GetSize(); + #endif const value_type* const pMid = pBegin + (nElementsAfter + 1); - memmove(internalLayout().EndPtr() + 1, pMid, (size_t)(pEnd - pMid) * sizeof(value_type)); - internalLayout().SetEndPtr(internalLayout().EndPtr() + (n - nElementsAfter)); + CharStringUninitializedCopy(pMid, pEnd, internalLayout().EndPtr() + 1); + internalLayout().SetSize(internalLayout().GetSize() + (n - nElementsAfter)); #if EASTL_EXCEPTIONS_ENABLED try { #endif - memmove(internalLayout().EndPtr(), p, (size_t)(pOldEnd - p + 1) * sizeof(value_type)); - internalLayout().SetEndPtr(internalLayout().EndPtr() + nElementsAfter); + // See comment in if block above + const size_type nSavedSize = internalLayout().GetSize(); + CharStringUninitializedCopy(p, pOldEnd + 1, internalLayout().EndPtr()); + internalLayout().SetSize(nSavedSize + nElementsAfter); #if EASTL_EXCEPTIONS_ENABLED } catch(...) { - internalLayout().EndPtr() = pOldEnd; + internalLayout().SetSize(nOldSize); throw; } #endif - memmove(const_cast(p), pBegin, (size_t)(pMid - pBegin) * sizeof(value_type)); + CharStringUninitializedCopy(pBegin, pMid, const_cast(p)); } } else // Else we need to reallocate to implement this. { - const size_type nOldSize = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); - const size_type nOldCap = (size_type)((internalLayout().CapacityPtr() - internalLayout().BeginPtr()) - 1); + const size_type nOldSize = internalLayout().GetSize(); + const size_type nOldCap = capacity(); size_type nLength; - if(bCapacityIsSufficient) // If bCapacityIsSufficient is true, then bSourceIsFromSelf must be false. - nLength = nOldSize + n + 1; // + 1 to accomodate the trailing 0. + if(bCapacityIsSufficient) // If bCapacityIsSufficient is true, then bSourceIsFromSelf must be true. + nLength = nOldSize + n; else - nLength = eastl::max_alt((size_type)GetNewCapacity(nOldCap), (size_type)(nOldSize + n)) + 1; // + 1 to accomodate the trailing 0. + nLength = eastl::max_alt(GetNewCapacity(nOldCap), (nOldSize + n)); - pointer pNewBegin = DoAllocate(nLength); - pointer pNewEnd = pNewBegin; + pointer pNewBegin = DoAllocate(nLength + 1); - pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), p, pNewBegin); - pNewEnd = CharStringUninitializedCopy(pBegin, pEnd, pNewEnd); - pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); - *pNewEnd = 0; + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), p, pNewBegin); + pNewEnd = CharStringUninitializedCopy(pBegin, pEnd, pNewEnd); + pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); + *pNewEnd = 0; DeallocateSelf(); - internalLayout().SetBeginPtr(pNewBegin); - internalLayout().SetEndPtr(pNewEnd); - internalLayout().SetCapacityPtr(pNewBegin + nLength); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nLength); + internalLayout().SetHeapSize(nOldSize + n); } } @@ -2137,21 +2248,20 @@ namespace eastl inline basic_string& basic_string::erase(size_type position, size_type n) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif #if EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) EASTL_FAIL_MSG("basic_string::erase -- invalid position"); #endif erase(internalLayout().BeginPtr() + position, - internalLayout().BeginPtr() + position + - eastl::min_alt(n, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - position)); + internalLayout().BeginPtr() + position + eastl::min_alt(n, internalLayout().GetSize() - position)); return *this; - } + } template @@ -2164,7 +2274,7 @@ namespace eastl #endif memmove(const_cast(p), p + 1, (size_t)(internalLayout().EndPtr() - p) * sizeof(value_type)); - internalLayout().SetEndPtr(internalLayout().EndPtr() - 1); + internalLayout().SetSize(internalLayout().GetSize() - 1); return const_cast(p); } @@ -2182,8 +2292,8 @@ namespace eastl if(pBegin != pEnd) { memmove(const_cast(pBegin), pEnd, (size_t)((internalLayout().EndPtr() - pEnd) + 1) * sizeof(value_type)); - const iterator pNewEnd = (internalLayout().EndPtr() - (pEnd - pBegin)); - internalLayout().SetEndPtr(pNewEnd); + const size_type n = (size_type)(pEnd - pBegin); + internalLayout().SetSize(internalLayout().GetSize() - n); } return const_cast(pBegin); } @@ -2209,14 +2319,14 @@ namespace eastl basic_string& basic_string::replace(size_type position, size_type n, const this_type& x) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif - const size_type nLength = eastl::min_alt(n, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - position); + const size_type nLength = eastl::min_alt(n, internalLayout().GetSize() - position); #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY(((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - nLength) >= (kMaxSize - (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())))) + if(EASTL_UNLIKELY((internalLayout().GetSize() - nLength) >= (max_size() - x.internalLayout().GetSize()))) ThrowLengthException(); #endif @@ -2228,15 +2338,15 @@ namespace eastl basic_string& basic_string::replace(size_type pos1, size_type n1, const this_type& x, size_type pos2, size_type n2) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY((pos1 > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr())) || (pos2 > (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())))) + if(EASTL_UNLIKELY((pos1 > internalLayout().GetSize()) || (pos2 > x.internalLayout().GetSize()))) ThrowRangeException(); #endif - const size_type nLength1 = eastl::min_alt(n1, (size_type)( internalLayout().EndPtr() - internalLayout().BeginPtr()) - pos1); - const size_type nLength2 = eastl::min_alt(n2, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()) - pos2); + const size_type nLength1 = eastl::min_alt(n1, internalLayout().GetSize() - pos1); + const size_type nLength2 = eastl::min_alt(n2, x.internalLayout().GetSize() - pos2); #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY(((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - nLength1) >= (kMaxSize - nLength2))) + if(EASTL_UNLIKELY((internalLayout().GetSize() - nLength1) >= (max_size() - nLength2))) ThrowLengthException(); #endif @@ -2248,14 +2358,14 @@ namespace eastl basic_string& basic_string::replace(size_type position, size_type n1, const value_type* p, size_type n2) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif - const size_type nLength = eastl::min_alt(n1, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - position); + const size_type nLength = eastl::min_alt(n1, internalLayout().GetSize() - position); #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY((n2 > kMaxSize) || (((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - nLength) >= (kMaxSize - n2)))) + if(EASTL_UNLIKELY((n2 > max_size()) || ((internalLayout().GetSize() - nLength) >= (max_size() - n2)))) ThrowLengthException(); #endif @@ -2267,15 +2377,15 @@ namespace eastl basic_string& basic_string::replace(size_type position, size_type n1, const value_type* p) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif - const size_type nLength = eastl::min_alt(n1, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - position); + const size_type nLength = eastl::min_alt(n1, internalLayout().GetSize() - position); #if EASTL_STRING_OPT_LENGTH_ERRORS const size_type n2 = (size_type)CharStrlen(p); - if(EASTL_UNLIKELY((n2 > kMaxSize) || (((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - nLength) >= (kMaxSize - n2)))) + if(EASTL_UNLIKELY((n2 > max_size()) || ((internalLayout().GetSize() - nLength) >= (max_size() - n2)))) ThrowLengthException(); #endif @@ -2287,14 +2397,14 @@ namespace eastl basic_string& basic_string::replace(size_type position, size_type n1, size_type n2, value_type c) { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif - const size_type nLength = eastl::min_alt(n1, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - position); + const size_type nLength = eastl::min_alt(n1, internalLayout().GetSize() - position); #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY((n2 > kMaxSize) || ((size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - nLength) >= (kMaxSize - n2))) + if(EASTL_UNLIKELY((n2 > max_size()) || (internalLayout().GetSize() - nLength) >= (max_size() - n2))) ThrowLengthException(); #endif @@ -2383,22 +2493,21 @@ namespace eastl else // else we have an overlapping operation. { // I can't think of any easy way of doing this without allocating temporary memory. - const size_type nOldSize = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); - const size_type nOldCap = (size_type)((internalLayout().CapacityPtr() - internalLayout().BeginPtr()) - 1); - const size_type nNewCapacity = eastl::max_alt((size_type)GetNewCapacity(nOldCap), (size_type)(nOldSize + (nLength2 - nLength1))) + 1; // + 1 to accomodate the trailing 0. + const size_type nOldSize = internalLayout().GetSize(); + const size_type nOldCap = capacity(); + const size_type nNewCapacity = eastl::max_alt(GetNewCapacity(nOldCap), (nOldSize + (nLength2 - nLength1))); - pointer pNewBegin = DoAllocate(nNewCapacity); - pointer pNewEnd = pNewBegin; + pointer pNewBegin = DoAllocate(nNewCapacity + 1); - pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), pBegin1, pNewBegin); - pNewEnd = CharStringUninitializedCopy(pBegin2, pEnd2, pNewEnd); - pNewEnd = CharStringUninitializedCopy(pEnd1, internalLayout().EndPtr(), pNewEnd); - *pNewEnd = 0; + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), pBegin1, pNewBegin); + pNewEnd = CharStringUninitializedCopy(pBegin2, pEnd2, pNewEnd); + pNewEnd = CharStringUninitializedCopy(pEnd1, internalLayout().EndPtr(), pNewEnd); + *pNewEnd = 0; DeallocateSelf(); - internalLayout().SetBeginPtr(pNewBegin); - internalLayout().SetEndPtr(pNewEnd); - internalLayout().SetCapacityPtr(pNewBegin + nNewCapacity); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nNewCapacity); + internalLayout().SetHeapSize(nOldSize + (nLength2 - nLength1)); } } return *this; @@ -2410,23 +2519,22 @@ namespace eastl basic_string::copy(value_type* p, size_type n, size_type position) const { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #endif - // It is not clear from the C++ standard if 'p' destination pointer is allowed to - // refer to memory from within the string itself. We assume so and use memmove - // instead of memcpy until we find otherwise. - const size_type nLength = eastl::min_alt(n, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - position); - memmove(p, internalLayout().BeginPtr() + position, (size_t)nLength * sizeof(value_type)); + // C++ std says the effects of this function are as if calling char_traits::copy() + // thus the 'p' must not overlap *this string, so we can use memcpy + const size_type nLength = eastl::min_alt(n, internalLayout().GetSize() - position); + CharStringUninitializedCopy(internalLayout().BeginPtr() + position, internalLayout().BeginPtr() + position + nLength, p); return nLength; } template - void basic_string::swap(this_type& x) + void basic_string::swap(this_type& x) { - if(get_allocator() == x.get_allocator()) // If allocators are equivalent... + if(get_allocator() == x.get_allocator() || (internalLayout().IsSSO() && x.internalLayout().IsSSO())) // If allocators are equivalent... { // We leave mAllocator as-is. eastl::swap(internalLayout(), x.internalLayout()); @@ -2444,7 +2552,7 @@ namespace eastl inline typename basic_string::size_type basic_string::find(const this_type& x, size_type position) const EA_NOEXCEPT { - return find(x.internalLayout().BeginPtr(), position, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())); + return find(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); } @@ -2467,7 +2575,7 @@ namespace eastl // EASTL_FAIL_MSG("basic_string::find -- invalid position"); //#endif - if(EASTL_LIKELY(((npos - n) >= position) && (position + n) <= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) // If the range is valid... + if(EASTL_LIKELY(((npos - n) >= position) && (position + n) <= internalLayout().GetSize())) // If the range is valid... { const value_type* const pTemp = eastl::search(internalLayout().BeginPtr() + position, internalLayout().EndPtr(), p, p + n); @@ -2489,7 +2597,7 @@ namespace eastl // EASTL_FAIL_MSG("basic_string::find -- invalid position"); //#endif - if(EASTL_LIKELY(position < (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) // If the position is valid... + if(EASTL_LIKELY(position < internalLayout().GetSize()))// If the position is valid... { const const_iterator pResult = eastl::find(internalLayout().BeginPtr() + position, internalLayout().EndPtr(), c); @@ -2504,7 +2612,7 @@ namespace eastl inline typename basic_string::size_type basic_string::rfind(const this_type& x, size_type position) const EA_NOEXCEPT { - return rfind(x.internalLayout().BeginPtr(), position, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())); + return rfind(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); } @@ -2520,8 +2628,8 @@ namespace eastl typename basic_string::size_type basic_string::rfind(const value_type* p, size_type position, size_type n) const { - // Disabled because it's not clear what values are valid for position. - // It is documented that npos is a valid value, though. We return npos and + // Disabled because it's not clear what values are valid for position. + // It is documented that npos is a valid value, though. We return npos and // don't crash if postion is any invalid value. //#if EASTL_ASSERT_ENABLED // if(EASTL_UNLIKELY((position != npos) && (position > (size_type)(mpEnd - mpBegin)))) @@ -2529,11 +2637,11 @@ namespace eastl //#endif // Note that a search for a zero length string starting at position = end() returns end() and not npos. - // Note by Paul Pedriana: I am not sure how this should behave in the case of n == 0 and position > size. - // The standard seems to suggest that rfind doesn't act exactly the same as find in that input position - // can be > size and the return value can still be other than npos. Thus, if n == 0 then you can + // Note by Paul Pedriana: I am not sure how this should behave in the case of n == 0 and position > size. + // The standard seems to suggest that rfind doesn't act exactly the same as find in that input position + // can be > size and the return value can still be other than npos. Thus, if n == 0 then you can // never return npos, unlike the case with find. - const size_type nLength = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + const size_type nLength = internalLayout().GetSize(); if(EASTL_LIKELY(n <= nLength)) { @@ -2557,7 +2665,7 @@ namespace eastl basic_string::rfind(value_type c, size_type position) const EA_NOEXCEPT { // If n is zero or position is >= size, we return npos. - const size_type nLength = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + const size_type nLength = internalLayout().GetSize(); if(EASTL_LIKELY(nLength)) { @@ -2575,12 +2683,12 @@ namespace eastl inline typename basic_string::size_type basic_string::find_first_of(const this_type& x, size_type position) const EA_NOEXCEPT { - return find_first_of(x.internalLayout().BeginPtr(), position, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())); + return find_first_of(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); } template - inline typename basic_string::size_type + inline typename basic_string::size_type basic_string::find_first_of(const value_type* p, size_type position) const { return find_first_of(p, position, (size_type)CharStrlen(p)); @@ -2592,7 +2700,7 @@ namespace eastl basic_string::find_first_of(const value_type* p, size_type position, size_type n) const { // If position is >= size, we return npos. - if(EASTL_LIKELY((position < (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr())))) + if(EASTL_LIKELY((position < internalLayout().GetSize()))) { const value_type* const pBegin = internalLayout().BeginPtr() + position; const const_iterator pResult = CharTypeStringFindFirstOf(pBegin, internalLayout().EndPtr(), p, p + n); @@ -2616,7 +2724,7 @@ namespace eastl inline typename basic_string::size_type basic_string::find_last_of(const this_type& x, size_type position) const EA_NOEXCEPT { - return find_last_of(x.internalLayout().BeginPtr(), position, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())); + return find_last_of(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); } @@ -2633,7 +2741,7 @@ namespace eastl basic_string::find_last_of(const value_type* p, size_type position, size_type n) const { // If n is zero or position is >= size, we return npos. - const size_type nLength = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + const size_type nLength = internalLayout().GetSize(); if(EASTL_LIKELY(nLength)) { @@ -2659,7 +2767,7 @@ namespace eastl inline typename basic_string::size_type basic_string::find_first_not_of(const this_type& x, size_type position) const EA_NOEXCEPT { - return find_first_not_of(x.internalLayout().BeginPtr(), position, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())); + return find_first_not_of(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); } @@ -2675,7 +2783,7 @@ namespace eastl typename basic_string::size_type basic_string::find_first_not_of(const value_type* p, size_type position, size_type n) const { - if(EASTL_LIKELY(position <= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_LIKELY(position <= internalLayout().GetSize())) { const const_iterator pResult = CharTypeStringFindFirstNotOf(internalLayout().BeginPtr() + position, internalLayout().EndPtr(), p, p + n); @@ -2691,7 +2799,7 @@ namespace eastl typename basic_string::size_type basic_string::find_first_not_of(value_type c, size_type position) const EA_NOEXCEPT { - if(EASTL_LIKELY(position <= (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_LIKELY(position <= internalLayout().GetSize())) { // Todo: Possibly make a specialized version of CharTypeStringFindFirstNotOf(pBegin, pEnd, c). const const_iterator pResult = @@ -2708,7 +2816,7 @@ namespace eastl inline typename basic_string::size_type basic_string::find_last_not_of(const this_type& x, size_type position) const EA_NOEXCEPT { - return find_last_not_of(x.internalLayout().BeginPtr(), position, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr())); + return find_last_not_of(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); } @@ -2724,7 +2832,7 @@ namespace eastl typename basic_string::size_type basic_string::find_last_not_of(const value_type* p, size_type position, size_type n) const { - const size_type nLength = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + const size_type nLength = internalLayout().GetSize(); if(EASTL_LIKELY(nLength)) { @@ -2742,7 +2850,7 @@ namespace eastl typename basic_string::size_type basic_string::find_last_not_of(value_type c, size_type position) const EA_NOEXCEPT { - const size_type nLength = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); + const size_type nLength = internalLayout().GetSize(); if(EASTL_LIKELY(nLength)) { @@ -2761,17 +2869,18 @@ namespace eastl inline basic_string basic_string::substr(size_type position, size_type n) const { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) ThrowRangeException(); #elif EASTL_ASSERT_ENABLED - if(EASTL_UNLIKELY(position > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) EASTL_FAIL_MSG("basic_string::substr -- invalid position"); #endif + // C++ std says the return string allocator must be default constructed, not a copy of this->get_allocator() return basic_string( internalLayout().BeginPtr() + position, internalLayout().BeginPtr() + position + - eastl::min_alt(n, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - position), get_allocator()); + eastl::min_alt(n, internalLayout().GetSize() - position), allocator_type()); } @@ -2786,13 +2895,13 @@ namespace eastl inline int basic_string::compare(size_type pos1, size_type n1, const this_type& x) const { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(pos1 > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(pos1 > internalLayout().GetSize())) ThrowRangeException(); #endif return compare( internalLayout().BeginPtr() + pos1, - internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - pos1), + internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, internalLayout().GetSize() - pos1), x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); } @@ -2805,10 +2914,10 @@ namespace eastl ThrowRangeException(); #endif - return compare(internalLayout().BeginPtr() + pos1, - internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - pos1), - x.internalLayout().BeginPtr() + pos2, - x.internalLayout().BeginPtr() + pos2 + eastl::min_alt(n2, (size_type)(x.internalLayout().EndPtr() - x.internalLayout().BeginPtr()) - pos2)); + return compare(internalLayout().BeginPtr() + pos1, + internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, internalLayout().GetSize() - pos1), + x.internalLayout().BeginPtr() + pos2, + x.internalLayout().BeginPtr() + pos2 + eastl::min_alt(n2, x.internalLayout().GetSize() - pos2)); } @@ -2823,12 +2932,12 @@ namespace eastl inline int basic_string::compare(size_type pos1, size_type n1, const value_type* p) const { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(pos1 > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(pos1 > internalLayout().GetSize())) ThrowRangeException(); #endif - return compare(internalLayout().BeginPtr() + pos1, - internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - pos1), + return compare(internalLayout().BeginPtr() + pos1, + internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, internalLayout().GetSize() - pos1), p, p + CharStrlen(p)); } @@ -2838,12 +2947,12 @@ namespace eastl inline int basic_string::compare(size_type pos1, size_type n1, const value_type* p, size_type n2) const { #if EASTL_STRING_OPT_RANGE_ERRORS - if(EASTL_UNLIKELY(pos1 > (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()))) + if(EASTL_UNLIKELY(pos1 > internalLayout().GetSize())) ThrowRangeException(); #endif - return compare(internalLayout().BeginPtr() + pos1, - internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()) - pos1), + return compare(internalLayout().BeginPtr() + pos1, + internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, internalLayout().GetSize() - pos1), p, p + n2); } @@ -2901,7 +3010,8 @@ namespace eastl const size_type nLength = length(); if(n < nLength) return substr(0, n); - return *this; + // C++ std says that substr must return default constructed allocated, so do the same here + return basic_string(*this, allocator_type()); } @@ -2911,7 +3021,8 @@ namespace eastl const size_type nLength = length(); if(n < nLength) return substr(nLength - n, n); - return *this; + // C++ std says that substr must return default constructed allocated, so do the same here + return basic_string(*this, allocator_type()); } @@ -2920,7 +3031,7 @@ namespace eastl { va_list arguments; va_start(arguments, pFormat); - internalLayout().SetEndPtr(internalLayout().BeginPtr()); // Fast truncate to zero length. + internalLayout().SetSize(0); // Fast truncate to zero length. append_sprintf_va_list(pFormat, arguments); va_end(arguments); @@ -2931,7 +3042,7 @@ namespace eastl template basic_string& basic_string::sprintf_va_list(const value_type* pFormat, va_list arguments) { - internalLayout().SetEndPtr(internalLayout().BeginPtr()); // Fast truncate to zero length. + internalLayout().SetSize(0); // Fast truncate to zero length. return append_sprintf_va_list(pFormat, arguments); } @@ -2941,9 +3052,9 @@ namespace eastl int basic_string::compare(const value_type* pBegin1, const value_type* pEnd1, const value_type* pBegin2, const value_type* pEnd2) { - const ptrdiff_t n1 = pEnd1 - pBegin1; - const ptrdiff_t n2 = pEnd2 - pBegin2; - const ptrdiff_t nMin = eastl::min_alt(n1, n2); + const difference_type n1 = pEnd1 - pBegin1; + const difference_type n2 = pEnd2 - pBegin2; + const difference_type nMin = eastl::min_alt(n1, n2); const int cmp = Compare(pBegin1, pBegin2, (size_t)nMin); return (cmp != 0 ? cmp : (n1 < n2 ? -1 : (n1 > n2 ? 1 : 0))); @@ -2951,12 +3062,12 @@ namespace eastl template - int basic_string::comparei(const value_type* pBegin1, const value_type* pEnd1, + int basic_string::comparei(const value_type* pBegin1, const value_type* pEnd1, const value_type* pBegin2, const value_type* pEnd2) { - const ptrdiff_t n1 = pEnd1 - pBegin1; - const ptrdiff_t n2 = pEnd2 - pBegin2; - const ptrdiff_t nMin = eastl::min_alt(n1, n2); + const difference_type n1 = pEnd1 - pBegin1; + const difference_type n2 = pEnd2 - pBegin2; + const difference_type nMin = eastl::min_alt(n1, n2); const int cmp = CompareI(pBegin1, pBegin2, (size_t)nMin); return (cmp != 0 ? cmp : (n1 < n2 ? -1 : (n1 > n2 ? 1 : 0))); @@ -2983,33 +3094,33 @@ namespace eastl { iterator pNewPosition = const_cast(p); - if((internalLayout().EndPtr() + 1) < internalLayout().CapacityPtr()) + if((internalLayout().EndPtr() + 1) <= internalLayout().CapacityPtr()) { - *(internalLayout().EndPtr() + 1) = 0; + const size_type nSavedSize = internalLayout().GetSize(); memmove(const_cast(p) + 1, p, (size_t)(internalLayout().EndPtr() - p) * sizeof(value_type)); + *(internalLayout().EndPtr() + 1) = 0; *pNewPosition = c; - internalLayout().SetEndPtr(internalLayout().EndPtr() + 1); + internalLayout().SetSize(nSavedSize + 1); } else { - const size_type nOldSize = (size_type)(internalLayout().EndPtr() - internalLayout().BeginPtr()); - const size_type nOldCap = (size_type)((internalLayout().CapacityPtr() - internalLayout().BeginPtr()) - 1); - const size_type nLength = eastl::max_alt((size_type)GetNewCapacity(nOldCap), (size_type)(nOldSize + 1)) + 1; // The second + 1 is to accomodate the trailing 0. + const size_type nOldSize = internalLayout().GetSize(); + const size_type nOldCap = capacity(); + const size_type nLength = eastl::max_alt(GetNewCapacity(nOldCap), (nOldSize + 1)); - iterator pNewBegin = DoAllocate(nLength); - iterator pNewEnd = pNewBegin; + iterator pNewBegin = DoAllocate(nLength + 1); pNewPosition = CharStringUninitializedCopy(internalLayout().BeginPtr(), p, pNewBegin); *pNewPosition = c; - pNewEnd = pNewPosition + 1; - pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); - *pNewEnd = 0; + iterator pNewEnd = pNewPosition + 1; + pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); + *pNewEnd = 0; DeallocateSelf(); - internalLayout().SetBeginPtr(pNewBegin); - internalLayout().SetEndPtr(pNewEnd); - internalLayout().SetCapacityPtr(pNewBegin + nLength); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nLength); + internalLayout().SetHeapSize(nOldSize + 1); } return pNewPosition; } @@ -3018,9 +3129,10 @@ namespace eastl template void basic_string::SizeInitialize(size_type n, value_type c) { - AllocateSelf((size_type)(n + 1)); // '+1' so that we have room for the terminating 0. + AllocateSelf(n); - internalLayout().SetEndPtr(CharStringUninitializedFillN(internalLayout().BeginPtr(), n, c)); + CharStringUninitializedFillN(internalLayout().BeginPtr(), n, c); + internalLayout().SetSize(n); *internalLayout().EndPtr() = 0; } @@ -3035,9 +3147,10 @@ namespace eastl const size_type n = (size_type)(pEnd - pBegin); - AllocateSelf((size_type)(n + 1)); // '+1' so that we have room for the terminating 0. + AllocateSelf(n); - internalLayout().SetEndPtr(CharStringUninitializedCopy(pBegin, pEnd, internalLayout().BeginPtr())); + CharStringUninitializedCopy(pBegin, pEnd, internalLayout().BeginPtr()); + internalLayout().SetSize(n); *internalLayout().EndPtr() = 0; } @@ -3081,11 +3194,8 @@ namespace eastl template inline void basic_string::AllocateSelf() { - const auto pSSOBegin = internalLayout().SSOBufferPtr(); - internalLayout().SetBeginPtr(pSSOBegin); - internalLayout().SetEndPtr(pSSOBegin); - internalLayout().SetCapacityPtr(pSSOBegin + 1); // mpCapacity is always mpEnd + 1. This is an important distinguising characteristic. - internalLayout().ClearSSOBuffer(); + internalLayout().ResetToSSO(); + internalLayout().SetSSOSize(0); } @@ -3098,28 +3208,16 @@ namespace eastl #endif #if EASTL_STRING_OPT_LENGTH_ERRORS - if(EASTL_UNLIKELY(n > kMaxSize)) + if(EASTL_UNLIKELY(n > max_size())) ThrowLengthException(); #endif - if(n > 1) + if(n > SSOLayout::SSO_CAPACITY) { - const auto nRequestedSizeInBytes = n * sizeof(value_type); - if(nRequestedSizeInBytes > SSOLayout::SSO_SIZE_IN_BYTES) - { - auto pBegin = DoAllocate(n); - internalLayout().SetBeginPtr(pBegin); - internalLayout().SetEndPtr(pBegin); - internalLayout().SetCapacityPtr(pBegin + n); - } - else - { - const auto pSSOBegin = internalLayout().SSOBufferPtr(); - internalLayout().SetBeginPtr(pSSOBegin); - internalLayout().SetEndPtr(pSSOBegin); - // internalLayout().SetCapacityPtr(pBegin + SSOLayout::SSO_SIZE_IN_BYTES); // not necessary as capacity never changes in SSO mode. - internalLayout().ClearSSOBuffer(); - } + pointer pBegin = DoAllocate(n + 1); + internalLayout().SetHeapBeginPtr(pBegin); + internalLayout().SetHeapCapacity(n); + internalLayout().SetHeapSize(0); } else AllocateSelf(); @@ -3129,10 +3227,9 @@ namespace eastl template inline void basic_string::DeallocateSelf() { - if (!internalLayout().IsSSO()) + if(internalLayout().IsHeap()) { - if ((internalLayout().CapacityPtr() - internalLayout().BeginPtr()) > 1) - DoFree(internalLayout().BeginPtr(), (size_type)(internalLayout().CapacityPtr() - internalLayout().BeginPtr())); + DoFree(internalLayout().BeginPtr(), internalLayout().GetHeapCapacity() + 1); } } @@ -3209,10 +3306,10 @@ namespace eastl // Purpose: find p2 within p1. Return p1End if not found or if either string is zero length. template const typename basic_string::value_type* - basic_string::CharTypeStringSearch(const value_type* p1Begin, const value_type* p1End, + basic_string::CharTypeStringSearch(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End) { - // Test for zero length strings, in which case we have a match or a failure, + // Test for zero length strings, in which case we have a match or a failure, // but the return value is the same either way. if((p1Begin == p1End) || (p2Begin == p2End)) return p1Begin; @@ -3233,7 +3330,7 @@ namespace eastl return p1End; pTemp = pTemp1; - pCurrent = p1Begin; + pCurrent = p1Begin; if(++pCurrent == p1End) return p1End; @@ -3256,11 +3353,11 @@ namespace eastl // Specialized value_type version of STL find_end() function (which really is a reverse search function). // Purpose: find last instance of p2 within p1. Return p1End if not found or if either string is zero length. template - const typename basic_string::value_type* - basic_string::CharTypeStringRSearch(const value_type* p1Begin, const value_type* p1End, + const typename basic_string::value_type* + basic_string::CharTypeStringRSearch(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End) { - // Test for zero length strings, in which case we have a match or a failure, + // Test for zero length strings, in which case we have a match or a failure, // but the return value is the same either way. if((p1Begin == p1End) || (p2Begin == p2End)) return p1Begin; @@ -3282,7 +3379,7 @@ namespace eastl { // Search for the last occurrence of *p2Begin. pCurrent1 = CharTypeStringFindEnd(p1Begin, pSearchEnd, *p2Begin); - if(pCurrent1 == pSearchEnd) // If the first char of p2 wasn't found, + if(pCurrent1 == pSearchEnd) // If the first char of p2 wasn't found, return p1End; // then we immediately have failure. // In this case, *pTemp == *p2Begin. So compare the rest. @@ -3307,7 +3404,7 @@ namespace eastl // This function is much like the C runtime strtok function, except the strings aren't null-terminated. template const typename basic_string::value_type* - basic_string::CharTypeStringFindFirstOf(const value_type* p1Begin, const value_type* p1End, + basic_string::CharTypeStringFindFirstOf(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End) { for( ; p1Begin != p1End; ++p1Begin) @@ -3327,7 +3424,7 @@ namespace eastl // This function is much like the C runtime strtok function, except the strings aren't null-terminated. template const typename basic_string::value_type* - basic_string::CharTypeStringRFindFirstOf(const value_type* p1RBegin, const value_type* p1REnd, + basic_string::CharTypeStringRFindFirstOf(const value_type* p1RBegin, const value_type* p1REnd, const value_type* p2Begin, const value_type* p2End) { for( ; p1RBegin != p1REnd; --p1RBegin) @@ -3347,7 +3444,7 @@ namespace eastl // Specialized value_type version of STL find_first_not_of() function. template const typename basic_string::value_type* - basic_string::CharTypeStringFindFirstNotOf(const value_type* p1Begin, const value_type* p1End, + basic_string::CharTypeStringFindFirstNotOf(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End) { for( ; p1Begin != p1End; ++p1Begin) @@ -3369,7 +3466,7 @@ namespace eastl // Specialized value_type version of STL find_first_not_of() function in reverse. template const typename basic_string::value_type* - basic_string::CharTypeStringRFindFirstNotOf(const value_type* p1RBegin, const value_type* p1REnd, + basic_string::CharTypeStringRFindFirstNotOf(const value_type* p1RBegin, const value_type* p1REnd, const value_type* p2Begin, const value_type* p2End) { for( ; p1RBegin != p1REnd; --p1RBegin) @@ -3391,7 +3488,7 @@ namespace eastl // iterator operators template - inline bool operator==(const typename basic_string::reverse_iterator& r1, + inline bool operator==(const typename basic_string::reverse_iterator& r1, const typename basic_string::reverse_iterator& r2) { return r1.mpCurrent == r2.mpCurrent; @@ -3399,7 +3496,7 @@ namespace eastl template - inline bool operator!=(const typename basic_string::reverse_iterator& r1, + inline bool operator!=(const typename basic_string::reverse_iterator& r1, const typename basic_string::reverse_iterator& r2) { return r1.mpCurrent != r2.mpCurrent; @@ -3508,7 +3605,7 @@ namespace eastl template inline bool basic_string::validate() const EA_NOEXCEPT { - if((internalLayout().BeginPtr() == NULL) || (internalLayout().EndPtr() == NULL)) + if((internalLayout().BeginPtr() == nullptr) || (internalLayout().EndPtr() == nullptr)) return false; if(internalLayout().EndPtr() < internalLayout().BeginPtr()) return false; @@ -3713,7 +3810,7 @@ namespace eastl size_t operator()(const string& x) const { const unsigned char* p = (const unsigned char*)x.c_str(); // To consider: limit p to at most 256 chars. - unsigned int c, result = 2166136261U; // We implement an FNV-like string hash. + unsigned int c, result = 2166136261U; // We implement an FNV-like string hash. while((c = *p++) != 0) // Using '!=' disables compiler warnings. result = (result * 16777619) ^ c; return (size_t)result; @@ -3762,59 +3859,59 @@ namespace eastl #endif - /// to_string + /// to_string /// /// Converts integral types to an eastl::string with the same content that sprintf produces. The following /// implementation provides a type safe conversion mechanism which avoids the common bugs associated with sprintf - /// style format strings. - /// + /// style format strings. + /// /// http://en.cppreference.com/w/cpp/string/basic_string/to_string /// - inline string to_string(int value) + inline string to_string(int value) { return string(string::CtorSprintf(), "%d", value); } - inline string to_string(long value) + inline string to_string(long value) { return string(string::CtorSprintf(), "%ld", value); } - inline string to_string(long long value) + inline string to_string(long long value) { return string(string::CtorSprintf(), "%lld", value); } - inline string to_string(unsigned value) + inline string to_string(unsigned value) { return string(string::CtorSprintf(), "%u", value); } - inline string to_string(unsigned long value) + inline string to_string(unsigned long value) { return string(string::CtorSprintf(), "%lu", value); } - inline string to_string(unsigned long long value) + inline string to_string(unsigned long long value) { return string(string::CtorSprintf(), "%llu", value); } - inline string to_string(float value) + inline string to_string(float value) { return string(string::CtorSprintf(), "%f", value); } - inline string to_string(double value) + inline string to_string(double value) { return string(string::CtorSprintf(), "%f", value); } - inline string to_string(long double value) + inline string to_string(long double value) { return string(string::CtorSprintf(), "%Lf", value); } - /// to_wstring + /// to_wstring /// /// Converts integral types to an eastl::wstring with the same content that sprintf produces. The following /// implementation provides a type safe conversion mechanism which avoids the common bugs associated with sprintf - /// style format strings. + /// style format strings. /// /// http://en.cppreference.com/w/cpp/string/basic_string/to_wstring /// - inline wstring to_wstring(int value) + inline wstring to_wstring(int value) { return wstring(wstring::CtorSprintf(), L"%d", value); } - inline wstring to_wstring(long value) + inline wstring to_wstring(long value) { return wstring(wstring::CtorSprintf(), L"%ld", value); } - inline wstring to_wstring(long long value) + inline wstring to_wstring(long long value) { return wstring(wstring::CtorSprintf(), L"%lld", value); } - inline wstring to_wstring(unsigned value) + inline wstring to_wstring(unsigned value) { return wstring(wstring::CtorSprintf(), L"%u", value); } - inline wstring to_wstring(unsigned long value) + inline wstring to_wstring(unsigned long value) { return wstring(wstring::CtorSprintf(), L"%lu", value); } - inline wstring to_wstring(unsigned long long value) + inline wstring to_wstring(unsigned long long value) { return wstring(wstring::CtorSprintf(), L"%llu", value); } - inline wstring to_wstring(float value) + inline wstring to_wstring(float value) { return wstring(wstring::CtorSprintf(), L"%f", value); } - inline wstring to_wstring(double value) + inline wstring to_wstring(double value) { return wstring(wstring::CtorSprintf(), L"%f", value); } - inline wstring to_wstring(long double value) + inline wstring to_wstring(long double value) { return wstring(wstring::CtorSprintf(), L"%Lf", value); } @@ -3850,26 +3947,3 @@ namespace eastl #endif #endif // Header include guard - - - - - - - - - - - - - - - - - - - - - - - diff --git a/include/EASTL/string_map.h b/include/EASTL/string_map.h index 411de993..775f583d 100644 --- a/include/EASTL/string_map.h +++ b/include/EASTL/string_map.h @@ -21,18 +21,18 @@ class string_map : public eastl::map { public: typedef eastl::map base; - typedef string_map this_type; - typedef typename base::base_type::allocator_type allocator_type; - typedef typename base::base_type::insert_return_type insert_return_type; - typedef typename base::base_type::iterator iterator; - typedef typename base::base_type::reverse_iterator reverse_iterator; - typedef typename base::base_type::const_iterator const_iterator; - typedef typename base::base_type::size_type size_type; - typedef typename base::base_type::key_type key_type; - typedef typename base::base_type::value_type value_type; - typedef typename base::mapped_type mapped_type; - - string_map(const allocator_type& allocator = allocator_type()) : base(allocator) {} + typedef string_map this_type; + typedef typename base::base_type::allocator_type allocator_type; + typedef typename base::base_type::insert_return_type insert_return_type; + typedef typename base::base_type::iterator iterator; + typedef typename base::base_type::reverse_iterator reverse_iterator; + typedef typename base::base_type::const_iterator const_iterator; + typedef typename base::base_type::size_type size_type; + typedef typename base::base_type::key_type key_type; + typedef typename base::base_type::value_type value_type; + typedef typename base::mapped_type mapped_type; + + string_map(const allocator_type& allocator = allocator_type()) : base(allocator) {} string_map(const string_map& src, const allocator_type& allocator = allocator_type()); ~string_map(); void clear(); diff --git a/include/EASTL/utility.h b/include/EASTL/utility.h index 54093439..0a4fccee 100644 --- a/include/EASTL/utility.h +++ b/include/EASTL/utility.h @@ -111,31 +111,23 @@ namespace eastl /// as it's dependent on the swap algorithm, which is at a higher level than /// type traits. /// - #if defined(EA_COMPILER_NO_DECLTYPE) || defined(EA_COMPILER_NO_NOEXCEPT) || defined(__EDG_VERSION__) // EDG mis-compiles the conforming code below and so must be placed here. - #define EASTL_TYPE_TRAIT_is_nothrow_swappable_CONFORMANCE 0 + #define EASTL_TYPE_TRAIT_is_nothrow_swappable_CONFORMANCE EASTL_TYPE_TRAIT_is_swappable_CONFORMANCE - template - struct is_nothrow_swappable - : public eastl::false_type {}; - #else - #define EASTL_TYPE_TRAIT_is_nothrow_swappable_CONFORMANCE EASTL_TYPE_TRAIT_is_swappable_CONFORMANCE - - template - struct is_nothrow_swappable_helper_noexcept_wrapper - { const static bool value = noexcept(swap(eastl::declval(), eastl::declval())); }; + template + struct is_nothrow_swappable_helper_noexcept_wrapper + { const static bool value = noexcept(swap(eastl::declval(), eastl::declval())); }; - template - struct is_nothrow_swappable_helper - : public eastl::integral_constant::value> {}; // Don't prefix swap with eastl:: as we want to allow user-defined swaps via argument-dependent lookup. + template + struct is_nothrow_swappable_helper + : public eastl::integral_constant::value> {}; // Don't prefix swap with eastl:: as we want to allow user-defined swaps via argument-dependent lookup. - template - struct is_nothrow_swappable_helper - : public eastl::false_type {}; + template + struct is_nothrow_swappable_helper + : public eastl::false_type {}; - template - struct is_nothrow_swappable - : public eastl::is_nothrow_swappable_helper::value> {}; - #endif + template + struct is_nothrow_swappable + : public eastl::is_nothrow_swappable_helper::value> {}; #if EASTL_VARIABLE_TEMPLATES_ENABLED template @@ -291,7 +283,6 @@ namespace eastl eastl::swap_ranges(a, a + N, b); } -#if EASTL_MOVE_SEMANTICS_ENABLED /// exchange /// @@ -300,25 +291,14 @@ namespace eastl /// /// http://en.cppreference.com/w/cpp/utility/exchange /// - #if defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) - template - inline T exchange(T& obj, U&& new_value, typename eastl::enable_if::value>::type* = 0) - { - T old_value = eastl::move(obj); - obj = eastl::forward(new_value); - return old_value; - } - #else - template - inline T exchange(T& obj, U&& new_value) - { - T old_value = eastl::move(obj); - obj = eastl::forward(new_value); - return old_value; - } - #endif + template + inline T exchange(T& obj, U&& new_value) + { + T old_value = eastl::move(obj); + obj = eastl::forward(new_value); + return old_value; + } -#endif // EASTL_MOVE_SEMANTICS_ENABLED /// as_const /// @@ -335,10 +315,8 @@ namespace eastl // The C++17 forbids 'eastl::as_const' from accepting rvalues. Passing an rvalue reference to 'eastl::as_const' // generates an 'const T&' or const lvalue reference to a temporary object. - #if !defined(EA_COMPILER_NO_DELETED_FUNCTIONS) - template - void as_const(const T&&) = delete; - #endif + template + void as_const(const T&&) = delete; /////////////////////////////////////////////////////////////////////// @@ -390,9 +368,15 @@ namespace eastl T1 first; T2 second; + template && + eastl::is_default_constructible_v>> EA_CONSTEXPR pair() - : first(), - second() {} + : first(), second() + { + } + // To consider: Use type traits to enable this ctor only if T2 (second is_default_constructible::value == true.) EA_CPP14_CONSTEXPR pair(const T1& x) @@ -403,10 +387,11 @@ namespace eastl // GCC has a bug with overloading rvalue and lvalue function templates. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54425 - // + // // error: 'eastl::pair::pair(T1&&) [with T1 = const int&; T2 = const int&]' cannot be overloaded // error: with 'eastl::pair::pair(const T1&) [with T1 = const int&; T2 = const int&]' - #if EASTL_MOVE_SEMANTICS_ENABLED && !defined(EA_COMPILER_GNUC) + #if !defined(EA_COMPILER_GNUC) + // To consider: Use type traits to enable this ctor only if T2 (second is_default_constructible::value == true.) EA_CPP14_CONSTEXPR pair(T1&& x) : first(eastl::move(x)), second() @@ -414,160 +399,129 @@ namespace eastl } #endif - EA_CPP14_CONSTEXPR pair(const T1& x, const T2& y) : first(x), second(y) {} + template < + typename TT1 = T1, + typename TT2 = T2, + class = eastl::enable_if_t && eastl::is_copy_constructible_v>> + EA_CPP14_CONSTEXPR pair(const T1& x, const T2& y) + : first(x), second(y) + { + } - template // This would enable_if situation be better solvable with C++14 concepts, but such compilers aren't currently available. - EA_CPP14_CONSTEXPR pair(const pair& p, typename eastl::enable_if::value && eastl::is_convertible::value>::type* = 0) - : first(p.first), - second(p.second) {} + EA_CPP14_CONSTEXPR pair(pair&& p) = default; + EA_CPP14_CONSTEXPR pair(const pair&) = default; - #if (EABASE_VERSION_N >= 20040) && !defined(EA_COMPILER_NO_DEFAULTED_FUNCTIONS) - pair(const pair&) = default; - #endif + template < + typename U, + typename V, + class = eastl::enable_if_t && eastl::is_convertible_v>> + EA_CPP14_CONSTEXPR pair(const pair& p) + : first(p.first), second(p.second) + { + } - #if EASTL_MOVE_SEMANTICS_ENABLED - #if defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) - template - EA_CPP14_CONSTEXPR pair(U&& u, V&& v, typename eastl::enable_if::value && eastl::is_convertible::value>::type* = 0) - : first(eastl::forward(u)), - second(eastl::forward(v)) {} - - template - EA_CPP14_CONSTEXPR pair(U&& x, const T2& y, typename eastl::enable_if::value>::type* = 0) - : first(eastl::forward(x)), - second(y) {} - - template - EA_CPP14_CONSTEXPR pair(const T1& x, V&& y, typename eastl::enable_if::value>::type* = 0) - : first(x), - second(eastl::forward(y)) {} - #else - template ::value && eastl::is_convertible::value>::type> - EA_CPP14_CONSTEXPR pair(U&& u, V&& v) - : first(eastl::forward(u)), - second(eastl::forward(v)) {} - - template ::value>::type> - EA_CPP14_CONSTEXPR pair(U&& x, const T2& y) - : first(eastl::forward(x)), - second(y) {} - - template ::value>::type> - EA_CPP14_CONSTEXPR pair(const T1& x, V&& y) - : first(x), - second(eastl::forward(y)) {} - #endif - - #if (EABASE_VERSION_N >= 20040) && !defined(EA_COMPILER_NO_DEFAULTED_FUNCTIONS) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1900) - pair(pair&& p) = default; - #else - // The C++11 Standard specifies the following as a defaulted function, but we cannot do that because - // otherwise we get GCC and MSVC compile errors in the rest of EASTL saying that it's a needed but implicitly deleted function. - EA_CPP14_CONSTEXPR pair(pair&& p) EA_NOEXCEPT_IF(eastl::is_nothrow_move_constructible::value && eastl::is_nothrow_move_constructible::value) - : first(eastl::forward(p.first)), - second(eastl::forward(p.second)) {} - #endif - - - #if defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) - template - EA_CPP14_CONSTEXPR pair(pair&& p, typename eastl::enable_if::value && eastl::is_convertible::value>::type* = 0) - : first(eastl::forward(p.first)), - second(eastl::forward(p.second)) {} - #else - template ::value && eastl::is_convertible::value>::type> - EA_CPP14_CONSTEXPR pair(pair&& p) - : first(eastl::forward(p.first)), - second(eastl::forward(p.second)) {} - #endif - #endif + template && eastl::is_convertible_v>> + EA_CPP14_CONSTEXPR pair(U&& u, V&& v) + : first(eastl::forward(u)), second(eastl::forward(v)) + { + } - - // Initializes first with arguments of types Args1... obtained by forwarding the elements of first_args and - // initializes second with arguments of types Args2... obtained by forwarding the elements of second_args. - template - // TODO(rparolin): Requires: is_constructible_v is true and is_constructible_v is true. - pair(eastl::piecewise_construct_t pwc, - eastl::tuple first_args, - eastl::tuple second_args) - : pair(pwc, - eastl::move(first_args), - eastl::move(second_args), - eastl::make_index_sequence(), - eastl::make_index_sequence()) - { - } - - private: - // NOTE(rparolin): Internal constructor used to expand the index_sequence required to expand the tuple elements. - template - pair(eastl::piecewise_construct_t, - eastl::tuple first_args, - eastl::tuple second_args, - eastl::index_sequence, - eastl::index_sequence) - : first(eastl::forward(eastl::get(first_args))...) - , second(eastl::forward(eastl::get(second_args))...) - { - } + template >> + EA_CPP14_CONSTEXPR pair(U&& x, const T2& y) + : first(eastl::forward(x)), second(y) + { + } - public: + template >> + EA_CPP14_CONSTEXPR pair(const T1& x, V&& y) + : first(x), second(eastl::forward(y)) + { + } + + template < + typename U, + typename V, + typename = eastl::enable_if_t && eastl::is_convertible_v>> + EA_CPP14_CONSTEXPR pair(pair&& p) + : first(eastl::forward(p.first)), second(eastl::forward(p.second)) + { + } + + // Initializes first with arguments of types Args1... obtained by forwarding the elements of first_args and + // initializes second with arguments of types Args2... obtained by forwarding the elements of second_args. + template && + eastl::is_constructible_v>> + pair(eastl::piecewise_construct_t pwc, eastl::tuple first_args, eastl::tuple second_args) + : pair(pwc, + eastl::move(first_args), + eastl::move(second_args), + eastl::make_index_sequence(), + eastl::make_index_sequence()) + { + } + + private: + // NOTE(rparolin): Internal constructor used to expand the index_sequence required to expand the tuple elements. + template + pair(eastl::piecewise_construct_t, + eastl::tuple first_args, + eastl::tuple second_args, + eastl::index_sequence, + eastl::index_sequence) + : first(eastl::forward(eastl::get(first_args))...) + , second(eastl::forward(eastl::get(second_args))...) + { + } - pair& operator=(const pair& p) EA_NOEXCEPT_IF(eastl::is_nothrow_copy_assignable::value && eastl::is_nothrow_copy_assignable::value) + public: + pair& operator=(const pair& p) + EA_NOEXCEPT_IF(eastl::is_nothrow_copy_assignable_v&& eastl::is_nothrow_copy_assignable_v) { - first = p.first; + first = p.first; second = p.second; return *this; } - - #if defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) - template - #else - template ::value && eastl::is_convertible::value>::type> - #endif + template && eastl::is_convertible_v>> pair& operator=(const pair& p) { - first = p.first; + first = p.first; second = p.second; return *this; } + pair& operator=(pair&& p) + EA_NOEXCEPT_IF(eastl::is_nothrow_move_assignable_v&& eastl::is_nothrow_move_assignable_v) + { + first = eastl::forward(p.first); + second = eastl::forward(p.second); + return *this; + } - #if EASTL_MOVE_SEMANTICS_ENABLED - pair& operator=(pair&& p) EA_NOEXCEPT_IF(eastl::is_nothrow_move_assignable::value && eastl::is_nothrow_move_assignable::value) - { - first = eastl::forward(p.first); - second = eastl::forward(p.second); - return *this; - } - - #if defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) - template - #else - template ::value && eastl::is_convertible::value>::type> - #endif - pair& operator=(pair&& p) - { - first = eastl::forward(p.first); - second = eastl::forward(p.second); - return *this; - } - #endif - + template && eastl::is_convertible_v>> + pair& operator=(pair&& p) + { + first = eastl::forward(p.first); + second = eastl::forward(p.second); + return *this; + } - void swap(pair& p) EA_NOEXCEPT_IF(eastl::is_nothrow_swappable::value && eastl::is_nothrow_swappable::value) + void swap(pair& p) EA_NOEXCEPT_IF(eastl::is_nothrow_swappable_v&& eastl::is_nothrow_swappable_v) { - eastl::iter_swap(&first, &p.first); + eastl::iter_swap(&first, &p.first); eastl::iter_swap(&second, &p.second); } }; - #if defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) || !EASTL_MOVE_SEMANTICS_ENABLED // We aren't concerned about the noexcept conformance above. - #define EASTL_PAIR_CONFORMANCE 0 - #else - #define EASTL_PAIR_CONFORMANCE 1 - #endif + #define EASTL_PAIR_CONFORMANCE 1 @@ -598,7 +552,7 @@ namespace eastl /// SGI SGL select1st utility. /// template - struct use_first // : public unary_function // Perhaps we want to make it a subclass of unary_function. + struct use_first { typedef Pair argument_type; typedef typename Pair::first_type result_type; @@ -694,61 +648,45 @@ namespace eastl /// return make_pair(charPtr, charPtr); /// return pair(charPtr, charPtr); /// - #if EASTL_MOVE_SEMANTICS_ENABLED - - template - EA_CPP14_CONSTEXPR inline pair::type>::type, - typename eastl::remove_reference_wrapper::type>::type> - make_pair(T1&& a, T2&& b) - { - typedef typename eastl::remove_reference_wrapper::type>::type T1Type; - typedef typename eastl::remove_reference_wrapper::type>::type T2Type; - - return eastl::pair(eastl::forward(a), eastl::forward(b)); - } - + template + EA_CPP14_CONSTEXPR inline pair::type>::type, + typename eastl::remove_reference_wrapper::type>::type> + make_pair(T1&& a, T2&& b) + { + typedef typename eastl::remove_reference_wrapper::type>::type T1Type; + typedef typename eastl::remove_reference_wrapper::type>::type T2Type; - // Without the following, VC++ fails to compile code like this: pair p = eastl::make_pair("hello", 0); - // We define a const reference version alternative to the above. "hello" is of type char const(&)[6] (array of 6 const chars), - // but VC++ decays it to const char* and allows this make_pair to be called with that. VC++ fails below with make_pair("hello", "people") - // because you can't assign arrays and until we have a better solution we just disable this make_pair specialization for when T1 or T2 - // are of type char const(&)[]. - #if defined(_MSC_VER) - template - EA_CPP14_CONSTEXPR inline pair make_pair(const T1& a, const T2& b, typename eastl::enable_if::value && !eastl::is_array::value>::type* = 0) - { - return eastl::pair(a, b); - } - #endif + return eastl::pair(eastl::forward(a), eastl::forward(b)); + } - // For backwards compatibility + // Without the following, VC++ fails to compile code like this: pair p = eastl::make_pair("hello", 0); + // We define a const reference version alternative to the above. "hello" is of type char const(&)[6] (array of 6 const chars), + // but VC++ decays it to const char* and allows this make_pair to be called with that. VC++ fails below with make_pair("hello", "people") + // because you can't assign arrays and until we have a better solution we just disable this make_pair specialization for when T1 or T2 + // are of type char const(&)[]. + #if defined(_MSC_VER) template - EA_CPP14_CONSTEXPR inline pair::type>::type, - typename eastl::remove_reference_wrapper::type>::type> - make_pair_ref(T1&& a, T2&& b) - { - typedef typename eastl::remove_reference_wrapper::type>::type T1Type; - typedef typename eastl::remove_reference_wrapper::type>::type T2Type; - - return eastl::pair(eastl::forward(a), eastl::forward(b)); - } - #else - - template - inline pair make_pair(T1 a, T2 b) + EA_CPP14_CONSTEXPR inline pair make_pair( + const T1& a, + const T2& b, + typename eastl::enable_if::value && !eastl::is_array::value>::type* = 0) { return eastl::pair(a, b); } + #endif + // For backwards compatibility + template + EA_CPP14_CONSTEXPR inline pair::type>::type, + typename eastl::remove_reference_wrapper::type>::type> + make_pair_ref(T1&& a, T2&& b) + { + typedef typename eastl::remove_reference_wrapper::type>::type T1Type; + typedef typename eastl::remove_reference_wrapper::type>::type T2Type; - template - inline pair make_pair_ref(const T1& a, const T2& b) - { - return eastl::pair(a, b); - } - - #endif + return eastl::pair(eastl::forward(a), eastl::forward(b)); + } #if EASTL_TUPLE_ENABLED diff --git a/include/EASTL/variant.h b/include/EASTL/variant.h index cce8d5c2..cc651ad2 100644 --- a/include/EASTL/variant.h +++ b/include/EASTL/variant.h @@ -237,7 +237,7 @@ namespace eastl variant_storage& operator=(variant_storage&& other) { - DoOp(StorageOp::MOVE, other); + DoOp(StorageOp::MOVE, eastl::move(other)); return *this; } @@ -570,8 +570,11 @@ namespace eastl template ...>>> variant(const variant& other) { - mIndex = other.mIndex; - mStorage = other.mStorage; + if (this != &other) + { + mIndex = other.mIndex; + mStorage = other.mStorage; + } } // Only participates in overload resolution if is_move_constructible_v is true for all T_i in Types... @@ -579,8 +582,11 @@ namespace eastl EA_CONSTEXPR variant(variant&& other) EA_NOEXCEPT(conjunction_v...>) : mIndex(variant_npos), mStorage() { - mIndex = other.mIndex; - mStorage = eastl::move(other.mStorage); + if(this != &other) + { + mIndex = other.mIndex; + mStorage = eastl::move(other.mStorage); + } } // Conversion constructor @@ -605,41 +611,37 @@ namespace eastl template < class T, class... Args, - class = enable_if_t, is_constructible>, T> - > + class = enable_if_t, is_constructible>, T>> EA_CPP14_CONSTEXPR explicit variant(in_place_type_t, Args&&... args) : variant(in_place>, forward(args)...) {} template < - class T, - class U, - class... Args, - class = enable_if_t, is_constructible>, T> - > + class T, + class U, + class... Args, + class = enable_if_t, is_constructible>, T>> EA_CPP14_CONSTEXPR explicit variant(in_place_type_t, std::initializer_list il, Args&&... args) - : variant(in_place>, il, forward(args)...) + : variant(in_place>, il, forward(args)...) {} - template < - size_t I, - class... Args, - class = enable_if_t, is_constructible, Args...>>> - > + template , + is_constructible, Args...>>>> EA_CPP14_CONSTEXPR explicit variant(in_place_index_t, Args&&... args) - : mIndex(I) + : mIndex(I) { mStorage.template set_as>(forward(args)...); } - template < - size_t I, - class U, - class... Args, - class = enable_if_t, is_constructible, Args...>>> - > + template , + is_constructible, Args...>>>> EA_CPP14_CONSTEXPR explicit variant(in_place_index_t, std::initializer_list il, Args&&... args) - : mIndex(I) + : mIndex(I) { mStorage.template set_as>(il, forward(args)...); } @@ -690,7 +692,7 @@ namespace eastl // template , + typename T = meta::get_type_at_t, typename = enable_if_t, meta::duplicate_type_check>>> variant_alternative_t& emplace(Args&&... args) @@ -728,19 +730,22 @@ namespace eastl /////////////////////////////////////////////////////////////////////////// // 20.7.2.3, assignment // - template < - class T, - typename T_j = meta::overload_resolution_t>, - ssize_t I = meta::get_type_index_v, Types...>, - typename = enable_if_t< - conjunction_v, variant>, is_assignable_v, is_constructible_v>>> + template >, + ssize_t I = meta::get_type_index_v, Types...>, + typename = enable_if_t, variant> && eastl::is_assignable_v && + eastl::is_constructible_v>> EA_CPP14_CONSTEXPR variant& operator=(T&& t) EA_NOEXCEPT(conjunction_v, is_nothrow_constructible>) { static_assert(I >= 0, "T not found in type-list."); - static_assert((meta::type_count_v == 1), "function overload is not unique - duplicate types in type list"); + static_assert((meta::type_count_v == 1), + "function overload is not unique - duplicate types in type list"); + + if (!valueless_by_exception()) + mStorage.destroy(); - mIndex = static_cast(index); + mIndex = static_cast(I); mStorage.template set_as(eastl::forward(t)); return *this; } @@ -753,8 +758,11 @@ namespace eastl typename = enable_if_t> // add a dependent type to enable sfinae variant& operator=(const variant& other) { - mIndex = other.mIndex; - mStorage = other.mStorage; + if (this != &other) + { + mIndex = other.mIndex; + mStorage = other.mStorage; + } return *this; } @@ -766,8 +774,11 @@ namespace eastl EA_NOEXCEPT(conjunction_v...>, conjunction...>>) { - mIndex = eastl::move(other.mIndex); - mStorage = eastl::move(other.mStorage); + if (this != &other) + { + mIndex = eastl::move(other.mIndex); + mStorage = eastl::move(other.mStorage); + } return *this; } @@ -848,7 +859,12 @@ namespace eastl // get(variant) based on the array index, and so we can unpack the final of arguments by // calling get(args) for each index in args. template - static decltype(auto) EA_CONSTEXPR call_next(Visitor&& visitor, index_sequence, index_sequence, ArgsTuple&& args, Variant&& variant, Variants&&... variants) + static decltype(auto) EA_CONSTEXPR call_next(Visitor&& visitor, + index_sequence, + index_sequence, + ArgsTuple&& args, + Variant&& variant, + Variants&&... variants) { // Call the appropriate get() function on the variant, and pack the result into a new tuple along with // all of the previous arguments. Then call the next visitor_caller with the new argument added, @@ -864,7 +880,12 @@ namespace eastl // Arguments are the same as for call_next (see above). template - static decltype(auto) EA_CPP14_CONSTEXPR call(Visitor&& visitor, index_sequence, index_sequence, ArgsTuple&& args, Variant&& variant, Variants&&... variants) + static decltype(auto) EA_CPP14_CONSTEXPR call(Visitor&& visitor, + index_sequence, + index_sequence, + ArgsTuple&& args, + Variant&& variant, + Variants&&... variants) { // Deduce the type of the inner array of call_next functions using return_type = decltype(call_next<0>( diff --git a/include/EASTL/vector.h b/include/EASTL/vector.h index 3900d608..1180dc81 100644 --- a/include/EASTL/vector.h +++ b/include/EASTL/vector.h @@ -204,13 +204,13 @@ namespace eastl using base_type::internalAllocator; public: - vector() EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(allocator_type())); - explicit vector(const allocator_type& allocator) EA_NOEXCEPT; + vector() noexcept(noexcept(allocator_type())); + explicit vector(const allocator_type& allocator) noexcept; explicit vector(size_type n, const allocator_type& allocator = EASTL_VECTOR_DEFAULT_ALLOCATOR); vector(size_type n, const value_type& value, const allocator_type& allocator = EASTL_VECTOR_DEFAULT_ALLOCATOR); vector(const this_type& x); vector(const this_type& x, const allocator_type& allocator); - vector(this_type&& x) EA_NOEXCEPT; + vector(this_type&& x) noexcept; vector(this_type&& x, const allocator_type& allocator); vector(std::initializer_list ilist, const allocator_type& allocator = EASTL_VECTOR_DEFAULT_ALLOCATOR); @@ -283,7 +283,7 @@ namespace eastl iterator emplace(const_iterator position, Args&&... args); template - void emplace_back(Args&&... args); + reference emplace_back(Args&&... args); iterator insert(const_iterator position, const value_type& value); iterator insert(const_iterator position, size_type n, const value_type& value); @@ -493,7 +493,7 @@ namespace eastl /////////////////////////////////////////////////////////////////////// template - inline vector::vector() EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(allocator_type())) + inline vector::vector() noexcept(noexcept(allocator_type())) : base_type() { // Empty @@ -501,7 +501,7 @@ namespace eastl template - inline vector::vector(const allocator_type& allocator) EA_NOEXCEPT + inline vector::vector(const allocator_type& allocator) noexcept : base_type(allocator) { // Empty @@ -543,7 +543,7 @@ namespace eastl template - inline vector::vector(this_type&& x) EA_NOEXCEPT + inline vector::vector(this_type&& x) noexcept : base_type(eastl::move(x.internalAllocator())) // vector requires move-construction of allocator in this case. { DoSwap(x); @@ -1090,7 +1090,8 @@ namespace eastl template template - inline void vector::emplace_back(Args&&... args) + inline typename vector::reference + vector::emplace_back(Args&&... args) { if(mpEnd < internalCapacityPtr()) { @@ -1099,8 +1100,9 @@ namespace eastl } else DoInsertValueEnd(eastl::forward(args)...); - } + return back(); + } template inline typename vector::iterator @@ -1954,7 +1956,7 @@ namespace eastl template - inline void swap(vector& a, vector& b) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(a.swap(b))) + inline void swap(vector& a, vector& b) noexcept(noexcept(a.swap(b))) { a.swap(b); } diff --git a/test/source/EASTLTest.cpp b/test/source/EASTLTest.cpp index 5688eff1..0718ab5f 100644 --- a/test/source/EASTLTest.cpp +++ b/test/source/EASTLTest.cpp @@ -103,13 +103,15 @@ int InstanceAllocator::mMismatchCount = 0; /////////////////////////////////////////////////////////////////////////////// // CountingAllocator // -uint64_t CountingAllocator::activeAllocCount = 0; -uint64_t CountingAllocator::totalAllocCount = 0; -uint64_t CountingAllocator::totalDeallocCount = 0; -uint64_t CountingAllocator::totalCtorCount = 0; -uint64_t CountingAllocator::defaultCtorCount = 0; -uint64_t CountingAllocator::copyCtorCount = 0; -uint64_t CountingAllocator::assignOpCount = 0; +uint64_t CountingAllocator::activeAllocCount = 0; +uint64_t CountingAllocator::totalAllocCount = 0; +uint64_t CountingAllocator::totalDeallocCount = 0; +uint64_t CountingAllocator::totalCtorCount = 0; +uint64_t CountingAllocator::defaultCtorCount = 0; +uint64_t CountingAllocator::copyCtorCount = 0; +uint64_t CountingAllocator::assignOpCount = 0; +uint64_t CountingAllocator::totalAllocatedMemory = 0; +uint64_t CountingAllocator::activeAllocatedMemory = 0; /////////////////////////////////////////////////////////////////////////////// diff --git a/test/source/EASTLTest.h b/test/source/EASTLTest.h index 2c84d33a..77dae05e 100644 --- a/test/source/EASTLTest.h +++ b/test/source/EASTLTest.h @@ -418,26 +418,24 @@ struct TestObject } } - #if !defined(EA_COMPILER_NO_RVALUE_REFERENCES) - // Due to the nature of TestObject, there isn't much special for us to - // do in our move constructor. A move constructor swaps its contents with - // the other object, whhich is often a default-constructed object. - TestObject(TestObject&& testObject) - : mX(testObject.mX), mbThrowOnCopy(testObject.mbThrowOnCopy), mMagicValue(testObject.mMagicValue) + // Due to the nature of TestObject, there isn't much special for us to + // do in our move constructor. A move constructor swaps its contents with + // the other object, whhich is often a default-constructed object. + TestObject(TestObject&& testObject) + : mX(testObject.mX), mbThrowOnCopy(testObject.mbThrowOnCopy), mMagicValue(testObject.mMagicValue) + { + ++sTOCount; + ++sTOCtorCount; + ++sTOMoveCtorCount; + mId = sTOCtorCount; // testObject keeps its mId, and we assign ours anew. + testObject.mX = 0; // We are swapping our contents with the TestObject, so give it our "previous" value. + if(mbThrowOnCopy) { - ++sTOCount; - ++sTOCtorCount; - ++sTOMoveCtorCount; - mId = sTOCtorCount; // testObject keeps its mId, and we assign ours anew. - testObject.mX = 0; // We are swapping our contents with the TestObject, so give it our "previous" value. - if(mbThrowOnCopy) - { - #if EASTL_EXCEPTIONS_ENABLED - throw "Disallowed TestObject copy"; - #endif - } + #if EASTL_EXCEPTIONS_ENABLED + throw "Disallowed TestObject copy"; + #endif } - #endif + } TestObject& operator=(const TestObject& testObject) { @@ -459,28 +457,26 @@ struct TestObject return *this; } - #if !defined(EA_COMPILER_NO_RVALUE_REFERENCES) - TestObject& operator=(TestObject&& testObject) + TestObject& operator=(TestObject&& testObject) + { + ++sTOMoveAssignCount; + + if(&testObject != this) { - ++sTOMoveAssignCount; + eastl::swap(mX, testObject.mX); + // Leave mId alone. + eastl::swap(mMagicValue, testObject.mMagicValue); + eastl::swap(mbThrowOnCopy, testObject.mbThrowOnCopy); - if(&testObject != this) + if(mbThrowOnCopy) { - eastl::swap(mX, testObject.mX); - // Leave mId alone. - eastl::swap(mMagicValue, testObject.mMagicValue); - eastl::swap(mbThrowOnCopy, testObject.mbThrowOnCopy); - - if(mbThrowOnCopy) - { - #if EASTL_EXCEPTIONS_ENABLED - throw "Disallowed TestObject copy"; - #endif - } + #if EASTL_EXCEPTIONS_ENABLED + throw "Disallowed TestObject copy"; + #endif } - return *this; } - #endif + return *this; + } ~TestObject() { @@ -1242,6 +1238,8 @@ class CountingAllocator { activeAllocCount++; totalAllocCount++; + totalAllocatedMemory += n; + activeAllocatedMemory += n; return mAllocator.allocate(n, flags); } @@ -1249,6 +1247,8 @@ class CountingAllocator { activeAllocCount++; totalAllocCount++; + totalAllocatedMemory += n; + activeAllocatedMemory += n; return mAllocator.allocate(n, alignment, offset, flags); } @@ -1256,26 +1256,28 @@ class CountingAllocator { activeAllocCount--; totalDeallocCount--; + activeAllocatedMemory -= n; return mAllocator.deallocate(p, n); } - const char* get_name() const { return mAllocator.get_name(); } - void set_name(const char* pName) { mAllocator.set_name(pName); } + const char* get_name() const { return mAllocator.get_name(); } + void set_name(const char* pName) { mAllocator.set_name(pName); } - static auto getAllocationCount() - { - return totalAllocCount; - } + static auto getAllocationCount() { return totalAllocCount; } + static auto getTotalAllocationSize() { return totalAllocatedMemory; } + static auto getActiveAllocationSize() { return activeAllocatedMemory; } static void resetCount() { - activeAllocCount = 0; - totalAllocCount = 0; - totalDeallocCount = 0; - totalCtorCount = 0; - defaultCtorCount = 0; - copyCtorCount = 0; - assignOpCount = 0; + activeAllocCount = 0; + totalAllocCount = 0; + totalDeallocCount = 0; + totalCtorCount = 0; + defaultCtorCount = 0; + copyCtorCount = 0; + assignOpCount = 0; + totalAllocatedMemory = 0; + activeAllocatedMemory = 0; } eastl::allocator mAllocator; @@ -1287,6 +1289,8 @@ class CountingAllocator static uint64_t defaultCtorCount; static uint64_t copyCtorCount; static uint64_t assignOpCount; + static uint64_t totalAllocatedMemory; // the total amount of memory allocated + static uint64_t activeAllocatedMemory; // currently allocated memory by allocator }; inline bool operator==(const CountingAllocator& rhs, const CountingAllocator& lhs) { return rhs.mAllocator == lhs.mAllocator; } diff --git a/test/source/EASTLTestAllocator.cpp b/test/source/EASTLTestAllocator.cpp index 1d90a8ea..42992366 100644 --- a/test/source/EASTLTestAllocator.cpp +++ b/test/source/EASTLTestAllocator.cpp @@ -305,11 +305,7 @@ gEASTLTest_AllocationCount--; // This IsConstructed functionality is needed by some mobile platforms due to some weaknesses in their application startup. - #if (PPMALLOC_VERSION_N >= 11602) - const bool bConstructed = EA::Allocator::gGeneralAllocator.IsConstructed(); - #else - const bool bConstructed = (EA::Allocator::gGeneralAllocator.GetTraceFunction(NULL) != NULL); // Old hacky way to test this. - #endif + const bool bConstructed = EA::Allocator::gGeneralAllocator.IsConstructed(); if(bConstructed) { diff --git a/test/source/TestAny.cpp b/test/source/TestAny.cpp index fd697379..e1e88786 100644 --- a/test/source/TestAny.cpp +++ b/test/source/TestAny.cpp @@ -408,6 +408,13 @@ int TestAny() } } + // user reported regression that eastl::any constructor was not decaying the deduced type correctly. + { + float f = 42.f; + eastl::any a(f); + VERIFY(any_cast(a) == 42.f); + } + return nErrorCount; } diff --git a/test/source/TestDeque.cpp b/test/source/TestDeque.cpp index a557a197..b16af5aa 100644 --- a/test/source/TestDeque.cpp +++ b/test/source/TestDeque.cpp @@ -19,21 +19,13 @@ #include #if !defined(EA_COMPILER_NO_STANDARD_CPP_LIBRARY) - #ifdef _MSC_VER - #pragma warning(push, 0) - #pragma warning(disable:4350) // for whatever reason, the push,0 above does not turn this warning off with vs2012. - // VC++ 2012 STL headers generate this. warning C4350: behavior change: 'std::_Wrap_alloc<_Alloc>::_Wrap_alloc(const std::_Wrap_alloc<_Alloc> &) throw()' called instead of 'std::_Wrap_alloc<_Alloc>::_Wrap_alloc>(_Other &) throw()' - #endif - + EA_DISABLE_ALL_VC_WARNINGS() #include #include #include #include #include - - #ifdef _MSC_VER - #pragma warning(pop) - #endif + EA_RESTORE_ALL_VC_WARNINGS() #endif @@ -718,15 +710,13 @@ int TestDeque() } { // Test complex mutating functionality. - #if !defined(EASTLTEST_STD_STL_VER_APACHE) // Apache std::deque is non-conforming. - nErrorCount += TestDequeComplexMutation(); - nErrorCount += TestDequeComplexMutation(); - nErrorCount += TestDequeComplexMutation(); - - nErrorCount += TestDequeComplexMutation(); - nErrorCount += TestDequeComplexMutation(); - nErrorCount += TestDequeComplexMutation(); - #endif + nErrorCount += TestDequeComplexMutation(); + nErrorCount += TestDequeComplexMutation(); + nErrorCount += TestDequeComplexMutation(); + + nErrorCount += TestDequeComplexMutation(); + nErrorCount += TestDequeComplexMutation(); + nErrorCount += TestDequeComplexMutation(); } #endif // EA_COMPILER_NO_STANDARD_CPP_LIBRARY @@ -754,122 +744,70 @@ int TestDeque() { // C++11 functionality - #if EASTL_MOVE_SEMANTICS_ENABLED - // deque(this_type&& x); - // deque(this_type&& x, const allocator_type& allocator); - // this_type& operator=(this_type&& x); - // void push_front(value_type&& value); - // void push_back(value_type&& value); - // iterator insert(const_iterator position, value_type&& value); - - using namespace eastl; - - deque deque3TO33(3, TestObject(33)); - deque toDequeA(eastl::move(deque3TO33)); - EATEST_VERIFY((toDequeA.size() == 3) && (toDequeA.front().mX == 33) && (deque3TO33.size() == 0)); - - // The following is not as strong a test of this ctor as it could be. A stronger test would be to use IntanceAllocator with different instances. - deque deque4TO44(4, TestObject(44)); - deque toDequeB(eastl::move(deque4TO44), MallocAllocator()); - EATEST_VERIFY((toDequeB.size() == 4) && (toDequeB.front().mX == 44) && (deque4TO44.size() == 0)); - - deque deque5TO55(5, TestObject(55)); - toDequeB = eastl::move(deque5TO55); - EATEST_VERIFY((toDequeB.size() == 5) && (toDequeB.front().mX == 55) && (deque5TO55.size() == 0)); - #endif + // deque(this_type&& x); + // deque(this_type&& x, const allocator_type& allocator); + // this_type& operator=(this_type&& x); + // void push_front(value_type&& value); + // void push_back(value_type&& value); + // iterator insert(const_iterator position, value_type&& value); + + using namespace eastl; + + deque deque3TO33(3, TestObject(33)); + deque toDequeA(eastl::move(deque3TO33)); + EATEST_VERIFY((toDequeA.size() == 3) && (toDequeA.front().mX == 33) && (deque3TO33.size() == 0)); + + // The following is not as strong a test of this ctor as it could be. A stronger test would be to use IntanceAllocator with different instances. + deque deque4TO44(4, TestObject(44)); + deque toDequeB(eastl::move(deque4TO44), MallocAllocator()); + EATEST_VERIFY((toDequeB.size() == 4) && (toDequeB.front().mX == 44) && (deque4TO44.size() == 0)); + + deque deque5TO55(5, TestObject(55)); + toDequeB = eastl::move(deque5TO55); + EATEST_VERIFY((toDequeB.size() == 5) && (toDequeB.front().mX == 55) && (deque5TO55.size() == 0)); } { // C++11 functionality - #if EASTL_MOVE_SEMANTICS_ENABLED && EASTL_VARIADIC_TEMPLATES_ENABLED - // template - // iterator emplace(const_iterator position, Args&&... args); - - // template - // void emplace_front(Args&&... args); - - // template - // void emplace_back(Args&&... args); - TestObject::Reset(); - - deque toDequeA; - - toDequeA.emplace_back(2, 3, 4); - EATEST_VERIFY_F((toDequeA.size() == 1) && (toDequeA.back().mX == (2+3+4)) && (TestObject::sTOCtorCount == 1), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.back().mX, (int)TestObject::sTOCtorCount); - - toDequeA.emplace(toDequeA.begin(), 3, 4, 5); // This is 3 because of how subarray allocation works. - EATEST_VERIFY_F((toDequeA.size() == 2) && (toDequeA.front().mX == (3+4+5)) && (TestObject::sTOCtorCount == 3), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.front().mX, (int)TestObject::sTOCtorCount); - - toDequeA.emplace_front(6, 7, 8); - EATEST_VERIFY_F((toDequeA.size() == 3) && (toDequeA.front().mX == (6+7+8)) && (TestObject::sTOCtorCount == 4), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.front().mX, (int)TestObject::sTOCtorCount); - #else - #if EASTL_MOVE_SEMANTICS_ENABLED - // iterator emplace(const_iterator position, value_type&& value); - // void emplace_front(value_type&& value); - // void emplace_back(value_type&& value); - TestObject::Reset(); - - // We have a potential problem here in that the compiler is not required to use move construction below. - // It is allowed to use standard copy construction if it wants. We could force it with eastl::move() usage. - deque toDequeA; - - toDequeA.emplace_back(TestObject(2, 3, 4)); - EATEST_VERIFY_F((toDequeA.size() == 1) && (toDequeA.back().mX == (2+3+4)) && (TestObject::sTOMoveCtorCount == 1), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.back().mX, (int)TestObject::sTOMoveCtorCount); - - toDequeA.emplace(toDequeA.begin(), TestObject(3, 4, 5)); - EATEST_VERIFY_F((toDequeA.size() == 2) && (toDequeA.front().mX == (3+4+5)) && (TestObject::sTOMoveCtorCount == 3), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.front().mX, (int)TestObject::sTOMoveCtorCount); - - toDequeA.emplace_front(TestObject(6, 7, 8)); - EATEST_VERIFY_F((toDequeA.size() == 3) && (toDequeA.front().mX == (6+7+8)) && (TestObject::sTOMoveCtorCount == 4), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.front().mX, (int)TestObject::sTOMoveCtorCount); - #endif - - // iterator emplace(const_iterator position, const value_type& value); - // void emplace_front(const value_type& value); - // void emplace_back(const value_type& value); - TestObject::Reset(); - - deque toDequeB; - TestObject to234(2, 3, 4); - TestObject to345(3, 4, 5); - TestObject to678(6, 7, 8); - - toDequeB.emplace_back(to234); // This will be copied (not moved) by compilers that don't support rvalue move because it's not a temporary and we don't use eastl::move() on it. - EATEST_VERIFY_F((toDequeB.size() == 1) && (toDequeB.back().mX == (2+3+4)) && (TestObject::sTOArgCtorCount == 3), "size: %u, mX: %u, count: %d", (unsigned)toDequeB.size(), (unsigned)toDequeB.back().mX, (int)TestObject::sTOArgCtorCount); - EATEST_VERIFY_F((TestObject::sTOCopyCtorCount + TestObject::sTOMoveCtorCount) == 1, "counts: %d %d", (int)TestObject::sTOCopyCtorCount, (int)TestObject::sTOMoveCtorCount); // A compiler that supports move may in fact take advantage of that internally. - - toDequeB.emplace(toDequeB.begin(), to345); // This will be copied (not moved) by compilers that don't support rvalue move because it's not a temporary and we don't use eastl::move() on it. - EATEST_VERIFY_F((toDequeB.size() == 2) && (toDequeB.front().mX == (3+4+5)) && (TestObject::sTOArgCtorCount == 3), "size: %u, mX: %u, count: %d", (unsigned)toDequeB.size(), (unsigned)toDequeB.front().mX, (int)TestObject::sTOArgCtorCount); - EATEST_VERIFY_F((TestObject::sTOCopyCtorCount + TestObject::sTOMoveCtorCount) == 3, "counts: %d %d", (int)TestObject::sTOCopyCtorCount, (int)TestObject::sTOMoveCtorCount); // A compiler that supports move may in fact take advantage of that internally. - - toDequeB.emplace_front(to678); // This will be copied (not moved) by compilers that don't support rvalue move because it's not a temporary and we don't use eastl::move() on it. - EATEST_VERIFY_F((toDequeB.size() == 3) && (toDequeB.front().mX == (6+7+8)) && (TestObject::sTOArgCtorCount == 3), "size: %u, mX: %u, count: %d", (unsigned)toDequeB.size(), (unsigned)toDequeB.front().mX, (int)TestObject::sTOArgCtorCount); - EATEST_VERIFY_F((TestObject::sTOCopyCtorCount + TestObject::sTOMoveCtorCount) == 4, "counts: %d %d", (int)TestObject::sTOCopyCtorCount, (int)TestObject::sTOMoveCtorCount); // A compiler that supports move may in fact take advantage of that internally. - - EATEST_VERIFY_F(to234.mX == (2+3+4), "mX: %d", to234.mX); // Verify that the object was copied and not moved. If it was moved then mX would be 0 and not 2+3+4. - EATEST_VERIFY_F(to345.mX == (3+4+5), "mX: %d", to345.mX); - EATEST_VERIFY_F(to678.mX == (6+7+8), "mX: %d", to678.mX); - #endif + // template + // iterator emplace(const_iterator position, Args&&... args); + // template + // void emplace_front(Args&&... args); - #if EASTL_MOVE_SEMANTICS_ENABLED - // This test is similar to the emplace EASTL_MOVE_SEMANTICS_ENABLED pathway above. - TestObject::Reset(); + // template + // void emplace_back(Args&&... args); + TestObject::Reset(); - //void push_front(T&& x); - //void push_back(T&& x); - //iterator insert(const_iterator position, T&& x); + deque toDequeA; - deque toDequeC; // Specify a non-small kSubarrayCount of 16 because the move count tests below assume there is no reallocation. + toDequeA.emplace_back(2, 3, 4); + EATEST_VERIFY_F((toDequeA.size() == 1) && (toDequeA.back().mX == (2+3+4)) && (TestObject::sTOCtorCount == 1), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.back().mX, (int)TestObject::sTOCtorCount); - toDequeC.push_back(TestObject(2, 3, 4)); - EATEST_VERIFY((toDequeC.size() == 1) && (toDequeC.back().mX == (2+3+4)) && (TestObject::sTOMoveCtorCount == 1)); + toDequeA.emplace(toDequeA.begin(), 3, 4, 5); // This is 3 because of how subarray allocation works. + EATEST_VERIFY_F((toDequeA.size() == 2) && (toDequeA.front().mX == (3+4+5)) && (TestObject::sTOCtorCount == 3), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.front().mX, (int)TestObject::sTOCtorCount); - toDequeC.insert(toDequeC.begin(), TestObject(3, 4, 5)); - EATEST_VERIFY((toDequeC.size() == 2) && (toDequeC.front().mX == (3+4+5)) && (TestObject::sTOMoveCtorCount == 3)); + toDequeA.emplace_front(6, 7, 8); + EATEST_VERIFY_F((toDequeA.size() == 3) && (toDequeA.front().mX == (6+7+8)) && (TestObject::sTOCtorCount == 4), "size: %u, mX: %u, count: %d", (unsigned)toDequeA.size(), (unsigned)toDequeA.front().mX, (int)TestObject::sTOCtorCount); - toDequeC.push_front(TestObject(6, 7, 8)); - EATEST_VERIFY((toDequeC.size() == 3) && (toDequeC.front().mX == (6+7+8)) && (TestObject::sTOMoveCtorCount == 4)); - #endif + + // This test is similar to the emplace EASTL_MOVE_SEMANTICS_ENABLED pathway above. + TestObject::Reset(); + + //void push_front(T&& x); + //void push_back(T&& x); + //iterator insert(const_iterator position, T&& x); + + deque toDequeC; // Specify a non-small kSubarrayCount of 16 because the move count tests below assume there is no reallocation. + + toDequeC.push_back(TestObject(2, 3, 4)); + EATEST_VERIFY((toDequeC.size() == 1) && (toDequeC.back().mX == (2+3+4)) && (TestObject::sTOMoveCtorCount == 1)); + + toDequeC.insert(toDequeC.begin(), TestObject(3, 4, 5)); + EATEST_VERIFY((toDequeC.size() == 2) && (toDequeC.front().mX == (3+4+5)) && (TestObject::sTOMoveCtorCount == 3)); + + toDequeC.push_front(TestObject(6, 7, 8)); + EATEST_VERIFY((toDequeC.size() == 3) && (toDequeC.front().mX == (6+7+8)) && (TestObject::sTOMoveCtorCount == 4)); } @@ -1007,6 +945,16 @@ int TestDeque() const eastl::deque constIntDeque5 = constIntDeque4; } + { + // test shrink_to_fit + eastl::deque d(4096); + d.erase(d.begin(), d.end()); + + auto prev = d.get_allocator().getActiveAllocationSize(); + d.shrink_to_fit(); + VERIFY(d.get_allocator().getActiveAllocationSize() < prev); + } + return nErrorCount; } diff --git a/test/source/TestFixedHash.cpp b/test/source/TestFixedHash.cpp index b40046b4..d7e20d00 100644 --- a/test/source/TestFixedHash.cpp +++ b/test/source/TestFixedHash.cpp @@ -137,6 +137,42 @@ template class eastl::fixed_hash_multiset, eastl:: template class eastl::fixed_hash_multimap, eastl::equal_to, false, MallocAllocator>; + +template +int TestFixedHashMapClearBuckets() +{ + int nErrorCount = 0; + + FixedHashMap fixedHashMap; + const auto nPreClearBucketCount = fixedHashMap.bucket_count(); + + for (int j = 0; j < ITERATION_MAX; j++) + { + // add elements and ensure container is valid + for (int i = 0; i < int(nPreClearBucketCount); i++) + fixedHashMap.emplace(i, i); + VERIFY(fixedHashMap.validate()); + + // ensure contents are expected values + for (int i = 0; i < int(nPreClearBucketCount); i++) + { + auto iter = fixedHashMap.find(i); + + VERIFY(iter != fixedHashMap.end()); + VERIFY(iter->second == i); + } + + // validate container after its cleared its nodes and buckets + fixedHashMap.clear(true); + VERIFY(fixedHashMap.validate()); + VERIFY(fixedHashMap.size() == 0); + VERIFY(fixedHashMap.bucket_count() == nPreClearBucketCount); + } + + return nErrorCount; +} + + EA_DISABLE_VC_WARNING(6262) int TestFixedHash() { @@ -174,7 +210,7 @@ int TestFixedHash() fixedHashMap.clear(true); VERIFY(fixedHashMap.validate()); VERIFY(fixedHashMap.size() == 0); - VERIFY(fixedHashMap.bucket_count() == 1); + VERIFY(fixedHashMap.bucket_count() == fixedHashMap.rehash_policy().GetPrevBucketCount(100)); } { @@ -203,13 +239,26 @@ int TestFixedHash() fixedHashMap.clear(true); VERIFY(fixedHashMap.validate()); VERIFY(fixedHashMap.size() == 0); - VERIFY(fixedHashMap.bucket_count() == 1); + VERIFY(fixedHashMap.bucket_count() == fixedHashMap.rehash_policy().GetPrevBucketCount(100)); // get_overflow_allocator / set_overflow_allocator // This is a weak test which should be improved. EASTLAllocatorType a = fixedHashMap.get_allocator().get_overflow_allocator(); fixedHashMap.get_allocator().set_overflow_allocator(a); } + + // Test that fixed_hash_map (with and without overflow enabled) is usable after the node and bucket array has + // been cleared. + { + constexpr const int ITERATION_MAX = 5; + constexpr const int ELEMENT_MAX = 100; + constexpr const int ELEMENT_OVERFLOW_MAX = ELEMENT_MAX * 2; + + TestFixedHashMapClearBuckets, ELEMENT_MAX, ITERATION_MAX>(); + TestFixedHashMapClearBuckets, ELEMENT_OVERFLOW_MAX, ITERATION_MAX>(); + TestFixedHashMapClearBuckets, ELEMENT_MAX, ITERATION_MAX>(); + TestFixedHashMapClearBuckets, ELEMENT_OVERFLOW_MAX, ITERATION_MAX>(); + } { // Test fixed_hash_map *with* overflow and ensure the underlying hashtable rehashes. @@ -583,49 +632,102 @@ int TestFixedHash() { // initializer_list support. - #if !defined(EA_COMPILER_NO_INITIALIZER_LISTS) && !defined(_MSC_VER) //MSVC2013 cannot handle nested initializer lists properly. A bug report will be submitted for this. - // fixed_hash_set(std::initializer_list ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR) - // this_type& operator=(std::initializer_list ilist); - // void insert(std::initializer_list ilist); - fixed_hash_set intHashSet = { 12, 13, 14 }; - EATEST_VERIFY(intHashSet.size() == 3); - EATEST_VERIFY(intHashSet.find(12) != intHashSet.end()); - EATEST_VERIFY(intHashSet.find(13) != intHashSet.end()); - EATEST_VERIFY(intHashSet.find(14) != intHashSet.end()); - - intHashSet = { 22, 23, 24 }; - EATEST_VERIFY(intHashSet.size() == 3); - EATEST_VERIFY(intHashSet.find(22) != intHashSet.end()); - EATEST_VERIFY(intHashSet.find(23) != intHashSet.end()); - EATEST_VERIFY(intHashSet.find(24) != intHashSet.end()); - - intHashSet.insert({ 42, 43, 44 }); - EATEST_VERIFY(intHashSet.size() == 6); - EATEST_VERIFY(intHashSet.find(42) != intHashSet.end()); - EATEST_VERIFY(intHashSet.find(43) != intHashSet.end()); - EATEST_VERIFY(intHashSet.find(44) != intHashSet.end()); - - // hash_map(std::initializer_list ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR) - // this_type& operator=(std::initializer_list ilist); - // void insert(std::initializer_list ilist); - fixed_hash_map intHashMap = { {12,12.0}, {13,13.0}, {14,14.0} }; - EATEST_VERIFY(intHashMap.size() == 3); - EATEST_VERIFY(intHashMap.find(12) != intHashMap.end()); - EATEST_VERIFY(intHashMap.find(13) != intHashMap.end()); - EATEST_VERIFY(intHashMap.find(14) != intHashMap.end()); - - intHashMap = { {22,22.0}, {23,23.0}, {24,24.0} }; - EATEST_VERIFY(intHashMap.size() == 3); - EATEST_VERIFY(intHashMap.find(22) != intHashMap.end()); - EATEST_VERIFY(intHashMap.find(23) != intHashMap.end()); - EATEST_VERIFY(intHashMap.find(24) != intHashMap.end()); - - intHashMap.insert({ {42,42.0}, {43,43.0}, {44,44.0} }); - EATEST_VERIFY(intHashMap.size() == 6); - EATEST_VERIFY(intHashMap.find(42) != intHashMap.end()); - EATEST_VERIFY(intHashMap.find(43) != intHashMap.end()); - EATEST_VERIFY(intHashMap.find(44) != intHashMap.end()); - #endif + // fixed_hash_set(std::initializer_list ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR) + // this_type& operator=(std::initializer_list ilist); + // void insert(std::initializer_list ilist); + fixed_hash_set intHashSet = { 12, 13, 14 }; + EATEST_VERIFY(intHashSet.size() == 3); + EATEST_VERIFY(intHashSet.find(12) != intHashSet.end()); + EATEST_VERIFY(intHashSet.find(13) != intHashSet.end()); + EATEST_VERIFY(intHashSet.find(14) != intHashSet.end()); + + intHashSet = { 22, 23, 24 }; + EATEST_VERIFY(intHashSet.size() == 3); + EATEST_VERIFY(intHashSet.find(22) != intHashSet.end()); + EATEST_VERIFY(intHashSet.find(23) != intHashSet.end()); + EATEST_VERIFY(intHashSet.find(24) != intHashSet.end()); + + intHashSet.insert({ 42, 43, 44 }); + EATEST_VERIFY(intHashSet.size() == 6); + EATEST_VERIFY(intHashSet.find(42) != intHashSet.end()); + EATEST_VERIFY(intHashSet.find(43) != intHashSet.end()); + EATEST_VERIFY(intHashSet.find(44) != intHashSet.end()); + + // hash_map(std::initializer_list ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR) + // this_type& operator=(std::initializer_list ilist); + // void insert(std::initializer_list ilist); + fixed_hash_map intHashMap = { {12,12.0}, {13,13.0}, {14,14.0} }; + EATEST_VERIFY(intHashMap.size() == 3); + EATEST_VERIFY(intHashMap.find(12) != intHashMap.end()); + EATEST_VERIFY(intHashMap.find(13) != intHashMap.end()); + EATEST_VERIFY(intHashMap.find(14) != intHashMap.end()); + + intHashMap = { {22,22.0}, {23,23.0}, {24,24.0} }; + EATEST_VERIFY(intHashMap.size() == 3); + EATEST_VERIFY(intHashMap.find(22) != intHashMap.end()); + EATEST_VERIFY(intHashMap.find(23) != intHashMap.end()); + EATEST_VERIFY(intHashMap.find(24) != intHashMap.end()); + + intHashMap.insert({ {42,42.0}, {43,43.0}, {44,44.0} }); + EATEST_VERIFY(intHashMap.size() == 6); + EATEST_VERIFY(intHashMap.find(42) != intHashMap.end()); + EATEST_VERIFY(intHashMap.find(43) != intHashMap.end()); + EATEST_VERIFY(intHashMap.find(44) != intHashMap.end()); + } + + { + constexpr int ELEM_MAX = 10; + typedef eastl::fixed_hash_map FixedHashMapFalse; + FixedHashMapFalse fixedHashMap; + VERIFY(fixedHashMap.size() == 0); + + for (int i = 0; i < ELEM_MAX; i++) + fixedHashMap.insert(FixedHashMapFalse::value_type(i, i)); + + VERIFY(fixedHashMap.validate()); + VERIFY(fixedHashMap.size() == ELEM_MAX); + + // Verify insert requests of nodes already in the container don't attempt to allocate memory. + // Because the fixed_hash_map is full any attempt to allocate memory will generate an OOM error. + { + auto result = fixedHashMap.insert(FixedHashMapFalse::value_type(0, 0)); + VERIFY(result.second == false); + } + + { + auto result = fixedHashMap.insert(fixedHashMap.begin(), FixedHashMapFalse::value_type(0, 0)); + VERIFY(result->first == 0); + VERIFY(result->second == 0); + } + + { + FixedHashMapFalse::value_type value(0, 0); + auto result = fixedHashMap.insert(eastl::move(value)); + VERIFY(result.second == false); + } + { + FixedHashMapFalse::value_type value(0, 0); + auto result = fixedHashMap.insert(fixedHashMap.begin(), eastl::move(value)); + VERIFY(result->first == 0); + VERIFY(result->second == 0); + } + + { + FixedHashMapFalse::value_type value(0, 0); + auto result = fixedHashMap.insert(value); + VERIFY(result.second == false); + } + + { + auto result = fixedHashMap.insert(eastl::make_pair(0, 0)); + VERIFY(result.second == false); + } + + { + // OOM, fixed allocator memory is exhausted so it can't create a node for insertation testing + // auto result = fixedHashMap.emplace(0, 0); + // VERIFY(result.second == false); + } } return nErrorCount; diff --git a/test/source/TestSmartPtr.cpp b/test/source/TestSmartPtr.cpp index a0db8291..387320b5 100644 --- a/test/source/TestSmartPtr.cpp +++ b/test/source/TestSmartPtr.cpp @@ -1812,6 +1812,41 @@ static int Test_intrusive_ptr() EATEST_VERIFY(ip7); } + { + // Test move-ctor + { + VERIFY(RefCountTest::mCount == 0); + intrusive_ptr ip1(new RefCountTest); + VERIFY(RefCountTest::mCount == 1); + VERIFY(ip1->mRefCount == 1); + { + intrusive_ptr ip2(eastl::move(ip1)); + VERIFY(ip1.get() != ip2.get()); + VERIFY(ip2->mRefCount == 1); + VERIFY(RefCountTest::mCount == 1); + } + VERIFY(ip1.get() == nullptr); + VERIFY(RefCountTest::mCount == 0); + } + + // Test move-assignment + { + VERIFY(RefCountTest::mCount == 0); + intrusive_ptr ip1(new RefCountTest); + VERIFY(RefCountTest::mCount == 1); + VERIFY(ip1->mRefCount == 1); + { + intrusive_ptr ip2; + ip2 = eastl::move(ip1); + VERIFY(ip1.get() != ip2.get()); + VERIFY(ip2->mRefCount == 1); + VERIFY(RefCountTest::mCount == 1); + } + VERIFY(ip1.get() == nullptr); + VERIFY(RefCountTest::mCount == 0); + } + } + { // Test modifiers (assign, attach, detach, reset, swap) RefCountTest* const p1 = new RefCountTest; RefCountTest* const p2 = new RefCountTest; diff --git a/test/source/TestString.inl b/test/source/TestString.inl index 0b160ddf..509c7aaf 100644 --- a/test/source/TestString.inl +++ b/test/source/TestString.inl @@ -17,34 +17,122 @@ template int TEST_STRING_NAME() { int nErrorCount = 0; - - // SSO (short string optimization) tests + + struct Failocator { - struct Failocator - { - Failocator() = default; - Failocator(const char*) {} + Failocator() = default; + Failocator(const char*) {} - void* allocate(size_t n) { EA_FAIL(); return nullptr; } - void deallocate(void* p, size_t) { EA_FAIL(); } - }; + void* allocate(size_t n) { EA_FAIL(); return nullptr; } + void deallocate(void* p, size_t) { EA_FAIL(); } + }; - // Use custom string type that always fails to allocate memory to highlight when SSO is not functioning correctly. - typedef eastl::basic_string SSOString; + #if defined(EA_PLATFORM_ANDROID) || defined(EA_PLATFORM_APPLE) + EA_DISABLE_CLANG_WARNING(-Winherited-variadic-ctor) // warning: inheriting constructor does not inherit ellipsis + #endif + struct SSOStringType : public StringType + { + using StringType::StringType; + using StringType::IsSSO; + }; + // Use custom string type that always fails to allocate memory to highlight when SSO is not functioning correctly. + struct SSOFailocatorString : public eastl::basic_string + { + using eastl::basic_string::basic_string; + using eastl::basic_string::IsSSO; + }; + #if defined(EA_PLATFORM_ANDROID) || defined(EA_PLATFORM_APPLE) + EA_RESTORE_CLANG_WARNING() + #endif + + // SSO (short string optimization) tests + { { - SSOString str; + SSOFailocatorString str; VERIFY(str.validate()); VERIFY(str.empty()); + VERIFY(str.IsSSO()); } - if(!(sizeof(typename StringType::value_type) >= 4) && (EA_PLATFORM_WORD_SIZE == 4)) // not even a single char32_t (plus null-terminator) can fit into the SSO buffer on 32-bit platforms + if(EA_PLATFORM_WORD_SIZE == 8) { - auto* pLiteral = LITERAL("a"); - SSOString str(pLiteral); - VERIFY(str == pLiteral); - VERIFY(str.validate()); - VERIFY(EA::StdC::Strcmp(str.data(), pLiteral) == 0); + // test SSO size on 64 bit platforms + if(sizeof(typename StringType::value_type) == 1) + { + // we can fit 23 characters on 64bit system with 1 byte chars + const auto* pLiteral = LITERAL("aaaaaaaaaaaaaaaaaaaaaaa"); + SSOFailocatorString str(pLiteral); + + VERIFY(EA::StdC::Strlen(pLiteral) == 23); + VERIFY(str == pLiteral); + VERIFY(str.validate()); + VERIFY(str.IsSSO()); + } + + if(sizeof(typename StringType::value_type) == 2) + { + // we can fit 11 characters on 64 bit system with 2 byte chars + const auto* pLiteral = LITERAL("aaaaaaaaaaa"); + SSOFailocatorString str(pLiteral); + + VERIFY(EA::StdC::Strlen(pLiteral) == 11); + VERIFY(str == pLiteral); + VERIFY(str.validate()); + VERIFY(str.IsSSO()); + } + + if(sizeof(typename StringType::value_type) == 4) + { + // we can fit 5 characters on 64 bit system with 4 byte chars + const auto* pLiteral = LITERAL("aaaaa"); + SSOFailocatorString str(pLiteral); + + VERIFY(EA::StdC::Strlen(pLiteral) == 5); + VERIFY(str == pLiteral); + VERIFY(str.validate()); + VERIFY(str.IsSSO()); + } + } + + if(EA_PLATFORM_WORD_SIZE == 4) + { + // test SSO size on 32 bit platforms + if(sizeof(typename StringType::value_type) == 1) + { + // we can fit 11 characters on 32bit system with 1 byte chars + const auto* pLiteral = LITERAL("aaaaaaaaaaa"); + SSOFailocatorString str(pLiteral); + + VERIFY(EA::StdC::Strlen(pLiteral) == 11); + VERIFY(str == pLiteral); + VERIFY(str.validate()); + VERIFY(str.IsSSO()); + } + + if(sizeof(typename StringType::value_type) == 2) + { + // we can fit 5 characters on 32 bit system with 2 byte chars + const auto* pLiteral = LITERAL("aaaaa"); + SSOFailocatorString str(pLiteral); + + VERIFY(EA::StdC::Strlen(pLiteral) == 5); + VERIFY(str == pLiteral); + VERIFY(str.validate()); + VERIFY(str.IsSSO()); + } + + if(sizeof(typename StringType::value_type) == 4) + { + // we can fit 2 characters on 32 bit system with 4 byte chars + const auto* pLiteral = LITERAL("aa"); + SSOFailocatorString str(pLiteral); + + VERIFY(EA::StdC::Strlen(pLiteral) == 2); + VERIFY(str == pLiteral); + VERIFY(str.validate()); + VERIFY(str.IsSSO()); + } } } @@ -876,6 +964,141 @@ int TEST_STRING_NAME() VERIFY(str == LITERAL("cccccccccccccccccccccccccccccccc")); } + // void shrink_to_fit + { + SSOStringType str(LITERAL("a")); + str.reserve(100); + VERIFY(str.capacity() == 100); + str.shrink_to_fit(); + // string should shrink to SSO + VERIFY(str.IsSSO()); + + str = LITERAL("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // 32 characters + str.reserve(100); + VERIFY(str.capacity() == 100); + str.shrink_to_fit(); + // string should shrink but still be heap + VERIFY(str.capacity() == 32); + VERIFY(!str.IsSSO()); + } + + // void set_capacity(n) + { + const auto *pLiteral32 = LITERAL("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + const auto *pLiteral31 = LITERAL("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + const auto *pLiteral1 = LITERAL("a"); + const auto *pLiteral2 = LITERAL("aa"); + + SSOStringType str = pLiteral32; + // set_capacity(0) - deallocate and reset to SSO; + { + // heap -> sso + VERIFY(!str.IsSSO()); + str.set_capacity(0); + VERIFY(str.IsSSO()); + VERIFY(str == LITERAL("")); + } + { + // sso -> sso + str = pLiteral1; + VERIFY(str.IsSSO()); + str.set_capacity(0); + VERIFY(str.IsSSO()); + VERIFY(str == LITERAL("")); + } + + // set_capacity(npos) - set capacity equal to current size - should realloc + { + // heap -> heap + str = pLiteral32; + str.reserve(100); + VERIFY(!str.IsSSO()); + VERIFY(str.capacity() == 100); + str.set_capacity(StringType::npos); + VERIFY(!str.IsSSO()); + VERIFY(str.capacity() == 32); + VERIFY(str == pLiteral32); + } + { + // heap -> sso + str = pLiteral1; + str.reserve(100); + VERIFY(!str.IsSSO()); + VERIFY(str.capacity() == 100); + str.set_capacity(StringType::npos); + VERIFY(str.IsSSO()); + VERIFY(str == pLiteral1); + } + { + // sso -> sso + str = pLiteral1; + VERIFY(str.IsSSO()); + str.set_capacity(StringType::npos); + VERIFY(str.IsSSO()); + VERIFY(str == pLiteral1); + } + + // set_capacity(n > capacity) - set capacity greater than out current capacity + { + // heap -> heap + str = pLiteral32; + VERIFY(!str.IsSSO()); + auto nSavedCap = str.capacity(); + str.set_capacity(nSavedCap + 1); + VERIFY(!str.IsSSO()); + VERIFY(str == pLiteral32); + VERIFY(str.capacity() > nSavedCap); + } + { + // sso -> heap + str.set_capacity(0); // reset to sso + str = pLiteral1; + VERIFY(str.IsSSO()); + auto nSavedCap = str.capacity(); + str.set_capacity(nSavedCap + 1); + VERIFY(!str.IsSSO()); + VERIFY(str == pLiteral1); + VERIFY(str.capacity() > nSavedCap); + } + { + // sso -> sso + str.set_capacity(0); // reset to sso + str = pLiteral1; + VERIFY(str.IsSSO()); + auto nSavedCap = str.capacity(); + str.set_capacity(str.size() + 1); + VERIFY(str.IsSSO()); + VERIFY(str == pLiteral1); + VERIFY(str.capacity() == nSavedCap); + } + + // set_capacity(n < size) - set capacity less than current size, str should truncate + { + // sso -> sso + str = pLiteral2; + VERIFY(str.IsSSO()); + str.set_capacity(1); + VERIFY(str.IsSSO()); + VERIFY(str == pLiteral1); + } + { + // heap -> sso + str = pLiteral32; + VERIFY(!str.IsSSO()); + str.set_capacity(1); + VERIFY(str.IsSSO()); + VERIFY(str == pLiteral1); + } + { + // heap -> heap + str = pLiteral32; + VERIFY(!str.IsSSO()); + str.set_capacity(31); + VERIFY(!str.IsSSO()); + VERIFY(str == pLiteral31); + } + } + // void reserve(size_type = 0); { StringType str(LITERAL("abcdefghijklmnopqrstuvwxyz")); @@ -900,8 +1123,19 @@ int TEST_STRING_NAME() } // void force_size(size_type n); - { - // todo: tests required + { + // force_size does not write terminating null, meant to set size when using external + // string writing mnethods like strcpy or sprintf + StringType str(LITERAL("aaa")); + VERIFY(str.size() == 3); + str.force_size(0); + VERIFY(str.size() == 0); + str.reserve(4); // 32 bit platform with char32_t can only hold 2 characters + str.force_size(4); + VERIFY(str.size() == 4); + str[4] = '0'; + str = LITERAL("aaa"); + VERIFY(str.size() == 3); } // const value_type* data() const EA_NOEXCEPT; @@ -1160,6 +1394,37 @@ int TEST_STRING_NAME() str.insert(30, StringType(LITERAL(" is an example of a substring")), 1, 14); VERIFY(str == LITERAL("1a234bcdefghijk567lmnopqrstuvwis an example xyz")); + + { + StringType strSSO; + auto nSSOCap = strSSO.capacity(); + StringType strCheck; + strCheck.append(nSSOCap, LITERAL('a')); + + strSSO.append(nSSOCap - 1, LITERAL('a')); + + strSSO.insert(strSSO.size() - 1, LITERAL("a")); + VERIFY(strSSO.validate()); + VERIFY(strSSO == strCheck); + } + + { + StringType strSSO; + auto nSSOCap = strSSO.capacity(); + + // 32 bit platform with char32_t can only hold 2 characters in SSO + if (nSSOCap - 2 > 0) + { + StringType strCheck; + strCheck.append(nSSOCap, LITERAL('a')); + + strSSO.append(nSSOCap - 2, LITERAL('a')); + + strSSO.insert(strSSO.size() - 1, LITERAL("aa")); + VERIFY(strSSO.validate()); + VERIFY(strSSO == strCheck); + } + } } // iterator insert(const_iterator p, std::initializer_list); @@ -1175,6 +1440,19 @@ int TEST_STRING_NAME() #endif } + // insert(const_iterator p, value_type c) + { + StringType str = LITERAL("aaa"); + auto it = str.insert(str.end(), 'b'); + VERIFY(*it == LITERAL('b')); + VERIFY(str == LITERAL("aaab")); + it = str.insert(str.begin(), 'c'); + VERIFY(*it == LITERAL('c')); + VERIFY(str == LITERAL("caaab")); + it = str.insert(str.begin() + 2, 'd'); + VERIFY(*it == LITERAL('d')); + VERIFY(str == LITERAL("cadaab")); + } // this_type& erase(size_type position = 0, size_type n = npos); // iterator erase(const_iterator p); diff --git a/test/source/TestTypeTraits.cpp b/test/source/TestTypeTraits.cpp index 205a91fa..4763228d 100644 --- a/test/source/TestTypeTraits.cpp +++ b/test/source/TestTypeTraits.cpp @@ -1140,6 +1140,21 @@ int TestTypeTraits() static_assert(is_trivially_copyable::value == true, "is_trivially_copyable failure"); #endif + { // user reported regression + struct Foo + { + int a; + Foo(int i) : a(i) {} + Foo(Foo&& other) : a(other.a) { other.a = 0; } + + Foo(const Foo&) = delete; + Foo& operator=(const Foo&) = delete; + }; + + static_assert(!eastl::is_trivially_copyable::value, "is_trivially_copyable failure"); + } + + // is_trivially_copy_assignable { static_assert(is_trivially_copy_assignable::value == true, "is_trivially_copy_assignable failure"); diff --git a/test/source/TestVariant.cpp b/test/source/TestVariant.cpp index 33c5dee0..d8129377 100644 --- a/test/source/TestVariant.cpp +++ b/test/source/TestVariant.cpp @@ -613,6 +613,25 @@ int TestVariantVisitor() } +int TestVariantAssignment() +{ + using namespace eastl; + int nErrorCount = 0; + + { + variant v = TestObject(1337); + VERIFY(get(v).mX == 1337); + TestObject::Reset(); + + v.operator=(42); // ensure assignment-operator is called + VERIFY(TestObject::sTODtorCount == 1); // verify TestObject dtor is called. + VERIFY(get(v) == 42); + TestObject::Reset(); + } + + return nErrorCount; +} + int TestVariant() { int nErrorCount = 0; @@ -629,6 +648,7 @@ int TestVariant() nErrorCount += TestVariantRelOps(); nErrorCount += TestVariantInplaceCtors(); nErrorCount += TestVariantVisitor(); + nErrorCount += TestVariantAssignment(); return nErrorCount; } #else diff --git a/test/source/TestVector.cpp b/test/source/TestVector.cpp index 7402cd24..29bed3c5 100644 --- a/test/source/TestVector.cpp +++ b/test/source/TestVector.cpp @@ -318,8 +318,9 @@ int TestVector() // Should be able to emplace_back an item with const members (non-copyable) eastl::vector myVec2; - myVec2.emplace_back(42); + ItemWithConst& ref = myVec2.emplace_back(42); EATEST_VERIFY(myVec2.back().i == 42); + EATEST_VERIFY(ref.i == 42); #endif } @@ -549,9 +550,10 @@ int TestVector() vector toVectorA; - toVectorA.emplace_back(2, 3, 4); + TestObject& ref = toVectorA.emplace_back(2, 3, 4); EATEST_VERIFY((toVectorA.size() == 1) && (toVectorA.back().mX == (2 + 3 + 4)) && (TestObject::sTOCtorCount == 1)); + EATEST_VERIFY(ref.mX == (2 + 3 + 4)); toVectorA.emplace(toVectorA.begin(), 3, 4, 5); EATEST_VERIFY((toVectorA.size() == 2) && (toVectorA.front().mX == (3 + 4 + 5)) && @@ -565,9 +567,10 @@ int TestVector() // It is allowed to use standard copy construction if it wants. We could force it with eastl::move() usage. vector toVectorA; - toVectorA.emplace_back(TestObject(2, 3, 4)); + TestObject& ref = toVectorA.emplace_back(TestObject(2, 3, 4)); EATEST_VERIFY((toVectorA.size() == 1) && (toVectorA.back().mX == (2 + 3 + 4)) && (TestObject::sTOMoveCtorCount == 1)); + EATEST_VERIFY(ref.mX == (2 + 3 + 4)); toVectorA.emplace(toVectorA.begin(), TestObject(3, 4, 5)); EATEST_VERIFY((toVectorA.size() == 2) && (toVectorA.front().mX == (3 + 4 + 5)) &&